From 9b41effd076b6d845a6ac2c31af431c83af4ed42 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 30 Mar 2021 19:29:26 +0200 Subject: Improve rustc diagnostic mapping --- .../test_data/handles_macro_location.txt | 118 +++++++++++++++++- .../diagnostics/test_data/macro_compiler_error.txt | 138 +++++++++++++++++++-- crates/rust-analyzer/src/diagnostics/to_proto.rs | 65 ++++------ 3 files changed, 272 insertions(+), 49 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt index e5f01fb33..206d89cfa 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt @@ -21,6 +21,94 @@ character: 26, }, }, + severity: Some( + Hint, + ), + code: Some( + String( + "E0277", + ), + ), + code_description: Some( + CodeDescription { + href: Url { + scheme: "https", + username: "", + password: None, + host: Some( + Domain( + "doc.rust-lang.org", + ), + ), + port: None, + path: "/error-index.html", + query: None, + fragment: Some( + "E0277", + ), + }, + }, + ), + source: Some( + "rustc", + ), + message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", + related_information: Some( + [ + DiagnosticRelatedInformation { + location: Location { + uri: Url { + scheme: "file", + username: "", + password: None, + host: None, + port: None, + path: "/test/%3C::core::macros::assert_eq%20macros%3E", + query: None, + fragment: None, + }, + range: Range { + start: Position { + line: 6, + character: 30, + }, + end: Position { + line: 6, + character: 32, + }, + }, + }, + message: "Exact error occurred here", + }, + ], + ), + tags: None, + data: None, + }, + fixes: [], + }, + MappedRustDiagnostic { + url: Url { + scheme: "file", + username: "", + password: None, + host: None, + port: None, + path: "/test/%3C::core::macros::assert_eq%20macros%3E", + query: None, + fragment: None, + }, + diagnostic: Diagnostic { + range: Range { + start: Position { + line: 6, + character: 30, + }, + end: Position { + line: 6, + character: 32, + }, + }, severity: Some( Error, ), @@ -53,7 +141,35 @@ "rustc", ), message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", - related_information: None, + related_information: Some( + [ + DiagnosticRelatedInformation { + location: Location { + uri: Url { + scheme: "file", + username: "", + password: None, + host: None, + port: None, + path: "/test/src/main.rs", + query: None, + fragment: None, + }, + range: Range { + start: Position { + line: 1, + character: 4, + }, + end: Position { + line: 1, + character: 26, + }, + }, + }, + message: "Error originated from macro call here", + }, + ], + ), tags: None, data: None, }, diff --git a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt index f999848a7..c847bbb35 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt @@ -13,16 +13,16 @@ diagnostic: Diagnostic { range: Range { start: Position { - line: 264, + line: 271, character: 8, }, end: Position { - line: 264, - character: 76, + line: 271, + character: 50, }, }, severity: Some( - Error, + Hint, ), code: None, code_description: None, @@ -40,18 +40,18 @@ password: None, host: None, port: None, - path: "/test/crates/hir_def/src/data.rs", + path: "/test/crates/hir_def/src/path.rs", query: None, fragment: None, }, range: Range { start: Position { - line: 79, - character: 15, + line: 264, + character: 8, }, end: Position { - line: 79, - character: 41, + line: 264, + character: 76, }, }, }, @@ -86,6 +86,71 @@ character: 41, }, }, + severity: Some( + Hint, + ), + code: None, + code_description: None, + source: Some( + "rustc", + ), + message: "Please register your known path in the path module", + related_information: Some( + [ + DiagnosticRelatedInformation { + location: Location { + uri: Url { + scheme: "file", + username: "", + password: None, + host: None, + port: None, + path: "/test/crates/hir_def/src/path.rs", + query: None, + fragment: None, + }, + range: Range { + start: Position { + line: 264, + character: 8, + }, + end: Position { + line: 264, + character: 76, + }, + }, + }, + message: "Exact error occurred here", + }, + ], + ), + tags: None, + data: None, + }, + fixes: [], + }, + MappedRustDiagnostic { + url: Url { + scheme: "file", + username: "", + password: None, + host: None, + port: None, + path: "/test/crates/hir_def/src/path.rs", + query: None, + fragment: None, + }, + diagnostic: Diagnostic { + range: Range { + start: Position { + line: 264, + character: 8, + }, + end: Position { + line: 264, + character: 76, + }, + }, severity: Some( Error, ), @@ -95,7 +160,60 @@ "rustc", ), message: "Please register your known path in the path module", - related_information: None, + related_information: Some( + [ + DiagnosticRelatedInformation { + location: Location { + uri: Url { + scheme: "file", + username: "", + password: None, + host: None, + port: None, + path: "/test/crates/hir_def/src/path.rs", + query: None, + fragment: None, + }, + range: Range { + start: Position { + line: 271, + character: 8, + }, + end: Position { + line: 271, + character: 50, + }, + }, + }, + message: "Error originated from macro call here", + }, + DiagnosticRelatedInformation { + location: Location { + uri: Url { + scheme: "file", + username: "", + password: None, + host: None, + port: None, + path: "/test/crates/hir_def/src/data.rs", + query: None, + fragment: None, + }, + range: Range { + start: Position { + line: 79, + character: 15, + }, + end: Position { + line: 79, + character: 41, + }, + }, + }, + message: "Error originated from macro call here", + }, + ], + ), tags: None, data: None, }, diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 76994de71..5de481021 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -34,22 +34,8 @@ fn diagnostic_severity( Some(res) } -/// Check whether a file name is from macro invocation -fn is_from_macro(file_name: &str) -> bool { - file_name.starts_with('<') && file_name.ends_with('>') -} - -/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary -fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { - let mut span = span.clone(); - while let Some(expansion) = span.expansion { - span = expansion.span; - } - return location_naive(workspace_root, &span); -} - /// Converts a Rust span to a LSP location -fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { +fn convert_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { let file_name = workspace_root.join(&span.file_name); let uri = url_from_abs_path(&file_name); @@ -62,7 +48,7 @@ fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Lo lsp_types::Location { uri, range } } -/// Converts a secondary Rust span to a LSP related inflocation(ormation +/// Converts a secondary Rust span to a LSP related information /// /// If the span is unlabelled this will return `None`. fn diagnostic_related_information( @@ -70,7 +56,7 @@ fn diagnostic_related_information( span: &DiagnosticSpan, ) -> Option { let message = span.label.clone()?; - let location = location(workspace_root, span); + let location = convert_location(workspace_root, span); Some(lsp_types::DiagnosticRelatedInformation { location, message }) } @@ -98,7 +84,7 @@ fn map_rust_child_diagnostic( let mut edit_map: HashMap> = HashMap::new(); for &span in &spans { if let Some(suggested_replacement) = &span.suggested_replacement { - let location = location(workspace_root, span); + let location = convert_location(workspace_root, span); let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone()); edit_map.entry(location.uri).or_default().push(edit); } @@ -107,7 +93,7 @@ fn map_rust_child_diagnostic( if edit_map.is_empty() { MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { related: lsp_types::DiagnosticRelatedInformation { - location: location(workspace_root, spans[0]), + location: convert_location(workspace_root, spans[0]), message: rd.message.clone(), }, suggested_fix: None, @@ -115,7 +101,7 @@ fn map_rust_child_diagnostic( } else { MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { related: lsp_types::DiagnosticRelatedInformation { - location: location(workspace_root, spans[0]), + location: convert_location(workspace_root, spans[0]), message: rd.message.clone(), }, suggested_fix: Some(lsp_ext::CodeAction { @@ -231,7 +217,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( primary_spans .iter() .flat_map(|primary_span| { - let location = location(workspace_root, &primary_span); + let location = convert_location(workspace_root, &primary_span); let mut message = message.clone(); if needs_primary_span_label { @@ -243,21 +229,22 @@ pub(crate) fn map_rust_diagnostic_to_lsp( // Each primary diagnostic span may result in multiple LSP diagnostics. let mut diagnostics = Vec::new(); - let mut related_macro_info = None; + let mut related_info_macro_calls = vec![]; // If error occurs from macro expansion, add related info pointing to // where the error originated // Also, we would generate an additional diagnostic, so that exact place of macro // will be highlighted in the error origin place. - if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { - let in_macro_location = location_naive(workspace_root, &primary_span); - - // Add related information for the main disagnostic. - related_macro_info = Some(lsp_types::DiagnosticRelatedInformation { + let macro_calls = std::iter::successors(Some(*primary_span), |span| { + Some(&span.expansion.as_ref()?.span) + }) + .skip(1); + for macro_span in macro_calls { + let in_macro_location = convert_location(workspace_root, ¯o_span); + related_info_macro_calls.push(lsp_types::DiagnosticRelatedInformation { location: in_macro_location.clone(), - message: "Error originated from macro here".to_string(), + message: "Error originated from macro call here".to_string(), }); - // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. let information_for_additional_diagnostic = vec![lsp_types::DiagnosticRelatedInformation { @@ -267,7 +254,8 @@ pub(crate) fn map_rust_diagnostic_to_lsp( let diagnostic = lsp_types::Diagnostic { range: in_macro_location.range, - severity, + // downgrade to hint if we're pointing at the macro + severity: Some(lsp_types::DiagnosticSeverity::Hint), code: code.clone().map(lsp_types::NumberOrString::String), code_description: code_description.clone(), source: Some(source.clone()), @@ -276,7 +264,6 @@ pub(crate) fn map_rust_diagnostic_to_lsp( tags: if tags.is_empty() { None } else { Some(tags.clone()) }, data: None, }; - diagnostics.push(MappedRustDiagnostic { url: in_macro_location.uri, diagnostic, @@ -294,15 +281,17 @@ pub(crate) fn map_rust_diagnostic_to_lsp( code_description: code_description.clone(), source: Some(source.clone()), message, - related_information: if subdiagnostics.is_empty() { - None - } else { - let mut related = subdiagnostics + related_information: { + let info = related_info_macro_calls .iter() - .map(|sub| sub.related.clone()) + .cloned() + .chain(subdiagnostics.iter().map(|sub| sub.related.clone())) .collect::>(); - related.extend(related_macro_info); - Some(related) + if info.is_empty() { + None + } else { + Some(info) + } }, tags: if tags.is_empty() { None } else { Some(tags.clone()) }, data: None, -- cgit v1.2.3 From 7723dd090913de991ea258bfd5b3f6c428591235 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 31 Mar 2021 17:18:21 +0200 Subject: Rename `convert_location` -> `location` --- crates/rust-analyzer/src/diagnostics/to_proto.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 5de481021..c632630d1 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -35,7 +35,7 @@ fn diagnostic_severity( } /// Converts a Rust span to a LSP location -fn convert_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { +fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { let file_name = workspace_root.join(&span.file_name); let uri = url_from_abs_path(&file_name); @@ -56,7 +56,7 @@ fn diagnostic_related_information( span: &DiagnosticSpan, ) -> Option { let message = span.label.clone()?; - let location = convert_location(workspace_root, span); + let location = location(workspace_root, span); Some(lsp_types::DiagnosticRelatedInformation { location, message }) } @@ -84,7 +84,7 @@ fn map_rust_child_diagnostic( let mut edit_map: HashMap> = HashMap::new(); for &span in &spans { if let Some(suggested_replacement) = &span.suggested_replacement { - let location = convert_location(workspace_root, span); + let location = location(workspace_root, span); let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone()); edit_map.entry(location.uri).or_default().push(edit); } @@ -93,7 +93,7 @@ fn map_rust_child_diagnostic( if edit_map.is_empty() { MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { related: lsp_types::DiagnosticRelatedInformation { - location: convert_location(workspace_root, spans[0]), + location: location(workspace_root, spans[0]), message: rd.message.clone(), }, suggested_fix: None, @@ -101,7 +101,7 @@ fn map_rust_child_diagnostic( } else { MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { related: lsp_types::DiagnosticRelatedInformation { - location: convert_location(workspace_root, spans[0]), + location: location(workspace_root, spans[0]), message: rd.message.clone(), }, suggested_fix: Some(lsp_ext::CodeAction { @@ -217,7 +217,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( primary_spans .iter() .flat_map(|primary_span| { - let location = convert_location(workspace_root, &primary_span); + let primary_location = location(workspace_root, &primary_span); let mut message = message.clone(); if needs_primary_span_label { @@ -240,7 +240,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( }) .skip(1); for macro_span in macro_calls { - let in_macro_location = convert_location(workspace_root, ¯o_span); + let in_macro_location = location(workspace_root, ¯o_span); related_info_macro_calls.push(lsp_types::DiagnosticRelatedInformation { location: in_macro_location.clone(), message: "Error originated from macro call here".to_string(), @@ -248,7 +248,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. let information_for_additional_diagnostic = vec![lsp_types::DiagnosticRelatedInformation { - location: location.clone(), + location: primary_location.clone(), message: "Exact error occurred here".to_string(), }]; @@ -273,9 +273,9 @@ pub(crate) fn map_rust_diagnostic_to_lsp( // Emit the primary diagnostic. diagnostics.push(MappedRustDiagnostic { - url: location.uri.clone(), + url: primary_location.uri.clone(), diagnostic: lsp_types::Diagnostic { - range: location.range, + range: primary_location.range, severity, code: code.clone().map(lsp_types::NumberOrString::String), code_description: code_description.clone(), @@ -303,7 +303,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( // This is useful because they will show up in the user's editor, unlike // `related_information`, which just produces hard-to-read links, at least in VS Code. let back_ref = lsp_types::DiagnosticRelatedInformation { - location, + location: primary_location, message: "original diagnostic".to_string(), }; for sub in &subdiagnostics { -- cgit v1.2.3 From 7c87e49638f6c4319cb9da3a59f87ca8e441c00d Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 31 Mar 2021 17:50:19 +0200 Subject: Search for a suitable primary location --- crates/rust-analyzer/src/diagnostics/to_proto.rs | 36 ++++++++++++++++++------ 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index c632630d1..89fe834f4 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -48,6 +48,22 @@ fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location lsp_types::Location { uri, range } } +/// Extracts a suitable "primary" location from a rustc diagnostic. +/// +/// This takes locations pointing into the standard library, or generally outside the current +/// workspace into account and tries to avoid those, in case macros are involved. +fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { + let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span)); + for span in span_stack { + let abs_path = workspace_root.join(&span.file_name); + if abs_path.starts_with(workspace_root) { + return location(workspace_root, span); + } + } + + location(workspace_root, span) +} + /// Converts a secondary Rust span to a LSP related information /// /// If the span is unlabelled this will return `None`. @@ -217,7 +233,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( primary_spans .iter() .flat_map(|primary_span| { - let primary_location = location(workspace_root, &primary_span); + let primary_location = primary_location(workspace_root, &primary_span); let mut message = message.clone(); if needs_primary_span_label { @@ -235,14 +251,16 @@ pub(crate) fn map_rust_diagnostic_to_lsp( // where the error originated // Also, we would generate an additional diagnostic, so that exact place of macro // will be highlighted in the error origin place. - let macro_calls = std::iter::successors(Some(*primary_span), |span| { + let span_stack = std::iter::successors(Some(*primary_span), |span| { Some(&span.expansion.as_ref()?.span) - }) - .skip(1); - for macro_span in macro_calls { - let in_macro_location = location(workspace_root, ¯o_span); + }); + for span in span_stack { + let secondary_location = location(workspace_root, &span); + if secondary_location == primary_location { + continue; + } related_info_macro_calls.push(lsp_types::DiagnosticRelatedInformation { - location: in_macro_location.clone(), + location: secondary_location.clone(), message: "Error originated from macro call here".to_string(), }); // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. @@ -253,7 +271,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( }]; let diagnostic = lsp_types::Diagnostic { - range: in_macro_location.range, + range: secondary_location.range, // downgrade to hint if we're pointing at the macro severity: Some(lsp_types::DiagnosticSeverity::Hint), code: code.clone().map(lsp_types::NumberOrString::String), @@ -265,7 +283,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( data: None, }; diagnostics.push(MappedRustDiagnostic { - url: in_macro_location.uri, + url: secondary_location.uri, diagnostic, fixes: Vec::new(), }); -- cgit v1.2.3 From 066330e95349f96a2481ec727bfe50f9dbca9951 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 1 Apr 2021 14:44:20 +0200 Subject: Adjust message when pointing at location in macro --- crates/rust-analyzer/src/diagnostics/to_proto.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 89fe834f4..8723c450c 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -254,14 +254,22 @@ pub(crate) fn map_rust_diagnostic_to_lsp( let span_stack = std::iter::successors(Some(*primary_span), |span| { Some(&span.expansion.as_ref()?.span) }); - for span in span_stack { + for (i, span) in span_stack.enumerate() { + // First span is the original diagnostic, others are macro call locations that + // generated that code. + let is_in_macro_call = i != 0; + let secondary_location = location(workspace_root, &span); if secondary_location == primary_location { continue; } related_info_macro_calls.push(lsp_types::DiagnosticRelatedInformation { location: secondary_location.clone(), - message: "Error originated from macro call here".to_string(), + message: if is_in_macro_call { + "Error originated from macro call here".to_string() + } else { + "Actual error occurred here".to_string() + }, }); // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. let information_for_additional_diagnostic = -- cgit v1.2.3 From 608a4653a3be3a35afce900e13d8b001fe4c5356 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 1 Apr 2021 15:51:02 +0200 Subject: Check for and skip dummy macro files --- .../test_data/handles_macro_location.txt | 118 +-------------------- crates/rust-analyzer/src/diagnostics/to_proto.rs | 18 +++- 2 files changed, 16 insertions(+), 120 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt index 206d89cfa..e5f01fb33 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt @@ -21,94 +21,6 @@ character: 26, }, }, - severity: Some( - Hint, - ), - code: Some( - String( - "E0277", - ), - ), - code_description: Some( - CodeDescription { - href: Url { - scheme: "https", - username: "", - password: None, - host: Some( - Domain( - "doc.rust-lang.org", - ), - ), - port: None, - path: "/error-index.html", - query: None, - fragment: Some( - "E0277", - ), - }, - }, - ), - source: Some( - "rustc", - ), - message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", - related_information: Some( - [ - DiagnosticRelatedInformation { - location: Location { - uri: Url { - scheme: "file", - username: "", - password: None, - host: None, - port: None, - path: "/test/%3C::core::macros::assert_eq%20macros%3E", - query: None, - fragment: None, - }, - range: Range { - start: Position { - line: 6, - character: 30, - }, - end: Position { - line: 6, - character: 32, - }, - }, - }, - message: "Exact error occurred here", - }, - ], - ), - tags: None, - data: None, - }, - fixes: [], - }, - MappedRustDiagnostic { - url: Url { - scheme: "file", - username: "", - password: None, - host: None, - port: None, - path: "/test/%3C::core::macros::assert_eq%20macros%3E", - query: None, - fragment: None, - }, - diagnostic: Diagnostic { - range: Range { - start: Position { - line: 6, - character: 30, - }, - end: Position { - line: 6, - character: 32, - }, - }, severity: Some( Error, ), @@ -141,35 +53,7 @@ "rustc", ), message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", - related_information: Some( - [ - DiagnosticRelatedInformation { - location: Location { - uri: Url { - scheme: "file", - username: "", - password: None, - host: None, - port: None, - path: "/test/src/main.rs", - query: None, - fragment: None, - }, - range: Range { - start: Position { - line: 1, - character: 4, - }, - end: Position { - line: 1, - character: 26, - }, - }, - }, - message: "Error originated from macro call here", - }, - ], - ), + related_information: None, tags: None, data: None, }, diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 8723c450c..e2f319f6b 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -34,6 +34,12 @@ fn diagnostic_severity( Some(res) } +/// Checks whether a file name is from macro invocation and does not refer to an actual file. +fn is_dummy_macro_file(file_name: &str) -> bool { + // FIXME: current rustc does not seem to emit `` files anymore? + file_name.starts_with('<') && file_name.ends_with('>') +} + /// Converts a Rust span to a LSP location fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { let file_name = workspace_root.join(&span.file_name); @@ -54,14 +60,16 @@ fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location /// workspace into account and tries to avoid those, in case macros are involved. fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span)); - for span in span_stack { + for span in span_stack.clone() { let abs_path = workspace_root.join(&span.file_name); - if abs_path.starts_with(workspace_root) { + if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) { return location(workspace_root, span); } } - location(workspace_root, span) + // Fall back to the outermost macro invocation if no suitable span comes up. + let last_span = span_stack.last().unwrap(); + location(workspace_root, last_span) } /// Converts a secondary Rust span to a LSP related information @@ -255,6 +263,10 @@ pub(crate) fn map_rust_diagnostic_to_lsp( Some(&span.expansion.as_ref()?.span) }); for (i, span) in span_stack.enumerate() { + if is_dummy_macro_file(&span.file_name) { + continue; + } + // First span is the original diagnostic, others are macro call locations that // generated that code. let is_in_macro_call = i != 0; -- cgit v1.2.3