Skip to content

Commit 7a4060c

Browse files
committed
Add detail and span to typing diagnostics
Closes #13646.
1 parent d5095f6 commit 7a4060c

File tree

9 files changed

+380
-328
lines changed

9 files changed

+380
-328
lines changed

lib/elixir/lib/exception.ex

+3-3
Original file line numberDiff line numberDiff line change
@@ -1212,7 +1212,7 @@ defmodule SyntaxError do
12121212
})
12131213
when not is_nil(snippet) and not is_nil(column) do
12141214
snippet =
1215-
:elixir_errors.format_snippet({line, column}, file, description, snippet, :error, [], nil)
1215+
:elixir_errors.format_snippet(:error, {line, column}, file, description, snippet, %{})
12161216

12171217
format_message(file, line, column, snippet)
12181218
end
@@ -1225,7 +1225,7 @@ defmodule SyntaxError do
12251225
description: description
12261226
}) do
12271227
snippet =
1228-
:elixir_errors.format_snippet({line, column}, file, description, nil, :error, [], nil)
1228+
:elixir_errors.format_snippet(:error, {line, column}, file, description, nil, %{})
12291229

12301230
padded = " " <> String.replace(snippet, "\n", "\n ")
12311231
format_message(file, line, column, padded)
@@ -1316,7 +1316,7 @@ defmodule TokenMissingError do
13161316
description: description
13171317
}) do
13181318
snippet =
1319-
:elixir_errors.format_snippet({line, column}, file, description, snippet, :error, [], nil)
1319+
:elixir_errors.format_snippet(:error, {line, column}, file, description, snippet, %{})
13201320

13211321
format_message(file, line, column, snippet)
13221322
end

lib/elixir/lib/module/behaviour.ex

+18-14
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,11 @@ defmodule Module.Behaviour do
284284
end
285285
end
286286

287-
def format_warning({:undefined_behaviour, module, behaviour}) do
287+
def format_diagnostic(warning) do
288+
%{message: IO.iodata_to_binary(format_warning(warning))}
289+
end
290+
291+
defp format_warning({:undefined_behaviour, module, behaviour}) do
288292
[
289293
"@behaviour ",
290294
inspect(behaviour),
@@ -294,12 +298,12 @@ defmodule Module.Behaviour do
294298
]
295299
end
296300

297-
def format_warning({:module_does_not_define_behaviour, module, behaviour}) do
301+
defp format_warning({:module_does_not_define_behaviour, module, behaviour}) do
298302
["module ", inspect(behaviour), " is not a behaviour (in module ", inspect(module), ")"]
299303
end
300304

301-
def format_warning({:duplicate_behaviour, module, behaviour, conflict, kind, callback})
302-
when conflict == behaviour do
305+
defp format_warning({:duplicate_behaviour, module, behaviour, conflict, kind, callback})
306+
when conflict == behaviour do
303307
[
304308
"the behaviour ",
305309
inspect(behaviour),
@@ -311,7 +315,7 @@ defmodule Module.Behaviour do
311315
]
312316
end
313317

314-
def format_warning({:duplicate_behaviour, module, behaviour, conflict, kind, callback}) do
318+
defp format_warning({:duplicate_behaviour, module, behaviour, conflict, kind, callback}) do
315319
[
316320
"conflicting behaviours found. Callback ",
317321
format_definition(kind, callback),
@@ -325,7 +329,7 @@ defmodule Module.Behaviour do
325329
]
326330
end
327331

328-
def format_warning({:missing_callback, module, callback, kind, behaviour}) do
332+
defp format_warning({:missing_callback, module, callback, kind, behaviour}) do
329333
[
330334
format_callback(callback, kind, behaviour),
331335
" is not implemented (in module ",
@@ -334,7 +338,7 @@ defmodule Module.Behaviour do
334338
]
335339
end
336340

337-
def format_warning({:callback_mismatch, module, callback, kind, wrong_kind, behaviour}) do
341+
defp format_warning({:callback_mismatch, module, callback, kind, wrong_kind, behaviour}) do
338342
[
339343
format_callback(callback, kind, behaviour),
340344
" was implemented as \"",
@@ -347,14 +351,14 @@ defmodule Module.Behaviour do
347351
]
348352
end
349353

350-
def format_warning({:private_function, callback, kind}) do
354+
defp format_warning({:private_function, callback, kind}) do
351355
[
352356
format_definition(kind, callback),
353357
" is private, @impl attribute is always discarded for private functions/macros"
354358
]
355359
end
356360

357-
def format_warning({:no_behaviours, callback, kind, value}) do
361+
defp format_warning({:no_behaviours, callback, kind, value}) do
358362
[
359363
"got \"@impl ",
360364
inspect(value),
@@ -364,7 +368,7 @@ defmodule Module.Behaviour do
364368
]
365369
end
366370

367-
def format_warning({:impl_not_defined, callback, kind, {_fa, behaviour}}) do
371+
defp format_warning({:impl_not_defined, callback, kind, {_fa, behaviour}}) do
368372
[
369373
"got \"@impl false\" for ",
370374
format_definition(kind, callback),
@@ -373,7 +377,7 @@ defmodule Module.Behaviour do
373377
]
374378
end
375379

376-
def format_warning({:impl_defined, callback, kind, callbacks}) do
380+
defp format_warning({:impl_defined, callback, kind, callbacks}) do
377381
[
378382
"got \"@impl true\" for ",
379383
format_definition(kind, callback),
@@ -382,7 +386,7 @@ defmodule Module.Behaviour do
382386
]
383387
end
384388

385-
def format_warning({:behaviour_not_declared, callback, kind, behaviour}) do
389+
defp format_warning({:behaviour_not_declared, callback, kind, behaviour}) do
386390
[
387391
"got \"@impl ",
388392
inspect(behaviour),
@@ -392,7 +396,7 @@ defmodule Module.Behaviour do
392396
]
393397
end
394398

395-
def format_warning({:behaviour_not_defined, callback, kind, behaviour, callbacks}) do
399+
defp format_warning({:behaviour_not_defined, callback, kind, behaviour, callbacks}) do
396400
[
397401
"got \"@impl ",
398402
inspect(behaviour),
@@ -403,7 +407,7 @@ defmodule Module.Behaviour do
403407
]
404408
end
405409

406-
def format_warning({:missing_impl, callback, kind, behaviour}) do
410+
defp format_warning({:missing_impl, callback, kind, behaviour}) do
407411
[
408412
"module attribute @impl was not set for ",
409413
format_definition(kind, callback),

lib/elixir/lib/module/parallel_checker.ex

+11-11
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ defmodule Module.ParallelChecker do
259259
definitions
260260
)
261261

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

273-
warnings
273+
diagnostics
274274
end
275275

276276
defp extract_no_warn_undefined(compile_opts) do
@@ -302,31 +302,31 @@ defmodule Module.ParallelChecker do
302302

303303
defp emit_warnings(warnings, log?) do
304304
Enum.flat_map(warnings, fn {module, warning, locations} ->
305-
message = module.format_warning(warning)
306-
diagnostics = Enum.map(locations, &to_diagnostic(message, &1))
307-
log? and print_warning(message, diagnostics)
305+
%{message: _} = diagnostic = module.format_diagnostic(warning)
306+
diagnostics = Enum.map(locations, &to_diagnostic(diagnostic, &1))
307+
log? and print_diagnostics(diagnostics)
308308
diagnostics
309309
end)
310310
end
311311

312-
defp print_warning(message, [diagnostic]) do
313-
:elixir_errors.print_warning(message, diagnostic)
312+
defp print_diagnostics([diagnostic]) do
313+
:elixir_errors.print_diagnostic(diagnostic, true)
314314
end
315315

316-
defp print_warning(message, grouped_warnings) do
317-
:elixir_errors.print_warning_group(message, grouped_warnings)
316+
defp print_diagnostics(diagnostics) do
317+
:elixir_errors.print_diagnostics(diagnostics)
318318
end
319319

320-
defp to_diagnostic(message, {file, position, mfa}) when is_list(position) do
320+
defp to_diagnostic(diagnostic, {file, position, mfa}) when is_list(position) do
321321
%{
322322
severity: :warning,
323323
source: file,
324324
file: file,
325325
position: position_to_tuple(position),
326-
message: IO.iodata_to_binary(message),
327326
stacktrace: [to_stacktrace(file, position, mfa)],
328327
span: nil
329328
}
329+
|> Map.merge(diagnostic)
330330
end
331331

332332
defp position_to_tuple(position) do

lib/elixir/lib/module/types/expr.ex

+19-18
Original file line numberDiff line numberDiff line change
@@ -399,11 +399,7 @@ defmodule Module.Types.Expr do
399399
expected = if structs == [], do: @exception, else: Enum.reduce(structs, &union/2)
400400

401401
formatter = fn expr ->
402-
[
403-
"rescue #{expr_to_string(expr)} ->" |> indent(4),
404-
?\n,
405-
format_hints(hints)
406-
]
402+
{"rescue #{expr_to_string(expr)} ->", hints}
407403
end
408404

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

538534
## Warning formatting
539535

540-
def format_warning({:badupdate, type, expr, expected_type, actual_type, context}) do
541-
[
542-
"""
543-
incompatible types in #{type} update:
536+
def format_diagnostic({:badupdate, type, expr, expected_type, actual_type, context}) do
537+
traces = Of.collect_traces(expr, context)
544538

545-
#{expr_to_string(expr) |> indent(4)}
539+
%{
540+
detail: %{typing_traces: traces},
541+
message:
542+
IO.iodata_to_binary([
543+
"""
544+
incompatible types in #{type} update:
546545
547-
expected type:
546+
#{expr_to_string(expr) |> indent(4)}
548547
549-
#{to_quoted_string(expected_type) |> indent(4)}
548+
expected type:
550549
551-
but got type:
550+
#{to_quoted_string(expected_type) |> indent(4)}
552551
553-
#{to_quoted_string(actual_type) |> indent(4)}
554-
""",
555-
Of.format_traces(expr, context),
556-
"\ntyping violation found at:"
557-
]
552+
but got type:
553+
554+
#{to_quoted_string(actual_type) |> indent(4)}
555+
""",
556+
Of.format_traces(traces)
557+
])
558+
}
558559
end
559560
end

0 commit comments

Comments
 (0)