Cleanup term search related changes

This commit is contained in:
Tavo Annus 2024-02-01 11:02:19 +02:00
parent 88964c0b6a
commit 125791386d
26 changed files with 590 additions and 516 deletions

View file

@ -377,6 +377,7 @@ impl AttrsWithOwner {
AttrDefId::GenericParamId(it) => match it { AttrDefId::GenericParamId(it) => match it {
GenericParamId::ConstParamId(it) => { GenericParamId::ConstParamId(it) => {
let src = it.parent().child_source(db); let src = it.parent().child_source(db);
// FIXME: We should be never getting `None` here.
match src.value.get(it.local_id()) { match src.value.get(it.local_id()) {
Some(val) => RawAttrs::from_attrs_owner( Some(val) => RawAttrs::from_attrs_owner(
db.upcast(), db.upcast(),
@ -388,6 +389,7 @@ impl AttrsWithOwner {
} }
GenericParamId::TypeParamId(it) => { GenericParamId::TypeParamId(it) => {
let src = it.parent().child_source(db); let src = it.parent().child_source(db);
// FIXME: We should be never getting `None` here.
match src.value.get(it.local_id()) { match src.value.get(it.local_id()) {
Some(val) => RawAttrs::from_attrs_owner( Some(val) => RawAttrs::from_attrs_owner(
db.upcast(), db.upcast(),
@ -399,6 +401,7 @@ impl AttrsWithOwner {
} }
GenericParamId::LifetimeParamId(it) => { GenericParamId::LifetimeParamId(it) => {
let src = it.parent.child_source(db); let src = it.parent.child_source(db);
// FIXME: We should be never getting `None` here.
match src.value.get(it.local_id) { match src.value.get(it.local_id) {
Some(val) => RawAttrs::from_attrs_owner( Some(val) => RawAttrs::from_attrs_owner(
db.upcast(), db.upcast(),

View file

@ -74,6 +74,12 @@ impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
} }
} }
/// Check if types unify.
///
/// Note that we consider placeholder types to unify with everything.
/// This means that there may be some unresolved goals that actually set bounds for the placeholder
/// type for the types to unify. For example `Option<T>` and `Option<U>` unify although there is
/// unresolved goal `T = U`.
pub fn could_unify( pub fn could_unify(
db: &dyn HirDatabase, db: &dyn HirDatabase,
env: Arc<TraitEnvironment>, env: Arc<TraitEnvironment>,
@ -82,30 +88,25 @@ pub fn could_unify(
unify(db, env, tys).is_some() unify(db, env, tys).is_some()
} }
/// Check if types unify eagerly making sure there are no unresolved goals.
///
/// This means that placeholder types are not considered to unify if there are any bounds set on
/// them. For example `Option<T>` and `Option<U>` do not unify as we cannot show that `T = U`
pub fn could_unify_deeply( pub fn could_unify_deeply(
db: &dyn HirDatabase, db: &dyn HirDatabase,
env: Arc<TraitEnvironment>, env: Arc<TraitEnvironment>,
tys: &Canonical<(Ty, Ty)>, tys: &Canonical<(Ty, Ty)>,
) -> bool { ) -> bool {
let mut table = InferenceTable::new(db, env); let mut table = InferenceTable::new(db, env);
let vars = Substitution::from_iter( let vars = make_substitutions(tys, &mut table);
Interner,
tys.binders.iter(Interner).map(|it| match &it.kind {
chalk_ir::VariableKind::Ty(_) => {
GenericArgData::Ty(table.new_type_var()).intern(Interner)
}
chalk_ir::VariableKind::Lifetime => {
GenericArgData::Ty(table.new_type_var()).intern(Interner)
} // FIXME: maybe wrong?
chalk_ir::VariableKind::Const(ty) => {
GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner)
}
}),
);
let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner);
let ty1_with_vars = table.normalize_associated_types_in(ty1_with_vars); let ty1_with_vars = table.normalize_associated_types_in(ty1_with_vars);
let ty2_with_vars = table.normalize_associated_types_in(ty2_with_vars); let ty2_with_vars = table.normalize_associated_types_in(ty2_with_vars);
table.resolve_obligations_as_possible();
table.propagate_diverging_flag();
let ty1_with_vars = table.resolve_completely(ty1_with_vars);
let ty2_with_vars = table.resolve_completely(ty2_with_vars);
table.unify_deeply(&ty1_with_vars, &ty2_with_vars) table.unify_deeply(&ty1_with_vars, &ty2_with_vars)
} }
@ -115,15 +116,7 @@ pub(crate) fn unify(
tys: &Canonical<(Ty, Ty)>, tys: &Canonical<(Ty, Ty)>,
) -> Option<Substitution> { ) -> Option<Substitution> {
let mut table = InferenceTable::new(db, env); let mut table = InferenceTable::new(db, env);
let vars = Substitution::from_iter( let vars = make_substitutions(tys, &mut table);
Interner,
tys.binders.iter(Interner).map(|it| match &it.kind {
chalk_ir::VariableKind::Ty(_) => table.new_type_var().cast(Interner),
// FIXME: maybe wrong?
chalk_ir::VariableKind::Lifetime => table.new_type_var().cast(Interner),
chalk_ir::VariableKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner),
}),
);
let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner);
if !table.unify(&ty1_with_vars, &ty2_with_vars) { if !table.unify(&ty1_with_vars, &ty2_with_vars) {
@ -152,6 +145,21 @@ pub(crate) fn unify(
)) ))
} }
fn make_substitutions(
tys: &chalk_ir::Canonical<(chalk_ir::Ty<Interner>, chalk_ir::Ty<Interner>)>,
table: &mut InferenceTable<'_>,
) -> chalk_ir::Substitution<Interner> {
Substitution::from_iter(
Interner,
tys.binders.iter(Interner).map(|it| match &it.kind {
chalk_ir::VariableKind::Ty(_) => table.new_type_var().cast(Interner),
// FIXME: maybe wrong?
chalk_ir::VariableKind::Lifetime => table.new_type_var().cast(Interner),
chalk_ir::VariableKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner),
}),
)
}
bitflags::bitflags! { bitflags::bitflags! {
#[derive(Default, Clone, Copy)] #[derive(Default, Clone, Copy)]
pub(crate) struct TypeVariableFlags: u8 { pub(crate) struct TypeVariableFlags: u8 {
@ -458,7 +466,7 @@ impl<'a> InferenceTable<'a> {
true true
} }
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. /// Unify two relatable values (e.g. `Ty`) and check whether trait goals which arise from that could be fulfilled
pub(crate) fn unify_deeply<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool { pub(crate) fn unify_deeply<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
let result = match self.try_unify(ty1, ty2) { let result = match self.try_unify(ty1, ty2) {
Ok(r) => r, Ok(r) => r,
@ -466,7 +474,7 @@ impl<'a> InferenceTable<'a> {
}; };
result.goals.iter().all(|goal| { result.goals.iter().all(|goal| {
let canonicalized = self.canonicalize(goal.clone()); let canonicalized = self.canonicalize(goal.clone());
self.try_fulfill_obligation(&canonicalized) self.try_resolve_obligation(&canonicalized).is_some()
}) })
} }
@ -540,7 +548,8 @@ impl<'a> InferenceTable<'a> {
fn register_obligation_in_env(&mut self, goal: InEnvironment<Goal>) { fn register_obligation_in_env(&mut self, goal: InEnvironment<Goal>) {
let canonicalized = self.canonicalize(goal); let canonicalized = self.canonicalize(goal);
if !self.try_resolve_obligation(&canonicalized) { let solution = self.try_resolve_obligation(&canonicalized);
if matches!(solution, Some(Solution::Ambig(_))) {
self.pending_obligations.push(canonicalized); self.pending_obligations.push(canonicalized);
} }
} }
@ -666,70 +675,35 @@ impl<'a> InferenceTable<'a> {
fn try_resolve_obligation( fn try_resolve_obligation(
&mut self, &mut self,
canonicalized: &Canonicalized<InEnvironment<Goal>>, canonicalized: &Canonicalized<InEnvironment<Goal>>,
) -> bool { ) -> Option<chalk_solve::Solution<Interner>> {
let solution = self.db.trait_solve( let solution = self.db.trait_solve(
self.trait_env.krate, self.trait_env.krate,
self.trait_env.block, self.trait_env.block,
canonicalized.value.clone(), canonicalized.value.clone(),
); );
match solution { match &solution {
Some(Solution::Unique(canonical_subst)) => { Some(Solution::Unique(canonical_subst)) => {
canonicalized.apply_solution( canonicalized.apply_solution(
self, self,
Canonical { Canonical {
binders: canonical_subst.binders, binders: canonical_subst.binders.clone(),
// FIXME: handle constraints // FIXME: handle constraints
value: canonical_subst.value.subst, value: canonical_subst.value.subst.clone(),
}, },
); );
true
} }
Some(Solution::Ambig(Guidance::Definite(substs))) => { Some(Solution::Ambig(Guidance::Definite(substs))) => {
canonicalized.apply_solution(self, substs); canonicalized.apply_solution(self, substs.clone());
false
} }
Some(_) => { Some(_) => {
// FIXME use this when trying to resolve everything at the end // FIXME use this when trying to resolve everything at the end
false
} }
None => { None => {
// FIXME obligation cannot be fulfilled => diagnostic // FIXME obligation cannot be fulfilled => diagnostic
true
} }
} }
} solution
fn try_fulfill_obligation(
&mut self,
canonicalized: &Canonicalized<InEnvironment<Goal>>,
) -> bool {
let solution = self.db.trait_solve(
self.trait_env.krate,
self.trait_env.block,
canonicalized.value.clone(),
);
// FIXME: Does just returning `solution.is_some()` work?
match solution {
Some(Solution::Unique(canonical_subst)) => {
canonicalized.apply_solution(
self,
Canonical {
binders: canonical_subst.binders,
// FIXME: handle constraints
value: canonical_subst.value.subst,
},
);
true
}
Some(Solution::Ambig(Guidance::Definite(substs))) => {
canonicalized.apply_solution(self, substs);
true
}
Some(_) => true,
None => false,
}
} }
pub(crate) fn callable_sig( pub(crate) fn callable_sig(

View file

@ -15,7 +15,7 @@ use crate::{
db::{HirDatabase, InternedClosure}, db::{HirDatabase, InternedClosure},
mir::Operand, mir::Operand,
utils::ClosureSubst, utils::ClosureSubst,
ClosureId, Interner, Ty, TyExt, TypeFlags, ClosureId, Interner, Substitution, Ty, TyExt, TypeFlags,
}; };
use super::{ use super::{
@ -105,6 +105,18 @@ pub fn borrowck_query(
Ok(res.into()) Ok(res.into())
} }
fn make_fetch_closure_field(
db: &dyn HirDatabase,
) -> impl FnOnce(ClosureId, &Substitution, usize) -> Ty + '_ {
|c: ClosureId, subst: &Substitution, f: usize| {
let InternedClosure(def, _) = db.lookup_intern_closure(c.into());
let infer = db.infer(def);
let (captures, _) = infer.closure_info(&c);
let parent_subst = ClosureSubst(subst).parent_subst();
captures.get(f).expect("broken closure field").ty.clone().substitute(Interner, parent_subst)
}
}
fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> { fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> {
let mut result = vec![]; let mut result = vec![];
let mut for_operand = |op: &Operand, span: MirSpan| match op { let mut for_operand = |op: &Operand, span: MirSpan| match op {
@ -118,18 +130,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
ty = proj.projected_ty( ty = proj.projected_ty(
ty, ty,
db, db,
|c, subst, f| { make_fetch_closure_field(db),
let InternedClosure(def, _) = db.lookup_intern_closure(c.into());
let infer = db.infer(def);
let (captures, _) = infer.closure_info(&c);
let parent_subst = ClosureSubst(subst).parent_subst();
captures
.get(f)
.expect("broken closure field")
.ty
.clone()
.substitute(Interner, parent_subst)
},
body.owner.module(db.upcast()).krate(), body.owner.module(db.upcast()).krate(),
); );
} }
@ -216,18 +217,7 @@ fn partially_moved(db: &dyn HirDatabase, body: &MirBody) -> Vec<PartiallyMoved>
ty = proj.projected_ty( ty = proj.projected_ty(
ty, ty,
db, db,
|c, subst, f| { make_fetch_closure_field(db),
let (def, _) = db.lookup_intern_closure(c.into());
let infer = db.infer(def);
let (captures, _) = infer.closure_info(&c);
let parent_subst = ClosureSubst(subst).parent_subst();
captures
.get(f)
.expect("broken closure field")
.ty
.clone()
.substitute(Interner, parent_subst)
},
body.owner.module(db.upcast()).krate(), body.owner.module(db.upcast()).krate(),
); );
} }
@ -309,23 +299,17 @@ fn borrow_regions(db: &dyn HirDatabase, body: &MirBody) -> Vec<BorrowRegion> {
for (_, block) in body.basic_blocks.iter() { for (_, block) in body.basic_blocks.iter() {
db.unwind_if_cancelled(); db.unwind_if_cancelled();
for statement in &block.statements { for statement in &block.statements {
match &statement.kind { if let StatementKind::Assign(_, Rvalue::Ref(kind, p)) = &statement.kind {
StatementKind::Assign(_, r) => match r { borrows
Rvalue::Ref(kind, p) => { .entry(p.local)
borrows .and_modify(|it: &mut BorrowRegion| {
.entry(p.local) it.places.push(statement.span);
.and_modify(|it: &mut BorrowRegion| { })
it.places.push(statement.span); .or_insert_with(|| BorrowRegion {
}) local: p.local,
.or_insert_with(|| BorrowRegion { kind: *kind,
local: p.local, places: vec![statement.span],
kind: *kind, });
places: vec![statement.span],
});
}
_ => (),
},
_ => (),
} }
} }
match &block.terminator { match &block.terminator {
@ -379,18 +363,7 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio
ty = proj.projected_ty( ty = proj.projected_ty(
ty, ty,
db, db,
|c, subst, f| { make_fetch_closure_field(db),
let InternedClosure(def, _) = db.lookup_intern_closure(c.into());
let infer = db.infer(def);
let (captures, _) = infer.closure_info(&c);
let parent_subst = ClosureSubst(subst).parent_subst();
captures
.get(f)
.expect("broken closure field")
.ty
.clone()
.substitute(Interner, parent_subst)
},
body.owner.module(db.upcast()).krate(), body.owner.module(db.upcast()).krate(),
); );
} }

View file

@ -1085,6 +1085,7 @@ impl Field {
Type::new(db, var_id, ty) Type::new(db, var_id, ty)
} }
// FIXME: Find better API to also handle const generics
pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> Type { pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> Type {
let var_id = self.parent.into(); let var_id = self.parent.into();
let def_id: AdtId = match self.parent { let def_id: AdtId = match self.parent {
@ -1094,12 +1095,11 @@ impl Field {
}; };
let mut generics = generics.map(|it| it.ty.clone()); let mut generics = generics.map(|it| it.ty.clone());
let substs = TyBuilder::subst_for_def(db, def_id, None) let substs = TyBuilder::subst_for_def(db, def_id, None)
.fill(|x| { .fill(|x| match x {
let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); ParamKind::Type => {
match x { generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner)
ParamKind::Type => ty.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
} }
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}) })
.build(); .build();
let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs); let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs);
@ -1159,21 +1159,6 @@ impl Struct {
Type::from_def(db, self.id) Type::from_def(db, self.id)
} }
pub fn ty_with_args(self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> Type {
let mut generics = generics.map(|it| it.ty.clone());
let substs = TyBuilder::subst_for_def(db, self.id, None)
.fill(|x| {
let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
match x {
ParamKind::Type => ty.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}
})
.build();
let ty = db.ty(self.id.into()).substitute(Interner, &substs);
Type::new(db, self.id, ty)
}
pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type { pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
Type::from_value_def(db, self.id) Type::from_value_def(db, self.id)
} }
@ -1273,22 +1258,6 @@ impl Enum {
Type::from_def(db, self.id) Type::from_def(db, self.id)
} }
pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> Type {
let mut generics = generics.map(|it| it.ty.clone());
let substs = TyBuilder::subst_for_def(db, self.id, None)
.fill(|x| {
let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
match x {
ParamKind::Type => ty.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}
})
.build();
let ty = db.ty(self.id.into()).substitute(Interner, &substs);
Type::new(db, self.id, ty)
}
/// The type of the enum variant bodies. /// The type of the enum variant bodies.
pub fn variant_body_ty(self, db: &dyn HirDatabase) -> Type { pub fn variant_body_ty(self, db: &dyn HirDatabase) -> Type {
Type::new_for_crate( Type::new_for_crate(
@ -1463,9 +1432,9 @@ impl Adt {
/// Turns this ADT into a type with the given type parameters. This isn't /// Turns this ADT into a type with the given type parameters. This isn't
/// the greatest API, FIXME find a better one. /// the greatest API, FIXME find a better one.
pub fn ty_with_args(self, db: &dyn HirDatabase, args: &[Type]) -> Type { pub fn ty_with_args(self, db: &dyn HirDatabase, args: impl Iterator<Item = Type>) -> Type {
let id = AdtId::from(self); let id = AdtId::from(self);
let mut it = args.iter().map(|t| t.ty.clone()); let mut it = args.map(|t| t.ty.clone());
let ty = TyBuilder::def_ty(db, id.into(), None) let ty = TyBuilder::def_ty(db, id.into(), None)
.fill(|x| { .fill(|x| {
let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
@ -1858,6 +1827,7 @@ impl Function {
Type::new_with_resolver_inner(db, &resolver, ty) Type::new_with_resolver_inner(db, &resolver, ty)
} }
// FIXME: Find better API to also handle const generics
pub fn ret_type_with_args( pub fn ret_type_with_args(
self, self,
db: &dyn HirDatabase, db: &dyn HirDatabase,
@ -1870,12 +1840,11 @@ impl Function {
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
}; };
let mut generics = generics.map(|it| it.ty.clone()); let mut generics = generics.map(|it| it.ty.clone());
let mut filler = |x: &_| { let mut filler = |x: &_| match x {
let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); ParamKind::Type => {
match x { generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner)
ParamKind::Type => ty.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
} }
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}; };
let parent_substs = let parent_substs =
@ -1953,10 +1922,11 @@ impl Function {
.collect() .collect()
} }
pub fn params_without_self_with_generics( // FIXME: Find better API to also handle const generics
pub fn params_without_self_with_args(
self, self,
db: &dyn HirDatabase, db: &dyn HirDatabase,
mut generics: impl Iterator<Item = Type>, generics: impl Iterator<Item = Type>,
) -> Vec<Param> { ) -> Vec<Param> {
let environment = db.trait_environment(self.id.into()); let environment = db.trait_environment(self.id.into());
let parent_id: Option<GenericDefId> = match self.id.lookup(db.upcast()).container { let parent_id: Option<GenericDefId> = match self.id.lookup(db.upcast()).container {
@ -1964,20 +1934,23 @@ impl Function {
ItemContainerId::TraitId(it) => Some(it.into()), ItemContainerId::TraitId(it) => Some(it.into()),
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
}; };
let mut generics = generics.map(|it| it.ty.clone());
let parent_substs = parent_id.map(|id| { let parent_substs = parent_id.map(|id| {
TyBuilder::subst_for_def(db, id, None) TyBuilder::subst_for_def(db, id, None)
.fill(|_| { .fill(|x| match x {
GenericArg::new( ParamKind::Type => generics
Interner, .next()
GenericArgData::Ty(generics.next().unwrap().ty.clone()), .unwrap_or_else(|| TyKind::Error.intern(Interner))
) .cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}) })
.build() .build()
}); });
let substs = TyBuilder::subst_for_def(db, self.id, parent_substs) let substs = TyBuilder::subst_for_def(db, self.id, parent_substs)
.fill(|_| { .fill(|_| {
GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone())) let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
GenericArg::new(Interner, GenericArgData::Ty(ty))
}) })
.build(); .build();
let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs); let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
@ -2197,6 +2170,7 @@ impl SelfParam {
Type { env: environment, ty } Type { env: environment, ty }
} }
// FIXME: Find better API to also handle const generics
pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> Type { pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> Type {
let parent_id: GenericDefId = match self.func.lookup(db.upcast()).container { let parent_id: GenericDefId = match self.func.lookup(db.upcast()).container {
ItemContainerId::ImplId(it) => it.into(), ItemContainerId::ImplId(it) => it.into(),
@ -2207,12 +2181,11 @@ impl SelfParam {
}; };
let mut generics = generics.map(|it| it.ty.clone()); let mut generics = generics.map(|it| it.ty.clone());
let mut filler = |x: &_| { let mut filler = |x: &_| match x {
let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); ParamKind::Type => {
match x { generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner)
ParamKind::Type => ty.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
} }
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}; };
let parent_substs = TyBuilder::subst_for_def(db, parent_id, None).fill(&mut filler).build(); let parent_substs = TyBuilder::subst_for_def(db, parent_id, None).fill(&mut filler).build();
@ -2936,40 +2909,6 @@ impl GenericDef {
}) })
.collect() .collect()
} }
pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> {
let generics = db.generic_params(self.into());
generics
.type_or_consts
.iter()
.filter_map(|(local_id, data)| match data {
hir_def::generics::TypeOrConstParamData::TypeParamData(_) => Some(TypeParam {
id: TypeParamId::from_unchecked(TypeOrConstParamId {
parent: self.into(),
local_id,
}),
}),
hir_def::generics::TypeOrConstParamData::ConstParamData(_) => None,
})
.collect()
}
pub fn const_params(self, db: &dyn HirDatabase) -> Vec<ConstParam> {
let generics = db.generic_params(self.into());
generics
.type_or_consts
.iter()
.filter_map(|(local_id, data)| match data {
hir_def::generics::TypeOrConstParamData::TypeParamData(_) => None,
hir_def::generics::TypeOrConstParamData::ConstParamData(_) => Some(ConstParam {
id: ConstParamId::from_unchecked(TypeOrConstParamId {
parent: self.into(),
local_id,
}),
}),
})
.collect()
}
} }
/// A single local definition. /// A single local definition.
@ -3451,6 +3390,26 @@ impl TypeOrConstParam {
Either::Right(it) => it.ty(db), Either::Right(it) => it.ty(db),
} }
} }
pub fn as_type_param(self, db: &dyn HirDatabase) -> Option<TypeParam> {
let params = db.generic_params(self.id.parent);
match &params.type_or_consts[self.id.local_id] {
hir_def::generics::TypeOrConstParamData::TypeParamData(_) => {
Some(TypeParam { id: TypeParamId::from_unchecked(self.id) })
}
hir_def::generics::TypeOrConstParamData::ConstParamData(_) => None,
}
}
pub fn as_const_param(self, db: &dyn HirDatabase) -> Option<ConstParam> {
let params = db.generic_params(self.id.parent);
match &params.type_or_consts[self.id.local_id] {
hir_def::generics::TypeOrConstParamData::TypeParamData(_) => None,
hir_def::generics::TypeOrConstParamData::ConstParamData(_) => {
Some(ConstParam { id: ConstParamId::from_unchecked(self.id) })
}
}
}
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -3496,7 +3455,11 @@ impl Impl {
) )
}); });
for Crate { id } in Crate::all(db) { for id in def_crates
.iter()
.flat_map(|&id| Crate { id }.transitive_reverse_dependencies(db))
.map(|Crate { id }| id)
{
all.extend( all.extend(
db.trait_impls_in_crate(id) db.trait_impls_in_crate(id)
.for_self_ty_without_blanket_impls(fp) .for_self_ty_without_blanket_impls(fp)
@ -3976,14 +3939,16 @@ impl Type {
) )
} }
// FIXME: Find better API that also handles const generics
pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool { pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
let mut it = args.iter().map(|t| t.ty.clone()); let mut it = args.iter().map(|t| t.ty.clone());
let trait_ref = TyBuilder::trait_ref(db, trait_.id) let trait_ref = TyBuilder::trait_ref(db, trait_.id)
.push(self.ty.clone()) .push(self.ty.clone())
.fill(|x| { .fill(|x| {
let r = it.next().unwrap();
match x { match x {
ParamKind::Type => r.cast(Interner), ParamKind::Type => {
it.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner)
}
ParamKind::Const(ty) => { ParamKind::Const(ty) => {
// FIXME: this code is not covered in tests. // FIXME: this code is not covered in tests.
unknown_const_as_generic(ty.clone()) unknown_const_as_generic(ty.clone())
@ -4617,12 +4582,19 @@ impl Type {
walk_type(db, self, &mut cb); walk_type(db, self, &mut cb);
} }
/// Check if type unifies with another type.
///
/// Note that we consider placeholder types to unify with everything.
/// For example `Option<T>` and `Option<U>` unify although there is unresolved goal `T = U`.
pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool { pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool {
let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone())); let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone()));
hir_ty::could_unify(db, self.env.clone(), &tys) hir_ty::could_unify(db, self.env.clone(), &tys)
} }
/// Check if type unifies with another type eagerly making sure there are no unresolved goals.
///
/// This means that placeholder types are not considered to unify if there are any bounds set on
/// them. For example `Option<T>` and `Option<U>` do not unify as we cannot show that `T = U`
pub fn could_unify_with_deeply(&self, db: &dyn HirDatabase, other: &Type) -> bool { pub fn could_unify_with_deeply(&self, db: &dyn HirDatabase, other: &Type) -> bool {
let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone())); let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone()));
hir_ty::could_unify_deeply(db, self.env.clone(), &tys) hir_ty::could_unify_deeply(db, self.env.clone(), &tys)

View file

@ -57,10 +57,10 @@ impl AlternativeExprs {
/// # Arguments /// # Arguments
/// `threshold` - threshold value for many trees (more than that is many) /// `threshold` - threshold value for many trees (more than that is many)
/// `exprs` - expressions iterator /// `exprs` - expressions iterator
fn extend_with_threshold(&mut self, threshold: usize, mut exprs: impl Iterator<Item = Expr>) { fn extend_with_threshold(&mut self, threshold: usize, exprs: impl Iterator<Item = Expr>) {
match self { match self {
AlternativeExprs::Few(tts) => { AlternativeExprs::Few(tts) => {
while let Some(it) = exprs.next() { for it in exprs {
if tts.len() > threshold { if tts.len() > threshold {
*self = AlternativeExprs::Many; *self = AlternativeExprs::Many;
break; break;
@ -131,7 +131,7 @@ impl LookupTable {
self.data self.data
.iter() .iter()
.find(|(t, _)| { .find(|(t, _)| {
Type::reference(t, Mutability::Shared).could_unify_with_deeply(db, &ty) Type::reference(t, Mutability::Shared).could_unify_with_deeply(db, ty)
}) })
.map(|(t, it)| { .map(|(t, it)| {
it.exprs(t) it.exprs(t)

View file

@ -2,7 +2,10 @@
use hir_def::find_path::PrefixKind; use hir_def::find_path::PrefixKind;
use hir_expand::mod_path::ModPath; use hir_expand::mod_path::ModPath;
use hir_ty::{db::HirDatabase, display::HirDisplay}; use hir_ty::{
db::HirDatabase,
display::{DisplaySourceCodeError, HirDisplay},
};
use itertools::Itertools; use itertools::Itertools;
use crate::{ use crate::{
@ -48,9 +51,10 @@ fn mod_item_path_str(
def: &ModuleDef, def: &ModuleDef,
prefer_no_std: bool, prefer_no_std: bool,
prefer_prelude: bool, prefer_prelude: bool,
) -> String { ) -> Result<String, DisplaySourceCodeError> {
let path = mod_item_path(sema_scope, def, prefer_no_std, prefer_prelude); let path = mod_item_path(sema_scope, def, prefer_no_std, prefer_prelude);
path.map(|it| it.display(sema_scope.db.upcast()).to_string()).unwrap() path.map(|it| it.display(sema_scope.db.upcast()).to_string())
.ok_or(DisplaySourceCodeError::PathNotFound)
} }
/// Helper function to get path to `Type` /// Helper function to get path to `Type`
@ -59,30 +63,34 @@ fn type_path(
ty: &Type, ty: &Type,
prefer_no_std: bool, prefer_no_std: bool,
prefer_prelude: bool, prefer_prelude: bool,
) -> String { ) -> Result<String, DisplaySourceCodeError> {
let db = sema_scope.db; let db = sema_scope.db;
let m = sema_scope.module();
match ty.as_adt() { match ty.as_adt() {
Some(adt) => { Some(adt) => {
let ty_name = ty.display(db).to_string(); let ty_name = ty.display_source_code(db, m.id, true)?;
let mut path = let mut path =
mod_item_path(sema_scope, &ModuleDef::Adt(adt), prefer_no_std, prefer_prelude) mod_item_path(sema_scope, &ModuleDef::Adt(adt), prefer_no_std, prefer_prelude)
.unwrap(); .unwrap();
path.pop_segment(); path.pop_segment();
let path = path.display(db.upcast()).to_string(); let path = path.display(db.upcast()).to_string();
match path.is_empty() { let res = match path.is_empty() {
true => ty_name, true => ty_name,
false => format!("{path}::{ty_name}"), false => format!("{path}::{ty_name}"),
} };
Ok(res)
} }
None => ty.display(db).to_string(), None => ty.display_source_code(db, m.id, true),
} }
} }
/// Helper function to filter out generic parameters that are default /// Helper function to filter out generic parameters that are default
fn non_default_generics(db: &dyn HirDatabase, def: GenericDef, generics: &[Type]) -> Vec<Type> { fn non_default_generics(db: &dyn HirDatabase, def: GenericDef, generics: &[Type]) -> Vec<Type> {
def.type_params(db) def.type_or_const_params(db)
.into_iter() .into_iter()
.filter_map(|it| it.as_type_param(db))
.zip(generics) .zip(generics)
.filter(|(tp, arg)| tp.default(db).as_ref() != Some(arg)) .filter(|(tp, arg)| tp.default(db).as_ref() != Some(arg))
.map(|(_, arg)| arg.clone()) .map(|(_, arg)| arg.clone())
@ -150,28 +158,30 @@ impl Expr {
many_formatter: &mut dyn FnMut(&Type) -> String, many_formatter: &mut dyn FnMut(&Type) -> String,
prefer_no_std: bool, prefer_no_std: bool,
prefer_prelude: bool, prefer_prelude: bool,
) -> String { ) -> Result<String, DisplaySourceCodeError> {
let db = sema_scope.db; let db = sema_scope.db;
let mod_item_path_str = |s, def| mod_item_path_str(s, def, prefer_no_std, prefer_prelude); let mod_item_path_str = |s, def| mod_item_path_str(s, def, prefer_no_std, prefer_prelude);
match self { match self {
Expr::Const(it) => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)), Expr::Const(it) => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)),
Expr::Static(it) => mod_item_path_str(sema_scope, &ModuleDef::Static(*it)), Expr::Static(it) => mod_item_path_str(sema_scope, &ModuleDef::Static(*it)),
Expr::Local(it) => return it.name(db).display(db.upcast()).to_string(), Expr::Local(it) => Ok(it.name(db).display(db.upcast()).to_string()),
Expr::ConstParam(it) => return it.name(db).display(db.upcast()).to_string(), Expr::ConstParam(it) => Ok(it.name(db).display(db.upcast()).to_string()),
Expr::FamousType { value, .. } => return value.to_string(), Expr::FamousType { value, .. } => Ok(value.to_string()),
Expr::Function { func, params, .. } => { Expr::Function { func, params, .. } => {
let args = params let args = params
.iter() .iter()
.map(|f| { .map(|f| {
f.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude) f.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude)
}) })
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
.into_iter()
.join(", "); .join(", ");
match func.as_assoc_item(db).map(|it| it.container(db)) { match func.as_assoc_item(db).map(|it| it.container(db)) {
Some(container) => { Some(container) => {
let container_name = match container { let container_name = match container {
crate::AssocItemContainer::Trait(trait_) => { crate::AssocItemContainer::Trait(trait_) => {
mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_)) mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_))?
} }
crate::AssocItemContainer::Impl(imp) => { crate::AssocItemContainer::Impl(imp) => {
let self_ty = imp.self_ty(db); let self_ty = imp.self_ty(db);
@ -190,17 +200,17 @@ impl Expr {
} }
}; };
let fn_name = func.name(db).display(db.upcast()).to_string(); let fn_name = func.name(db).display(db.upcast()).to_string();
format!("{container_name}::{fn_name}({args})",) Ok(format!("{container_name}::{fn_name}({args})"))
} }
None => { None => {
let fn_name = mod_item_path_str(sema_scope, &ModuleDef::Function(*func)); let fn_name = mod_item_path_str(sema_scope, &ModuleDef::Function(*func))?;
format!("{fn_name}({args})",) Ok(format!("{fn_name}({args})"))
} }
} }
} }
Expr::Method { func, target, params, .. } => { Expr::Method { func, target, params, .. } => {
if target.contains_many_in_illegal_pos() { if target.contains_many_in_illegal_pos() {
return many_formatter(&target.ty(db)); return Ok(many_formatter(&target.ty(db)));
} }
let func_name = func.name(db).display(db.upcast()).to_string(); let func_name = func.name(db).display(db.upcast()).to_string();
@ -210,28 +220,31 @@ impl Expr {
many_formatter, many_formatter,
prefer_no_std, prefer_no_std,
prefer_prelude, prefer_prelude,
); )?;
let args = params let args = params
.iter() .iter()
.map(|f| { .map(|f| {
f.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude) f.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude)
}) })
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
.into_iter()
.join(", "); .join(", ");
match func.as_assoc_item(db).and_then(|it| it.containing_trait_or_trait_impl(db)) { match func.as_assoc_item(db).and_then(|it| it.container_or_implemented_trait(db)) {
Some(trait_) => { Some(trait_) => {
let trait_name = mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_)); let trait_name = mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_))?;
let target = match self_param.access(db) { let target = match self_param.access(db) {
crate::Access::Shared => format!("&{target}"), crate::Access::Shared => format!("&{target}"),
crate::Access::Exclusive => format!("&mut {target}"), crate::Access::Exclusive => format!("&mut {target}"),
crate::Access::Owned => target, crate::Access::Owned => target,
}; };
match args.is_empty() { let res = match args.is_empty() {
true => format!("{trait_name}::{func_name}({target})",), true => format!("{trait_name}::{func_name}({target})",),
false => format!("{trait_name}::{func_name}({target}, {args})",), false => format!("{trait_name}::{func_name}({target}, {args})",),
} };
Ok(res)
} }
None => format!("{target}.{func_name}({args})"), None => Ok(format!("{target}.{func_name}({args})")),
} }
} }
Expr::Variant { variant, generics, params } => { Expr::Variant { variant, generics, params } => {
@ -242,6 +255,8 @@ impl Expr {
let generics = generics let generics = generics
.iter() .iter()
.map(|it| type_path(sema_scope, it, prefer_no_std, prefer_prelude)) .map(|it| type_path(sema_scope, it, prefer_no_std, prefer_prelude))
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
.into_iter()
.join(", "); .join(", ");
format!("::<{generics}>") format!("::<{generics}>")
} }
@ -258,6 +273,8 @@ impl Expr {
prefer_prelude, prefer_prelude,
) )
}) })
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
.into_iter()
.join(", "); .join(", ");
format!("{generics_str}({args})") format!("{generics_str}({args})")
} }
@ -267,25 +284,28 @@ impl Expr {
.iter() .iter()
.zip(fields.iter()) .zip(fields.iter())
.map(|(a, f)| { .map(|(a, f)| {
format!( let tmp = format!(
"{}: {}", "{}: {}",
f.name(db).display(db.upcast()).to_string(), f.name(db).display(db.upcast()),
a.gen_source_code( a.gen_source_code(
sema_scope, sema_scope,
many_formatter, many_formatter,
prefer_no_std, prefer_no_std,
prefer_prelude prefer_prelude
) )?
) );
Ok(tmp)
}) })
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
.into_iter()
.join(", "); .join(", ");
format!("{generics_str}{{ {args} }}") format!("{generics_str}{{ {args} }}")
} }
StructKind::Unit => generics_str, StructKind::Unit => generics_str,
}; };
let prefix = mod_item_path_str(sema_scope, &ModuleDef::Variant(*variant)); let prefix = mod_item_path_str(sema_scope, &ModuleDef::Variant(*variant))?;
format!("{prefix}{inner}") Ok(format!("{prefix}{inner}"))
} }
Expr::Struct { strukt, generics, params } => { Expr::Struct { strukt, generics, params } => {
let generics = non_default_generics(db, (*strukt).into(), generics); let generics = non_default_generics(db, (*strukt).into(), generics);
@ -301,6 +321,8 @@ impl Expr {
prefer_prelude, prefer_prelude,
) )
}) })
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
.into_iter()
.join(", "); .join(", ");
format!("({args})") format!("({args})")
} }
@ -310,17 +332,20 @@ impl Expr {
.iter() .iter()
.zip(fields.iter()) .zip(fields.iter())
.map(|(a, f)| { .map(|(a, f)| {
format!( let tmp = format!(
"{}: {}", "{}: {}",
f.name(db).display(db.upcast()).to_string(), f.name(db).display(db.upcast()),
a.gen_source_code( a.gen_source_code(
sema_scope, sema_scope,
many_formatter, many_formatter,
prefer_no_std, prefer_no_std,
prefer_prelude prefer_prelude
) )?
) );
Ok(tmp)
}) })
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
.into_iter()
.join(", "); .join(", ");
format!(" {{ {args} }}") format!(" {{ {args} }}")
} }
@ -330,35 +355,45 @@ impl Expr {
let generics = generics let generics = generics
.iter() .iter()
.map(|it| type_path(sema_scope, it, prefer_no_std, prefer_prelude)) .map(|it| type_path(sema_scope, it, prefer_no_std, prefer_prelude))
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
.into_iter()
.join(", "); .join(", ");
format!("::<{generics}>") format!("::<{generics}>")
} }
}, },
}; };
let prefix = mod_item_path_str(sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt))); let prefix = mod_item_path_str(sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt)))?;
format!("{prefix}{inner}") Ok(format!("{prefix}{inner}"))
} }
Expr::Field { expr, field } => { Expr::Field { expr, field } => {
if expr.contains_many_in_illegal_pos() { if expr.contains_many_in_illegal_pos() {
return many_formatter(&expr.ty(db)); return Ok(many_formatter(&expr.ty(db)));
} }
let strukt = let strukt = expr.gen_source_code(
expr.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude); sema_scope,
many_formatter,
prefer_no_std,
prefer_prelude,
)?;
let field = field.name(db).display(db.upcast()).to_string(); let field = field.name(db).display(db.upcast()).to_string();
format!("{strukt}.{field}") Ok(format!("{strukt}.{field}"))
} }
Expr::Reference(expr) => { Expr::Reference(expr) => {
if expr.contains_many_in_illegal_pos() { if expr.contains_many_in_illegal_pos() {
return many_formatter(&expr.ty(db)); return Ok(many_formatter(&expr.ty(db)));
} }
let inner = let inner = expr.gen_source_code(
expr.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude); sema_scope,
format!("&{inner}") many_formatter,
prefer_no_std,
prefer_prelude,
)?;
Ok(format!("&{inner}"))
} }
Expr::Many(ty) => many_formatter(ty), Expr::Many(ty) => Ok(many_formatter(ty)),
} }
} }
@ -380,10 +415,10 @@ impl Expr {
target.ty(db).type_arguments().chain(generics.iter().cloned()), target.ty(db).type_arguments().chain(generics.iter().cloned()),
), ),
Expr::Variant { variant, generics, .. } => { Expr::Variant { variant, generics, .. } => {
variant.parent_enum(db).ty_with_args(db, generics.iter().cloned()) Adt::from(variant.parent_enum(db)).ty_with_args(db, generics.iter().cloned())
} }
Expr::Struct { strukt, generics, .. } => { Expr::Struct { strukt, generics, .. } => {
strukt.ty_with_args(db, generics.iter().cloned()) Adt::from(*strukt).ty_with_args(db, generics.iter().cloned())
} }
Expr::Field { expr, field } => field.ty_with_args(db, expr.ty(db).type_arguments()), Expr::Field { expr, field } => field.ty_with_args(db, expr.ty(db).type_arguments()),
Expr::Reference(it) => it.ty(db), Expr::Reference(it) => it.ty(db),
@ -395,16 +430,13 @@ impl Expr {
pub fn traits_used(&self, db: &dyn HirDatabase) -> Vec<Trait> { pub fn traits_used(&self, db: &dyn HirDatabase) -> Vec<Trait> {
let mut res = Vec::new(); let mut res = Vec::new();
match self { if let Expr::Method { func, params, .. } = self {
Expr::Method { func, params, .. } => { res.extend(params.iter().flat_map(|it| it.traits_used(db)));
res.extend(params.iter().flat_map(|it| it.traits_used(db))); if let Some(it) = func.as_assoc_item(db) {
if let Some(it) = func.as_assoc_item(db) { if let Some(it) = it.container_or_implemented_trait(db) {
if let Some(it) = it.containing_trait_or_trait_impl(db) { res.push(it);
res.push(it);
}
} }
} }
_ => (),
} }
res res

View file

@ -16,7 +16,7 @@ use rustc_hash::FxHashSet;
use crate::{ use crate::{
Adt, AssocItem, Enum, GenericDef, GenericParam, HasVisibility, Impl, ModuleDef, ScopeDef, Type, Adt, AssocItem, Enum, GenericDef, GenericParam, HasVisibility, Impl, ModuleDef, ScopeDef, Type,
Variant, TypeParam, Variant,
}; };
use crate::term_search::{Expr, TermSearchConfig}; use crate::term_search::{Expr, TermSearchConfig};
@ -82,7 +82,7 @@ pub(super) fn trivial<'a, DB: HirDatabase>(
return None; return None;
} }
ty.could_unify_with_deeply(db, &ctx.goal).then(|| expr) ty.could_unify_with_deeply(db, &ctx.goal).then_some(expr)
}) })
} }
@ -118,11 +118,15 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
} }
let generics = GenericDef::from(variant.parent_enum(db)); let generics = GenericDef::from(variant.parent_enum(db));
let Some(type_params) = generics
// Ignore enums with const generics .type_or_const_params(db)
if !generics.const_params(db).is_empty() { .into_iter()
.map(|it| it.as_type_param(db))
.collect::<Option<Vec<TypeParam>>>()
else {
// Ignore enums with const generics
return Vec::new(); return Vec::new();
} };
// We currently do not check lifetime bounds so ignore all types that have something to do // We currently do not check lifetime bounds so ignore all types that have something to do
// with them // with them
@ -130,9 +134,6 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
return Vec::new(); return Vec::new();
} }
// Only account for stable type parameters for now
let type_params = generics.type_params(db);
// Only account for stable type parameters for now, unstable params can be default // Only account for stable type parameters for now, unstable params can be default
// tho, for example in `Box<T, #[unstable] A: Allocator>` // tho, for example in `Box<T, #[unstable] A: Allocator>`
if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) { if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) {
@ -154,13 +155,10 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
let mut g = generics.into_iter(); let mut g = generics.into_iter();
let generics: Vec<_> = type_params let generics: Vec<_> = type_params
.iter() .iter()
.map(|it| match it.default(db) { .map(|it| it.default(db).unwrap_or_else(|| g.next().expect("No generic")))
Some(ty) => ty,
None => g.next().expect("Missing type param"),
})
.collect(); .collect();
let enum_ty = parent_enum.ty_with_args(db, generics.iter().cloned()); let enum_ty = Adt::from(parent_enum).ty_with_args(db, generics.iter().cloned());
// Allow types with generics only if they take us straight to goal for // Allow types with generics only if they take us straight to goal for
// performance reasons // performance reasons
@ -212,9 +210,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
let exprs: Vec<(Type, Vec<Expr>)> = enum_ let exprs: Vec<(Type, Vec<Expr>)> = enum_
.variants(db) .variants(db)
.into_iter() .into_iter()
.flat_map(|it| { .flat_map(|it| variant_helper(db, lookup, *enum_, it, &ctx.goal, &ctx.config))
variant_helper(db, lookup, enum_.clone(), it, &ctx.goal, &ctx.config)
})
.collect(); .collect();
if !exprs.is_empty() { if !exprs.is_empty() {
@ -231,10 +227,12 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
let generics = GenericDef::from(*it); let generics = GenericDef::from(*it);
// Ignore enums with const generics // Ignore const params for now
if !generics.const_params(db).is_empty() { let type_params = generics
return None; .type_or_const_params(db)
} .into_iter()
.map(|it| it.as_type_param(db))
.collect::<Option<Vec<TypeParam>>>()?;
// We currently do not check lifetime bounds so ignore all types that have something to do // We currently do not check lifetime bounds so ignore all types that have something to do
// with them // with them
@ -242,8 +240,6 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
return None; return None;
} }
let type_params = generics.type_params(db);
// Only account for stable type parameters for now, unstable params can be default // Only account for stable type parameters for now, unstable params can be default
// tho, for example in `Box<T, #[unstable] A: Allocator>` // tho, for example in `Box<T, #[unstable] A: Allocator>`
if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) { if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) {
@ -265,12 +261,13 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
let mut g = generics.into_iter(); let mut g = generics.into_iter();
let generics: Vec<_> = type_params let generics: Vec<_> = type_params
.iter() .iter()
.map(|it| match it.default(db) { .map(|it| {
Some(ty) => ty, it.default(db)
None => g.next().expect("Missing type param"), .unwrap_or_else(|| g.next().expect("Missing type param"))
}) })
.collect(); .collect();
let struct_ty = it.ty_with_args(db, generics.iter().cloned());
let struct_ty = Adt::from(*it).ty_with_args(db, generics.iter().cloned());
// Allow types with generics only if they take us straight to goal for // Allow types with generics only if they take us straight to goal for
// performance reasons // performance reasons
@ -324,7 +321,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
_ => None, _ => None,
}) })
.flatten() .flatten()
.filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| exprs)) .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs))
.flatten() .flatten()
} }
@ -352,18 +349,18 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
ScopeDef::ModuleDef(ModuleDef::Function(it)) => { ScopeDef::ModuleDef(ModuleDef::Function(it)) => {
let generics = GenericDef::from(*it); let generics = GenericDef::from(*it);
// Skip functions that require const generics // Ignore const params for now
if !generics.const_params(db).is_empty() { let type_params = generics
return None; .type_or_const_params(db)
} .into_iter()
.map(|it| it.as_type_param(db))
.collect::<Option<Vec<TypeParam>>>()?;
// Ignore lifetimes as we do not check them // Ignore lifetimes as we do not check them
if !generics.lifetime_params(db).is_empty() { if !generics.lifetime_params(db).is_empty() {
return None; return None;
} }
let type_params = generics.type_params(db);
// Only account for stable type parameters for now, unstable params can be default // Only account for stable type parameters for now, unstable params can be default
// tho, for example in `Box<T, #[unstable] A: Allocator>` // tho, for example in `Box<T, #[unstable] A: Allocator>`
if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) { if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) {
@ -391,10 +388,14 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
let generics: Vec<_> = type_params let generics: Vec<_> = type_params
.iter() .iter()
.map(|it| match it.default(db) { .map(|it| match it.default(db) {
Some(ty) => ty, Some(ty) => Some(ty),
None => g.next().expect("Missing type param"), None => {
let generic = g.next().expect("Missing type param");
// Filter out generics that do not unify due to trait bounds
it.ty(db).could_unify_with(db, &generic).then_some(generic)
}
}) })
.collect(); .collect::<Option<_>>()?;
let ret_ty = it.ret_type_with_args(db, generics.iter().cloned()); let ret_ty = it.ret_type_with_args(db, generics.iter().cloned());
// Filter out private and unsafe functions // Filter out private and unsafe functions
@ -409,13 +410,13 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
// Early exit if some param cannot be filled from lookup // Early exit if some param cannot be filled from lookup
let param_exprs: Vec<Vec<Expr>> = it let param_exprs: Vec<Vec<Expr>> = it
.params_without_self_with_generics(db, generics.iter().cloned()) .params_without_self_with_args(db, generics.iter().cloned())
.into_iter() .into_iter()
.map(|field| { .map(|field| {
let ty = field.ty(); let ty = field.ty();
match ty.is_mutable_reference() { match ty.is_mutable_reference() {
true => None, true => None,
false => lookup.find_autoref(db, &ty), false => lookup.find_autoref(db, ty),
} }
}) })
.collect::<Option<_>>()?; .collect::<Option<_>>()?;
@ -447,7 +448,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
_ => None, _ => None,
}) })
.flatten() .flatten()
.filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| exprs)) .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs))
.flatten() .flatten()
} }
@ -487,11 +488,19 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
let fn_generics = GenericDef::from(it); let fn_generics = GenericDef::from(it);
let imp_generics = GenericDef::from(imp); let imp_generics = GenericDef::from(imp);
// Ignore impl if it has const type arguments // Ignore const params for now
if !fn_generics.const_params(db).is_empty() || !imp_generics.const_params(db).is_empty() let imp_type_params = imp_generics
{ .type_or_const_params(db)
return None; .into_iter()
} .map(|it| it.as_type_param(db))
.collect::<Option<Vec<TypeParam>>>()?;
// Ignore const params for now
let fn_type_params = fn_generics
.type_or_const_params(db)
.into_iter()
.map(|it| it.as_type_param(db))
.collect::<Option<Vec<TypeParam>>>()?;
// Ignore all functions that have something to do with lifetimes as we don't check them // Ignore all functions that have something to do with lifetimes as we don't check them
if !fn_generics.lifetime_params(db).is_empty() { if !fn_generics.lifetime_params(db).is_empty() {
@ -508,9 +517,6 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
return None; return None;
} }
let imp_type_params = imp_generics.type_params(db);
let fn_type_params = fn_generics.type_params(db);
// Only account for stable type parameters for now, unstable params can be default // Only account for stable type parameters for now, unstable params can be default
// tho, for example in `Box<T, #[unstable] A: Allocator>` // tho, for example in `Box<T, #[unstable] A: Allocator>`
if imp_type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) if imp_type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none())
@ -544,10 +550,14 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
.iter() .iter()
.chain(fn_type_params.iter()) .chain(fn_type_params.iter())
.map(|it| match it.default(db) { .map(|it| match it.default(db) {
Some(ty) => ty, Some(ty) => Some(ty),
None => g.next().expect("Missing type param"), None => {
let generic = g.next().expect("Missing type param");
// Filter out generics that do not unify due to trait bounds
it.ty(db).could_unify_with(db, &generic).then_some(generic)
}
}) })
.collect(); .collect::<Option<_>>()?;
let ret_ty = it.ret_type_with_args( let ret_ty = it.ret_type_with_args(
db, db,
@ -579,16 +589,16 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
// Early exit if some param cannot be filled from lookup // Early exit if some param cannot be filled from lookup
let param_exprs: Vec<Vec<Expr>> = it let param_exprs: Vec<Vec<Expr>> = it
.params_without_self_with_generics( .params_without_self_with_args(
db, db,
ty.type_arguments().chain(generics.iter().cloned()), ty.type_arguments().chain(generics.iter().cloned()),
) )
.into_iter() .into_iter()
.map(|field| lookup.find_autoref(db, &field.ty())) .map(|field| lookup.find_autoref(db, field.ty()))
.collect::<Option<_>>()?; .collect::<Option<_>>()?;
let fn_exprs: Vec<Expr> = std::iter::once(target_type_exprs) let fn_exprs: Vec<Expr> = std::iter::once(target_type_exprs)
.chain(param_exprs.into_iter()) .chain(param_exprs)
.multi_cartesian_product() .multi_cartesian_product()
.map(|params| { .map(|params| {
let mut params = params.into_iter(); let mut params = params.into_iter();
@ -609,7 +619,7 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
Some(exprs) Some(exprs)
}) })
.flatten() .flatten()
.filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| exprs)) .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs))
.flatten() .flatten()
} }
@ -647,7 +657,7 @@ pub(super) fn struct_projection<'a, DB: HirDatabase>(
Some((filed_ty, exprs)) Some((filed_ty, exprs))
}) })
}) })
.filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| exprs)) .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs))
.flatten() .flatten()
} }
@ -719,11 +729,19 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
let fn_generics = GenericDef::from(it); let fn_generics = GenericDef::from(it);
let imp_generics = GenericDef::from(imp); let imp_generics = GenericDef::from(imp);
// Ignore impl if it has const type arguments // Ignore const params for now
if !fn_generics.const_params(db).is_empty() || !imp_generics.const_params(db).is_empty() let imp_type_params = imp_generics
{ .type_or_const_params(db)
return None; .into_iter()
} .map(|it| it.as_type_param(db))
.collect::<Option<Vec<TypeParam>>>()?;
// Ignore const params for now
let fn_type_params = fn_generics
.type_or_const_params(db)
.into_iter()
.map(|it| it.as_type_param(db))
.collect::<Option<Vec<TypeParam>>>()?;
// Ignore all functions that have something to do with lifetimes as we don't check them // Ignore all functions that have something to do with lifetimes as we don't check them
if !fn_generics.lifetime_params(db).is_empty() if !fn_generics.lifetime_params(db).is_empty()
@ -742,9 +760,6 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
return None; return None;
} }
let imp_type_params = imp_generics.type_params(db);
let fn_type_params = fn_generics.type_params(db);
// Only account for stable type parameters for now, unstable params can be default // Only account for stable type parameters for now, unstable params can be default
// tho, for example in `Box<T, #[unstable] A: Allocator>` // tho, for example in `Box<T, #[unstable] A: Allocator>`
if imp_type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) if imp_type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none())
@ -778,10 +793,17 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
.iter() .iter()
.chain(fn_type_params.iter()) .chain(fn_type_params.iter())
.map(|it| match it.default(db) { .map(|it| match it.default(db) {
Some(ty) => ty, Some(ty) => Some(ty),
None => g.next().expect("Missing type param"), None => {
let generic = g.next().expect("Missing type param");
it.trait_bounds(db)
.into_iter()
.all(|bound| generic.impls_trait(db, bound, &[]));
// Filter out generics that do not unify due to trait bounds
it.ty(db).could_unify_with(db, &generic).then_some(generic)
}
}) })
.collect(); .collect::<Option<_>>()?;
let ret_ty = it.ret_type_with_args( let ret_ty = it.ret_type_with_args(
db, db,
@ -801,12 +823,12 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
// Early exit if some param cannot be filled from lookup // Early exit if some param cannot be filled from lookup
let param_exprs: Vec<Vec<Expr>> = it let param_exprs: Vec<Vec<Expr>> = it
.params_without_self_with_generics( .params_without_self_with_args(
db, db,
ty.type_arguments().chain(generics.iter().cloned()), ty.type_arguments().chain(generics.iter().cloned()),
) )
.into_iter() .into_iter()
.map(|field| lookup.find_autoref(db, &field.ty())) .map(|field| lookup.find_autoref(db, field.ty()))
.collect::<Option<_>>()?; .collect::<Option<_>>()?;
// Note that we need special case for 0 param constructors because of multi cartesian // Note that we need special case for 0 param constructors because of multi cartesian
@ -832,6 +854,6 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
Some(exprs) Some(exprs)
}) })
.flatten() .flatten()
.filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| exprs)) .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs))
.flatten() .flatten()
} }

View file

@ -1,6 +1,9 @@
//! Term search assist //! Term search assist
use hir::term_search::TermSearchCtx; use hir::term_search::TermSearchCtx;
use ide_db::assists::{AssistId, AssistKind, GroupLabel}; use ide_db::{
assists::{AssistId, AssistKind, GroupLabel},
famous_defs::FamousDefs,
};
use itertools::Itertools; use itertools::Itertools;
use syntax::{ast, AstNode}; use syntax::{ast, AstNode};
@ -12,18 +15,21 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
let syntax = unexpanded.syntax(); let syntax = unexpanded.syntax();
let goal_range = syntax.text_range(); let goal_range = syntax.text_range();
let excl = unexpanded.excl_token()?; let parent = syntax.parent()?;
let macro_name_token = excl.prev_token()?; let scope = ctx.sema.scope(&parent)?;
let name = macro_name_token.text();
if name != "todo" { let macro_call = ctx.sema.resolve_macro_call(&unexpanded)?;
let famous_defs = FamousDefs(&ctx.sema, scope.krate());
let std_todo = famous_defs.core_macros_todo()?;
let std_unimplemented = famous_defs.core_macros_unimplemented()?;
if macro_call != std_todo && macro_call != std_unimplemented {
return None; return None;
} }
let parent = syntax.parent()?;
let target_ty = ctx.sema.type_of_expr(&ast::Expr::cast(parent.clone())?)?.adjusted(); let target_ty = ctx.sema.type_of_expr(&ast::Expr::cast(parent.clone())?)?.adjusted();
let scope = ctx.sema.scope(&parent)?;
let term_search_ctx = TermSearchCtx { let term_search_ctx = TermSearchCtx {
sema: &ctx.sema, sema: &ctx.sema,
scope: &scope, scope: &scope,
@ -37,13 +43,21 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
} }
let mut formatter = |_: &hir::Type| String::from("todo!()"); let mut formatter = |_: &hir::Type| String::from("todo!()");
for path in paths.iter().unique() {
let code = path.gen_source_code( let paths = paths
&scope, .into_iter()
&mut formatter, .filter_map(|path| {
ctx.config.prefer_no_std, path.gen_source_code(
ctx.config.prefer_prelude, &scope,
); &mut formatter,
ctx.config.prefer_no_std,
ctx.config.prefer_prelude,
)
.ok()
})
.unique();
for code in paths {
acc.add_group( acc.add_group(
&GroupLabel(String::from("Term search")), &GroupLabel(String::from("Term search")),
AssistId("term_search", AssistKind::Generate), AssistId("term_search", AssistKind::Generate),
@ -68,8 +82,9 @@ mod tests {
fn test_complete_local() { fn test_complete_local() {
check_assist( check_assist(
term_search, term_search,
"macro_rules! todo { () => (_) }; fn f() { let a: u128 = 1; let b: u128 = todo$0!() }", r#"//- minicore: todo, unimplemented
"macro_rules! todo { () => (_) }; fn f() { let a: u128 = 1; let b: u128 = a }", fn f() { let a: u128 = 1; let b: u128 = todo$0!() }"#,
r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
) )
} }
@ -77,8 +92,29 @@ mod tests {
fn test_complete_todo_with_msg() { fn test_complete_todo_with_msg() {
check_assist( check_assist(
term_search, term_search,
"macro_rules! todo { ($($arg:tt)+) => (_) }; fn f() { let a: u128 = 1; let b: u128 = todo$0!(\"asd\") }", r#"//- minicore: todo, unimplemented
"macro_rules! todo { ($($arg:tt)+) => (_) }; fn f() { let a: u128 = 1; let b: u128 = a }", fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
)
}
#[test]
fn test_complete_unimplemented_with_msg() {
check_assist(
term_search,
r#"//- minicore: todo, unimplemented
fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
)
}
#[test]
fn test_complete_unimplemented() {
check_assist(
term_search,
r#"//- minicore: todo, unimplemented
fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
) )
} }
@ -86,12 +122,11 @@ mod tests {
fn test_complete_struct_field() { fn test_complete_struct_field() {
check_assist( check_assist(
term_search, term_search,
r#"macro_rules! todo { () => (_) }; r#"//- minicore: todo, unimplemented
struct A { pub x: i32, y: bool } struct A { pub x: i32, y: bool }
fn f() { let a = A { x: 1, y: true }; let b: i32 = todo$0!(); }"#, fn f() { let a = A { x: 1, y: true }; let b: i32 = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) }; r#"struct A { pub x: i32, y: bool }
struct A { pub x: i32, y: bool } fn f() { let a = A { x: 1, y: true }; let b: i32 = a.x; }"#,
fn f() { let a = A { x: 1, y: true }; let b: i32 = a.x; }"#,
) )
} }
@ -99,12 +134,9 @@ mod tests {
fn test_enum_with_generics() { fn test_enum_with_generics() {
check_assist( check_assist(
term_search, term_search,
r#"macro_rules! todo { () => (_) }; r#"//- minicore: todo, unimplemented, option
enum Option<T> { Some(T), None } fn f() { let a: i32 = 1; let b: Option<i32> = todo$0!(); }"#,
fn f() { let a: i32 = 1; let b: Option<i32> = todo$0!(); }"#, r#"fn f() { let a: i32 = 1; let b: Option<i32> = None; }"#,
r#"macro_rules! todo { () => (_) };
enum Option<T> { Some(T), None }
fn f() { let a: i32 = 1; let b: Option<i32> = Option::None; }"#,
) )
} }
@ -112,12 +144,11 @@ mod tests {
fn test_enum_with_generics2() { fn test_enum_with_generics2() {
check_assist( check_assist(
term_search, term_search,
r#"macro_rules! todo { () => (_) }; r#"//- minicore: todo, unimplemented
enum Option<T> { None, Some(T) } enum Option<T> { None, Some(T) }
fn f() { let a: i32 = 1; let b: Option<i32> = todo$0!(); }"#, fn f() { let a: i32 = 1; let b: Option<i32> = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) }; r#"enum Option<T> { None, Some(T) }
enum Option<T> { None, Some(T) } fn f() { let a: i32 = 1; let b: Option<i32> = Option::Some(a); }"#,
fn f() { let a: i32 = 1; let b: Option<i32> = Option::Some(a); }"#,
) )
} }
@ -125,12 +156,11 @@ mod tests {
fn test_enum_with_generics3() { fn test_enum_with_generics3() {
check_assist( check_assist(
term_search, term_search,
r#"macro_rules! todo { () => (_) }; r#"//- minicore: todo, unimplemented
enum Option<T> { None, Some(T) } enum Option<T> { None, Some(T) }
fn f() { let a: Option<i32> = Option::None; let b: Option<Option<i32>> = todo$0!(); }"#, fn f() { let a: Option<i32> = Option::None; let b: Option<Option<i32>> = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) }; r#"enum Option<T> { None, Some(T) }
enum Option<T> { None, Some(T) } fn f() { let a: Option<i32> = Option::None; let b: Option<Option<i32>> = Option::Some(a); }"#,
fn f() { let a: Option<i32> = Option::None; let b: Option<Option<i32>> = Option::Some(a); }"#,
) )
} }
@ -138,22 +168,20 @@ mod tests {
fn test_enum_with_generics4() { fn test_enum_with_generics4() {
check_assist( check_assist(
term_search, term_search,
r#"macro_rules! todo { () => (_) }; r#"//- minicore: todo, unimplemented
enum Foo<T = i32> { Foo(T) } enum Foo<T = i32> { Foo(T) }
fn f() { let a = 0; let b: Foo = todo$0!(); }"#, fn f() { let a = 0; let b: Foo = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) }; r#"enum Foo<T = i32> { Foo(T) }
enum Foo<T = i32> { Foo(T) } fn f() { let a = 0; let b: Foo = Foo::Foo(a); }"#,
fn f() { let a = 0; let b: Foo = Foo::Foo(a); }"#,
); );
check_assist( check_assist(
term_search, term_search,
r#"macro_rules! todo { () => (_) }; r#"//- minicore: todo, unimplemented
enum Foo<T = i32> { Foo(T) } enum Foo<T = i32> { Foo(T) }
fn f() { let a: Foo<u32> = Foo::Foo(0); let b: Foo<u32> = todo$0!(); }"#, fn f() { let a: Foo<u32> = Foo::Foo(0); let b: Foo<u32> = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) }; r#"enum Foo<T = i32> { Foo(T) }
enum Foo<T = i32> { Foo(T) } fn f() { let a: Foo<u32> = Foo::Foo(0); let b: Foo<u32> = a; }"#,
fn f() { let a: Foo<u32> = Foo::Foo(0); let b: Foo<u32> = a; }"#,
) )
} }
@ -161,12 +189,11 @@ mod tests {
fn test_newtype() { fn test_newtype() {
check_assist( check_assist(
term_search, term_search,
r#"macro_rules! todo { () => (_) }; r#"//- minicore: todo, unimplemented
struct Foo(i32); struct Foo(i32);
fn f() { let a: i32 = 1; let b: Foo = todo$0!(); }"#, fn f() { let a: i32 = 1; let b: Foo = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) }; r#"struct Foo(i32);
struct Foo(i32); fn f() { let a: i32 = 1; let b: Foo = Foo(a); }"#,
fn f() { let a: i32 = 1; let b: Foo = Foo(a); }"#,
) )
} }
@ -174,10 +201,9 @@ mod tests {
fn test_shadowing() { fn test_shadowing() {
check_assist( check_assist(
term_search, term_search,
r#"macro_rules! todo { () => (_) }; r#"//- minicore: todo, unimplemented
fn f() { let a: i32 = 1; let b: i32 = 2; let a: u32 = 0; let c: i32 = todo$0!(); }"#, fn f() { let a: i32 = 1; let b: i32 = 2; let a: u32 = 0; let c: i32 = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) }; r#"fn f() { let a: i32 = 1; let b: i32 = 2; let a: u32 = 0; let c: i32 = b; }"#,
fn f() { let a: i32 = 1; let b: i32 = 2; let a: u32 = 0; let c: i32 = b; }"#,
) )
} }
@ -185,10 +211,9 @@ mod tests {
fn test_famous_bool() { fn test_famous_bool() {
check_assist( check_assist(
term_search, term_search,
r#"macro_rules! todo { () => (_) }; r#"//- minicore: todo, unimplemented
fn f() { let a: bool = todo$0!(); }"#, fn f() { let a: bool = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) }; r#"fn f() { let a: bool = false; }"#,
fn f() { let a: bool = false; }"#,
) )
} }
@ -196,12 +221,11 @@ mod tests {
fn test_fn_with_reference_types() { fn test_fn_with_reference_types() {
check_assist( check_assist(
term_search, term_search,
r#"macro_rules! todo { () => (_) }; r#"//- minicore: todo, unimplemented
fn f(a: &i32) -> f32 { a as f32 } fn f(a: &i32) -> f32 { a as f32 }
fn g() { let a = 1; let b: f32 = todo$0!(); }"#, fn g() { let a = 1; let b: f32 = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) }; r#"fn f(a: &i32) -> f32 { a as f32 }
fn f(a: &i32) -> f32 { a as f32 } fn g() { let a = 1; let b: f32 = f(&a); }"#,
fn g() { let a = 1; let b: f32 = f(&a); }"#,
) )
} }
@ -209,12 +233,11 @@ mod tests {
fn test_fn_with_reference_types2() { fn test_fn_with_reference_types2() {
check_assist( check_assist(
term_search, term_search,
r#"macro_rules! todo { () => (_) }; r#"//- minicore: todo, unimplemented
fn f(a: &i32) -> f32 { a as f32 } fn f(a: &i32) -> f32 { a as f32 }
fn g() { let a = &1; let b: f32 = todo$0!(); }"#, fn g() { let a = &1; let b: f32 = todo$0!(); }"#,
r#"macro_rules! todo { () => (_) }; r#"fn f(a: &i32) -> f32 { a as f32 }
fn f(a: &i32) -> f32 { a as f32 } fn g() { let a = &1; let b: f32 = f(a); }"#,
fn g() { let a = &1; let b: f32 = f(a); }"#,
) )
} }
@ -222,7 +245,7 @@ mod tests {
fn test_fn_with_reference_types3() { fn test_fn_with_reference_types3() {
check_assist_not_applicable( check_assist_not_applicable(
term_search, term_search,
r#"macro_rules! todo { () => (_) }; r#"//- minicore: todo, unimplemented
fn f(a: &i32) -> f32 { a as f32 } fn f(a: &i32) -> f32 { a as f32 }
fn g() { let a = &mut 1; let b: f32 = todo$0!(); }"#, fn g() { let a = &mut 1; let b: f32 = todo$0!(); }"#,
) )

View file

@ -159,9 +159,8 @@ impl Completions {
} }
pub(crate) fn add_expr(&mut self, ctx: &CompletionContext<'_>, expr: &hir::term_search::Expr) { pub(crate) fn add_expr(&mut self, ctx: &CompletionContext<'_>, expr: &hir::term_search::Expr) {
match render_expr(ctx, expr) { if let Some(item) = render_expr(ctx, expr) {
Some(item) => item.add_to(self, ctx.db), item.add_to(self, ctx.db)
None => (),
} }
} }
@ -759,7 +758,6 @@ pub(super) fn complete_name_ref(
flyimport::import_on_the_fly_dot(acc, ctx, dot_access); flyimport::import_on_the_fly_dot(acc, ctx, dot_access);
dot::complete_dot(acc, ctx, dot_access); dot::complete_dot(acc, ctx, dot_access);
postfix::complete_postfix(acc, ctx, dot_access); postfix::complete_postfix(acc, ctx, dot_access);
expr::complete_expr(acc, ctx);
} }
NameRefKind::Keyword(item) => { NameRefKind::Keyword(item) => {
keyword::complete_for_and_where(acc, ctx, item); keyword::complete_for_and_where(acc, ctx, item);

View file

@ -342,7 +342,7 @@ pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>)
if let Some(ty) = &ctx.expected_type { if let Some(ty) = &ctx.expected_type {
// Ignore unit types as they are not very interesting // Ignore unit types as they are not very interesting
if ty.is_unit() { if ty.is_unit() || ty.is_unknown() {
return; return;
} }

View file

@ -297,6 +297,7 @@ pub enum CompletionItemKind {
Method, Method,
Snippet, Snippet,
UnresolvedReference, UnresolvedReference,
Expression,
} }
impl_from!(SymbolKind for CompletionItemKind); impl_from!(SymbolKind for CompletionItemKind);
@ -341,6 +342,7 @@ impl CompletionItemKind {
CompletionItemKind::Method => "me", CompletionItemKind::Method => "me",
CompletionItemKind::Snippet => "sn", CompletionItemKind::Snippet => "sn",
CompletionItemKind::UnresolvedReference => "??", CompletionItemKind::UnresolvedReference => "??",
CompletionItemKind::Expression => "ex",
} }
} }
} }

View file

@ -295,22 +295,24 @@ pub(crate) fn render_expr(
.unwrap_or_else(|| String::from("...")) .unwrap_or_else(|| String::from("..."))
}; };
let label = expr.gen_source_code( let label = expr
&ctx.scope, .gen_source_code(
&mut label_formatter, &ctx.scope,
ctx.config.prefer_no_std, &mut label_formatter,
ctx.config.prefer_prelude, ctx.config.prefer_no_std,
); ctx.config.prefer_prelude,
)
.ok()?;
let source_range = match ctx.original_token.parent() { let source_range = match ctx.original_token.parent() {
Some(node) => match node.ancestors().find_map(|n| ast::Path::cast(n)) { Some(node) => match node.ancestors().find_map(ast::Path::cast) {
Some(path) => path.syntax().text_range(), Some(path) => path.syntax().text_range(),
None => node.text_range(), None => node.text_range(),
}, },
None => ctx.source_range(), None => ctx.source_range(),
}; };
let mut item = CompletionItem::new(CompletionItemKind::Snippet, source_range, label.clone()); let mut item = CompletionItem::new(CompletionItemKind::Expression, source_range, label.clone());
let snippet = format!( let snippet = format!(
"{}$0", "{}$0",
@ -320,6 +322,7 @@ pub(crate) fn render_expr(
ctx.config.prefer_no_std, ctx.config.prefer_no_std,
ctx.config.prefer_prelude ctx.config.prefer_prelude
) )
.ok()?
); );
let edit = TextEdit::replace(source_range, snippet); let edit = TextEdit::replace(source_range, snippet);
item.snippet_edit(ctx.config.snippet_cap?, edit); item.snippet_edit(ctx.config.snippet_cap?, edit);
@ -1034,6 +1037,7 @@ fn func(input: Struct) { }
st Self [type] st Self [type]
sp Self [type] sp Self [type]
st Struct [type] st Struct [type]
ex Struct [type]
lc self [local] lc self [local]
fn func() [] fn func() []
me self.test() [] me self.test() []
@ -1058,6 +1062,9 @@ fn main() {
"#, "#,
expect![[r#" expect![[r#"
lc input [type+name+local] lc input [type+name+local]
ex input [type]
ex true [type]
ex false [type]
lc inputbad [local] lc inputbad [local]
fn main() [] fn main() []
fn test() [] fn test() []
@ -1738,6 +1745,10 @@ fn f() { A { bar: b$0 }; }
expect![[r#" expect![[r#"
fn bar() [type+name] fn bar() [type+name]
fn baz() [type] fn baz() [type]
ex baz() [type]
ex bar() [type]
ex A { bar: baz() }.bar [type]
ex A { bar: bar() }.bar [type]
st A [] st A []
fn f() [] fn f() []
"#]], "#]],
@ -1822,6 +1833,8 @@ fn main() {
lc s [type+name+local] lc s [type+name+local]
st S [type] st S [type]
st S [type] st S [type]
ex s [type]
ex S [type]
fn foo() [] fn foo() []
fn main() [] fn main() []
"#]], "#]],
@ -1839,6 +1852,8 @@ fn main() {
lc ssss [type+local] lc ssss [type+local]
st S [type] st S [type]
st S [type] st S [type]
ex ssss [type]
ex S [type]
fn foo() [] fn foo() []
fn main() [] fn main() []
"#]], "#]],
@ -1871,6 +1886,8 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify]
ex core::ops::Deref::deref(&t) (use core::ops::Deref) [type_could_unify]
lc m [local] lc m [local]
lc t [local] lc t [local]
lc &t [type+local] lc &t [type+local]
@ -1919,6 +1936,8 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ex core::ops::DerefMut::deref_mut(&mut T(S)) (use core::ops::DerefMut) [type_could_unify]
ex core::ops::DerefMut::deref_mut(&mut t) (use core::ops::DerefMut) [type_could_unify]
lc m [local] lc m [local]
lc t [local] lc t [local]
lc &mut t [type+local] lc &mut t [type+local]
@ -1967,6 +1986,8 @@ fn bar(t: Foo) {}
ev Foo::A [type] ev Foo::A [type]
ev Foo::B [type] ev Foo::B [type]
en Foo [type] en Foo [type]
ex Foo::A [type]
ex Foo::B [type]
fn bar() [] fn bar() []
fn foo() [] fn foo() []
"#]], "#]],
@ -2020,6 +2041,8 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify]
ex core::ops::Deref::deref(&bar()) (use core::ops::Deref) [type_could_unify]
st S [] st S []
st &S [type] st &S [type]
st S [] st S []
@ -2233,6 +2256,7 @@ fn foo() {
"#, "#,
expect![[r#" expect![[r#"
lc foo [type+local] lc foo [type+local]
ex foo [type]
ev Foo::A() [type_could_unify] ev Foo::A() [type_could_unify]
ev Foo::B [type_could_unify] ev Foo::B [type_could_unify]
en Foo [type_could_unify] en Foo [type_could_unify]
@ -2267,8 +2291,6 @@ fn main() {
&[CompletionItemKind::Snippet, CompletionItemKind::Method], &[CompletionItemKind::Snippet, CompletionItemKind::Method],
expect![[r#" expect![[r#"
sn not [snippet] sn not [snippet]
sn true [type]
sn false [type]
me not() (use ops::Not) [type_could_unify+requires_import] me not() (use ops::Not) [type_could_unify+requires_import]
sn if [] sn if []
sn while [] sn while []

View file

@ -97,11 +97,11 @@ fn func(param0 @ (param1, param2): (i32, i32)) {
kw unsafe kw unsafe
kw while kw while
kw while let kw while let
sn ifletlocal ex ifletlocal
sn letlocal ex letlocal
sn matcharm ex matcharm
sn param1 ex param1
sn param2 ex param2
"#]], "#]],
); );
} }
@ -243,11 +243,11 @@ fn complete_in_block() {
kw use kw use
kw while kw while
kw while let kw while let
sn false
sn macro_rules sn macro_rules
sn pd sn pd
sn ppd sn ppd
sn true ex false
ex true
"#]], "#]],
) )
} }
@ -690,8 +690,8 @@ fn main() {
"#, "#,
expect![[r#" expect![[r#"
fn test() fn() -> Zulu fn test() fn() -> Zulu
sn Zulu ex Zulu
sn Zulu::test() ex Zulu::test()
"#]], "#]],
); );
} }

View file

@ -192,8 +192,8 @@ fn main() {
bt u32 u32 bt u32 u32
kw crate:: kw crate::
kw self:: kw self::
sn Foo::default() ex Foo::default()
sn foo ex foo
"#]], "#]],
); );
check( check(

View file

@ -225,10 +225,10 @@ impl S {
fn foo() { let _ = lib::S::$0 } fn foo() { let _ = lib::S::$0 }
"#, "#,
expect![[r#" expect![[r#"
ct PUBLIC_CONST pub const PUBLIC_CONST: u32 ct PUBLIC_CONST pub const PUBLIC_CONST: u32
fn public_method() fn() fn public_method() fn()
ta PublicType pub type PublicType = u32 ta PublicType pub type PublicType = u32
"#]], "#]],
); );
} }
@ -242,8 +242,8 @@ impl U { fn m() { } }
fn foo() { let _ = U::$0 } fn foo() { let _ = U::$0 }
"#, "#,
expect![[r#" expect![[r#"
fn m() fn() fn m() fn()
"#]], "#]],
); );
} }
@ -256,8 +256,8 @@ trait Trait { fn m(); }
fn foo() { let _ = Trait::$0 } fn foo() { let _ = Trait::$0 }
"#, "#,
expect![[r#" expect![[r#"
fn m() (as Trait) fn() fn m() (as Trait) fn()
"#]], "#]],
); );
} }
@ -273,8 +273,8 @@ impl Trait for S {}
fn foo() { let _ = S::$0 } fn foo() { let _ = S::$0 }
"#, "#,
expect![[r#" expect![[r#"
fn m() (as Trait) fn() fn m() (as Trait) fn()
"#]], "#]],
); );
} }
@ -290,8 +290,8 @@ impl Trait for S {}
fn foo() { let _ = <S as Trait>::$0 } fn foo() { let _ = <S as Trait>::$0 }
"#, "#,
expect![[r#" expect![[r#"
fn m() (as Trait) fn() fn m() (as Trait) fn()
"#]], "#]],
); );
} }
@ -396,9 +396,9 @@ macro_rules! foo { () => {} }
fn main() { let _ = crate::$0 } fn main() { let _ = crate::$0 }
"#, "#,
expect![[r#" expect![[r#"
fn main() fn() fn main() fn()
ma foo!() macro_rules! foo ma foo!() macro_rules! foo
"#]], "#]],
); );
} }
@ -694,8 +694,10 @@ fn bar() -> Bar {
} }
"#, "#,
expect![[r#" expect![[r#"
fn foo() (as Foo) fn() -> Self fn foo() (as Foo) fn() -> Self
"#]], ex Bar
ex bar()
"#]],
); );
} }
@ -722,6 +724,8 @@ fn bar() -> Bar {
expect![[r#" expect![[r#"
fn bar() fn() fn bar() fn()
fn foo() (as Foo) fn() -> Self fn foo() (as Foo) fn() -> Self
ex Bar
ex bar()
"#]], "#]],
); );
} }
@ -748,6 +752,8 @@ fn bar() -> Bar {
"#, "#,
expect![[r#" expect![[r#"
fn foo() (as Foo) fn() -> Self fn foo() (as Foo) fn() -> Self
ex Bar
ex bar()
"#]], "#]],
); );
} }
@ -1230,10 +1236,6 @@ fn here_we_go() {
"#, "#,
expect![[r#" expect![[r#"
st Bar (alias Qux) Bar st Bar (alias Qux) Bar
sn ()
sn false
sn here_we_go()
sn true
"#]], "#]],
); );
} }
@ -1288,10 +1290,6 @@ fn here_we_go() {
kw unsafe kw unsafe
kw while kw while
kw while let kw while let
sn ()
sn false
sn here_we_go()
sn true
"#]], "#]],
); );
} }

View file

@ -114,6 +114,14 @@ impl FamousDefs<'_, '_> {
self.find_function("core:mem:drop") self.find_function("core:mem:drop")
} }
pub fn core_macros_todo(&self) -> Option<Macro> {
self.find_macro("core:todo")
}
pub fn core_macros_unimplemented(&self) -> Option<Macro> {
self.find_macro("core:unimplemented")
}
pub fn builtin_crates(&self) -> impl Iterator<Item = Crate> { pub fn builtin_crates(&self) -> impl Iterator<Item = Crate> {
IntoIterator::into_iter([ IntoIterator::into_iter([
self.std(), self.std(),

View file

@ -112,7 +112,8 @@ fn add_missing_ok_or_some(
let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" }; let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" };
let wrapped_actual_ty = expected_adt.ty_with_args(ctx.sema.db, &[d.actual.clone()]); let wrapped_actual_ty =
expected_adt.ty_with_args(ctx.sema.db, std::iter::once(d.actual.clone()));
if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) { if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) {
return None; return None;

View file

@ -51,17 +51,21 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
}; };
let paths = term_search(&term_search_ctx); let paths = term_search(&term_search_ctx);
let mut assists = vec![];
let mut formatter = |_: &hir::Type| String::from("_"); let mut formatter = |_: &hir::Type| String::from("_");
for path in paths.into_iter().unique() {
let code = path.gen_source_code(
&scope,
&mut formatter,
ctx.config.prefer_no_std,
ctx.config.prefer_prelude,
);
assists.push(Assist { let assists: Vec<Assist> = paths
.into_iter()
.filter_map(|path| {
path.gen_source_code(
&scope,
&mut formatter,
ctx.config.prefer_no_std,
ctx.config.prefer_prelude,
)
.ok()
})
.unique()
.map(|code| Assist {
id: AssistId("typed-hole", AssistKind::QuickFix), id: AssistId("typed-hole", AssistKind::QuickFix),
label: Label::new(format!("Replace `_` with `{}`", &code)), label: Label::new(format!("Replace `_` with `{}`", &code)),
group: Some(GroupLabel("Replace `_` with a term".to_owned())), group: Some(GroupLabel("Replace `_` with a term".to_owned())),
@ -71,8 +75,9 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
TextEdit::replace(original_range.range, code), TextEdit::replace(original_range.range, code),
)), )),
trigger_signature_help: false, trigger_signature_help: false,
}); })
} .collect();
if !assists.is_empty() { if !assists.is_empty() {
Some(assists) Some(assists)
} else { } else {
@ -242,31 +247,33 @@ fn main<const CP: Foo>(param: Foo) {
check_has_fix( check_has_fix(
r#" r#"
struct Bar; struct Bar;
struct Baz;
trait Foo { trait Foo {
fn foo(self) -> Bar; fn foo(self) -> Bar;
} }
impl Foo for i32 { impl Foo for Baz {
fn foo(self) -> Bar { fn foo(self) -> Bar {
unimplemented!() unimplemented!()
} }
} }
fn asd() -> Bar { fn asd() -> Bar {
let a: i32 = 1; let a = Baz;
_$0 _$0
} }
"#, "#,
r" r"
struct Bar; struct Bar;
struct Baz;
trait Foo { trait Foo {
fn foo(self) -> Bar; fn foo(self) -> Bar;
} }
impl Foo for i32 { impl Foo for Baz {
fn foo(self) -> Bar { fn foo(self) -> Bar {
unimplemented!() unimplemented!()
} }
} }
fn asd() -> Bar { fn asd() -> Bar {
let a: i32 = 1; let a = Baz;
Foo::foo(a) Foo::foo(a)
} }
", ",
@ -330,30 +337,32 @@ fn main() {
check_has_fix( check_has_fix(
r#" r#"
struct Bar {} struct Bar {}
struct A;
trait Foo { trait Foo {
type Res; type Res;
fn foo(&self) -> Self::Res; fn foo(&self) -> Self::Res;
} }
impl Foo for i32 { impl Foo for A {
type Res = Bar; type Res = Bar;
fn foo(&self) -> Self::Res { Bar { } } fn foo(&self) -> Self::Res { Bar { } }
} }
fn main() { fn main() {
let a: i32 = 1; let a = A;
let c: Bar = _$0; let c: Bar = _$0;
}"#, }"#,
r#" r#"
struct Bar {} struct Bar {}
struct A;
trait Foo { trait Foo {
type Res; type Res;
fn foo(&self) -> Self::Res; fn foo(&self) -> Self::Res;
} }
impl Foo for i32 { impl Foo for A {
type Res = Bar; type Res = Bar;
fn foo(&self) -> Self::Res { Bar { } } fn foo(&self) -> Self::Res { Bar { } }
} }
fn main() { fn main() {
let a: i32 = 1; let a = A;
let c: Bar = Foo::foo(&a); let c: Bar = Foo::foo(&a);
}"#, }"#,
); );

View file

@ -7263,8 +7263,8 @@ impl Iterator for S {
file_id: FileId( file_id: FileId(
1, 1,
), ),
full_range: 6157..6365, full_range: 6290..6498,
focus_range: 6222..6228, focus_range: 6355..6361,
name: "Future", name: "Future",
kind: Trait, kind: Trait,
container_name: "future", container_name: "future",
@ -7277,8 +7277,8 @@ impl Iterator for S {
file_id: FileId( file_id: FileId(
1, 1,
), ),
full_range: 6995..7461, full_range: 7128..7594,
focus_range: 7039..7047, focus_range: 7172..7180,
name: "Iterator", name: "Iterator",
kind: Trait, kind: Trait,
container_name: "iterator", container_name: "iterator",

View file

@ -333,10 +333,12 @@ impl flags::AnalysisStats {
mut file_ids: Vec<FileId>, mut file_ids: Vec<FileId>,
verbosity: Verbosity, verbosity: Verbosity,
) { ) {
let mut cargo_config = CargoConfig::default(); let cargo_config = CargoConfig {
cargo_config.sysroot = match self.no_sysroot { sysroot: match self.no_sysroot {
true => None, true => None,
false => Some(RustLibSource::Discover), false => Some(RustLibSource::Discover),
},
..Default::default()
}; };
let mut bar = match verbosity { let mut bar = match verbosity {
@ -392,16 +394,15 @@ impl flags::AnalysisStats {
continue; continue;
} }
let range = sema.original_range(&expected_tail.syntax()).range; let range = sema.original_range(expected_tail.syntax()).range;
let original_text: String = db let original_text: String = db
.file_text(file_id) .file_text(file_id)
.chars() .chars()
.into_iter()
.skip(usize::from(range.start())) .skip(usize::from(range.start()))
.take(usize::from(range.end()) - usize::from(range.start())) .take(usize::from(range.end()) - usize::from(range.start()))
.collect(); .collect();
let scope = match sema.scope(&expected_tail.syntax()) { let scope = match sema.scope(expected_tail.syntax()) {
Some(it) => it, Some(it) => it,
None => continue, None => continue,
}; };
@ -425,14 +426,15 @@ impl flags::AnalysisStats {
}; };
fn trim(s: &str) -> String { fn trim(s: &str) -> String {
s.chars().into_iter().filter(|c| !c.is_whitespace()).collect() s.chars().filter(|c| !c.is_whitespace()).collect()
} }
let todo = syntax::ast::make::ext::expr_todo().to_string(); let todo = syntax::ast::make::ext::expr_todo().to_string();
let mut formatter = |_: &hir::Type| todo.clone(); let mut formatter = |_: &hir::Type| todo.clone();
let mut syntax_hit_found = false; let mut syntax_hit_found = false;
for term in found_terms { for term in found_terms {
let generated = term.gen_source_code(&scope, &mut formatter, false, true); let generated =
term.gen_source_code(&scope, &mut formatter, false, true).unwrap();
syntax_hit_found |= trim(&original_text) == trim(&generated); syntax_hit_found |= trim(&original_text) == trim(&generated);
// Validate if type-checks // Validate if type-checks

View file

@ -93,9 +93,10 @@ xflags::xflags! {
/// and annotations. This is useful for benchmarking the memory usage on a project that has /// and annotations. This is useful for benchmarking the memory usage on a project that has
/// been worked on for a bit in a longer running session. /// been worked on for a bit in a longer running session.
optional --run-all-ide-things optional --run-all-ide-things
/// Run term search /// Run term search on all the tail expressions (of functions, block, if statements etc.)
optional --run-term-search optional --run-term-search
/// Validate term search by running `cargo check` on every response /// Validate term search by running `cargo check` on every response.
/// Note that this also temporarily modifies the files on disk, use with caution!
optional --validate-term-search optional --validate-term-search
} }

View file

@ -287,7 +287,7 @@ config_data! {
} }
}"#, }"#,
/// Whether to enable term search based snippets like `Some(foo.bar().baz())`. /// Whether to enable term search based snippets like `Some(foo.bar().baz())`.
completion_term_search_enable: bool = "true", completion_termSearch_enable: bool = "false",
/// List of rust-analyzer diagnostics to disable. /// List of rust-analyzer diagnostics to disable.
diagnostics_disabled: FxHashSet<String> = "[]", diagnostics_disabled: FxHashSet<String> = "[]",
@ -1537,7 +1537,7 @@ impl Config {
&& completion_item_edit_resolve(&self.caps), && completion_item_edit_resolve(&self.caps),
enable_self_on_the_fly: self.data.completion_autoself_enable, enable_self_on_the_fly: self.data.completion_autoself_enable,
enable_private_editable: self.data.completion_privateEditable_enable, enable_private_editable: self.data.completion_privateEditable_enable,
enable_term_search: self.data.completion_term_search_enable, enable_term_search: self.data.completion_termSearch_enable,
full_function_signatures: self.data.completion_fullFunctionSignatures_enable, full_function_signatures: self.data.completion_fullFunctionSignatures_enable,
callable: match self.data.completion_callable_snippets { callable: match self.data.completion_callable_snippets {
CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments), CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments),

View file

@ -123,6 +123,7 @@ pub(crate) fn completion_item_kind(
CompletionItemKind::Method => lsp_types::CompletionItemKind::METHOD, CompletionItemKind::Method => lsp_types::CompletionItemKind::METHOD,
CompletionItemKind::Snippet => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::Snippet => lsp_types::CompletionItemKind::SNIPPET,
CompletionItemKind::UnresolvedReference => lsp_types::CompletionItemKind::REFERENCE, CompletionItemKind::UnresolvedReference => lsp_types::CompletionItemKind::REFERENCE,
CompletionItemKind::Expression => lsp_types::CompletionItemKind::SNIPPET,
CompletionItemKind::SymbolKind(symbol) => match symbol { CompletionItemKind::SymbolKind(symbol) => match symbol {
SymbolKind::Attribute => lsp_types::CompletionItemKind::FUNCTION, SymbolKind::Attribute => lsp_types::CompletionItemKind::FUNCTION,
SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT, SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT,

View file

@ -60,6 +60,8 @@
//! try: infallible //! try: infallible
//! unpin: sized //! unpin: sized
//! unsize: sized //! unsize: sized
//! todo: panic
//! unimplemented: panic
#![rustc_coherence_is_core] #![rustc_coherence_is_core]
@ -927,6 +929,10 @@ pub mod fmt {
use crate::mem::transmute; use crate::mem::transmute;
unsafe { Argument { formatter: transmute(f), value: transmute(x) } } unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
} }
pub fn new_display<'b, T: Display>(x: &'b T) -> Argument<'_> {
Self::new(x, Display::fmt)
}
} }
#[lang = "format_alignment"] #[lang = "format_alignment"]
@ -1438,6 +1444,33 @@ mod macros {
// endregion:fmt // endregion:fmt
// region:todo
#[macro_export]
#[allow_internal_unstable(core_panic)]
macro_rules! todo {
() => {
$crate::panicking::panic("not yet implemented")
};
($($arg:tt)+) => {
$crate::panic!("not yet implemented: {}", $crate::format_args!($($arg)+))
};
}
// endregion:todo
// region:unimplemented
#[macro_export]
#[allow_internal_unstable(core_panic)]
macro_rules! unimplemented {
() => {
$crate::panicking::panic("not implemented")
};
($($arg:tt)+) => {
$crate::panic!("not implemented: {}", $crate::format_args!($($arg)+))
};
}
// endregion:unimplemented
// region:derive // region:derive
pub(crate) mod builtin { pub(crate) mod builtin {
#[rustc_builtin_macro] #[rustc_builtin_macro]

View file

@ -344,7 +344,7 @@ Default:
Custom completion snippets. Custom completion snippets.
-- --
[[rust-analyzer.completion.term.search.enable]]rust-analyzer.completion.term.search.enable (default: `true`):: [[rust-analyzer.completion.termSearch.enable]]rust-analyzer.completion.termSearch.enable (default: `false`)::
+ +
-- --
Whether to enable term search based snippets like `Some(foo.bar().baz())`. Whether to enable term search based snippets like `Some(foo.bar().baz())`.

View file

@ -902,9 +902,9 @@
}, },
"type": "object" "type": "object"
}, },
"rust-analyzer.completion.term.search.enable": { "rust-analyzer.completion.termSearch.enable": {
"markdownDescription": "Whether to enable term search based snippets like `Some(foo.bar().baz())`.", "markdownDescription": "Whether to enable term search based snippets like `Some(foo.bar().baz())`.",
"default": true, "default": false,
"type": "boolean" "type": "boolean"
}, },
"rust-analyzer.diagnostics.disabled": { "rust-analyzer.diagnostics.disabled": {