Skip to main content

hir/term_search/
tactics.rs

1//! Tactics for term search
2//!
3//! All the tactics take following arguments
4//! * `ctx` - Context for the term search
5//! * `defs` - Set of items in scope at term search target location
6//! * `lookup` - Lookup table for types
7//! * `should_continue` - Function that indicates when to stop iterating
8//!
9//! And they return iterator that yields type trees that unify with the `goal` type.
10
11use std::iter;
12
13use hir_ty::{db::HirDatabase, mir::BorrowKind};
14use itertools::Itertools;
15use rustc_hash::FxHashSet;
16
17use crate::{
18    Adt, AssocItem, BuiltinType, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl,
19    ModuleDef, ScopeDef, Type, TypeParam, term_search::Expr,
20};
21
22use super::{LookupTable, NewTypesKey, TermSearchCtx};
23
24/// # Trivial tactic
25///
26/// Attempts to fulfill the goal by trying items in scope
27/// Also works as a starting point to move all items in scope to lookup table.
28///
29/// # Arguments
30/// * `ctx` - Context for the term search
31/// * `defs` - Set of items in scope at term search target location
32/// * `lookup` - Lookup table for types
33///
34/// Returns iterator that yields elements that unify with `goal`.
35///
36/// _Note that there is no use of calling this tactic in every iteration as the output does not
37/// depend on the current state of `lookup`_
38pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>(
39    ctx: &'a TermSearchCtx<'_, 'db, DB>,
40    defs: &'a FxHashSet<ScopeDef>,
41    lookup: &'lt mut LookupTable<'db>,
42) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
43    let db = ctx.sema.db;
44    defs.iter().filter_map(|def| {
45        let expr = match def {
46            ScopeDef::ModuleDef(ModuleDef::Const(it)) => Some(Expr::Const(*it)),
47            ScopeDef::ModuleDef(ModuleDef::Static(it)) => Some(Expr::Static(*it)),
48            ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(Expr::ConstParam(*it)),
49            ScopeDef::Local(it) => {
50                if ctx.config.enable_borrowcheck {
51                    let borrowck = db.borrowck(it.parent_infer).ok()?;
52
53                    let invalid = borrowck.iter().any(|b| {
54                        let mir_body = b.mir_body(ctx.sema.db);
55                        b.partially_moved.iter().any(|moved| {
56                            Some(&moved.local) == mir_body.binding_locals.get(it.binding_id)
57                        }) || b.borrow_regions.iter().any(|region| {
58                            // Shared borrows are fine
59                            Some(&region.local) == mir_body.binding_locals.get(it.binding_id)
60                                && region.kind != BorrowKind::Shared
61                        })
62                    });
63
64                    if invalid {
65                        return None;
66                    }
67                }
68
69                Some(Expr::Local(*it))
70            }
71            _ => None,
72        }?;
73
74        let ty = expr.ty(db);
75        lookup.insert(ty.clone(), std::iter::once(expr.clone()));
76
77        // Don't suggest local references as they are not valid for return
78        if matches!(expr, Expr::Local(_))
79            && ty.contains_reference(db)
80            && ctx.config.enable_borrowcheck
81        {
82            return None;
83        }
84
85        ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr)
86    })
87}
88
89/// # Associated constant tactic
90///
91/// Attempts to fulfill the goal by trying constants defined as associated items.
92/// Only considers them on types that are in scope.
93///
94/// # Arguments
95/// * `ctx` - Context for the term search
96/// * `defs` - Set of items in scope at term search target location
97/// * `lookup` - Lookup table for types
98///
99/// Returns iterator that yields elements that unify with `goal`.
100///
101/// _Note that there is no use of calling this tactic in every iteration as the output does not
102/// depend on the current state of `lookup`_
103pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>(
104    ctx: &'a TermSearchCtx<'_, 'db, DB>,
105    defs: &'a FxHashSet<ScopeDef>,
106    lookup: &'lt mut LookupTable<'db>,
107) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
108    let db = ctx.sema.db;
109    let module = ctx.scope.module();
110
111    defs.iter()
112        .filter_map(|def| match def {
113            ScopeDef::ModuleDef(ModuleDef::Adt(it)) => Some(it),
114            _ => None,
115        })
116        .flat_map(|it| Impl::all_for_type(db, it.ty(db)))
117        .filter(|it| !it.is_unsafe(db))
118        .flat_map(|it| it.items(db))
119        .filter(move |it| it.is_visible_from(db, module))
120        .filter_map(AssocItem::as_const)
121        .filter_map(|it| {
122            if it.attrs(db).is_unstable() {
123                return None;
124            }
125
126            let expr = Expr::Const(it);
127            let ty = it.ty(db);
128
129            if ty.contains_unknown() {
130                return None;
131            }
132
133            lookup.insert(ty.clone(), std::iter::once(expr.clone()));
134
135            ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr)
136        })
137}
138
139/// # Data constructor tactic
140///
141/// Attempts different data constructors for enums and structs in scope
142///
143/// Updates lookup by new types reached and returns iterator that yields
144/// elements that unify with `goal`.
145///
146/// # Arguments
147/// * `ctx` - Context for the term search
148/// * `defs` - Set of items in scope at term search target location
149/// * `lookup` - Lookup table for types
150/// * `should_continue` - Function that indicates when to stop iterating
151pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>(
152    ctx: &'a TermSearchCtx<'_, 'db, DB>,
153    _defs: &'a FxHashSet<ScopeDef>,
154    lookup: &'lt mut LookupTable<'db>,
155    should_continue: &'a dyn std::ops::Fn() -> bool,
156) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
157    let db = ctx.sema.db;
158    let module = ctx.scope.module();
159    lookup
160        .types_wishlist()
161        .clone()
162        .into_iter()
163        .chain(iter::once(ctx.goal.clone()))
164        .filter_map(|ty| ty.as_adt().map(|adt| (adt, ty)))
165        .filter(|_| should_continue())
166        .filter_map(move |(adt, ty)| match adt {
167            Adt::Struct(strukt) => {
168                // Ignore unstable or not visible
169                if strukt.is_unstable(db) || !strukt.is_visible_from(db, module) {
170                    return None;
171                }
172
173                let generics = GenericDef::from(strukt);
174
175                // We currently do not check lifetime bounds so ignore all types that have something to do
176                // with them
177                if !generics.lifetime_params(db).is_empty() {
178                    return None;
179                }
180
181                if ty.contains_unknown() {
182                    return None;
183                }
184
185                // Ignore types that have something to do with lifetimes
186                if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
187                    return None;
188                }
189                let fields = strukt.fields(db);
190                // Check if all fields are visible, otherwise we cannot fill them
191                if fields.iter().any(|it| !it.is_visible_from(db, module)) {
192                    return None;
193                }
194
195                let generics: Vec<_> = ty.type_arguments().collect();
196
197                // Early exit if some param cannot be filled from lookup
198                let param_exprs: Vec<Vec<Expr<'_>>> = fields
199                    .into_iter()
200                    .map(|field| {
201                        lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned()))
202                    })
203                    .collect::<Option<_>>()?;
204
205                // Note that we need special case for 0 param constructors because of multi cartesian
206                // product
207                let exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
208                    vec![Expr::Struct { strukt, generics, params: Vec::new() }]
209                } else {
210                    param_exprs
211                        .into_iter()
212                        .multi_cartesian_product()
213                        .map(|params| Expr::Struct { strukt, generics: generics.clone(), params })
214                        .collect()
215                };
216
217                lookup.insert(ty.clone(), exprs.iter().cloned());
218                Some((ty, exprs))
219            }
220            Adt::Enum(enum_) => {
221                // Ignore unstable or not visible
222                if enum_.is_unstable(db) || !enum_.is_visible_from(db, module) {
223                    return None;
224                }
225
226                let generics = GenericDef::from(enum_);
227                // We currently do not check lifetime bounds so ignore all types that have something to do
228                // with them
229                if !generics.lifetime_params(db).is_empty() {
230                    return None;
231                }
232
233                if ty.contains_unknown() {
234                    return None;
235                }
236
237                // Ignore types that have something to do with lifetimes
238                if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
239                    return None;
240                }
241
242                let generics: Vec<_> = ty.type_arguments().collect();
243                let exprs = enum_
244                    .variants(db)
245                    .into_iter()
246                    .filter_map(|variant| {
247                        // Early exit if some param cannot be filled from lookup
248                        let param_exprs: Vec<Vec<Expr<'_>>> = variant
249                            .fields(db)
250                            .into_iter()
251                            .map(|field| {
252                                lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned()))
253                            })
254                            .collect::<Option<_>>()?;
255
256                        // Note that we need special case for 0 param constructors because of multi cartesian
257                        // product
258                        let variant_exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
259                            vec![Expr::Variant {
260                                variant,
261                                generics: generics.clone(),
262                                params: Vec::new(),
263                            }]
264                        } else {
265                            param_exprs
266                                .into_iter()
267                                .multi_cartesian_product()
268                                .map(|params| Expr::Variant {
269                                    variant,
270                                    generics: generics.clone(),
271                                    params,
272                                })
273                                .collect()
274                        };
275                        lookup.insert(ty.clone(), variant_exprs.iter().cloned());
276                        Some(variant_exprs)
277                    })
278                    .flatten()
279                    .collect();
280
281                Some((ty, exprs))
282            }
283            Adt::Union(_) => None,
284        })
285        .filter_map(|(ty, exprs)| {
286            ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
287        })
288        .flatten()
289}
290
291/// # Free function tactic
292///
293/// Attempts to call different functions in scope with parameters from lookup table.
294/// Functions that include generics are not used for performance reasons.
295///
296/// Updates lookup by new types reached and returns iterator that yields
297/// elements that unify with `goal`.
298///
299/// # Arguments
300/// * `ctx` - Context for the term search
301/// * `defs` - Set of items in scope at term search target location
302/// * `lookup` - Lookup table for types
303/// * `should_continue` - Function that indicates when to stop iterating
304pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>(
305    ctx: &'a TermSearchCtx<'_, 'db, DB>,
306    defs: &'a FxHashSet<ScopeDef>,
307    lookup: &'lt mut LookupTable<'db>,
308    should_continue: &'a dyn std::ops::Fn() -> bool,
309) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
310    let db = ctx.sema.db;
311    let module = ctx.scope.module();
312    defs.iter()
313        .filter_map(move |def| match def {
314            ScopeDef::ModuleDef(ModuleDef::Function(it)) => {
315                let generics = GenericDef::from(*it);
316
317                // Ignore const params for now
318                let type_params = generics
319                    .type_or_const_params(db)
320                    .into_iter()
321                    .map(|it| it.as_type_param(db))
322                    .collect::<Option<Vec<TypeParam>>>()?;
323
324                // Ignore lifetimes as we do not check them
325                if !generics.lifetime_params(db).is_empty() {
326                    return None;
327                }
328
329                // Only account for stable type parameters for now, unstable params can be default
330                // tho, for example in `Box<T, #[unstable] A: Allocator>`
331                if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) {
332                    return None;
333                }
334
335                let non_default_type_params_len =
336                    type_params.iter().filter(|it| it.default(db).is_none()).count();
337
338                // Ignore bigger number of generics for now as they kill the performance
339                if non_default_type_params_len > 0 {
340                    return None;
341                }
342
343                let generic_params = lookup
344                    .iter_types()
345                    .collect::<Vec<_>>() // Force take ownership
346                    .into_iter()
347                    .permutations(non_default_type_params_len);
348
349                let exprs: Vec<_> = generic_params
350                    .filter(|_| should_continue())
351                    .filter_map(|generics| {
352                        // Insert default type params
353                        let mut g = generics.into_iter();
354                        let generics: Vec<_> = type_params
355                            .iter()
356                            .map(|it| match it.default(db) {
357                                Some(ty) => Some(ty),
358                                None => {
359                                    let generic = g.next().expect("Missing type param");
360                                    // Filter out generics that do not unify due to trait bounds
361                                    it.ty(db).could_unify_with(db, &generic).then_some(generic)
362                                }
363                            })
364                            .collect::<Option<_>>()?;
365
366                        let ret_ty = it.ret_type(db).instantiate(generics.iter().cloned());
367                        // Filter out private and unsafe functions
368                        if !it.is_visible_from(db, module)
369                            || it.is_unsafe_to_call(
370                                db,
371                                None,
372                                crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
373                            )
374                            || it.is_unstable(db)
375                            || ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
376                            || ret_ty.is_raw_ptr()
377                        {
378                            return None;
379                        }
380
381                        // Early exit if some param cannot be filled from lookup
382                        let param_exprs: Vec<Vec<Expr<'_>>> = it
383                            .params_without_self(db)
384                            .into_iter()
385                            .map(|field| {
386                                let ty = &field.ty().instantiate(&generics);
387                                match ty.is_mutable_reference() {
388                                    true => None,
389                                    false => lookup.find_autoref(db, ty),
390                                }
391                            })
392                            .collect::<Option<_>>()?;
393
394                        // Note that we need special case for 0 param constructors because of multi cartesian
395                        // product
396                        let fn_exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
397                            vec![Expr::Function { func: *it, generics, params: Vec::new() }]
398                        } else {
399                            param_exprs
400                                .into_iter()
401                                .multi_cartesian_product()
402                                .map(|params| Expr::Function {
403                                    func: *it,
404                                    generics: generics.clone(),
405
406                                    params,
407                                })
408                                .collect()
409                        };
410
411                        lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned());
412                        Some((ret_ty, fn_exprs))
413                    })
414                    .collect();
415                Some(exprs)
416            }
417            _ => None,
418        })
419        .flatten()
420        .filter_map(|(ty, exprs)| {
421            ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
422        })
423        .flatten()
424}
425
426/// # Impl method tactic
427///
428/// Attempts to call methods on types from lookup table.
429/// This includes both functions from direct impl blocks as well as functions from traits.
430/// Methods defined in impl blocks that are generic and methods that are themselves have
431/// generics are ignored for performance reasons.
432///
433/// Updates lookup by new types reached and returns iterator that yields
434/// elements that unify with `goal`.
435///
436/// # Arguments
437/// * `ctx` - Context for the term search
438/// * `defs` - Set of items in scope at term search target location
439/// * `lookup` - Lookup table for types
440/// * `should_continue` - Function that indicates when to stop iterating
441pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>(
442    ctx: &'a TermSearchCtx<'_, 'db, DB>,
443    _defs: &'a FxHashSet<ScopeDef>,
444    lookup: &'lt mut LookupTable<'db>,
445    should_continue: &'a dyn std::ops::Fn() -> bool,
446) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
447    let db = ctx.sema.db;
448    let module = ctx.scope.module();
449    lookup
450        .new_types(NewTypesKey::ImplMethod)
451        .into_iter()
452        .filter(|ty| !ty.type_arguments().any(|it| it.contains_unknown()))
453        .filter(|_| should_continue())
454        .flat_map(|ty| {
455            Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
456        })
457        .flat_map(|(ty, imp)| imp.items(db).into_iter().map(move |item| (imp, ty.clone(), item)))
458        .filter_map(|(imp, ty, it)| match it {
459            AssocItem::Function(f) => Some((imp, ty, f)),
460            _ => None,
461        })
462        .filter(|_| should_continue())
463        .filter_map(move |(imp, ty, it)| {
464            let fn_generics = GenericDef::from(it);
465            let imp_generics = GenericDef::from(imp);
466
467            // Ignore all functions that have something to do with lifetimes as we don't check them
468            if !fn_generics.lifetime_params(db).is_empty()
469                || !imp_generics.lifetime_params(db).is_empty()
470            {
471                return None;
472            }
473
474            // Ignore functions without self param
475            if !it.has_self_param(db) {
476                return None;
477            }
478
479            // Filter out private and unsafe functions
480            if !it.is_visible_from(db, module)
481                || it.is_unsafe_to_call(
482                    db,
483                    None,
484                    crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
485                )
486                || it.is_unstable(db)
487            {
488                return None;
489            }
490
491            // Ignore functions with generics for now as they kill the performance
492            // Also checking bounds for generics is problematic
493            if !fn_generics.type_or_const_params(db).is_empty() {
494                return None;
495            }
496
497            let ret_ty = it.ret_type(db).instantiate(ty.type_arguments());
498            // Filter out functions that return references
499            if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr()
500            {
501                return None;
502            }
503
504            // Ignore functions that do not change the type
505            if ty.instantiate_with_errors().could_unify_with_deeply(db, &ret_ty) {
506                return None;
507            }
508
509            let self_ty =
510                it.self_param(db).expect("No self param").ty(db).instantiate(ty.type_arguments());
511
512            // Ignore functions that have different self type
513            if !self_ty.autoderef(db).any(|s_ty| ty == s_ty) {
514                return None;
515            }
516
517            let target_type_exprs = lookup.find(db, &ty).expect("Type not in lookup");
518
519            // Early exit if some param cannot be filled from lookup
520            let param_exprs: Vec<Vec<Expr<'_>>> = it
521                .params_without_self(db)
522                .into_iter()
523                .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments())))
524                .collect::<Option<_>>()?;
525
526            let generics: Vec<_> = ty.type_arguments().collect();
527            let fn_exprs: Vec<Expr<'_>> = std::iter::once(target_type_exprs)
528                .chain(param_exprs)
529                .multi_cartesian_product()
530                .map(|params| {
531                    let mut params = params.into_iter();
532                    let target = Box::new(params.next().unwrap());
533                    Expr::Method {
534                        func: it,
535                        generics: generics.clone(),
536                        target,
537                        params: params.collect(),
538                    }
539                })
540                .collect();
541
542            Some((ret_ty, fn_exprs))
543        })
544        .filter_map(|(ty, exprs)| {
545            ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
546        })
547        .flatten()
548}
549
550/// # Struct projection tactic
551///
552/// Attempts different struct fields (`foo.bar.baz`)
553///
554/// Updates lookup by new types reached and returns iterator that yields
555/// elements that unify with `goal`.
556///
557/// # Arguments
558/// * `ctx` - Context for the term search
559/// * `defs` - Set of items in scope at term search target location
560/// * `lookup` - Lookup table for types
561/// * `should_continue` - Function that indicates when to stop iterating
562pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>(
563    ctx: &'a TermSearchCtx<'_, 'db, DB>,
564    _defs: &'a FxHashSet<ScopeDef>,
565    lookup: &'lt mut LookupTable<'db>,
566    should_continue: &'a dyn std::ops::Fn() -> bool,
567) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
568    let db = ctx.sema.db;
569    let module = ctx.scope.module();
570    lookup
571        .new_types(NewTypesKey::StructProjection)
572        .into_iter()
573        .map(|ty| (ty.clone(), lookup.find(db, &ty).expect("Expr not in lookup")))
574        .filter(|_| should_continue())
575        .flat_map(move |(ty, targets)| {
576            ty.fields(db).into_iter().filter_map(move |(field, filed_ty)| {
577                if !field.is_visible_from(db, module) {
578                    return None;
579                }
580                let exprs = targets
581                    .clone()
582                    .into_iter()
583                    .map(move |target| Expr::Field { field, expr: Box::new(target) });
584                Some((filed_ty, exprs))
585            })
586        })
587        .filter_map(|(ty, exprs)| {
588            ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
589        })
590        .flatten()
591}
592
593/// # Famous types tactic
594///
595/// Attempts different values of well known types such as `true` or `false`.
596///
597/// Updates lookup by new types reached and returns iterator that yields
598/// elements that unify with `goal`.
599///
600/// _Note that there is no point of calling it iteratively as the output is always the same_
601///
602/// # Arguments
603/// * `ctx` - Context for the term search
604/// * `defs` - Set of items in scope at term search target location
605/// * `lookup` - Lookup table for types
606pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>(
607    ctx: &'a TermSearchCtx<'_, 'db, DB>,
608    _defs: &'a FxHashSet<ScopeDef>,
609    lookup: &'lt mut LookupTable<'db>,
610) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
611    let db = ctx.sema.db;
612    let bool_ty = BuiltinType::bool().ty(db);
613    let unit_ty = Type::new_unit();
614    [
615        Expr::FamousType { ty: bool_ty.clone(), value: "true" },
616        Expr::FamousType { ty: bool_ty, value: "false" },
617        Expr::FamousType { ty: unit_ty, value: "()" },
618    ]
619    .into_iter()
620    .inspect(|exprs| {
621        lookup.insert(exprs.ty(db), std::iter::once(exprs.clone()));
622    })
623    .filter(|expr| expr.ty(db).instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal))
624}
625
626/// # Impl static method (without self type) tactic
627///
628/// Attempts different functions from impl blocks that take no self parameter.
629///
630/// Updates lookup by new types reached and returns iterator that yields
631/// elements that unify with `goal`.
632///
633/// # Arguments
634/// * `ctx` - Context for the term search
635/// * `defs` - Set of items in scope at term search target location
636/// * `lookup` - Lookup table for types
637/// * `should_continue` - Function that indicates when to stop iterating
638pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>(
639    ctx: &'a TermSearchCtx<'_, 'db, DB>,
640    _defs: &'a FxHashSet<ScopeDef>,
641    lookup: &'lt mut LookupTable<'db>,
642    should_continue: &'a dyn std::ops::Fn() -> bool,
643) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
644    let db = ctx.sema.db;
645    let module = ctx.scope.module();
646    lookup
647        .types_wishlist()
648        .clone()
649        .into_iter()
650        .chain(iter::once(ctx.goal.clone()))
651        .filter(|ty| !ty.type_arguments().any(|it| it.contains_unknown()))
652        .filter(|_| should_continue())
653        .flat_map(|ty| {
654            Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
655        })
656        .filter(|(_, imp)| !imp.is_unsafe(db))
657        .flat_map(|(ty, imp)| imp.items(db).into_iter().map(move |item| (imp, ty.clone(), item)))
658        .filter_map(|(imp, ty, it)| match it {
659            AssocItem::Function(f) => Some((imp, ty, f)),
660            _ => None,
661        })
662        .filter(|_| should_continue())
663        .filter_map(move |(imp, ty, it)| {
664            let fn_generics = GenericDef::from(it);
665            let imp_generics = GenericDef::from(imp);
666
667            // Ignore all functions that have something to do with lifetimes as we don't check them
668            if !fn_generics.lifetime_params(db).is_empty()
669                || !imp_generics.lifetime_params(db).is_empty()
670            {
671                return None;
672            }
673
674            // Ignore functions with self param
675            if it.has_self_param(db) {
676                return None;
677            }
678
679            // Filter out private and unsafe functions
680            if !it.is_visible_from(db, module)
681                || it.is_unsafe_to_call(
682                    db,
683                    None,
684                    crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
685                )
686                || it.is_unstable(db)
687            {
688                return None;
689            }
690
691            // Ignore functions with generics for now as they kill the performance
692            // Also checking bounds for generics is problematic
693            if !fn_generics.type_or_const_params(db).is_empty() {
694                return None;
695            }
696
697            let ret_ty = it.ret_type(db).instantiate(ty.type_arguments());
698            // Filter out functions that return references
699            if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr()
700            {
701                return None;
702            }
703
704            // Early exit if some param cannot be filled from lookup
705            let param_exprs: Vec<Vec<Expr<'_>>> = it
706                .params_without_self(db)
707                .into_iter()
708                .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments())))
709                .collect::<Option<_>>()?;
710
711            // Note that we need special case for 0 param constructors because of multi cartesian
712            // product
713            let generics = ty.type_arguments().collect();
714            let fn_exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
715                vec![Expr::Function { func: it, generics, params: Vec::new() }]
716            } else {
717                param_exprs
718                    .into_iter()
719                    .multi_cartesian_product()
720                    .map(|params| Expr::Function { func: it, generics: generics.clone(), params })
721                    .collect()
722            };
723
724            lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned());
725
726            Some((ret_ty, fn_exprs))
727        })
728        .filter_map(|(ty, exprs)| {
729            ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
730        })
731        .flatten()
732}
733
734/// # Make tuple tactic
735///
736/// Attempts to create tuple types if any are listed in types wishlist
737///
738/// Updates lookup by new types reached and returns iterator that yields
739/// elements that unify with `goal`.
740///
741/// # Arguments
742/// * `ctx` - Context for the term search
743/// * `defs` - Set of items in scope at term search target location
744/// * `lookup` - Lookup table for types
745/// * `should_continue` - Function that indicates when to stop iterating
746pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>(
747    ctx: &'a TermSearchCtx<'_, 'db, DB>,
748    _defs: &'a FxHashSet<ScopeDef>,
749    lookup: &'lt mut LookupTable<'db>,
750    should_continue: &'a dyn std::ops::Fn() -> bool,
751) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
752    let db = ctx.sema.db;
753
754    lookup
755        .types_wishlist()
756        .clone()
757        .into_iter()
758        .filter(|_| should_continue())
759        .filter(|ty| ty.is_tuple())
760        .filter_map(move |ty| {
761            // Double check to not contain unknown
762            if ty.contains_unknown() {
763                return None;
764            }
765
766            // Ignore types that have something to do with lifetimes
767            if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
768                return None;
769            }
770
771            // Early exit if some param cannot be filled from lookup
772            let param_exprs: Vec<Vec<Expr<'db>>> =
773                ty.type_arguments().map(|field| lookup.find(db, &field)).collect::<Option<_>>()?;
774
775            let exprs: Vec<Expr<'db>> = param_exprs
776                .into_iter()
777                .multi_cartesian_product()
778                .filter(|_| should_continue())
779                .map(|params| {
780                    let tys: Vec<Type<'_>> = params.iter().map(|it| it.ty(db)).collect();
781                    let tuple_ty = Type::new_tuple(db, &tys);
782
783                    let expr = Expr::Tuple { ty: tuple_ty.clone(), params };
784                    lookup.insert(tuple_ty, iter::once(expr.clone()));
785                    expr
786                })
787                .collect();
788
789            Some(exprs)
790        })
791        .flatten()
792        .filter_map(|expr| {
793            expr.ty(db)
794                .instantiate_with_errors()
795                .could_unify_with_deeply(db, &ctx.goal)
796                .then_some(expr)
797        })
798}