mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-11-03 13:13:18 +00:00 
			
		
		
		
	Diagnostic Remap Path Prefixes added.
This commit is contained in:
		
							parent
							
								
									60841f4276
								
							
						
					
					
						commit
						9fcad82980
					
				@ -17,7 +17,7 @@ use ide_db::helpers::{
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
use lsp_types::{ClientCapabilities, MarkupKind};
 | 
					use lsp_types::{ClientCapabilities, MarkupKind};
 | 
				
			||||||
use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
 | 
					use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
 | 
				
			||||||
use rustc_hash::FxHashSet;
 | 
					use rustc_hash::{FxHashMap, FxHashSet};
 | 
				
			||||||
use serde::{de::DeserializeOwned, Deserialize};
 | 
					use serde::{de::DeserializeOwned, Deserialize};
 | 
				
			||||||
use vfs::AbsPathBuf;
 | 
					use vfs::AbsPathBuf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -99,6 +99,9 @@ config_data! {
 | 
				
			|||||||
        diagnostics_enableExperimental: bool    = "true",
 | 
					        diagnostics_enableExperimental: bool    = "true",
 | 
				
			||||||
        /// List of rust-analyzer diagnostics to disable.
 | 
					        /// List of rust-analyzer diagnostics to disable.
 | 
				
			||||||
        diagnostics_disabled: FxHashSet<String> = "[]",
 | 
					        diagnostics_disabled: FxHashSet<String> = "[]",
 | 
				
			||||||
 | 
					        /// 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<String, String> = "{}",
 | 
				
			||||||
        /// List of warnings that should be displayed with info severity.
 | 
					        /// List of warnings that should be displayed with info severity.
 | 
				
			||||||
        ///
 | 
					        ///
 | 
				
			||||||
        /// The warnings will be indicated by a blue squiggly underline in code
 | 
					        /// The warnings will be indicated by a blue squiggly underline in code
 | 
				
			||||||
@ -474,6 +477,7 @@ impl Config {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
 | 
					    pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
 | 
				
			||||||
        DiagnosticsMapConfig {
 | 
					        DiagnosticsMapConfig {
 | 
				
			||||||
 | 
					            remap_path_prefixes: self.data.diagnostics_remapPathPrefixes.clone(),
 | 
				
			||||||
            warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
 | 
					            warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
 | 
				
			||||||
            warnings_as_hint: self.data.diagnostics_warningsAsHint.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" },
 | 
					            "items": { "type": "string" },
 | 
				
			||||||
            "uniqueItems": true,
 | 
					            "uniqueItems": true,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "FxHashMap<String, String>" => set! {
 | 
				
			||||||
 | 
					            "type": "object",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "Option<usize>" => set! {
 | 
					        "Option<usize>" => set! {
 | 
				
			||||||
            "type": ["null", "integer"],
 | 
					            "type": ["null", "integer"],
 | 
				
			||||||
            "minimum": 0,
 | 
					            "minimum": 0,
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@ pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Default, Clone)]
 | 
					#[derive(Debug, Default, Clone)]
 | 
				
			||||||
pub struct DiagnosticsMapConfig {
 | 
					pub struct DiagnosticsMapConfig {
 | 
				
			||||||
 | 
					    pub remap_path_prefixes: FxHashMap<String, String>,
 | 
				
			||||||
    pub warnings_as_info: Vec<String>,
 | 
					    pub warnings_as_info: Vec<String>,
 | 
				
			||||||
    pub warnings_as_hint: Vec<String>,
 | 
					    pub warnings_as_hint: Vec<String>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -44,8 +44,12 @@ fn is_dummy_macro_file(file_name: &str) -> bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Converts a Rust span to a LSP location
 | 
					/// Converts a Rust span to a LSP location
 | 
				
			||||||
fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
 | 
					fn location(
 | 
				
			||||||
    let file_name = resolve_path(workspace_root, &span.file_name);
 | 
					    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);
 | 
					    let uri = url_from_abs_path(&file_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // FIXME: this doesn't handle UTF16 offsets correctly
 | 
					    // 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
 | 
					/// 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.
 | 
					/// 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));
 | 
					    let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span));
 | 
				
			||||||
    for span in span_stack.clone() {
 | 
					    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) {
 | 
					        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.
 | 
					    // Fall back to the outermost macro invocation if no suitable span comes up.
 | 
				
			||||||
    let last_span = span_stack.last().unwrap();
 | 
					    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
 | 
					/// Converts a secondary Rust span to a LSP related information
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// If the span is unlabelled this will return `None`.
 | 
					/// If the span is unlabelled this will return `None`.
 | 
				
			||||||
fn diagnostic_related_information(
 | 
					fn diagnostic_related_information(
 | 
				
			||||||
 | 
					    config: &DiagnosticsMapConfig,
 | 
				
			||||||
    workspace_root: &Path,
 | 
					    workspace_root: &Path,
 | 
				
			||||||
    span: &DiagnosticSpan,
 | 
					    span: &DiagnosticSpan,
 | 
				
			||||||
) -> Option<lsp_types::DiagnosticRelatedInformation> {
 | 
					) -> Option<lsp_types::DiagnosticRelatedInformation> {
 | 
				
			||||||
    let message = span.label.clone()?;
 | 
					    let message = span.label.clone()?;
 | 
				
			||||||
    let location = location(workspace_root, span);
 | 
					    let location = location(config, workspace_root, span);
 | 
				
			||||||
    Some(lsp_types::DiagnosticRelatedInformation { location, message })
 | 
					    Some(lsp_types::DiagnosticRelatedInformation { location, message })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Resolves paths mimicking VSCode's behavior when `file_name` starts
 | 
					/// Resolves paths applying any matching path prefix remappings, and then
 | 
				
			||||||
/// with the root directory component, which does not discard the base
 | 
					/// joining the path to the workspace root.
 | 
				
			||||||
/// path.  If this relative path exists, use it, otherwise fall back
 | 
					fn resolve_path(config: &DiagnosticsMapConfig, workspace_root: &Path, file_name: &str) -> PathBuf {
 | 
				
			||||||
/// to the existing Rust behavior of path joining.
 | 
					    match config.remap_path_prefixes.iter().find(|(from, _)| file_name.starts_with(*from)) {
 | 
				
			||||||
fn resolve_path(workspace_root: &Path, file_name: &str) -> PathBuf {
 | 
					        Some((from, to)) => {
 | 
				
			||||||
    let file_name = Path::new(file_name);
 | 
					            workspace_root.join(format!("{}{}", to, file_name.strip_prefix(from).unwrap()))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    // Test path with VSCode's path join behavior.
 | 
					        None => workspace_root.join(file_name),
 | 
				
			||||||
    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 {
 | 
					struct SubDiagnostic {
 | 
				
			||||||
@ -122,6 +118,7 @@ enum MappedRustChildDiagnostic {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn map_rust_child_diagnostic(
 | 
					fn map_rust_child_diagnostic(
 | 
				
			||||||
 | 
					    config: &DiagnosticsMapConfig,
 | 
				
			||||||
    workspace_root: &Path,
 | 
					    workspace_root: &Path,
 | 
				
			||||||
    rd: &flycheck::Diagnostic,
 | 
					    rd: &flycheck::Diagnostic,
 | 
				
			||||||
) -> MappedRustChildDiagnostic {
 | 
					) -> MappedRustChildDiagnostic {
 | 
				
			||||||
@ -135,7 +132,7 @@ fn map_rust_child_diagnostic(
 | 
				
			|||||||
    let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
 | 
					    let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
 | 
				
			||||||
    for &span in &spans {
 | 
					    for &span in &spans {
 | 
				
			||||||
        if let Some(suggested_replacement) = &span.suggested_replacement {
 | 
					        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());
 | 
					            let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
 | 
				
			||||||
            edit_map.entry(location.uri).or_default().push(edit);
 | 
					            edit_map.entry(location.uri).or_default().push(edit);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -144,7 +141,7 @@ fn map_rust_child_diagnostic(
 | 
				
			|||||||
    if edit_map.is_empty() {
 | 
					    if edit_map.is_empty() {
 | 
				
			||||||
        MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
 | 
					        MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
 | 
				
			||||||
            related: lsp_types::DiagnosticRelatedInformation {
 | 
					            related: lsp_types::DiagnosticRelatedInformation {
 | 
				
			||||||
                location: location(workspace_root, spans[0]),
 | 
					                location: location(config, workspace_root, spans[0]),
 | 
				
			||||||
                message: rd.message.clone(),
 | 
					                message: rd.message.clone(),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            suggested_fix: None,
 | 
					            suggested_fix: None,
 | 
				
			||||||
@ -152,7 +149,7 @@ fn map_rust_child_diagnostic(
 | 
				
			|||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
 | 
					        MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
 | 
				
			||||||
            related: lsp_types::DiagnosticRelatedInformation {
 | 
					            related: lsp_types::DiagnosticRelatedInformation {
 | 
				
			||||||
                location: location(workspace_root, spans[0]),
 | 
					                location: location(config, workspace_root, spans[0]),
 | 
				
			||||||
                message: rd.message.clone(),
 | 
					                message: rd.message.clone(),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            suggested_fix: Some(lsp_ext::CodeAction {
 | 
					            suggested_fix: Some(lsp_ext::CodeAction {
 | 
				
			||||||
@ -217,7 +214,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
 | 
				
			|||||||
    let mut tags = Vec::new();
 | 
					    let mut tags = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
 | 
					    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 {
 | 
					        if let Some(related) = related {
 | 
				
			||||||
            subdiagnostics.push(SubDiagnostic { related, suggested_fix: None });
 | 
					            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();
 | 
					    let mut message = rd.message.clone();
 | 
				
			||||||
    for child in &rd.children {
 | 
					    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 {
 | 
					        match child {
 | 
				
			||||||
            MappedRustChildDiagnostic::SubDiagnostic(sub) => {
 | 
					            MappedRustChildDiagnostic::SubDiagnostic(sub) => {
 | 
				
			||||||
                subdiagnostics.push(sub);
 | 
					                subdiagnostics.push(sub);
 | 
				
			||||||
@ -269,7 +266,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
 | 
				
			|||||||
    primary_spans
 | 
					    primary_spans
 | 
				
			||||||
        .iter()
 | 
					        .iter()
 | 
				
			||||||
        .flat_map(|primary_span| {
 | 
					        .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();
 | 
					            let mut message = message.clone();
 | 
				
			||||||
            if needs_primary_span_label {
 | 
					            if needs_primary_span_label {
 | 
				
			||||||
@ -299,7 +296,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
 | 
				
			|||||||
                // generated that code.
 | 
					                // generated that code.
 | 
				
			||||||
                let is_in_macro_call = i != 0;
 | 
					                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 {
 | 
					                if secondary_location == primary_location {
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
				
			|||||||
@ -147,6 +147,12 @@ have more false positives than usual.
 | 
				
			|||||||
--
 | 
					--
 | 
				
			||||||
List of rust-analyzer diagnostics to disable.
 | 
					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: `[]`)::
 | 
					[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`)::
 | 
				
			||||||
+
 | 
					+
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
 | 
				
			|||||||
@ -565,6 +565,11 @@
 | 
				
			|||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    "uniqueItems": true
 | 
					                    "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": {
 | 
					                "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`.",
 | 
					                    "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": [],
 | 
					                    "default": [],
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user