aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2019-05-12 17:33:47 +0100
committerFlorian Diebold <[email protected]>2019-06-15 17:21:23 +0100
commit9c5e7dd849eff7bd6f20aa353feef083d089ff58 (patch)
tree5639ff8814d3f4ba0f5ac41de215ed3720f3d1b9 /crates
parent49489dc20cc9f340d43acb467677b9bc59495ed2 (diff)
Implement autoderef using the Deref trait
- add support for other lang item targets, since we need the Deref lang item
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/fill_match_arms.rs2
-rw-r--r--crates/ra_hir/src/lang_item.rs55
-rw-r--r--crates/ra_hir/src/name.rs5
-rw-r--r--crates/ra_hir/src/source_binder.rs11
-rw-r--r--crates/ra_hir/src/ty.rs5
-rw-r--r--crates/ra_hir/src/ty/autoderef.rs68
-rw-r--r--crates/ra_hir/src/ty/infer.rs40
-rw-r--r--crates/ra_hir/src/ty/method_resolution.rs5
-rw-r--r--crates/ra_hir/src/ty/traits.rs4
-rw-r--r--crates/ra_ide_api/src/completion/complete_dot.rs2
-rw-r--r--crates/ra_ide_api/src/goto_type_definition.rs2
11 files changed, 152 insertions, 47 deletions
diff --git a/crates/ra_assists/src/fill_match_arms.rs b/crates/ra_assists/src/fill_match_arms.rs
index a7de6ae9f..c7a8bce20 100644
--- a/crates/ra_assists/src/fill_match_arms.rs
+++ b/crates/ra_assists/src/fill_match_arms.rs
@@ -22,7 +22,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
22 let expr = match_expr.expr()?; 22 let expr = match_expr.expr()?;
23 let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, expr.syntax(), None); 23 let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, expr.syntax(), None);
24 let match_expr_ty = analyzer.type_of(ctx.db, expr)?; 24 let match_expr_ty = analyzer.type_of(ctx.db, expr)?;
25 let enum_def = match_expr_ty.autoderef(ctx.db).find_map(|ty| match ty.as_adt() { 25 let enum_def = analyzer.autoderef(ctx.db, match_expr_ty).find_map(|ty| match ty.as_adt() {
26 Some((AdtDef::Enum(e), _)) => Some(e), 26 Some((AdtDef::Enum(e), _)) => Some(e),
27 _ => None, 27 _ => None,
28 })?; 28 })?;
diff --git a/crates/ra_hir/src/lang_item.rs b/crates/ra_hir/src/lang_item.rs
index cdc9182d6..ada8aeb5b 100644
--- a/crates/ra_hir/src/lang_item.rs
+++ b/crates/ra_hir/src/lang_item.rs
@@ -4,7 +4,7 @@ use rustc_hash::FxHashMap;
4use ra_syntax::{SmolStr, ast::AttrsOwner}; 4use ra_syntax::{SmolStr, ast::AttrsOwner};
5 5
6use crate::{ 6use crate::{
7 Crate, DefDatabase, Enum, Function, HirDatabase, ImplBlock, Module, Static, Struct, Trait, AstDatabase, 7 Crate, DefDatabase, Enum, Function, HirDatabase, ImplBlock, Module, Static, Struct, Trait, ModuleDef, AstDatabase, HasSource
8}; 8};
9 9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -87,19 +87,48 @@ impl LangItems {
87 let source = module.definition_source(db).ast; 87 let source = module.definition_source(db).ast;
88 for (impl_id, _) in impl_blocks.impls.iter() { 88 for (impl_id, _) in impl_blocks.impls.iter() {
89 let impl_block = source_map.get(&source, impl_id); 89 let impl_block = source_map.get(&source, impl_id);
90 let lang_item_name = impl_block 90 if let Some(lang_item_name) = lang_item_name(&*impl_block) {
91 .attrs()
92 .filter_map(|a| a.as_key_value())
93 .filter(|(key, _)| key == "lang")
94 .map(|(_, val)| val)
95 .nth(0);
96 if let Some(lang_item_name) = lang_item_name {
97 let imp = ImplBlock::from_id(*module, impl_id); 91 let imp = ImplBlock::from_id(*module, impl_id);
98 self.items.entry(lang_item_name).or_insert_with(|| LangItemTarget::ImplBlock(imp)); 92 self.items.entry(lang_item_name).or_insert_with(|| LangItemTarget::ImplBlock(imp));
99 } 93 }
100 } 94 }
101 95
102 // FIXME we should look for the other lang item targets (traits, structs, ...) 96 // FIXME make this nicer
97 for def in module.declarations(db) {
98 match def {
99 ModuleDef::Trait(trait_) => {
100 let node = trait_.source(db).ast;
101 if let Some(lang_item_name) = lang_item_name(&*node) {
102 self.items.entry(lang_item_name).or_insert(LangItemTarget::Trait(trait_));
103 }
104 }
105 ModuleDef::Enum(e) => {
106 let node = e.source(db).ast;
107 if let Some(lang_item_name) = lang_item_name(&*node) {
108 self.items.entry(lang_item_name).or_insert(LangItemTarget::Enum(e));
109 }
110 }
111 ModuleDef::Struct(s) => {
112 let node = s.source(db).ast;
113 if let Some(lang_item_name) = lang_item_name(&*node) {
114 self.items.entry(lang_item_name).or_insert(LangItemTarget::Struct(s));
115 }
116 }
117 ModuleDef::Function(f) => {
118 let node = f.source(db).ast;
119 if let Some(lang_item_name) = lang_item_name(&*node) {
120 self.items.entry(lang_item_name).or_insert(LangItemTarget::Function(f));
121 }
122 }
123 ModuleDef::Static(s) => {
124 let node = s.source(db).ast;
125 if let Some(lang_item_name) = lang_item_name(&*node) {
126 self.items.entry(lang_item_name).or_insert(LangItemTarget::Static(s));
127 }
128 }
129 _ => {}
130 }
131 }
103 132
104 // Look for lang items in the children 133 // Look for lang items in the children
105 for child in module.children(db) { 134 for child in module.children(db) {
@@ -107,3 +136,11 @@ impl LangItems {
107 } 136 }
108 } 137 }
109} 138}
139
140fn lang_item_name<T: AttrsOwner>(node: &T) -> Option<SmolStr> {
141 node.attrs()
142 .filter_map(|a| a.as_key_value())
143 .filter(|(key, _)| key == "lang")
144 .map(|(_, val)| val)
145 .nth(0)
146}
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs
index e9003e00b..ba17958eb 100644
--- a/crates/ra_hir/src/name.rs
+++ b/crates/ra_hir/src/name.rs
@@ -46,6 +46,11 @@ impl Name {
46 Name::new(idx.to_string().into()) 46 Name::new(idx.to_string().into())
47 } 47 }
48 48
49 // Needed for Deref
50 pub(crate) fn target() -> Name {
51 Name::new("Target".into())
52 }
53
49 // There's should be no way to extract a string out of `Name`: `Name` in the 54 // There's should be no way to extract a string out of `Name`: `Name` in the
50 // future, `Name` will include hygiene information, and you can't encode 55 // future, `Name` will include hygiene information, and you can't encode
51 // hygiene into a String. 56 // hygiene into a String.
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 4f9e8c5a9..08e86844d 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -369,6 +369,17 @@ impl SourceAnalyzer {
369 ) 369 )
370 } 370 }
371 371
372 pub fn autoderef<'a>(
373 &'a self,
374 db: &'a impl HirDatabase,
375 ty: Ty,
376 ) -> impl Iterator<Item = Ty> + 'a {
377 // There should be no inference vars in types passed here
378 // FIXME check that?
379 let canonical = crate::ty::Canonical { value: ty, num_vars: 0 };
380 crate::ty::autoderef(db, &self.resolver, canonical).map(|canonical| canonical.value)
381 }
382
372 #[cfg(test)] 383 #[cfg(test)]
373 pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { 384 pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> {
374 self.body_source_map.clone().unwrap() 385 self.body_source_map.clone().unwrap()
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index d9a50b230..d2f92a1f2 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -22,6 +22,7 @@ use display::{HirDisplay, HirFormatter};
22pub(crate) use lower::{TypableDef, type_for_def, type_for_field, callable_item_sig, generic_predicates, generic_defaults}; 22pub(crate) use lower::{TypableDef, type_for_def, type_for_field, callable_item_sig, generic_predicates, generic_defaults};
23pub(crate) use infer::{infer_query, InferenceResult, InferTy}; 23pub(crate) use infer::{infer_query, InferenceResult, InferTy};
24pub use lower::CallableDef; 24pub use lower::CallableDef;
25pub(crate) use autoderef::autoderef;
25 26
26/// A type constructor or type name: this might be something like the primitive 27/// A type constructor or type name: this might be something like the primitive
27/// type `bool`, a struct like `Vec`, or things like function pointers or 28/// type `bool`, a struct like `Vec`, or things like function pointers or
@@ -225,8 +226,8 @@ impl Deref for Substs {
225#[derive(Clone, PartialEq, Eq, Debug, Hash)] 226#[derive(Clone, PartialEq, Eq, Debug, Hash)]
226pub struct TraitRef { 227pub struct TraitRef {
227 /// FIXME name? 228 /// FIXME name?
228 trait_: Trait, 229 pub trait_: Trait,
229 substs: Substs, 230 pub substs: Substs,
230} 231}
231 232
232impl TraitRef { 233impl TraitRef {
diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs
index a442a856c..bee756d80 100644
--- a/crates/ra_hir/src/ty/autoderef.rs
+++ b/crates/ra_hir/src/ty/autoderef.rs
@@ -5,17 +5,67 @@
5 5
6use std::iter::successors; 6use std::iter::successors;
7 7
8use crate::HirDatabase; 8use log::info;
9use super::Ty;
10 9
11impl Ty { 10use crate::{HirDatabase, Name, Resolver};
12 /// Iterates over the possible derefs of `ty`. 11use super::{traits::Solution, Ty, Canonical};
13 pub fn autoderef<'a>(self, db: &'a impl HirDatabase) -> impl Iterator<Item = Ty> + 'a { 12
14 successors(Some(self), move |ty| ty.autoderef_step(db)) 13pub(crate) fn autoderef<'a>(
14 db: &'a impl HirDatabase,
15 resolver: &'a Resolver,
16 ty: Canonical<Ty>,
17) -> impl Iterator<Item = Canonical<Ty>> + 'a {
18 successors(Some(ty), move |ty| deref(db, resolver, ty))
19}
20
21pub(crate) fn deref(
22 db: &impl HirDatabase,
23 resolver: &Resolver,
24 ty: &Canonical<Ty>,
25) -> Option<Canonical<Ty>> {
26 if let Some(derefed) = ty.value.builtin_deref() {
27 Some(Canonical { value: derefed, num_vars: ty.num_vars })
28 } else {
29 deref_by_trait(db, resolver, ty)
15 } 30 }
31}
32
33fn deref_by_trait(
34 db: &impl HirDatabase,
35 resolver: &Resolver,
36 ty: &Canonical<Ty>,
37) -> Option<Canonical<Ty>> {
38 let krate = resolver.krate()?;
39 let deref_trait = match db.lang_item(krate, "deref".into())? {
40 crate::lang_item::LangItemTarget::Trait(t) => t,
41 _ => return None,
42 };
43 let target = deref_trait.associated_type_by_name(db, Name::target())?;
44
45 // FIXME we should check that Deref has no type parameters, because we assume it below
46
47 // FIXME make the Canonical handling nicer
48 // TODO shift inference variables in ty
49
50 let projection = super::traits::ProjectionPredicate {
51 ty: Ty::Bound(0),
52 projection_ty: super::ProjectionTy {
53 associated_ty: target,
54 parameters: vec![ty.value.clone()].into(),
55 },
56 };
57
58 let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: projection };
59
60 let solution = db.normalize(krate, canonical)?;
16 61
17 fn autoderef_step(&self, _db: &impl HirDatabase) -> Option<Ty> { 62 match &solution {
18 // FIXME Deref::deref 63 Solution::Unique(vars) => {
19 self.builtin_deref() 64 Some(Canonical { value: vars.0.value[0].clone(), num_vars: vars.0.num_vars })
65 }
66 Solution::Ambig(_) => {
67 info!("Ambiguous solution for deref: {:?}", solution);
68 None
69 }
20 } 70 }
21} 71}
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index e150d7fd8..fdb444de2 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -46,7 +46,7 @@ use crate::{
46use super::{ 46use super::{
47 Ty, TypableDef, Substs, primitive, op, ApplicationTy, TypeCtor, CallableDef, TraitRef, 47 Ty, TypableDef, Substs, primitive, op, ApplicationTy, TypeCtor, CallableDef, TraitRef,
48 traits::{Solution, Obligation, Guidance}, 48 traits::{Solution, Obligation, Guidance},
49 method_resolution, 49 method_resolution, autoderef,
50}; 50};
51 51
52mod unify; 52mod unify;
@@ -1074,25 +1074,27 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
1074 } 1074 }
1075 Expr::Field { expr, name } => { 1075 Expr::Field { expr, name } => {
1076 let receiver_ty = self.infer_expr(*expr, &Expectation::none()); 1076 let receiver_ty = self.infer_expr(*expr, &Expectation::none());
1077 let ty = receiver_ty 1077 let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty);
1078 .autoderef(self.db) 1078 let ty = autoderef::autoderef(
1079 .find_map(|derefed_ty| match derefed_ty { 1079 self.db,
1080 Ty::Apply(a_ty) => match a_ty.ctor { 1080 &self.resolver.clone(),
1081 TypeCtor::Tuple { .. } => { 1081 canonicalized.value.clone(),
1082 let i = name.to_string().parse::<usize>().ok(); 1082 )
1083 i.and_then(|i| a_ty.parameters.0.get(i).cloned()) 1083 .find_map(|derefed_ty| match canonicalized.decanonicalize_ty(derefed_ty.value) {
1084 } 1084 Ty::Apply(a_ty) => match a_ty.ctor {
1085 TypeCtor::Adt(AdtDef::Struct(s)) => { 1085 TypeCtor::Tuple { .. } => {
1086 s.field(self.db, name).map(|field| { 1086 let i = name.to_string().parse::<usize>().ok();
1087 self.write_field_resolution(tgt_expr, field); 1087 i.and_then(|i| a_ty.parameters.0.get(i).cloned())
1088 field.ty(self.db).subst(&a_ty.parameters) 1088 }
1089 }) 1089 TypeCtor::Adt(AdtDef::Struct(s)) => s.field(self.db, name).map(|field| {
1090 } 1090 self.write_field_resolution(tgt_expr, field);
1091 _ => None, 1091 field.ty(self.db).subst(&a_ty.parameters)
1092 }, 1092 }),
1093 _ => None, 1093 _ => None,
1094 }) 1094 },
1095 .unwrap_or(Ty::Unknown); 1095 _ => None,
1096 })
1097 .unwrap_or(Ty::Unknown);
1096 self.insert_type_vars(ty) 1098 self.insert_type_vars(ty)
1097 } 1099 }
1098 Expr::Try { expr } => { 1100 Expr::Try { expr } => {
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs
index 646e58aa9..ad26d591c 100644
--- a/crates/ra_hir/src/ty/method_resolution.rs
+++ b/crates/ra_hir/src/ty/method_resolution.rs
@@ -16,7 +16,7 @@ use crate::{
16 generics::HasGenericParams, 16 generics::HasGenericParams,
17 ty::primitive::{UncertainIntTy, UncertainFloatTy} 17 ty::primitive::{UncertainIntTy, UncertainFloatTy}
18}; 18};
19use super::{TraitRef, Canonical}; 19use super::{TraitRef, Canonical, autoderef};
20 20
21/// This is used as a key for indexing impls. 21/// This is used as a key for indexing impls.
22#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 22#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -162,8 +162,7 @@ pub(crate) fn iterate_method_candidates<T>(
162 // rustc does an autoderef and then autoref again). 162 // rustc does an autoderef and then autoref again).
163 163
164 let krate = resolver.krate()?; 164 let krate = resolver.krate()?;
165 for derefed_ty in ty.value.clone().autoderef(db) { 165 for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) {
166 let derefed_ty = Canonical { value: derefed_ty, num_vars: ty.num_vars };
167 if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback) 166 if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback)
168 { 167 {
169 return Some(result); 168 return Some(result);
diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs
index f3e488403..6cf3dd70a 100644
--- a/crates/ra_hir/src/ty/traits.rs
+++ b/crates/ra_hir/src/ty/traits.rs
@@ -80,8 +80,8 @@ pub enum Obligation {
80 80
81#[derive(Clone, Debug, PartialEq, Eq, Hash)] 81#[derive(Clone, Debug, PartialEq, Eq, Hash)]
82pub struct ProjectionPredicate { 82pub struct ProjectionPredicate {
83 projection_ty: ProjectionTy, 83 pub projection_ty: ProjectionTy,
84 ty: Ty, 84 pub ty: Ty,
85} 85}
86 86
87/// Check using Chalk whether trait is implemented for given parameters including `Self` type. 87/// Check using Chalk whether trait is implemented for given parameters including `Self` type.
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs
index 0822a0e7e..f26fd06b3 100644
--- a/crates/ra_ide_api/src/completion/complete_dot.rs
+++ b/crates/ra_ide_api/src/completion/complete_dot.rs
@@ -15,7 +15,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
15} 15}
16 16
17fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { 17fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
18 for receiver in receiver.autoderef(ctx.db) { 18 for receiver in ctx.analyzer.autoderef(ctx.db, receiver) {
19 if let Ty::Apply(a_ty) = receiver { 19 if let Ty::Apply(a_ty) = receiver {
20 match a_ty.ctor { 20 match a_ty.ctor {
21 TypeCtor::Adt(AdtDef::Struct(s)) => { 21 TypeCtor::Adt(AdtDef::Struct(s)) => {
diff --git a/crates/ra_ide_api/src/goto_type_definition.rs b/crates/ra_ide_api/src/goto_type_definition.rs
index 0f638b170..6f5164e0b 100644
--- a/crates/ra_ide_api/src/goto_type_definition.rs
+++ b/crates/ra_ide_api/src/goto_type_definition.rs
@@ -30,7 +30,7 @@ pub(crate) fn goto_type_definition(
30 return None; 30 return None;
31 }; 31 };
32 32
33 let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt().map(|adt| adt.0))?; 33 let adt_def = analyzer.autoderef(db, ty).find_map(|ty| ty.as_adt().map(|adt| adt.0))?;
34 34
35 let nav = NavigationTarget::from_adt_def(db, adt_def); 35 let nav = NavigationTarget::from_adt_def(db, adt_def);
36 Some(RangeInfo::new(node.range(), vec![nav])) 36 Some(RangeInfo::new(node.range(), vec![nav]))