From a54e481646edb151075d12ca6903091abe7cfc4e Mon Sep 17 00:00:00 2001
From: Jonas Schievink <jonas.schievink@ferrous-systems.com>
Date: Tue, 20 Oct 2020 17:48:43 +0200
Subject: [PATCH] Simplify diagnostic construction, add unused field

---
 crates/ide/src/diagnostics.rs                 | 73 +++++++++----------
 crates/ide/src/diagnostics/field_shorthand.rs | 32 ++++----
 crates/rust-analyzer/src/handlers.rs          | 14 ++--
 3 files changed, 55 insertions(+), 64 deletions(-)

diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 1e5ea4617b7..c92c1c06637 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -31,6 +31,21 @@ pub struct Diagnostic {
     pub range: TextRange,
     pub severity: Severity,
     pub fix: Option<Fix>,
+    pub unused: bool,
+}
+
+impl Diagnostic {
+    fn error(range: TextRange, message: String) -> Self {
+        Self { message, range, severity: Severity::Error, fix: None, unused: false }
+    }
+
+    fn hint(range: TextRange, message: String) -> Self {
+        Self { message, range, severity: Severity::WeakWarning, fix: None, unused: false }
+    }
+
+    fn with_fix(self, fix: Option<Fix>) -> Self {
+        Self { fix, ..self }
+    }
 }
 
 #[derive(Debug)]
@@ -71,13 +86,13 @@ pub(crate) fn diagnostics(
     let mut res = Vec::new();
 
     // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
-    res.extend(parse.errors().iter().take(128).map(|err| Diagnostic {
-        // name: None,
-        range: err.range(),
-        message: format!("Syntax Error: {}", err),
-        severity: Severity::Error,
-        fix: None,
-    }));
+    res.extend(
+        parse
+            .errors()
+            .iter()
+            .take(128)
+            .map(|err| Diagnostic::error(err.range(), format!("Syntax Error: {}", err))),
+    );
 
     for node in parse.tree().syntax().descendants() {
         check_unnecessary_braces_in_use_statement(&mut res, file_id, &node);
@@ -108,13 +123,8 @@ pub(crate) fn diagnostics(
     let mut sink = sink_builder
         // Diagnostics not handled above get no fix and default treatment.
         .build(|d| {
-            res.borrow_mut().push(Diagnostic {
-                // name: Some(d.name().into()),
-                message: d.message(),
-                range: sema.diagnostics_display_range(d).range,
-                severity: Severity::Error,
-                fix: None,
-            })
+            res.borrow_mut()
+                .push(Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()));
         });
 
     if let Some(m) = sema.to_module_def(file_id) {
@@ -125,22 +135,11 @@ pub(crate) fn diagnostics(
 }
 
 fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
-    Diagnostic {
-        // name: Some(d.name().into()),
-        range: sema.diagnostics_display_range(d).range,
-        message: d.message(),
-        severity: Severity::Error,
-        fix: d.fix(&sema),
-    }
+    Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema))
 }
 
 fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
-    Diagnostic {
-        range: sema.diagnostics_display_range(d).range,
-        message: d.message(),
-        severity: Severity::WeakWarning,
-        fix: d.fix(&sema),
-    }
+    Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema))
 }
 
 fn check_unnecessary_braces_in_use_statement(
@@ -161,17 +160,14 @@ fn check_unnecessary_braces_in_use_statement(
                     edit_builder.finish()
                 });
 
-        acc.push(Diagnostic {
-            // name: None,
-            range: use_range,
-            message: "Unnecessary braces in use statement".to_string(),
-            severity: Severity::WeakWarning,
-            fix: Some(Fix::new(
-                "Remove unnecessary braces",
-                SourceFileEdit { file_id, edit }.into(),
-                use_range,
-            )),
-        });
+        acc.push(
+            Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string())
+                .with_fix(Some(Fix::new(
+                    "Remove unnecessary braces",
+                    SourceFileEdit { file_id, edit }.into(),
+                    use_range,
+                ))),
+        );
     }
 
     Some(())
@@ -578,6 +574,7 @@ fn test_fn() {
                                 fix_trigger_range: 0..8,
                             },
                         ),
+                        unused: false,
                     },
                 ]
             "#]],
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs
index 2c4acd783d1..54e9fce9e5e 100644
--- a/crates/ide/src/diagnostics/field_shorthand.rs
+++ b/crates/ide/src/diagnostics/field_shorthand.rs
@@ -6,7 +6,7 @@ use ide_db::source_change::SourceFileEdit;
 use syntax::{ast, match_ast, AstNode, SyntaxNode};
 use text_edit::TextEdit;
 
-use crate::{Diagnostic, Fix, Severity};
+use crate::{Diagnostic, Fix};
 
 pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) {
     match_ast! {
@@ -46,17 +46,15 @@ fn check_expr_field_shorthand(
         let edit = edit_builder.finish();
 
         let field_range = record_field.syntax().text_range();
-        acc.push(Diagnostic {
-            // name: None,
-            range: field_range,
-            message: "Shorthand struct initialization".to_string(),
-            severity: Severity::WeakWarning,
-            fix: Some(Fix::new(
-                "Use struct shorthand initialization",
-                SourceFileEdit { file_id, edit }.into(),
-                field_range,
-            )),
-        });
+        acc.push(
+            Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix(
+                Some(Fix::new(
+                    "Use struct shorthand initialization",
+                    SourceFileEdit { file_id, edit }.into(),
+                    field_range,
+                )),
+            ),
+        );
     }
 }
 
@@ -88,17 +86,13 @@ fn check_pat_field_shorthand(
         let edit = edit_builder.finish();
 
         let field_range = record_pat_field.syntax().text_range();
-        acc.push(Diagnostic {
-            // name: None,
-            range: field_range,
-            message: "Shorthand struct pattern".to_string(),
-            severity: Severity::WeakWarning,
-            fix: Some(Fix::new(
+        acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix(
+            Some(Fix::new(
                 "Use struct field shorthand",
                 SourceFileEdit { file_id, edit }.into(),
                 field_range,
             )),
-        });
+        ));
     }
 }
 
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 215be850fdd..f2d57f9867f 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -16,12 +16,12 @@ use lsp_server::ErrorCode;
 use lsp_types::{
     CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
     CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
-    CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams,
-    DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location,
-    Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams,
-    SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams,
-    SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag,
-    TextDocumentIdentifier, Url, WorkspaceEdit,
+    CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag,
+    DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
+    HoverContents, Location, Position, PrepareRenameResponse, Range, RenameParams,
+    SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
+    SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
+    SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
 };
 use project_model::TargetKind;
 use serde::{Deserialize, Serialize};
@@ -1124,7 +1124,7 @@ pub(crate) fn publish_diagnostics(
             source: Some("rust-analyzer".to_string()),
             message: d.message,
             related_information: None,
-            tags: None,
+            tags: if d.unused { Some(vec![DiagnosticTag::Unnecessary]) } else { None },
         })
         .collect();
     Ok(diagnostics)