Skip to content

Commit f08f5ab

Browse files
authored
Dialyzer improvements (elixir-editors#422)
* avoid another pass over enumerable * avoid expand_references on the same modules if they are used in more than 1 app avoid getting path from module when path is already known * add always started otp apps to plt before they were included indirectly as elixir app deps not all modules from those apps were analyzed * prefer consolidated protocol beams * avoid unnecessary passes over map * run formatter * avoid another iterations
1 parent bdf225b commit f08f5ab

File tree

3 files changed

+39
-25
lines changed

3 files changed

+39
-25
lines changed

apps/language_server/lib/language_server/dialyzer.ex

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,21 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
210210
defp update_stale(md5, removed_files, file_changes, timestamp) do
211211
prev_paths = Map.keys(md5) |> MapSet.new()
212212

213+
# FIXME: Private API
214+
consolidation_path = Mix.Project.consolidation_path()
215+
216+
consolidated_protocol_beams =
217+
for path <- Path.join(consolidation_path, "*.beam") |> Path.wildcard(),
218+
into: MapSet.new(),
219+
do: Path.basename(path)
220+
213221
# FIXME: Private API
214222
all_paths =
215-
Mix.Utils.extract_files([Mix.Project.build_path()], [:beam])
216-
|> Enum.map(&Path.relative_to_cwd(&1))
217-
|> MapSet.new()
223+
for path <- Mix.Utils.extract_files([Mix.Project.build_path()], [:beam]),
224+
Path.basename(path) not in consolidated_protocol_beams or
225+
Path.dirname(path) == consolidation_path,
226+
into: MapSet.new(),
227+
do: Path.relative_to_cwd(path)
218228

219229
removed =
220230
prev_paths
@@ -373,13 +383,13 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
373383
mod_deps = update_mod_deps(mod_deps, new_mod_deps, removed_modules)
374384
warnings = add_warnings(warnings, raw_warnings)
375385

386+
md5 = Map.drop(md5, removed_files)
387+
376388
md5 =
377389
for {file, {_, hash}} <- file_changes, into: md5 do
378390
{file, hash}
379391
end
380392

381-
md5 = remove_files(md5, removed_files)
382-
383393
{active_plt, mod_deps, md5, warnings}
384394
end)
385395

@@ -392,14 +402,11 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
392402
end
393403

394404
defp update_mod_deps(mod_deps, new_mod_deps, removed_modules) do
395-
mod_deps
396-
|> Map.merge(new_mod_deps)
397-
|> Map.drop(removed_modules)
398-
|> Map.new(fn {mod, deps} -> {mod, deps -- removed_modules} end)
399-
end
400-
401-
defp remove_files(md5, removed_files) do
402-
Map.drop(md5, removed_files)
405+
for {mod, deps} <- mod_deps,
406+
mod not in removed_modules,
407+
into: new_mod_deps do
408+
{mod, deps -- removed_modules}
409+
end
403410
end
404411

405412
defp add_warnings(warnings, raw_warnings) do

apps/language_server/lib/language_server/dialyzer/manifest.ex

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,23 +115,31 @@ defmodule ElixirLS.LanguageServer.Dialyzer.Manifest do
115115
Path.join([Mix.Utils.mix_home(), "elixir-ls-#{otp_vsn()}_elixir-#{System.version()}"])
116116
end
117117

118-
@elixir_apps [:elixir, :eex, :ex_unit, :iex, :logger, :mix]
118+
@elixir_apps [:elixir, :ex_unit, :mix, :iex, :logger, :eex]
119+
@erlang_apps [:erts, :kernel, :stdlib, :compiler]
119120

120121
defp build_elixir_plt() do
121122
JsonRpc.show_message(
122123
:info,
123124
"Building core Dialyzer Elixir PLT. This will take a few minutes (often 15+) and can be disabled in the settings."
124125
)
125126

127+
modules_to_paths =
128+
for app <- @erlang_apps ++ @elixir_apps,
129+
path <- Path.join([Application.app_dir(app), "**/*.beam"]) |> Path.wildcard(),
130+
into: %{},
131+
do: {pathname_to_module(path), path |> String.to_charlist()}
132+
133+
modules =
134+
modules_to_paths
135+
|> Map.keys()
136+
|> expand_references
137+
126138
files =
127-
Enum.flat_map(@elixir_apps, fn app ->
128-
Path.join([Application.app_dir(app), "**/*.beam"])
129-
|> Path.wildcard()
130-
|> Enum.map(&pathname_to_module/1)
131-
|> expand_references()
132-
|> Enum.map(&Utils.get_beam_file/1)
133-
|> Enum.filter(&is_list/1)
134-
end)
139+
for mod <- modules,
140+
path = modules_to_paths[mod] || Utils.get_beam_file(mod),
141+
is_list(path),
142+
do: path
135143

136144
File.mkdir_p!(Path.dirname(elixir_plt_path()))
137145

apps/language_server/lib/language_server/dialyzer/utils.ex

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ defmodule ElixirLS.LanguageServer.Dialyzer.Utils do
2525
String.to_atom(Path.basename(path, ".beam"))
2626
end
2727

28-
def expand_references(modules, exclude \\ [], result \\ MapSet.new())
28+
def expand_references(modules, exclude \\ MapSet.new(), result \\ MapSet.new())
2929

3030
def expand_references([], _, result) do
3131
result
@@ -60,8 +60,7 @@ defmodule ElixirLS.LanguageServer.Dialyzer.Utils do
6060
forms
6161
)
6262

63-
modules = for {:call, _, {:remote, _, {:atom, _, module}, _}, _} <- calls, do: module
64-
Enum.uniq(modules)
63+
for {:call, _, {:remote, _, {:atom, _, module}, _}, _} <- calls, uniq: true, do: module
6564
rescue
6665
_ -> []
6766
catch

0 commit comments

Comments
 (0)