1834bae5b8
This wasn't a right decision in the first place, the feature flag was broken in the last rustfmt release, and syntax highlighting of imports is more important anyway
93 lines
3.3 KiB
Rust
93 lines
3.3 KiB
Rust
//! In certain situations, rust automatically inserts derefs as necessary: for
|
|
//! example, field accesses `foo.bar` still work when `foo` is actually a
|
|
//! reference to a type with the field `bar`. This is an approximation of the
|
|
//! logic in rustc (which lives in librustc_typeck/check/autoderef.rs).
|
|
|
|
use std::iter::successors;
|
|
|
|
use log::{info, warn};
|
|
|
|
use super::{traits::Solution, Canonical, Ty};
|
|
use crate::{HasGenericParams, HirDatabase, Name, Resolver};
|
|
|
|
const AUTODEREF_RECURSION_LIMIT: usize = 10;
|
|
|
|
pub(crate) fn autoderef<'a>(
|
|
db: &'a impl HirDatabase,
|
|
resolver: &'a Resolver,
|
|
ty: Canonical<Ty>,
|
|
) -> impl Iterator<Item = Canonical<Ty>> + 'a {
|
|
successors(Some(ty), move |ty| deref(db, resolver, ty)).take(AUTODEREF_RECURSION_LIMIT)
|
|
}
|
|
|
|
pub(crate) fn deref(
|
|
db: &impl HirDatabase,
|
|
resolver: &Resolver,
|
|
ty: &Canonical<Ty>,
|
|
) -> Option<Canonical<Ty>> {
|
|
if let Some(derefed) = ty.value.builtin_deref() {
|
|
Some(Canonical { value: derefed, num_vars: ty.num_vars })
|
|
} else {
|
|
deref_by_trait(db, resolver, ty)
|
|
}
|
|
}
|
|
|
|
fn deref_by_trait(
|
|
db: &impl HirDatabase,
|
|
resolver: &Resolver,
|
|
ty: &Canonical<Ty>,
|
|
) -> Option<Canonical<Ty>> {
|
|
let krate = resolver.krate()?;
|
|
let deref_trait = match db.lang_item(krate, "deref".into())? {
|
|
crate::lang_item::LangItemTarget::Trait(t) => t,
|
|
_ => return None,
|
|
};
|
|
let target = deref_trait.associated_type_by_name(db, Name::target())?;
|
|
|
|
if target.generic_params(db).count_params_including_parent() != 1 {
|
|
// the Target type + Deref trait should only have one generic parameter,
|
|
// namely Deref's Self type
|
|
return None;
|
|
}
|
|
|
|
// FIXME make the Canonical handling nicer
|
|
|
|
let projection = super::traits::ProjectionPredicate {
|
|
ty: Ty::Bound(0),
|
|
projection_ty: super::ProjectionTy {
|
|
associated_ty: target,
|
|
parameters: vec![ty.value.clone().shift_bound_vars(1)].into(),
|
|
},
|
|
};
|
|
|
|
let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: projection };
|
|
|
|
let solution = db.normalize(krate, canonical)?;
|
|
|
|
match &solution {
|
|
Solution::Unique(vars) => {
|
|
// FIXME: vars may contain solutions for any inference variables
|
|
// that happened to be inside ty. To correctly handle these, we
|
|
// would have to pass the solution up to the inference context, but
|
|
// that requires a larger refactoring (especially if the deref
|
|
// happens during method resolution). So for the moment, we just
|
|
// check that we're not in the situation we're we would actually
|
|
// need to handle the values of the additional variables, i.e.
|
|
// they're just being 'passed through'. In the 'standard' case where
|
|
// we have `impl<T> Deref for Foo<T> { Target = T }`, that should be
|
|
// the case.
|
|
for i in 1..vars.0.num_vars {
|
|
if vars.0.value[i] != Ty::Bound((i - 1) as u32) {
|
|
warn!("complex solution for derefing {:?}: {:?}, ignoring", ty, solution);
|
|
return None;
|
|
}
|
|
}
|
|
Some(Canonical { value: vars.0.value[0].clone(), num_vars: vars.0.num_vars })
|
|
}
|
|
Solution::Ambig(_) => {
|
|
info!("Ambiguous solution for derefing {:?}: {:?}", ty, solution);
|
|
None
|
|
}
|
|
}
|
|
}
|