aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-08-12 14:44:13 +0100
committerGitHub <[email protected]>2020-08-12 14:44:13 +0100
commit5b8fdfe23100b88e4fd8e210ccf6b852f5c9bf2a (patch)
treee321c900fe4997ec5ffe20cbb09946502745849c /crates/ra_hir_ty
parent11de7ac2fb6514484076217acb8d93eb36468681 (diff)
parentdb12ccee96bf37367b39ad99638d06da7123c088 (diff)
Merge #5553
5553: Add fix ranges for diagnostics r=matklad a=SomeoneToIgnore A follow-up of https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Less.20red.20in.20the.20code Now diagnostics can apply fixes in a range that's different from the range used to highlight the diagnostics. Previous logic did not consider the fix range, having both ranges equal, which could cause a lot of red noise in the editor. Now, the fix range gets used with the fix, the diagnostics range is used for everything else which allows to improve the error highlighting. before: <img width="191" alt="image" src="https://user-images.githubusercontent.com/2690773/88590727-df9a6a00-d063-11ea-97ed-9809c1c5e6e6.png"> after: <img width="222" alt="image" src="https://user-images.githubusercontent.com/2690773/88590734-e1fcc400-d063-11ea-9b7c-25701cbd5352.png"> `MissingFields` and `MissingPatFields` diagnostics now have the fix range as `ast::RecordFieldList` of the expression with an error (as it was before this PR), and the diagnostics range as a `ast::Path` of the expression, if it's present (do you have any example of `ast::Expr::RecordLit` that has no path btw?). The rest of the diagnostics have both ranges equal, same as it was before this PR. Co-authored-by: Kirill Bulatov <[email protected]>
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs128
-rw-r--r--crates/ra_hir_ty/src/diagnostics/expr.rs14
-rw-r--r--crates/ra_hir_ty/src/diagnostics/match_check.rs8
3 files changed, 59 insertions, 91 deletions
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 977c0525b..7ab7f79db 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -6,10 +6,10 @@ mod unsafe_check;
6use std::any::Any; 6use std::any::Any;
7 7
8use hir_def::DefWithBodyId; 8use hir_def::DefWithBodyId;
9use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; 9use hir_expand::diagnostics::{Diagnostic, DiagnosticSink};
10use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; 10use hir_expand::{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 display_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
@@ -46,20 +46,11 @@ impl Diagnostic for NoSuchField {
46 } 46 }
47} 47}
48 48
49impl AstDiagnostic for NoSuchField {
50 type AST = ast::RecordExprField;
51
52 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
53 let root = db.parse_or_expand(self.source().file_id).unwrap();
54 let node = self.source().value.to_node(&root);
55 ast::RecordExprField::cast(node).unwrap()
56 }
57}
58
59#[derive(Debug)] 49#[derive(Debug)]
60pub struct MissingFields { 50pub struct MissingFields {
61 pub file: HirFileId, 51 pub file: HirFileId,
62 pub field_list: AstPtr<ast::RecordExprFieldList>, 52 pub field_list_parent: AstPtr<ast::RecordExpr>,
53 pub field_list_parent_path: Option<AstPtr<ast::Path>>,
63 pub missed_fields: Vec<Name>, 54 pub missed_fields: Vec<Name>,
64} 55}
65 56
@@ -71,28 +62,28 @@ impl Diagnostic for MissingFields {
71 } 62 }
72 buf 63 buf
73 } 64 }
74 fn source(&self) -> InFile<SyntaxNodePtr> { 65
75 InFile { file_id: self.file, value: self.field_list.clone().into() } 66 fn display_source(&self) -> InFile<SyntaxNodePtr> {
67 InFile {
68 file_id: self.file,
69 value: self
70 .field_list_parent_path
71 .clone()
72 .map(SyntaxNodePtr::from)
73 .unwrap_or_else(|| self.field_list_parent.clone().into()),
74 }
76 } 75 }
76
77 fn as_any(&self) -> &(dyn Any + Send + 'static) { 77 fn as_any(&self) -> &(dyn Any + Send + 'static) {
78 self 78 self
79 } 79 }
80} 80}
81 81
82impl AstDiagnostic for MissingFields {
83 type AST = ast::RecordExprFieldList;
84
85 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
86 let root = db.parse_or_expand(self.source().file_id).unwrap();
87 let node = self.source().value.to_node(&root);
88 ast::RecordExprFieldList::cast(node).unwrap()
89 }
90}
91
92#[derive(Debug)] 82#[derive(Debug)]
93pub struct MissingPatFields { 83pub struct MissingPatFields {
94 pub file: HirFileId, 84 pub file: HirFileId,
95 pub field_list: AstPtr<ast::RecordPatFieldList>, 85 pub field_list_parent: AstPtr<ast::RecordPat>,
86 pub field_list_parent_path: Option<AstPtr<ast::Path>>,
96 pub missed_fields: Vec<Name>, 87 pub missed_fields: Vec<Name>,
97} 88}
98 89
@@ -104,8 +95,15 @@ impl Diagnostic for MissingPatFields {
104 } 95 }
105 buf 96 buf
106 } 97 }
107 fn source(&self) -> InFile<SyntaxNodePtr> { 98 fn display_source(&self) -> InFile<SyntaxNodePtr> {
108 InFile { file_id: self.file, value: self.field_list.clone().into() } 99 InFile {
100 file_id: self.file,
101 value: self
102 .field_list_parent_path
103 .clone()
104 .map(SyntaxNodePtr::from)
105 .unwrap_or_else(|| self.field_list_parent.clone().into()),
106 }
109 } 107 }
110 fn as_any(&self) -> &(dyn Any + Send + 'static) { 108 fn as_any(&self) -> &(dyn Any + Send + 'static) {
111 self 109 self
@@ -123,7 +121,7 @@ impl Diagnostic for MissingMatchArms {
123 fn message(&self) -> String { 121 fn message(&self) -> String {
124 String::from("Missing match arm") 122 String::from("Missing match arm")
125 } 123 }
126 fn source(&self) -> InFile<SyntaxNodePtr> { 124 fn display_source(&self) -> InFile<SyntaxNodePtr> {
127 InFile { file_id: self.file, value: self.match_expr.clone().into() } 125 InFile { file_id: self.file, value: self.match_expr.clone().into() }
128 } 126 }
129 fn as_any(&self) -> &(dyn Any + Send + 'static) { 127 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -141,7 +139,7 @@ impl Diagnostic for MissingOkInTailExpr {
141 fn message(&self) -> String { 139 fn message(&self) -> String {
142 "wrap return expression in Ok".to_string() 140 "wrap return expression in Ok".to_string()
143 } 141 }
144 fn source(&self) -> InFile<SyntaxNodePtr> { 142 fn display_source(&self) -> InFile<SyntaxNodePtr> {
145 InFile { file_id: self.file, value: self.expr.clone().into() } 143 InFile { file_id: self.file, value: self.expr.clone().into() }
146 } 144 }
147 fn as_any(&self) -> &(dyn Any + Send + 'static) { 145 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -149,16 +147,6 @@ impl Diagnostic for MissingOkInTailExpr {
149 } 147 }
150} 148}
151 149
152impl AstDiagnostic for MissingOkInTailExpr {
153 type AST = ast::Expr;
154
155 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
156 let root = db.parse_or_expand(self.file).unwrap();
157 let node = self.source().value.to_node(&root);
158 ast::Expr::cast(node).unwrap()
159 }
160}
161
162#[derive(Debug)] 150#[derive(Debug)]
163pub struct BreakOutsideOfLoop { 151pub struct BreakOutsideOfLoop {
164 pub file: HirFileId, 152 pub file: HirFileId,
@@ -169,7 +157,7 @@ impl Diagnostic for BreakOutsideOfLoop {
169 fn message(&self) -> String { 157 fn message(&self) -> String {
170 "break outside of loop".to_string() 158 "break outside of loop".to_string()
171 } 159 }
172 fn source(&self) -> InFile<SyntaxNodePtr> { 160 fn display_source(&self) -> InFile<SyntaxNodePtr> {
173 InFile { file_id: self.file, value: self.expr.clone().into() } 161 InFile { file_id: self.file, value: self.expr.clone().into() }
174 } 162 }
175 fn as_any(&self) -> &(dyn Any + Send + 'static) { 163 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -177,16 +165,6 @@ impl Diagnostic for BreakOutsideOfLoop {
177 } 165 }
178} 166}
179 167
180impl AstDiagnostic for BreakOutsideOfLoop {
181 type AST = ast::Expr;
182
183 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
184 let root = db.parse_or_expand(self.file).unwrap();
185 let node = self.source().value.to_node(&root);
186 ast::Expr::cast(node).unwrap()
187 }
188}
189
190#[derive(Debug)] 168#[derive(Debug)]
191pub struct MissingUnsafe { 169pub struct MissingUnsafe {
192 pub file: HirFileId, 170 pub file: HirFileId,
@@ -197,7 +175,7 @@ impl Diagnostic for MissingUnsafe {
197 fn message(&self) -> String { 175 fn message(&self) -> String {
198 format!("This operation is unsafe and requires an unsafe function or block") 176 format!("This operation is unsafe and requires an unsafe function or block")
199 } 177 }
200 fn source(&self) -> InFile<SyntaxNodePtr> { 178 fn display_source(&self) -> InFile<SyntaxNodePtr> {
201 InFile { file_id: self.file, value: self.expr.clone().into() } 179 InFile { file_id: self.file, value: self.expr.clone().into() }
202 } 180 }
203 fn as_any(&self) -> &(dyn Any + Send + 'static) { 181 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -205,16 +183,6 @@ impl Diagnostic for MissingUnsafe {
205 } 183 }
206} 184}
207 185
208impl AstDiagnostic for MissingUnsafe {
209 type AST = ast::Expr;
210
211 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
212 let root = db.parse_or_expand(self.source().file_id).unwrap();
213 let node = self.source().value.to_node(&root);
214 ast::Expr::cast(node).unwrap()
215 }
216}
217
218#[derive(Debug)] 186#[derive(Debug)]
219pub struct MismatchedArgCount { 187pub struct MismatchedArgCount {
220 pub file: HirFileId, 188 pub file: HirFileId,
@@ -228,7 +196,7 @@ impl Diagnostic for MismatchedArgCount {
228 let s = if self.expected == 1 { "" } else { "s" }; 196 let s = if self.expected == 1 { "" } else { "s" };
229 format!("Expected {} argument{}, found {}", self.expected, s, self.found) 197 format!("Expected {} argument{}, found {}", self.expected, s, self.found)
230 } 198 }
231 fn source(&self) -> InFile<SyntaxNodePtr> { 199 fn display_source(&self) -> InFile<SyntaxNodePtr> {
232 InFile { file_id: self.file, value: self.call_expr.clone().into() } 200 InFile { file_id: self.file, value: self.call_expr.clone().into() }
233 } 201 }
234 fn as_any(&self) -> &(dyn Any + Send + 'static) { 202 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -239,19 +207,13 @@ impl Diagnostic for MismatchedArgCount {
239 } 207 }
240} 208}
241 209
242impl AstDiagnostic for MismatchedArgCount {
243 type AST = ast::CallExpr;
244 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
245 let root = db.parse_or_expand(self.source().file_id).unwrap();
246 let node = self.source().value.to_node(&root);
247 ast::CallExpr::cast(node).unwrap()
248 }
249}
250
251#[cfg(test)] 210#[cfg(test)]
252mod tests { 211mod tests {
253 use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; 212 use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
254 use hir_expand::diagnostics::{Diagnostic, DiagnosticSinkBuilder}; 213 use hir_expand::{
214 db::AstDatabase,
215 diagnostics::{Diagnostic, DiagnosticSinkBuilder},
216 };
255 use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; 217 use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
256 use ra_syntax::{TextRange, TextSize}; 218 use ra_syntax::{TextRange, TextSize};
257 use rustc_hash::FxHashMap; 219 use rustc_hash::FxHashMap;
@@ -296,9 +258,11 @@ mod tests {
296 258
297 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); 259 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
298 db.diagnostics(|d| { 260 db.diagnostics(|d| {
299 // FXIME: macros... 261 let src = d.display_source();
300 let file_id = d.source().file_id.original_file(&db); 262 let root = db.parse_or_expand(src.file_id).unwrap();
301 let range = d.syntax_node(&db).text_range(); 263 // FIXME: macros...
264 let file_id = src.file_id.original_file(&db);
265 let range = src.value.to_node(&root).text_range();
302 let message = d.message().to_owned(); 266 let message = d.message().to_owned();
303 actual.entry(file_id).or_default().push((range, message)); 267 actual.entry(file_id).or_default().push((range, message));
304 }); 268 });
@@ -326,8 +290,8 @@ struct S { foo: i32, bar: () }
326impl S { 290impl S {
327 fn new() -> S { 291 fn new() -> S {
328 S { 292 S {
329 //^... Missing structure fields: 293 //^ Missing structure fields:
330 //| - bar 294 //| - bar
331 foo: 92, 295 foo: 92,
332 baz: 62, 296 baz: 62,
333 //^^^^^^^ no such field 297 //^^^^^^^ no such field
@@ -448,8 +412,8 @@ impl Foo {
448struct S { foo: i32, bar: () } 412struct S { foo: i32, bar: () }
449fn baz(s: S) { 413fn baz(s: S) {
450 let S { foo: _ } = s; 414 let S { foo: _ } = s;
451 //^^^^^^^^^^ Missing structure fields: 415 //^ Missing structure fields:
452 // | - bar 416 //| - bar
453} 417}
454"#, 418"#,
455 ); 419 );
diff --git a/crates/ra_hir_ty/src/diagnostics/expr.rs b/crates/ra_hir_ty/src/diagnostics/expr.rs
index 95bbf2d95..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,7 +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_expr.path().map(|path| AstPtr::new(&path)),
113 missed_fields, 114 missed_fields,
114 }) 115 })
115 } 116 }
@@ -131,7 +132,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
131 if let Some(expr) = source_ptr.value.as_ref().left() { 132 if let Some(expr) = source_ptr.value.as_ref().left() {
132 let root = source_ptr.file_syntax(db.upcast()); 133 let root = source_ptr.file_syntax(db.upcast());
133 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { 134 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
134 if let Some(field_list) = record_pat.record_pat_field_list() { 135 if let Some(_) = record_pat.record_pat_field_list() {
135 let variant_data = variant_data(db.upcast(), variant_def); 136 let variant_data = variant_data(db.upcast(), variant_def);
136 let missed_fields = missed_fields 137 let missed_fields = missed_fields
137 .into_iter() 138 .into_iter()
@@ -139,7 +140,10 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
139 .collect(); 140 .collect();
140 self.sink.push(MissingPatFields { 141 self.sink.push(MissingPatFields {
141 file: source_ptr.file_id, 142 file: source_ptr.file_id,
142 field_list: AstPtr::new(&field_list), 143 field_list_parent: AstPtr::new(&record_pat),
144 field_list_parent_path: record_pat
145 .path()
146 .map(|path| AstPtr::new(&path)),
143 missed_fields, 147 missed_fields,
144 }) 148 })
145 } 149 }
diff --git a/crates/ra_hir_ty/src/diagnostics/match_check.rs b/crates/ra_hir_ty/src/diagnostics/match_check.rs
index 507edcb7d..deca244db 100644
--- a/crates/ra_hir_ty/src/diagnostics/match_check.rs
+++ b/crates/ra_hir_ty/src/diagnostics/match_check.rs
@@ -1161,15 +1161,15 @@ fn main() {
1161 //^ Missing match arm 1161 //^ Missing match arm
1162 match a { 1162 match a {
1163 Either::A { } => (), 1163 Either::A { } => (),
1164 //^^^ Missing structure fields: 1164 //^^^^^^^^^ Missing structure fields:
1165 // | - foo 1165 // | - foo
1166 Either::B => (), 1166 Either::B => (),
1167 } 1167 }
1168 match a { 1168 match a {
1169 //^ Missing match arm 1169 //^ Missing match arm
1170 Either::A { } => (), 1170 Either::A { } => (),
1171 } //^^^ Missing structure fields: 1171 } //^^^^^^^^^ Missing structure fields:
1172 // | - foo 1172 // | - foo
1173 1173
1174 match a { 1174 match a {
1175 Either::A { foo: true } => (), 1175 Either::A { foo: true } => (),