Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Purge consolidated protocols before compilation #406

Merged
merged 4 commits into from
Nov 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 27 additions & 2 deletions apps/language_server/lib/language_server/build.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ defmodule ElixirLS.LanguageServer.Build do
fetch_deps()
end

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

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

_ ->
:ok
Expand Down Expand Up @@ -207,6 +208,30 @@ defmodule ElixirLS.LanguageServer.Build do
end
end

defp purge_consolidated_protocols do
config = Mix.Project.config()
path = Mix.Project.consolidation_path(config)

with {:ok, beams} <- File.ls(path) do
Enum.map(beams, &(&1 |> Path.rootname(".beam") |> String.to_atom() |> purge_module()))
else
{:error, reason} ->
JsonRpc.show_message(
:warning,
"Unable to purge consolidated protocols from #{path}: #{inspect(reason)}"
)
end

# NOTE this implementation is based on https://github.com/phoenixframework/phoenix/commit/b5580e9
# calling `Code.delete_path(path)` may be unnecessary in our case
Code.delete_path(path)
end

defp purge_module(module) do
:code.purge(module)
:code.delete(module)
end

defp cached_deps do
try do
# FIXME: Private API
Expand Down
77 changes: 77 additions & 0 deletions apps/language_server/test/dialyzer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,81 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
end)
end)
end

test "protocol rebuild does not trigger consolidation warnings", %{server: server} do
in_fixture(__DIR__, "protocols", fn ->
root_uri = SourceFile.path_to_uri(File.cwd!())
uri = SourceFile.path_to_uri(Path.absname("lib/implementations.ex"))

Server.receive_packet(server, initialize_req(1, root_uri, %{}))
Server.receive_packet(server, notification("initialized"))

Server.receive_packet(
server,
did_change_configuration(%{"elixirLS" => %{"dialyzerEnabled" => true}})
)

assert_receive notification("window/logMessage", %{"message" => "Compile took" <> _}), 5000

assert_receive notification("window/logMessage", %{
"message" => "[ElixirLS Dialyzer] Done writing manifest" <> _
}),
30000

v2_text = """
defimpl Protocols.Example, for: List do
def some(t), do: t
end

defimpl Protocols.Example, for: String do
def some(t), do: t
end

defimpl Protocols.Example, for: Map do
def some(t), do: t
end
"""

Server.receive_packet(server, did_open(uri, "elixir", 1, v2_text))
File.write!("lib/implementations.ex", v2_text)
Server.receive_packet(server, did_save(uri))

assert_receive notification("window/logMessage", %{"message" => "Compile took" <> _}), 5000

assert_receive notification("textDocument/publishDiagnostics", %{"diagnostics" => []}),
30000

Process.sleep(2000)

v2_text = """
defimpl Protocols.Example, for: List do
def some(t), do: t
end

defimpl Protocols.Example, for: String do
def some(t), do: t
end

defimpl Protocols.Example, for: Map do
def some(t), do: t
end

defimpl Protocols.Example, for: Atom do
def some(t), do: t
end
"""

Server.receive_packet(server, did_open(uri, "elixir", 1, v2_text))
File.write!("lib/implementations.ex", v2_text)
Server.receive_packet(server, did_save(uri))

assert_receive notification("window/logMessage", %{"message" => "Compile took" <> _}), 5000

assert_receive notification("textDocument/publishDiagnostics", %{"diagnostics" => []}),
30000

# we should not receive Protocol has already been consolidated warnings here
refute_receive notification("textDocument/publishDiagnostics", _), 3000
end)
end
end
4 changes: 4 additions & 0 deletions apps/language_server/test/fixtures/protocols/lib/example.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
defprotocol Protocols.Example do
@spec some(t) :: any
def some(t)
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defimpl Protocols.Example, for: List do
def some(t), do: t
end

defimpl Protocols.Example, for: String do
def some(t), do: t
end
14 changes: 14 additions & 0 deletions apps/language_server/test/fixtures/protocols/mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
defmodule Protocols.MixProject do
use Mix.Project

def project do
[
app: :protocols,
version: "0.1.0"
]
end

def application do
[]
end
end