aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/semantics.rs11
-rw-r--r--crates/ra_hir_def/src/diagnostics.rs2
-rw-r--r--crates/ra_hir_expand/src/diagnostics.rs6
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs55
-rw-r--r--crates/ra_ide/src/diagnostics.rs31
5 files changed, 57 insertions, 48 deletions
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index e392130ab..1c5dc3d51 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -145,6 +145,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
145 self.imp.original_range(node) 145 self.imp.original_range(node)
146 } 146 }
147 147
148 pub fn diagnostics_fix_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
149 self.imp.diagnostics_fix_range(diagnostics)
150 }
151
148 pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { 152 pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
149 self.imp.diagnostics_range(diagnostics) 153 self.imp.diagnostics_range(diagnostics)
150 } 154 }
@@ -376,6 +380,13 @@ impl<'db> SemanticsImpl<'db> {
376 original_range(self.db, node.as_ref()) 380 original_range(self.db, node.as_ref())
377 } 381 }
378 382
383 fn diagnostics_fix_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
384 let src = diagnostics.fix_source();
385 let root = self.db.parse_or_expand(src.file_id).unwrap();
386 let node = src.value.to_node(&root);
387 original_range(self.db, src.with_value(&node))
388 }
389
379 fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { 390 fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
380 let src = diagnostics.source(); 391 let src = diagnostics.source();
381 let root = self.db.parse_or_expand(src.file_id).unwrap(); 392 let root = self.db.parse_or_expand(src.file_id).unwrap();
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index 30db48f86..e53269589 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -18,7 +18,7 @@ impl Diagnostic for UnresolvedModule {
18 fn message(&self) -> String { 18 fn message(&self) -> String {
19 "unresolved module".to_string() 19 "unresolved module".to_string()
20 } 20 }
21 fn source(&self) -> InFile<SyntaxNodePtr> { 21 fn fix_source(&self) -> InFile<SyntaxNodePtr> {
22 InFile::new(self.file, self.decl.clone().into()) 22 InFile::new(self.file, self.decl.clone().into())
23 } 23 }
24 fn as_any(&self) -> &(dyn Any + Send + 'static) { 24 fn as_any(&self) -> &(dyn Any + Send + 'static) {
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index ffeca5e82..074a8c45e 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -22,9 +22,9 @@ use crate::{db::AstDatabase, InFile};
22 22
23pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { 23pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
24 fn message(&self) -> String; 24 fn message(&self) -> String;
25 fn source(&self) -> InFile<SyntaxNodePtr>; 25 fn fix_source(&self) -> InFile<SyntaxNodePtr>;
26 fn highlighting_source(&self) -> InFile<SyntaxNodePtr> { 26 fn source(&self) -> InFile<SyntaxNodePtr> {
27 self.source() 27 self.fix_source()
28 } 28 }
29 fn as_any(&self) -> &(dyn Any + Send + 'static); 29 fn as_any(&self) -> &(dyn Any + Send + 'static);
30 fn is_experimental(&self) -> bool { 30 fn is_experimental(&self) -> bool {
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 73d241434..a4cede81d 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -37,7 +37,7 @@ impl Diagnostic for NoSuchField {
37 "no such field".to_string() 37 "no such field".to_string()
38 } 38 }
39 39
40 fn source(&self) -> InFile<SyntaxNodePtr> { 40 fn fix_source(&self) -> InFile<SyntaxNodePtr> {
41 InFile::new(self.file, self.field.clone().into()) 41 InFile::new(self.file, self.field.clone().into())
42 } 42 }
43 43
@@ -50,9 +50,8 @@ impl AstDiagnostic for NoSuchField {
50 type AST = ast::RecordExprField; 50 type AST = ast::RecordExprField;
51 51
52 fn ast(&self, db: &dyn AstDatabase) -> Self::AST { 52 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
53 let root = db.parse_or_expand(self.source().file_id).unwrap(); 53 let root = db.parse_or_expand(self.file).unwrap();
54 let node = self.source().value.to_node(&root); 54 self.field.to_node(&root)
55 ast::RecordExprField::cast(node).unwrap()
56 } 55 }
57} 56}
58 57
@@ -72,19 +71,19 @@ impl Diagnostic for MissingFields {
72 } 71 }
73 buf 72 buf
74 } 73 }
75 fn source(&self) -> InFile<SyntaxNodePtr> { 74 fn fix_source(&self) -> InFile<SyntaxNodePtr> {
76 InFile { file_id: self.file, value: self.field_list.clone().into() } 75 InFile { file_id: self.file, value: self.field_list.clone().into() }
77 } 76 }
78 77
79 fn as_any(&self) -> &(dyn Any + Send + 'static) { 78 fn source(&self) -> InFile<SyntaxNodePtr> {
80 self
81 }
82
83 fn highlighting_source(&self) -> InFile<SyntaxNodePtr> {
84 self.list_parent_path 79 self.list_parent_path
85 .clone() 80 .clone()
86 .map(|path| InFile { file_id: self.file, value: path.into() }) 81 .map(|path| InFile { file_id: self.file, value: path.into() })
87 .unwrap_or_else(|| self.source()) 82 .unwrap_or_else(|| self.fix_source())
83 }
84
85 fn as_any(&self) -> &(dyn Any + Send + 'static) {
86 self
88 } 87 }
89} 88}
90 89
@@ -112,7 +111,7 @@ impl Diagnostic for MissingPatFields {
112 } 111 }
113 buf 112 buf
114 } 113 }
115 fn source(&self) -> InFile<SyntaxNodePtr> { 114 fn fix_source(&self) -> InFile<SyntaxNodePtr> {
116 InFile { file_id: self.file, value: self.field_list.clone().into() } 115 InFile { file_id: self.file, value: self.field_list.clone().into() }
117 } 116 }
118 fn as_any(&self) -> &(dyn Any + Send + 'static) { 117 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -131,7 +130,7 @@ impl Diagnostic for MissingMatchArms {
131 fn message(&self) -> String { 130 fn message(&self) -> String {
132 String::from("Missing match arm") 131 String::from("Missing match arm")
133 } 132 }
134 fn source(&self) -> InFile<SyntaxNodePtr> { 133 fn fix_source(&self) -> InFile<SyntaxNodePtr> {
135 InFile { file_id: self.file, value: self.match_expr.clone().into() } 134 InFile { file_id: self.file, value: self.match_expr.clone().into() }
136 } 135 }
137 fn as_any(&self) -> &(dyn Any + Send + 'static) { 136 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -149,7 +148,7 @@ impl Diagnostic for MissingOkInTailExpr {
149 fn message(&self) -> String { 148 fn message(&self) -> String {
150 "wrap return expression in Ok".to_string() 149 "wrap return expression in Ok".to_string()
151 } 150 }
152 fn source(&self) -> InFile<SyntaxNodePtr> { 151 fn fix_source(&self) -> InFile<SyntaxNodePtr> {
153 InFile { file_id: self.file, value: self.expr.clone().into() } 152 InFile { file_id: self.file, value: self.expr.clone().into() }
154 } 153 }
155 fn as_any(&self) -> &(dyn Any + Send + 'static) { 154 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -162,8 +161,7 @@ impl AstDiagnostic for MissingOkInTailExpr {
162 161
163 fn ast(&self, db: &dyn AstDatabase) -> Self::AST { 162 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
164 let root = db.parse_or_expand(self.file).unwrap(); 163 let root = db.parse_or_expand(self.file).unwrap();
165 let node = self.source().value.to_node(&root); 164 self.expr.to_node(&root)
166 ast::Expr::cast(node).unwrap()
167 } 165 }
168} 166}
169 167
@@ -177,7 +175,7 @@ impl Diagnostic for BreakOutsideOfLoop {
177 fn message(&self) -> String { 175 fn message(&self) -> String {
178 "break outside of loop".to_string() 176 "break outside of loop".to_string()
179 } 177 }
180 fn source(&self) -> InFile<SyntaxNodePtr> { 178 fn fix_source(&self) -> InFile<SyntaxNodePtr> {
181 InFile { file_id: self.file, value: self.expr.clone().into() } 179 InFile { file_id: self.file, value: self.expr.clone().into() }
182 } 180 }
183 fn as_any(&self) -> &(dyn Any + Send + 'static) { 181 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -190,8 +188,7 @@ impl AstDiagnostic for BreakOutsideOfLoop {
190 188
191 fn ast(&self, db: &dyn AstDatabase) -> Self::AST { 189 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
192 let root = db.parse_or_expand(self.file).unwrap(); 190 let root = db.parse_or_expand(self.file).unwrap();
193 let node = self.source().value.to_node(&root); 191 self.expr.to_node(&root)
194 ast::Expr::cast(node).unwrap()
195 } 192 }
196} 193}
197 194
@@ -205,7 +202,7 @@ impl Diagnostic for MissingUnsafe {
205 fn message(&self) -> String { 202 fn message(&self) -> String {
206 format!("This operation is unsafe and requires an unsafe function or block") 203 format!("This operation is unsafe and requires an unsafe function or block")
207 } 204 }
208 fn source(&self) -> InFile<SyntaxNodePtr> { 205 fn fix_source(&self) -> InFile<SyntaxNodePtr> {
209 InFile { file_id: self.file, value: self.expr.clone().into() } 206 InFile { file_id: self.file, value: self.expr.clone().into() }
210 } 207 }
211 fn as_any(&self) -> &(dyn Any + Send + 'static) { 208 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -217,9 +214,8 @@ impl AstDiagnostic for MissingUnsafe {
217 type AST = ast::Expr; 214 type AST = ast::Expr;
218 215
219 fn ast(&self, db: &dyn AstDatabase) -> Self::AST { 216 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
220 let root = db.parse_or_expand(self.source().file_id).unwrap(); 217 let root = db.parse_or_expand(self.file).unwrap();
221 let node = self.source().value.to_node(&root); 218 self.expr.to_node(&root)
222 ast::Expr::cast(node).unwrap()
223 } 219 }
224} 220}
225 221
@@ -236,7 +232,7 @@ impl Diagnostic for MismatchedArgCount {
236 let s = if self.expected == 1 { "" } else { "s" }; 232 let s = if self.expected == 1 { "" } else { "s" };
237 format!("Expected {} argument{}, found {}", self.expected, s, self.found) 233 format!("Expected {} argument{}, found {}", self.expected, s, self.found)
238 } 234 }
239 fn source(&self) -> InFile<SyntaxNodePtr> { 235 fn fix_source(&self) -> InFile<SyntaxNodePtr> {
240 InFile { file_id: self.file, value: self.call_expr.clone().into() } 236 InFile { file_id: self.file, value: self.call_expr.clone().into() }
241 } 237 }
242 fn as_any(&self) -> &(dyn Any + Send + 'static) { 238 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -250,7 +246,7 @@ impl Diagnostic for MismatchedArgCount {
250impl AstDiagnostic for MismatchedArgCount { 246impl AstDiagnostic for MismatchedArgCount {
251 type AST = ast::CallExpr; 247 type AST = ast::CallExpr;
252 fn ast(&self, db: &dyn AstDatabase) -> Self::AST { 248 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
253 let root = db.parse_or_expand(self.source().file_id).unwrap(); 249 let root = db.parse_or_expand(self.file).unwrap();
254 let node = self.source().value.to_node(&root); 250 let node = self.source().value.to_node(&root);
255 ast::CallExpr::cast(node).unwrap() 251 ast::CallExpr::cast(node).unwrap()
256 } 252 }
@@ -308,12 +304,11 @@ mod tests {
308 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); 304 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
309 db.diagnostics(|d| { 305 db.diagnostics(|d| {
310 // FXIME: macros... 306 // FXIME: macros...
311 let file_id = d.source().file_id.original_file(&db); 307 let source = d.source();
312 let highlighting_source = d.highlighting_source(); 308 let root = db.parse_or_expand(source.file_id).unwrap();
313 let node = db.parse_or_expand(highlighting_source.file_id).unwrap(); 309 let range = source.value.to_node(&root).text_range();
314 let range = highlighting_source.value.to_node(&node).text_range();
315 let message = d.message().to_owned(); 310 let message = d.message().to_owned();
316 actual.entry(file_id).or_default().push((range, message)); 311 actual.entry(source.file_id.original_file(&db)).or_default().push((range, message));
317 }); 312 });
318 313
319 for (file_id, diags) in actual.iter_mut() { 314 for (file_id, diags) in actual.iter_mut() {
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index e847df6ea..0d2ff17e1 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -54,18 +54,19 @@ pub(crate) fn diagnostics(
54 let res = RefCell::new(res); 54 let res = RefCell::new(res);
55 let mut sink = DiagnosticSinkBuilder::new() 55 let mut sink = DiagnosticSinkBuilder::new()
56 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 56 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
57 let original_file = d.source().file_id.original_file(db);
58 let fix = Fix::new( 57 let fix = Fix::new(
59 "Create module", 58 "Create module",
60 FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() } 59 FileSystemEdit::CreateFile {
61 .into(), 60 anchor: d.file.original_file(db),
61 dst: d.candidate.clone(),
62 }
63 .into(),
62 ); 64 );
63 let range = sema.diagnostics_range(d).range;
64 res.borrow_mut().push(Diagnostic { 65 res.borrow_mut().push(Diagnostic {
65 range, 66 range: sema.diagnostics_range(d).range,
66 message: d.message(), 67 message: d.message(),
67 severity: Severity::Error, 68 severity: Severity::Error,
68 fix: Some((fix, range)), 69 fix: Some((fix, sema.diagnostics_fix_range(d).range)),
69 }) 70 })
70 }) 71 })
71 .on::<hir::diagnostics::MissingFields, _>(|d| { 72 .on::<hir::diagnostics::MissingFields, _>(|d| {
@@ -94,12 +95,12 @@ pub(crate) fn diagnostics(
94 }; 95 };
95 Some(( 96 Some((
96 Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()), 97 Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()),
97 sema.diagnostics_range(d).range, 98 sema.diagnostics_fix_range(d).range,
98 )) 99 ))
99 }; 100 };
100 101
101 res.borrow_mut().push(Diagnostic { 102 res.borrow_mut().push(Diagnostic {
102 range: d.highlighting_source().file_syntax(db).text_range(), 103 range: sema.diagnostics_range(d).range,
103 message: d.message(), 104 message: d.message(),
104 severity: Severity::Error, 105 severity: Severity::Error,
105 fix, 106 fix,
@@ -110,21 +111,23 @@ pub(crate) fn diagnostics(
110 let replacement = format!("Ok({})", node.syntax()); 111 let replacement = format!("Ok({})", node.syntax());
111 let edit = TextEdit::replace(node.syntax().text_range(), replacement); 112 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
112 let source_change = SourceFileEdit { file_id, edit }.into(); 113 let source_change = SourceFileEdit { file_id, edit }.into();
113 let range = sema.diagnostics_range(d).range;
114 res.borrow_mut().push(Diagnostic { 114 res.borrow_mut().push(Diagnostic {
115 range, 115 range: sema.diagnostics_range(d).range,
116 message: d.message(), 116 message: d.message(),
117 severity: Severity::Error, 117 severity: Severity::Error,
118 fix: Some((Fix::new("Wrap with ok", source_change), range)), 118 fix: Some((
119 Fix::new("Wrap with ok", source_change),
120 sema.diagnostics_fix_range(d).range,
121 )),
119 }) 122 })
120 }) 123 })
121 .on::<hir::diagnostics::NoSuchField, _>(|d| { 124 .on::<hir::diagnostics::NoSuchField, _>(|d| {
122 let range = sema.diagnostics_range(d).range;
123 res.borrow_mut().push(Diagnostic { 125 res.borrow_mut().push(Diagnostic {
124 range, 126 range: sema.diagnostics_range(d).range,
125 message: d.message(), 127 message: d.message(),
126 severity: Severity::Error, 128 severity: Severity::Error,
127 fix: missing_struct_field_fix(&sema, file_id, d).map(|fix| (fix, range)), 129 fix: missing_struct_field_fix(&sema, file_id, d)
130 .map(|fix| (fix, sema.diagnostics_fix_range(d).range)),
128 }) 131 })
129 }) 132 })
130 // Only collect experimental diagnostics when they're enabled. 133 // Only collect experimental diagnostics when they're enabled.