1use 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
29pub(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 Some(®ion.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 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
93pub(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
143pub(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 if strukt.is_unstable(db) || !strukt.is_visible_from(db, module) {
174 return None;
175 }
176
177 let generics = GenericDef::from(strukt);
178
179 if !generics.lifetime_params(db).is_empty() {
182 return None;
183 }
184
185 if ty.contains_unknown() {
186 return None;
187 }
188
189 if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
191 return None;
192 }
193 let fields = strukt.fields(db);
194 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 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 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 if enum_.is_unstable(db) || !enum_.is_visible_from(db, module) {
225 return None;
226 }
227
228 let generics = GenericDef::from(enum_);
229 if !generics.lifetime_params(db).is_empty() {
232 return None;
233 }
234
235 if ty.contains_unknown() {
236 return None;
237 }
238
239 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 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 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
291pub(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 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 if !generics.lifetime_params(db).is_empty() {
326 return None;
327 }
328
329 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 if non_default_type_params_len > 0 {
340 return None;
341 }
342
343 let generic_params = lookup
344 .iter_types()
345 .collect::<Vec<_>>() .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 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 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 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 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 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
424pub(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 if !fn_generics.lifetime_params(db).is_empty()
467 || !imp_generics.lifetime_params(db).is_empty()
468 {
469 return None;
470 }
471
472 if !it.has_self_param(db) {
474 return None;
475 }
476
477 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 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 if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr()
498 {
499 return None;
500 }
501
502 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 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 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
546pub(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
587pub(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
622pub(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 if !fn_generics.lifetime_params(db).is_empty()
665 || !imp_generics.lifetime_params(db).is_empty()
666 {
667 return None;
668 }
669
670 if it.has_self_param(db) {
672 return None;
673 }
674
675 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 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 if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr()
696 {
697 return None;
698 }
699
700 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 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
728pub(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 if ty.contains_unknown() {
758 return None;
759 }
760
761 if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
763 return None;
764 }
765
766 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}