mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Cleanup term search related changes
This commit is contained in:
parent
88964c0b6a
commit
125791386d
26 changed files with 590 additions and 516 deletions
|
@ -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(),
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ¶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<ConstParam> {
|
||||||
|
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)]
|
#[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)
|
||||||
|
|
|
@ -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)
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!(); }"#,
|
||||||
)
|
)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 []
|
||||||
|
|
|
@ -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()
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}"#,
|
}"#,
|
||||||
);
|
);
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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())`.
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
Loading…
Reference in a new issue