mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 13:33:31 +00:00
Merge branch 'master' into feature/themes
This commit is contained in:
commit
1d8bb4c6c1
12 changed files with 605 additions and 103 deletions
|
@ -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(
|
||||
|
@ -255,7 +255,9 @@ impl SourceAnalyzer {
|
|||
|
||||
let items =
|
||||
self.resolver.resolve_module_path(db, &path).take_types().map(PathResolution::Def);
|
||||
types.or(values).or(items)
|
||||
types.or(values).or(items).or_else(|| {
|
||||
self.resolver.resolve_path_as_macro(db, &path).map(|def| PathResolution::Macro(def))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> {
|
||||
|
@ -325,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!(
|
||||
|
|
|
@ -315,6 +315,25 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_definition_works_for_macros_in_use_tree() {
|
||||
check_goto(
|
||||
"
|
||||
//- /lib.rs
|
||||
use foo::foo<|>;
|
||||
|
||||
//- /foo/lib.rs
|
||||
#[macro_export]
|
||||
macro_rules! foo {
|
||||
() => {
|
||||
{}
|
||||
};
|
||||
}
|
||||
",
|
||||
"foo MACRO_CALL FileId(2) [0; 66) [29; 32)",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_definition_works_for_methods() {
|
||||
covers!(goto_definition_works_for_methods);
|
||||
|
@ -371,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(
|
||||
|
|
|
@ -112,6 +112,20 @@ fn test_doc_comment_of_items() {
|
|||
assert_eq!("doc", module.doc_comment_text().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_doc_comment_of_statics() {
|
||||
let file = SourceFile::parse(
|
||||
r#"
|
||||
/// Number of levels
|
||||
static LEVELS: i32 = 0;
|
||||
"#,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
let st = file.syntax().descendants().find_map(StaticDef::cast).unwrap();
|
||||
assert_eq!("Number of levels", st.doc_comment_text().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_doc_comment_preserves_indents() {
|
||||
let file = SourceFile::parse(
|
||||
|
|
|
@ -147,7 +147,7 @@ fn n_attached_trivias<'a>(
|
|||
) -> usize {
|
||||
match kind {
|
||||
MACRO_CALL | CONST_DEF | TYPE_ALIAS_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF
|
||||
| TRAIT_DEF | MODULE | RECORD_FIELD_DEF => {
|
||||
| TRAIT_DEF | MODULE | RECORD_FIELD_DEF | STATIC_DEF => {
|
||||
let mut res = 0;
|
||||
for (i, (kind, text)) in trivias.enumerate() {
|
||||
match kind {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
pub mod codegen;
|
||||
|
||||
use std::{
|
||||
env,
|
||||
error::Error,
|
||||
fs,
|
||||
io::{Error as IoError, ErrorKind},
|
||||
|
@ -17,7 +18,13 @@ pub type Result<T> = std::result::Result<T, Box<dyn Error>>;
|
|||
const TOOLCHAIN: &str = "stable";
|
||||
|
||||
pub fn project_root() -> PathBuf {
|
||||
Path::new(&env!("CARGO_MANIFEST_DIR")).ancestors().nth(1).unwrap().to_path_buf()
|
||||
Path::new(
|
||||
&env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
|
||||
)
|
||||
.ancestors()
|
||||
.nth(1)
|
||||
.unwrap()
|
||||
.to_path_buf()
|
||||
}
|
||||
|
||||
pub struct Cmd<'a> {
|
||||
|
|
Loading…
Reference in a new issue