Skip to content

Commit 32b6226

Browse files
authored
Purge consolidated protocols before compilation (elixir-editors#406)
* Purge consolidated protocols before compilation Fixes elixir-editors#395 * move to dialyzer_test.exs * add comments * warn if ls fails
1 parent 3acc1fd commit 32b6226

File tree

5 files changed

+129
-2
lines changed

5 files changed

+129
-2
lines changed

apps/language_server/lib/language_server/build.ex

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ defmodule ElixirLS.LanguageServer.Build do
2424
fetch_deps()
2525
end
2626

27+
# if we won't do it elixir >= 1.11 warns that protocols have already been consolidated
28+
purge_consolidated_protocols()
2729
{status, diagnostics} = compile()
2830

2931
if status in [:ok, :noop] and Keyword.get(opts, :load_all_modules?) do
@@ -123,8 +125,7 @@ defmodule ElixirLS.LanguageServer.Build do
123125
%{file: ^mixfile, name: module} ->
124126
# FIXME: Private API
125127
Mix.Project.pop()
126-
:code.purge(module)
127-
:code.delete(module)
128+
purge_module(module)
128129

129130
_ ->
130131
:ok
@@ -207,6 +208,30 @@ defmodule ElixirLS.LanguageServer.Build do
207208
end
208209
end
209210

211+
defp purge_consolidated_protocols do
212+
config = Mix.Project.config()
213+
path = Mix.Project.consolidation_path(config)
214+
215+
with {:ok, beams} <- File.ls(path) do
216+
Enum.map(beams, &(&1 |> Path.rootname(".beam") |> String.to_atom() |> purge_module()))
217+
else
218+
{:error, reason} ->
219+
JsonRpc.show_message(
220+
:warning,
221+
"Unable to purge consolidated protocols from #{path}: #{inspect(reason)}"
222+
)
223+
end
224+
225+
# NOTE this implementation is based on https://github.com/phoenixframework/phoenix/commit/b5580e9
226+
# calling `Code.delete_path(path)` may be unnecessary in our case
227+
Code.delete_path(path)
228+
end
229+
230+
defp purge_module(module) do
231+
:code.purge(module)
232+
:code.delete(module)
233+
end
234+
210235
defp cached_deps do
211236
try do
212237
# FIXME: Private API

apps/language_server/test/dialyzer_test.exs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,4 +347,81 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
347347
end)
348348
end)
349349
end
350+
351+
test "protocol rebuild does not trigger consolidation warnings", %{server: server} do
352+
in_fixture(__DIR__, "protocols", fn ->
353+
root_uri = SourceFile.path_to_uri(File.cwd!())
354+
uri = SourceFile.path_to_uri(Path.absname("lib/implementations.ex"))
355+
356+
Server.receive_packet(server, initialize_req(1, root_uri, %{}))
357+
Server.receive_packet(server, notification("initialized"))
358+
359+
Server.receive_packet(
360+
server,
361+
did_change_configuration(%{"elixirLS" => %{"dialyzerEnabled" => true}})
362+
)
363+
364+
assert_receive notification("window/logMessage", %{"message" => "Compile took" <> _}), 5000
365+
366+
assert_receive notification("window/logMessage", %{
367+
"message" => "[ElixirLS Dialyzer] Done writing manifest" <> _
368+
}),
369+
30000
370+
371+
v2_text = """
372+
defimpl Protocols.Example, for: List do
373+
def some(t), do: t
374+
end
375+
376+
defimpl Protocols.Example, for: String do
377+
def some(t), do: t
378+
end
379+
380+
defimpl Protocols.Example, for: Map do
381+
def some(t), do: t
382+
end
383+
"""
384+
385+
Server.receive_packet(server, did_open(uri, "elixir", 1, v2_text))
386+
File.write!("lib/implementations.ex", v2_text)
387+
Server.receive_packet(server, did_save(uri))
388+
389+
assert_receive notification("window/logMessage", %{"message" => "Compile took" <> _}), 5000
390+
391+
assert_receive notification("textDocument/publishDiagnostics", %{"diagnostics" => []}),
392+
30000
393+
394+
Process.sleep(2000)
395+
396+
v2_text = """
397+
defimpl Protocols.Example, for: List do
398+
def some(t), do: t
399+
end
400+
401+
defimpl Protocols.Example, for: String do
402+
def some(t), do: t
403+
end
404+
405+
defimpl Protocols.Example, for: Map do
406+
def some(t), do: t
407+
end
408+
409+
defimpl Protocols.Example, for: Atom do
410+
def some(t), do: t
411+
end
412+
"""
413+
414+
Server.receive_packet(server, did_open(uri, "elixir", 1, v2_text))
415+
File.write!("lib/implementations.ex", v2_text)
416+
Server.receive_packet(server, did_save(uri))
417+
418+
assert_receive notification("window/logMessage", %{"message" => "Compile took" <> _}), 5000
419+
420+
assert_receive notification("textDocument/publishDiagnostics", %{"diagnostics" => []}),
421+
30000
422+
423+
# we should not receive Protocol has already been consolidated warnings here
424+
refute_receive notification("textDocument/publishDiagnostics", _), 3000
425+
end)
426+
end
350427
end
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
defprotocol Protocols.Example do
2+
@spec some(t) :: any
3+
def some(t)
4+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
defimpl Protocols.Example, for: List do
2+
def some(t), do: t
3+
end
4+
5+
defimpl Protocols.Example, for: String do
6+
def some(t), do: t
7+
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
defmodule Protocols.MixProject do
2+
use Mix.Project
3+
4+
def project do
5+
[
6+
app: :protocols,
7+
version: "0.1.0"
8+
]
9+
end
10+
11+
def application do
12+
[]
13+
end
14+
end

0 commit comments

Comments
 (0)