diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index ae6ef76067..c97ea18a24 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -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") + } } diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index 52e1fbf294..9c261eda9f 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -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, diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 152bc71bd2..a4ca59bba1 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -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 { @@ -325,16 +327,42 @@ impl SourceAnalyzer { db: &impl HirDatabase, ty: Ty, name: Option<&Name>, - callback: impl FnMut(&Ty, Function) -> Option, + mut callback: impl FnMut(&Ty, Function) -> Option, ) -> Option { // 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( + &self, + db: &impl HirDatabase, + ty: Ty, + name: Option<&Name>, + callback: impl FnMut(&Ty, AssocItem) -> Option, + ) -> Option { + // 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, ) } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index d2bfcdc7db..d1a9d74111 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -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) -> Self { + self.vec.extend(filler.take(self.remaining())); + assert_eq!(self.remaining(), 0); self } diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs index 77aa35ce13..59b7f7eb64 100644 --- a/crates/ra_hir/src/ty/infer/path.rs +++ b/crates/ra_hir/src/ty/infer/path.rs @@ -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)> { 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 { diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index eb69344f6a..8c3d32d09d 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -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( ty: &Canonical, db: &impl HirDatabase, resolver: &Resolver, name: Option<&Name>, - mut callback: impl FnMut(&Ty, Function) -> Option, + mode: LookupMode, + mut callback: impl FnMut(&Ty, AssocItem) -> Option, ) -> Option { - // 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( db: &impl HirDatabase, resolver: &Resolver, name: Option<&Name>, - mut callback: impl FnMut(&Ty, Function) -> Option, + mode: LookupMode, + mut callback: impl FnMut(&Ty, AssocItem) -> Option, ) -> Option { 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( // 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( ty: &Canonical, db: &impl HirDatabase, name: Option<&Name>, + mode: LookupMode, krate: Crate, - mut callback: impl FnMut(&Ty, Function) -> Option, + mut callback: impl FnMut(&Ty, AssocItem) -> Option, ) -> Option { 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( 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, db: &impl HirDatabase, diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index c12326643c..bfef48b161 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -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() -> 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() -> Self + [138; 150) 'S::default()': S [160; 162) 's3': S [165; 188) '() -> Self [165; 190) ' { + fn make() -> T; +} +struct S; +impl Trait for S {} +struct G; +impl Trait for G {} +fn test() { + let a = S::make(); + let b = G::::make(); + let c: f64 = G::make(); +} +"#), + @r###" + [127; 211) '{ ...e(); }': () + [137; 138) 'a': u32 + [141; 148) 'S::make': fn make() -> T + [141; 150) 'S::make()': u32 + [160; 161) 'b': u64 + [164; 178) 'G::::make': fn make, u64>() -> T + [164; 180) 'G::, f64>() -> T + [199; 208) 'G::make()': f64 + "### + ); +} + +#[test] +fn infer_trait_assoc_method_generics_2() { + assert_snapshot!( + infer(r#" +trait Trait { + fn make() -> (T, U); +} +struct S; +impl Trait for S {} +struct G; +impl Trait for G {} +fn test() { + let a = S::make::(); + let b: (_, i64) = S::make(); + let c = G::::make::(); + let d: (u32, _) = G::make::(); + let e: (u32, i64) = G::make(); +} +"#), + @r###" + [135; 313) '{ ...e(); }': () + [145; 146) 'a': (u32, i64) + [149; 163) 'S::make::': fn make() -> (T, U) + [149; 165) 'S::mak...i64>()': (u32, i64) + [175; 176) 'b': (u32, i64) + [189; 196) 'S::make': fn make() -> (T, U) + [189; 198) 'S::make()': (u32, i64) + [208; 209) 'c': (u32, i64) + [212; 233) 'G::': fn make, u32, i64>() -> (T, U) + [212; 235) 'G::()': (u32, i64) + [245; 246) 'd': (u32, i64) + [259; 273) 'G::make::': fn make, u32, i64>() -> (T, U) + [259; 275) 'G::mak...i64>()': (u32, i64) + [285; 286) 'e': (u32, i64) + [301; 308) 'G::make': fn make, u32, i64>() -> (T, U) + [301; 310) 'G::make()': (u32, i64) + "### + ); +} + +#[test] +fn infer_trait_assoc_method_generics_3() { + assert_snapshot!( + infer(r#" +trait Trait { + fn make() -> (Self, T); +} +struct S; +impl Trait for S {} +fn test() { + let a = S::make(); +} +"#), + @r###" + [101; 127) '{ ...e(); }': () + [111; 112) 'a': (S, i64) + [115; 122) 'S::make': fn make, i64>() -> (Self, T) + [115; 124) 'S::make()': (S, i64) + "### + ); +} + +#[test] +fn infer_trait_assoc_method_generics_4() { + assert_snapshot!( + infer(r#" +trait Trait { + fn make() -> (Self, T); +} +struct S; +impl Trait for S {} +impl Trait for S {} +fn test() { + let a: (S, _) = S::make(); + let b: (_, i32) = S::make(); +} +"#), + @r###" + [131; 203) '{ ...e(); }': () + [141; 142) 'a': (S, i64) + [158; 165) 'S::make': fn make, i64>() -> (Self, T) + [158; 167) 'S::make()': (S, i64) + [177; 178) 'b': (S, i32) + [191; 198) 'S::make': fn make, i32>() -> (Self, T) + [191; 200) 'S::make()': (S, i32) + "### + ); +} + +#[test] +fn infer_trait_assoc_method_generics_5() { + assert_snapshot!( + infer(r#" +trait Trait { + fn make() -> (Self, T, U); +} +struct S; +impl Trait for S {} +fn test() { + let a = >::make::(); + let b: (S, _, _) = Trait::::make::(); +} +"#), + @r###" + [107; 211) '{ ...>(); }': () + [117; 118) 'a': (S, i64, u8) + [121; 150) '': fn make, i64, u8>() -> (Self, T, U) + [121; 152) '()': (S, i64, u8) + [162; 163) 'b': (S, i64, u8) + [182; 206) 'Trait:...::': fn make, i64, u8>() -> (Self, T, U) + [182; 208) 'Trait:...()': (S, 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( diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index a58fdc036f..9ac9768afb 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs @@ -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 _ = ::<|> } + " + ), + @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!( diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 1f3fa6c57b..c1ce54bea3 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs @@ -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( diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index a12da5be2a..1ec9881b98 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -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( diff --git a/crates/ra_syntax/src/parsing/text_tree_sink.rs b/crates/ra_syntax/src/parsing/text_tree_sink.rs index 1421643163..c36756d6c3 100644 --- a/crates/ra_syntax/src/parsing/text_tree_sink.rs +++ b/crates/ra_syntax/src/parsing/text_tree_sink.rs @@ -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 { diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index cc69463a98..bae4c4650c 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -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 = std::result::Result>; 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> {