aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs33
1 files changed, 30 insertions, 3 deletions
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index ca18997e4..8b01a7e5d 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -1,6 +1,9 @@
1//! This module provides the functionality needed to convert diagnostics from 1//! This module provides the functionality needed to convert diagnostics from
2//! `cargo check` json format to the LSP diagnostic format. 2//! `cargo check` json format to the LSP diagnostic format.
3use std::{collections::HashMap, path::Path}; 3use std::{
4 collections::HashMap,
5 path::{Path, PathBuf},
6};
4 7
5use flycheck::{DiagnosticLevel, DiagnosticSpan}; 8use flycheck::{DiagnosticLevel, DiagnosticSpan};
6use stdx::format_to; 9use stdx::format_to;
@@ -42,7 +45,7 @@ fn is_dummy_macro_file(file_name: &str) -> bool {
42 45
43/// Converts a Rust span to a LSP location 46/// Converts a Rust span to a LSP location
44fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { 47fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
45 let file_name = workspace_root.join(&span.file_name); 48 let file_name = resolve_path(workspace_root, &span.file_name);
46 let uri = url_from_abs_path(&file_name); 49 let uri = url_from_abs_path(&file_name);
47 50
48 // FIXME: this doesn't handle UTF16 offsets correctly 51 // FIXME: this doesn't handle UTF16 offsets correctly
@@ -61,7 +64,7 @@ fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location
61fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { 64fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
62 let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span)); 65 let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span));
63 for span in span_stack.clone() { 66 for span in span_stack.clone() {
64 let abs_path = workspace_root.join(&span.file_name); 67 let abs_path = resolve_path(workspace_root, &span.file_name);
65 if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) { 68 if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) {
66 return location(workspace_root, span); 69 return location(workspace_root, span);
67 } 70 }
@@ -84,6 +87,30 @@ fn diagnostic_related_information(
84 Some(lsp_types::DiagnosticRelatedInformation { location, message }) 87 Some(lsp_types::DiagnosticRelatedInformation { location, message })
85} 88}
86 89
90/// Resolves paths mimicking VSCode's behavior when `file_name` starts
91/// with the root directory component, which does not discard the base
92/// path. If this relative path exists, use it, otherwise fall back
93/// to the existing Rust behavior of path joining.
94fn resolve_path(workspace_root: &Path, file_name: &str) -> PathBuf {
95 let file_name = Path::new(file_name);
96
97 // Test path with VSCode's path join behavior.
98 let vscode_path = {
99 let mut result = PathBuf::from(workspace_root);
100 result.extend(file_name.components().skip_while(|component| match component {
101 std::path::Component::RootDir => true,
102 _ => false,
103 }));
104 result
105 };
106 if vscode_path.exists() {
107 return vscode_path;
108 }
109
110 // Default to Rust's path join behavior.
111 workspace_root.join(file_name)
112}
113
87struct SubDiagnostic { 114struct SubDiagnostic {
88 related: lsp_types::DiagnosticRelatedInformation, 115 related: lsp_types::DiagnosticRelatedInformation,
89 suggested_fix: Option<lsp_ext::CodeAction>, 116 suggested_fix: Option<lsp_ext::CodeAction>,