aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2020-08-08 23:59:26 +0100
committerKirill Bulatov <[email protected]>2020-08-11 13:09:08 +0100
commit9963f43d51071ea02f8f6d490b9c49882034b42c (patch)
tree7ecdbbd66fb8c988989e6de17c77ab14262589c0 /crates
parentcfbbd91a886e2394e7411f9d7f4966dcbd454764 (diff)
Refactor the diagnostics
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/semantics.rs28
-rw-r--r--crates/ra_hir_def/src/diagnostics.rs12
-rw-r--r--crates/ra_hir_expand/src/diagnostics.rs17
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs97
-rw-r--r--crates/ra_hir_ty/src/diagnostics/expr.rs12
-rw-r--r--crates/ra_ide/src/diagnostics.rs76
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
3use std::any::Any; 3use std::any::Any;
4 4
5use hir_expand::diagnostics::Diagnostic; 5use hir_expand::diagnostics::{AstDiagnostic, Diagnostic};
6use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; 6use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
7 7
8use hir_expand::{HirFileId, InFile}; 8use 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
29impl 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
17use std::{any::Any, fmt}; 17use std::{any::Any, fmt};
18 18
19use ra_syntax::{SyntaxNode, SyntaxNodePtr}; 19use ra_syntax::SyntaxNodePtr;
20 20
21use crate::{db::AstDatabase, InFile}; 21use 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 /// 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
37pub trait AstDiagnostic { 32pub 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
42impl dyn Diagnostic { 37impl 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;
9use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; 9use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
10use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; 10use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
11use ra_prof::profile; 11use ra_prof::profile;
12use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; 12use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
13use stdx::format_to; 13use stdx::format_to;
14 14
15use crate::db::HirDatabase; 15use 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 {
49impl AstDiagnostic for NoSuchField { 49impl 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)]
59pub struct MissingFields { 59pub 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
90impl AstDiagnostic for MissingFields { 91impl 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)]
100pub struct MissingPatFields { 101pub 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 {
166impl AstDiagnostic for MissingOkInTailExpr { 166impl 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
193impl 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)]
203pub struct MissingUnsafe { 194pub 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
220impl 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)]
230pub struct MismatchedArgCount { 212pub 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
253impl 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)]
263mod tests { 236mod 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 @@
7use std::cell::RefCell; 7use std::cell::RefCell;
8 8
9use hir::{ 9use hir::{
10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSinkBuilder}, 10 diagnostics::{Diagnostic as _, DiagnosticSinkBuilder},
11 HasSource, HirDisplay, Semantics, VariantDef, 11 HasSource, HirDisplay, Semantics, VariantDef,
12}; 12};
13use itertools::Itertools; 13use 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