]> git.lizzy.rs Git - rust.git/commitdiff
Emit additional diagnostics for hints/help/etc
authorJonas Schievink <jonasschievink@gmail.com>
Sun, 6 Dec 2020 00:24:37 +0000 (01:24 +0100)
committerJonas Schievink <jonasschievink@gmail.com>
Sun, 6 Dec 2020 00:24:37 +0000 (01:24 +0100)
crates/rust-analyzer/src/diagnostics/to_proto.rs

index 324019614d528ab7ffe8d4b23845e4c3cb388c3e..766c342f1ff7f8d554d224c88f978dff339af8d3 100644 (file)
@@ -75,8 +75,10 @@ fn diagnostic_related_information(
 }
 
 enum MappedRustChildDiagnostic {
-    Related(lsp_types::DiagnosticRelatedInformation),
-    SuggestedFix(lsp_ext::CodeAction),
+    Related {
+        related: lsp_types::DiagnosticRelatedInformation,
+        suggested_fix: Option<lsp_ext::CodeAction>,
+    },
     MessageLine(String),
 }
 
@@ -103,23 +105,32 @@ fn map_rust_child_diagnostic(
     }
 
     if edit_map.is_empty() {
-        MappedRustChildDiagnostic::Related(lsp_types::DiagnosticRelatedInformation {
-            location: location(workspace_root, spans[0]),
-            message: rd.message.clone(),
-        })
+        MappedRustChildDiagnostic::Related {
+            related: lsp_types::DiagnosticRelatedInformation {
+                location: location(workspace_root, spans[0]),
+                message: rd.message.clone(),
+            },
+            suggested_fix: None,
+        }
     } else {
-        MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
-            title: rd.message.clone(),
-            group: None,
-            kind: Some(lsp_types::CodeActionKind::QUICKFIX),
-            edit: Some(lsp_ext::SnippetWorkspaceEdit {
-                // FIXME: there's no good reason to use edit_map here....
-                changes: Some(edit_map),
-                document_changes: None,
+        MappedRustChildDiagnostic::Related {
+            related: lsp_types::DiagnosticRelatedInformation {
+                location: location(workspace_root, spans[0]),
+                message: rd.message.clone(),
+            },
+            suggested_fix: Some(lsp_ext::CodeAction {
+                title: rd.message.clone(),
+                group: None,
+                kind: Some(lsp_types::CodeActionKind::QUICKFIX),
+                edit: Some(lsp_ext::SnippetWorkspaceEdit {
+                    // FIXME: there's no good reason to use edit_map here....
+                    changes: Some(edit_map),
+                    document_changes: None,
+                }),
+                is_preferred: Some(true),
+                data: None,
             }),
-            is_preferred: Some(true),
-            data: None,
-        })
+        }
     }
 }
 
@@ -179,8 +190,12 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
     for child in &rd.children {
         let child = map_rust_child_diagnostic(workspace_root, &child);
         match child {
-            MappedRustChildDiagnostic::Related(related) => related_information.push(related),
-            MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action),
+            MappedRustChildDiagnostic::Related { related, suggested_fix } => {
+                related_information.push(related);
+                if let Some(code_action) = suggested_fix {
+                    fixes.push(code_action);
+                }
+            }
             MappedRustChildDiagnostic::MessageLine(message_line) => {
                 format_to!(message, "\n{}", message_line);
 
@@ -219,7 +234,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
 
     primary_spans
         .iter()
-        .map(|primary_span| {
+        .flat_map(|primary_span| {
             let location = location(workspace_root, &primary_span);
 
             let mut message = message.clone();
@@ -229,72 +244,100 @@ 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;
+
             // 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.
-            let additional_diagnostic =
-                if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
-                    let in_macro_location = location_naive(workspace_root, &primary_span);
+            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_information.push(lsp_types::DiagnosticRelatedInformation {
-                        location: in_macro_location.clone(),
-                        message: "Error originated from macro here".to_string(),
-                    });
+                // Add related information for the main disagnostic.
+                related_macro_info = Some(lsp_types::DiagnosticRelatedInformation {
+                    location: in_macro_location.clone(),
+                    message: "Error originated from macro 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 {
-                            location: location.clone(),
-                            message: "Exact error occured 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 {
+                        location: location.clone(),
+                        message: "Exact error occured here".to_string(),
+                    }];
 
-                    let diagnostic = lsp_types::Diagnostic {
-                        range: in_macro_location.range,
-                        severity,
-                        code: code.clone().map(lsp_types::NumberOrString::String),
-                        code_description: code_description.clone(),
-                        source: Some(source.clone()),
-                        message: message.clone(),
-                        related_information: Some(information_for_additional_diagnostic),
-                        tags: if tags.is_empty() { None } else { Some(tags.clone()) },
-                        data: None,
-                    };
-
-                    Some(MappedRustDiagnostic {
-                        url: in_macro_location.uri,
-                        diagnostic,
-                        fixes: fixes.clone(),
-                    })
-                } else {
-                    None
+                let diagnostic = lsp_types::Diagnostic {
+                    range: in_macro_location.range,
+                    severity,
+                    code: code.clone().map(lsp_types::NumberOrString::String),
+                    code_description: code_description.clone(),
+                    source: Some(source.clone()),
+                    message: message.clone(),
+                    related_information: Some(information_for_additional_diagnostic),
+                    tags: if tags.is_empty() { None } else { Some(tags.clone()) },
+                    data: None,
                 };
 
-            let diagnostic = lsp_types::Diagnostic {
-                range: location.range,
-                severity,
-                code: code.clone().map(lsp_types::NumberOrString::String),
-                code_description: code_description.clone(),
-                source: Some(source.clone()),
-                message,
-                related_information: if related_information.is_empty() {
-                    None
-                } else {
-                    Some(related_information.clone())
+                diagnostics.push(MappedRustDiagnostic {
+                    url: in_macro_location.uri,
+                    diagnostic,
+                    fixes: fixes.clone(),
+                });
+            }
+
+            // Emit the primary diagnostic.
+            diagnostics.push(MappedRustDiagnostic {
+                url: location.uri.clone(),
+                diagnostic: lsp_types::Diagnostic {
+                    range: location.range,
+                    severity,
+                    code: code.clone().map(lsp_types::NumberOrString::String),
+                    code_description: code_description.clone(),
+                    source: Some(source.clone()),
+                    message,
+                    related_information: if related_information.is_empty() {
+                        None
+                    } else {
+                        let mut related = related_information.clone();
+                        related.extend(related_macro_info);
+                        Some(related)
+                    },
+                    tags: if tags.is_empty() { None } else { Some(tags.clone()) },
+                    data: None,
                 },
-                tags: if tags.is_empty() { None } else { Some(tags.clone()) },
-                data: None,
-            };
+                fixes: fixes.clone(),
+            });
 
-            let main_diagnostic =
-                MappedRustDiagnostic { url: location.uri, diagnostic, fixes: fixes.clone() };
-            match additional_diagnostic {
-                None => vec![main_diagnostic],
-                Some(additional_diagnostic) => vec![main_diagnostic, additional_diagnostic],
+            // Emit hint-level diagnostics for all `related_information` entries such as "help"s.
+            // 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,
+                message: "original diagnostic".to_string(),
+            };
+            for info in &related_information {
+                diagnostics.push(MappedRustDiagnostic {
+                    url: info.location.uri.clone(),
+                    fixes: fixes.clone(), // share fixes to make them easier to apply
+                    diagnostic: lsp_types::Diagnostic {
+                        range: info.location.range,
+                        severity: Some(lsp_types::DiagnosticSeverity::Hint),
+                        code: code.clone().map(lsp_types::NumberOrString::String),
+                        code_description: code_description.clone(),
+                        source: Some(source.clone()),
+                        message: info.message.clone(),
+                        related_information: Some(vec![back_ref.clone()]),
+                        tags: None, // don't apply modifiers again
+                        data: None,
+                    },
+                });
             }
+
+            diagnostics
         })
-        .flatten()
         .collect()
 }