mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-20 19:52:48 +00:00
Merge #2151
2151: Resolve (and complete) trait calls like `Vec::default()` r=flodiebold a=flodiebold Similar to rustc, we do this using the same code as the method call resolution, just without doing autoderef (and considering more potential candidates). (Btw, we currently don't complete methods with `self` in path notation, even though they'd be legal to use, so maybe we should -- on the other hand, that will usually not be the most interesting completions...) Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
9db97820f4
@ -1053,4 +1053,13 @@ impl AssocItem {
|
||||
AssocItem::TypeAlias(t) => t.module(db),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn container(self, db: &impl DefDatabase) -> Container {
|
||||
match self {
|
||||
AssocItem::Function(f) => f.container(db),
|
||||
AssocItem::Const(c) => c.container(db),
|
||||
AssocItem::TypeAlias(t) => t.container(db),
|
||||
}
|
||||
.expect("AssocItem without container")
|
||||
}
|
||||
}
|
||||
|
@ -77,9 +77,10 @@ impl GenericParams {
|
||||
let parent = match def {
|
||||
GenericDef::Function(it) => it.container(db).map(GenericDef::from),
|
||||
GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from),
|
||||
GenericDef::Const(it) => it.container(db).map(GenericDef::from),
|
||||
GenericDef::EnumVariant(it) => Some(it.parent_enum(db).into()),
|
||||
GenericDef::Adt(_) | GenericDef::Trait(_) => None,
|
||||
GenericDef::ImplBlock(_) | GenericDef::Const(_) => None,
|
||||
GenericDef::ImplBlock(_) => None,
|
||||
};
|
||||
let mut generics = GenericParams {
|
||||
def,
|
||||
|
@ -27,9 +27,9 @@ use crate::{
|
||||
},
|
||||
ids::LocationCtx,
|
||||
resolve::{ScopeDef, TypeNs, ValueNs},
|
||||
ty::method_resolution::implements_trait,
|
||||
Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, MacroDef, Module,
|
||||
Name, Path, Resolver, Static, Struct, Ty,
|
||||
ty::method_resolution::{self, implements_trait},
|
||||
AssocItem, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId,
|
||||
MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty,
|
||||
};
|
||||
|
||||
fn try_get_resolver_for_node(
|
||||
@ -327,16 +327,42 @@ impl SourceAnalyzer {
|
||||
db: &impl HirDatabase,
|
||||
ty: Ty,
|
||||
name: Option<&Name>,
|
||||
callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
// There should be no inference vars in types passed here
|
||||
// FIXME check that?
|
||||
// FIXME replace Unknown by bound vars here
|
||||
let canonical = crate::ty::Canonical { value: ty, num_vars: 0 };
|
||||
crate::ty::method_resolution::iterate_method_candidates(
|
||||
method_resolution::iterate_method_candidates(
|
||||
&canonical,
|
||||
db,
|
||||
&self.resolver,
|
||||
name,
|
||||
method_resolution::LookupMode::MethodCall,
|
||||
|ty, it| match it {
|
||||
AssocItem::Function(f) => callback(ty, f),
|
||||
_ => None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn iterate_path_candidates<T>(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
ty: Ty,
|
||||
name: Option<&Name>,
|
||||
callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
// There should be no inference vars in types passed here
|
||||
// FIXME check that?
|
||||
// FIXME replace Unknown by bound vars here
|
||||
let canonical = crate::ty::Canonical { value: ty, num_vars: 0 };
|
||||
method_resolution::iterate_method_candidates(
|
||||
&canonical,
|
||||
db,
|
||||
&self.resolver,
|
||||
name,
|
||||
method_resolution::LookupMode::Path,
|
||||
callback,
|
||||
)
|
||||
}
|
||||
|
@ -385,13 +385,22 @@ impl SubstsBuilder {
|
||||
self.param_count - self.vec.len()
|
||||
}
|
||||
|
||||
pub fn fill_with_bound_vars(mut self, starting_from: u32) -> Self {
|
||||
self.vec.extend((starting_from..starting_from + self.remaining() as u32).map(Ty::Bound));
|
||||
self
|
||||
pub fn fill_with_bound_vars(self, starting_from: u32) -> Self {
|
||||
self.fill((starting_from..).map(Ty::Bound))
|
||||
}
|
||||
|
||||
pub fn fill_with_unknown(mut self) -> Self {
|
||||
self.vec.extend(iter::repeat(Ty::Unknown).take(self.remaining()));
|
||||
pub fn fill_with_params(self) -> Self {
|
||||
let start = self.vec.len() as u32;
|
||||
self.fill((start..).map(|idx| Ty::Param { idx, name: Name::missing() }))
|
||||
}
|
||||
|
||||
pub fn fill_with_unknown(self) -> Self {
|
||||
self.fill(iter::repeat(Ty::Unknown))
|
||||
}
|
||||
|
||||
pub fn fill(mut self, filler: impl Iterator<Item = Ty>) -> Self {
|
||||
self.vec.extend(filler.take(self.remaining()));
|
||||
assert_eq!(self.remaining(), 0);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,8 @@ use super::{ExprOrPatId, InferenceContext, TraitRef};
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
|
||||
ty::{Substs, Ty, TypableDef, TypeWalk},
|
||||
AssocItem, HasGenericParams, Namespace, Path,
|
||||
ty::{method_resolution, Substs, Ty, TypableDef, TypeWalk},
|
||||
AssocItem, Container, HasGenericParams, Name, Namespace, Path,
|
||||
};
|
||||
|
||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
@ -39,7 +39,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
let ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty);
|
||||
self.resolve_ty_assoc_item(
|
||||
ty,
|
||||
path.segments.last().expect("path had at least one segment"),
|
||||
&path.segments.last().expect("path had at least one segment").name,
|
||||
id,
|
||||
)?
|
||||
} else {
|
||||
@ -122,10 +122,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ty = self.insert_type_vars(ty);
|
||||
let ty = self.normalize_associated_types_in(ty);
|
||||
|
||||
let segment =
|
||||
remaining_segments.last().expect("there should be at least one segment here");
|
||||
|
||||
self.resolve_ty_assoc_item(ty, segment, id)
|
||||
self.resolve_ty_assoc_item(ty, &segment.name, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,7 +165,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
};
|
||||
let substs = Substs::build_for_def(self.db, item)
|
||||
.use_parent_substs(&trait_ref.substs)
|
||||
.fill_with_unknown()
|
||||
.fill_with_params()
|
||||
.build();
|
||||
|
||||
self.write_assoc_resolution(id, item);
|
||||
@ -172,44 +175,51 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
fn resolve_ty_assoc_item(
|
||||
&mut self,
|
||||
ty: Ty,
|
||||
segment: &PathSegment,
|
||||
name: &Name,
|
||||
id: ExprOrPatId,
|
||||
) -> Option<(ValueNs, Option<Substs>)> {
|
||||
if let Ty::Unknown = ty {
|
||||
return None;
|
||||
}
|
||||
|
||||
let krate = self.resolver.krate()?;
|
||||
let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone());
|
||||
|
||||
// Find impl
|
||||
// FIXME: consider trait candidates
|
||||
let item = ty.clone().iterate_impl_items(self.db, krate, |item| match item {
|
||||
AssocItem::Function(func) => {
|
||||
if segment.name == func.name(self.db) {
|
||||
Some(AssocItem::Function(func))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
method_resolution::iterate_method_candidates(
|
||||
&canonical_ty.value,
|
||||
self.db,
|
||||
&self.resolver.clone(),
|
||||
Some(name),
|
||||
method_resolution::LookupMode::Path,
|
||||
move |_ty, item| {
|
||||
let def = match item {
|
||||
AssocItem::Function(f) => ValueNs::Function(f),
|
||||
AssocItem::Const(c) => ValueNs::Const(c),
|
||||
AssocItem::TypeAlias(_) => unreachable!(),
|
||||
};
|
||||
let substs = match item.container(self.db) {
|
||||
Container::ImplBlock(_) => self.find_self_types(&def, ty.clone()),
|
||||
Container::Trait(t) => {
|
||||
// we're picking this method
|
||||
let trait_substs = Substs::build_for_def(self.db, t)
|
||||
.push(ty.clone())
|
||||
.fill(std::iter::repeat_with(|| self.new_type_var()))
|
||||
.build();
|
||||
let substs = Substs::build_for_def(self.db, item)
|
||||
.use_parent_substs(&trait_substs)
|
||||
.fill_with_params()
|
||||
.build();
|
||||
self.obligations.push(super::Obligation::Trait(TraitRef {
|
||||
trait_: t,
|
||||
substs: trait_substs,
|
||||
}));
|
||||
Some(substs)
|
||||
}
|
||||
};
|
||||
|
||||
AssocItem::Const(konst) => {
|
||||
if konst.name(self.db).map_or(false, |n| n == segment.name) {
|
||||
Some(AssocItem::Const(konst))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
AssocItem::TypeAlias(_) => None,
|
||||
})?;
|
||||
let def = match item {
|
||||
AssocItem::Function(f) => ValueNs::Function(f),
|
||||
AssocItem::Const(c) => ValueNs::Const(c),
|
||||
AssocItem::TypeAlias(_) => unreachable!(),
|
||||
};
|
||||
let substs = self.find_self_types(&def, ty);
|
||||
|
||||
self.write_assoc_resolution(id, item);
|
||||
Some((def, substs))
|
||||
self.write_assoc_resolution(id, item);
|
||||
Some((def, substs))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> {
|
||||
|
@ -166,37 +166,78 @@ pub(crate) fn lookup_method(
|
||||
name: &Name,
|
||||
resolver: &Resolver,
|
||||
) -> Option<(Ty, Function)> {
|
||||
iterate_method_candidates(ty, db, resolver, Some(name), |ty, f| Some((ty.clone(), f)))
|
||||
iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| match f
|
||||
{
|
||||
AssocItem::Function(f) => Some((ty.clone(), f)),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether we're looking up a dotted method call (like `v.len()`) or a path
|
||||
/// (like `Vec::new`).
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum LookupMode {
|
||||
/// Looking up a method call like `v.len()`: We only consider candidates
|
||||
/// that have a `self` parameter, and do autoderef.
|
||||
MethodCall,
|
||||
/// Looking up a path like `Vec::new` or `Vec::default`: We consider all
|
||||
/// candidates including associated constants, but don't do autoderef.
|
||||
Path,
|
||||
}
|
||||
|
||||
// This would be nicer if it just returned an iterator, but that runs into
|
||||
// lifetime problems, because we need to borrow temp `CrateImplBlocks`.
|
||||
// FIXME add a context type here?
|
||||
pub(crate) fn iterate_method_candidates<T>(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
name: Option<&Name>,
|
||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||
mode: LookupMode,
|
||||
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
// For method calls, rust first does any number of autoderef, and then one
|
||||
// autoref (i.e. when the method takes &self or &mut self). We just ignore
|
||||
// the autoref currently -- when we find a method matching the given name,
|
||||
// we assume it fits.
|
||||
|
||||
// Also note that when we've got a receiver like &S, even if the method we
|
||||
// find in the end takes &self, we still do the autoderef step (just as
|
||||
// rustc does an autoderef and then autoref again).
|
||||
|
||||
let krate = resolver.krate()?;
|
||||
for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) {
|
||||
if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback)
|
||||
{
|
||||
return Some(result);
|
||||
match mode {
|
||||
LookupMode::MethodCall => {
|
||||
// For method calls, rust first does any number of autoderef, and then one
|
||||
// autoref (i.e. when the method takes &self or &mut self). We just ignore
|
||||
// the autoref currently -- when we find a method matching the given name,
|
||||
// we assume it fits.
|
||||
|
||||
// Also note that when we've got a receiver like &S, even if the method we
|
||||
// find in the end takes &self, we still do the autoderef step (just as
|
||||
// rustc does an autoderef and then autoref again).
|
||||
|
||||
for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) {
|
||||
if let Some(result) =
|
||||
iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback)
|
||||
{
|
||||
return Some(result);
|
||||
}
|
||||
if let Some(result) = iterate_trait_method_candidates(
|
||||
&derefed_ty,
|
||||
db,
|
||||
resolver,
|
||||
name,
|
||||
mode,
|
||||
&mut callback,
|
||||
) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(result) =
|
||||
iterate_trait_method_candidates(&derefed_ty, db, resolver, name, &mut callback)
|
||||
{
|
||||
return Some(result);
|
||||
LookupMode::Path => {
|
||||
// No autoderef for path lookups
|
||||
if let Some(result) =
|
||||
iterate_inherent_methods(&ty, db, name, mode, krate, &mut callback)
|
||||
{
|
||||
return Some(result);
|
||||
}
|
||||
if let Some(result) =
|
||||
iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback)
|
||||
{
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
@ -207,7 +248,8 @@ fn iterate_trait_method_candidates<T>(
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
name: Option<&Name>,
|
||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||
mode: LookupMode,
|
||||
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let krate = resolver.krate()?;
|
||||
// FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
|
||||
@ -231,22 +273,20 @@ fn iterate_trait_method_candidates<T>(
|
||||
// trait, but if we find out it doesn't, we'll skip the rest of the
|
||||
// iteration
|
||||
let mut known_implemented = inherently_implemented;
|
||||
for item in data.items() {
|
||||
if let AssocItem::Function(m) = *item {
|
||||
let data = m.data(db);
|
||||
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
|
||||
if !known_implemented {
|
||||
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
|
||||
if db.trait_solve(krate, goal).is_none() {
|
||||
continue 'traits;
|
||||
}
|
||||
}
|
||||
known_implemented = true;
|
||||
if let Some(result) = callback(&ty.value, m) {
|
||||
return Some(result);
|
||||
}
|
||||
for &item in data.items() {
|
||||
if !is_valid_candidate(db, name, mode, item) {
|
||||
continue;
|
||||
}
|
||||
if !known_implemented {
|
||||
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
|
||||
if db.trait_solve(krate, goal).is_none() {
|
||||
continue 'traits;
|
||||
}
|
||||
}
|
||||
known_implemented = true;
|
||||
if let Some(result) = callback(&ty.value, item) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
@ -256,21 +296,20 @@ fn iterate_inherent_methods<T>(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &impl HirDatabase,
|
||||
name: Option<&Name>,
|
||||
mode: LookupMode,
|
||||
krate: Crate,
|
||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
for krate in def_crates(db, krate, &ty.value)? {
|
||||
let impls = db.impls_in_crate(krate);
|
||||
|
||||
for impl_block in impls.lookup_impl_blocks(&ty.value) {
|
||||
for item in impl_block.items(db) {
|
||||
if let AssocItem::Function(f) = item {
|
||||
let data = f.data(db);
|
||||
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
|
||||
if let Some(result) = callback(&ty.value, f) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
if !is_valid_candidate(db, name, mode, item) {
|
||||
continue;
|
||||
}
|
||||
if let Some(result) = callback(&ty.value, item) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -278,6 +317,26 @@ fn iterate_inherent_methods<T>(
|
||||
None
|
||||
}
|
||||
|
||||
fn is_valid_candidate(
|
||||
db: &impl HirDatabase,
|
||||
name: Option<&Name>,
|
||||
mode: LookupMode,
|
||||
item: AssocItem,
|
||||
) -> bool {
|
||||
match item {
|
||||
AssocItem::Function(m) => {
|
||||
let data = m.data(db);
|
||||
name.map_or(true, |name| data.name() == name)
|
||||
&& (data.has_self_param() || mode == LookupMode::Path)
|
||||
}
|
||||
AssocItem::Const(c) => {
|
||||
name.map_or(true, |name| Some(name) == c.name(db).as_ref())
|
||||
&& (mode == LookupMode::Path)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn implements_trait(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &impl HirDatabase,
|
||||
|
@ -1841,8 +1841,8 @@ fn test() {
|
||||
[243; 254) 'Struct::FOO': u32
|
||||
[264; 265) 'y': u32
|
||||
[268; 277) 'Enum::BAR': u32
|
||||
[287; 288) 'z': {unknown}
|
||||
[291; 304) 'TraitTest::ID': {unknown}
|
||||
[287; 288) 'z': u32
|
||||
[291; 304) 'TraitTest::ID': u32
|
||||
"###
|
||||
);
|
||||
}
|
||||
@ -2782,9 +2782,9 @@ fn test() {
|
||||
[97; 99) 's1': S
|
||||
[105; 121) 'Defaul...efault': fn default<S>() -> Self
|
||||
[105; 123) 'Defaul...ault()': S
|
||||
[133; 135) 's2': {unknown}
|
||||
[138; 148) 'S::default': {unknown}
|
||||
[138; 150) 'S::default()': {unknown}
|
||||
[133; 135) 's2': S
|
||||
[138; 148) 'S::default': fn default<S>() -> Self
|
||||
[138; 150) 'S::default()': S
|
||||
[160; 162) 's3': S
|
||||
[165; 188) '<S as ...efault': fn default<S>() -> Self
|
||||
[165; 190) '<S as ...ault()': S
|
||||
@ -2792,6 +2792,153 @@ fn test() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_trait_assoc_method_generics_1() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
trait Trait<T> {
|
||||
fn make() -> T;
|
||||
}
|
||||
struct S;
|
||||
impl Trait<u32> for S {}
|
||||
struct G<T>;
|
||||
impl<T> Trait<T> for G<T> {}
|
||||
fn test() {
|
||||
let a = S::make();
|
||||
let b = G::<u64>::make();
|
||||
let c: f64 = G::make();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[127; 211) '{ ...e(); }': ()
|
||||
[137; 138) 'a': u32
|
||||
[141; 148) 'S::make': fn make<S, u32>() -> T
|
||||
[141; 150) 'S::make()': u32
|
||||
[160; 161) 'b': u64
|
||||
[164; 178) 'G::<u64>::make': fn make<G<u64>, u64>() -> T
|
||||
[164; 180) 'G::<u6...make()': u64
|
||||
[190; 191) 'c': f64
|
||||
[199; 206) 'G::make': fn make<G<f64>, f64>() -> T
|
||||
[199; 208) 'G::make()': f64
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_trait_assoc_method_generics_2() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
trait Trait<T> {
|
||||
fn make<U>() -> (T, U);
|
||||
}
|
||||
struct S;
|
||||
impl Trait<u32> for S {}
|
||||
struct G<T>;
|
||||
impl<T> Trait<T> for G<T> {}
|
||||
fn test() {
|
||||
let a = S::make::<i64>();
|
||||
let b: (_, i64) = S::make();
|
||||
let c = G::<u32>::make::<i64>();
|
||||
let d: (u32, _) = G::make::<i64>();
|
||||
let e: (u32, i64) = G::make();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[135; 313) '{ ...e(); }': ()
|
||||
[145; 146) 'a': (u32, i64)
|
||||
[149; 163) 'S::make::<i64>': fn make<S, u32, i64>() -> (T, U)
|
||||
[149; 165) 'S::mak...i64>()': (u32, i64)
|
||||
[175; 176) 'b': (u32, i64)
|
||||
[189; 196) 'S::make': fn make<S, u32, i64>() -> (T, U)
|
||||
[189; 198) 'S::make()': (u32, i64)
|
||||
[208; 209) 'c': (u32, i64)
|
||||
[212; 233) 'G::<u3...:<i64>': fn make<G<u32>, u32, i64>() -> (T, U)
|
||||
[212; 235) 'G::<u3...i64>()': (u32, i64)
|
||||
[245; 246) 'd': (u32, i64)
|
||||
[259; 273) 'G::make::<i64>': fn make<G<u32>, u32, i64>() -> (T, U)
|
||||
[259; 275) 'G::mak...i64>()': (u32, i64)
|
||||
[285; 286) 'e': (u32, i64)
|
||||
[301; 308) 'G::make': fn make<G<u32>, u32, i64>() -> (T, U)
|
||||
[301; 310) 'G::make()': (u32, i64)
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_trait_assoc_method_generics_3() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
trait Trait<T> {
|
||||
fn make() -> (Self, T);
|
||||
}
|
||||
struct S<T>;
|
||||
impl Trait<i64> for S<i32> {}
|
||||
fn test() {
|
||||
let a = S::make();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[101; 127) '{ ...e(); }': ()
|
||||
[111; 112) 'a': (S<i32>, i64)
|
||||
[115; 122) 'S::make': fn make<S<i32>, i64>() -> (Self, T)
|
||||
[115; 124) 'S::make()': (S<i32>, i64)
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_trait_assoc_method_generics_4() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
trait Trait<T> {
|
||||
fn make() -> (Self, T);
|
||||
}
|
||||
struct S<T>;
|
||||
impl Trait<i64> for S<u64> {}
|
||||
impl Trait<i32> for S<u32> {}
|
||||
fn test() {
|
||||
let a: (S<u64>, _) = S::make();
|
||||
let b: (_, i32) = S::make();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[131; 203) '{ ...e(); }': ()
|
||||
[141; 142) 'a': (S<u64>, i64)
|
||||
[158; 165) 'S::make': fn make<S<u64>, i64>() -> (Self, T)
|
||||
[158; 167) 'S::make()': (S<u64>, i64)
|
||||
[177; 178) 'b': (S<u32>, i32)
|
||||
[191; 198) 'S::make': fn make<S<u32>, i32>() -> (Self, T)
|
||||
[191; 200) 'S::make()': (S<u32>, i32)
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_trait_assoc_method_generics_5() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
trait Trait<T> {
|
||||
fn make<U>() -> (Self, T, U);
|
||||
}
|
||||
struct S<T>;
|
||||
impl Trait<i64> for S<u64> {}
|
||||
fn test() {
|
||||
let a = <S as Trait<i64>>::make::<u8>();
|
||||
let b: (S<u64>, _, _) = Trait::<i64>::make::<u8>();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[107; 211) '{ ...>(); }': ()
|
||||
[117; 118) 'a': (S<u64>, i64, u8)
|
||||
[121; 150) '<S as ...::<u8>': fn make<S<u64>, i64, u8>() -> (Self, T, U)
|
||||
[121; 152) '<S as ...<u8>()': (S<u64>, i64, u8)
|
||||
[162; 163) 'b': (S<u64>, i64, u8)
|
||||
[182; 206) 'Trait:...::<u8>': fn make<S<u64>, i64, u8>() -> (Self, T, U)
|
||||
[182; 208) 'Trait:...<u8>()': (S<u64>, i64, u8)
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_from_bound_1() {
|
||||
assert_snapshot!(
|
||||
@ -3303,6 +3450,22 @@ fn test() { S.foo()<|>; }
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn method_resolution_by_value_before_autoref() {
|
||||
let t = type_at(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Clone { fn clone(&self) -> Self; }
|
||||
struct S;
|
||||
impl Clone for S {}
|
||||
impl Clone for &S {}
|
||||
fn test() { (S.clone(), (&S).clone(), (&&S).clone())<|>; }
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "(S, S, &S)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_trait_before_autoderef() {
|
||||
let t = type_at(
|
||||
|
@ -50,23 +50,46 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
ctx.analyzer.iterate_path_candidates(ctx.db, ty.clone(), None, |_ty, item| {
|
||||
match item {
|
||||
hir::AssocItem::Function(func) => {
|
||||
let data = func.data(ctx.db);
|
||||
if !data.has_self_param() {
|
||||
acc.add_function(ctx, func);
|
||||
}
|
||||
}
|
||||
hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
|
||||
hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
|
||||
}
|
||||
None::<()>
|
||||
});
|
||||
// Iterate assoc types separately
|
||||
// FIXME: complete T::AssocType
|
||||
let krate = ctx.module.map(|m| m.krate());
|
||||
if let Some(krate) = krate {
|
||||
ty.iterate_impl_items(ctx.db, krate, |item| {
|
||||
match item {
|
||||
hir::AssocItem::Function(func) => {
|
||||
let data = func.data(ctx.db);
|
||||
if !data.has_self_param() {
|
||||
acc.add_function(ctx, func);
|
||||
}
|
||||
}
|
||||
hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
|
||||
hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {}
|
||||
hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
|
||||
}
|
||||
None::<()>
|
||||
});
|
||||
}
|
||||
}
|
||||
hir::ModuleDef::Trait(t) => {
|
||||
for item in t.items(ctx.db) {
|
||||
match item {
|
||||
hir::AssocItem::Function(func) => {
|
||||
let data = func.data(ctx.db);
|
||||
if !data.has_self_param() {
|
||||
acc.add_function(ctx, func);
|
||||
}
|
||||
}
|
||||
hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
|
||||
hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
@ -558,6 +581,111 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_trait_associated_method_1() {
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
"
|
||||
//- /lib.rs
|
||||
trait Trait {
|
||||
/// A trait method
|
||||
fn m();
|
||||
}
|
||||
|
||||
fn foo() { let _ = Trait::<|> }
|
||||
"
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "m()",
|
||||
source_range: [73; 73),
|
||||
delete: [73; 73),
|
||||
insert: "m()$0",
|
||||
kind: Function,
|
||||
lookup: "m",
|
||||
detail: "fn m()",
|
||||
documentation: Documentation(
|
||||
"A trait method",
|
||||
),
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_trait_associated_method_2() {
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
"
|
||||
//- /lib.rs
|
||||
trait Trait {
|
||||
/// A trait method
|
||||
fn m();
|
||||
}
|
||||
|
||||
struct S;
|
||||
impl Trait for S {}
|
||||
|
||||
fn foo() { let _ = S::<|> }
|
||||
"
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "m()",
|
||||
source_range: [99; 99),
|
||||
delete: [99; 99),
|
||||
insert: "m()$0",
|
||||
kind: Function,
|
||||
lookup: "m",
|
||||
detail: "fn m()",
|
||||
documentation: Documentation(
|
||||
"A trait method",
|
||||
),
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_trait_associated_method_3() {
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
"
|
||||
//- /lib.rs
|
||||
trait Trait {
|
||||
/// A trait method
|
||||
fn m();
|
||||
}
|
||||
|
||||
struct S;
|
||||
impl Trait for S {}
|
||||
|
||||
fn foo() { let _ = <S as Trait>::<|> }
|
||||
"
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "m()",
|
||||
source_range: [110; 110),
|
||||
delete: [110; 110),
|
||||
insert: "m()$0",
|
||||
kind: Function,
|
||||
lookup: "m",
|
||||
detail: "fn m()",
|
||||
documentation: Documentation(
|
||||
"A trait method",
|
||||
),
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_type_alias() {
|
||||
assert_debug_snapshot!(
|
||||
|
@ -390,6 +390,61 @@ mod tests {
|
||||
"spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_definition_works_for_ufcs_inherent_methods() {
|
||||
check_goto(
|
||||
"
|
||||
//- /lib.rs
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
fn frobnicate() { }
|
||||
}
|
||||
|
||||
fn bar(foo: &Foo) {
|
||||
Foo::frobnicate<|>();
|
||||
}
|
||||
",
|
||||
"frobnicate FN_DEF FileId(1) [27; 47) [30; 40)",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_definition_works_for_ufcs_trait_methods_through_traits() {
|
||||
check_goto(
|
||||
"
|
||||
//- /lib.rs
|
||||
trait Foo {
|
||||
fn frobnicate();
|
||||
}
|
||||
|
||||
fn bar() {
|
||||
Foo::frobnicate<|>();
|
||||
}
|
||||
",
|
||||
"frobnicate FN_DEF FileId(1) [16; 32) [19; 29)",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_definition_works_for_ufcs_trait_methods_through_self() {
|
||||
check_goto(
|
||||
"
|
||||
//- /lib.rs
|
||||
struct Foo;
|
||||
trait Trait {
|
||||
fn frobnicate();
|
||||
}
|
||||
impl Trait for Foo {}
|
||||
|
||||
fn bar() {
|
||||
Foo::frobnicate<|>();
|
||||
}
|
||||
",
|
||||
"frobnicate FN_DEF FileId(1) [30; 46) [33; 43)",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_definition_on_self() {
|
||||
check_goto(
|
||||
|
Loading…
Reference in New Issue
Block a user