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 _;
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
30pub(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 Some(®ion.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 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
94pub(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
144pub(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 if strukt.is_unstable(db) || !strukt.is_visible_from(db, module) {
175 return None;
176 }
177
178 let generics = GenericDef::from(strukt);
179
180 if !generics.lifetime_params(db).is_empty() {
183 return None;
184 }
185
186 if ty.contains_unknown() {
187 return None;
188 }
189
190 if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
192 return None;
193 }
194 let fields = strukt.fields(db);
195 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 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 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 if enum_.is_unstable(db) || !enum_.is_visible_from(db, module) {
226 return None;
227 }
228
229 let generics = GenericDef::from(enum_);
230 if !generics.lifetime_params(db).is_empty() {
233 return None;
234 }
235
236 if ty.contains_unknown() {
237 return None;
238 }
239
240 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 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 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
292pub(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 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 if !generics.lifetime_params(db).is_empty() {
327 return None;
328 }
329
330 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 if non_default_type_params_len > 0 {
341 return None;
342 }
343
344 let generic_params = lookup
345 .iter_types()
346 .collect::<Vec<_>>() .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 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 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 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 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 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
421pub(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 if !fn_generics.lifetime_params(db).is_empty()
464 || !imp_generics.lifetime_params(db).is_empty()
465 {
466 return None;
467 }
468
469 if !it.has_self_param(db) {
471 return None;
472 }
473
474 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 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 if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr()
491 {
492 return None;
493 }
494
495 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 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 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
539pub(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
580pub(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
615pub(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 if !fn_generics.lifetime_params(db).is_empty()
658 || !imp_generics.lifetime_params(db).is_empty()
659 {
660 return None;
661 }
662
663 if it.has_self_param(db) {
665 return None;
666 }
667
668 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 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 if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr()
685 {
686 return None;
687 }
688
689 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 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
717pub(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 if ty.contains_unknown() {
747 return None;
748 }
749
750 if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
752 return None;
753 }
754
755 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}