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