diff --git a/.llms/rules/elp_development.md b/.llms/rules/elp_development.md index 0a024728a1..efd132ac4b 100644 --- a/.llms/rules/elp_development.md +++ b/.llms/rules/elp_development.md @@ -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 diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 4f8a9f8732..5e25b9d385 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs @@ -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 "#, ); } diff --git a/crates/ide/src/diagnostics/head_mismatch.rs b/crates/ide/src/diagnostics/head_mismatch.rs index b548b84dff..3a92d6f931 100644 --- a/crates/ide/src/diagnostics/head_mismatch.rs +++ b/crates/ide/src/diagnostics/head_mismatch.rs @@ -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(). "#, diff --git a/crates/ide/src/diagnostics/misspelled_attribute.rs b/crates/ide/src/diagnostics/misspelled_attribute.rs index 9dc5868879..6ba95c8ef9 100644 --- a/crates/ide/src/diagnostics/misspelled_attribute.rs +++ b/crates/ide/src/diagnostics/misspelled_attribute.rs @@ -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. diff --git a/crates/ide/src/diagnostics/unused_include.rs b/crates/ide/src/diagnostics/unused_include.rs index 3a91377338..959181a328 100644 --- a/crates/ide/src/diagnostics/unused_include.rs +++ b/crates/ide/src/diagnostics/unused_include.rs @@ -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). "#, diff --git a/crates/ide/src/tests.rs b/crates/ide/src/tests.rs index 71125f5094..8d71b65dad 100644 --- a/crates/ide/src/tests.rs +++ b/crates/ide/src/tests.rs @@ -401,6 +401,23 @@ fn convert_diagnostics_to_annotations(diagnostics: Vec) -> 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::>(); diff --git a/crates/project_model/src/test_fixture.rs b/crates/project_model/src/test_fixture.rs index 769891190a..ac60d3b7c2 100644 --- a/crates/project_model/src/test_fixture.rs +++ b/crates/project_model/src/test_fixture.rs @@ -472,6 +472,14 @@ pub fn extract_tags(mut text: &str, tag: &str) -> (Vec<(TextRange, Option 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); }