mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 01:17:27 +00:00
Implement basic support for Associated Methods and Constants
This is done in `infer_path_expr`. When `Resolver::resolve_path` returns `PartiallyResolved`, we use the returned `Resolution` together with the given `segment_index` to check if we can find something matching the segment at segment_index in the impls for that particular type.
This commit is contained in:
parent
c84561bb62
commit
816971ebc9
13 changed files with 430 additions and 50 deletions
|
@ -88,7 +88,7 @@ impl ImplBlock {
|
|||
if let Some(TypeRef::Path(path)) = self.target_trait_ref(db) {
|
||||
let resolver = self.resolver(db);
|
||||
if let Some(Resolution::Def(ModuleDef::Trait(tr))) =
|
||||
resolver.resolve_path(db, &path).take_types()
|
||||
resolver.resolve_path(db, &path).into_per_ns().take_types()
|
||||
{
|
||||
return Some(tr);
|
||||
}
|
||||
|
|
|
@ -119,6 +119,10 @@ impl<T> PerNs<T> {
|
|||
self.types.is_some() && self.values.is_some()
|
||||
}
|
||||
|
||||
pub fn is_values(&self) -> bool {
|
||||
self.values.is_some() && self.types.is_none()
|
||||
}
|
||||
|
||||
pub fn take(self, namespace: Namespace) -> Option<T> {
|
||||
match namespace {
|
||||
Namespace::Types => self.types,
|
||||
|
@ -297,7 +301,14 @@ where
|
|||
);
|
||||
(res, if res.is_none() { ReachedFixedPoint::No } else { ReachedFixedPoint::Yes })
|
||||
} else {
|
||||
self.result.resolve_path_fp(self.db, ResolveMode::Import, original_module, &import.path)
|
||||
let res = self.result.resolve_path_fp(
|
||||
self.db,
|
||||
ResolveMode::Import,
|
||||
original_module,
|
||||
&import.path,
|
||||
);
|
||||
|
||||
(res.module, res.reached_fixedpoint)
|
||||
};
|
||||
|
||||
if reached_fixedpoint != ReachedFixedPoint::Yes {
|
||||
|
@ -435,6 +446,27 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ResolvePathResult {
|
||||
pub(crate) module: PerNs<ModuleDef>,
|
||||
pub(crate) segment_index: Option<usize>,
|
||||
reached_fixedpoint: ReachedFixedPoint,
|
||||
}
|
||||
|
||||
impl ResolvePathResult {
|
||||
fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
|
||||
ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
|
||||
}
|
||||
|
||||
fn with(
|
||||
module: PerNs<ModuleDef>,
|
||||
reached_fixedpoint: ReachedFixedPoint,
|
||||
segment_index: Option<usize>,
|
||||
) -> ResolvePathResult {
|
||||
ResolvePathResult { module, reached_fixedpoint, segment_index }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum ResolveMode {
|
||||
Import,
|
||||
|
@ -468,8 +500,9 @@ impl ItemMap {
|
|||
db: &impl PersistentHirDatabase,
|
||||
original_module: Module,
|
||||
path: &Path,
|
||||
) -> PerNs<ModuleDef> {
|
||||
self.resolve_path_fp(db, ResolveMode::Other, original_module, path).0
|
||||
) -> (PerNs<ModuleDef>, Option<usize>) {
|
||||
let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path);
|
||||
(res.module, res.segment_index)
|
||||
}
|
||||
|
||||
fn resolve_in_prelude(
|
||||
|
@ -534,7 +567,7 @@ impl ItemMap {
|
|||
mode: ResolveMode,
|
||||
original_module: Module,
|
||||
path: &Path,
|
||||
) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
|
||||
) -> ResolvePathResult {
|
||||
let mut segments = path.segments.iter().enumerate();
|
||||
let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
|
||||
PathKind::Crate => PerNs::types(original_module.crate_root(db).into()),
|
||||
|
@ -549,7 +582,7 @@ impl ItemMap {
|
|||
{
|
||||
let segment = match segments.next() {
|
||||
Some((_, segment)) => segment,
|
||||
None => return (PerNs::none(), ReachedFixedPoint::Yes),
|
||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||
};
|
||||
log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
|
||||
self.resolve_name_in_crate_root_or_extern_prelude(
|
||||
|
@ -561,7 +594,7 @@ impl ItemMap {
|
|||
PathKind::Plain => {
|
||||
let segment = match segments.next() {
|
||||
Some((_, segment)) => segment,
|
||||
None => return (PerNs::none(), ReachedFixedPoint::Yes),
|
||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||
};
|
||||
log::debug!("resolving {:?} in module", segment);
|
||||
self.resolve_name_in_module(db, original_module, &segment.name)
|
||||
|
@ -571,20 +604,20 @@ impl ItemMap {
|
|||
PerNs::types(p.into())
|
||||
} else {
|
||||
log::debug!("super path in root module");
|
||||
return (PerNs::none(), ReachedFixedPoint::Yes);
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
||||
}
|
||||
}
|
||||
PathKind::Abs => {
|
||||
// 2018-style absolute path -- only extern prelude
|
||||
let segment = match segments.next() {
|
||||
Some((_, segment)) => segment,
|
||||
None => return (PerNs::none(), ReachedFixedPoint::Yes),
|
||||
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||
};
|
||||
if let Some(def) = self.extern_prelude.get(&segment.name) {
|
||||
log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
|
||||
PerNs::types(*def)
|
||||
} else {
|
||||
return (PerNs::none(), ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -598,7 +631,7 @@ impl ItemMap {
|
|||
// (don't break here because `curr_per_ns` might contain
|
||||
// something in the value namespace, and it would be wrong
|
||||
// to return that)
|
||||
return (PerNs::none(), ReachedFixedPoint::No);
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::No);
|
||||
}
|
||||
};
|
||||
// resolve segment in curr
|
||||
|
@ -612,15 +645,15 @@ impl ItemMap {
|
|||
};
|
||||
log::debug!("resolving {:?} in other crate", path);
|
||||
let item_map = db.item_map(module.krate);
|
||||
let def = item_map.resolve_path(db, *module, &path);
|
||||
return (def, ReachedFixedPoint::Yes);
|
||||
let (def, s) = item_map.resolve_path(db, *module, &path);
|
||||
return ResolvePathResult::with(def, ReachedFixedPoint::Yes, s);
|
||||
}
|
||||
|
||||
match self[module.module_id].items.get(&segment.name) {
|
||||
Some(res) if !res.def.is_none() => res.def,
|
||||
_ => {
|
||||
log::debug!("path segment {:?} not found", segment.name);
|
||||
return (PerNs::none(), ReachedFixedPoint::No);
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::No);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -629,9 +662,22 @@ impl ItemMap {
|
|||
tested_by!(item_map_enum_importing);
|
||||
match e.variant(db, &segment.name) {
|
||||
Some(variant) => PerNs::both(variant.into(), variant.into()),
|
||||
None => PerNs::none(),
|
||||
None => {
|
||||
return ResolvePathResult::with(
|
||||
PerNs::types((*e).into()),
|
||||
ReachedFixedPoint::Yes,
|
||||
Some(i),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
ModuleDef::Struct(s) => {
|
||||
return ResolvePathResult::with(
|
||||
PerNs::types((*s).into()),
|
||||
ReachedFixedPoint::Yes,
|
||||
Some(i),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
// could be an inherent method call in UFCS form
|
||||
// (`Struct::method`), or some other kind of associated
|
||||
|
@ -641,11 +687,11 @@ impl ItemMap {
|
|||
segment.name,
|
||||
curr,
|
||||
);
|
||||
return (PerNs::none(), ReachedFixedPoint::Yes);
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
||||
}
|
||||
};
|
||||
}
|
||||
(curr_per_ns, ReachedFixedPoint::Yes)
|
||||
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,32 @@ pub(crate) struct ExprScope {
|
|||
scope_id: ScopeId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PathResult {
|
||||
/// Path was fully resolved
|
||||
FullyResolved(PerNs<Resolution>),
|
||||
/// Path was partially resolved, first element contains the resolution
|
||||
/// second contains the index in the Path.segments which we were unable to resolve
|
||||
PartiallyResolved(PerNs<Resolution>, usize),
|
||||
}
|
||||
|
||||
impl PathResult {
|
||||
pub fn segment_index(&self) -> Option<usize> {
|
||||
match self {
|
||||
PathResult::FullyResolved(_) => None,
|
||||
PathResult::PartiallyResolved(_, ref i) => Some(*i),
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes `PathResult` and returns the contained `PerNs<Resolution>`
|
||||
pub fn into_per_ns(self) -> PerNs<Resolution> {
|
||||
match self {
|
||||
PathResult::FullyResolved(def) => def,
|
||||
PathResult::PartiallyResolved(def, _) => def,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum Scope {
|
||||
/// All the items and imported names of a module
|
||||
|
@ -67,18 +93,26 @@ impl Resolver {
|
|||
resolution
|
||||
}
|
||||
|
||||
pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> {
|
||||
pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PathResult {
|
||||
use self::PathResult::*;
|
||||
if let Some(name) = path.as_ident() {
|
||||
self.resolve_name(db, name)
|
||||
FullyResolved(self.resolve_name(db, name))
|
||||
} else if path.is_self() {
|
||||
self.resolve_name(db, &Name::self_param())
|
||||
FullyResolved(self.resolve_name(db, &Name::self_param()))
|
||||
} else {
|
||||
let (item_map, module) = match self.module() {
|
||||
Some(m) => m,
|
||||
_ => return PerNs::none(),
|
||||
_ => return FullyResolved(PerNs::none()),
|
||||
};
|
||||
let module_res = item_map.resolve_path(db, module, path);
|
||||
module_res.map(Resolution::Def)
|
||||
let (module_res, segment_index) = item_map.resolve_path(db, module, path);
|
||||
|
||||
let def = module_res.map(Resolution::Def);
|
||||
|
||||
if let Some(index) = segment_index {
|
||||
PartiallyResolved(def, index)
|
||||
} else {
|
||||
FullyResolved(def)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,17 +32,20 @@ use rustc_hash::FxHashMap;
|
|||
|
||||
use test_utils::tested_by;
|
||||
|
||||
use ra_syntax::ast::NameOwner;
|
||||
|
||||
use crate::{
|
||||
Function, Struct, StructField, Enum, EnumVariant, Path, Name,
|
||||
Const,
|
||||
FnSignature, ModuleDef, AdtDef,
|
||||
HirDatabase,
|
||||
type_ref::{TypeRef, Mutability},
|
||||
name::KnownName,
|
||||
name::{KnownName, AsName},
|
||||
expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self},
|
||||
generics::GenericParams,
|
||||
path::GenericArg,
|
||||
adt::VariantDef,
|
||||
resolve::{Resolver, Resolution}, nameres::Namespace
|
||||
resolve::{Resolver, Resolution, PathResult}, nameres::Namespace
|
||||
};
|
||||
|
||||
/// The ID of a type variable.
|
||||
|
@ -370,7 +373,7 @@ impl Ty {
|
|||
}
|
||||
|
||||
// Resolve the path (in type namespace)
|
||||
let resolution = resolver.resolve_path(db, path).take_types();
|
||||
let resolution = resolver.resolve_path(db, path).into_per_ns().take_types();
|
||||
|
||||
let def = match resolution {
|
||||
Some(Resolution::Def(def)) => def,
|
||||
|
@ -678,6 +681,19 @@ fn type_for_fn(db: &impl HirDatabase, def: Function) -> Ty {
|
|||
Ty::FnDef { def: def.into(), sig, name, substs }
|
||||
}
|
||||
|
||||
fn type_for_const(db: &impl HirDatabase, resolver: &Resolver, def: Const) -> Ty {
|
||||
let node = def.source(db).1;
|
||||
|
||||
let tr = node
|
||||
.type_ref()
|
||||
.map(TypeRef::from_ast)
|
||||
.as_ref()
|
||||
.map(|tr| Ty::from_hir(db, resolver, tr))
|
||||
.unwrap_or_else(|| Ty::Unknown);
|
||||
|
||||
tr
|
||||
}
|
||||
|
||||
/// Compute the type of a tuple struct constructor.
|
||||
fn type_for_struct_constructor(db: &impl HirDatabase, def: Struct) -> Ty {
|
||||
let var_data = def.variant_data(db);
|
||||
|
@ -1172,15 +1188,56 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
|
||||
fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path) -> Option<Ty> {
|
||||
let resolved = resolver.resolve_path(self.db, &path).take_values()?;
|
||||
let resolved = resolver.resolve_path(self.db, &path);
|
||||
|
||||
let (resolved, segment_index) = match resolved {
|
||||
PathResult::FullyResolved(def) => (def.take_values()?, None),
|
||||
PathResult::PartiallyResolved(def, index) => (def.take_types()?, Some(index)),
|
||||
};
|
||||
|
||||
match resolved {
|
||||
Resolution::Def(def) => {
|
||||
let typable: Option<TypableDef> = def.into();
|
||||
let typable = typable?;
|
||||
let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable);
|
||||
let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs);
|
||||
let ty = self.insert_type_vars(ty);
|
||||
Some(ty)
|
||||
|
||||
if let Some(segment_index) = segment_index {
|
||||
let ty = self.db.type_for_def(typable, Namespace::Types);
|
||||
// TODO: What to do if segment_index is not the last segment
|
||||
// in the path
|
||||
let segment = &path.segments[segment_index];
|
||||
|
||||
// Attempt to find an impl_item for the type which has a name matching
|
||||
// the current segment
|
||||
let ty = ty.iterate_impl_items(self.db, |item| match item {
|
||||
crate::ImplItem::Method(func) => {
|
||||
let sig = func.signature(self.db);
|
||||
if segment.name == *sig.name() {
|
||||
return Some(type_for_fn(self.db, func));
|
||||
}
|
||||
None
|
||||
}
|
||||
crate::ImplItem::Const(c) => {
|
||||
let node = c.source(self.db).1;
|
||||
|
||||
if let Some(name) = node.name().map(|n| n.as_name()) {
|
||||
if segment.name == name {
|
||||
return Some(type_for_const(self.db, resolver, c));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// TODO: Resolve associated types
|
||||
crate::ImplItem::Type(_) => None,
|
||||
});
|
||||
ty
|
||||
} else {
|
||||
let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable);
|
||||
let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs);
|
||||
let ty = self.insert_type_vars(ty);
|
||||
Some(ty)
|
||||
}
|
||||
}
|
||||
Resolution::LocalBinding(pat) => {
|
||||
let ty = self.type_of_pat.get(pat)?;
|
||||
|
@ -1204,23 +1261,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
None => return (Ty::Unknown, None),
|
||||
};
|
||||
let resolver = &self.resolver;
|
||||
let typable: Option<TypableDef> = match resolver.resolve_path(self.db, &path).take_types() {
|
||||
Some(Resolution::Def(def)) => def.into(),
|
||||
Some(Resolution::LocalBinding(..)) => {
|
||||
// this cannot happen
|
||||
log::error!("path resolved to local binding in type ns");
|
||||
return (Ty::Unknown, None);
|
||||
}
|
||||
Some(Resolution::GenericParam(..)) => {
|
||||
// generic params can't be used in struct literals
|
||||
return (Ty::Unknown, None);
|
||||
}
|
||||
Some(Resolution::SelfType(..)) => {
|
||||
// TODO this is allowed in an impl for a struct, handle this
|
||||
return (Ty::Unknown, None);
|
||||
}
|
||||
None => return (Ty::Unknown, None),
|
||||
};
|
||||
let typable: Option<TypableDef> =
|
||||
match resolver.resolve_path(self.db, &path).into_per_ns().take_types() {
|
||||
Some(Resolution::Def(def)) => def.into(),
|
||||
Some(Resolution::LocalBinding(..)) => {
|
||||
// this cannot happen
|
||||
log::error!("path resolved to local binding in type ns");
|
||||
return (Ty::Unknown, None);
|
||||
}
|
||||
Some(Resolution::GenericParam(..)) => {
|
||||
// generic params can't be used in struct literals
|
||||
return (Ty::Unknown, None);
|
||||
}
|
||||
Some(Resolution::SelfType(..)) => {
|
||||
// TODO this is allowed in an impl for a struct, handle this
|
||||
return (Ty::Unknown, None);
|
||||
}
|
||||
None => return (Ty::Unknown, None),
|
||||
};
|
||||
let def = match typable {
|
||||
None => return (Ty::Unknown, None),
|
||||
Some(it) => it,
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
created: "2019-02-20T11:04:56.553382800Z"
|
||||
creator: insta@0.6.3
|
||||
source: crates/ra_hir/src/ty/tests.rs
|
||||
expression: "&result"
|
||||
---
|
||||
[227; 305) '{ ...:ID; }': ()
|
||||
[237; 238) 'x': u32
|
||||
[241; 252) 'Struct::FOO': u32
|
||||
[262; 263) 'y': u32
|
||||
[266; 275) 'Enum::BAR': u32
|
||||
[285; 286) 'z': u32
|
||||
[289; 302) 'TraitTest::ID': u32
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
created: "2019-02-20T11:04:56.553382800Z"
|
||||
creator: insta@0.6.3
|
||||
source: crates/ra_hir/src/ty/tests.rs
|
||||
expression: "&result"
|
||||
---
|
||||
[48; 68) '{ ... }': A
|
||||
[58; 62) 'A::B': A
|
||||
[89; 109) '{ ... }': A
|
||||
[99; 103) 'A::C': A
|
||||
[122; 179) '{ ... c; }': ()
|
||||
[132; 133) 'a': A
|
||||
[136; 140) 'A::b': fn b() -> A
|
||||
[136; 142) 'A::b()': A
|
||||
[148; 149) 'a': A
|
||||
[159; 160) 'c': A
|
||||
[163; 167) 'A::c': fn c() -> A
|
||||
[163; 169) 'A::c()': A
|
||||
[175; 176) 'c': A
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
created: "2019-02-21T10:25:18.568887300Z"
|
||||
creator: insta@0.6.3
|
||||
source: crates/ra_hir/src/ty/tests.rs
|
||||
expression: "&result"
|
||||
---
|
||||
[64; 67) 'val': T
|
||||
[82; 109) '{ ... }': Gen<T>
|
||||
[92; 103) 'Gen { val }': Gen<T>
|
||||
[98; 101) 'val': T
|
||||
[123; 155) '{ ...32); }': ()
|
||||
[133; 134) 'a': Gen<[unknown]>
|
||||
[137; 146) 'Gen::make': fn make<[unknown]>(T) -> Gen<T>
|
||||
[137; 152) 'Gen::make(0u32)': Gen<[unknown]>
|
||||
[147; 151) '0u32': u32
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
created: "2019-02-20T11:04:56.553382800Z"
|
||||
creator: insta@0.6.3
|
||||
source: crates/ra_hir/src/ty/tests.rs
|
||||
expression: "&result"
|
||||
---
|
||||
[50; 76) '{ ... }': A
|
||||
[60; 70) 'A { x: 0 }': A
|
||||
[67; 68) '0': u32
|
||||
[89; 123) '{ ...a.x; }': ()
|
||||
[99; 100) 'a': A
|
||||
[103; 109) 'A::new': fn new() -> A
|
||||
[103; 111) 'A::new()': A
|
||||
[117; 118) 'a': A
|
||||
[117; 120) 'a.x': u32
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
created: "2019-02-21T08:55:53.926725400Z"
|
||||
creator: insta@0.6.3
|
||||
source: crates/ra_hir/src/ty/tests.rs
|
||||
expression: "&result"
|
||||
---
|
||||
[56; 64) '{ A {} }': A
|
||||
[58; 62) 'A {}': A
|
||||
[126; 132) '{ 99 }': u32
|
||||
[128; 130) '99': u32
|
||||
[202; 210) '{ C {} }': C
|
||||
[204; 208) 'C {}': C
|
||||
[241; 325) '{ ...g(); }': ()
|
||||
[251; 252) 'x': A
|
||||
[255; 266) 'a::A::thing': fn thing() -> A
|
||||
[255; 268) 'a::A::thing()': A
|
||||
[278; 279) 'y': u32
|
||||
[282; 293) 'b::B::thing': fn thing() -> u32
|
||||
[282; 295) 'b::B::thing()': u32
|
||||
[305; 306) 'z': C
|
||||
[309; 320) 'c::C::thing': fn thing() -> C
|
||||
[309; 322) 'c::C::thing()': C
|
||||
|
|
@ -586,6 +586,139 @@ fn test() -> i128 {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_associated_const() {
|
||||
check_inference(
|
||||
"infer_associated_const",
|
||||
r#"
|
||||
struct Struct;
|
||||
|
||||
impl Struct {
|
||||
const FOO: u32 = 1;
|
||||
}
|
||||
|
||||
enum Enum;
|
||||
|
||||
impl Enum {
|
||||
const BAR: u32 = 2;
|
||||
}
|
||||
|
||||
trait Trait {
|
||||
const ID: u32;
|
||||
}
|
||||
|
||||
struct TraitTest;
|
||||
|
||||
impl Trait for TraitTest {
|
||||
const ID: u32 = 5;
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let x = Struct::FOO;
|
||||
let y = Enum::BAR;
|
||||
let z = TraitTest::ID;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_associated_method_struct() {
|
||||
check_inference(
|
||||
"infer_associated_method_struct",
|
||||
r#"
|
||||
struct A { x: u32 };
|
||||
|
||||
impl A {
|
||||
fn new() -> A {
|
||||
A { x: 0 }
|
||||
}
|
||||
}
|
||||
fn test() {
|
||||
let a = A::new();
|
||||
a.x;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_associated_method_enum() {
|
||||
check_inference(
|
||||
"infer_associated_method_enum",
|
||||
r#"
|
||||
enum A { B, C };
|
||||
|
||||
impl A {
|
||||
pub fn b() -> A {
|
||||
A::B
|
||||
}
|
||||
pub fn c() -> A {
|
||||
A::C
|
||||
}
|
||||
}
|
||||
fn test() {
|
||||
let a = A::b();
|
||||
a;
|
||||
let c = A::c();
|
||||
c;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_associated_method_with_modules() {
|
||||
check_inference(
|
||||
"infer_associated_method_with_modules",
|
||||
r#"
|
||||
mod a {
|
||||
struct A;
|
||||
impl A { pub fn thing() -> A { A {} }}
|
||||
}
|
||||
|
||||
mod b {
|
||||
struct B;
|
||||
impl B { pub fn thing() -> u32 { 99 }}
|
||||
|
||||
mod c {
|
||||
struct C;
|
||||
impl C { pub fn thing() -> C { C {} }}
|
||||
}
|
||||
}
|
||||
use b::c;
|
||||
|
||||
fn test() {
|
||||
let x = a::A::thing();
|
||||
let y = b::B::thing();
|
||||
let z = c::C::thing();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_associated_method_generics() {
|
||||
check_inference(
|
||||
"infer_associated_method_generics",
|
||||
r#"
|
||||
struct Gen<T> {
|
||||
val: T
|
||||
}
|
||||
|
||||
impl<T> Gen<T> {
|
||||
pub fn make(val: T) -> Gen<T> {
|
||||
Gen { val }
|
||||
}
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let a = Gen::make(0u32);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_panic_on_field_of_enum() {
|
||||
check_inference(
|
||||
|
|
|
@ -10,7 +10,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
|||
Some(path) => path.clone(),
|
||||
_ => return,
|
||||
};
|
||||
let def = match ctx.resolver.resolve_path(ctx.db, &path).take_types() {
|
||||
let def = match ctx.resolver.resolve_path(ctx.db, &path).into_per_ns().take_types() {
|
||||
Some(Resolution::Def(def)) => def,
|
||||
_ => return,
|
||||
};
|
||||
|
|
|
@ -79,7 +79,7 @@ pub(crate) fn reference_definition(
|
|||
if let Some(path) =
|
||||
name_ref.syntax().ancestors().find_map(ast::Path::cast).and_then(hir::Path::from_ast)
|
||||
{
|
||||
let resolved = resolver.resolve_path(db, &path);
|
||||
let resolved = resolver.resolve_path(db, &path).into_per_ns();
|
||||
match resolved.clone().take_types().or_else(|| resolved.take_values()) {
|
||||
Some(Resolution::Def(def)) => return Exact(NavigationTarget::from_def(db, def)),
|
||||
Some(Resolution::LocalBinding(pat)) => {
|
||||
|
|
|
@ -223,4 +223,24 @@ mod tests {
|
|||
assert_eq!("usize", &type_name);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hover_infer_associated_method_result() {
|
||||
let (analysis, position) = single_file_with_position(
|
||||
"
|
||||
struct Thing { x: u32 };
|
||||
|
||||
impl Thing {
|
||||
fn new() -> Thing {
|
||||
Thing { x: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let foo_<|>test = Thing::new();
|
||||
}
|
||||
",
|
||||
);
|
||||
let hover = analysis.hover(position).unwrap().unwrap();
|
||||
assert_eq!(hover.info, "Thing");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue