diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-01-23 08:42:45 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-01-23 08:42:45 +0000 |
commit | fb2b9c7212b8a8ad88132473ea03cd6de20c85ab (patch) | |
tree | 582ca4f048a09f86c0e489bbd31950d2b738036b /crates/hir_ty/src/diagnostics | |
parent | eab5db20edd9604ba5d489fa8c6430eb7bac6610 (diff) | |
parent | db6dda94a39534bbf20da844a4f221c3d14509c4 (diff) |
Merge #7062
7062: Add diagnostic for filter_map followed by next r=theotherphil a=theotherphil
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/1725
Co-authored-by: Phil Ellison <[email protected]>
Diffstat (limited to 'crates/hir_ty/src/diagnostics')
-rw-r--r-- | crates/hir_ty/src/diagnostics/expr.rs | 85 |
1 files changed, 74 insertions, 11 deletions
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 107417c27..d740b7265 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -2,8 +2,10 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_def::{expr::Statement, path::path, resolver::HasResolver, AdtId, DefWithBodyId}; | 5 | use hir_def::{ |
6 | use hir_expand::diagnostics::DiagnosticSink; | 6 | expr::Statement, path::path, resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, |
7 | }; | ||
8 | use hir_expand::{diagnostics::DiagnosticSink, name}; | ||
7 | use rustc_hash::FxHashSet; | 9 | use rustc_hash::FxHashSet; |
8 | use syntax::{ast, AstPtr}; | 10 | use syntax::{ast, AstPtr}; |
9 | 11 | ||
@@ -24,6 +26,8 @@ pub(crate) use hir_def::{ | |||
24 | LocalFieldId, VariantId, | 26 | LocalFieldId, VariantId, |
25 | }; | 27 | }; |
26 | 28 | ||
29 | use super::ReplaceFilterMapNextWithFindMap; | ||
30 | |||
27 | pub(super) struct ExprValidator<'a, 'b: 'a> { | 31 | pub(super) struct ExprValidator<'a, 'b: 'a> { |
28 | owner: DefWithBodyId, | 32 | owner: DefWithBodyId, |
29 | infer: Arc<InferenceResult>, | 33 | infer: Arc<InferenceResult>, |
@@ -40,6 +44,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
40 | } | 44 | } |
41 | 45 | ||
42 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { | 46 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { |
47 | self.check_for_filter_map_next(db); | ||
48 | |||
43 | let body = db.body(self.owner.into()); | 49 | let body = db.body(self.owner.into()); |
44 | 50 | ||
45 | for (id, expr) in body.exprs.iter() { | 51 | for (id, expr) in body.exprs.iter() { |
@@ -150,20 +156,76 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
150 | } | 156 | } |
151 | } | 157 | } |
152 | 158 | ||
153 | fn validate_call(&mut self, db: &dyn HirDatabase, call_id: ExprId, expr: &Expr) -> Option<()> { | 159 | fn check_for_filter_map_next(&mut self, db: &dyn HirDatabase) { |
160 | // Find the FunctionIds for Iterator::filter_map and Iterator::next | ||
161 | let iterator_path = path![core::iter::Iterator]; | ||
162 | let resolver = self.owner.resolver(db.upcast()); | ||
163 | let iterator_trait_id = match resolver.resolve_known_trait(db.upcast(), &iterator_path) { | ||
164 | Some(id) => id, | ||
165 | None => return, | ||
166 | }; | ||
167 | let iterator_trait_items = &db.trait_data(iterator_trait_id).items; | ||
168 | let filter_map_function_id = | ||
169 | match iterator_trait_items.iter().find(|item| item.0 == name![filter_map]) { | ||
170 | Some((_, AssocItemId::FunctionId(id))) => id, | ||
171 | _ => return, | ||
172 | }; | ||
173 | let next_function_id = match iterator_trait_items.iter().find(|item| item.0 == name![next]) | ||
174 | { | ||
175 | Some((_, AssocItemId::FunctionId(id))) => id, | ||
176 | _ => return, | ||
177 | }; | ||
178 | |||
179 | // Search function body for instances of .filter_map(..).next() | ||
180 | let body = db.body(self.owner.into()); | ||
181 | let mut prev = None; | ||
182 | for (id, expr) in body.exprs.iter() { | ||
183 | if let Expr::MethodCall { receiver, .. } = expr { | ||
184 | let function_id = match self.infer.method_resolution(id) { | ||
185 | Some(id) => id, | ||
186 | None => continue, | ||
187 | }; | ||
188 | |||
189 | if function_id == *filter_map_function_id { | ||
190 | prev = Some(id); | ||
191 | continue; | ||
192 | } | ||
193 | |||
194 | if function_id == *next_function_id { | ||
195 | if let Some(filter_map_id) = prev { | ||
196 | if *receiver == filter_map_id { | ||
197 | let (_, source_map) = db.body_with_source_map(self.owner.into()); | ||
198 | if let Ok(next_source_ptr) = source_map.expr_syntax(id) { | ||
199 | self.sink.push(ReplaceFilterMapNextWithFindMap { | ||
200 | file: next_source_ptr.file_id, | ||
201 | next_expr: next_source_ptr.value, | ||
202 | }); | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | } | ||
208 | prev = None; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | fn validate_call(&mut self, db: &dyn HirDatabase, call_id: ExprId, expr: &Expr) { | ||
154 | // Check that the number of arguments matches the number of parameters. | 213 | // Check that the number of arguments matches the number of parameters. |
155 | 214 | ||
156 | // FIXME: Due to shortcomings in the current type system implementation, only emit this | 215 | // FIXME: Due to shortcomings in the current type system implementation, only emit this |
157 | // diagnostic if there are no type mismatches in the containing function. | 216 | // diagnostic if there are no type mismatches in the containing function. |
158 | if self.infer.type_mismatches.iter().next().is_some() { | 217 | if self.infer.type_mismatches.iter().next().is_some() { |
159 | return None; | 218 | return; |
160 | } | 219 | } |
161 | 220 | ||
162 | let is_method_call = matches!(expr, Expr::MethodCall { .. }); | 221 | let is_method_call = matches!(expr, Expr::MethodCall { .. }); |
163 | let (sig, args) = match expr { | 222 | let (sig, args) = match expr { |
164 | Expr::Call { callee, args } => { | 223 | Expr::Call { callee, args } => { |
165 | let callee = &self.infer.type_of_expr[*callee]; | 224 | let callee = &self.infer.type_of_expr[*callee]; |
166 | let sig = callee.callable_sig(db)?; | 225 | let sig = match callee.callable_sig(db) { |
226 | Some(sig) => sig, | ||
227 | None => return, | ||
228 | }; | ||
167 | (sig, args.clone()) | 229 | (sig, args.clone()) |
168 | } | 230 | } |
169 | Expr::MethodCall { receiver, args, .. } => { | 231 | Expr::MethodCall { receiver, args, .. } => { |
@@ -175,22 +237,25 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
175 | // if the receiver is of unknown type, it's very likely we | 237 | // if the receiver is of unknown type, it's very likely we |
176 | // don't know enough to correctly resolve the method call. | 238 | // don't know enough to correctly resolve the method call. |
177 | // This is kind of a band-aid for #6975. | 239 | // This is kind of a band-aid for #6975. |
178 | return None; | 240 | return; |
179 | } | 241 | } |
180 | 242 | ||
181 | // FIXME: note that we erase information about substs here. This | 243 | // FIXME: note that we erase information about substs here. This |
182 | // is not right, but, luckily, doesn't matter as we care only | 244 | // is not right, but, luckily, doesn't matter as we care only |
183 | // about the number of params | 245 | // about the number of params |
184 | let callee = self.infer.method_resolution(call_id)?; | 246 | let callee = match self.infer.method_resolution(call_id) { |
247 | Some(callee) => callee, | ||
248 | None => return, | ||
249 | }; | ||
185 | let sig = db.callable_item_signature(callee.into()).value; | 250 | let sig = db.callable_item_signature(callee.into()).value; |
186 | 251 | ||
187 | (sig, args) | 252 | (sig, args) |
188 | } | 253 | } |
189 | _ => return None, | 254 | _ => return, |
190 | }; | 255 | }; |
191 | 256 | ||
192 | if sig.is_varargs { | 257 | if sig.is_varargs { |
193 | return None; | 258 | return; |
194 | } | 259 | } |
195 | 260 | ||
196 | let params = sig.params(); | 261 | let params = sig.params(); |
@@ -213,8 +278,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
213 | }); | 278 | }); |
214 | } | 279 | } |
215 | } | 280 | } |
216 | |||
217 | None | ||
218 | } | 281 | } |
219 | 282 | ||
220 | fn validate_match( | 283 | fn validate_match( |