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