Skip to content

Commit 7e9dd6b

Browse files
authored
Get dialyzer warnings working in umbrella apps. (#149)
* Get dialyzer warnings working in umbrella apps. * Add helper functions after the function that needs them.
1 parent 396edd1 commit 7e9dd6b

File tree

7 files changed

+107
-7
lines changed

7 files changed

+107
-7
lines changed

apps/language_server/lib/language_server/dialyzer.ex

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -365,13 +365,13 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
365365

366366
defp add_warnings(warnings, raw_warnings) do
367367
new_warnings =
368-
for {_, {file, line, m_or_mfa}, _} = warning <- raw_warnings, in_project?(file) do
369-
module =
370-
case m_or_mfa do
371-
module when is_atom(module) -> module
372-
{module, _, _} when is_atom(module) -> module
373-
end
374-
368+
for {_, {file, line, m_or_mfa}, _} = warning <- raw_warnings,
369+
module = resolve_module(m_or_mfa),
370+
# Dialyzer warnings have the file path at the start of the app it's
371+
# in, which breaks umbrella apps. We have to manually resolve the file
372+
# from the module instead.
373+
file = resolve_module_file(module, file),
374+
in_project?(file) do
375375
{module, {file, line, warning}}
376376
end
377377

@@ -380,6 +380,19 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
380380
|> Enum.into(warnings)
381381
end
382382

383+
defp resolve_module(module) when is_atom(module), do: module
384+
defp resolve_module({module, _, _}) when is_atom(module), do: module
385+
386+
defp resolve_module_file(module, fallback) do
387+
# We try to resolve the module to its source file. The only time the source
388+
# info may not be available is when it has been stripped by the beam_lib
389+
# module, but that shouldn't be the case. More info:
390+
# http://erlang.org/doc/reference_manual/modules.html#module_info-0-and-module_info-1-functions
391+
module.module_info(:compile)
392+
|> Keyword.get(:source, fallback)
393+
|> Path.relative_to_cwd()
394+
end
395+
383396
defp dependent_modules(modules, mod_deps, result \\ MapSet.new())
384397

385398
defp dependent_modules([], _, result) do

apps/language_server/test/dialyzer_test.exs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,50 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
234234
end)
235235
end
236236

237+
test "reports dialyxir_short error in umbrella", %{server: server} do
238+
in_fixture(__DIR__, "umbrella_dialyzer", fn ->
239+
file_a = SourceFile.path_to_uri(Path.absname("apps/app1/lib/app1.ex"))
240+
241+
capture_log(fn ->
242+
root_uri = SourceFile.path_to_uri(File.cwd!())
243+
Server.receive_packet(server, initialize_req(1, root_uri, %{}))
244+
245+
Server.receive_packet(
246+
server,
247+
did_change_configuration(%{
248+
"elixirLS" => %{"dialyzerEnabled" => true, "dialyzerFormat" => "dialyxir_short"}
249+
})
250+
)
251+
252+
message = assert_receive %{"method" => "textDocument/publishDiagnostics"}, 20000
253+
254+
assert publish_diagnostics_notif(^file_a, [
255+
%{
256+
"message" => error_message1,
257+
"range" => %{
258+
"end" => %{"character" => 0, "line" => 1},
259+
"start" => %{"character" => 0, "line" => 1}
260+
},
261+
"severity" => 2,
262+
"source" => "ElixirLS Dialyzer"
263+
},
264+
%{
265+
"message" => error_message2,
266+
"range" => %{
267+
"end" => %{"character" => 0, "line" => 2},
268+
"start" => %{"character" => 0, "line" => 2}
269+
},
270+
"severity" => 2,
271+
"source" => "ElixirLS Dialyzer"
272+
}
273+
]) = message
274+
275+
assert error_message1 == "Function check_error/0 has no local return."
276+
assert error_message2 == "The pattern can never match the type :error."
277+
end)
278+
end)
279+
end
280+
237281
# Failing and extremely slow (~3s)
238282
@tag :pending
239283
test "clears diagnostics when source files are deleted", %{server: server} do
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
defmodule App1 do
2+
def check_error() do
3+
:ok = App2.error()
4+
end
5+
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
defmodule App1.Mixfile do
2+
use Mix.Project
3+
4+
def project do
5+
[app: :app1, version: "0.1.0", deps: deps()]
6+
end
7+
8+
def application do
9+
[]
10+
end
11+
12+
defp deps do
13+
[{:app2, in_umbrella: true}]
14+
end
15+
end
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
defmodule App2 do
2+
def error do
3+
:error
4+
end
5+
end
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
defmodule App2.Mixfile do
2+
use Mix.Project
3+
4+
def project do
5+
[app: :app2, version: "0.1.0"]
6+
end
7+
8+
def application do
9+
[]
10+
end
11+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
defmodule UmbrellaDialyzer.Mixfile do
2+
use Mix.Project
3+
4+
def project do
5+
[apps_path: "apps"]
6+
end
7+
end

0 commit comments

Comments
 (0)