From a1776b27c7d7c266d751360b80cc573b1520ef65 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 7 Sep 2019 16:24:26 +0200 Subject: [PATCH] Use traits from where clauses for method resolution E.g. if we have `T: some::Trait`, we can call methods from that trait without it needing to be in scope. --- crates/ra_hir/src/code_model.rs | 21 ++++++++++++--------- crates/ra_hir/src/ty/method_resolution.rs | 8 +++++++- crates/ra_hir/src/ty/tests.rs | 21 ++++++++++----------- crates/ra_hir/src/ty/traits.rs | 15 +++++++++++++++ 4 files changed, 44 insertions(+), 21 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 4739246cb1..52ee1834ff 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -847,16 +847,22 @@ impl Trait { .collect() } - /// Returns an iterator over the whole super trait hierarchy (not including - /// the trait itself). (This iterator may be infinite in case of circular - /// super trait dependencies, which are possible in malformed code.) + /// Returns an iterator over the whole super trait hierarchy (including the + /// trait itself). pub fn all_super_traits<'a>( self, db: &'a impl HirDatabase, ) -> impl Iterator + 'a { - self.direct_super_traits(db).into_iter().flat_map(move |t| { + self.all_super_traits_inner(db).unique() + } + + fn all_super_traits_inner<'a>( + self, + db: &'a impl HirDatabase, + ) -> impl Iterator + 'a { + iter::once(self).chain(self.direct_super_traits(db).into_iter().flat_map(move |t| { iter::once(t).chain(Box::new(t.all_super_traits(db)) as Box>) - }) + })) } pub fn associated_type_by_name(self, db: &impl DefDatabase, name: &Name) -> Option { @@ -876,10 +882,7 @@ impl Trait { db: &impl HirDatabase, name: &Name, ) -> Option { - iter::once(self) - .chain(self.all_super_traits(db)) - .unique() - .find_map(|t| t.associated_type_by_name(db, name)) + self.all_super_traits(db).find_map(|t| t.associated_type_by_name(db, name)) } pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc { diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 9873a0440b..cf787bdaad 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -212,7 +212,13 @@ fn iterate_trait_method_candidates( // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) let env = lower::trait_env(db, resolver); // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope - let traits = ty.value.inherent_trait().into_iter().chain(resolver.traits_in_scope(db)); + let inherent_trait = ty.value.inherent_trait().into_iter(); + // if we have `T: Trait` in the param env, the trait doesn't need to be in scope + let traits_from_env = env + .trait_predicates_for_self_ty(&ty.value) + .map(|tr| tr.trait_) + .flat_map(|t| t.all_super_traits(db)); + let traits = inherent_trait.chain(traits_from_env).chain(resolver.traits_in_scope(db)); 'traits: for t in traits { let data = t.trait_data(db); diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index c414e6a955..127c69f8aa 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3660,8 +3660,7 @@ fn test(x: T) { } "#, ); - // FIXME should be u32 - assert_eq!(t, "{unknown}"); + assert_eq!(t, "u32"); } #[test] @@ -3673,8 +3672,8 @@ mod foo { fn foo(&self) -> u32 {} } } -trait Trait1: SuperTrait {} -trait Trait2 where Self: SuperTrait {} +trait Trait1: foo::SuperTrait {} +trait Trait2 where Self: foo::SuperTrait {} fn test(x: T, y: U) { x.foo(); @@ -3684,13 +3683,13 @@ fn test(x: T, y: U) { @r###" [50; 54) 'self': &Self [63; 65) '{}': () - [172; 173) 'x': T - [178; 179) 'y': U - [184; 213) '{ ...o(); }': () - [190; 191) 'x': T - [190; 197) 'x.foo()': {unknown} - [203; 204) 'y': U - [203; 210) 'y.foo()': {unknown} + [182; 183) 'x': T + [188; 189) 'y': U + [194; 223) '{ ...o(); }': () + [200; 201) 'x': T + [200; 207) 'x.foo()': {unknown} + [213; 214) 'y': U + [213; 220) 'y.foo()': {unknown} "### ); } diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index 6e0271a966..c0c132809b 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -96,6 +96,21 @@ pub struct TraitEnvironment { pub predicates: Vec, } +impl TraitEnvironment { + /// Returns trait refs with the given self type which are supposed to hold + /// in this trait env. E.g. if we are in `foo()`, this will + /// find that `T: SomeTrait` if we call it for `T`. + pub(crate) fn trait_predicates_for_self_ty<'a>( + &'a self, + ty: &'a Ty, + ) -> impl Iterator + 'a { + self.predicates.iter().filter_map(move |pred| match pred { + GenericPredicate::Implemented(tr) if tr.self_ty() == ty => Some(tr), + _ => None, + }) + } +} + /// Something (usually a goal), along with an environment. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct InEnvironment {