aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/diagnostics.rs2
-rw-r--r--crates/hir_def/src/diagnostics.rs25
-rw-r--r--crates/hir_def/src/item_tree.rs18
-rw-r--r--crates/hir_def/src/nameres.rs15
-rw-r--r--crates/hir_def/src/nameres/collector.rs13
-rw-r--r--crates/ide/src/diagnostics.rs89
-rw-r--r--crates/ide/src/diagnostics/field_shorthand.rs32
-rw-r--r--crates/rust-analyzer/src/handlers.rs14
8 files changed, 141 insertions, 67 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index da2b40849..c18c1c587 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -1,5 +1,5 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2pub use hir_def::diagnostics::UnresolvedModule; 2pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule};
3pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder}; 3pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder};
4pub use hir_ty::diagnostics::{ 4pub use hir_ty::diagnostics::{
5 IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, 5 IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
index fcfbbbad3..c9c08b01f 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -86,3 +86,28 @@ impl Diagnostic for UnresolvedImport {
86 true 86 true
87 } 87 }
88} 88}
89
90// Diagnostic: unconfigured-code
91//
92// This diagnostic is shown for code with inactive `#[cfg]` attributes.
93#[derive(Debug)]
94pub struct InactiveCode {
95 pub file: HirFileId,
96 pub node: SyntaxNodePtr,
97}
98
99impl Diagnostic for InactiveCode {
100 fn code(&self) -> DiagnosticCode {
101 DiagnosticCode("inactive-code")
102 }
103 fn message(&self) -> String {
104 // FIXME: say *why* it is configured out
105 "code is inactive due to #[cfg] directives".to_string()
106 }
107 fn display_source(&self) -> InFile<SyntaxNodePtr> {
108 InFile::new(self.file, self.node.clone())
109 }
110 fn as_any(&self) -> &(dyn Any + Send + 'static) {
111 self
112 }
113}
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 8a1121bbd..7eb388bae 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -672,6 +672,24 @@ impl ModItem {
672 pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> { 672 pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
673 N::id_from_mod_item(self) 673 N::id_from_mod_item(self)
674 } 674 }
675
676 pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
677 match self {
678 ModItem::Import(it) => tree[it.index].ast_id().upcast(),
679 ModItem::ExternCrate(it) => tree[it.index].ast_id().upcast(),
680 ModItem::Function(it) => tree[it.index].ast_id().upcast(),
681 ModItem::Struct(it) => tree[it.index].ast_id().upcast(),
682 ModItem::Union(it) => tree[it.index].ast_id().upcast(),
683 ModItem::Enum(it) => tree[it.index].ast_id().upcast(),
684 ModItem::Const(it) => tree[it.index].ast_id().upcast(),
685 ModItem::Static(it) => tree[it.index].ast_id().upcast(),
686 ModItem::Trait(it) => tree[it.index].ast_id().upcast(),
687 ModItem::Impl(it) => tree[it.index].ast_id().upcast(),
688 ModItem::TypeAlias(it) => tree[it.index].ast_id().upcast(),
689 ModItem::Mod(it) => tree[it.index].ast_id().upcast(),
690 ModItem::MacroCall(it) => tree[it.index].ast_id().upcast(),
691 }
692 }
675} 693}
676 694
677#[derive(Debug, Copy, Clone, Eq, PartialEq)] 695#[derive(Debug, Copy, Clone, Eq, PartialEq)]
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 3d04f81c6..01a28aeeb 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -286,7 +286,7 @@ mod diagnostics {
286 use hir_expand::diagnostics::DiagnosticSink; 286 use hir_expand::diagnostics::DiagnosticSink;
287 use hir_expand::hygiene::Hygiene; 287 use hir_expand::hygiene::Hygiene;
288 use hir_expand::InFile; 288 use hir_expand::InFile;
289 use syntax::{ast, AstPtr}; 289 use syntax::{ast, AstPtr, SyntaxNodePtr};
290 290
291 use crate::path::ModPath; 291 use crate::path::ModPath;
292 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; 292 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
@@ -298,6 +298,8 @@ mod diagnostics {
298 UnresolvedExternCrate { ast: AstId<ast::ExternCrate> }, 298 UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
299 299
300 UnresolvedImport { ast: AstId<ast::Use>, index: usize }, 300 UnresolvedImport { ast: AstId<ast::Use>, index: usize },
301
302 UnconfiguredCode { ast: InFile<SyntaxNodePtr> },
301 } 303 }
302 304
303 #[derive(Debug, PartialEq, Eq)] 305 #[derive(Debug, PartialEq, Eq)]
@@ -336,6 +338,13 @@ mod diagnostics {
336 Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } } 338 Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } }
337 } 339 }
338 340
341 pub(super) fn unconfigured_code(
342 container: LocalModuleId,
343 ast: InFile<SyntaxNodePtr>,
344 ) -> Self {
345 Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast } }
346 }
347
339 pub(super) fn add_to( 348 pub(super) fn add_to(
340 &self, 349 &self,
341 db: &dyn DefDatabase, 350 db: &dyn DefDatabase,
@@ -385,6 +394,10 @@ mod diagnostics {
385 sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) }); 394 sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
386 } 395 }
387 } 396 }
397
398 DiagnosticKind::UnconfiguredCode { ast } => {
399 sink.push(InactiveCode { file: ast.file_id, node: ast.value.clone() });
400 }
388 } 401 }
389 } 402 }
390 } 403 }
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index c8cd04264..bff8edb62 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -913,6 +913,7 @@ impl ModCollector<'_, '_> {
913 for &item in items { 913 for &item in items {
914 let attrs = self.item_tree.attrs(item.into()); 914 let attrs = self.item_tree.attrs(item.into());
915 if !self.is_cfg_enabled(attrs) { 915 if !self.is_cfg_enabled(attrs) {
916 self.emit_unconfigured_diagnostic(item);
916 continue; 917 continue;
917 } 918 }
918 let module = 919 let module =
@@ -1323,6 +1324,18 @@ impl ModCollector<'_, '_> {
1323 fn is_cfg_enabled(&self, attrs: &Attrs) -> bool { 1324 fn is_cfg_enabled(&self, attrs: &Attrs) -> bool {
1324 attrs.is_cfg_enabled(self.def_collector.cfg_options) 1325 attrs.is_cfg_enabled(self.def_collector.cfg_options)
1325 } 1326 }
1327
1328 fn emit_unconfigured_diagnostic(&mut self, item: ModItem) {
1329 let ast_id = item.ast_id(self.item_tree);
1330 let id_map = self.def_collector.db.ast_id_map(self.file_id);
1331 let syntax_ptr = id_map.get(ast_id).syntax_node_ptr();
1332
1333 let ast_node = InFile::new(self.file_id, syntax_ptr);
1334 self.def_collector
1335 .def_map
1336 .diagnostics
1337 .push(DefDiagnostic::unconfigured_code(self.module_id, ast_node));
1338 }
1326} 1339}
1327 1340
1328fn is_macro_rules(path: &ModPath) -> bool { 1341fn is_macro_rules(path: &ModPath) -> bool {
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 1e5ea4617..90574cb35 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -10,7 +10,10 @@ mod field_shorthand;
10use std::cell::RefCell; 10use std::cell::RefCell;
11 11
12use base_db::SourceDatabase; 12use base_db::SourceDatabase;
13use hir::{diagnostics::DiagnosticSinkBuilder, Semantics}; 13use hir::{
14 diagnostics::{Diagnostic as _, DiagnosticSinkBuilder},
15 Semantics,
16};
14use ide_db::RootDatabase; 17use ide_db::RootDatabase;
15use itertools::Itertools; 18use itertools::Itertools;
16use rustc_hash::FxHashSet; 19use rustc_hash::FxHashSet;
@@ -31,6 +34,25 @@ pub struct Diagnostic {
31 pub range: TextRange, 34 pub range: TextRange,
32 pub severity: Severity, 35 pub severity: Severity,
33 pub fix: Option<Fix>, 36 pub fix: Option<Fix>,
37 pub unused: bool,
38}
39
40impl Diagnostic {
41 fn error(range: TextRange, message: String) -> Self {
42 Self { message, range, severity: Severity::Error, fix: None, unused: false }
43 }
44
45 fn hint(range: TextRange, message: String) -> Self {
46 Self { message, range, severity: Severity::WeakWarning, fix: None, unused: false }
47 }
48
49 fn with_fix(self, fix: Option<Fix>) -> Self {
50 Self { fix, ..self }
51 }
52
53 fn with_unused(self, unused: bool) -> Self {
54 Self { unused, ..self }
55 }
34} 56}
35 57
36#[derive(Debug)] 58#[derive(Debug)]
@@ -71,13 +93,13 @@ pub(crate) fn diagnostics(
71 let mut res = Vec::new(); 93 let mut res = Vec::new();
72 94
73 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. 95 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
74 res.extend(parse.errors().iter().take(128).map(|err| Diagnostic { 96 res.extend(
75 // name: None, 97 parse
76 range: err.range(), 98 .errors()
77 message: format!("Syntax Error: {}", err), 99 .iter()
78 severity: Severity::Error, 100 .take(128)
79 fix: None, 101 .map(|err| Diagnostic::error(err.range(), format!("Syntax Error: {}", err))),
80 })); 102 );
81 103
82 for node in parse.tree().syntax().descendants() { 104 for node in parse.tree().syntax().descendants() {
83 check_unnecessary_braces_in_use_statement(&mut res, file_id, &node); 105 check_unnecessary_braces_in_use_statement(&mut res, file_id, &node);
@@ -100,6 +122,13 @@ pub(crate) fn diagnostics(
100 .on::<hir::diagnostics::IncorrectCase, _>(|d| { 122 .on::<hir::diagnostics::IncorrectCase, _>(|d| {
101 res.borrow_mut().push(warning_with_fix(d, &sema)); 123 res.borrow_mut().push(warning_with_fix(d, &sema));
102 }) 124 })
125 .on::<hir::diagnostics::InactiveCode, _>(|d| {
126 // Override severity and mark as unused.
127 res.borrow_mut().push(
128 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
129 .with_unused(true),
130 );
131 })
103 // Only collect experimental diagnostics when they're enabled. 132 // Only collect experimental diagnostics when they're enabled.
104 .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) 133 .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
105 .filter(|diag| !config.disabled.contains(diag.code().as_str())); 134 .filter(|diag| !config.disabled.contains(diag.code().as_str()));
@@ -108,13 +137,8 @@ pub(crate) fn diagnostics(
108 let mut sink = sink_builder 137 let mut sink = sink_builder
109 // Diagnostics not handled above get no fix and default treatment. 138 // Diagnostics not handled above get no fix and default treatment.
110 .build(|d| { 139 .build(|d| {
111 res.borrow_mut().push(Diagnostic { 140 res.borrow_mut()
112 // name: Some(d.name().into()), 141 .push(Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()));
113 message: d.message(),
114 range: sema.diagnostics_display_range(d).range,
115 severity: Severity::Error,
116 fix: None,
117 })
118 }); 142 });
119 143
120 if let Some(m) = sema.to_module_def(file_id) { 144 if let Some(m) = sema.to_module_def(file_id) {
@@ -125,22 +149,11 @@ pub(crate) fn diagnostics(
125} 149}
126 150
127fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { 151fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
128 Diagnostic { 152 Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema))
129 // name: Some(d.name().into()),
130 range: sema.diagnostics_display_range(d).range,
131 message: d.message(),
132 severity: Severity::Error,
133 fix: d.fix(&sema),
134 }
135} 153}
136 154
137fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { 155fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
138 Diagnostic { 156 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema))
139 range: sema.diagnostics_display_range(d).range,
140 message: d.message(),
141 severity: Severity::WeakWarning,
142 fix: d.fix(&sema),
143 }
144} 157}
145 158
146fn check_unnecessary_braces_in_use_statement( 159fn check_unnecessary_braces_in_use_statement(
@@ -161,17 +174,14 @@ fn check_unnecessary_braces_in_use_statement(
161 edit_builder.finish() 174 edit_builder.finish()
162 }); 175 });
163 176
164 acc.push(Diagnostic { 177 acc.push(
165 // name: None, 178 Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string())
166 range: use_range, 179 .with_fix(Some(Fix::new(
167 message: "Unnecessary braces in use statement".to_string(), 180 "Remove unnecessary braces",
168 severity: Severity::WeakWarning, 181 SourceFileEdit { file_id, edit }.into(),
169 fix: Some(Fix::new( 182 use_range,
170 "Remove unnecessary braces", 183 ))),
171 SourceFileEdit { file_id, edit }.into(), 184 );
172 use_range,
173 )),
174 });
175 } 185 }
176 186
177 Some(()) 187 Some(())
@@ -578,6 +588,7 @@ fn test_fn() {
578 fix_trigger_range: 0..8, 588 fix_trigger_range: 0..8,
579 }, 589 },
580 ), 590 ),
591 unused: false,
581 }, 592 },
582 ] 593 ]
583 "#]], 594 "#]],
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs
index 2c4acd783..54e9fce9e 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;
6use syntax::{ast, match_ast, AstNode, SyntaxNode}; 6use syntax::{ast, match_ast, AstNode, SyntaxNode};
7use text_edit::TextEdit; 7use text_edit::TextEdit;
8 8
9use crate::{Diagnostic, Fix, Severity}; 9use crate::{Diagnostic, Fix};
10 10
11pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { 11pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) {
12 match_ast! { 12 match_ast! {
@@ -46,17 +46,15 @@ fn check_expr_field_shorthand(
46 let edit = edit_builder.finish(); 46 let edit = edit_builder.finish();
47 47
48 let field_range = record_field.syntax().text_range(); 48 let field_range = record_field.syntax().text_range();
49 acc.push(Diagnostic { 49 acc.push(
50 // name: None, 50 Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix(
51 range: field_range, 51 Some(Fix::new(
52 message: "Shorthand struct initialization".to_string(), 52 "Use struct shorthand initialization",
53 severity: Severity::WeakWarning, 53 SourceFileEdit { file_id, edit }.into(),
54 fix: Some(Fix::new( 54 field_range,
55 "Use struct shorthand initialization", 55 )),
56 SourceFileEdit { file_id, edit }.into(), 56 ),
57 field_range, 57 );
58 )),
59 });
60 } 58 }
61} 59}
62 60
@@ -88,17 +86,13 @@ fn check_pat_field_shorthand(
88 let edit = edit_builder.finish(); 86 let edit = edit_builder.finish();
89 87
90 let field_range = record_pat_field.syntax().text_range(); 88 let field_range = record_pat_field.syntax().text_range();
91 acc.push(Diagnostic { 89 acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix(
92 // name: None, 90 Some(Fix::new(
93 range: field_range,
94 message: "Shorthand struct pattern".to_string(),
95 severity: Severity::WeakWarning,
96 fix: Some(Fix::new(
97 "Use struct field shorthand", 91 "Use struct field shorthand",
98 SourceFileEdit { file_id, edit }.into(), 92 SourceFileEdit { file_id, edit }.into(),
99 field_range, 93 field_range,
100 )), 94 )),
101 }); 95 ));
102 } 96 }
103} 97}
104 98
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 215be850f..f2d57f986 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -16,12 +16,12 @@ use lsp_server::ErrorCode;
16use lsp_types::{ 16use lsp_types::{
17 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, 17 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
18 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, 18 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
19 CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, 19 CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag,
20 DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, 20 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
21 Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams, 21 HoverContents, Location, Position, PrepareRenameResponse, Range, RenameParams,
22 SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams, 22 SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
23 SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag, 23 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
24 TextDocumentIdentifier, Url, WorkspaceEdit, 24 SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
25}; 25};
26use project_model::TargetKind; 26use project_model::TargetKind;
27use serde::{Deserialize, Serialize}; 27use serde::{Deserialize, Serialize};
@@ -1124,7 +1124,7 @@ pub(crate) fn publish_diagnostics(
1124 source: Some("rust-analyzer".to_string()), 1124 source: Some("rust-analyzer".to_string()),
1125 message: d.message, 1125 message: d.message,
1126 related_information: None, 1126 related_information: None,
1127 tags: None, 1127 tags: if d.unused { Some(vec![DiagnosticTag::Unnecessary]) } else { None },
1128 }) 1128 })
1129 .collect(); 1129 .collect();
1130 Ok(diagnostics) 1130 Ok(diagnostics)