diff options
-rw-r--r-- | crates/ra_hir/src/semantics.rs | 28 | ||||
-rw-r--r-- | crates/ra_hir_def/src/diagnostics.rs | 12 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/diagnostics.rs | 17 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics.rs | 97 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/expr.rs | 12 | ||||
-rw-r--r-- | crates/ra_ide/src/diagnostics.rs | 76 |
6 files changed, 106 insertions, 136 deletions
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 1c5dc3d51..b4420d378 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -109,11 +109,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
109 | self.imp.parse(file_id) | 109 | self.imp.parse(file_id) |
110 | } | 110 | } |
111 | 111 | ||
112 | pub fn ast<T: AstDiagnostic + Diagnostic>(&self, d: &T) -> <T as AstDiagnostic>::AST { | 112 | pub fn diagnostic_fix_source<T: AstDiagnostic + Diagnostic>( |
113 | let file_id = d.source().file_id; | 113 | &self, |
114 | d: &T, | ||
115 | ) -> <T as AstDiagnostic>::AST { | ||
116 | let file_id = d.presentation().file_id; | ||
114 | let root = self.db.parse_or_expand(file_id).unwrap(); | 117 | let root = self.db.parse_or_expand(file_id).unwrap(); |
115 | self.imp.cache(root, file_id); | 118 | self.imp.cache(root, file_id); |
116 | d.ast(self.db.upcast()) | 119 | d.fix_source(self.db.upcast()) |
117 | } | 120 | } |
118 | 121 | ||
119 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { | 122 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { |
@@ -145,12 +148,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
145 | self.imp.original_range(node) | 148 | self.imp.original_range(node) |
146 | } | 149 | } |
147 | 150 | ||
148 | pub fn diagnostics_fix_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { | 151 | pub fn diagnostics_presentation_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { |
149 | self.imp.diagnostics_fix_range(diagnostics) | 152 | self.imp.diagnostics_presentation_range(diagnostics) |
150 | } | ||
151 | |||
152 | pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { | ||
153 | self.imp.diagnostics_range(diagnostics) | ||
154 | } | 153 | } |
155 | 154 | ||
156 | pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { | 155 | pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { |
@@ -380,15 +379,8 @@ impl<'db> SemanticsImpl<'db> { | |||
380 | original_range(self.db, node.as_ref()) | 379 | original_range(self.db, node.as_ref()) |
381 | } | 380 | } |
382 | 381 | ||
383 | fn diagnostics_fix_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { | 382 | fn diagnostics_presentation_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { |
384 | let src = diagnostics.fix_source(); | 383 | let src = diagnostics.presentation(); |
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 | |||
390 | fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { | ||
391 | let src = diagnostics.source(); | ||
392 | let root = self.db.parse_or_expand(src.file_id).unwrap(); | 384 | let root = self.db.parse_or_expand(src.file_id).unwrap(); |
393 | let node = src.value.to_node(&root); | 385 | let node = src.value.to_node(&root); |
394 | original_range(self.db, src.with_value(&node)) | 386 | original_range(self.db, src.with_value(&node)) |
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs index 30db48f86..be9612846 100644 --- a/crates/ra_hir_def/src/diagnostics.rs +++ b/crates/ra_hir_def/src/diagnostics.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::any::Any; | 3 | use std::any::Any; |
4 | 4 | ||
5 | use hir_expand::diagnostics::Diagnostic; | 5 | use hir_expand::diagnostics::{AstDiagnostic, Diagnostic}; |
6 | use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; | 6 | use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; |
7 | 7 | ||
8 | use hir_expand::{HirFileId, InFile}; | 8 | use hir_expand::{HirFileId, InFile}; |
@@ -18,10 +18,18 @@ 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 presentation(&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) { |
25 | self | 25 | self |
26 | } | 26 | } |
27 | } | 27 | } |
28 | |||
29 | impl AstDiagnostic for UnresolvedModule { | ||
30 | type AST = ast::Module; | ||
31 | fn fix_source(&self, db: &dyn hir_expand::db::AstDatabase) -> Self::AST { | ||
32 | let root = db.parse_or_expand(self.file).unwrap(); | ||
33 | self.decl.to_node(&root) | ||
34 | } | ||
35 | } | ||
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs index 90a3b87f9..2b74473ce 100644 --- a/crates/ra_hir_expand/src/diagnostics.rs +++ b/crates/ra_hir_expand/src/diagnostics.rs | |||
@@ -16,18 +16,13 @@ | |||
16 | 16 | ||
17 | use std::{any::Any, fmt}; | 17 | use std::{any::Any, fmt}; |
18 | 18 | ||
19 | use ra_syntax::{SyntaxNode, SyntaxNodePtr}; | 19 | use ra_syntax::SyntaxNodePtr; |
20 | 20 | ||
21 | use crate::{db::AstDatabase, InFile}; | 21 | use crate::{db::AstDatabase, InFile}; |
22 | 22 | ||
23 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | 23 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { |
24 | fn message(&self) -> String; | 24 | fn message(&self) -> String; |
25 | /// A source to be used in highlighting and other visual representations | 25 | fn presentation(&self) -> InFile<SyntaxNodePtr>; |
26 | fn source(&self) -> InFile<SyntaxNodePtr>; | ||
27 | /// A source to be used during the fix application | ||
28 | fn fix_source(&self) -> InFile<SyntaxNodePtr> { | ||
29 | self.source() | ||
30 | } | ||
31 | fn as_any(&self) -> &(dyn Any + Send + 'static); | 26 | fn as_any(&self) -> &(dyn Any + Send + 'static); |
32 | fn is_experimental(&self) -> bool { | 27 | fn is_experimental(&self) -> bool { |
33 | false | 28 | false |
@@ -36,16 +31,10 @@ pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | |||
36 | 31 | ||
37 | pub trait AstDiagnostic { | 32 | pub trait AstDiagnostic { |
38 | type AST; | 33 | type AST; |
39 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST; | 34 | fn fix_source(&self, db: &dyn AstDatabase) -> Self::AST; |
40 | } | 35 | } |
41 | 36 | ||
42 | impl dyn Diagnostic { | 37 | impl dyn Diagnostic { |
43 | pub fn syntax_node(&self, db: &impl AstDatabase) -> SyntaxNode { | ||
44 | let source = self.source(); | ||
45 | let node = db.parse_or_expand(source.file_id).unwrap(); | ||
46 | source.value.to_node(&node) | ||
47 | } | ||
48 | |||
49 | pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> { | 38 | pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> { |
50 | self.as_any().downcast_ref() | 39 | self.as_any().downcast_ref() |
51 | } | 40 | } |
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index efca09619..1e3a44637 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -9,7 +9,7 @@ use hir_def::DefWithBodyId; | |||
9 | use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; | 9 | use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; |
10 | use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; | 10 | use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; |
11 | use ra_prof::profile; | 11 | use ra_prof::profile; |
12 | use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; | 12 | use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; |
13 | use stdx::format_to; | 13 | use stdx::format_to; |
14 | 14 | ||
15 | use crate::db::HirDatabase; | 15 | use crate::db::HirDatabase; |
@@ -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 presentation(&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 | ||
@@ -49,7 +49,7 @@ impl Diagnostic for NoSuchField { | |||
49 | impl AstDiagnostic for NoSuchField { | 49 | 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 fix_source(&self, db: &dyn AstDatabase) -> Self::AST { |
53 | let root = db.parse_or_expand(self.file).unwrap(); | 53 | let root = db.parse_or_expand(self.file).unwrap(); |
54 | self.field.to_node(&root) | 54 | self.field.to_node(&root) |
55 | } | 55 | } |
@@ -58,7 +58,7 @@ impl AstDiagnostic for NoSuchField { | |||
58 | #[derive(Debug)] | 58 | #[derive(Debug)] |
59 | pub struct MissingFields { | 59 | pub struct MissingFields { |
60 | pub file: HirFileId, | 60 | pub file: HirFileId, |
61 | pub field_list: AstPtr<ast::RecordExprFieldList>, | 61 | pub field_list_parent: AstPtr<ast::RecordExpr>, |
62 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, | 62 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, |
63 | pub missed_fields: Vec<Name>, | 63 | pub missed_fields: Vec<Name>, |
64 | } | 64 | } |
@@ -71,15 +71,16 @@ impl Diagnostic for MissingFields { | |||
71 | } | 71 | } |
72 | buf | 72 | buf |
73 | } | 73 | } |
74 | fn fix_source(&self) -> InFile<SyntaxNodePtr> { | ||
75 | InFile { file_id: self.file, value: self.field_list.clone().into() } | ||
76 | } | ||
77 | 74 | ||
78 | fn source(&self) -> InFile<SyntaxNodePtr> { | 75 | fn presentation(&self) -> InFile<SyntaxNodePtr> { |
79 | self.field_list_parent_path | 76 | InFile { |
80 | .clone() | 77 | file_id: self.file, |
81 | .map(|path| InFile { file_id: self.file, value: path.into() }) | 78 | value: self |
82 | .unwrap_or_else(|| self.fix_source()) | 79 | .field_list_parent_path |
80 | .clone() | ||
81 | .map(SyntaxNodePtr::from) | ||
82 | .unwrap_or_else(|| self.field_list_parent.clone().into()), | ||
83 | } | ||
83 | } | 84 | } |
84 | 85 | ||
85 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 86 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
@@ -88,18 +89,18 @@ impl Diagnostic for MissingFields { | |||
88 | } | 89 | } |
89 | 90 | ||
90 | impl AstDiagnostic for MissingFields { | 91 | impl AstDiagnostic for MissingFields { |
91 | type AST = ast::RecordExprFieldList; | 92 | type AST = ast::RecordExpr; |
92 | 93 | ||
93 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { | 94 | fn fix_source(&self, db: &dyn AstDatabase) -> Self::AST { |
94 | let root = db.parse_or_expand(self.file).unwrap(); | 95 | let root = db.parse_or_expand(self.file).unwrap(); |
95 | self.field_list.to_node(&root) | 96 | self.field_list_parent.to_node(&root) |
96 | } | 97 | } |
97 | } | 98 | } |
98 | 99 | ||
99 | #[derive(Debug)] | 100 | #[derive(Debug)] |
100 | pub struct MissingPatFields { | 101 | pub struct MissingPatFields { |
101 | pub file: HirFileId, | 102 | pub file: HirFileId, |
102 | pub field_list: AstPtr<ast::RecordPatFieldList>, | 103 | pub field_list_parent: AstPtr<ast::RecordPat>, |
103 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, | 104 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, |
104 | pub missed_fields: Vec<Name>, | 105 | pub missed_fields: Vec<Name>, |
105 | } | 106 | } |
@@ -112,14 +113,13 @@ impl Diagnostic for MissingPatFields { | |||
112 | } | 113 | } |
113 | buf | 114 | buf |
114 | } | 115 | } |
115 | fn fix_source(&self) -> InFile<SyntaxNodePtr> { | 116 | fn presentation(&self) -> InFile<SyntaxNodePtr> { |
116 | InFile { file_id: self.file, value: self.field_list.clone().into() } | 117 | let value = self |
117 | } | 118 | .field_list_parent_path |
118 | fn source(&self) -> InFile<SyntaxNodePtr> { | ||
119 | self.field_list_parent_path | ||
120 | .clone() | 119 | .clone() |
121 | .map(|path| InFile { file_id: self.file, value: path.into() }) | 120 | .map(SyntaxNodePtr::from) |
122 | .unwrap_or_else(|| self.fix_source()) | 121 | .unwrap_or_else(|| self.field_list_parent.clone().into()); |
122 | InFile { file_id: self.file, value } | ||
123 | } | 123 | } |
124 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 124 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
125 | self | 125 | self |
@@ -137,7 +137,7 @@ impl Diagnostic for MissingMatchArms { | |||
137 | fn message(&self) -> String { | 137 | fn message(&self) -> String { |
138 | String::from("Missing match arm") | 138 | String::from("Missing match arm") |
139 | } | 139 | } |
140 | fn source(&self) -> InFile<SyntaxNodePtr> { | 140 | fn presentation(&self) -> InFile<SyntaxNodePtr> { |
141 | InFile { file_id: self.file, value: self.match_expr.clone().into() } | 141 | InFile { file_id: self.file, value: self.match_expr.clone().into() } |
142 | } | 142 | } |
143 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 143 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
@@ -155,7 +155,7 @@ impl Diagnostic for MissingOkInTailExpr { | |||
155 | fn message(&self) -> String { | 155 | fn message(&self) -> String { |
156 | "wrap return expression in Ok".to_string() | 156 | "wrap return expression in Ok".to_string() |
157 | } | 157 | } |
158 | fn source(&self) -> InFile<SyntaxNodePtr> { | 158 | fn presentation(&self) -> InFile<SyntaxNodePtr> { |
159 | InFile { file_id: self.file, value: self.expr.clone().into() } | 159 | InFile { file_id: self.file, value: self.expr.clone().into() } |
160 | } | 160 | } |
161 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 161 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
@@ -166,7 +166,7 @@ impl Diagnostic for MissingOkInTailExpr { | |||
166 | impl AstDiagnostic for MissingOkInTailExpr { | 166 | impl AstDiagnostic for MissingOkInTailExpr { |
167 | type AST = ast::Expr; | 167 | type AST = ast::Expr; |
168 | 168 | ||
169 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { | 169 | fn fix_source(&self, db: &dyn AstDatabase) -> Self::AST { |
170 | let root = db.parse_or_expand(self.file).unwrap(); | 170 | let root = db.parse_or_expand(self.file).unwrap(); |
171 | self.expr.to_node(&root) | 171 | self.expr.to_node(&root) |
172 | } | 172 | } |
@@ -182,7 +182,7 @@ impl Diagnostic for BreakOutsideOfLoop { | |||
182 | fn message(&self) -> String { | 182 | fn message(&self) -> String { |
183 | "break outside of loop".to_string() | 183 | "break outside of loop".to_string() |
184 | } | 184 | } |
185 | fn source(&self) -> InFile<SyntaxNodePtr> { | 185 | fn presentation(&self) -> InFile<SyntaxNodePtr> { |
186 | InFile { file_id: self.file, value: self.expr.clone().into() } | 186 | InFile { file_id: self.file, value: self.expr.clone().into() } |
187 | } | 187 | } |
188 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 188 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
@@ -190,15 +190,6 @@ impl Diagnostic for BreakOutsideOfLoop { | |||
190 | } | 190 | } |
191 | } | 191 | } |
192 | 192 | ||
193 | impl AstDiagnostic for BreakOutsideOfLoop { | ||
194 | type AST = ast::Expr; | ||
195 | |||
196 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { | ||
197 | let root = db.parse_or_expand(self.file).unwrap(); | ||
198 | self.expr.to_node(&root) | ||
199 | } | ||
200 | } | ||
201 | |||
202 | #[derive(Debug)] | 193 | #[derive(Debug)] |
203 | pub struct MissingUnsafe { | 194 | pub struct MissingUnsafe { |
204 | pub file: HirFileId, | 195 | pub file: HirFileId, |
@@ -209,7 +200,7 @@ impl Diagnostic for MissingUnsafe { | |||
209 | fn message(&self) -> String { | 200 | fn message(&self) -> String { |
210 | format!("This operation is unsafe and requires an unsafe function or block") | 201 | format!("This operation is unsafe and requires an unsafe function or block") |
211 | } | 202 | } |
212 | fn source(&self) -> InFile<SyntaxNodePtr> { | 203 | fn presentation(&self) -> InFile<SyntaxNodePtr> { |
213 | InFile { file_id: self.file, value: self.expr.clone().into() } | 204 | InFile { file_id: self.file, value: self.expr.clone().into() } |
214 | } | 205 | } |
215 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 206 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
@@ -217,15 +208,6 @@ impl Diagnostic for MissingUnsafe { | |||
217 | } | 208 | } |
218 | } | 209 | } |
219 | 210 | ||
220 | impl AstDiagnostic for MissingUnsafe { | ||
221 | type AST = ast::Expr; | ||
222 | |||
223 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { | ||
224 | let root = db.parse_or_expand(self.file).unwrap(); | ||
225 | self.expr.to_node(&root) | ||
226 | } | ||
227 | } | ||
228 | |||
229 | #[derive(Debug)] | 211 | #[derive(Debug)] |
230 | pub struct MismatchedArgCount { | 212 | pub struct MismatchedArgCount { |
231 | pub file: HirFileId, | 213 | pub file: HirFileId, |
@@ -239,7 +221,7 @@ impl Diagnostic for MismatchedArgCount { | |||
239 | let s = if self.expected == 1 { "" } else { "s" }; | 221 | let s = if self.expected == 1 { "" } else { "s" }; |
240 | format!("Expected {} argument{}, found {}", self.expected, s, self.found) | 222 | format!("Expected {} argument{}, found {}", self.expected, s, self.found) |
241 | } | 223 | } |
242 | fn source(&self) -> InFile<SyntaxNodePtr> { | 224 | fn presentation(&self) -> InFile<SyntaxNodePtr> { |
243 | InFile { file_id: self.file, value: self.call_expr.clone().into() } | 225 | InFile { file_id: self.file, value: self.call_expr.clone().into() } |
244 | } | 226 | } |
245 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | 227 | fn as_any(&self) -> &(dyn Any + Send + 'static) { |
@@ -250,19 +232,13 @@ impl Diagnostic for MismatchedArgCount { | |||
250 | } | 232 | } |
251 | } | 233 | } |
252 | 234 | ||
253 | impl AstDiagnostic for MismatchedArgCount { | ||
254 | type AST = ast::CallExpr; | ||
255 | fn ast(&self, db: &dyn AstDatabase) -> Self::AST { | ||
256 | let root = db.parse_or_expand(self.file).unwrap(); | ||
257 | let node = self.source().value.to_node(&root); | ||
258 | ast::CallExpr::cast(node).unwrap() | ||
259 | } | ||
260 | } | ||
261 | |||
262 | #[cfg(test)] | 235 | #[cfg(test)] |
263 | mod tests { | 236 | mod tests { |
264 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; | 237 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; |
265 | use hir_expand::diagnostics::{Diagnostic, DiagnosticSinkBuilder}; | 238 | use hir_expand::{ |
239 | db::AstDatabase, | ||
240 | diagnostics::{Diagnostic, DiagnosticSinkBuilder}, | ||
241 | }; | ||
266 | use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; | 242 | use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; |
267 | use ra_syntax::{TextRange, TextSize}; | 243 | use ra_syntax::{TextRange, TextSize}; |
268 | use rustc_hash::FxHashMap; | 244 | use rustc_hash::FxHashMap; |
@@ -308,8 +284,11 @@ mod tests { | |||
308 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | 284 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); |
309 | db.diagnostics(|d| { | 285 | db.diagnostics(|d| { |
310 | // FIXME: macros... | 286 | // FIXME: macros... |
311 | let file_id = d.source().file_id.original_file(&db); | 287 | let diagnostics_presentation = d.presentation(); |
312 | let range = d.syntax_node(&db).text_range(); | 288 | let root = db.parse_or_expand(diagnostics_presentation.file_id).unwrap(); |
289 | |||
290 | let file_id = diagnostics_presentation.file_id.original_file(&db); | ||
291 | let range = diagnostics_presentation.value.to_node(&root).text_range(); | ||
313 | let message = d.message().to_owned(); | 292 | let message = d.message().to_owned(); |
314 | actual.entry(file_id).or_default().push((range, message)); | 293 | actual.entry(file_id).or_default().push((range, message)); |
315 | }); | 294 | }); |
diff --git a/crates/ra_hir_ty/src/diagnostics/expr.rs b/crates/ra_hir_ty/src/diagnostics/expr.rs index 98959ab68..51adcecaf 100644 --- a/crates/ra_hir_ty/src/diagnostics/expr.rs +++ b/crates/ra_hir_ty/src/diagnostics/expr.rs | |||
@@ -100,8 +100,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
100 | 100 | ||
101 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 101 | if let Ok(source_ptr) = source_map.expr_syntax(id) { |
102 | let root = source_ptr.file_syntax(db.upcast()); | 102 | let root = source_ptr.file_syntax(db.upcast()); |
103 | if let ast::Expr::RecordExpr(record_lit) = &source_ptr.value.to_node(&root) { | 103 | if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) { |
104 | if let Some(field_list) = record_lit.record_expr_field_list() { | 104 | if let Some(_) = record_expr.record_expr_field_list() { |
105 | let variant_data = variant_data(db.upcast(), variant_def); | 105 | let variant_data = variant_data(db.upcast(), variant_def); |
106 | let missed_fields = missed_fields | 106 | let missed_fields = missed_fields |
107 | .into_iter() | 107 | .into_iter() |
@@ -109,8 +109,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
109 | .collect(); | 109 | .collect(); |
110 | self.sink.push(MissingFields { | 110 | self.sink.push(MissingFields { |
111 | file: source_ptr.file_id, | 111 | file: source_ptr.file_id, |
112 | field_list: AstPtr::new(&field_list), | 112 | field_list_parent: AstPtr::new(&record_expr), |
113 | field_list_parent_path: record_lit.path().map(|path| AstPtr::new(&path)), | 113 | field_list_parent_path: record_expr.path().map(|path| AstPtr::new(&path)), |
114 | missed_fields, | 114 | missed_fields, |
115 | }) | 115 | }) |
116 | } | 116 | } |
@@ -132,7 +132,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
132 | if let Some(expr) = source_ptr.value.as_ref().left() { | 132 | if let Some(expr) = source_ptr.value.as_ref().left() { |
133 | let root = source_ptr.file_syntax(db.upcast()); | 133 | let root = source_ptr.file_syntax(db.upcast()); |
134 | if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { | 134 | if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { |
135 | if let Some(field_list) = record_pat.record_pat_field_list() { | 135 | if let Some(_) = record_pat.record_pat_field_list() { |
136 | let variant_data = variant_data(db.upcast(), variant_def); | 136 | let variant_data = variant_data(db.upcast(), variant_def); |
137 | let missed_fields = missed_fields | 137 | let missed_fields = missed_fields |
138 | .into_iter() | 138 | .into_iter() |
@@ -140,7 +140,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
140 | .collect(); | 140 | .collect(); |
141 | self.sink.push(MissingPatFields { | 141 | self.sink.push(MissingPatFields { |
142 | file: source_ptr.file_id, | 142 | file: source_ptr.file_id, |
143 | field_list: AstPtr::new(&field_list), | 143 | field_list_parent: AstPtr::new(&record_pat), |
144 | field_list_parent_path: record_pat | 144 | field_list_parent_path: record_pat |
145 | .path() | 145 | .path() |
146 | .map(|path| AstPtr::new(&path)), | 146 | .map(|path| AstPtr::new(&path)), |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 0d2ff17e1..55593a8cb 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -7,7 +7,7 @@ | |||
7 | use std::cell::RefCell; | 7 | use std::cell::RefCell; |
8 | 8 | ||
9 | use hir::{ | 9 | use hir::{ |
10 | diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSinkBuilder}, | 10 | diagnostics::{Diagnostic as _, DiagnosticSinkBuilder}, |
11 | HasSource, HirDisplay, Semantics, VariantDef, | 11 | HasSource, HirDisplay, Semantics, VariantDef, |
12 | }; | 12 | }; |
13 | use itertools::Itertools; | 13 | use itertools::Itertools; |
@@ -63,10 +63,10 @@ pub(crate) fn diagnostics( | |||
63 | .into(), | 63 | .into(), |
64 | ); | 64 | ); |
65 | res.borrow_mut().push(Diagnostic { | 65 | res.borrow_mut().push(Diagnostic { |
66 | range: sema.diagnostics_range(d).range, | 66 | range: sema.diagnostics_presentation_range(d).range, |
67 | message: d.message(), | 67 | message: d.message(), |
68 | severity: Severity::Error, | 68 | severity: Severity::Error, |
69 | fix: Some((fix, sema.diagnostics_fix_range(d).range)), | 69 | fix: Some((fix, sema.diagnostic_fix_source(d).syntax().text_range())), |
70 | }) | 70 | }) |
71 | }) | 71 | }) |
72 | .on::<hir::diagnostics::MissingFields, _>(|d| { | 72 | .on::<hir::diagnostics::MissingFields, _>(|d| { |
@@ -78,56 +78,58 @@ pub(crate) fn diagnostics( | |||
78 | let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { | 78 | let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { |
79 | None | 79 | None |
80 | } else { | 80 | } else { |
81 | let mut field_list = d.ast(db); | 81 | let record_expr = sema.diagnostic_fix_source(d); |
82 | for f in d.missed_fields.iter() { | 82 | if let Some(old_field_list) = record_expr.record_expr_field_list() { |
83 | let field = make::record_expr_field( | 83 | let mut new_field_list = old_field_list.clone(); |
84 | make::name_ref(&f.to_string()), | 84 | for f in d.missed_fields.iter() { |
85 | Some(make::expr_unit()), | 85 | let field = make::record_expr_field( |
86 | ); | 86 | make::name_ref(&f.to_string()), |
87 | field_list = field_list.append_field(&field); | 87 | Some(make::expr_unit()), |
88 | ); | ||
89 | new_field_list = new_field_list.append_field(&field); | ||
90 | } | ||
91 | |||
92 | let edit = { | ||
93 | let mut builder = TextEditBuilder::default(); | ||
94 | algo::diff(&old_field_list.syntax(), &new_field_list.syntax()) | ||
95 | .into_text_edit(&mut builder); | ||
96 | builder.finish() | ||
97 | }; | ||
98 | Some(( | ||
99 | Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()), | ||
100 | sema.original_range(&old_field_list.syntax()).range, | ||
101 | )) | ||
102 | } else { | ||
103 | None | ||
88 | } | 104 | } |
89 | |||
90 | let edit = { | ||
91 | let mut builder = TextEditBuilder::default(); | ||
92 | algo::diff(&d.ast(db).syntax(), &field_list.syntax()) | ||
93 | .into_text_edit(&mut builder); | ||
94 | builder.finish() | ||
95 | }; | ||
96 | Some(( | ||
97 | Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()), | ||
98 | sema.diagnostics_fix_range(d).range, | ||
99 | )) | ||
100 | }; | 105 | }; |
101 | 106 | ||
102 | res.borrow_mut().push(Diagnostic { | 107 | res.borrow_mut().push(Diagnostic { |
103 | range: sema.diagnostics_range(d).range, | 108 | range: sema.diagnostics_presentation_range(d).range, |
104 | message: d.message(), | 109 | message: d.message(), |
105 | severity: Severity::Error, | 110 | severity: Severity::Error, |
106 | fix, | 111 | fix, |
107 | }) | 112 | }) |
108 | }) | 113 | }) |
109 | .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { | 114 | .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { |
110 | let node = d.ast(db); | 115 | let tail_expr = sema.diagnostic_fix_source(d); |
111 | let replacement = format!("Ok({})", node.syntax()); | 116 | let tail_expr_range = tail_expr.syntax().text_range(); |
112 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); | 117 | let edit = TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax())); |
113 | let source_change = SourceFileEdit { file_id, edit }.into(); | 118 | let source_change = SourceFileEdit { file_id, edit }.into(); |
114 | res.borrow_mut().push(Diagnostic { | 119 | res.borrow_mut().push(Diagnostic { |
115 | range: sema.diagnostics_range(d).range, | 120 | range: sema.diagnostics_presentation_range(d).range, |
116 | message: d.message(), | 121 | message: d.message(), |
117 | severity: Severity::Error, | 122 | severity: Severity::Error, |
118 | fix: Some(( | 123 | fix: Some((Fix::new("Wrap with ok", source_change), tail_expr_range)), |
119 | Fix::new("Wrap with ok", source_change), | ||
120 | sema.diagnostics_fix_range(d).range, | ||
121 | )), | ||
122 | }) | 124 | }) |
123 | }) | 125 | }) |
124 | .on::<hir::diagnostics::NoSuchField, _>(|d| { | 126 | .on::<hir::diagnostics::NoSuchField, _>(|d| { |
125 | res.borrow_mut().push(Diagnostic { | 127 | res.borrow_mut().push(Diagnostic { |
126 | range: sema.diagnostics_range(d).range, | 128 | range: sema.diagnostics_presentation_range(d).range, |
127 | message: d.message(), | 129 | message: d.message(), |
128 | severity: Severity::Error, | 130 | severity: Severity::Error, |
129 | fix: missing_struct_field_fix(&sema, file_id, d) | 131 | fix: missing_struct_field_fix(&sema, file_id, d) |
130 | .map(|fix| (fix, sema.diagnostics_fix_range(d).range)), | 132 | .map(|fix| (fix, sema.diagnostic_fix_source(d).syntax().text_range())), |
131 | }) | 133 | }) |
132 | }) | 134 | }) |
133 | // Only collect experimental diagnostics when they're enabled. | 135 | // Only collect experimental diagnostics when they're enabled. |
@@ -136,7 +138,7 @@ pub(crate) fn diagnostics( | |||
136 | .build(|d| { | 138 | .build(|d| { |
137 | res.borrow_mut().push(Diagnostic { | 139 | res.borrow_mut().push(Diagnostic { |
138 | message: d.message(), | 140 | message: d.message(), |
139 | range: sema.diagnostics_range(d).range, | 141 | range: sema.diagnostics_presentation_range(d).range, |
140 | severity: Severity::Error, | 142 | severity: Severity::Error, |
141 | fix: None, | 143 | fix: None, |
142 | }) | 144 | }) |
@@ -154,9 +156,9 @@ fn missing_struct_field_fix( | |||
154 | usage_file_id: FileId, | 156 | usage_file_id: FileId, |
155 | d: &hir::diagnostics::NoSuchField, | 157 | d: &hir::diagnostics::NoSuchField, |
156 | ) -> Option<Fix> { | 158 | ) -> Option<Fix> { |
157 | let record_expr = sema.ast(d); | 159 | let record_expr_field = sema.diagnostic_fix_source(d); |
158 | 160 | ||
159 | let record_lit = ast::RecordExpr::cast(record_expr.syntax().parent()?.parent()?)?; | 161 | let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?; |
160 | let def_id = sema.resolve_variant(record_lit)?; | 162 | let def_id = sema.resolve_variant(record_lit)?; |
161 | let module; | 163 | let module; |
162 | let def_file_id; | 164 | let def_file_id; |
@@ -184,12 +186,12 @@ fn missing_struct_field_fix( | |||
184 | }; | 186 | }; |
185 | let def_file_id = def_file_id.original_file(sema.db); | 187 | let def_file_id = def_file_id.original_file(sema.db); |
186 | 188 | ||
187 | let new_field_type = sema.type_of_expr(&record_expr.expr()?)?; | 189 | let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?; |
188 | if new_field_type.is_unknown() { | 190 | if new_field_type.is_unknown() { |
189 | return None; | 191 | return None; |
190 | } | 192 | } |
191 | let new_field = make::record_field( | 193 | let new_field = make::record_field( |
192 | record_expr.field_name()?, | 194 | record_expr_field.field_name()?, |
193 | make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), | 195 | make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), |
194 | ); | 196 | ); |
195 | 197 | ||