Skip to content

Commit

Permalink
Add detail and span to typing diagnostics
Browse files Browse the repository at this point in the history
Closes #13646.
  • Loading branch information
josevalim committed Jun 10, 2024
1 parent d5095f6 commit 7a4060c
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 328 deletions.
6 changes: 3 additions & 3 deletions lib/elixir/lib/exception.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ defmodule SyntaxError do
})
when not is_nil(snippet) and not is_nil(column) do
snippet =
:elixir_errors.format_snippet({line, column}, file, description, snippet, :error, [], nil)
:elixir_errors.format_snippet(:error, {line, column}, file, description, snippet, %{})

format_message(file, line, column, snippet)
end
Expand All @@ -1225,7 +1225,7 @@ defmodule SyntaxError do
description: description
}) do
snippet =
:elixir_errors.format_snippet({line, column}, file, description, nil, :error, [], nil)
:elixir_errors.format_snippet(:error, {line, column}, file, description, nil, %{})

padded = " " <> String.replace(snippet, "\n", "\n ")
format_message(file, line, column, padded)
Expand Down Expand Up @@ -1316,7 +1316,7 @@ defmodule TokenMissingError do
description: description
}) do
snippet =
:elixir_errors.format_snippet({line, column}, file, description, snippet, :error, [], nil)
:elixir_errors.format_snippet(:error, {line, column}, file, description, snippet, %{})

format_message(file, line, column, snippet)
end
Expand Down
32 changes: 18 additions & 14 deletions lib/elixir/lib/module/behaviour.ex
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,11 @@ defmodule Module.Behaviour do
end
end

def format_warning({:undefined_behaviour, module, behaviour}) do
def format_diagnostic(warning) do
%{message: IO.iodata_to_binary(format_warning(warning))}
end

defp format_warning({:undefined_behaviour, module, behaviour}) do
[
"@behaviour ",
inspect(behaviour),
Expand All @@ -294,12 +298,12 @@ defmodule Module.Behaviour do
]
end

def format_warning({:module_does_not_define_behaviour, module, behaviour}) do
defp format_warning({:module_does_not_define_behaviour, module, behaviour}) do
["module ", inspect(behaviour), " is not a behaviour (in module ", inspect(module), ")"]
end

def format_warning({:duplicate_behaviour, module, behaviour, conflict, kind, callback})
when conflict == behaviour do
defp format_warning({:duplicate_behaviour, module, behaviour, conflict, kind, callback})
when conflict == behaviour do
[
"the behaviour ",
inspect(behaviour),
Expand All @@ -311,7 +315,7 @@ defmodule Module.Behaviour do
]
end

def format_warning({:duplicate_behaviour, module, behaviour, conflict, kind, callback}) do
defp format_warning({:duplicate_behaviour, module, behaviour, conflict, kind, callback}) do
[
"conflicting behaviours found. Callback ",
format_definition(kind, callback),
Expand All @@ -325,7 +329,7 @@ defmodule Module.Behaviour do
]
end

def format_warning({:missing_callback, module, callback, kind, behaviour}) do
defp format_warning({:missing_callback, module, callback, kind, behaviour}) do
[
format_callback(callback, kind, behaviour),
" is not implemented (in module ",
Expand All @@ -334,7 +338,7 @@ defmodule Module.Behaviour do
]
end

def format_warning({:callback_mismatch, module, callback, kind, wrong_kind, behaviour}) do
defp format_warning({:callback_mismatch, module, callback, kind, wrong_kind, behaviour}) do
[
format_callback(callback, kind, behaviour),
" was implemented as \"",
Expand All @@ -347,14 +351,14 @@ defmodule Module.Behaviour do
]
end

def format_warning({:private_function, callback, kind}) do
defp format_warning({:private_function, callback, kind}) do
[
format_definition(kind, callback),
" is private, @impl attribute is always discarded for private functions/macros"
]
end

def format_warning({:no_behaviours, callback, kind, value}) do
defp format_warning({:no_behaviours, callback, kind, value}) do
[
"got \"@impl ",
inspect(value),
Expand All @@ -364,7 +368,7 @@ defmodule Module.Behaviour do
]
end

def format_warning({:impl_not_defined, callback, kind, {_fa, behaviour}}) do
defp format_warning({:impl_not_defined, callback, kind, {_fa, behaviour}}) do
[
"got \"@impl false\" for ",
format_definition(kind, callback),
Expand All @@ -373,7 +377,7 @@ defmodule Module.Behaviour do
]
end

def format_warning({:impl_defined, callback, kind, callbacks}) do
defp format_warning({:impl_defined, callback, kind, callbacks}) do
[
"got \"@impl true\" for ",
format_definition(kind, callback),
Expand All @@ -382,7 +386,7 @@ defmodule Module.Behaviour do
]
end

def format_warning({:behaviour_not_declared, callback, kind, behaviour}) do
defp format_warning({:behaviour_not_declared, callback, kind, behaviour}) do
[
"got \"@impl ",
inspect(behaviour),
Expand All @@ -392,7 +396,7 @@ defmodule Module.Behaviour do
]
end

def format_warning({:behaviour_not_defined, callback, kind, behaviour, callbacks}) do
defp format_warning({:behaviour_not_defined, callback, kind, behaviour, callbacks}) do
[
"got \"@impl ",
inspect(behaviour),
Expand All @@ -403,7 +407,7 @@ defmodule Module.Behaviour do
]
end

def format_warning({:missing_impl, callback, kind, behaviour}) do
defp format_warning({:missing_impl, callback, kind, behaviour}) do
[
"module attribute @impl was not set for ",
format_definition(kind, callback),
Expand Down
22 changes: 11 additions & 11 deletions lib/elixir/lib/module/parallel_checker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ defmodule Module.ParallelChecker do
definitions
)

warnings =
diagnostics =
module
|> Module.Types.warnings(file, definitions, no_warn_undefined, cache)
|> Kernel.++(behaviour_warnings)
Expand All @@ -270,7 +270,7 @@ defmodule Module.ParallelChecker do
|> Map.get(:after_verify, [])
|> Enum.each(fn {verify_mod, verify_fun} -> apply(verify_mod, verify_fun, [module]) end)

warnings
diagnostics
end

defp extract_no_warn_undefined(compile_opts) do
Expand Down Expand Up @@ -302,31 +302,31 @@ defmodule Module.ParallelChecker do

defp emit_warnings(warnings, log?) do
Enum.flat_map(warnings, fn {module, warning, locations} ->
message = module.format_warning(warning)
diagnostics = Enum.map(locations, &to_diagnostic(message, &1))
log? and print_warning(message, diagnostics)
%{message: _} = diagnostic = module.format_diagnostic(warning)
diagnostics = Enum.map(locations, &to_diagnostic(diagnostic, &1))
log? and print_diagnostics(diagnostics)
diagnostics
end)
end

defp print_warning(message, [diagnostic]) do
:elixir_errors.print_warning(message, diagnostic)
defp print_diagnostics([diagnostic]) do
:elixir_errors.print_diagnostic(diagnostic, true)
end

defp print_warning(message, grouped_warnings) do
:elixir_errors.print_warning_group(message, grouped_warnings)
defp print_diagnostics(diagnostics) do
:elixir_errors.print_diagnostics(diagnostics)
end

defp to_diagnostic(message, {file, position, mfa}) when is_list(position) do
defp to_diagnostic(diagnostic, {file, position, mfa}) when is_list(position) do
%{
severity: :warning,
source: file,
file: file,
position: position_to_tuple(position),
message: IO.iodata_to_binary(message),
stacktrace: [to_stacktrace(file, position, mfa)],
span: nil
}
|> Map.merge(diagnostic)
end

defp position_to_tuple(position) do
Expand Down
37 changes: 19 additions & 18 deletions lib/elixir/lib/module/types/expr.ex
Original file line number Diff line number Diff line change
Expand Up @@ -399,11 +399,7 @@ defmodule Module.Types.Expr do
expected = if structs == [], do: @exception, else: Enum.reduce(structs, &union/2)

formatter = fn expr ->
[
"rescue #{expr_to_string(expr)} ->" |> indent(4),
?\n,
format_hints(hints)
]
{"rescue #{expr_to_string(expr)} ->", hints}
end

{:ok, _type, context} = Of.refine_var(var, expected, expr, formatter, stack, context)
Expand Down Expand Up @@ -537,23 +533,28 @@ defmodule Module.Types.Expr do

## Warning formatting

def format_warning({:badupdate, type, expr, expected_type, actual_type, context}) do
[
"""
incompatible types in #{type} update:
def format_diagnostic({:badupdate, type, expr, expected_type, actual_type, context}) do
traces = Of.collect_traces(expr, context)

#{expr_to_string(expr) |> indent(4)}
%{
detail: %{typing_traces: traces},
message:
IO.iodata_to_binary([
"""
incompatible types in #{type} update:
expected type:
#{expr_to_string(expr) |> indent(4)}
#{to_quoted_string(expected_type) |> indent(4)}
expected type:
but got type:
#{to_quoted_string(expected_type) |> indent(4)}
#{to_quoted_string(actual_type) |> indent(4)}
""",
Of.format_traces(expr, context),
"\ntyping violation found at:"
]
but got type:
#{to_quoted_string(actual_type) |> indent(4)}
""",
Of.format_traces(traces)
])
}
end
end
Loading

0 comments on commit 7a4060c

Please sign in to comment.