BE: track related info in declarative tests
Some checks are pending
ELP CI / edb (push) Waiting to run
ELP CI / ci (26, 26.2.5.13, linux, 26.2, ubuntu-22.04, ubuntu-22.04-x64, x86_64-unknown-linux-gnu, true, linux-x64) (push) Blocked by required conditions
ELP CI / ci (26, 26.2.5.13, linux, 26.2, ubuntu-22.04-arm, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, true, linux-arm64) (push) Blocked by required conditions
ELP CI / ci (26, 26.2.5.13, macos, 26.2, macos-13, macos-13-x64, x86_64-apple-darwin, true, darwin-x64) (push) Blocked by required conditions
ELP CI / ci (26, 26.2.5.13, macos, 26.2, macos-latest, macos-latest-arm, aarch64-apple-darwin, true, darwin-arm64) (push) Blocked by required conditions
ELP CI / ci (26, 26.2.5.13, windows, 26.2, windows-2022, windows-2022-x64, x86_64-pc-windows-msvc, true, win32-x64) (push) Blocked by required conditions
ELP CI / ci (27, 27.3.4, linux, 27.3, ubuntu-22.04, ubuntu-22.04-x64, x86_64-unknown-linux-gnu, false, linux-x64) (push) Blocked by required conditions
ELP CI / ci (27, 27.3.4, linux, 27.3, ubuntu-22.04-arm, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, false, linux-arm64) (push) Blocked by required conditions
ELP CI / ci (27, 27.3.4, macos, 27.3, macos-13, macos-13-x64, x86_64-apple-darwin, false, darwin-x64) (push) Blocked by required conditions
ELP CI / ci (27, 27.3.4, macos, 27.3, macos-latest, macos-latest-arm, aarch64-apple-darwin, false, darwin-arm64) (push) Blocked by required conditions
ELP CI / ci (27, 27.3.4, windows, 27.3, windows-2022, windows-2022-x64, x86_64-pc-windows-msvc, false, win32-x64) (push) Blocked by required conditions
ELP CI / ci (28, 28.0.1, linux, 28, ubuntu-22.04, ubuntu-22.04-x64, x86_64-unknown-linux-gnu, false, linux-x64) (push) Blocked by required conditions
ELP CI / ci (28, 28.0.1, linux, 28, ubuntu-22.04-arm, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, false, linux-arm64) (push) Blocked by required conditions
ELP CI / ci (28, 28.0.1, macos, 28, macos-13, macos-13-x64, x86_64-apple-darwin, false, darwin-x64) (push) Blocked by required conditions
ELP CI / ci (28, 28.0.1, macos, 28, macos-latest, macos-latest-arm, aarch64-apple-darwin, false, darwin-arm64) (push) Blocked by required conditions
ELP CI / ci (28, 28.0.1, windows, 28, windows-2022, windows-2022-x64, x86_64-pc-windows-msvc, false, win32-x64) (push) Blocked by required conditions
Deploy Website to GitHub Pages / Deploy Website to GitHub Pages (push) Waiting to run

Summary:
Our declarative tests allow us to add annotations for expected diagnostics, so we can clearly see and check what is generated.

But at present there is no way to check any related information for a diagnostic.

This diff adds that capability, showing related information together with the FileId and range each refers to.

Reviewed By: robertoaloi

Differential Revision: D87316965

fbshipit-source-id: ea7db2980b11950c3552befec7b666665ceef84b
This commit is contained in:
Alan Zimmerman 2025-11-19 03:56:10 -08:00 committed by meta-codesync[bot]
parent dcf4e3a4ff
commit 8efb4fe29c
7 changed files with 54 additions and 2 deletions

View file

@ -168,6 +168,12 @@ directly in test code:
- **Left-margin annotation**: `%%<^^^ text` - Annotation starts at `%%` position
instead of first `^`
- **Multiline annotations**: Use continuation lines with `%% | next line`
- Continuation lines are particularly useful for diagnostics with related information:
```erlang
foo() -> syntax error oops.
%% ^^^^^ error: P1711: syntax error before: error
%% | Related info: 0:45-50 function foo/0 undefined
```
#### Example Test Fixture

View file

@ -3433,6 +3433,9 @@ baz(1)->4.
-spec foo() -> ok.
foo( -> ok. %%
%% ^ error: W0004: Missing ')'
%% | Related info: 0:21-43 function foo/0 undefined
%% | Related info: 0:74-79 function foo/0 undefined
%% | Related info: 0:82-99 spec for undefined function foo/0
"#,
);
}
@ -3463,6 +3466,7 @@ baz(1)->4.
foo() -> syntax error oops.
%% ^^^^^ error: P1711: syntax error before: error
%% | Related info: 0:25-30 function foo/0 undefined
"#,
);
}
@ -3766,6 +3770,7 @@ baz(1)->4.
\~"\"\\µA\"" = \~/"\\µA"/
X = 3.
%% ^ error: P1711: syntax error before: X
%% | Related info: 0:32-37 function foo/0 undefined
"#,
);
}

View file

@ -396,6 +396,7 @@ mod tests {
foo(0) -> 1;
boo(1) -> 2.
%% ^^^ 💡 error: P1700: head mismatch 'boo' vs 'foo'
%% | Related info: 0:21-24 Mismatched clause name
"#,
);
check_fix(
@ -421,6 +422,7 @@ mod tests {
ok;
fooX(_X) ->
%% ^^^^ 💡 error: P1700: head mismatch 'fooX' vs 'food'
%% | Related info: 0:21-25 Mismatched clause name
no.
bar() ->
@ -450,6 +452,7 @@ mod tests {
-module(main).
foo(0) -> 1;
%% ^^^ 💡 error: P1700: head mismatch 'foo' vs 'boo'
%% | Related info: 0:37-40 Mismatched clause name
boo(1) -> 2;
boo(2) -> 3.
"#,
@ -478,6 +481,7 @@ mod tests {
foo(0) -> 1;
foo(1,0) -> 2.
%% ^^^^^^^^^^^^^ error: P1700: head arity mismatch 2 vs 1
%% | Related info: 0:21-32 Mismatched clause
"#,
);
}
@ -490,6 +494,7 @@ mod tests {
foo(2,0) -> 3;
foo(0) -> 1;
%% ^^^^^^^^^^^ error: P1700: head arity mismatch 1 vs 2
%% | Related info: 0:21-34 Mismatched clause
foo(1,0) -> 2.
"#,
);
@ -516,6 +521,7 @@ mod tests {
(0) -> ok;
A(N) -> ok
%% ^ 💡 error: P1700: head mismatch 'A' vs ''
%% | Related info: 0:44-53 Mismatched clause name
end,
F().
"#,

View file

@ -164,6 +164,7 @@ mod tests {
-module(main).
-dyalizer({nowarn_function, f/0}).
%%% ^^^^^^^^ 💡 error: W0013: misspelled attribute, saw 'dyalizer' but expected 'dialyzer'
%%% | Related info: 0:22-30 Misspelled attribute
"#,
);
check_fix(
@ -224,6 +225,7 @@ mod tests {
-module(main).
-module_doc """
%%% ^^^^^^^^^^ 💡 error: W0013: misspelled attribute, saw 'module_doc' but expected 'moduledoc'
%%% | Related info: 0:24-34 Misspelled attribute
Hola
""".
"#,
@ -237,6 +239,7 @@ mod tests {
-module(main).
-docs """
%%% ^^^^ 💡 error: W0013: misspelled attribute, saw 'docs' but expected 'doc'
%%% | Related info: 0:24-28 Misspelled attribute
Hola
""".
foo() -> ok.

View file

@ -581,6 +581,7 @@ foo() -> ok.
%% The following shows up as a wild attribute, which we regard as being used.
-defin e(X, 1).
%% ^^^^^ 💡 error: W0013: misspelled attribute, saw 'defin' but expected 'define'
%% | Related info: 1:82-87 Misspelled attribute
-def ine(Y, 2).
"#,

View file

@ -401,6 +401,23 @@ fn convert_diagnostics_to_annotations(diagnostics: Vec<Diagnostic>) -> Vec<(Text
annotation.push_str(&d.code.as_code());
annotation.push_str(": ");
annotation.push_str(&convert_diagnostic_message(&d));
// Append related info to the annotation
if let Some(related_info) = &d.related_info {
// Sort related info alphabetically by message for consistent test output
let mut sorted_info = related_info.clone();
sorted_info.sort_by(|a, b| a.message.cmp(&b.message));
for info in sorted_info {
annotation.push_str(&format!(
"\nRelated info: {}:{}-{} {}",
info.file_id.index(),
u32::from(info.range.start()),
u32::from(info.range.end()),
info.message
));
}
}
(d.range, annotation)
})
.collect::<Vec<_>>();

View file

@ -472,6 +472,14 @@ pub fn extract_tags(mut text: &str, tag: &str) -> (Vec<(TextRange, Option<String
/// %% | second line
/// ```
///
/// This is useful for representing diagnostics with related information:
///
/// ```not_rust
/// foo() -> syntax error oops.
/// %% ^^^^^ error: P1711: syntax error before: error
/// %% | Related info: 0:25-30 function foo/0 undefined
/// ```
///
/// Annotations point to the last line that actually was long enough for the
/// range, not counting annotations themselves. So overlapping annotations are
/// possible:
@ -551,10 +559,16 @@ pub fn extract_annotations(text: &str) -> (Vec<(TextRange, String)>, String) {
if !res.is_empty() {
offset += annotation_offset;
this_line_annotations.push((offset, res.len() - 1));
let &(_, idx) = prev_line_annotations
// Try to find a previous annotation at the same offset
let idx = if let Some(&(_, idx)) = prev_line_annotations
.iter()
.find(|&&(off, _idx)| off == offset)
.unwrap();
{
idx
} else {
// If no exact offset match, append to the most recent annotation
res.len() - 1
};
res[idx].1.push('\n');
res[idx].1.push_str(&content);
}