Skip to content

Commit b14af5c

Browse files
bors[bot]yue4u
andauthored
Merge #11115
11115: internal: refactor: avoid separate traversal in replace filter map next with find map r=Veykril a=rainy-me fix: #7428 Co-authored-by: rainy-me <[email protected]>
2 parents 1ba9a92 + d77d323 commit b14af5c

File tree

1 file changed

+84
-59
lines changed
  • crates/hir_ty/src/diagnostics

1 file changed

+84
-59
lines changed

crates/hir_ty/src/diagnostics/expr.rs

Lines changed: 84 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,8 @@ impl ExprValidator {
8181
}
8282

8383
fn validate_body(&mut self, db: &dyn HirDatabase) {
84-
self.check_for_filter_map_next(db);
85-
8684
let body = db.body(self.owner);
85+
let mut filter_map_next_checker = None;
8786

8887
for (id, expr) in body.exprs.iter() {
8988
if let Some((variant, missed_fields, true)) =
@@ -101,7 +100,7 @@ impl ExprValidator {
101100
self.validate_match(id, *expr, arms, db, self.infer.clone());
102101
}
103102
Expr::Call { .. } | Expr::MethodCall { .. } => {
104-
self.validate_call(db, id, expr);
103+
self.validate_call(db, id, expr, &mut filter_map_next_checker);
105104
}
106105
_ => {}
107106
}
@@ -143,58 +142,13 @@ impl ExprValidator {
143142
});
144143
}
145144

146-
fn check_for_filter_map_next(&mut self, db: &dyn HirDatabase) {
147-
// Find the FunctionIds for Iterator::filter_map and Iterator::next
148-
let iterator_path = path![core::iter::Iterator];
149-
let resolver = self.owner.resolver(db.upcast());
150-
let iterator_trait_id = match resolver.resolve_known_trait(db.upcast(), &iterator_path) {
151-
Some(id) => id,
152-
None => return,
153-
};
154-
let iterator_trait_items = &db.trait_data(iterator_trait_id).items;
155-
let filter_map_function_id =
156-
match iterator_trait_items.iter().find(|item| item.0 == name![filter_map]) {
157-
Some((_, AssocItemId::FunctionId(id))) => id,
158-
_ => return,
159-
};
160-
let next_function_id = match iterator_trait_items.iter().find(|item| item.0 == name![next])
161-
{
162-
Some((_, AssocItemId::FunctionId(id))) => id,
163-
_ => return,
164-
};
165-
166-
// Search function body for instances of .filter_map(..).next()
167-
let body = db.body(self.owner);
168-
let mut prev = None;
169-
for (id, expr) in body.exprs.iter() {
170-
if let Expr::MethodCall { receiver, .. } = expr {
171-
let function_id = match self.infer.method_resolution(id) {
172-
Some((id, _)) => id,
173-
None => continue,
174-
};
175-
176-
if function_id == *filter_map_function_id {
177-
prev = Some(id);
178-
continue;
179-
}
180-
181-
if function_id == *next_function_id {
182-
if let Some(filter_map_id) = prev {
183-
if *receiver == filter_map_id {
184-
self.diagnostics.push(
185-
BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap {
186-
method_call_expr: id,
187-
},
188-
);
189-
}
190-
}
191-
}
192-
}
193-
prev = None;
194-
}
195-
}
196-
197-
fn validate_call(&mut self, db: &dyn HirDatabase, call_id: ExprId, expr: &Expr) {
145+
fn validate_call(
146+
&mut self,
147+
db: &dyn HirDatabase,
148+
call_id: ExprId,
149+
expr: &Expr,
150+
filter_map_next_checker: &mut Option<FilterMapNextChecker>,
151+
) {
198152
// Check that the number of arguments matches the number of parameters.
199153

200154
// FIXME: Due to shortcomings in the current type system implementation, only emit this
@@ -214,6 +168,24 @@ impl ExprValidator {
214168
(sig, args.len())
215169
}
216170
Expr::MethodCall { receiver, args, .. } => {
171+
let (callee, subst) = match self.infer.method_resolution(call_id) {
172+
Some(it) => it,
173+
None => return,
174+
};
175+
176+
if filter_map_next_checker
177+
.get_or_insert_with(|| {
178+
FilterMapNextChecker::new(&self.owner.resolver(db.upcast()), db)
179+
})
180+
.check(call_id, receiver, &callee)
181+
.is_some()
182+
{
183+
self.diagnostics.push(
184+
BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap {
185+
method_call_expr: call_id,
186+
},
187+
);
188+
}
217189
let receiver = &self.infer.type_of_expr[*receiver];
218190
if receiver.strip_references().is_unknown() {
219191
// if the receiver is of unknown type, it's very likely we
@@ -222,10 +194,6 @@ impl ExprValidator {
222194
return;
223195
}
224196

225-
let (callee, subst) = match self.infer.method_resolution(call_id) {
226-
Some(it) => it,
227-
None => return,
228-
};
229197
let sig = db.callable_item_signature(callee.into()).substitute(Interner, &subst);
230198

231199
(sig, args.len() + 1)
@@ -424,6 +392,63 @@ impl ExprValidator {
424392
}
425393
}
426394

395+
struct FilterMapNextChecker {
396+
filter_map_function_id: Option<hir_def::FunctionId>,
397+
next_function_id: Option<hir_def::FunctionId>,
398+
prev_filter_map_expr_id: Option<ExprId>,
399+
}
400+
401+
impl FilterMapNextChecker {
402+
fn new(resolver: &hir_def::resolver::Resolver, db: &dyn HirDatabase) -> Self {
403+
// Find and store the FunctionIds for Iterator::filter_map and Iterator::next
404+
let iterator_path = path![core::iter::Iterator];
405+
let mut filter_map_function_id = None;
406+
let mut next_function_id = None;
407+
408+
if let Some(iterator_trait_id) = resolver.resolve_known_trait(db.upcast(), &iterator_path) {
409+
let iterator_trait_items = &db.trait_data(iterator_trait_id).items;
410+
for item in iterator_trait_items.iter() {
411+
if let (name, AssocItemId::FunctionId(id)) = item {
412+
if *name == name![filter_map] {
413+
filter_map_function_id = Some(*id);
414+
}
415+
if *name == name![next] {
416+
next_function_id = Some(*id);
417+
}
418+
}
419+
if filter_map_function_id.is_some() && next_function_id.is_some() {
420+
break;
421+
}
422+
}
423+
}
424+
Self { filter_map_function_id, next_function_id, prev_filter_map_expr_id: None }
425+
}
426+
427+
// check for instances of .filter_map(..).next()
428+
fn check(
429+
&mut self,
430+
current_expr_id: ExprId,
431+
receiver_expr_id: &ExprId,
432+
function_id: &hir_def::FunctionId,
433+
) -> Option<()> {
434+
if *function_id == self.filter_map_function_id? {
435+
self.prev_filter_map_expr_id = Some(current_expr_id);
436+
return None;
437+
}
438+
439+
if *function_id == self.next_function_id? {
440+
if let Some(prev_filter_map_expr_id) = self.prev_filter_map_expr_id {
441+
if *receiver_expr_id == prev_filter_map_expr_id {
442+
return Some(());
443+
}
444+
}
445+
}
446+
447+
self.prev_filter_map_expr_id = None;
448+
None
449+
}
450+
}
451+
427452
pub fn record_literal_missing_fields(
428453
db: &dyn HirDatabase,
429454
infer: &InferenceResult,

0 commit comments

Comments
 (0)