From be6f8e2648d99d8e858710460c89e9cf26685f00 Mon Sep 17 00:00:00 2001 From: Tavo Annus Date: Sun, 25 Feb 2024 17:24:07 +0200 Subject: [PATCH] Add `make_tuple` tactic --- crates/hir/src/lib.rs | 4 +- crates/hir/src/term_search.rs | 1 + crates/hir/src/term_search/tactics.rs | 58 +++++++++++++++++++ .../ide-assists/src/handlers/term_search.rs | 20 +++++++ 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 14066dee5f8..f157b11df57 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -4321,8 +4321,10 @@ pub fn type_arguments(&self) -> impl Iterator + '_ { self.ty .strip_references() .as_adt() + .map(|(_, substs)| substs) + .or_else(|| self.ty.strip_references().as_tuple()) .into_iter() - .flat_map(|(_, substs)| substs.iter(Interner)) + .flat_map(|substs| substs.iter(Interner)) .filter_map(|arg| arg.ty(Interner).cloned()) .map(move |ty| self.derived(ty)) } diff --git a/crates/hir/src/term_search.rs b/crates/hir/src/term_search.rs index 68244b12721..93e73004911 100644 --- a/crates/hir/src/term_search.rs +++ b/crates/hir/src/term_search.rs @@ -311,6 +311,7 @@ pub fn term_search(ctx: &TermSearchCtx<'_, DB>) -> Vec { solutions.extend(tactics::impl_method(ctx, &defs, &mut lookup)); solutions.extend(tactics::struct_projection(ctx, &defs, &mut lookup)); solutions.extend(tactics::impl_static_method(ctx, &defs, &mut lookup)); + solutions.extend(tactics::make_tuple(ctx, &defs, &mut lookup)); // Discard not interesting `ScopeDef`s for speedup for def in lookup.exhausted_scopedefs() { diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs index 93a780d4702..102e0ca4c3d 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs @@ -872,3 +872,61 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) .flatten() } + +/// # Make tuple tactic +/// +/// Attempts to create tuple types if any are listed in types wishlist +/// +/// Updates lookup by new types reached and returns iterator that yields +/// elements that unify with `goal`. +/// +/// # Arguments +/// * `ctx` - Context for the term search +/// * `defs` - Set of items in scope at term search target location +/// * `lookup` - Lookup table for types +pub(super) fn make_tuple<'a, DB: HirDatabase>( + ctx: &'a TermSearchCtx<'a, DB>, + _defs: &'a FxHashSet, + lookup: &'a mut LookupTable, +) -> impl Iterator + 'a { + let db = ctx.sema.db; + let module = ctx.scope.module(); + + lookup + .types_wishlist() + .clone() + .into_iter() + .filter(|ty| ty.is_tuple()) + .filter_map(move |ty| { + // Double check to not contain unknown + if ty.contains_unknown() { + return None; + } + + // Ignore types that have something to do with lifetimes + if ctx.config.enable_borrowcheck && ty.contains_reference(db) { + return None; + } + + // Early exit if some param cannot be filled from lookup + let param_exprs: Vec> = + ty.type_arguments().map(|field| lookup.find(db, &field)).collect::>()?; + + let exprs: Vec = param_exprs + .into_iter() + .multi_cartesian_product() + .map(|params| { + let tys: Vec = params.iter().map(|it| it.ty(db)).collect(); + let tuple_ty = Type::new_tuple(module.krate().into(), &tys); + + let expr = Expr::Tuple { ty: tuple_ty.clone(), params }; + lookup.insert(tuple_ty, iter::once(expr.clone())); + expr + }) + .collect(); + + Some(exprs) + }) + .flatten() + .filter_map(|expr| expr.ty(db).could_unify_with_deeply(db, &ctx.goal).then_some(expr)) +} diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs index fa32a3bbe75..0f4a8e3aecb 100644 --- a/crates/ide-assists/src/handlers/term_search.rs +++ b/crates/ide-assists/src/handlers/term_search.rs @@ -253,4 +253,24 @@ fn f(a: &i32) -> f32 { a as f32 } fn g() { let a = &mut 1; let b: f32 = todo$0!(); }"#, ) } + + #[test] + fn test_tuple_simple() { + check_assist( + term_search, + r#"//- minicore: todo, unimplemented +fn f() { let a = 1; let b = 0.0; let c: (i32, f64) = todo$0!(); }"#, + r#"fn f() { let a = 1; let b = 0.0; let c: (i32, f64) = (a, b); }"#, + ) + } + + #[test] + fn test_tuple_nested() { + check_assist( + term_search, + r#"//- minicore: todo, unimplemented +fn f() { let a = 1; let b = 0.0; let c: (i32, (i32, f64)) = todo$0!(); }"#, + r#"fn f() { let a = 1; let b = 0.0; let c: (i32, (i32, f64)) = (a, (a, b)); }"#, + ) + } }