Merge branch 'master' into feature/themes

This commit is contained in:
Seivan Heidari 2019-11-02 13:43:57 +01:00
commit 1d8bb4c6c1
12 changed files with 605 additions and 103 deletions

View File

@ -1053,4 +1053,13 @@ pub fn module(self, db: &impl DefDatabase) -> Module {
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")
}
}

View File

@ -77,9 +77,10 @@ pub(crate) fn generic_params_query(
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,

View File

@ -27,9 +27,9 @@
},
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 @@ pub fn resolve_hir_path(
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 @@ pub fn iterate_method_candidates<T>(
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,
)
}

View File

@ -385,13 +385,22 @@ fn remaining(&self) -> usize {
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
}

View File

@ -6,8 +6,8 @@
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 @@ fn resolve_value_path(
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 @@ fn resolve_assoc_item(
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 @@ fn resolve_trait_assoc_item(
};
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 @@ fn resolve_trait_assoc_item(
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> {

View File

@ -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,

View File

@ -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 @@ impl Trait for S { fn foo(self) -> u128 { 0 } }
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(

View File

@ -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 @@ pub mod bar {
);
}
#[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!(

View File

@ -315,6 +315,25 @@ macro_rules! foo {
);
}
#[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 @@ fn bar() -> Foo {
"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(

View File

@ -112,6 +112,20 @@ mod foo {}
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(

View File

@ -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 {

View File

@ -3,6 +3,7 @@
pub mod codegen;
use std::{
env,
error::Error,
fs,
io::{Error as IoError, ErrorKind},
@ -17,7 +18,13 @@
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> {