From 4ae4d9c311084e3092eb4c2d35e98f6c2c70315b Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 4 Sep 2019 22:32:02 +0200 Subject: Add some more tests --- crates/ra_hir/src/ty/tests.rs | 205 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index d92d4659b..cb5540cb6 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3643,6 +3643,211 @@ fn test>(x: T) { "### ); } + +#[test] +fn where_clause_trait_in_scope_for_method_resolution() { + let t = type_at( + r#" +//- /main.rs +mod foo { + trait Trait { + fn foo(&self) -> u32 {} + } +} + +fn test(x: T) { + x.foo()<|>; +} +"#, + ); + // FIXME should be u32 + assert_eq!(t, "{unknown}"); +} + +#[test] +fn super_trait_method_resolution() { + assert_snapshot!( + infer(r#" +mod foo { + trait SuperTrait { + fn foo(&self) -> u32 {} + } +} +trait Trait1: SuperTrait {} +trait Trait2 where Self: SuperTrait {} + +fn test(x: T, y: U) { + x.foo(); + y.foo(); +} +"#), + @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} + "### + ); +} + +#[test] +fn super_trait_assoc_type_bounds() { + assert_snapshot!( + infer(r#" +trait SuperTrait { type Type; } +trait Trait where Self: SuperTrait {} + +fn get2>(t: T) -> U {} +fn set>(t: T) -> T {t} + +struct S; +impl SuperTrait for S { type Type = T; } +impl Trait for S {} + +fn test() { + get2(set(S)); +} +"#), + @r###" + [103; 104) 't': T + [114; 116) '{}': () + [146; 147) 't': T + [157; 160) '{t}': T + [158; 159) 't': T + [259; 280) '{ ...S)); }': () + [265; 269) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U + [265; 277) 'get2(set(S))': {unknown} + [270; 273) 'set': fn set>(T) -> T + [270; 276) 'set(S)': S<{unknown}> + [274; 275) 'S': S<{unknown}> + "### + ); +} + +#[test] +fn fn_trait() { + assert_snapshot!( + infer(r#" +trait FnOnce { + type Output; + + fn call_once(self, args: Args) -> Self::Output; +} + +fn test u128>(f: F) { + f.call_once((1, 2)); +} +"#), + @r###" + [57; 61) 'self': Self + [63; 67) 'args': Args + [132; 133) 'f': F + [138; 166) '{ ...2)); }': () + [144; 145) 'f': F + [144; 163) 'f.call...1, 2))': {unknown} + [156; 162) '(1, 2)': (i32, i32) + [157; 158) '1': i32 + [160; 161) '2': i32 + "### + ); +} + +#[test] +fn closure_1() { + assert_snapshot!( + infer(r#" +trait FnOnce { + type Output; +} + +enum Option { Some(T), None } +impl Option { + fn map U>(self, f: F) -> U {} +} + +fn test() { + let x = Option::Some(1i32); + x.map(|v| v + 1); + x.map(|_v| 1u64); + let y: Option = x.map(|_v| 1); +} +"#), + @r###" + [128; 132) 'self': Option + [134; 135) 'f': F + [145; 147) '{}': () + [161; 280) '{ ... 1); }': () + [171; 172) 'x': Option + [175; 187) 'Option::Some': Some(T) -> Option + [175; 193) 'Option...(1i32)': Option + [188; 192) '1i32': i32 + [199; 200) 'x': Option + [199; 215) 'x.map(...v + 1)': {unknown} + [205; 214) '|v| v + 1': {unknown} + [206; 207) 'v': {unknown} + [209; 210) 'v': {unknown} + [209; 214) 'v + 1': i32 + [213; 214) '1': i32 + [221; 222) 'x': Option + [221; 237) 'x.map(... 1u64)': {unknown} + [227; 236) '|_v| 1u64': {unknown} + [228; 230) '_v': {unknown} + [232; 236) '1u64': u64 + [247; 248) 'y': Option + [264; 265) 'x': Option + [264; 277) 'x.map(|_v| 1)': Option + [270; 276) '|_v| 1': {unknown} + [271; 273) '_v': {unknown} + [275; 276) '1': i32 + "### + ); +} + +#[test] +fn closure_2() { + assert_snapshot!( + infer(r#" +trait FnOnce { + type Output; +} + +fn test u64>(f: F) { + f(1); + let g = |v| v + 1; + g(1u64); + let h = |v| 1u128 + v; +} +"#), + @r###" + [73; 74) 'f': F + [79; 155) '{ ...+ v; }': () + [85; 86) 'f': F + [85; 89) 'f(1)': {unknown} + [87; 88) '1': i32 + [99; 100) 'g': {unknown} + [103; 112) '|v| v + 1': {unknown} + [104; 105) 'v': {unknown} + [107; 108) 'v': {unknown} + [107; 112) 'v + 1': i32 + [111; 112) '1': i32 + [118; 119) 'g': {unknown} + [118; 125) 'g(1u64)': {unknown} + [120; 124) '1u64': u64 + [135; 136) 'h': {unknown} + [139; 152) '|v| 1u128 + v': {unknown} + [140; 141) 'v': u128 + [143; 148) '1u128': u128 + [143; 152) '1u128 + v': u128 + [151; 152) 'v': u128 + "### + ); +} + fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { let file = db.parse(pos.file_id).ok().unwrap(); let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); -- cgit v1.2.3 From 60bdb66ef23f78d8c73afa1897a4542e7e722ed2 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 7 Sep 2019 14:31:43 +0200 Subject: Lower bounds on trait definition, and resolve assoc types from super traits --- crates/ra_hir/src/code_model.rs | 54 ++++++++++++++++++++++++++++++++++-- crates/ra_hir/src/generics.rs | 22 +++++++++------ crates/ra_hir/src/name.rs | 6 +--- crates/ra_hir/src/ty/autoderef.rs | 4 +-- crates/ra_hir/src/ty/infer.rs | 6 ++-- crates/ra_hir/src/ty/lower.rs | 27 ++++++++++-------- crates/ra_hir/src/ty/tests.rs | 10 +++---- crates/ra_hir/src/ty/traits/chalk.rs | 2 +- crates/ra_hir/src/type_ref.rs | 7 +++++ 9 files changed, 100 insertions(+), 38 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index f7efc1b66..4739246cb 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -1,8 +1,11 @@ pub(crate) mod src; pub(crate) mod docs; +use std::iter; use std::sync::Arc; +use itertools::Itertools; + use ra_db::{CrateId, Edition, FileId, SourceRootId}; use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; @@ -820,7 +823,43 @@ impl Trait { self.trait_data(db).items().to_vec() } - pub fn associated_type_by_name(self, db: &impl DefDatabase, name: Name) -> Option { + fn direct_super_traits(self, db: &impl HirDatabase) -> Vec { + let resolver = self.resolver(db); + // returning the iterator directly doesn't easily work because of + // lifetime problems, but since there usually shouldn't be more than a + // few direct traits this should be fine (we could even use some kind of + // SmallVec if performance is a concern) + self.generic_params(db) + .where_predicates + .iter() + .filter_map(|pred| match &pred.type_ref { + TypeRef::Path(p) if p.as_ident() == Some(&crate::name::SELF_TYPE) => { + pred.bound.as_path() + } + _ => None, + }) + .filter_map(|path| { + match resolver.resolve_path_without_assoc_items(db, path).take_types() { + Some(crate::Resolution::Def(ModuleDef::Trait(t))) => Some(t), + _ => None, + } + }) + .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.) + 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| { + 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 { let trait_data = self.trait_data(db); trait_data .items() @@ -829,7 +868,18 @@ impl Trait { TraitItem::TypeAlias(t) => Some(*t), _ => None, }) - .find(|t| t.name(db) == name) + .find(|t| &t.name(db) == name) + } + + pub fn associated_type_by_name_including_super_traits( + self, + 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)) } pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc { diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index d6728cc9f..c76df0698 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -87,11 +87,15 @@ impl GenericParams { // traits get the Self type as an implicit first type parameter generics.params.push(GenericParam { idx: start, name: SELF_TYPE, default: None }); generics.fill(&it.source(db).ast, start + 1); + // add super traits as bounds on Self + // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar + let self_param = TypeRef::Path(SELF_TYPE.into()); + generics.fill_bounds(&it.source(db).ast, self_param); } GenericDef::TypeAlias(it) => generics.fill(&it.source(db).ast, start), // Note that we don't add `Self` here: in `impl`s, `Self` is not a // type-parameter, but rather is a type-alias for impl's target - // type, so this is handled by the resovler. + // type, so this is handled by the resolver. GenericDef::ImplBlock(it) => generics.fill(&it.source(db).ast, start), GenericDef::EnumVariant(_) => {} } @@ -108,6 +112,14 @@ impl GenericParams { } } + fn fill_bounds(&mut self, node: &impl ast::TypeBoundsOwner, type_ref: TypeRef) { + for bound in + node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds()) + { + self.add_where_predicate_from_bound(bound, type_ref.clone()); + } + } + fn fill_params(&mut self, params: ast::TypeParamList, start: u32) { for (idx, type_param) in params.type_params().enumerate() { let name = type_param.name().map_or_else(Name::missing, |it| it.as_name()); @@ -117,13 +129,7 @@ impl GenericParams { self.params.push(param); let type_ref = TypeRef::Path(name.into()); - for bound in type_param - .type_bound_list() - .iter() - .flat_map(|type_bound_list| type_bound_list.bounds()) - { - self.add_where_predicate_from_bound(bound, type_ref.clone()); - } + self.fill_bounds(&type_param, type_ref); } } diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index 9c4822d91..13bc901bc 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs @@ -38,11 +38,6 @@ impl Name { Name::new(idx.to_string().into()) } - // Needed for Deref - pub(crate) fn target() -> Name { - Name::new("Target".into()) - } - // There's should be no way to extract a string out of `Name`: `Name` in the // future, `Name` will include hygiene information, and you can't encode // hygiene into a String. @@ -123,6 +118,7 @@ pub(crate) const FUTURE_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, pub(crate) const RESULT_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"result")); pub(crate) const RESULT_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Result")); pub(crate) const OUTPUT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output")); +pub(crate) const TARGET: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Target")); fn resolve_name(text: &SmolStr) -> SmolStr { let raw_start = "r#"; diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs index 08f52a53b..caa17f64e 100644 --- a/crates/ra_hir/src/ty/autoderef.rs +++ b/crates/ra_hir/src/ty/autoderef.rs @@ -8,7 +8,7 @@ use std::iter::successors; use log::{info, warn}; use super::{traits::Solution, Canonical, Ty, TypeWalk}; -use crate::{HasGenericParams, HirDatabase, Name, Resolver}; +use crate::{name, HasGenericParams, HirDatabase, Resolver}; const AUTODEREF_RECURSION_LIMIT: usize = 10; @@ -42,7 +42,7 @@ fn deref_by_trait( crate::lang_item::LangItemTarget::Trait(t) => t, _ => return None, }; - let target = deref_trait.associated_type_by_name(db, Name::target())?; + let target = deref_trait.associated_type_by_name(db, &name::TARGET)?; if target.generic_params(db).count_params_including_parent() != 1 { // the Target type + Deref trait should only have one generic parameter, diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index ec3b7ffef..0e6ebd365 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -1453,7 +1453,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { match self.resolver.resolve_path_segments(self.db, &into_iter_path).into_fully_resolved() { PerNs { types: Some(Def(Trait(trait_))), .. } => { - Some(trait_.associated_type_by_name(self.db, name::ITEM)?) + Some(trait_.associated_type_by_name(self.db, &name::ITEM)?) } _ => None, } @@ -1471,7 +1471,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { match self.resolver.resolve_path_segments(self.db, &ops_try_path).into_fully_resolved() { PerNs { types: Some(Def(Trait(trait_))), .. } => { - Some(trait_.associated_type_by_name(self.db, name::OK)?) + Some(trait_.associated_type_by_name(self.db, &name::OK)?) } _ => None, } @@ -1493,7 +1493,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .into_fully_resolved() { PerNs { types: Some(Def(Trait(trait_))), .. } => { - Some(trait_.associated_type_by_name(self.db, name::OUTPUT)?) + Some(trait_.associated_type_by_name(self.db, &name::OUTPUT)?) } _ => None, } diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index f6f0137cf..480bae740 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -132,14 +132,16 @@ impl Ty { if let Some(remaining_index) = remaining_index { if remaining_index == path.segments.len() - 1 { let segment = &path.segments[remaining_index]; - let associated_ty = - match trait_ref.trait_.associated_type_by_name(db, segment.name.clone()) { - Some(t) => t, - None => { - // associated type not found - return Ty::Unknown; - } - }; + let associated_ty = match trait_ref + .trait_ + .associated_type_by_name_including_super_traits(db, &segment.name) + { + Some(t) => t, + None => { + // associated type not found + return Ty::Unknown; + } + }; // FIXME handle type parameters on the segment Ty::Projection(ProjectionTy { associated_ty, parameters: trait_ref.substs }) } else { @@ -387,10 +389,11 @@ fn assoc_type_bindings_from_type_bound<'a>( .flat_map(|segment| segment.args_and_bindings.iter()) .flat_map(|args_and_bindings| args_and_bindings.bindings.iter()) .map(move |(name, type_ref)| { - let associated_ty = match trait_ref.trait_.associated_type_by_name(db, name.clone()) { - None => return GenericPredicate::Error, - Some(t) => t, - }; + let associated_ty = + match trait_ref.trait_.associated_type_by_name_including_super_traits(db, &name) { + None => return GenericPredicate::Error, + Some(t) => t, + }; let projection_ty = ProjectionTy { associated_ty, parameters: trait_ref.substs.clone() }; let ty = Ty::from_hir(db, resolver, type_ref); diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index cb5540cb6..17c4e3556 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3720,11 +3720,11 @@ fn test() { [157; 160) '{t}': T [158; 159) 't': T [259; 280) '{ ...S)); }': () - [265; 269) 'get2': fn get2<{unknown}, S<{unknown}>>(T) -> U - [265; 277) 'get2(set(S))': {unknown} - [270; 273) 'set': fn set>(T) -> T - [270; 276) 'set(S)': S<{unknown}> - [274; 275) 'S': S<{unknown}> + [265; 269) 'get2': fn get2>(T) -> U + [265; 277) 'get2(set(S))': u64 + [270; 273) 'set': fn set>(T) -> T + [270; 276) 'set(S)': S + [274; 275) 'S': S "### ); } diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index c201c5e50..cfe0cab16 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -636,7 +636,7 @@ pub(crate) fn impl_datum_query( _ => None, }) .filter_map(|t| { - let assoc_ty = trait_.associated_type_by_name(db, t.name(db))?; + let assoc_ty = trait_.associated_type_by_name(db, &t.name(db))?; let ty = db.type_for_def(t.into(), crate::Namespace::Types).subst(&bound_vars); Some(chalk_rust_ir::AssociatedTyValue { impl_id, diff --git a/crates/ra_hir/src/type_ref.rs b/crates/ra_hir/src/type_ref.rs index fa91bfb22..bc8acc7ee 100644 --- a/crates/ra_hir/src/type_ref.rs +++ b/crates/ra_hir/src/type_ref.rs @@ -150,4 +150,11 @@ impl TypeBound { ast::TypeBoundKind::ForType(_) | ast::TypeBoundKind::Lifetime(_) => TypeBound::Error, } } + + pub fn as_path(&self) -> Option<&Path> { + match self { + TypeBound::Path(p) => Some(p), + _ => None, + } + } } -- cgit v1.2.3 From d21cdf3c998bb24e48e81a7e6909df2146ce097c Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 7 Sep 2019 15:13:05 +0200 Subject: Lower `Fn(X, Y) -> Z` paths --- crates/ra_hir/src/path.rs | 44 +++++++++++++++++++++++++++++++++++++++---- crates/ra_hir/src/ty/tests.rs | 16 ++++++++-------- 2 files changed, 48 insertions(+), 12 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 24316fc91..d6c78593b 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -1,11 +1,11 @@ use std::sync::Arc; use ra_syntax::{ - ast::{self, NameOwner}, + ast::{self, NameOwner, TypeAscriptionOwner}, AstNode, }; -use crate::{type_ref::TypeRef, AsName, Name}; +use crate::{name, type_ref::TypeRef, AsName, Name}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Path { @@ -76,8 +76,16 @@ impl Path { match segment.kind()? { ast::PathSegmentKind::Name(name) => { - let args = - segment.type_arg_list().and_then(GenericArgs::from_ast).map(Arc::new); + let args = segment + .type_arg_list() + .and_then(GenericArgs::from_ast) + .or_else(|| { + GenericArgs::from_fn_like_path_ast( + segment.param_list(), + segment.ret_type(), + ) + }) + .map(Arc::new); let segment = PathSegment { name: name.as_name(), args_and_bindings: args }; segments.push(segment); } @@ -187,6 +195,34 @@ impl GenericArgs { } } + /// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y) + /// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`). + pub(crate) fn from_fn_like_path_ast( + params: Option, + ret_type: Option, + ) -> Option { + let mut args = Vec::new(); + let mut bindings = Vec::new(); + if let Some(params) = params { + let mut param_types = Vec::new(); + for param in params.params() { + let type_ref = TypeRef::from_ast_opt(param.ascribed_type()); + param_types.push(type_ref); + } + let arg = GenericArg::Type(TypeRef::Tuple(param_types)); + args.push(arg); + } + if let Some(ret_type) = ret_type { + let type_ref = TypeRef::from_ast_opt(ret_type.type_ref()); + bindings.push((name::OUTPUT, type_ref)) + } + if args.is_empty() && bindings.is_empty() { + None + } else { + Some(GenericArgs { args, has_self_type: false, bindings }) + } + } + pub(crate) fn empty() -> GenericArgs { GenericArgs { args: Vec::new(), has_self_type: false, bindings: Vec::new() } } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 17c4e3556..c414e6a95 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3736,7 +3736,7 @@ fn fn_trait() { trait FnOnce { type Output; - fn call_once(self, args: Args) -> Self::Output; + fn call_once(self, args: Args) -> >::Output; } fn test u128>(f: F) { @@ -3746,13 +3746,13 @@ fn test u128>(f: F) { @r###" [57; 61) 'self': Self [63; 67) 'args': Args - [132; 133) 'f': F - [138; 166) '{ ...2)); }': () - [144; 145) 'f': F - [144; 163) 'f.call...1, 2))': {unknown} - [156; 162) '(1, 2)': (i32, i32) - [157; 158) '1': i32 - [160; 161) '2': i32 + [150; 151) 'f': F + [156; 184) '{ ...2)); }': () + [162; 163) 'f': F + [162; 181) 'f.call...1, 2))': {unknown} + [174; 180) '(1, 2)': (u32, u64) + [175; 176) '1': u32 + [178; 179) '2': u64 "### ); } -- cgit v1.2.3 From a1776b27c7d7c266d751360b80cc573b1520ef65 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 7 Sep 2019 16:24:26 +0200 Subject: 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(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 4739246cb..52ee1834f 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 9873a0440..cf787bdaa 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 c414e6a95..127c69f8a 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 6e0271a96..c0c132809 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 { -- cgit v1.2.3 From 9db34eec209c740ed919afb288f75daa755cd268 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 7 Sep 2019 16:30:37 +0200 Subject: Fix Chalk environments The clauses need to be wrapped in `FromEnv` clauses for elaboration (i.e. things like inferring `T: Clone` from `T: Copy`) to work correctly. --- crates/ra_hir/src/ty/tests.rs | 4 ++-- crates/ra_hir/src/ty/traits/chalk.rs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 127c69f8a..3f86a5c80 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3687,9 +3687,9 @@ fn test(x: T, y: U) { [188; 189) 'y': U [194; 223) '{ ...o(); }': () [200; 201) 'x': T - [200; 207) 'x.foo()': {unknown} + [200; 207) 'x.foo()': u32 [213; 214) 'y': U - [213; 220) 'y.foo()': {unknown} + [213; 220) 'y.foo()': u32 "### ); } diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index cfe0cab16..8a127efa1 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -314,7 +314,8 @@ impl ToChalk for Arc { // for env, we just ignore errors continue; } - clauses.push(pred.clone().to_chalk(db).cast()); + let program_clause: chalk_ir::ProgramClause = pred.clone().to_chalk(db).cast(); + clauses.push(program_clause.into_from_env_clause()); } chalk_ir::Environment::new().add_clauses(clauses) } -- cgit v1.2.3 From 8fb3cab76c60fbff5ae6f5984ac07b09b42b742c Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 7 Sep 2019 16:39:05 +0200 Subject: Fix crash for super trait cycles --- crates/ra_hir/src/code_model.rs | 36 ++++++++++++++++++------------------ crates/ra_hir/src/ty/tests.rs | 21 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 18 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 52ee1834f..1bb2f9f28 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -1,11 +1,8 @@ pub(crate) mod src; pub(crate) mod docs; -use std::iter; use std::sync::Arc; -use itertools::Itertools; - use ra_db::{CrateId, Edition, FileId, SourceRootId}; use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; @@ -849,20 +846,23 @@ impl Trait { /// 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.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 all_super_traits(self, db: &impl HirDatabase) -> Vec { + // we need to take care a bit here to avoid infinite loops in case of cycles + // (i.e. if we have `trait A: B; trait B: A;`) + let mut result = vec![self]; + let mut i = 0; + while i < result.len() { + let t = result[i]; + // yeah this is quadratic, but trait hierarchies should be flat + // enough that this doesn't matter + for tt in t.direct_super_traits(db) { + if !result.contains(&tt) { + result.push(tt); + } + } + i += 1; + } + result } pub fn associated_type_by_name(self, db: &impl DefDatabase, name: &Name) -> Option { @@ -882,7 +882,7 @@ impl Trait { db: &impl HirDatabase, name: &Name, ) -> Option { - self.all_super_traits(db).find_map(|t| t.associated_type_by_name(db, name)) + self.all_super_traits(db).into_iter().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/tests.rs b/crates/ra_hir/src/ty/tests.rs index 3f86a5c80..c4bddde85 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3694,6 +3694,27 @@ fn test(x: T, y: U) { ); } +#[test] +fn super_trait_cycle() { + // This just needs to not crash + assert_snapshot!( + infer(r#" +trait A: B {} +trait B: A {} + +fn test(x: T) { + x.foo(); +} +"#), + @r###" + [44; 45) 'x': T + [50; 66) '{ ...o(); }': () + [56; 57) 'x': T + [56; 63) 'x.foo()': {unknown} + "### + ); +} + #[test] fn super_trait_assoc_type_bounds() { assert_snapshot!( -- cgit v1.2.3