⬆️ rust-analyzer

This commit is contained in:
Laurențiu Nicola 2022-11-01 11:31:31 +02:00
parent 8807fc4cc3
commit c60b1f6414
40 changed files with 827 additions and 407 deletions

View file

@ -295,7 +295,9 @@ impl FlycheckActor {
} => {
let mut cmd = Command::new(toolchain::cargo());
cmd.arg(command);
cmd.args(&["--workspace", "--message-format=json"]);
cmd.current_dir(&self.root);
cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
.arg(self.root.join("Cargo.toml").as_os_str());
if let Some(target) = target_triple {
cmd.args(&["--target", target.as_str()]);

View file

@ -662,8 +662,12 @@ fn desugar_future_path(orig: TypeRef) -> Path {
let mut generic_args: Vec<_> =
std::iter::repeat(None).take(path.segments().len() - 1).collect();
let mut last = GenericArgs::empty();
let binding =
AssociatedTypeBinding { name: name![Output], type_ref: Some(orig), bounds: Vec::new() };
let binding = AssociatedTypeBinding {
name: name![Output],
args: None,
type_ref: Some(orig),
bounds: Vec::new(),
};
last.bindings.push(binding);
generic_args.push(Some(Interned::new(last)));

View file

@ -68,6 +68,9 @@ pub struct GenericArgs {
pub struct AssociatedTypeBinding {
/// The name of the associated type.
pub name: Name,
/// The generic arguments to the associated type. e.g. For `Trait<Assoc<'a, T> = &'a T>`, this
/// would be `['a, T]`.
pub args: Option<Interned<GenericArgs>>,
/// The type bound to this associated type (in `Item = T`, this would be the
/// `T`). This can be `None` if there are bounds instead.
pub type_ref: Option<TypeRef>,

View file

@ -163,6 +163,10 @@ pub(super) fn lower_generic_args(
ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
if let Some(name_ref) = assoc_type_arg.name_ref() {
let name = name_ref.as_name();
let args = assoc_type_arg
.generic_arg_list()
.and_then(|args| lower_generic_args(lower_ctx, args))
.map(Interned::new);
let type_ref = assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it));
let bounds = if let Some(l) = assoc_type_arg.type_bound_list() {
l.bounds()
@ -171,7 +175,7 @@ pub(super) fn lower_generic_args(
} else {
Vec::new()
};
bindings.push(AssociatedTypeBinding { name, type_ref, bounds });
bindings.push(AssociatedTypeBinding { name, args, type_ref, bounds });
}
}
ast::GenericArg::LifetimeArg(lifetime_arg) => {
@ -214,6 +218,7 @@ fn lower_generic_args_from_fn_path(
let type_ref = TypeRef::from_ast_opt(ctx, ret_type.ty());
bindings.push(AssociatedTypeBinding {
name: name![Output],
args: None,
type_ref: Some(type_ref),
bounds: Vec::new(),
});
@ -222,6 +227,7 @@ fn lower_generic_args_from_fn_path(
let type_ref = TypeRef::Tuple(Vec::new());
bindings.push(AssociatedTypeBinding {
name: name![Output],
args: None,
type_ref: Some(type_ref),
bounds: Vec::new(),
});

View file

@ -11,9 +11,9 @@ use syntax::SmolStr;
use crate::{
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId,
CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause,
Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
CallableDefId, CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
};
pub trait TyExt {
@ -338,10 +338,13 @@ pub trait ProjectionTyExt {
impl ProjectionTyExt for ProjectionTy {
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
TraitRef {
trait_id: to_chalk_trait_id(self.trait_(db)),
substitution: self.substitution.clone(),
}
// FIXME: something like `Split` trait from chalk-solve might be nice.
let generics = generics(db.upcast(), from_assoc_type_id(self.associated_ty_id).into());
let substitution = Substitution::from_iter(
Interner,
self.substitution.iter(Interner).skip(generics.len_self()),
);
TraitRef { trait_id: to_chalk_trait_id(self.trait_(db)), substitution }
}
fn trait_(&self, db: &dyn HirDatabase) -> TraitId {

View file

@ -289,16 +289,18 @@ impl HirDisplay for ProjectionTy {
return write!(f, "{}", TYPE_HINT_TRUNCATION);
}
let trait_ = f.db.trait_data(self.trait_(f.db));
let trait_ref = self.trait_ref(f.db);
write!(f, "<")?;
self.self_type_parameter(f.db).hir_fmt(f)?;
write!(f, " as {}", trait_.name)?;
if self.substitution.len(Interner) > 1 {
fmt_trait_ref(&trait_ref, f, true)?;
write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?;
let proj_params_count =
self.substitution.len(Interner) - trait_ref.substitution.len(Interner);
let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
if !proj_params.is_empty() {
write!(f, "<")?;
f.write_joined(&self.substitution.as_slice(Interner)[1..], ", ")?;
f.write_joined(proj_params, ", ")?;
write!(f, ">")?;
}
write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?;
Ok(())
}
}
@ -641,9 +643,12 @@ impl HirDisplay for Ty {
// Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
if f.display_target.is_test() {
write!(f, "{}::{}", trait_.name, type_alias_data.name)?;
// Note that the generic args for the associated type come before those for the
// trait (including the self type).
// FIXME: reconsider the generic args order upon formatting?
if parameters.len(Interner) > 0 {
write!(f, "<")?;
f.write_joined(&*parameters.as_slice(Interner), ", ")?;
f.write_joined(parameters.as_slice(Interner), ", ")?;
write!(f, ">")?;
}
} else {
@ -972,9 +977,20 @@ fn write_bounds_like_dyn_trait(
angle_open = true;
}
if let AliasTy::Projection(proj) = alias {
let type_alias =
f.db.type_alias_data(from_assoc_type_id(proj.associated_ty_id));
write!(f, "{} = ", type_alias.name)?;
let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id);
let type_alias = f.db.type_alias_data(assoc_ty_id);
write!(f, "{}", type_alias.name)?;
let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
if proj_arg_count > 0 {
write!(f, "<")?;
f.write_joined(
&proj.substitution.as_slice(Interner)[..proj_arg_count],
", ",
)?;
write!(f, ">")?;
}
write!(f, " = ")?;
}
ty.hir_fmt(f)?;
}

View file

@ -157,7 +157,7 @@ impl<'a> InferenceContext<'a> {
remaining_segments_for_ty,
true,
);
if let TyKind::Error = ty.kind(Interner) {
if ty.is_unknown() {
return None;
}

View file

@ -340,8 +340,8 @@ impl<'a> InferenceTable<'a> {
self.resolve_with_fallback(t, &|_, _, d, _| d)
}
/// Unify two types and register new trait goals that arise from that.
pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
let result = match self.try_unify(ty1, ty2) {
Ok(r) => r,
Err(_) => return false,
@ -350,9 +350,13 @@ impl<'a> InferenceTable<'a> {
true
}
/// Unify two types and return new trait goals arising from it, so the
/// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the
/// caller needs to deal with them.
pub(crate) fn try_unify<T: Zip<Interner>>(&mut self, t1: &T, t2: &T) -> InferResult<()> {
pub(crate) fn try_unify<T: ?Sized + Zip<Interner>>(
&mut self,
t1: &T,
t2: &T,
) -> InferResult<()> {
match self.var_unification_table.relate(
Interner,
&self.db,

View file

@ -81,7 +81,20 @@ pub type PlaceholderIndex = chalk_ir::PlaceholderIndex;
pub type VariableKind = chalk_ir::VariableKind<Interner>;
pub type VariableKinds = chalk_ir::VariableKinds<Interner>;
pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds<Interner>;
/// Represents generic parameters and an item bound by them. When the item has parent, the binders
/// also contain the generic parameters for its parent. See chalk's documentation for details.
///
/// One thing to keep in mind when working with `Binders` (and `Substitution`s, which represent
/// generic arguments) in rust-analyzer is that the ordering within *is* significant - the generic
/// parameters/arguments for an item MUST come before those for its parent. This is to facilitate
/// the integration with chalk-solve, which mildly puts constraints as such. See #13335 for its
/// motivation in detail.
pub type Binders<T> = chalk_ir::Binders<T>;
/// Interned list of generic arguments for an item. When an item has parent, the `Substitution` for
/// it contains generic arguments for both its parent and itself. See chalk's documentation for
/// details.
///
/// See `Binders` for the constraint on the ordering.
pub type Substitution = chalk_ir::Substitution<Interner>;
pub type GenericArg = chalk_ir::GenericArg<Interner>;
pub type GenericArgData = chalk_ir::GenericArgData<Interner>;
@ -124,14 +137,6 @@ pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>;
pub type Guidance = chalk_solve::Guidance<Interner>;
pub type WhereClause = chalk_ir::WhereClause<Interner>;
// FIXME: get rid of this
pub fn subst_prefix(s: &Substitution, n: usize) -> Substitution {
Substitution::from_iter(
Interner,
s.as_slice(Interner)[..std::cmp::min(s.len(Interner), n)].iter().cloned(),
)
}
/// Return an index of a parameter in the generic type parameter list by it's id.
pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize> {
generics(db.upcast(), id.parent).param_idx(id)
@ -382,7 +387,6 @@ pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + TypeFold
pub fn replace_errors_with_variables<T>(t: &T) -> Canonical<T>
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner> + Clone,
T: HasInterner<Interner = Interner>,
{
use chalk_ir::{
fold::{FallibleTypeFolder, TypeSuperFoldable},

View file

@ -447,12 +447,31 @@ impl<'a> TyLoweringContext<'a> {
.db
.trait_data(trait_ref.hir_trait_id())
.associated_type_by_name(segment.name);
match found {
Some(associated_ty) => {
// FIXME handle type parameters on the segment
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`trait_ref.substitution`).
let substitution = self.substs_from_path_segment(
segment,
Some(associated_ty.into()),
false,
None,
);
let len_self =
generics(self.db.upcast(), associated_ty.into()).len_self();
let substitution = Substitution::from_iter(
Interner,
substitution
.iter(Interner)
.take(len_self)
.chain(trait_ref.substitution.iter(Interner)),
);
TyKind::Alias(AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution: trait_ref.substitution,
substitution,
}))
.intern(Interner)
}
@ -590,36 +609,48 @@ impl<'a> TyLoweringContext<'a> {
res,
Some(segment.name.clone()),
move |name, t, associated_ty| {
if name == segment.name {
let substs = match self.type_param_mode {
ParamLoweringMode::Placeholder => {
// if we're lowering to placeholders, we have to put
// them in now
let generics = generics(
self.db.upcast(),
self.resolver
.generic_def()
.expect("there should be generics if there's a generic param"),
);
let s = generics.placeholder_subst(self.db);
s.apply(t.substitution.clone(), Interner)
}
ParamLoweringMode::Variable => t.substitution.clone(),
};
// We need to shift in the bound vars, since
// associated_type_shorthand_candidates does not do that
let substs = substs.shifted_in_from(Interner, self.in_binders);
// FIXME handle type parameters on the segment
Some(
TyKind::Alias(AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution: substs,
}))
.intern(Interner),
)
} else {
None
if name != segment.name {
return None;
}
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`t.substitution`).
let substs = self.substs_from_path_segment(
segment.clone(),
Some(associated_ty.into()),
false,
None,
);
let len_self = generics(self.db.upcast(), associated_ty.into()).len_self();
let substs = Substitution::from_iter(
Interner,
substs.iter(Interner).take(len_self).chain(t.substitution.iter(Interner)),
);
let substs = match self.type_param_mode {
ParamLoweringMode::Placeholder => {
// if we're lowering to placeholders, we have to put
// them in now
let generics = generics(self.db.upcast(), def);
let s = generics.placeholder_subst(self.db);
s.apply(substs, Interner)
}
ParamLoweringMode::Variable => substs,
};
// We need to shift in the bound vars, since
// associated_type_shorthand_candidates does not do that
let substs = substs.shifted_in_from(Interner, self.in_binders);
Some(
TyKind::Alias(AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution: substs,
}))
.intern(Interner),
)
},
);
@ -777,7 +808,15 @@ impl<'a> TyLoweringContext<'a> {
// handle defaults. In expression or pattern path segments without
// explicitly specified type arguments, missing type arguments are inferred
// (i.e. defaults aren't used).
if !infer_args || had_explicit_args {
// Generic parameters for associated types are not supposed to have defaults, so we just
// ignore them.
let is_assoc_ty = if let GenericDefId::TypeAliasId(id) = def {
let container = id.lookup(self.db.upcast()).container;
matches!(container, ItemContainerId::TraitId(_))
} else {
false
};
if !is_assoc_ty && (!infer_args || had_explicit_args) {
let defaults = self.db.generic_defaults(def);
assert_eq!(total_len, defaults.len());
let parent_from = item_len - substs.len();
@ -966,9 +1005,28 @@ impl<'a> TyLoweringContext<'a> {
None => return SmallVec::new(),
Some(t) => t,
};
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`super_trait_ref.substitution`).
let substitution = self.substs_from_path_segment(
// FIXME: This is hack. We shouldn't really build `PathSegment` directly.
PathSegment { name: &binding.name, args_and_bindings: binding.args.as_deref() },
Some(associated_ty.into()),
false, // this is not relevant
Some(super_trait_ref.self_type_parameter(Interner)),
);
let self_params = generics(self.db.upcast(), associated_ty.into()).len_self();
let substitution = Substitution::from_iter(
Interner,
substitution
.iter(Interner)
.take(self_params)
.chain(super_trait_ref.substitution.iter(Interner)),
);
let projection_ty = ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution: super_trait_ref.substitution,
substitution,
};
let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity(
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),

View file

@ -22,10 +22,10 @@ use crate::{
from_foreign_def_id,
infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
primitive::{FloatTy, IntTy, UintTy},
static_lifetime,
static_lifetime, to_chalk_trait_id,
utils::all_super_traits,
AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner,
Scalar, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
};
/// This is used as a key for indexing impls.
@ -624,52 +624,76 @@ pub(crate) fn iterate_method_candidates<T>(
slot
}
/// Looks up the impl method that actually runs for the trait method `func`.
///
/// Returns `func` if it's not a method defined in a trait or the lookup failed.
pub fn lookup_impl_method(
self_ty: &Ty,
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
trait_: TraitId,
func: FunctionId,
fn_subst: Substitution,
) -> FunctionId {
let trait_id = match func.lookup(db.upcast()).container {
ItemContainerId::TraitId(id) => id,
_ => return func,
};
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
let fn_params = fn_subst.len(Interner) - trait_params;
let trait_ref = TraitRef {
trait_id: to_chalk_trait_id(trait_id),
substitution: Substitution::from_iter(Interner, fn_subst.iter(Interner).skip(fn_params)),
};
let name = &db.function_data(func).name;
lookup_impl_method_for_trait_ref(trait_ref, db, env, name).unwrap_or(func)
}
fn lookup_impl_method_for_trait_ref(
trait_ref: TraitRef,
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
name: &Name,
) -> Option<FunctionId> {
let self_ty_fp = TyFingerprint::for_trait_impl(self_ty)?;
let trait_impls = db.trait_impls_in_deps(env.krate);
let impls = trait_impls.for_trait_and_self_ty(trait_, self_ty_fp);
let mut table = InferenceTable::new(db, env.clone());
find_matching_impl(impls, &mut table, &self_ty).and_then(|data| {
data.items.iter().find_map(|it| match it {
AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f),
_ => None,
})
let self_ty = trait_ref.self_type_parameter(Interner);
let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
let impls = db.trait_impls_in_deps(env.krate);
let impls = impls.for_trait_and_self_ty(trait_ref.hir_trait_id(), self_ty_fp);
let table = InferenceTable::new(db, env);
let impl_data = find_matching_impl(impls, table, trait_ref)?;
impl_data.items.iter().find_map(|it| match it {
AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f),
_ => None,
})
}
fn find_matching_impl(
mut impls: impl Iterator<Item = ImplId>,
table: &mut InferenceTable<'_>,
self_ty: &Ty,
mut table: InferenceTable<'_>,
actual_trait_ref: TraitRef,
) -> Option<Arc<ImplData>> {
let db = table.db;
loop {
let impl_ = impls.next()?;
let r = table.run_in_snapshot(|table| {
let impl_data = db.impl_data(impl_);
let substs =
let impl_substs =
TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build();
let impl_ty = db.impl_self_ty(impl_).substitute(Interner, &substs);
let trait_ref = db
.impl_trait(impl_)
.expect("non-trait method in find_matching_impl")
.substitute(Interner, &impl_substs);
table
.unify(self_ty, &impl_ty)
.then(|| {
let wh_goals =
crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs)
.into_iter()
.map(|b| b.cast(Interner));
if !table.unify(&trait_ref, &actual_trait_ref) {
return None;
}
let goal = crate::Goal::all(Interner, wh_goals);
table.try_obligation(goal).map(|_| impl_data)
})
.flatten()
let wcs = crate::chalk_db::convert_where_clauses(db, impl_.into(), &impl_substs)
.into_iter()
.map(|b| b.cast(Interner));
let goal = crate::Goal::all(Interner, wcs);
table.try_obligation(goal).map(|_| impl_data)
});
if r.is_some() {
break r;
@ -1214,7 +1238,7 @@ fn is_valid_fn_candidate(
let expected_receiver =
sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
check_that!(table.unify(&receiver_ty, &expected_receiver));
check_that!(table.unify(receiver_ty, &expected_receiver));
}
if let ItemContainerId::ImplId(impl_id) = container {

View file

@ -196,3 +196,34 @@ fn test(
"#,
);
}
#[test]
fn projection_type_correct_arguments_order() {
check_types_source_code(
r#"
trait Foo<T> {
type Assoc<U>;
}
fn f<T: Foo<i32>>(a: T::Assoc<usize>) {
a;
//^ <T as Foo<i32>>::Assoc<usize>
}
"#,
);
}
#[test]
fn generic_associated_type_binding_in_impl_trait() {
check_types_source_code(
r#"
//- minicore: sized
trait Foo<T> {
type Assoc<U>;
}
fn f(a: impl Foo<i8, Assoc<i16> = i32>) {
a;
//^ impl Foo<i8, Assoc<i16> = i32>
}
"#,
);
}

View file

@ -3963,3 +3963,124 @@ fn g(t: &(dyn T + Send)) {
"#,
);
}
#[test]
fn gats_in_path() {
check_types(
r#"
//- minicore: deref
use core::ops::Deref;
trait PointerFamily {
type Pointer<T>: Deref<Target = T>;
}
fn f<P: PointerFamily>(p: P::Pointer<i32>) {
let a = *p;
//^ i32
}
fn g<P: PointerFamily>(p: <P as PointerFamily>::Pointer<i32>) {
let a = *p;
//^ i32
}
"#,
);
}
#[test]
fn gats_with_impl_trait() {
// FIXME: the last function (`fn i()`) is not valid Rust as of this writing because you cannot
// specify the same associated type multiple times even if their arguments are different (c.f.
// `fn h()`, which is valid). Reconsider how to treat these invalid types.
check_types(
r#"
//- minicore: deref
use core::ops::Deref;
trait Trait {
type Assoc<T>: Deref<Target = T>;
fn get<U>(&self) -> Self::Assoc<U>;
}
fn f<T>(v: impl Trait) {
let a = v.get::<i32>().deref();
//^ &i32
let a = v.get::<T>().deref();
//^ &T
}
fn g<'a, T: 'a>(v: impl Trait<Assoc<T> = &'a T>) {
let a = v.get::<T>();
//^ &T
let a = v.get::<()>();
//^ Trait::Assoc<(), impl Trait<Assoc<T> = &T>>
}
fn h<'a>(v: impl Trait<Assoc<i32> = &'a i32> + Trait<Assoc<i64> = &'a i64>) {
let a = v.get::<i32>();
//^ &i32
let a = v.get::<i64>();
//^ &i64
}
fn i<'a>(v: impl Trait<Assoc<i32> = &'a i32, Assoc<i64> = &'a i64>) {
let a = v.get::<i32>();
//^ &i32
let a = v.get::<i64>();
//^ &i64
}
"#,
);
}
#[test]
fn gats_with_dyn() {
// This test is here to keep track of how we infer things despite traits with GATs being not
// object-safe currently.
// FIXME: reconsider how to treat these invalid types.
check_infer_with_mismatches(
r#"
//- minicore: deref
use core::ops::Deref;
trait Trait {
type Assoc<T>: Deref<Target = T>;
fn get<U>(&self) -> Self::Assoc<U>;
}
fn f<'a>(v: &dyn Trait<Assoc<i32> = &'a i32>) {
v.get::<i32>().deref();
}
"#,
expect![[r#"
90..94 'self': &Self
127..128 'v': &(dyn Trait<Assoc<i32> = &i32>)
164..195 '{ ...f(); }': ()
170..171 'v': &(dyn Trait<Assoc<i32> = &i32>)
170..184 'v.get::<i32>()': &i32
170..192 'v.get:...eref()': &i32
"#]],
);
}
#[test]
fn gats_in_associated_type_binding() {
check_types(
r#"
trait Trait {
type Assoc<T>;
fn get<U>(&self) -> Self::Assoc<U>;
}
fn f<T>(t: T)
where
T: Trait<Assoc<i32> = u32>,
T: Trait<Assoc<isize> = usize>,
{
let a = t.get::<i32>();
//^ u32
let a = t.get::<isize>();
//^ usize
let a = t.get::<()>();
//^ Trait::Assoc<(), T>
}
"#,
);
}

View file

@ -5,7 +5,7 @@ use itertools::Itertools;
use crate::{
chalk_db, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, mapping::from_chalk,
CallableDefId, Interner,
CallableDefId, Interner, ProjectionTyExt,
};
use hir_def::{AdtId, ItemContainerId, Lookup, TypeAliasId};
@ -63,17 +63,31 @@ impl DebugContext<'_> {
ItemContainerId::TraitId(t) => t,
_ => panic!("associated type not in trait"),
};
let trait_data = self.0.trait_data(trait_);
let params = projection_ty.substitution.as_slice(Interner);
write!(fmt, "<{:?} as {}", &params[0], trait_data.name,)?;
if params.len() > 1 {
let trait_name = &self.0.trait_data(trait_).name;
let trait_ref = projection_ty.trait_ref(self.0);
let trait_params = trait_ref.substitution.as_slice(Interner);
let self_ty = trait_ref.self_type_parameter(Interner);
write!(fmt, "<{:?} as {}", self_ty, trait_name)?;
if trait_params.len() > 1 {
write!(
fmt,
"<{}>",
&params[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
)?;
}
write!(fmt, ">::{}", type_alias_data.name)
write!(fmt, ">::{}", type_alias_data.name)?;
let proj_params_count = projection_ty.substitution.len(Interner) - trait_params.len();
let proj_params = &projection_ty.substitution.as_slice(Interner)[..proj_params_count];
if !proj_params.is_empty() {
write!(
fmt,
"<{}>",
proj_params.iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
)?;
}
Ok(())
}
pub(crate) fn debug_fn_def_id(

View file

@ -270,7 +270,7 @@ impl SourceAnalyzer {
let expr_id = self.expr_id(db, &call.clone().into())?;
let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, &substs))
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs))
}
pub(crate) fn resolve_await_to_poll(
@ -311,7 +311,7 @@ impl SourceAnalyzer {
// HACK: subst for `poll()` coincides with that for `Future` because `poll()` itself
// doesn't have any generic parameters, so we skip building another subst for `poll()`.
let substs = hir_ty::TyBuilder::subst_for_def(db, future_trait, None).push(ty).build();
Some(self.resolve_impl_method_or_trait_def(db, poll_fn, &substs))
Some(self.resolve_impl_method_or_trait_def(db, poll_fn, substs))
}
pub(crate) fn resolve_prefix_expr(
@ -331,7 +331,7 @@ impl SourceAnalyzer {
// don't have any generic parameters, so we skip building another subst for the methods.
let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None).push(ty.clone()).build();
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs))
}
pub(crate) fn resolve_index_expr(
@ -351,7 +351,7 @@ impl SourceAnalyzer {
.push(base_ty.clone())
.push(index_ty.clone())
.build();
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs))
}
pub(crate) fn resolve_bin_expr(
@ -372,7 +372,7 @@ impl SourceAnalyzer {
.push(rhs.clone())
.build();
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs))
}
pub(crate) fn resolve_try_expr(
@ -392,7 +392,7 @@ impl SourceAnalyzer {
// doesn't have any generic parameters, so we skip building another subst for `branch()`.
let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None).push(ty.clone()).build();
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs))
}
pub(crate) fn resolve_field(
@ -487,9 +487,9 @@ impl SourceAnalyzer {
let mut prefer_value_ns = false;
let resolved = (|| {
let infer = self.infer.as_deref()?;
if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) {
let expr_id = self.expr_id(db, &path_expr.into())?;
let infer = self.infer.as_ref()?;
if let Some(assoc) = infer.assoc_resolutions_for_expr(expr_id) {
let assoc = match assoc {
AssocItemId::FunctionId(f_in_trait) => {
@ -497,9 +497,12 @@ impl SourceAnalyzer {
None => assoc,
Some(func_ty) => {
if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) {
self.resolve_impl_method(db, f_in_trait, subs)
.map(AssocItemId::FunctionId)
.unwrap_or(assoc)
self.resolve_impl_method_or_trait_def(
db,
f_in_trait,
subs.clone(),
)
.into()
} else {
assoc
}
@ -520,18 +523,18 @@ impl SourceAnalyzer {
prefer_value_ns = true;
} else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
let pat_id = self.pat_id(&path_pat.into())?;
if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) {
if let Some(assoc) = infer.assoc_resolutions_for_pat(pat_id) {
return Some(PathResolution::Def(AssocItem::from(assoc).into()));
}
if let Some(VariantId::EnumVariantId(variant)) =
self.infer.as_ref()?.variant_resolution_for_pat(pat_id)
infer.variant_resolution_for_pat(pat_id)
{
return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
}
} else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) {
let expr_id = self.expr_id(db, &rec_lit.into())?;
if let Some(VariantId::EnumVariantId(variant)) =
self.infer.as_ref()?.variant_resolution_for_expr(expr_id)
infer.variant_resolution_for_expr(expr_id)
{
return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
}
@ -541,8 +544,7 @@ impl SourceAnalyzer {
|| parent().and_then(ast::TupleStructPat::cast).map(ast::Pat::from);
if let Some(pat) = record_pat.or_else(tuple_struct_pat) {
let pat_id = self.pat_id(&pat)?;
let variant_res_for_pat =
self.infer.as_ref()?.variant_resolution_for_pat(pat_id);
let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id);
if let Some(VariantId::EnumVariantId(variant)) = variant_res_for_pat {
return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
}
@ -780,37 +782,22 @@ impl SourceAnalyzer {
false
}
fn resolve_impl_method(
&self,
db: &dyn HirDatabase,
func: FunctionId,
substs: &Substitution,
) -> Option<FunctionId> {
let impled_trait = match func.lookup(db.upcast()).container {
ItemContainerId::TraitId(trait_id) => trait_id,
_ => return None,
};
if substs.is_empty(Interner) {
return None;
}
let self_ty = substs.at(Interner, 0).ty(Interner)?;
let krate = self.resolver.krate();
let trait_env = self.resolver.body_owner()?.as_generic_def_id().map_or_else(
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
|d| db.trait_environment(d),
);
let fun_data = db.function_data(func);
method_resolution::lookup_impl_method(self_ty, db, trait_env, impled_trait, &fun_data.name)
}
fn resolve_impl_method_or_trait_def(
&self,
db: &dyn HirDatabase,
func: FunctionId,
substs: &Substitution,
substs: Substitution,
) -> FunctionId {
self.resolve_impl_method(db, func, substs).unwrap_or(func)
let krate = self.resolver.krate();
let owner = match self.resolver.body_owner() {
Some(it) => it,
None => return func,
};
let env = owner.as_generic_def_id().map_or_else(
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
|d| db.trait_environment(d),
);
method_resolution::lookup_impl_method(db, env, func, substs)
}
fn lang_trait_fn(

View file

@ -1834,4 +1834,86 @@ fn f() {
"#,
);
}
#[test]
fn goto_bin_op_multiple_impl() {
check(
r#"
//- minicore: add
struct S;
impl core::ops::Add for S {
fn add(
//^^^
) {}
}
impl core::ops::Add<usize> for S {
fn add(
) {}
}
fn f() {
S +$0 S
}
"#,
);
check(
r#"
//- minicore: add
struct S;
impl core::ops::Add for S {
fn add(
) {}
}
impl core::ops::Add<usize> for S {
fn add(
//^^^
) {}
}
fn f() {
S +$0 0usize
}
"#,
);
}
#[test]
fn path_call_multiple_trait_impl() {
check(
r#"
trait Trait<T> {
fn f(_: T);
}
impl Trait<i32> for usize {
fn f(_: i32) {}
//^
}
impl Trait<i64> for usize {
fn f(_: i64) {}
}
fn main() {
usize::f$0(0i32);
}
"#,
);
check(
r#"
trait Trait<T> {
fn f(_: T);
}
impl Trait<i32> for usize {
fn f(_: i32) {}
}
impl Trait<i64> for usize {
fn f(_: i64) {}
//^
}
fn main() {
usize::f$0(0i64);
}
"#,
)
}
}

View file

@ -23,7 +23,7 @@ crossbeam-channel = "0.5.5"
dissimilar = "1.0.4"
itertools = "0.10.5"
scip = "0.1.1"
lsp-types = { version = "0.93.1", features = ["proposed"] }
lsp-types = { version = "=0.93.2", features = ["proposed"] }
parking_lot = "0.12.1"
xflags = "0.3.0"
oorandom = "11.1.3"

View file

@ -11,7 +11,7 @@ use std::{env, fs, path::Path, process};
use lsp_server::Connection;
use project_model::ProjectManifest;
use rust_analyzer::{cli::flags, config::Config, from_json, lsp_ext::supports_utf8, Result};
use rust_analyzer::{cli::flags, config::Config, from_json, Result};
use vfs::AbsPathBuf;
#[cfg(all(feature = "mimalloc"))]
@ -191,11 +191,7 @@ fn run_server() -> Result<()> {
name: String::from("rust-analyzer"),
version: Some(rust_analyzer::version().to_string()),
}),
offset_encoding: if supports_utf8(config.caps()) {
Some("utf-8".to_string())
} else {
None
},
offset_encoding: None,
};
let initialize_result = serde_json::to_value(initialize_result).unwrap();

View file

@ -6,19 +6,25 @@ use lsp_types::{
FileOperationFilter, FileOperationPattern, FileOperationPatternKind,
FileOperationRegistrationOptions, FoldingRangeProviderCapability, HoverProviderCapability,
ImplementationProviderCapability, InlayHintOptions, InlayHintServerCapabilities, OneOf,
RenameOptions, SaveOptions, SelectionRangeProviderCapability, SemanticTokensFullOptions,
SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions,
TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
TypeDefinitionProviderCapability, WorkDoneProgressOptions,
PositionEncodingKind, RenameOptions, SaveOptions, SelectionRangeProviderCapability,
SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities,
SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions,
WorkspaceFileOperationsServerCapabilities, WorkspaceServerCapabilities,
};
use serde_json::json;
use crate::config::{Config, RustfmtConfig};
use crate::lsp_ext::supports_utf8;
use crate::semantic_tokens;
pub fn server_capabilities(config: &Config) -> ServerCapabilities {
ServerCapabilities {
position_encoding: if supports_utf8(config.caps()) {
Some(PositionEncodingKind::UTF8)
} else {
None
},
text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
open_close: Some(true),
change: Some(TextDocumentSyncKind::INCREMENTAL),

View file

@ -20,7 +20,7 @@ use crate::cli::{
load_cargo::{load_workspace, LoadCargoConfig},
Result,
};
use crate::line_index::{LineEndings, LineIndex, OffsetEncoding};
use crate::line_index::{LineEndings, LineIndex, PositionEncoding};
use crate::to_proto;
use crate::version::version;
@ -126,7 +126,7 @@ impl LsifManager<'_> {
let line_index = self.db.line_index(file_id);
let line_index = LineIndex {
index: line_index,
encoding: OffsetEncoding::Utf16,
encoding: PositionEncoding::Utf16,
endings: LineEndings::Unix,
};
let range_id = self.add_vertex(lsif::Vertex::Range {
@ -248,7 +248,7 @@ impl LsifManager<'_> {
let line_index = self.db.line_index(file_id);
let line_index = LineIndex {
index: line_index,
encoding: OffsetEncoding::Utf16,
encoding: PositionEncoding::Utf16,
endings: LineEndings::Unix,
};
let result = folds

View file

@ -5,7 +5,7 @@ use std::{
time::Instant,
};
use crate::line_index::{LineEndings, LineIndex, OffsetEncoding};
use crate::line_index::{LineEndings, LineIndex, PositionEncoding};
use hir::Name;
use ide::{
LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId,
@ -91,7 +91,7 @@ impl flags::Scip {
let line_index = LineIndex {
index: db.line_index(file_id),
encoding: OffsetEncoding::Utf8,
encoding: PositionEncoding::Utf8,
endings: LineEndings::Unix,
};

View file

@ -32,7 +32,7 @@ use vfs::AbsPathBuf;
use crate::{
caps::completion_item_edit_resolve,
diagnostics::DiagnosticsMapConfig,
line_index::OffsetEncoding,
line_index::PositionEncoding,
lsp_ext::{self, supports_utf8, WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope},
};
@ -948,11 +948,11 @@ impl Config {
.is_some()
}
pub fn offset_encoding(&self) -> OffsetEncoding {
pub fn position_encoding(&self) -> PositionEncoding {
if supports_utf8(&self.caps) {
OffsetEncoding::Utf8
PositionEncoding::Utf8
} else {
OffsetEncoding::Utf16
PositionEncoding::Utf16
}
}

View file

@ -8,7 +8,7 @@ use stdx::format_to;
use vfs::{AbsPath, AbsPathBuf};
use crate::{
global_state::GlobalStateSnapshot, line_index::OffsetEncoding, lsp_ext,
global_state::GlobalStateSnapshot, line_index::PositionEncoding, lsp_ext,
to_proto::url_from_abs_path,
};
@ -66,17 +66,17 @@ fn location(
let uri = url_from_abs_path(&file_name);
let range = {
let offset_encoding = snap.config.offset_encoding();
let position_encoding = snap.config.position_encoding();
lsp_types::Range::new(
position(&offset_encoding, span, span.line_start, span.column_start),
position(&offset_encoding, span, span.line_end, span.column_end),
position(&position_encoding, span, span.line_start, span.column_start),
position(&position_encoding, span, span.line_end, span.column_end),
)
};
lsp_types::Location::new(uri, range)
}
fn position(
offset_encoding: &OffsetEncoding,
position_encoding: &PositionEncoding,
span: &DiagnosticSpan,
line_offset: usize,
column_offset: usize,
@ -93,9 +93,9 @@ fn position(
};
}
let mut char_offset = 0;
let len_func = match offset_encoding {
OffsetEncoding::Utf8 => char::len_utf8,
OffsetEncoding::Utf16 => char::len_utf16,
let len_func = match position_encoding {
PositionEncoding::Utf8 => char::len_utf8,
PositionEncoding::Utf16 => char::len_utf16,
};
for c in line.text.chars() {
char_offset += 1;

View file

@ -8,7 +8,7 @@ use vfs::AbsPathBuf;
use crate::{
from_json,
global_state::GlobalStateSnapshot,
line_index::{LineIndex, OffsetEncoding},
line_index::{LineIndex, PositionEncoding},
lsp_ext,
lsp_utils::invalid_params_error,
Result,
@ -25,10 +25,10 @@ pub(crate) fn vfs_path(url: &lsp_types::Url) -> Result<vfs::VfsPath> {
pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> Result<TextSize> {
let line_col = match line_index.encoding {
OffsetEncoding::Utf8 => {
PositionEncoding::Utf8 => {
LineCol { line: position.line as u32, col: position.character as u32 }
}
OffsetEncoding::Utf16 => {
PositionEncoding::Utf16 => {
let line_col =
LineColUtf16 { line: position.line as u32, col: position.character as u32 };
line_index.index.to_utf8(line_col)

View file

@ -383,7 +383,7 @@ impl GlobalStateSnapshot {
pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
let endings = self.vfs.read().1[&file_id];
let index = self.analysis.file_line_index(file_id)?;
let res = LineIndex { index, endings, encoding: self.config.offset_encoding() };
let res = LineIndex { index, endings, encoding: self.config.position_encoding() };
Ok(res)
}

View file

@ -7,7 +7,7 @@
use std::sync::Arc;
pub enum OffsetEncoding {
pub enum PositionEncoding {
Utf8,
Utf16,
}
@ -15,7 +15,7 @@ pub enum OffsetEncoding {
pub(crate) struct LineIndex {
pub(crate) index: Arc<ide::LineIndex>,
pub(crate) endings: LineEndings,
pub(crate) encoding: OffsetEncoding,
pub(crate) encoding: PositionEncoding,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]

View file

@ -3,6 +3,7 @@
use std::{collections::HashMap, path::PathBuf};
use lsp_types::request::Request;
use lsp_types::PositionEncodingKind;
use lsp_types::{
notification::Notification, CodeActionKind, DocumentOnTypeFormattingParams,
PartialResultParams, Position, Range, TextDocumentIdentifier, WorkDoneProgressParams,
@ -455,7 +456,15 @@ pub(crate) enum CodeLensResolveData {
}
pub fn supports_utf8(caps: &lsp_types::ClientCapabilities) -> bool {
caps.offset_encoding.as_deref().unwrap_or_default().iter().any(|it| it == "utf-8")
match &caps.general {
Some(general) => general
.position_encodings
.as_deref()
.unwrap_or_default()
.iter()
.any(|it| it == &PositionEncodingKind::UTF8),
_ => false,
}
}
pub enum MoveItem {}

View file

@ -6,7 +6,7 @@ use lsp_server::Notification;
use crate::{
from_proto,
global_state::GlobalState,
line_index::{LineEndings, LineIndex, OffsetEncoding},
line_index::{LineEndings, LineIndex, PositionEncoding},
LspError,
};
@ -140,7 +140,7 @@ pub(crate) fn apply_document_changes(
index: Arc::new(ide::LineIndex::new(old_text)),
// We don't care about line endings or offset encoding here.
endings: LineEndings::Unix,
encoding: OffsetEncoding::Utf16,
encoding: PositionEncoding::Utf16,
};
// The changes we got must be applied sequentially, but can cross lines so we

View file

@ -607,30 +607,34 @@ impl GlobalState {
/// Handles a request.
fn on_request(&mut self, req: Request) {
if self.shutdown_requested {
self.respond(lsp_server::Response::new_err(
req.id,
lsp_server::ErrorCode::InvalidRequest as i32,
"Shutdown already requested.".to_owned(),
));
return;
let mut dispatcher = RequestDispatcher { req: Some(req), global_state: self };
dispatcher.on_sync_mut::<lsp_types::request::Shutdown>(|s, ()| {
s.shutdown_requested = true;
Ok(())
});
if let RequestDispatcher { req: Some(req), global_state: this } = &mut dispatcher {
if this.shutdown_requested {
this.respond(lsp_server::Response::new_err(
req.id.clone(),
lsp_server::ErrorCode::InvalidRequest as i32,
"Shutdown already requested.".to_owned(),
));
return;
}
// Avoid flashing a bunch of unresolved references during initial load.
if this.workspaces.is_empty() && !this.is_quiescent() {
this.respond(lsp_server::Response::new_err(
req.id.clone(),
lsp_server::ErrorCode::ContentModified as i32,
"waiting for cargo metadata or cargo check".to_owned(),
));
return;
}
}
// Avoid flashing a bunch of unresolved references during initial load.
if self.workspaces.is_empty() && !self.is_quiescent() {
self.respond(lsp_server::Response::new_err(
req.id,
lsp_server::ErrorCode::ContentModified as i32,
"waiting for cargo metadata or cargo check".to_owned(),
));
return;
}
RequestDispatcher { req: Some(req), global_state: self }
.on_sync_mut::<lsp_types::request::Shutdown>(|s, ()| {
s.shutdown_requested = true;
Ok(())
})
dispatcher
.on_sync_mut::<lsp_ext::ReloadWorkspace>(handlers::handle_workspace_reload)
.on_sync_mut::<lsp_ext::MemoryUsage>(handlers::handle_memory_usage)
.on_sync_mut::<lsp_ext::ShuffleCrateGraph>(handlers::handle_shuffle_crate_graph)

View file

@ -21,7 +21,7 @@ use crate::{
cargo_target_spec::CargoTargetSpec,
config::{CallInfoConfig, Config},
global_state::GlobalStateSnapshot,
line_index::{LineEndings, LineIndex, OffsetEncoding},
line_index::{LineEndings, LineIndex, PositionEncoding},
lsp_ext,
lsp_utils::invalid_params_error,
semantic_tokens, Result,
@ -30,8 +30,8 @@ use crate::{
pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
let line_col = line_index.index.line_col(offset);
match line_index.encoding {
OffsetEncoding::Utf8 => lsp_types::Position::new(line_col.line, line_col.col),
OffsetEncoding::Utf16 => {
PositionEncoding::Utf8 => lsp_types::Position::new(line_col.line, line_col.col),
PositionEncoding::Utf16 => {
let line_col = line_index.index.to_utf16(line_col);
lsp_types::Position::new(line_col.line, line_col.col)
}
@ -1394,7 +1394,7 @@ fn main() {
let line_index = LineIndex {
index: Arc::new(ide::LineIndex::new(text)),
endings: LineEndings::Unix,
encoding: OffsetEncoding::Utf16,
encoding: PositionEncoding::Utf16,
};
let converted: Vec<lsp_types::FoldingRange> =
folds.into_iter().map(|it| folding_range(text, &line_index, true, it)).collect();

View file

@ -51,7 +51,7 @@ TypeArg =
Type
AssocTypeArg =
NameRef GenericParamList? (':' TypeBoundList | ('=' Type | ConstArg))
NameRef GenericArgList? (':' TypeBoundList | ('=' Type | ConstArg))
LifetimeArg =
Lifetime

View file

@ -120,7 +120,7 @@ pub struct AssocTypeArg {
impl ast::HasTypeBounds for AssocTypeArg {}
impl AssocTypeArg {
pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
pub fn generic_param_list(&self) -> Option<GenericParamList> { support::child(&self.syntax) }
pub fn generic_arg_list(&self) -> Option<GenericArgList> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
pub fn const_arg(&self) -> Option<ConstArg> { support::child(&self.syntax) }
@ -142,16 +142,6 @@ impl ConstArg {
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GenericParamList {
pub(crate) syntax: SyntaxNode,
}
impl GenericParamList {
pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
pub fn generic_params(&self) -> AstChildren<GenericParam> { support::children(&self.syntax) }
pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TypeBoundList {
pub(crate) syntax: SyntaxNode,
@ -527,6 +517,16 @@ impl Abi {
pub fn extern_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![extern]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GenericParamList {
pub(crate) syntax: SyntaxNode,
}
impl GenericParamList {
pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
pub fn generic_params(&self) -> AstChildren<GenericParam> { support::children(&self.syntax) }
pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct WhereClause {
pub(crate) syntax: SyntaxNode,
@ -1834,17 +1834,6 @@ impl AstNode for ConstArg {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for GenericParamList {
fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_PARAM_LIST }
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for TypeBoundList {
fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_BOUND_LIST }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@ -2153,6 +2142,17 @@ impl AstNode for Abi {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for GenericParamList {
fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_PARAM_LIST }
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for WhereClause {
fn can_cast(kind: SyntaxKind) -> bool { kind == WHERE_CLAUSE }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@ -4263,11 +4263,6 @@ impl std::fmt::Display for ConstArg {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for GenericParamList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for TypeBoundList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@ -4408,6 +4403,11 @@ impl std::fmt::Display for Abi {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for GenericParamList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for WhereClause {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)