Skip to content

Commit dca9b0e

Browse files
authored
trap exit :dialyzer_utils.get_core_from_beam (#814)
* trap exit :dialyzer_utils.get_core_from_beam prevent silent exit of dialyzer process when it's unable to process some of the beams * apply PR suggestions * run formatter
1 parent 85487e7 commit dca9b0e

File tree

1 file changed

+64
-22
lines changed

1 file changed

+64
-22
lines changed

apps/language_server/lib/language_server/dialyzer.ex

Lines changed: 64 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -248,16 +248,7 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
248248
|> Enum.concat(new_paths)
249249
|> Enum.uniq()
250250

251-
changed_contents =
252-
Task.async_stream(
253-
changed,
254-
fn file ->
255-
content = File.read!(file)
256-
{file, content, module_md5(file)}
257-
end,
258-
timeout: :infinity
259-
)
260-
|> Enum.into([])
251+
changed_contents = get_changed_files_contents(changed)
261252

262253
{changed, changed_contents}
263254
end)
@@ -267,19 +258,52 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
267258
)
268259

269260
file_changes =
270-
Enum.reduce(changed_contents, file_changes, fn {:ok, {file, content, hash}}, file_changes ->
271-
if is_nil(hash) or hash == md5[file] do
272-
Map.delete(file_changes, file)
273-
else
274-
Map.put(file_changes, file, {content, hash})
275-
end
261+
Enum.reduce(changed_contents, file_changes, fn
262+
{:ok, {file, content, hash}}, file_changes ->
263+
if is_nil(hash) or hash == md5[file] do
264+
Map.delete(file_changes, file)
265+
else
266+
Map.put(file_changes, file, {content, hash})
267+
end
268+
269+
{:exit, reason}, file_changes ->
270+
# on elixir >= 1.14 reason will actually be {beam_path, reason} but
271+
# it's not easy to pattern match on that
272+
Logger.error(
273+
"[ElixirLS Dialyzer] Unable to process one of the beams: #{inspect(reason)}"
274+
)
275+
276+
file_changes
276277
end)
277278

278279
undialyzable = for {:ok, {file, _, nil}} <- changed_contents, do: file
279280
removed_files = Enum.uniq(removed_files ++ removed ++ (undialyzable -- changed))
280281
{removed_files, file_changes}
281282
end
282283

284+
defp get_changed_files_contents(changed) do
285+
with_trapping_exits(fn ->
286+
# TODO remove if when we require elixir 1.14
287+
task_options =
288+
if Version.match?(System.version(), ">= 1.14.0") do
289+
[zip_input_on_exit: true]
290+
else
291+
[]
292+
end
293+
|> Keyword.put(:timeout, :infinity)
294+
295+
Task.async_stream(
296+
changed,
297+
fn file ->
298+
content = File.read!(file)
299+
{file, content, module_md5(file)}
300+
end,
301+
task_options
302+
)
303+
|> Enum.into([])
304+
end)
305+
end
306+
283307
defp extract_stale(sources, timestamp) do
284308
for source <- sources,
285309
last_modified(source) > timestamp do
@@ -322,10 +346,12 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
322346

323347
{us, {active_plt, mod_deps, md5, warnings}} =
324348
:timer.tc(fn ->
325-
Task.async_stream(file_changes, fn {file, {content, _}} ->
326-
write_temp_file(root_path, file, content)
349+
with_trapping_exits(fn ->
350+
Task.async_stream(file_changes, fn {file, {content, _}} ->
351+
write_temp_file(root_path, file, content)
352+
end)
353+
|> Stream.run()
327354
end)
328-
|> Stream.run()
329355

330356
for file <- removed_files do
331357
path = temp_file_path(root_path, file)
@@ -338,7 +364,9 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
338364
:ok
339365

340366
{:error, reason} ->
341-
Logger.warn("Unable to remove temporary file #{path}: #{inspect(reason)}")
367+
Logger.warn(
368+
"[ElixirLS Dialyzer] Unable to remove temporary file #{path}: #{inspect(reason)}"
369+
)
342370
end
343371
end
344372

@@ -464,7 +492,11 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
464492
core_bin = :erlang.term_to_binary(core)
465493
:crypto.hash(:md5, core_bin)
466494

467-
{:error, _} ->
495+
{:error, reason} ->
496+
Logger.warn(
497+
"[ElixirLS Dialyzer] get_core_from_beam failed for #{file}: #{inspect(reason)}"
498+
)
499+
468500
nil
469501
end
470502
end
@@ -503,7 +535,10 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
503535
end
504536

505537
defp normalize_postion(position) do
506-
Logger.warn("dialyzer returned warning with invalid position #{inspect(position)}")
538+
Logger.warn(
539+
"[ElixirLS Dialyzer] dialyzer returned warning with invalid position #{inspect(position)}"
540+
)
541+
507542
0
508543
end
509544

@@ -556,4 +591,11 @@ defmodule ElixirLS.LanguageServer.Dialyzer do
556591
Process.unlink(pid)
557592
Process.exit(pid, :kill)
558593
end
594+
595+
defp with_trapping_exits(fun) do
596+
Process.flag(:trap_exit, true)
597+
fun.()
598+
after
599+
Process.flag(:trap_exit, false)
600+
end
559601
end

0 commit comments

Comments
 (0)