1use std::iter;
12
13use hir_ty::db::HirDatabase;
14use itertools::Itertools;
15use rustc_hash::FxHashSet;
16
17use crate::{
18 Adt, AssocItem, BuiltinType, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl,
19 ModuleDef, ScopeDef, Type, TypeParam, term_search::Expr,
20};
21
22use super::{LookupTable, NewTypesKey, TermSearchCtx};
23
24pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>(
39 ctx: &'a TermSearchCtx<'_, 'db, DB>,
40 defs: &'a FxHashSet<ScopeDef<'db>>,
41 lookup: &'lt mut LookupTable<'db>,
42) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
43 let db = ctx.sema.db;
44 defs.iter().filter_map(|def| {
45 let expr = match def {
46 ScopeDef::ModuleDef(ModuleDef::Const(it)) => Some(Expr::Const(*it)),
47 ScopeDef::ModuleDef(ModuleDef::Static(it)) => Some(Expr::Static(*it)),
48 ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(Expr::ConstParam(*it)),
49 ScopeDef::Local(it) => Some(Expr::Local(*it)),
50 _ => None,
51 }?;
52
53 let ty = expr.ty(db);
54 if ty.contains_unknown() {
55 return None;
56 }
57
58 lookup.insert(ty.clone(), std::iter::once(expr.clone()));
59
60 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr)
61 })
62}
63
64pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>(
79 ctx: &'a TermSearchCtx<'_, 'db, DB>,
80 defs: &'a FxHashSet<ScopeDef<'db>>,
81 lookup: &'lt mut LookupTable<'db>,
82) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
83 let db = ctx.sema.db;
84 let module = ctx.scope.module();
85
86 defs.iter()
87 .filter_map(|def| match def {
88 ScopeDef::ModuleDef(ModuleDef::Adt(it)) => Some(it),
89 _ => None,
90 })
91 .flat_map(|it| Impl::all_for_type(db, it.ty(db)))
92 .filter(|it| !it.is_unsafe(db))
93 .flat_map(|it| it.items(db))
94 .filter(move |it| it.is_visible_from(db, module))
95 .filter_map(AssocItem::as_const)
96 .filter_map(|it| {
97 if it.attrs(db).is_unstable() {
98 return None;
99 }
100
101 let expr = Expr::Const(it);
102 let ty = it.ty(db);
103
104 if ty.contains_unknown() {
105 return None;
106 }
107
108 lookup.insert(ty.clone(), std::iter::once(expr.clone()));
109
110 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr)
111 })
112}
113
114pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>(
127 ctx: &'a TermSearchCtx<'_, 'db, DB>,
128 _defs: &'a FxHashSet<ScopeDef<'db>>,
129 lookup: &'lt mut LookupTable<'db>,
130 should_continue: &'a dyn std::ops::Fn() -> bool,
131) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
132 let db = ctx.sema.db;
133 let module = ctx.scope.module();
134 lookup
135 .types_wishlist()
136 .clone()
137 .into_iter()
138 .chain(iter::once(ctx.goal.clone()))
139 .filter_map(|ty| ty.as_adt().map(|adt| (adt, ty)))
140 .filter(|_| should_continue())
141 .filter_map(move |(adt, ty)| match adt {
142 Adt::Struct(strukt) => {
143 if strukt.is_unstable(db) || !strukt.is_visible_from(db, module) {
145 return None;
146 }
147
148 let generics = GenericDef::from(strukt);
149
150 if !generics.lifetime_params(db).is_empty() {
153 return None;
154 }
155
156 if ty.contains_unknown() {
157 return None;
158 }
159
160 let fields = strukt.fields(db);
161 if fields.iter().any(|it| !it.is_visible_from(db, module)) {
163 return None;
164 }
165
166 let generics: Vec<_> = ty.type_arguments().collect();
167
168 let param_exprs: Vec<Vec<Expr<'_>>> = fields
170 .into_iter()
171 .map(|field| {
172 lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned()))
173 })
174 .collect::<Option<_>>()?;
175
176 let exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
179 vec![Expr::Struct { strukt, generics, params: Vec::new() }]
180 } else {
181 param_exprs
182 .into_iter()
183 .multi_cartesian_product()
184 .map(|params| Expr::Struct { strukt, generics: generics.clone(), params })
185 .collect()
186 };
187
188 lookup.insert(ty.clone(), exprs.iter().cloned());
189 Some((ty, exprs))
190 }
191 Adt::Enum(enum_) => {
192 if enum_.is_unstable(db) || !enum_.is_visible_from(db, module) {
194 return None;
195 }
196
197 let generics = GenericDef::from(enum_);
198 if !generics.lifetime_params(db).is_empty() {
201 return None;
202 }
203
204 if ty.contains_unknown() {
205 return None;
206 }
207
208 let generics: Vec<_> = ty.type_arguments().collect();
209 let exprs = enum_
210 .variants(db)
211 .into_iter()
212 .filter_map(|variant| {
213 let param_exprs: Vec<Vec<Expr<'_>>> = variant
215 .fields(db)
216 .into_iter()
217 .map(|field| {
218 lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned()))
219 })
220 .collect::<Option<_>>()?;
221
222 let variant_exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
225 vec![Expr::Variant {
226 variant,
227 generics: generics.clone(),
228 params: Vec::new(),
229 }]
230 } else {
231 param_exprs
232 .into_iter()
233 .multi_cartesian_product()
234 .map(|params| Expr::Variant {
235 variant,
236 generics: generics.clone(),
237 params,
238 })
239 .collect()
240 };
241 lookup.insert(ty.clone(), variant_exprs.iter().cloned());
242 Some(variant_exprs)
243 })
244 .flatten()
245 .collect();
246
247 Some((ty, exprs))
248 }
249 Adt::Union(_) => None,
250 })
251 .filter_map(|(ty, exprs)| {
252 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
253 })
254 .flatten()
255}
256
257pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>(
271 ctx: &'a TermSearchCtx<'_, 'db, DB>,
272 defs: &'a FxHashSet<ScopeDef<'db>>,
273 lookup: &'lt mut LookupTable<'db>,
274 should_continue: &'a dyn std::ops::Fn() -> bool,
275) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
276 let db = ctx.sema.db;
277 let module = ctx.scope.module();
278 defs.iter()
279 .filter_map(move |def| match def {
280 ScopeDef::ModuleDef(ModuleDef::Function(it)) => {
281 let generics = GenericDef::from(*it);
282
283 let type_params = generics
285 .type_or_const_params(db)
286 .into_iter()
287 .map(|it| it.as_type_param(db))
288 .collect::<Option<Vec<TypeParam>>>()?;
289
290 if !generics.lifetime_params(db).is_empty() {
292 return None;
293 }
294
295 if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) {
298 return None;
299 }
300
301 let non_default_type_params_len =
302 type_params.iter().filter(|it| it.default(db).is_none()).count();
303
304 if non_default_type_params_len > 0 {
306 return None;
307 }
308
309 let generic_params = lookup
310 .iter_types()
311 .collect::<Vec<_>>() .into_iter()
313 .permutations(non_default_type_params_len);
314
315 let exprs: Vec<_> = generic_params
316 .filter(|_| should_continue())
317 .filter_map(|generics| {
318 let mut g = generics.into_iter();
320 let generics: Vec<_> = type_params
321 .iter()
322 .map(|it| match it.default(db) {
323 Some(ty) => Some(ty),
324 None => {
325 let generic = g.next().expect("Missing type param");
326 it.ty(db).could_unify_with(db, &generic).then_some(generic)
328 }
329 })
330 .collect::<Option<_>>()?;
331
332 let ret_ty = it.ret_type(db).instantiate(generics.iter().cloned());
333 if !it.is_visible_from(db, module)
335 || it.is_unsafe_to_call(
336 db,
337 None,
338 crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
339 )
340 || it.is_unstable(db)
341 || ret_ty.is_raw_ptr()
342 {
343 return None;
344 }
345
346 let param_exprs: Vec<Vec<Expr<'_>>> = it
348 .params_without_self(db)
349 .into_iter()
350 .map(|field| {
351 let ty = &field.ty().instantiate(&generics);
352 match ty.is_mutable_reference() {
353 true => None,
354 false => lookup.find_autoref(db, ty),
355 }
356 })
357 .collect::<Option<_>>()?;
358
359 let fn_exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
362 vec![Expr::Function { func: *it, generics, params: Vec::new() }]
363 } else {
364 param_exprs
365 .into_iter()
366 .multi_cartesian_product()
367 .map(|params| Expr::Function {
368 func: *it,
369 generics: generics.clone(),
370
371 params,
372 })
373 .collect()
374 };
375
376 lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned());
377 Some((ret_ty, fn_exprs))
378 })
379 .collect();
380 Some(exprs)
381 }
382 _ => None,
383 })
384 .flatten()
385 .filter_map(|(ty, exprs)| {
386 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
387 })
388 .flatten()
389}
390
391pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>(
407 ctx: &'a TermSearchCtx<'_, 'db, DB>,
408 _defs: &'a FxHashSet<ScopeDef<'db>>,
409 lookup: &'lt mut LookupTable<'db>,
410 should_continue: &'a dyn std::ops::Fn() -> bool,
411) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
412 let db = ctx.sema.db;
413 let module = ctx.scope.module();
414 lookup
415 .new_types(NewTypesKey::ImplMethod)
416 .into_iter()
417 .filter(|ty| !ty.type_arguments().any(|it| it.contains_unknown()))
418 .filter(|_| should_continue())
419 .flat_map(|ty| {
420 Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
421 })
422 .flat_map(|(ty, imp)| imp.items(db).into_iter().map(move |item| (imp, ty.clone(), item)))
423 .filter_map(|(imp, ty, it)| match it {
424 AssocItem::Function(f) => Some((imp, ty, f)),
425 _ => None,
426 })
427 .filter(|_| should_continue())
428 .filter_map(move |(imp, ty, it)| {
429 let fn_generics = GenericDef::from(it);
430 let imp_generics = GenericDef::from(imp);
431
432 if !fn_generics.lifetime_params(db).is_empty()
434 || !imp_generics.lifetime_params(db).is_empty()
435 {
436 return None;
437 }
438
439 if !it.has_self_param(db) {
441 return None;
442 }
443
444 if !it.is_visible_from(db, module)
446 || it.is_unsafe_to_call(
447 db,
448 None,
449 crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
450 )
451 || it.is_unstable(db)
452 {
453 return None;
454 }
455
456 if !fn_generics.type_or_const_params(db).is_empty() {
459 return None;
460 }
461
462 let ret_ty = it.ret_type(db).instantiate(ty.type_arguments());
463
464 if ty.instantiate_with_errors().could_unify_with_deeply(db, &ret_ty) {
466 return None;
467 }
468
469 let self_ty =
470 it.self_param(db).expect("No self param").ty(db).instantiate(ty.type_arguments());
471
472 if !self_ty.autoderef(db).any(|s_ty| ty == s_ty) {
474 return None;
475 }
476
477 let target_type_exprs = lookup.find(db, &ty).expect("Type not in lookup");
478
479 let param_exprs: Vec<Vec<Expr<'_>>> = it
481 .params_without_self(db)
482 .into_iter()
483 .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments())))
484 .collect::<Option<_>>()?;
485
486 let generics: Vec<_> = ty.type_arguments().collect();
487 let fn_exprs: Vec<Expr<'_>> = std::iter::once(target_type_exprs)
488 .chain(param_exprs)
489 .multi_cartesian_product()
490 .map(|params| {
491 let mut params = params.into_iter();
492 let target = Box::new(params.next().unwrap());
493 Expr::Method {
494 func: it,
495 generics: generics.clone(),
496 target,
497 params: params.collect(),
498 }
499 })
500 .collect();
501
502 Some((ret_ty, fn_exprs))
503 })
504 .filter_map(|(ty, exprs)| {
505 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
506 })
507 .flatten()
508}
509
510pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>(
523 ctx: &'a TermSearchCtx<'_, 'db, DB>,
524 _defs: &'a FxHashSet<ScopeDef<'db>>,
525 lookup: &'lt mut LookupTable<'db>,
526 should_continue: &'a dyn std::ops::Fn() -> bool,
527) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
528 let db = ctx.sema.db;
529 let module = ctx.scope.module();
530 lookup
531 .new_types(NewTypesKey::StructProjection)
532 .into_iter()
533 .map(|ty| (ty.clone(), lookup.find(db, &ty).expect("Expr not in lookup")))
534 .filter(|_| should_continue())
535 .flat_map(move |(ty, targets)| {
536 ty.fields(db).into_iter().filter_map(move |(field, filed_ty)| {
537 if !field.is_visible_from(db, module) {
538 return None;
539 }
540 let exprs = targets
541 .clone()
542 .into_iter()
543 .map(move |target| Expr::Field { field, expr: Box::new(target) });
544 Some((filed_ty, exprs))
545 })
546 })
547 .filter_map(|(ty, exprs)| {
548 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
549 })
550 .flatten()
551}
552
553pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>(
567 ctx: &'a TermSearchCtx<'_, 'db, DB>,
568 _defs: &'a FxHashSet<ScopeDef<'db>>,
569 lookup: &'lt mut LookupTable<'db>,
570) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
571 let db = ctx.sema.db;
572 let bool_ty = BuiltinType::bool().ty(db);
573 let unit_ty = Type::new_unit();
574 [
575 Expr::FamousType { ty: bool_ty.clone(), value: "true" },
576 Expr::FamousType { ty: bool_ty, value: "false" },
577 Expr::FamousType { ty: unit_ty, value: "()" },
578 ]
579 .into_iter()
580 .inspect(|exprs| {
581 lookup.insert(exprs.ty(db), std::iter::once(exprs.clone()));
582 })
583 .filter(|expr| expr.ty(db).instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal))
584}
585
586pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>(
599 ctx: &'a TermSearchCtx<'_, 'db, DB>,
600 _defs: &'a FxHashSet<ScopeDef<'db>>,
601 lookup: &'lt mut LookupTable<'db>,
602 should_continue: &'a dyn std::ops::Fn() -> bool,
603) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
604 let db = ctx.sema.db;
605 let module = ctx.scope.module();
606 lookup
607 .types_wishlist()
608 .clone()
609 .into_iter()
610 .chain(iter::once(ctx.goal.clone()))
611 .filter(|ty| !ty.type_arguments().any(|it| it.contains_unknown()))
612 .filter(|_| should_continue())
613 .flat_map(|ty| {
614 Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
615 })
616 .filter(|(_, imp)| !imp.is_unsafe(db))
617 .flat_map(|(ty, imp)| imp.items(db).into_iter().map(move |item| (imp, ty.clone(), item)))
618 .filter_map(|(imp, ty, it)| match it {
619 AssocItem::Function(f) => Some((imp, ty, f)),
620 _ => None,
621 })
622 .filter(|_| should_continue())
623 .filter_map(move |(imp, ty, it)| {
624 let fn_generics = GenericDef::from(it);
625 let imp_generics = GenericDef::from(imp);
626
627 if !fn_generics.lifetime_params(db).is_empty()
629 || !imp_generics.lifetime_params(db).is_empty()
630 {
631 return None;
632 }
633
634 if it.has_self_param(db) {
636 return None;
637 }
638
639 if !it.is_visible_from(db, module)
641 || it.is_unsafe_to_call(
642 db,
643 None,
644 crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
645 )
646 || it.is_unstable(db)
647 {
648 return None;
649 }
650
651 if !fn_generics.type_or_const_params(db).is_empty() {
654 return None;
655 }
656
657 let ret_ty = it.ret_type(db).instantiate(ty.type_arguments());
658
659 let param_exprs: Vec<Vec<Expr<'_>>> = it
661 .params_without_self(db)
662 .into_iter()
663 .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments())))
664 .collect::<Option<_>>()?;
665
666 let generics = ty.type_arguments().collect();
669 let fn_exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
670 vec![Expr::Function { func: it, generics, params: Vec::new() }]
671 } else {
672 param_exprs
673 .into_iter()
674 .multi_cartesian_product()
675 .map(|params| Expr::Function { func: it, generics: generics.clone(), params })
676 .collect()
677 };
678
679 lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned());
680
681 Some((ret_ty, fn_exprs))
682 })
683 .filter_map(|(ty, exprs)| {
684 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
685 })
686 .flatten()
687}
688
689pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>(
702 ctx: &'a TermSearchCtx<'_, 'db, DB>,
703 _defs: &'a FxHashSet<ScopeDef<'db>>,
704 lookup: &'lt mut LookupTable<'db>,
705 should_continue: &'a dyn std::ops::Fn() -> bool,
706) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
707 let db = ctx.sema.db;
708
709 lookup
710 .types_wishlist()
711 .clone()
712 .into_iter()
713 .filter(|_| should_continue())
714 .filter(|ty| ty.is_tuple())
715 .filter_map(move |ty| {
716 if ty.contains_unknown() {
718 return None;
719 }
720
721 let param_exprs: Vec<Vec<Expr<'db>>> =
723 ty.type_arguments().map(|field| lookup.find(db, &field)).collect::<Option<_>>()?;
724
725 let exprs: Vec<Expr<'db>> = param_exprs
726 .into_iter()
727 .multi_cartesian_product()
728 .filter(|_| should_continue())
729 .map(|params| {
730 let tys: Vec<Type<'_>> = params.iter().map(|it| it.ty(db)).collect();
731 let tuple_ty = Type::new_tuple(db, &tys);
732
733 let expr = Expr::Tuple { ty: tuple_ty.clone(), params };
734 lookup.insert(tuple_ty, iter::once(expr.clone()));
735 expr
736 })
737 .collect();
738
739 Some(exprs)
740 })
741 .flatten()
742 .filter_map(|expr| {
743 expr.ty(db)
744 .instantiate_with_errors()
745 .could_unify_with_deeply(db, &ctx.goal)
746 .then_some(expr)
747 })
748}