diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index 247ec096cbe2..519706c65f29 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -377,6 +377,7 @@ impl AttrsWithOwner { AttrDefId::GenericParamId(it) => match it { GenericParamId::ConstParamId(it) => { let src = it.parent().child_source(db); + // FIXME: We should be never getting `None` here. match src.value.get(it.local_id()) { Some(val) => RawAttrs::from_attrs_owner( db.upcast(), @@ -388,6 +389,7 @@ impl AttrsWithOwner { } GenericParamId::TypeParamId(it) => { let src = it.parent().child_source(db); + // FIXME: We should be never getting `None` here. match src.value.get(it.local_id()) { Some(val) => RawAttrs::from_attrs_owner( db.upcast(), @@ -399,6 +401,7 @@ impl AttrsWithOwner { } GenericParamId::LifetimeParamId(it) => { let src = it.parent.child_source(db); + // FIXME: We should be never getting `None` here. match src.value.get(it.local_id) { Some(val) => RawAttrs::from_attrs_owner( db.upcast(), diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 25dde2d15934..709760b64fd3 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -74,6 +74,12 @@ impl> Canonicalized { } } +/// 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` and `Option` unify although there is +/// unresolved goal `T = U`. pub fn could_unify( db: &dyn HirDatabase, env: Arc, @@ -82,30 +88,25 @@ pub fn could_unify( 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` and `Option` do not unify as we cannot show that `T = U` pub fn could_unify_deeply( db: &dyn HirDatabase, env: Arc, tys: &Canonical<(Ty, Ty)>, ) -> bool { let mut table = InferenceTable::new(db, env); - let vars = Substitution::from_iter( - 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 vars = make_substitutions(tys, &mut table); let ty1_with_vars = vars.apply(tys.value.0.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 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) } @@ -115,15 +116,7 @@ pub(crate) fn unify( tys: &Canonical<(Ty, Ty)>, ) -> Option { let mut table = InferenceTable::new(db, env); - let vars = 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), - }), - ); + let vars = make_substitutions(tys, &mut table); let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); 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, chalk_ir::Ty)>, + table: &mut InferenceTable<'_>, +) -> chalk_ir::Substitution { + 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! { #[derive(Default, Clone, Copy)] pub(crate) struct TypeVariableFlags: u8 { @@ -458,7 +466,7 @@ impl<'a> InferenceTable<'a> { 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>(&mut self, ty1: &T, ty2: &T) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, @@ -466,7 +474,7 @@ impl<'a> InferenceTable<'a> { }; result.goals.iter().all(|goal| { 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) { 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); } } @@ -666,70 +675,35 @@ impl<'a> InferenceTable<'a> { fn try_resolve_obligation( &mut self, canonicalized: &Canonicalized>, - ) -> bool { + ) -> Option> { let solution = self.db.trait_solve( self.trait_env.krate, self.trait_env.block, canonicalized.value.clone(), ); - match solution { + match &solution { Some(Solution::Unique(canonical_subst)) => { canonicalized.apply_solution( self, Canonical { - binders: canonical_subst.binders, + binders: canonical_subst.binders.clone(), // FIXME: handle constraints - value: canonical_subst.value.subst, + value: canonical_subst.value.subst.clone(), }, ); - true } Some(Solution::Ambig(Guidance::Definite(substs))) => { - canonicalized.apply_solution(self, substs); - false + canonicalized.apply_solution(self, substs.clone()); } Some(_) => { // FIXME use this when trying to resolve everything at the end - false } None => { // FIXME obligation cannot be fulfilled => diagnostic - true - } - } - } - - fn try_fulfill_obligation( - &mut self, - canonicalized: &Canonicalized>, - ) -> 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, } + solution } pub(crate) fn callable_sig( diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index 8d3200098127..63fa87ad6628 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -15,7 +15,7 @@ use crate::{ db::{HirDatabase, InternedClosure}, mir::Operand, utils::ClosureSubst, - ClosureId, Interner, Ty, TyExt, TypeFlags, + ClosureId, Interner, Substitution, Ty, TyExt, TypeFlags, }; use super::{ @@ -105,6 +105,18 @@ pub fn borrowck_query( 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 { let mut result = vec![]; 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 ty = proj.projected_ty( ty, db, - |c, subst, f| { - 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) - }, + make_fetch_closure_field(db), body.owner.module(db.upcast()).krate(), ); } @@ -216,18 +217,7 @@ fn partially_moved(db: &dyn HirDatabase, body: &MirBody) -> Vec ty = proj.projected_ty( ty, db, - |c, subst, f| { - 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) - }, + make_fetch_closure_field(db), body.owner.module(db.upcast()).krate(), ); } @@ -309,23 +299,17 @@ fn borrow_regions(db: &dyn HirDatabase, body: &MirBody) -> Vec { for (_, block) in body.basic_blocks.iter() { db.unwind_if_cancelled(); for statement in &block.statements { - match &statement.kind { - StatementKind::Assign(_, r) => match r { - Rvalue::Ref(kind, p) => { - borrows - .entry(p.local) - .and_modify(|it: &mut BorrowRegion| { - it.places.push(statement.span); - }) - .or_insert_with(|| BorrowRegion { - local: p.local, - kind: *kind, - places: vec![statement.span], - }); - } - _ => (), - }, - _ => (), + if let StatementKind::Assign(_, Rvalue::Ref(kind, p)) = &statement.kind { + borrows + .entry(p.local) + .and_modify(|it: &mut BorrowRegion| { + it.places.push(statement.span); + }) + .or_insert_with(|| BorrowRegion { + local: p.local, + kind: *kind, + places: vec![statement.span], + }); } } match &block.terminator { @@ -379,18 +363,7 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio ty = proj.projected_ty( ty, db, - |c, subst, f| { - 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) - }, + make_fetch_closure_field(db), body.owner.module(db.upcast()).krate(), ); } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 283461f2199f..08f7bb14caa3 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1085,6 +1085,7 @@ impl Field { 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) -> Type { let var_id = self.parent.into(); let def_id: AdtId = match self.parent { @@ -1094,12 +1095,11 @@ impl Field { }; let mut generics = generics.map(|it| it.ty.clone()); let substs = TyBuilder::subst_for_def(db, def_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()), + .fill(|x| match x { + ParamKind::Type => { + generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) } + ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), }) .build(); 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) } - pub fn ty_with_args(self, db: &dyn HirDatabase, generics: impl Iterator) -> 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 { Type::from_value_def(db, self.id) } @@ -1273,22 +1258,6 @@ impl Enum { Type::from_def(db, self.id) } - pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator) -> 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. pub fn variant_body_ty(self, db: &dyn HirDatabase) -> Type { Type::new_for_crate( @@ -1463,9 +1432,9 @@ impl Adt { /// Turns this ADT into a type with the given type parameters. This isn't /// 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) -> Type { 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) .fill(|x| { 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) } + // FIXME: Find better API to also handle const generics pub fn ret_type_with_args( self, db: &dyn HirDatabase, @@ -1870,12 +1840,11 @@ impl Function { ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, }; let mut generics = generics.map(|it| it.ty.clone()); - let mut filler = |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()), + let mut filler = |x: &_| match x { + ParamKind::Type => { + generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) } + ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), }; let parent_substs = @@ -1953,10 +1922,11 @@ impl Function { .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, db: &dyn HirDatabase, - mut generics: impl Iterator, + generics: impl Iterator, ) -> Vec { let environment = db.trait_environment(self.id.into()); let parent_id: Option = match self.id.lookup(db.upcast()).container { @@ -1964,20 +1934,23 @@ impl Function { ItemContainerId::TraitId(it) => Some(it.into()), ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, }; + let mut generics = generics.map(|it| it.ty.clone()); let parent_substs = parent_id.map(|id| { TyBuilder::subst_for_def(db, id, None) - .fill(|_| { - GenericArg::new( - Interner, - GenericArgData::Ty(generics.next().unwrap().ty.clone()), - ) + .fill(|x| match x { + ParamKind::Type => generics + .next() + .unwrap_or_else(|| TyKind::Error.intern(Interner)) + .cast(Interner), + ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), }) .build() }); let substs = TyBuilder::subst_for_def(db, self.id, parent_substs) .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(); let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs); @@ -2197,6 +2170,7 @@ impl SelfParam { 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) -> Type { let parent_id: GenericDefId = match self.func.lookup(db.upcast()).container { ItemContainerId::ImplId(it) => it.into(), @@ -2207,12 +2181,11 @@ impl SelfParam { }; let mut generics = generics.map(|it| it.ty.clone()); - let mut filler = |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()), + let mut filler = |x: &_| match x { + ParamKind::Type => { + generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) } + ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), }; let parent_substs = TyBuilder::subst_for_def(db, parent_id, None).fill(&mut filler).build(); @@ -2936,40 +2909,6 @@ impl GenericDef { }) .collect() } - - pub fn type_params(self, db: &dyn HirDatabase) -> Vec { - 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 { - 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. @@ -3451,6 +3390,26 @@ impl TypeOrConstParam { Either::Right(it) => it.ty(db), } } + + pub fn as_type_param(self, db: &dyn HirDatabase) -> Option { + let params = db.generic_params(self.id.parent); + match ¶ms.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 { + let params = db.generic_params(self.id.parent); + match ¶ms.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)] @@ -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( db.trait_impls_in_crate(id) .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 { let mut it = args.iter().map(|t| t.ty.clone()); let trait_ref = TyBuilder::trait_ref(db, trait_.id) .push(self.ty.clone()) .fill(|x| { - let r = it.next().unwrap(); match x { - ParamKind::Type => r.cast(Interner), + ParamKind::Type => { + it.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) + } ParamKind::Const(ty) => { // FIXME: this code is not covered in tests. unknown_const_as_generic(ty.clone()) @@ -4617,12 +4582,19 @@ impl Type { 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` and `Option` unify although there is unresolved goal `T = U`. 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())); 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` and `Option` do not unify as we cannot show that `T = U` 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())); hir_ty::could_unify_deeply(db, self.env.clone(), &tys) diff --git a/crates/hir/src/term_search/mod.rs b/crates/hir/src/term_search.rs similarity index 98% rename from crates/hir/src/term_search/mod.rs rename to crates/hir/src/term_search.rs index f0c3fdb4d094..72762007dc98 100644 --- a/crates/hir/src/term_search/mod.rs +++ b/crates/hir/src/term_search.rs @@ -57,10 +57,10 @@ impl AlternativeExprs { /// # Arguments /// `threshold` - threshold value for many trees (more than that is many) /// `exprs` - expressions iterator - fn extend_with_threshold(&mut self, threshold: usize, mut exprs: impl Iterator) { + fn extend_with_threshold(&mut self, threshold: usize, exprs: impl Iterator) { match self { AlternativeExprs::Few(tts) => { - while let Some(it) = exprs.next() { + for it in exprs { if tts.len() > threshold { *self = AlternativeExprs::Many; break; @@ -131,7 +131,7 @@ impl LookupTable { self.data .iter() .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)| { it.exprs(t) diff --git a/crates/hir/src/term_search/expr.rs b/crates/hir/src/term_search/expr.rs index 29590a0730f9..254fbe7e2b53 100644 --- a/crates/hir/src/term_search/expr.rs +++ b/crates/hir/src/term_search/expr.rs @@ -2,7 +2,10 @@ use hir_def::find_path::PrefixKind; 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 crate::{ @@ -48,9 +51,10 @@ fn mod_item_path_str( def: &ModuleDef, prefer_no_std: bool, prefer_prelude: bool, -) -> String { +) -> Result { 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` @@ -59,30 +63,34 @@ fn type_path( ty: &Type, prefer_no_std: bool, prefer_prelude: bool, -) -> String { +) -> Result { let db = sema_scope.db; + let m = sema_scope.module(); + match ty.as_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 = mod_item_path(sema_scope, &ModuleDef::Adt(adt), prefer_no_std, prefer_prelude) .unwrap(); path.pop_segment(); let path = path.display(db.upcast()).to_string(); - match path.is_empty() { + let res = match path.is_empty() { true => 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 fn non_default_generics(db: &dyn HirDatabase, def: GenericDef, generics: &[Type]) -> Vec { - def.type_params(db) + def.type_or_const_params(db) .into_iter() + .filter_map(|it| it.as_type_param(db)) .zip(generics) .filter(|(tp, arg)| tp.default(db).as_ref() != Some(arg)) .map(|(_, arg)| arg.clone()) @@ -150,28 +158,30 @@ impl Expr { many_formatter: &mut dyn FnMut(&Type) -> String, prefer_no_std: bool, prefer_prelude: bool, - ) -> String { + ) -> Result { let db = sema_scope.db; let mod_item_path_str = |s, def| mod_item_path_str(s, def, prefer_no_std, prefer_prelude); match self { 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::Local(it) => return it.name(db).display(db.upcast()).to_string(), - Expr::ConstParam(it) => return it.name(db).display(db.upcast()).to_string(), - Expr::FamousType { value, .. } => return value.to_string(), + Expr::Local(it) => Ok(it.name(db).display(db.upcast()).to_string()), + Expr::ConstParam(it) => Ok(it.name(db).display(db.upcast()).to_string()), + Expr::FamousType { value, .. } => Ok(value.to_string()), Expr::Function { func, params, .. } => { let args = params .iter() .map(|f| { f.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude) }) + .collect::, DisplaySourceCodeError>>()? + .into_iter() .join(", "); match func.as_assoc_item(db).map(|it| it.container(db)) { Some(container) => { let container_name = match container { 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) => { let self_ty = imp.self_ty(db); @@ -190,17 +200,17 @@ impl Expr { } }; 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 => { - let fn_name = mod_item_path_str(sema_scope, &ModuleDef::Function(*func)); - format!("{fn_name}({args})",) + let fn_name = mod_item_path_str(sema_scope, &ModuleDef::Function(*func))?; + Ok(format!("{fn_name}({args})")) } } } Expr::Method { func, target, params, .. } => { 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(); @@ -210,28 +220,31 @@ impl Expr { many_formatter, prefer_no_std, prefer_prelude, - ); + )?; let args = params .iter() .map(|f| { f.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude) }) + .collect::, DisplaySourceCodeError>>()? + .into_iter() .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_) => { - 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) { crate::Access::Shared => format!("&{target}"), crate::Access::Exclusive => format!("&mut {target}"), crate::Access::Owned => target, }; - match args.is_empty() { + let res = match args.is_empty() { true => format!("{trait_name}::{func_name}({target})",), 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 } => { @@ -242,6 +255,8 @@ impl Expr { let generics = generics .iter() .map(|it| type_path(sema_scope, it, prefer_no_std, prefer_prelude)) + .collect::, DisplaySourceCodeError>>()? + .into_iter() .join(", "); format!("::<{generics}>") } @@ -258,6 +273,8 @@ impl Expr { prefer_prelude, ) }) + .collect::, DisplaySourceCodeError>>()? + .into_iter() .join(", "); format!("{generics_str}({args})") } @@ -267,25 +284,28 @@ impl Expr { .iter() .zip(fields.iter()) .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( sema_scope, many_formatter, prefer_no_std, prefer_prelude - ) - ) + )? + ); + Ok(tmp) }) + .collect::, DisplaySourceCodeError>>()? + .into_iter() .join(", "); format!("{generics_str}{{ {args} }}") } StructKind::Unit => generics_str, }; - let prefix = mod_item_path_str(sema_scope, &ModuleDef::Variant(*variant)); - format!("{prefix}{inner}") + let prefix = mod_item_path_str(sema_scope, &ModuleDef::Variant(*variant))?; + Ok(format!("{prefix}{inner}")) } Expr::Struct { strukt, generics, params } => { let generics = non_default_generics(db, (*strukt).into(), generics); @@ -301,6 +321,8 @@ impl Expr { prefer_prelude, ) }) + .collect::, DisplaySourceCodeError>>()? + .into_iter() .join(", "); format!("({args})") } @@ -310,17 +332,20 @@ impl Expr { .iter() .zip(fields.iter()) .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( sema_scope, many_formatter, prefer_no_std, prefer_prelude - ) - ) + )? + ); + Ok(tmp) }) + .collect::, DisplaySourceCodeError>>()? + .into_iter() .join(", "); format!(" {{ {args} }}") } @@ -330,35 +355,45 @@ impl Expr { let generics = generics .iter() .map(|it| type_path(sema_scope, it, prefer_no_std, prefer_prelude)) + .collect::, DisplaySourceCodeError>>()? + .into_iter() .join(", "); format!("::<{generics}>") } }, }; - let prefix = mod_item_path_str(sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt))); - format!("{prefix}{inner}") + let prefix = mod_item_path_str(sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt)))?; + Ok(format!("{prefix}{inner}")) } Expr::Field { expr, field } => { if expr.contains_many_in_illegal_pos() { - return many_formatter(&expr.ty(db)); + return Ok(many_formatter(&expr.ty(db))); } - let strukt = - expr.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude); + let strukt = expr.gen_source_code( + sema_scope, + many_formatter, + prefer_no_std, + prefer_prelude, + )?; let field = field.name(db).display(db.upcast()).to_string(); - format!("{strukt}.{field}") + Ok(format!("{strukt}.{field}")) } Expr::Reference(expr) => { if expr.contains_many_in_illegal_pos() { - return many_formatter(&expr.ty(db)); + return Ok(many_formatter(&expr.ty(db))); } - let inner = - expr.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude); - format!("&{inner}") + let inner = expr.gen_source_code( + sema_scope, + 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()), ), 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, .. } => { - 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::Reference(it) => it.ty(db), @@ -395,16 +430,13 @@ impl Expr { pub fn traits_used(&self, db: &dyn HirDatabase) -> Vec { let mut res = Vec::new(); - match self { - Expr::Method { func, params, .. } => { - res.extend(params.iter().flat_map(|it| it.traits_used(db))); - if let Some(it) = func.as_assoc_item(db) { - if let Some(it) = it.containing_trait_or_trait_impl(db) { - res.push(it); - } + if let Expr::Method { func, params, .. } = self { + res.extend(params.iter().flat_map(|it| it.traits_used(db))); + if let Some(it) = func.as_assoc_item(db) { + if let Some(it) = it.container_or_implemented_trait(db) { + res.push(it); } } - _ => (), } res diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs index 012d815394e2..666d63ac1558 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs @@ -16,7 +16,7 @@ use rustc_hash::FxHashSet; use crate::{ Adt, AssocItem, Enum, GenericDef, GenericParam, HasVisibility, Impl, ModuleDef, ScopeDef, Type, - Variant, + TypeParam, Variant, }; use crate::term_search::{Expr, TermSearchConfig}; @@ -82,7 +82,7 @@ pub(super) fn trivial<'a, DB: HirDatabase>( 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)); - - // Ignore enums with const generics - if !generics.const_params(db).is_empty() { + let Some(type_params) = generics + .type_or_const_params(db) + .into_iter() + .map(|it| it.as_type_param(db)) + .collect::>>() + else { + // Ignore enums with const generics return Vec::new(); - } + }; // We currently do not check lifetime bounds so ignore all types that have something to do // with them @@ -130,9 +134,6 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( 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 // tho, for example in `Box` 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 generics: Vec<_> = type_params .iter() - .map(|it| match it.default(db) { - Some(ty) => ty, - None => g.next().expect("Missing type param"), - }) + .map(|it| it.default(db).unwrap_or_else(|| g.next().expect("No generic"))) .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 // performance reasons @@ -212,9 +210,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( let exprs: Vec<(Type, Vec)> = enum_ .variants(db) .into_iter() - .flat_map(|it| { - variant_helper(db, lookup, enum_.clone(), it, &ctx.goal, &ctx.config) - }) + .flat_map(|it| variant_helper(db, lookup, *enum_, it, &ctx.goal, &ctx.config)) .collect(); if !exprs.is_empty() { @@ -231,10 +227,12 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( let generics = GenericDef::from(*it); - // Ignore enums with const generics - if !generics.const_params(db).is_empty() { - return None; - } + // Ignore const params for now + let type_params = generics + .type_or_const_params(db) + .into_iter() + .map(|it| it.as_type_param(db)) + .collect::>>()?; // We currently do not check lifetime bounds so ignore all types that have something to do // with them @@ -242,8 +240,6 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( return None; } - let type_params = generics.type_params(db); - // Only account for stable type parameters for now, unstable params can be default // tho, for example in `Box` 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 generics: Vec<_> = type_params .iter() - .map(|it| match it.default(db) { - Some(ty) => ty, - None => g.next().expect("Missing type param"), + .map(|it| { + it.default(db) + .unwrap_or_else(|| g.next().expect("Missing type param")) }) .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 // performance reasons @@ -324,7 +321,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( _ => None, }) .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() } @@ -352,18 +349,18 @@ pub(super) fn free_function<'a, DB: HirDatabase>( ScopeDef::ModuleDef(ModuleDef::Function(it)) => { let generics = GenericDef::from(*it); - // Skip functions that require const generics - if !generics.const_params(db).is_empty() { - return None; - } + // Ignore const params for now + let type_params = generics + .type_or_const_params(db) + .into_iter() + .map(|it| it.as_type_param(db)) + .collect::>>()?; // Ignore lifetimes as we do not check them if !generics.lifetime_params(db).is_empty() { return None; } - let type_params = generics.type_params(db); - // Only account for stable type parameters for now, unstable params can be default // tho, for example in `Box` 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 .iter() .map(|it| match it.default(db) { - Some(ty) => ty, - None => g.next().expect("Missing type param"), + Some(ty) => Some(ty), + 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::>()?; let ret_ty = it.ret_type_with_args(db, generics.iter().cloned()); // 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 let param_exprs: Vec> = it - .params_without_self_with_generics(db, generics.iter().cloned()) + .params_without_self_with_args(db, generics.iter().cloned()) .into_iter() .map(|field| { let ty = field.ty(); match ty.is_mutable_reference() { true => None, - false => lookup.find_autoref(db, &ty), + false => lookup.find_autoref(db, ty), } }) .collect::>()?; @@ -447,7 +448,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( _ => None, }) .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() } @@ -487,11 +488,19 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( let fn_generics = GenericDef::from(it); let imp_generics = GenericDef::from(imp); - // Ignore impl if it has const type arguments - if !fn_generics.const_params(db).is_empty() || !imp_generics.const_params(db).is_empty() - { - return None; - } + // Ignore const params for now + let imp_type_params = imp_generics + .type_or_const_params(db) + .into_iter() + .map(|it| it.as_type_param(db)) + .collect::>>()?; + + // 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::>>()?; // Ignore all functions that have something to do with lifetimes as we don't check them if !fn_generics.lifetime_params(db).is_empty() { @@ -508,9 +517,6 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( 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 // tho, for example in `Box` 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() .chain(fn_type_params.iter()) .map(|it| match it.default(db) { - Some(ty) => ty, - None => g.next().expect("Missing type param"), + Some(ty) => Some(ty), + 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::>()?; let ret_ty = it.ret_type_with_args( db, @@ -579,16 +589,16 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec> = it - .params_without_self_with_generics( + .params_without_self_with_args( db, ty.type_arguments().chain(generics.iter().cloned()), ) .into_iter() - .map(|field| lookup.find_autoref(db, &field.ty())) + .map(|field| lookup.find_autoref(db, field.ty())) .collect::>()?; let fn_exprs: Vec = std::iter::once(target_type_exprs) - .chain(param_exprs.into_iter()) + .chain(param_exprs) .multi_cartesian_product() .map(|params| { let mut params = params.into_iter(); @@ -609,7 +619,7 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( Some(exprs) }) .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() } @@ -647,7 +657,7 @@ pub(super) fn struct_projection<'a, DB: HirDatabase>( 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() } @@ -719,11 +729,19 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( let fn_generics = GenericDef::from(it); let imp_generics = GenericDef::from(imp); - // Ignore impl if it has const type arguments - if !fn_generics.const_params(db).is_empty() || !imp_generics.const_params(db).is_empty() - { - return None; - } + // Ignore const params for now + let imp_type_params = imp_generics + .type_or_const_params(db) + .into_iter() + .map(|it| it.as_type_param(db)) + .collect::>>()?; + + // 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::>>()?; // Ignore all functions that have something to do with lifetimes as we don't check them if !fn_generics.lifetime_params(db).is_empty() @@ -742,9 +760,6 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( 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 // tho, for example in `Box` 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() .chain(fn_type_params.iter()) .map(|it| match it.default(db) { - Some(ty) => ty, - None => g.next().expect("Missing type param"), + Some(ty) => Some(ty), + 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::>()?; let ret_ty = it.ret_type_with_args( db, @@ -801,12 +823,12 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec> = it - .params_without_self_with_generics( + .params_without_self_with_args( db, ty.type_arguments().chain(generics.iter().cloned()), ) .into_iter() - .map(|field| lookup.find_autoref(db, &field.ty())) + .map(|field| lookup.find_autoref(db, field.ty())) .collect::>()?; // 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) }) .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() } diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs index 6b054790e9c7..51a1a406f316 100644 --- a/crates/ide-assists/src/handlers/term_search.rs +++ b/crates/ide-assists/src/handlers/term_search.rs @@ -1,6 +1,9 @@ //! Term search assist 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 syntax::{ast, AstNode}; @@ -12,18 +15,21 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< let syntax = unexpanded.syntax(); let goal_range = syntax.text_range(); - let excl = unexpanded.excl_token()?; - let macro_name_token = excl.prev_token()?; - let name = macro_name_token.text(); - if name != "todo" { + let parent = syntax.parent()?; + let scope = ctx.sema.scope(&parent)?; + + 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; } - let parent = syntax.parent()?; 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 { sema: &ctx.sema, 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!()"); - for path in paths.iter().unique() { - let code = path.gen_source_code( - &scope, - &mut formatter, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, - ); + + let paths = paths + .into_iter() + .filter_map(|path| { + path.gen_source_code( + &scope, + &mut formatter, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) + .ok() + }) + .unique(); + + for code in paths { acc.add_group( &GroupLabel(String::from("Term search")), AssistId("term_search", AssistKind::Generate), @@ -68,8 +82,9 @@ mod tests { fn test_complete_local() { check_assist( term_search, - "macro_rules! todo { () => (_) }; fn f() { let a: u128 = 1; let b: u128 = todo$0!() }", - "macro_rules! todo { () => (_) }; fn f() { let a: u128 = 1; let b: u128 = a }", + r#"//- minicore: todo, unimplemented +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() { check_assist( term_search, - "macro_rules! todo { ($($arg:tt)+) => (_) }; fn f() { let a: u128 = 1; let b: u128 = todo$0!(\"asd\") }", - "macro_rules! todo { ($($arg:tt)+) => (_) }; fn f() { let a: u128 = 1; let b: u128 = a }", + 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_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() { check_assist( term_search, - r#"macro_rules! todo { () => (_) }; - struct A { pub x: i32, y: bool } - fn f() { let a = A { x: 1, y: true }; let b: i32 = todo$0!(); }"#, - r#"macro_rules! todo { () => (_) }; - struct A { pub x: i32, y: bool } - fn f() { let a = A { x: 1, y: true }; let b: i32 = a.x; }"#, + r#"//- minicore: todo, unimplemented +struct A { pub x: i32, y: bool } +fn f() { let a = A { x: 1, y: true }; let b: i32 = todo$0!(); }"#, + r#"struct A { pub x: i32, y: bool } +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() { check_assist( term_search, - r#"macro_rules! todo { () => (_) }; - enum Option { Some(T), None } - fn f() { let a: i32 = 1; let b: Option = todo$0!(); }"#, - r#"macro_rules! todo { () => (_) }; - enum Option { Some(T), None } - fn f() { let a: i32 = 1; let b: Option = Option::None; }"#, + r#"//- minicore: todo, unimplemented, option +fn f() { let a: i32 = 1; let b: Option = todo$0!(); }"#, + r#"fn f() { let a: i32 = 1; let b: Option = None; }"#, ) } @@ -112,12 +144,11 @@ mod tests { fn test_enum_with_generics2() { check_assist( term_search, - r#"macro_rules! todo { () => (_) }; - enum Option { None, Some(T) } - fn f() { let a: i32 = 1; let b: Option = todo$0!(); }"#, - r#"macro_rules! todo { () => (_) }; - enum Option { None, Some(T) } - fn f() { let a: i32 = 1; let b: Option = Option::Some(a); }"#, + r#"//- minicore: todo, unimplemented +enum Option { None, Some(T) } +fn f() { let a: i32 = 1; let b: Option = todo$0!(); }"#, + r#"enum Option { None, Some(T) } +fn f() { let a: i32 = 1; let b: Option = Option::Some(a); }"#, ) } @@ -125,12 +156,11 @@ mod tests { fn test_enum_with_generics3() { check_assist( term_search, - r#"macro_rules! todo { () => (_) }; - enum Option { None, Some(T) } - fn f() { let a: Option = Option::None; let b: Option> = todo$0!(); }"#, - r#"macro_rules! todo { () => (_) }; - enum Option { None, Some(T) } - fn f() { let a: Option = Option::None; let b: Option> = Option::Some(a); }"#, + r#"//- minicore: todo, unimplemented +enum Option { None, Some(T) } +fn f() { let a: Option = Option::None; let b: Option> = todo$0!(); }"#, + r#"enum Option { None, Some(T) } +fn f() { let a: Option = Option::None; let b: Option> = Option::Some(a); }"#, ) } @@ -138,22 +168,20 @@ mod tests { fn test_enum_with_generics4() { check_assist( term_search, - r#"macro_rules! todo { () => (_) }; - enum Foo { Foo(T) } - fn f() { let a = 0; let b: Foo = todo$0!(); }"#, - r#"macro_rules! todo { () => (_) }; - enum Foo { Foo(T) } - fn f() { let a = 0; let b: Foo = Foo::Foo(a); }"#, + r#"//- minicore: todo, unimplemented +enum Foo { Foo(T) } +fn f() { let a = 0; let b: Foo = todo$0!(); }"#, + r#"enum Foo { Foo(T) } +fn f() { let a = 0; let b: Foo = Foo::Foo(a); }"#, ); check_assist( term_search, - r#"macro_rules! todo { () => (_) }; - enum Foo { Foo(T) } - fn f() { let a: Foo = Foo::Foo(0); let b: Foo = todo$0!(); }"#, - r#"macro_rules! todo { () => (_) }; - enum Foo { Foo(T) } - fn f() { let a: Foo = Foo::Foo(0); let b: Foo = a; }"#, + r#"//- minicore: todo, unimplemented +enum Foo { Foo(T) } +fn f() { let a: Foo = Foo::Foo(0); let b: Foo = todo$0!(); }"#, + r#"enum Foo { Foo(T) } +fn f() { let a: Foo = Foo::Foo(0); let b: Foo = a; }"#, ) } @@ -161,12 +189,11 @@ mod tests { fn test_newtype() { check_assist( term_search, - r#"macro_rules! todo { () => (_) }; - struct Foo(i32); - fn f() { let a: i32 = 1; let b: Foo = todo$0!(); }"#, - r#"macro_rules! todo { () => (_) }; - struct Foo(i32); - fn f() { let a: i32 = 1; let b: Foo = Foo(a); }"#, + r#"//- minicore: todo, unimplemented +struct Foo(i32); +fn f() { let a: i32 = 1; let b: Foo = todo$0!(); }"#, + r#"struct Foo(i32); +fn f() { let a: i32 = 1; let b: Foo = Foo(a); }"#, ) } @@ -174,10 +201,9 @@ mod tests { fn test_shadowing() { check_assist( term_search, - r#"macro_rules! todo { () => (_) }; - fn f() { let a: i32 = 1; let b: i32 = 2; let a: u32 = 0; let c: i32 = todo$0!(); }"#, - r#"macro_rules! todo { () => (_) }; - fn f() { let a: i32 = 1; let b: i32 = 2; let a: u32 = 0; let c: i32 = b; }"#, + r#"//- minicore: todo, unimplemented +fn f() { let a: i32 = 1; let b: i32 = 2; let a: u32 = 0; let c: i32 = todo$0!(); }"#, + r#"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() { check_assist( term_search, - r#"macro_rules! todo { () => (_) }; - fn f() { let a: bool = todo$0!(); }"#, - r#"macro_rules! todo { () => (_) }; - fn f() { let a: bool = false; }"#, + r#"//- minicore: todo, unimplemented +fn f() { let a: bool = todo$0!(); }"#, + r#"fn f() { let a: bool = false; }"#, ) } @@ -196,12 +221,11 @@ mod tests { fn test_fn_with_reference_types() { check_assist( term_search, - r#"macro_rules! todo { () => (_) }; - fn f(a: &i32) -> f32 { a as f32 } - fn g() { let a = 1; let b: f32 = todo$0!(); }"#, - r#"macro_rules! todo { () => (_) }; - fn f(a: &i32) -> f32 { a as f32 } - fn g() { let a = 1; let b: f32 = f(&a); }"#, + r#"//- minicore: todo, unimplemented +fn f(a: &i32) -> f32 { a as f32 } +fn g() { let a = 1; let b: f32 = todo$0!(); }"#, + r#"fn f(a: &i32) -> f32 { a as f32 } +fn g() { let a = 1; let b: f32 = f(&a); }"#, ) } @@ -209,12 +233,11 @@ mod tests { fn test_fn_with_reference_types2() { check_assist( term_search, - r#"macro_rules! todo { () => (_) }; - fn f(a: &i32) -> f32 { a as f32 } - fn g() { let a = &1; let b: f32 = todo$0!(); }"#, - r#"macro_rules! todo { () => (_) }; - fn f(a: &i32) -> f32 { a as f32 } - fn g() { let a = &1; let b: f32 = f(a); }"#, + r#"//- minicore: todo, unimplemented +fn f(a: &i32) -> f32 { a as f32 } +fn g() { let a = &1; let b: f32 = todo$0!(); }"#, + r#"fn f(a: &i32) -> f32 { a as f32 } +fn g() { let a = &1; let b: f32 = f(a); }"#, ) } @@ -222,7 +245,7 @@ mod tests { fn test_fn_with_reference_types3() { check_assist_not_applicable( term_search, - r#"macro_rules! todo { () => (_) }; + r#"//- minicore: todo, unimplemented fn f(a: &i32) -> f32 { a as f32 } fn g() { let a = &mut 1; let b: f32 = todo$0!(); }"#, ) diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 87016a605739..1ea7220960d2 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -159,9 +159,8 @@ impl Completions { } pub(crate) fn add_expr(&mut self, ctx: &CompletionContext<'_>, expr: &hir::term_search::Expr) { - match render_expr(ctx, expr) { - Some(item) => item.add_to(self, ctx.db), - None => (), + if let Some(item) = render_expr(ctx, expr) { + item.add_to(self, ctx.db) } } @@ -759,7 +758,6 @@ pub(super) fn complete_name_ref( flyimport::import_on_the_fly_dot(acc, ctx, dot_access); dot::complete_dot(acc, ctx, dot_access); postfix::complete_postfix(acc, ctx, dot_access); - expr::complete_expr(acc, ctx); } NameRefKind::Keyword(item) => { keyword::complete_for_and_where(acc, ctx, item); diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index c37b325ee76a..802e9bc3a807 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -342,7 +342,7 @@ pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>) if let Some(ty) = &ctx.expected_type { // Ignore unit types as they are not very interesting - if ty.is_unit() { + if ty.is_unit() || ty.is_unknown() { return; } diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index 8552a20392ab..17dfcc08ca1f 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -297,6 +297,7 @@ pub enum CompletionItemKind { Method, Snippet, UnresolvedReference, + Expression, } impl_from!(SymbolKind for CompletionItemKind); @@ -341,6 +342,7 @@ impl CompletionItemKind { CompletionItemKind::Method => "me", CompletionItemKind::Snippet => "sn", CompletionItemKind::UnresolvedReference => "??", + CompletionItemKind::Expression => "ex", } } } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 1bac2e30c19c..88dc3b5cbe7c 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -295,22 +295,24 @@ pub(crate) fn render_expr( .unwrap_or_else(|| String::from("...")) }; - let label = expr.gen_source_code( - &ctx.scope, - &mut label_formatter, - ctx.config.prefer_no_std, - ctx.config.prefer_prelude, - ); + let label = expr + .gen_source_code( + &ctx.scope, + &mut label_formatter, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) + .ok()?; 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(), None => node.text_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!( "{}$0", @@ -320,6 +322,7 @@ pub(crate) fn render_expr( ctx.config.prefer_no_std, ctx.config.prefer_prelude ) + .ok()? ); let edit = TextEdit::replace(source_range, snippet); item.snippet_edit(ctx.config.snippet_cap?, edit); @@ -1034,6 +1037,7 @@ fn func(input: Struct) { } st Self [type] sp Self [type] st Struct [type] + ex Struct [type] lc self [local] fn func(…) [] me self.test() [] @@ -1058,6 +1062,9 @@ fn main() { "#, expect![[r#" lc input [type+name+local] + ex input [type] + ex true [type] + ex false [type] lc inputbad [local] fn main() [] fn test(…) [] @@ -1738,6 +1745,10 @@ fn f() { A { bar: b$0 }; } expect![[r#" fn bar() [type+name] fn baz() [type] + ex baz() [type] + ex bar() [type] + ex A { bar: baz() }.bar [type] + ex A { bar: bar() }.bar [type] st A [] fn f() [] "#]], @@ -1822,6 +1833,8 @@ fn main() { lc s [type+name+local] st S [type] st S [type] + ex s [type] + ex S [type] fn foo(…) [] fn main() [] "#]], @@ -1839,6 +1852,8 @@ fn main() { lc ssss [type+local] st S [type] st S [type] + ex ssss [type] + ex S [type] fn foo(…) [] fn main() [] "#]], @@ -1871,6 +1886,8 @@ fn main() { } "#, 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 t [local] lc &t [type+local] @@ -1919,6 +1936,8 @@ fn main() { } "#, 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 t [local] lc &mut t [type+local] @@ -1967,6 +1986,8 @@ fn bar(t: Foo) {} ev Foo::A [type] ev Foo::B [type] en Foo [type] + ex Foo::A [type] + ex Foo::B [type] fn bar(…) [] fn foo() [] "#]], @@ -2020,6 +2041,8 @@ fn main() { } "#, 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 [type] st S [] @@ -2233,6 +2256,7 @@ fn foo() { "#, expect![[r#" lc foo [type+local] + ex foo [type] ev Foo::A(…) [type_could_unify] ev Foo::B [type_could_unify] en Foo [type_could_unify] @@ -2267,8 +2291,6 @@ fn main() { &[CompletionItemKind::Snippet, CompletionItemKind::Method], expect![[r#" sn not [snippet] - sn true [type] - sn false [type] me not() (use ops::Not) [type_could_unify+requires_import] sn if [] sn while [] diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index b835820260a9..556d3872d7ed 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -97,11 +97,11 @@ fn func(param0 @ (param1, param2): (i32, i32)) { kw unsafe kw while kw while let - sn ifletlocal - sn letlocal - sn matcharm - sn param1 - sn param2 + ex ifletlocal + ex letlocal + ex matcharm + ex param1 + ex param2 "#]], ); } @@ -243,11 +243,11 @@ fn complete_in_block() { kw use kw while kw while let - sn false sn macro_rules sn pd sn ppd - sn true + ex false + ex true "#]], ) } @@ -690,8 +690,8 @@ fn main() { "#, expect![[r#" fn test() fn() -> Zulu - sn Zulu - sn Zulu::test() + ex Zulu + ex Zulu::test() "#]], ); } diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs index c137de3e52d6..e64ec74c6106 100644 --- a/crates/ide-completion/src/tests/record.rs +++ b/crates/ide-completion/src/tests/record.rs @@ -192,8 +192,8 @@ fn main() { bt u32 u32 kw crate:: kw self:: - sn Foo::default() - sn foo + ex Foo::default() + ex foo "#]], ); check( diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index 68a6c678501f..ff32eccfbff4 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -225,10 +225,10 @@ impl S { fn foo() { let _ = lib::S::$0 } "#, expect![[r#" - ct PUBLIC_CONST pub const PUBLIC_CONST: u32 - fn public_method() fn() - ta PublicType pub type PublicType = u32 - "#]], + ct PUBLIC_CONST pub const PUBLIC_CONST: u32 + fn public_method() fn() + ta PublicType pub type PublicType = u32 + "#]], ); } @@ -242,8 +242,8 @@ impl U { fn m() { } } fn foo() { let _ = U::$0 } "#, expect![[r#" - fn m() fn() - "#]], + fn m() fn() + "#]], ); } @@ -256,8 +256,8 @@ trait Trait { fn m(); } fn foo() { let _ = Trait::$0 } "#, 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 } "#, expect![[r#" - fn m() (as Trait) fn() - "#]], + fn m() (as Trait) fn() + "#]], ); } @@ -290,8 +290,8 @@ impl Trait for S {} fn foo() { let _ = ::$0 } "#, expect![[r#" - fn m() (as Trait) fn() - "#]], + fn m() (as Trait) fn() + "#]], ); } @@ -396,9 +396,9 @@ macro_rules! foo { () => {} } fn main() { let _ = crate::$0 } "#, expect![[r#" - fn main() fn() - ma foo!(…) macro_rules! foo - "#]], + fn main() fn() + ma foo!(…) macro_rules! foo + "#]], ); } @@ -694,8 +694,10 @@ fn bar() -> Bar { } "#, 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#" fn bar() fn() fn foo() (as Foo) fn() -> Self + ex Bar + ex bar() "#]], ); } @@ -748,6 +752,8 @@ fn bar() -> Bar { "#, expect![[r#" fn foo() (as Foo) fn() -> Self + ex Bar + ex bar() "#]], ); } @@ -1230,10 +1236,6 @@ fn here_we_go() { "#, expect![[r#" 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 while kw while let - sn () - sn false - sn here_we_go() - sn true "#]], ); } diff --git a/crates/ide-db/src/famous_defs.rs b/crates/ide-db/src/famous_defs.rs index 4edfa37b3290..3106772e63b1 100644 --- a/crates/ide-db/src/famous_defs.rs +++ b/crates/ide-db/src/famous_defs.rs @@ -114,6 +114,14 @@ impl FamousDefs<'_, '_> { self.find_function("core:mem:drop") } + pub fn core_macros_todo(&self) -> Option { + self.find_macro("core:todo") + } + + pub fn core_macros_unimplemented(&self) -> Option { + self.find_macro("core:unimplemented") + } + pub fn builtin_crates(&self) -> impl Iterator { IntoIterator::into_iter([ self.std(), diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index e93eea8ce29e..8c97281b7832 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -112,7 +112,8 @@ fn add_missing_ok_or_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) { return None; diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs index 7aeadce8e269..56c8181e84ce 100644 --- a/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -51,17 +51,21 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option }; let paths = term_search(&term_search_ctx); - let mut assists = vec![]; 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 = 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), label: Label::new(format!("Replace `_` with `{}`", &code)), group: Some(GroupLabel("Replace `_` with a term".to_owned())), @@ -71,8 +75,9 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option TextEdit::replace(original_range.range, code), )), trigger_signature_help: false, - }); - } + }) + .collect(); + if !assists.is_empty() { Some(assists) } else { @@ -242,31 +247,33 @@ fn main(param: Foo) { check_has_fix( r#" struct Bar; +struct Baz; trait Foo { fn foo(self) -> Bar; } -impl Foo for i32 { +impl Foo for Baz { fn foo(self) -> Bar { unimplemented!() } } fn asd() -> Bar { - let a: i32 = 1; + let a = Baz; _$0 } "#, r" struct Bar; +struct Baz; trait Foo { fn foo(self) -> Bar; } -impl Foo for i32 { +impl Foo for Baz { fn foo(self) -> Bar { unimplemented!() } } fn asd() -> Bar { - let a: i32 = 1; + let a = Baz; Foo::foo(a) } ", @@ -330,30 +337,32 @@ fn main() { check_has_fix( r#" struct Bar {} +struct A; trait Foo { type Res; fn foo(&self) -> Self::Res; } -impl Foo for i32 { +impl Foo for A { type Res = Bar; fn foo(&self) -> Self::Res { Bar { } } } fn main() { - let a: i32 = 1; + let a = A; let c: Bar = _$0; }"#, r#" struct Bar {} +struct A; trait Foo { type Res; fn foo(&self) -> Self::Res; } -impl Foo for i32 { +impl Foo for A { type Res = Bar; fn foo(&self) -> Self::Res { Bar { } } } fn main() { - let a: i32 = 1; + let a = A; let c: Bar = Foo::foo(&a); }"#, ); diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 30bfe6ee9dc3..69ddc1e45efb 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -7263,8 +7263,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 6157..6365, - focus_range: 6222..6228, + full_range: 6290..6498, + focus_range: 6355..6361, name: "Future", kind: Trait, container_name: "future", @@ -7277,8 +7277,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 6995..7461, - focus_range: 7039..7047, + full_range: 7128..7594, + focus_range: 7172..7180, name: "Iterator", kind: Trait, container_name: "iterator", diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index efad2ff6d1db..ce7e3b3cd6a4 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -333,10 +333,12 @@ impl flags::AnalysisStats { mut file_ids: Vec, verbosity: Verbosity, ) { - let mut cargo_config = CargoConfig::default(); - cargo_config.sysroot = match self.no_sysroot { - true => None, - false => Some(RustLibSource::Discover), + let cargo_config = CargoConfig { + sysroot: match self.no_sysroot { + true => None, + false => Some(RustLibSource::Discover), + }, + ..Default::default() }; let mut bar = match verbosity { @@ -392,16 +394,15 @@ impl flags::AnalysisStats { continue; } - let range = sema.original_range(&expected_tail.syntax()).range; + let range = sema.original_range(expected_tail.syntax()).range; let original_text: String = db .file_text(file_id) .chars() - .into_iter() .skip(usize::from(range.start())) .take(usize::from(range.end()) - usize::from(range.start())) .collect(); - let scope = match sema.scope(&expected_tail.syntax()) { + let scope = match sema.scope(expected_tail.syntax()) { Some(it) => it, None => continue, }; @@ -425,14 +426,15 @@ impl flags::AnalysisStats { }; 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 mut formatter = |_: &hir::Type| todo.clone(); let mut syntax_hit_found = false; 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); // Validate if type-checks diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs index af2b136f9288..493e614dce68 100644 --- a/crates/rust-analyzer/src/cli/flags.rs +++ b/crates/rust-analyzer/src/cli/flags.rs @@ -93,9 +93,10 @@ xflags::xflags! { /// 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. 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 - /// 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 } diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 298d92468f67..7a1d2596371f 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -287,7 +287,7 @@ config_data! { } }"#, /// 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. diagnostics_disabled: FxHashSet = "[]", @@ -1537,7 +1537,7 @@ impl Config { && completion_item_edit_resolve(&self.caps), enable_self_on_the_fly: self.data.completion_autoself_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, callable: match self.data.completion_callable_snippets { CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments), diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index 64f19f0b32d7..bc4666c1221b 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -123,6 +123,7 @@ pub(crate) fn completion_item_kind( CompletionItemKind::Method => lsp_types::CompletionItemKind::METHOD, CompletionItemKind::Snippet => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::UnresolvedReference => lsp_types::CompletionItemKind::REFERENCE, + CompletionItemKind::Expression => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::SymbolKind(symbol) => match symbol { SymbolKind::Attribute => lsp_types::CompletionItemKind::FUNCTION, SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT, diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 23a3a7e0afa4..f125792d1258 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -60,6 +60,8 @@ //! try: infallible //! unpin: sized //! unsize: sized +//! todo: panic +//! unimplemented: panic #![rustc_coherence_is_core] @@ -927,6 +929,10 @@ pub mod fmt { use crate::mem::transmute; 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"] @@ -1438,6 +1444,33 @@ mod macros { // 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 pub(crate) mod builtin { #[rustc_builtin_macro] diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 9d54999fa38b..5669c8fa6bbe 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -344,7 +344,7 @@ Default: 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())`. diff --git a/editors/code/package.json b/editors/code/package.json index 3352007253e9..5bb87fa5b718 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -902,9 +902,9 @@ }, "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())`.", - "default": true, + "default": false, "type": "boolean" }, "rust-analyzer.diagnostics.disabled": {