Skip to content

Commit eeceba7

Browse files
committed
Auto merge of #14065 - lowr:patch/generate-generic-function, r=Veykril
Support generic function in `generate_function` assist Part of #3639 This PR adds support for generic function generation in `generate_function` assist. Now the assist looks for generic parameters and trait bounds in scope, filters out irrelevant ones, and generates new function with them. See `fn_generic_params()` for the outline of the procedure, and see comments on `filter_unnecessary_bounds()` for criteria for filtering. I think it's good criteria for most cases, but I'm open to opinions and suggestions. The diff is pretty big, but it should run in linear time w.r.t. the number of nodes we operate on and should be fast enough. Some notes: - When we generate function in an existing impl, generic parameters may cause name conflict. While we can detect the conflict and rename conflicting params, I didn't find it worthwhile mainly because it's really easy to resolve on IDE: use Rename functionality. - I've implemented graph structure myself, because we don't have graph library as a dependency and we only need the simplest one. - Although `petgraph` is in our dependency graph and I was initially looking to use it, we don't actually depend on it AFAICT since it's only used in chalk's specialization graph handling, which we don't use. I'd be happy to replace my implementation with `petgraph` if it's okay to use it though. - There are some caveats that I consider out of scope of this PR. See FIXME notes on added tests.
2 parents ccd142c + 493cabb commit eeceba7

File tree

7 files changed

+980
-64
lines changed

7 files changed

+980
-64
lines changed

crates/hir-ty/src/lib.rs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ use std::sync::Arc;
3939
use chalk_ir::{
4040
fold::{Shift, TypeFoldable},
4141
interner::HasInterner,
42-
NoSolution,
42+
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
43+
NoSolution, TyData,
4344
};
4445
use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
4546
use hir_expand::name;
4647
use itertools::Either;
48+
use rustc_hash::FxHashSet;
4749
use traits::FnTrait;
4850
use utils::Generics;
4951

@@ -562,3 +564,68 @@ pub fn callable_sig_from_fnonce(
562564

563565
Some(CallableSig::from_params_and_return(params, ret_ty, false, Safety::Safe))
564566
}
567+
568+
struct PlaceholderCollector<'db> {
569+
db: &'db dyn HirDatabase,
570+
placeholders: FxHashSet<TypeOrConstParamId>,
571+
}
572+
573+
impl PlaceholderCollector<'_> {
574+
fn collect(&mut self, idx: PlaceholderIndex) {
575+
let id = from_placeholder_idx(self.db, idx);
576+
self.placeholders.insert(id);
577+
}
578+
}
579+
580+
impl TypeVisitor<Interner> for PlaceholderCollector<'_> {
581+
type BreakTy = ();
582+
583+
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
584+
self
585+
}
586+
587+
fn interner(&self) -> Interner {
588+
Interner
589+
}
590+
591+
fn visit_ty(
592+
&mut self,
593+
ty: &Ty,
594+
outer_binder: DebruijnIndex,
595+
) -> std::ops::ControlFlow<Self::BreakTy> {
596+
let has_placeholder_bits = TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER;
597+
let TyData { kind, flags } = ty.data(Interner);
598+
599+
if let TyKind::Placeholder(idx) = kind {
600+
self.collect(*idx);
601+
} else if flags.intersects(has_placeholder_bits) {
602+
return ty.super_visit_with(self, outer_binder);
603+
} else {
604+
// Fast path: don't visit inner types (e.g. generic arguments) when `flags` indicate
605+
// that there are no placeholders.
606+
}
607+
608+
std::ops::ControlFlow::Continue(())
609+
}
610+
611+
fn visit_const(
612+
&mut self,
613+
constant: &chalk_ir::Const<Interner>,
614+
_outer_binder: DebruijnIndex,
615+
) -> std::ops::ControlFlow<Self::BreakTy> {
616+
if let chalk_ir::ConstValue::Placeholder(idx) = constant.data(Interner).value {
617+
self.collect(idx);
618+
}
619+
std::ops::ControlFlow::Continue(())
620+
}
621+
}
622+
623+
/// Returns unique placeholders for types and consts contained in `value`.
624+
pub fn collect_placeholders<T>(value: &T, db: &dyn HirDatabase) -> Vec<TypeOrConstParamId>
625+
where
626+
T: ?Sized + TypeVisitable<Interner>,
627+
{
628+
let mut collector = PlaceholderCollector { db, placeholders: FxHashSet::default() };
629+
value.visit_with(&mut collector, DebruijnIndex::INNERMOST);
630+
collector.placeholders.into_iter().collect()
631+
}

crates/hir/src/lib.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2165,6 +2165,16 @@ impl AsAssocItem for ModuleDef {
21652165
}
21662166
}
21672167
}
2168+
impl AsAssocItem for DefWithBody {
2169+
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
2170+
match self {
2171+
DefWithBody::Function(it) => it.as_assoc_item(db),
2172+
DefWithBody::Const(it) => it.as_assoc_item(db),
2173+
DefWithBody::Static(_) | DefWithBody::Variant(_) => None,
2174+
}
2175+
}
2176+
}
2177+
21682178
fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem>
21692179
where
21702180
ID: Lookup<Data = AssocItemLoc<AST>>,
@@ -2565,6 +2575,14 @@ impl GenericParam {
25652575
GenericParam::LifetimeParam(it) => it.name(db),
25662576
}
25672577
}
2578+
2579+
pub fn parent(self) -> GenericDef {
2580+
match self {
2581+
GenericParam::TypeParam(it) => it.id.parent().into(),
2582+
GenericParam::ConstParam(it) => it.id.parent().into(),
2583+
GenericParam::LifetimeParam(it) => it.id.parent.into(),
2584+
}
2585+
}
25682586
}
25692587

25702588
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -3144,15 +3162,15 @@ impl Type {
31443162
}
31453163

31463164
pub fn is_closure(&self) -> bool {
3147-
matches!(&self.ty.kind(Interner), TyKind::Closure { .. })
3165+
matches!(self.ty.kind(Interner), TyKind::Closure { .. })
31483166
}
31493167

31503168
pub fn is_fn(&self) -> bool {
3151-
matches!(&self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. })
3169+
matches!(self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. })
31523170
}
31533171

31543172
pub fn is_array(&self) -> bool {
3155-
matches!(&self.ty.kind(Interner), TyKind::Array(..))
3173+
matches!(self.ty.kind(Interner), TyKind::Array(..))
31563174
}
31573175

31583176
pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
@@ -3169,7 +3187,7 @@ impl Type {
31693187
}
31703188

31713189
pub fn is_raw_ptr(&self) -> bool {
3172-
matches!(&self.ty.kind(Interner), TyKind::Raw(..))
3190+
matches!(self.ty.kind(Interner), TyKind::Raw(..))
31733191
}
31743192

31753193
pub fn contains_unknown(&self) -> bool {
@@ -3604,6 +3622,14 @@ impl Type {
36043622
_ => None,
36053623
}
36063624
}
3625+
3626+
/// Returns unique `GenericParam`s contained in this type.
3627+
pub fn generic_params(&self, db: &dyn HirDatabase) -> FxHashSet<GenericParam> {
3628+
hir_ty::collect_placeholders(&self.ty, db)
3629+
.into_iter()
3630+
.map(|id| TypeOrConstParam { id }.split(db).either_into())
3631+
.collect()
3632+
}
36073633
}
36083634

36093635
#[derive(Debug)]

crates/hir/src/semantics.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,10 +1319,7 @@ impl<'db> SemanticsImpl<'db> {
13191319
let _p = profile::span("Semantics::analyze_impl");
13201320
let node = self.find_file(node);
13211321

1322-
let container = match self.with_ctx(|ctx| ctx.find_container(node)) {
1323-
Some(it) => it,
1324-
None => return None,
1325-
};
1322+
let container = self.with_ctx(|ctx| ctx.find_container(node))?;
13261323

13271324
let resolver = match container {
13281325
ChildContainer::DefWithBodyId(def) => {
@@ -1582,7 +1579,7 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode {
15821579
node.ancestors().last().unwrap()
15831580
}
15841581

1585-
/// `SemanticScope` encapsulates the notion of a scope (the set of visible
1582+
/// `SemanticsScope` encapsulates the notion of a scope (the set of visible
15861583
/// names) at a particular program point.
15871584
///
15881585
/// It is a bit tricky, as scopes do not really exist inside the compiler.

crates/ide-assists/src/handlers/generate_delegate_methods.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
109109
let tail_expr_finished =
110110
if is_async { make::expr_await(tail_expr) } else { tail_expr };
111111
let body = make::block_expr([], Some(tail_expr_finished));
112-
let f = make::fn_(vis, name, type_params, params, body, ret_type, is_async)
112+
let f = make::fn_(vis, name, type_params, None, params, body, ret_type, is_async)
113113
.indent(ast::edit::IndentLevel(1))
114114
.clone_for_update();
115115

0 commit comments

Comments
 (0)