From 60841f42769b154864252b04d622494a9ddfc696 Mon Sep 17 00:00:00 2001 From: James Leitch Date: Mon, 19 Apr 2021 20:18:54 -0700 Subject: Diagnostic paths attempt to use VSCode's path join behavior before defaulting to Rust's path join behavior. --- crates/rust-analyzer/src/diagnostics/to_proto.rs | 33 +++++++++++++++++++++--- 1 file 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 @@ //! This module provides the functionality needed to convert diagnostics from //! `cargo check` json format to the LSP diagnostic format. -use std::{collections::HashMap, path::Path}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; use flycheck::{DiagnosticLevel, DiagnosticSpan}; use stdx::format_to; @@ -42,7 +45,7 @@ fn is_dummy_macro_file(file_name: &str) -> bool { /// 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); + let file_name = resolve_path(workspace_root, &span.file_name); let uri = url_from_abs_path(&file_name); // FIXME: this doesn't handle UTF16 offsets correctly @@ -61,7 +64,7 @@ fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location 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.clone() { - let abs_path = workspace_root.join(&span.file_name); + let abs_path = resolve_path(workspace_root, &span.file_name); if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) { return location(workspace_root, span); } @@ -84,6 +87,30 @@ fn diagnostic_related_information( Some(lsp_types::DiagnosticRelatedInformation { location, message }) } +/// Resolves paths mimicking VSCode's behavior when `file_name` starts +/// with the root directory component, which does not discard the base +/// path. If this relative path exists, use it, otherwise fall back +/// to the existing Rust behavior of path joining. +fn resolve_path(workspace_root: &Path, file_name: &str) -> PathBuf { + let file_name = Path::new(file_name); + + // Test path with VSCode's path join behavior. + let vscode_path = { + let mut result = PathBuf::from(workspace_root); + result.extend(file_name.components().skip_while(|component| match component { + std::path::Component::RootDir => true, + _ => false, + })); + result + }; + if vscode_path.exists() { + return vscode_path; + } + + // Default to Rust's path join behavior. + workspace_root.join(file_name) +} + struct SubDiagnostic { related: lsp_types::DiagnosticRelatedInformation, suggested_fix: Option, -- cgit v1.2.3 From 9fcad829807a0306fbf4eb2ebc1603a11a6df182 Mon Sep 17 00:00:00 2001 From: James Leitch Date: Tue, 20 Apr 2021 20:03:35 -0700 Subject: Diagnostic Remap Path Prefixes added. --- crates/rust-analyzer/src/config.rs | 9 +++- crates/rust-analyzer/src/diagnostics.rs | 1 + crates/rust-analyzer/src/diagnostics/to_proto.rs | 67 +++++++++++------------- docs/user/generated_config.adoc | 6 +++ editors/code/package.json | 7 ++- 5 files changed, 53 insertions(+), 37 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 7ddea22c8..3be335550 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -17,7 +17,7 @@ use ide_db::helpers::{ }; use lsp_types::{ClientCapabilities, MarkupKind}; use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use serde::{de::DeserializeOwned, Deserialize}; use vfs::AbsPathBuf; @@ -99,6 +99,9 @@ config_data! { diagnostics_enableExperimental: bool = "true", /// List of rust-analyzer diagnostics to disable. diagnostics_disabled: FxHashSet = "[]", + /// Map of path prefixes to be substituted when parsing diagnostic file paths. + /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. + diagnostics_remapPathPrefixes: FxHashMap = "{}", /// List of warnings that should be displayed with info severity. /// /// The warnings will be indicated by a blue squiggly underline in code @@ -474,6 +477,7 @@ impl Config { } pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { DiagnosticsMapConfig { + remap_path_prefixes: self.data.diagnostics_remapPathPrefixes.clone(), warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(), warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(), } @@ -835,6 +839,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "items": { "type": "string" }, "uniqueItems": true, }, + "FxHashMap" => set! { + "type": "object", + }, "Option" => set! { "type": ["null", "integer"], "minimum": 0, diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index f01548c50..776d21778 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs @@ -12,6 +12,7 @@ pub(crate) type CheckFixes = Arc>>; #[derive(Debug, Default, Clone)] pub struct DiagnosticsMapConfig { + pub remap_path_prefixes: FxHashMap, pub warnings_as_info: Vec, pub warnings_as_hint: Vec, } diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 8b01a7e5d..08303a781 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -44,8 +44,12 @@ fn is_dummy_macro_file(file_name: &str) -> bool { } /// Converts a Rust span to a LSP location -fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { - let file_name = resolve_path(workspace_root, &span.file_name); +fn location( + config: &DiagnosticsMapConfig, + workspace_root: &Path, + span: &DiagnosticSpan, +) -> lsp_types::Location { + let file_name = resolve_path(config, workspace_root, &span.file_name); let uri = url_from_abs_path(&file_name); // FIXME: this doesn't handle UTF16 offsets correctly @@ -61,54 +65,46 @@ fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location /// /// 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 { +fn primary_location( + config: &DiagnosticsMapConfig, + 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.clone() { - let abs_path = resolve_path(workspace_root, &span.file_name); + let abs_path = resolve_path(config, workspace_root, &span.file_name); if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) { - return location(workspace_root, span); + return location(config, 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) + location(config, workspace_root, last_span) } /// Converts a secondary Rust span to a LSP related information /// /// If the span is unlabelled this will return `None`. fn diagnostic_related_information( + config: &DiagnosticsMapConfig, workspace_root: &Path, span: &DiagnosticSpan, ) -> Option { let message = span.label.clone()?; - let location = location(workspace_root, span); + let location = location(config, workspace_root, span); Some(lsp_types::DiagnosticRelatedInformation { location, message }) } -/// Resolves paths mimicking VSCode's behavior when `file_name` starts -/// with the root directory component, which does not discard the base -/// path. If this relative path exists, use it, otherwise fall back -/// to the existing Rust behavior of path joining. -fn resolve_path(workspace_root: &Path, file_name: &str) -> PathBuf { - let file_name = Path::new(file_name); - - // Test path with VSCode's path join behavior. - let vscode_path = { - let mut result = PathBuf::from(workspace_root); - result.extend(file_name.components().skip_while(|component| match component { - std::path::Component::RootDir => true, - _ => false, - })); - result - }; - if vscode_path.exists() { - return vscode_path; +/// Resolves paths applying any matching path prefix remappings, and then +/// joining the path to the workspace root. +fn resolve_path(config: &DiagnosticsMapConfig, workspace_root: &Path, file_name: &str) -> PathBuf { + match config.remap_path_prefixes.iter().find(|(from, _)| file_name.starts_with(*from)) { + Some((from, to)) => { + workspace_root.join(format!("{}{}", to, file_name.strip_prefix(from).unwrap())) + } + None => workspace_root.join(file_name), } - - // Default to Rust's path join behavior. - workspace_root.join(file_name) } struct SubDiagnostic { @@ -122,6 +118,7 @@ enum MappedRustChildDiagnostic { } fn map_rust_child_diagnostic( + config: &DiagnosticsMapConfig, workspace_root: &Path, rd: &flycheck::Diagnostic, ) -> MappedRustChildDiagnostic { @@ -135,7 +132,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 = location(config, workspace_root, span); let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone()); edit_map.entry(location.uri).or_default().push(edit); } @@ -144,7 +141,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: location(config, workspace_root, spans[0]), message: rd.message.clone(), }, suggested_fix: None, @@ -152,7 +149,7 @@ fn map_rust_child_diagnostic( } else { MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { related: lsp_types::DiagnosticRelatedInformation { - location: location(workspace_root, spans[0]), + location: location(config, workspace_root, spans[0]), message: rd.message.clone(), }, suggested_fix: Some(lsp_ext::CodeAction { @@ -217,7 +214,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( let mut tags = Vec::new(); for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { - let related = diagnostic_related_information(workspace_root, secondary_span); + let related = diagnostic_related_information(config, workspace_root, secondary_span); if let Some(related) = related { subdiagnostics.push(SubDiagnostic { related, suggested_fix: None }); } @@ -225,7 +222,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( let mut message = rd.message.clone(); for child in &rd.children { - let child = map_rust_child_diagnostic(workspace_root, &child); + let child = map_rust_child_diagnostic(config, workspace_root, &child); match child { MappedRustChildDiagnostic::SubDiagnostic(sub) => { subdiagnostics.push(sub); @@ -269,7 +266,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( primary_spans .iter() .flat_map(|primary_span| { - let primary_location = primary_location(workspace_root, &primary_span); + let primary_location = primary_location(config, workspace_root, &primary_span); let mut message = message.clone(); if needs_primary_span_label { @@ -299,7 +296,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( // generated that code. let is_in_macro_call = i != 0; - let secondary_location = location(workspace_root, &span); + let secondary_location = location(config, workspace_root, &span); if secondary_location == primary_location { continue; } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index e0ee35b4e..cb6fe5850 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -147,6 +147,12 @@ have more false positives than usual. -- List of rust-analyzer diagnostics to disable. -- +[[rust-analyzer.diagnostics.remapPathPrefixes]]rust-analyzer.diagnostics.remapPathPrefixes (default: `{}`):: ++ +-- +Map of path prefixes to be substituted when parsing diagnostic file paths. +This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. +-- [[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index 06ed62d8d..dcb2e89d1 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -565,6 +565,11 @@ }, "uniqueItems": true }, + "rust-analyzer.diagnostics.remapPathPrefixes": { + "markdownDescription": "Map of path prefixes to be substituted when parsing diagnostic file paths.\nThis should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.", + "default": {}, + "type": "object" + }, "rust-analyzer.diagnostics.warningsAsHint": { "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.", "default": [], @@ -1195,4 +1200,4 @@ ] } } -} \ No newline at end of file +} -- cgit v1.2.3 From 72718bc2d7113874536b8bcd486aa5bd7dacafe6 Mon Sep 17 00:00:00 2001 From: James Leitch Date: Wed, 21 Apr 2021 15:09:37 -0700 Subject: Code review feedback. --- crates/rust-analyzer/src/config.rs | 6 +++--- crates/rust-analyzer/src/diagnostics.rs | 2 +- crates/rust-analyzer/src/diagnostics/to_proto.rs | 10 ++++++---- docs/user/generated_config.adoc | 4 ++-- editors/code/package.json | 4 ++-- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 3be335550..1109d2daf 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -99,9 +99,9 @@ config_data! { diagnostics_enableExperimental: bool = "true", /// List of rust-analyzer diagnostics to disable. diagnostics_disabled: FxHashSet = "[]", - /// Map of path prefixes to be substituted when parsing diagnostic file paths. + /// Map of prefixes to be substituted when parsing diagnostic file paths. /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. - diagnostics_remapPathPrefixes: FxHashMap = "{}", + diagnostics_remapPrefix: FxHashMap = "{}", /// List of warnings that should be displayed with info severity. /// /// The warnings will be indicated by a blue squiggly underline in code @@ -477,7 +477,7 @@ impl Config { } pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { DiagnosticsMapConfig { - remap_path_prefixes: self.data.diagnostics_remapPathPrefixes.clone(), + remap_prefix: self.data.diagnostics_remapPrefix.clone(), warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(), warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(), } diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index 776d21778..d4b9db362 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs @@ -12,7 +12,7 @@ pub(crate) type CheckFixes = Arc>>; #[derive(Debug, Default, Clone)] pub struct DiagnosticsMapConfig { - pub remap_path_prefixes: FxHashMap, + pub remap_prefix: FxHashMap, pub warnings_as_info: Vec, pub warnings_as_hint: Vec, } diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 08303a781..82dd0da9a 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -99,10 +99,12 @@ fn diagnostic_related_information( /// Resolves paths applying any matching path prefix remappings, and then /// joining the path to the workspace root. fn resolve_path(config: &DiagnosticsMapConfig, workspace_root: &Path, file_name: &str) -> PathBuf { - match config.remap_path_prefixes.iter().find(|(from, _)| file_name.starts_with(*from)) { - Some((from, to)) => { - workspace_root.join(format!("{}{}", to, file_name.strip_prefix(from).unwrap())) - } + match config + .remap_prefix + .iter() + .find_map(|(from, to)| file_name.strip_prefix(from).map(|file_name| (to, file_name))) + { + Some((to, file_name)) => workspace_root.join(format!("{}{}", to, file_name)), None => workspace_root.join(file_name), } } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index cb6fe5850..e28423e99 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -147,10 +147,10 @@ have more false positives than usual. -- List of rust-analyzer diagnostics to disable. -- -[[rust-analyzer.diagnostics.remapPathPrefixes]]rust-analyzer.diagnostics.remapPathPrefixes (default: `{}`):: +[[rust-analyzer.diagnostics.remapPrefix]]rust-analyzer.diagnostics.remapPrefix (default: `{}`):: + -- -Map of path prefixes to be substituted when parsing diagnostic file paths. +Map of prefixes to be substituted when parsing diagnostic file paths. This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. -- [[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: diff --git a/editors/code/package.json b/editors/code/package.json index dcb2e89d1..fa5632f90 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -565,8 +565,8 @@ }, "uniqueItems": true }, - "rust-analyzer.diagnostics.remapPathPrefixes": { - "markdownDescription": "Map of path prefixes to be substituted when parsing diagnostic file paths.\nThis should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.", + "rust-analyzer.diagnostics.remapPrefix": { + "markdownDescription": "Map of prefixes to be substituted when parsing diagnostic file paths.\nThis should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.", "default": {}, "type": "object" }, -- cgit v1.2.3