mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
1721: Impl/dyn trait r=flodiebold a=flodiebold This adds support for `impl Trait` and `dyn Trait` types as far as possible without Chalk. So we can represent them in the type system, and handle them in method resolution, but Chalk doesn't know about them yet. There's a small temporary hack here where we bypass Chalk during method resolution, so we can handle simple method calls on them and completion works. Fixes #1608. 1723: Make sysroot use `RUST_SRC_PATH` if set r=matklad a=bkchr Co-authored-by: Florian Diebold <flodiebold@gmail.com> Co-authored-by: Bastian Köcher <git@kchr.de>
This commit is contained in:
commit
0c35d82329
11 changed files with 471 additions and 55 deletions
|
@ -11,7 +11,7 @@ use crate::{
|
|||
db::{AstDatabase, DefDatabase, HirDatabase},
|
||||
name::SELF_TYPE,
|
||||
path::Path,
|
||||
type_ref::TypeRef,
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
AdtDef, AsName, Container, Enum, EnumVariant, Function, HasSource, ImplBlock, Name, Struct,
|
||||
Trait, TypeAlias, Union,
|
||||
};
|
||||
|
@ -35,10 +35,12 @@ pub struct GenericParams {
|
|||
|
||||
/// A single predicate from a where clause, i.e. `where Type: Trait`. Combined
|
||||
/// where clauses like `where T: Foo + Bar` are turned into multiple of these.
|
||||
/// It might still result in multiple actual predicates though, because of
|
||||
/// associated type bindings like `Iterator<Item = u32>`.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct WherePredicate {
|
||||
pub(crate) type_ref: TypeRef,
|
||||
pub(crate) trait_ref: Path,
|
||||
pub(crate) bound: TypeBound,
|
||||
}
|
||||
|
||||
// FIXME: consts can have type parameters from their parents (i.e. associated consts of traits)
|
||||
|
@ -143,18 +145,8 @@ impl GenericParams {
|
|||
// FIXME: remove this bound
|
||||
return;
|
||||
}
|
||||
let path = bound
|
||||
.type_ref()
|
||||
.and_then(|tr| match tr {
|
||||
ast::TypeRef::PathType(path) => path.path(),
|
||||
_ => None,
|
||||
})
|
||||
.and_then(Path::from_ast);
|
||||
let path = match path {
|
||||
Some(p) => p,
|
||||
None => return,
|
||||
};
|
||||
self.where_predicates.push(WherePredicate { type_ref, trait_ref: path });
|
||||
let bound = TypeBound::from_ast(bound);
|
||||
self.where_predicates.push(WherePredicate { type_ref, bound });
|
||||
}
|
||||
|
||||
pub(crate) fn find_by_name(&self, name: &Name) -> Option<&GenericParam> {
|
||||
|
|
|
@ -161,14 +161,28 @@ pub enum Ty {
|
|||
name: Name,
|
||||
},
|
||||
|
||||
/// A bound type variable. Only used during trait resolution to represent
|
||||
/// Chalk variables.
|
||||
/// A bound type variable. Used during trait resolution to represent Chalk
|
||||
/// variables, and in `Dyn` and `Opaque` bounds to represent the `Self` type.
|
||||
Bound(u32),
|
||||
|
||||
/// A type variable used during type checking. Not to be confused with a
|
||||
/// type parameter.
|
||||
Infer(InferTy),
|
||||
|
||||
/// A trait object (`dyn Trait` or bare `Trait` in pre-2018 Rust).
|
||||
///
|
||||
/// The predicates are quantified over the `Self` type, i.e. `Ty::Bound(0)`
|
||||
/// represents the `Self` type inside the bounds. This is currently
|
||||
/// implicit; Chalk has the `Binders` struct to make it explicit, but it
|
||||
/// didn't seem worth the overhead yet.
|
||||
Dyn(Arc<[GenericPredicate]>),
|
||||
|
||||
/// An opaque type (`impl Trait`).
|
||||
///
|
||||
/// The predicates are quantified over the `Self` type; see `Ty::Dyn` for
|
||||
/// more.
|
||||
Opaque(Arc<[GenericPredicate]>),
|
||||
|
||||
/// A placeholder for a type which could not be computed; this is propagated
|
||||
/// to avoid useless error messages. Doubles as a placeholder where type
|
||||
/// variables are inserted before type checking, since we want to try to
|
||||
|
@ -194,6 +208,12 @@ impl Substs {
|
|||
Substs(self.0.iter().cloned().take(n).collect::<Vec<_>>().into())
|
||||
}
|
||||
|
||||
pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
for t in self.0.iter() {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
|
||||
// Without an Arc::make_mut_slice, we can't avoid the clone here:
|
||||
let mut v: Vec<_> = self.0.iter().cloned().collect();
|
||||
|
@ -270,6 +290,14 @@ impl TraitRef {
|
|||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
self.substs.walk(f);
|
||||
}
|
||||
|
||||
pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
|
||||
self.substs.walk_mut(f);
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `generics::WherePredicate`, but with resolved types: A condition on the
|
||||
|
@ -299,6 +327,20 @@ impl GenericPredicate {
|
|||
GenericPredicate::Error => self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
match self {
|
||||
GenericPredicate::Implemented(trait_ref) => trait_ref.walk(f),
|
||||
GenericPredicate::Error => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
|
||||
match self {
|
||||
GenericPredicate::Implemented(trait_ref) => trait_ref.walk_mut(f),
|
||||
GenericPredicate::Error => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Basically a claim (currently not validated / checked) that the contained
|
||||
|
@ -386,6 +428,11 @@ impl Ty {
|
|||
t.walk(f);
|
||||
}
|
||||
}
|
||||
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
|
||||
for p in predicates.iter() {
|
||||
p.walk(f);
|
||||
}
|
||||
}
|
||||
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
|
||||
}
|
||||
f(self);
|
||||
|
@ -402,6 +449,13 @@ impl Ty {
|
|||
Ty::UnselectedProjection(p_ty) => {
|
||||
p_ty.parameters.walk_mut(f);
|
||||
}
|
||||
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
|
||||
let mut v: Vec<_> = predicates.iter().cloned().collect();
|
||||
for p in &mut v {
|
||||
p.walk_mut(f);
|
||||
}
|
||||
*predicates = v.into();
|
||||
}
|
||||
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
|
||||
}
|
||||
f(self);
|
||||
|
@ -529,6 +583,19 @@ impl Ty {
|
|||
ty => ty,
|
||||
})
|
||||
}
|
||||
|
||||
/// If this is an `impl Trait` or `dyn Trait`, returns that trait.
|
||||
pub fn inherent_trait(&self) -> Option<Trait> {
|
||||
match self {
|
||||
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
|
||||
predicates.iter().find_map(|pred| match pred {
|
||||
GenericPredicate::Implemented(tr) => Some(tr.trait_),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for &Ty {
|
||||
|
@ -669,6 +736,28 @@ impl HirDisplay for Ty {
|
|||
Ty::UnselectedProjection(p_ty) => p_ty.hir_fmt(f)?,
|
||||
Ty::Param { name, .. } => write!(f, "{}", name)?,
|
||||
Ty::Bound(idx) => write!(f, "?{}", idx)?,
|
||||
Ty::Dyn(predicates) | Ty::Opaque(predicates) => {
|
||||
match self {
|
||||
Ty::Dyn(_) => write!(f, "dyn ")?,
|
||||
Ty::Opaque(_) => write!(f, "impl ")?,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
// looping by hand here just to format the bounds in a slightly nicer way
|
||||
let mut first = true;
|
||||
for p in predicates.iter() {
|
||||
if !first {
|
||||
write!(f, " + ")?;
|
||||
}
|
||||
first = false;
|
||||
match p {
|
||||
// don't show the $0 self type
|
||||
GenericPredicate::Implemented(trait_ref) => {
|
||||
trait_ref.hir_fmt_ext(f, false)?
|
||||
}
|
||||
GenericPredicate::Error => p.hir_fmt(f)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
Ty::Unknown => write!(f, "{{unknown}}")?,
|
||||
Ty::Infer(..) => write!(f, "_")?,
|
||||
}
|
||||
|
@ -676,14 +765,16 @@ impl HirDisplay for Ty {
|
|||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for TraitRef {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}: {}",
|
||||
self.substs[0].display(f.db),
|
||||
self.trait_.name(f.db).unwrap_or_else(Name::missing)
|
||||
)?;
|
||||
impl TraitRef {
|
||||
fn hir_fmt_ext(
|
||||
&self,
|
||||
f: &mut HirFormatter<impl HirDatabase>,
|
||||
with_self_ty: bool,
|
||||
) -> fmt::Result {
|
||||
if with_self_ty {
|
||||
write!(f, "{}: ", self.substs[0].display(f.db),)?;
|
||||
}
|
||||
write!(f, "{}", self.trait_.name(f.db).unwrap_or_else(Name::missing))?;
|
||||
if self.substs.len() > 1 {
|
||||
write!(f, "<")?;
|
||||
f.write_joined(&self.substs[1..], ", ")?;
|
||||
|
@ -693,6 +784,28 @@ impl HirDisplay for TraitRef {
|
|||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for TraitRef {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
self.hir_fmt_ext(f, true)
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for &GenericPredicate {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
HirDisplay::hir_fmt(*self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for GenericPredicate {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
match self {
|
||||
GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
|
||||
GenericPredicate::Error => write!(f, "{{error}}")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for Obligation {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
match self {
|
||||
|
|
|
@ -17,7 +17,7 @@ use crate::{
|
|||
path::{GenericArg, PathSegment},
|
||||
resolve::{Resolution, Resolver},
|
||||
ty::AdtDef,
|
||||
type_ref::TypeRef,
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
BuiltinType, Const, Enum, EnumVariant, Function, HirDatabase, ModuleDef, Path, Static, Struct,
|
||||
StructField, Trait, TypeAlias, Union,
|
||||
};
|
||||
|
@ -58,6 +58,22 @@ impl Ty {
|
|||
let sig = Substs(inner_tys.into());
|
||||
Ty::apply(TypeCtor::FnPtr { num_args: sig.len() as u16 - 1 }, sig)
|
||||
}
|
||||
TypeRef::DynTrait(bounds) => {
|
||||
let self_ty = Ty::Bound(0);
|
||||
let predicates = bounds
|
||||
.iter()
|
||||
.map(|b| GenericPredicate::from_type_bound(db, resolver, b, self_ty.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
Ty::Dyn(predicates.into())
|
||||
}
|
||||
TypeRef::ImplTrait(bounds) => {
|
||||
let self_ty = Ty::Bound(0);
|
||||
let predicates = bounds
|
||||
.iter()
|
||||
.map(|b| GenericPredicate::from_type_bound(db, resolver, b, self_ty.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
Ty::Opaque(predicates.into())
|
||||
}
|
||||
TypeRef::Error => Ty::Unknown,
|
||||
}
|
||||
}
|
||||
|
@ -310,13 +326,46 @@ impl TraitRef {
|
|||
TraitRef { trait_, substs }
|
||||
}
|
||||
|
||||
pub(crate) fn for_where_predicate(
|
||||
pub(crate) fn from_where_predicate(
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
pred: &WherePredicate,
|
||||
) -> Option<TraitRef> {
|
||||
let self_ty = Ty::from_hir(db, resolver, &pred.type_ref);
|
||||
TraitRef::from_path(db, resolver, &pred.trait_ref, Some(self_ty))
|
||||
TraitRef::from_type_bound(db, resolver, &pred.bound, self_ty)
|
||||
}
|
||||
|
||||
pub(crate) fn from_type_bound(
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
bound: &TypeBound,
|
||||
self_ty: Ty,
|
||||
) -> Option<TraitRef> {
|
||||
match bound {
|
||||
TypeBound::Path(path) => TraitRef::from_path(db, resolver, path, Some(self_ty)),
|
||||
TypeBound::Error => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericPredicate {
|
||||
pub(crate) fn from_where_predicate(
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
where_predicate: &WherePredicate,
|
||||
) -> GenericPredicate {
|
||||
TraitRef::from_where_predicate(db, &resolver, where_predicate)
|
||||
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
|
||||
}
|
||||
|
||||
pub(crate) fn from_type_bound(
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
bound: &TypeBound,
|
||||
self_ty: Ty,
|
||||
) -> GenericPredicate {
|
||||
TraitRef::from_type_bound(db, &resolver, bound, self_ty)
|
||||
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,10 +425,7 @@ pub(crate) fn trait_env(
|
|||
) -> Arc<super::TraitEnvironment> {
|
||||
let predicates = resolver
|
||||
.where_predicates_in_scope()
|
||||
.map(|pred| {
|
||||
TraitRef::for_where_predicate(db, &resolver, pred)
|
||||
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
|
||||
})
|
||||
.map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Arc::new(super::TraitEnvironment { predicates })
|
||||
|
@ -393,10 +439,7 @@ pub(crate) fn generic_predicates_query(
|
|||
let resolver = def.resolver(db);
|
||||
let predicates = resolver
|
||||
.where_predicates_in_scope()
|
||||
.map(|pred| {
|
||||
TraitRef::for_where_predicate(db, &resolver, pred)
|
||||
.map_or(GenericPredicate::Error, GenericPredicate::Implemented)
|
||||
})
|
||||
.map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred))
|
||||
.collect::<Vec<_>>();
|
||||
predicates.into()
|
||||
}
|
||||
|
|
|
@ -211,12 +211,19 @@ fn iterate_trait_method_candidates<T>(
|
|||
let krate = resolver.krate()?;
|
||||
// FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
|
||||
let env = lower::trait_env(db, resolver);
|
||||
'traits: for t in resolver.traits_in_scope(db) {
|
||||
// if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope
|
||||
let traits = ty.value.inherent_trait().into_iter().chain(resolver.traits_in_scope(db));
|
||||
'traits: for t in traits {
|
||||
let data = t.trait_data(db);
|
||||
|
||||
// FIXME this is a bit of a hack, since Chalk should say the same thing
|
||||
// anyway, but currently Chalk doesn't implement `dyn/impl Trait` yet
|
||||
let inherently_implemented = ty.value.inherent_trait() == Some(t);
|
||||
|
||||
// we'll be lazy about checking whether the type implements the
|
||||
// trait, but if we find out it doesn't, we'll skip the rest of the
|
||||
// iteration
|
||||
let mut known_implemented = false;
|
||||
let mut known_implemented = inherently_implemented;
|
||||
for item in data.items() {
|
||||
if let TraitItem::Function(m) = *item {
|
||||
let data = m.data(db);
|
||||
|
@ -271,6 +278,11 @@ pub(crate) fn implements_trait(
|
|||
krate: Crate,
|
||||
trait_: Trait,
|
||||
) -> bool {
|
||||
if ty.value.inherent_trait() == Some(trait_) {
|
||||
// FIXME this is a bit of a hack, since Chalk should say the same thing
|
||||
// anyway, but currently Chalk doesn't implement `dyn/impl Trait` yet
|
||||
return true;
|
||||
}
|
||||
let env = lower::trait_env(db, resolver);
|
||||
let goal = generic_implements_goal(db, env.clone(), trait_, ty.clone());
|
||||
let solution = db.trait_solve(krate, goal);
|
||||
|
|
|
@ -3273,6 +3273,181 @@ fn test<T: ApplyL>(t: T) {
|
|||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn impl_trait() {
|
||||
assert_snapshot_matches!(
|
||||
infer(r#"
|
||||
trait Trait<T> {
|
||||
fn foo(&self) -> T;
|
||||
fn foo2(&self) -> i64;
|
||||
}
|
||||
fn bar() -> impl Trait<u64> {}
|
||||
|
||||
fn test(x: impl Trait<u64>, y: &impl Trait<u64>) {
|
||||
x;
|
||||
y;
|
||||
let z = bar();
|
||||
x.foo();
|
||||
y.foo();
|
||||
z.foo();
|
||||
x.foo2();
|
||||
y.foo2();
|
||||
z.foo2();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
⋮
|
||||
⋮[30; 34) 'self': &Self
|
||||
⋮[55; 59) 'self': &Self
|
||||
⋮[99; 101) '{}': ()
|
||||
⋮[111; 112) 'x': impl Trait<u64>
|
||||
⋮[131; 132) 'y': &impl Trait<u64>
|
||||
⋮[152; 269) '{ ...2(); }': ()
|
||||
⋮[158; 159) 'x': impl Trait<u64>
|
||||
⋮[165; 166) 'y': &impl Trait<u64>
|
||||
⋮[176; 177) 'z': impl Trait<u64>
|
||||
⋮[180; 183) 'bar': fn bar() -> impl Trait<u64>
|
||||
⋮[180; 185) 'bar()': impl Trait<u64>
|
||||
⋮[191; 192) 'x': impl Trait<u64>
|
||||
⋮[191; 198) 'x.foo()': {unknown}
|
||||
⋮[204; 205) 'y': &impl Trait<u64>
|
||||
⋮[204; 211) 'y.foo()': {unknown}
|
||||
⋮[217; 218) 'z': impl Trait<u64>
|
||||
⋮[217; 224) 'z.foo()': {unknown}
|
||||
⋮[230; 231) 'x': impl Trait<u64>
|
||||
⋮[230; 238) 'x.foo2()': i64
|
||||
⋮[244; 245) 'y': &impl Trait<u64>
|
||||
⋮[244; 252) 'y.foo2()': i64
|
||||
⋮[258; 259) 'z': impl Trait<u64>
|
||||
⋮[258; 266) 'z.foo2()': i64
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dyn_trait() {
|
||||
assert_snapshot_matches!(
|
||||
infer(r#"
|
||||
trait Trait<T> {
|
||||
fn foo(&self) -> T;
|
||||
fn foo2(&self) -> i64;
|
||||
}
|
||||
fn bar() -> dyn Trait<u64> {}
|
||||
|
||||
fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) {
|
||||
x;
|
||||
y;
|
||||
let z = bar();
|
||||
x.foo();
|
||||
y.foo();
|
||||
z.foo();
|
||||
x.foo2();
|
||||
y.foo2();
|
||||
z.foo2();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
⋮
|
||||
⋮[30; 34) 'self': &Self
|
||||
⋮[55; 59) 'self': &Self
|
||||
⋮[98; 100) '{}': ()
|
||||
⋮[110; 111) 'x': dyn Trait<u64>
|
||||
⋮[129; 130) 'y': &dyn Trait<u64>
|
||||
⋮[149; 266) '{ ...2(); }': ()
|
||||
⋮[155; 156) 'x': dyn Trait<u64>
|
||||
⋮[162; 163) 'y': &dyn Trait<u64>
|
||||
⋮[173; 174) 'z': dyn Trait<u64>
|
||||
⋮[177; 180) 'bar': fn bar() -> dyn Trait<u64>
|
||||
⋮[177; 182) 'bar()': dyn Trait<u64>
|
||||
⋮[188; 189) 'x': dyn Trait<u64>
|
||||
⋮[188; 195) 'x.foo()': {unknown}
|
||||
⋮[201; 202) 'y': &dyn Trait<u64>
|
||||
⋮[201; 208) 'y.foo()': {unknown}
|
||||
⋮[214; 215) 'z': dyn Trait<u64>
|
||||
⋮[214; 221) 'z.foo()': {unknown}
|
||||
⋮[227; 228) 'x': dyn Trait<u64>
|
||||
⋮[227; 235) 'x.foo2()': i64
|
||||
⋮[241; 242) 'y': &dyn Trait<u64>
|
||||
⋮[241; 249) 'y.foo2()': i64
|
||||
⋮[255; 256) 'z': dyn Trait<u64>
|
||||
⋮[255; 263) 'z.foo2()': i64
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dyn_trait_bare() {
|
||||
assert_snapshot_matches!(
|
||||
infer(r#"
|
||||
trait Trait {
|
||||
fn foo(&self) -> u64;
|
||||
}
|
||||
fn bar() -> Trait {}
|
||||
|
||||
fn test(x: Trait, y: &Trait) -> u64 {
|
||||
x;
|
||||
y;
|
||||
let z = bar();
|
||||
x.foo();
|
||||
y.foo();
|
||||
z.foo();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
⋮
|
||||
⋮[27; 31) 'self': &Self
|
||||
⋮[61; 63) '{}': ()
|
||||
⋮[73; 74) 'x': {unknown}
|
||||
⋮[83; 84) 'y': &{unknown}
|
||||
⋮[101; 176) '{ ...o(); }': ()
|
||||
⋮[107; 108) 'x': {unknown}
|
||||
⋮[114; 115) 'y': &{unknown}
|
||||
⋮[125; 126) 'z': {unknown}
|
||||
⋮[129; 132) 'bar': fn bar() -> {unknown}
|
||||
⋮[129; 134) 'bar()': {unknown}
|
||||
⋮[140; 141) 'x': {unknown}
|
||||
⋮[140; 147) 'x.foo()': {unknown}
|
||||
⋮[153; 154) 'y': &{unknown}
|
||||
⋮[153; 160) 'y.foo()': {unknown}
|
||||
⋮[166; 167) 'z': {unknown}
|
||||
⋮[166; 173) 'z.foo()': {unknown}
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn weird_bounds() {
|
||||
assert_snapshot_matches!(
|
||||
infer(r#"
|
||||
trait Trait {}
|
||||
fn test() {
|
||||
let a: impl Trait + 'lifetime = foo;
|
||||
let b: impl 'lifetime = foo;
|
||||
let b: impl (Trait) = foo;
|
||||
let b: impl ('lifetime) = foo;
|
||||
let d: impl ?Sized = foo;
|
||||
let e: impl Trait + ?Sized = foo;
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
⋮
|
||||
⋮[26; 237) '{ ...foo; }': ()
|
||||
⋮[36; 37) 'a': impl Trait + {error}
|
||||
⋮[64; 67) 'foo': impl Trait + {error}
|
||||
⋮[77; 78) 'b': impl {error}
|
||||
⋮[97; 100) 'foo': impl {error}
|
||||
⋮[110; 111) 'b': impl Trait
|
||||
⋮[128; 131) 'foo': impl Trait
|
||||
⋮[141; 142) 'b': impl {error}
|
||||
⋮[163; 166) 'foo': impl {error}
|
||||
⋮[176; 177) 'd': impl {error}
|
||||
⋮[193; 196) 'foo': impl {error}
|
||||
⋮[206; 207) 'e': impl Trait + {error}
|
||||
⋮[231; 234) 'foo': impl Trait + {error}
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
|
||||
let file = db.parse(pos.file_id).ok().unwrap();
|
||||
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
|
||||
|
|
|
@ -80,7 +80,9 @@ impl ToChalk for Ty {
|
|||
// FIXME this is clearly incorrect, but probably not too incorrect
|
||||
// and I'm not sure what to actually do with Ty::Unknown
|
||||
// maybe an alternative would be `for<T> T`? (meaningless in rust, but expressible in chalk's Ty)
|
||||
Ty::Unknown => {
|
||||
//
|
||||
// FIXME also dyn and impl Trait are currently handled like Unknown because Chalk doesn't have them yet
|
||||
Ty::Unknown | Ty::Dyn(_) | Ty::Opaque(_) => {
|
||||
PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::max_value() }.to_ty()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! HIR for references to types. Paths in these are not yet resolved. They can
|
||||
//! be directly created from an ast::TypeRef, without further queries.
|
||||
|
||||
use ra_syntax::ast::{self, TypeAscriptionOwner};
|
||||
use ra_syntax::ast::{self, TypeAscriptionOwner, TypeBoundsOwner};
|
||||
|
||||
use crate::Path;
|
||||
|
||||
|
@ -49,8 +49,16 @@ pub enum TypeRef {
|
|||
/// A fn pointer. Last element of the vector is the return type.
|
||||
Fn(Vec<TypeRef>),
|
||||
// For
|
||||
// ImplTrait,
|
||||
// DynTrait,
|
||||
ImplTrait(Vec<TypeBound>),
|
||||
DynTrait(Vec<TypeBound>),
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum TypeBound {
|
||||
Path(Path),
|
||||
// also for<> bounds
|
||||
// also Lifetimes
|
||||
Error,
|
||||
}
|
||||
|
||||
|
@ -95,8 +103,12 @@ impl TypeRef {
|
|||
}
|
||||
// for types are close enough for our purposes to the inner type for now...
|
||||
ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
|
||||
ast::TypeRef::ImplTraitType(_inner) => TypeRef::Error,
|
||||
ast::TypeRef::DynTraitType(_inner) => TypeRef::Error,
|
||||
ast::TypeRef::ImplTraitType(inner) => {
|
||||
TypeRef::ImplTrait(type_bounds_from_ast(inner.type_bound_list()))
|
||||
}
|
||||
ast::TypeRef::DynTraitType(inner) => {
|
||||
TypeRef::DynTrait(type_bounds_from_ast(inner.type_bound_list()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,3 +124,30 @@ impl TypeRef {
|
|||
TypeRef::Tuple(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn type_bounds_from_ast(type_bounds_opt: Option<ast::TypeBoundList>) -> Vec<TypeBound> {
|
||||
if let Some(type_bounds) = type_bounds_opt {
|
||||
type_bounds.bounds().map(TypeBound::from_ast).collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeBound {
|
||||
pub(crate) fn from_ast(node: ast::TypeBound) -> Self {
|
||||
match node.kind() {
|
||||
ast::TypeBoundKind::PathType(path_type) => {
|
||||
let path = match path_type.path() {
|
||||
Some(p) => p,
|
||||
None => return TypeBound::Error,
|
||||
};
|
||||
let path = match Path::from_ast(path) {
|
||||
Some(p) => p,
|
||||
None => return TypeBound::Error,
|
||||
};
|
||||
TypeBound::Path(path)
|
||||
}
|
||||
ast::TypeBoundKind::ForType(_) | ast::TypeBoundKind::Lifetime(_) => TypeBound::Error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::{
|
||||
env,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
@ -33,21 +34,13 @@ impl Sysroot {
|
|||
}
|
||||
|
||||
pub fn discover(cargo_toml: &Path) -> Result<Sysroot> {
|
||||
let rustc_output = Command::new("rustc")
|
||||
.current_dir(cargo_toml.parent().unwrap())
|
||||
.args(&["--print", "sysroot"])
|
||||
.output()?;
|
||||
if !rustc_output.status.success() {
|
||||
Err("failed to locate sysroot")?
|
||||
}
|
||||
let stdout = String::from_utf8(rustc_output.stdout)?;
|
||||
let sysroot_path = Path::new(stdout.trim());
|
||||
let src = sysroot_path.join("lib/rustlib/src/rust/src");
|
||||
let src = try_find_src_path(cargo_toml)?;
|
||||
|
||||
if !src.exists() {
|
||||
Err(format!(
|
||||
"can't load standard library from sysroot\n\
|
||||
{:?}\n\
|
||||
try running `rustup component add rust-src`",
|
||||
try running `rustup component add rust-src` or set `RUST_SRC_PATH`",
|
||||
src,
|
||||
))?;
|
||||
}
|
||||
|
@ -83,6 +76,23 @@ impl Sysroot {
|
|||
}
|
||||
}
|
||||
|
||||
fn try_find_src_path(cargo_toml: &Path) -> Result<PathBuf> {
|
||||
if let Ok(path) = env::var("RUST_SRC_PATH") {
|
||||
return Ok(path.into());
|
||||
}
|
||||
|
||||
let rustc_output = Command::new("rustc")
|
||||
.current_dir(cargo_toml.parent().unwrap())
|
||||
.args(&["--print", "sysroot"])
|
||||
.output()?;
|
||||
if !rustc_output.status.success() {
|
||||
Err("failed to locate sysroot")?;
|
||||
}
|
||||
let stdout = String::from_utf8(rustc_output.stdout)?;
|
||||
let sysroot_path = Path::new(stdout.trim());
|
||||
Ok(sysroot_path.join("lib/rustlib/src/rust/src"))
|
||||
}
|
||||
|
||||
impl SysrootCrate {
|
||||
pub fn name(self, sysroot: &Sysroot) -> &str {
|
||||
&sysroot.crates[self].name
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
|
||||
pub use self::{
|
||||
expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp},
|
||||
extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind},
|
||||
extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind},
|
||||
generated::*,
|
||||
tokens::*,
|
||||
traits::*,
|
||||
|
|
|
@ -382,7 +382,36 @@ impl ast::WherePred {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum TypeBoundKind {
|
||||
/// Trait
|
||||
PathType(ast::PathType),
|
||||
/// for<'a> ...
|
||||
ForType(ast::ForType),
|
||||
/// 'a
|
||||
Lifetime(ast::SyntaxToken),
|
||||
}
|
||||
|
||||
impl ast::TypeBound {
|
||||
pub fn kind(&self) -> TypeBoundKind {
|
||||
if let Some(path_type) = children(self).next() {
|
||||
TypeBoundKind::PathType(path_type)
|
||||
} else if let Some(for_type) = children(self).next() {
|
||||
TypeBoundKind::ForType(for_type)
|
||||
} else if let Some(lifetime) = self.lifetime() {
|
||||
TypeBoundKind::Lifetime(lifetime)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
fn lifetime(&self) -> Option<SyntaxToken> {
|
||||
self.syntax()
|
||||
.children_with_tokens()
|
||||
.filter_map(|it| it.into_token())
|
||||
.find(|it| it.kind() == LIFETIME)
|
||||
}
|
||||
|
||||
pub fn question_mark_token(&self) -> Option<SyntaxToken> {
|
||||
self.syntax()
|
||||
.children_with_tokens()
|
||||
|
|
|
@ -78,6 +78,7 @@ See https://github.com/microsoft/vscode/issues/72308[microsoft/vscode#72308] for
|
|||
(e.g: `--features="shumway,pdf"` will run as `cargo watch -x "check --features="shumway,pdf""` )
|
||||
* `rust-analyzer.trace.server`: enables internal logging
|
||||
* `rust-analyzer.trace.cargo-watch`: enables cargo-watch logging
|
||||
* `RUST_SRC_PATH`: environment variable that overwrites the sysroot
|
||||
|
||||
|
||||
## Emacs
|
||||
|
|
Loading…
Reference in a new issue