aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2019-01-06 18:51:42 +0000
committerFlorian Diebold <[email protected]>2019-01-07 13:54:23 +0000
commit7bb279b365e54ee0051e09ead5aa157ff6be917b (patch)
tree7a495d68453ea1e3c7a726e97bbbe19f9bc90532 /crates/ra_hir
parenta6071c9f4c8441b4b8f2e970bc055d66cc9be5f0 (diff)
Implement autoderef for field accesses
Diffstat (limited to 'crates/ra_hir')
-rw-r--r--crates/ra_hir/src/db.rs2
-rw-r--r--crates/ra_hir/src/ty.rs72
-rw-r--r--crates/ra_hir/src/ty/autoderef.rs21
-rw-r--r--crates/ra_hir/src/ty/tests.rs33
-rw-r--r--crates/ra_hir/src/ty/tests/data/field_autoderef.txt43
5 files changed, 147 insertions, 24 deletions
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 033f9d25f..04249400b 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -56,7 +56,7 @@ pub trait HirDatabase: SyntaxDatabase
56 use fn crate::ty::type_for_def; 56 use fn crate::ty::type_for_def;
57 } 57 }
58 58
59 fn type_for_field(def_id: DefId, field: Name) -> Cancelable<Ty> { 59 fn type_for_field(def_id: DefId, field: Name) -> Cancelable<Option<Ty>> {
60 type TypeForFieldQuery; 60 type TypeForFieldQuery;
61 use fn crate::ty::type_for_field; 61 use fn crate::ty::type_for_field;
62 } 62 }
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index d57990cd2..bba8527b7 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -13,6 +13,7 @@
13//! the union-find implementation from the `ena` crate, which is extracted from 13//! the union-find implementation from the `ena` crate, which is extracted from
14//! rustc. 14//! rustc.
15 15
16mod autoderef;
16mod primitive; 17mod primitive;
17#[cfg(test)] 18#[cfg(test)]
18mod tests; 19mod tests;
@@ -36,6 +37,14 @@ use crate::{
36 expr::{Body, Expr, ExprId, PatId, UnaryOp, BinaryOp, Statement}, 37 expr::{Body, Expr, ExprId, PatId, UnaryOp, BinaryOp, Statement},
37}; 38};
38 39
40fn transpose<T>(x: Cancelable<Option<T>>) -> Option<Cancelable<T>> {
41 match x {
42 Ok(Some(t)) => Some(Ok(t)),
43 Ok(None) => None,
44 Err(e) => Some(Err(e)),
45 }
46}
47
39/// The ID of a type variable. 48/// The ID of a type variable.
40#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] 49#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
41pub struct TypeVarId(u32); 50pub struct TypeVarId(u32);
@@ -357,6 +366,14 @@ impl Ty {
357 }); 366 });
358 self 367 self
359 } 368 }
369
370 fn builtin_deref(&self) -> Option<Ty> {
371 match self {
372 Ty::Ref(t, _) => Some(Ty::clone(t)),
373 Ty::RawPtr(t, _) => Some(Ty::clone(t)),
374 _ => None,
375 }
376 }
360} 377}
361 378
362impl fmt::Display for Ty { 379impl fmt::Display for Ty {
@@ -443,7 +460,11 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<T
443 } 460 }
444} 461}
445 462
446pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Cancelable<Ty> { 463pub(super) fn type_for_field(
464 db: &impl HirDatabase,
465 def_id: DefId,
466 field: Name,
467) -> Cancelable<Option<Ty>> {
447 let def = def_id.resolve(db)?; 468 let def = def_id.resolve(db)?;
448 let variant_data = match def { 469 let variant_data = match def {
449 Def::Struct(s) => { 470 Def::Struct(s) => {
@@ -459,12 +480,13 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name)
459 }; 480 };
460 let module = def_id.module(db)?; 481 let module = def_id.module(db)?;
461 let impl_block = def_id.impl_block(db)?; 482 let impl_block = def_id.impl_block(db)?;
462 let type_ref = if let Some(tr) = variant_data.get_field_type_ref(&field) { 483 let type_ref = ctry!(variant_data.get_field_type_ref(&field));
463 tr 484 Ok(Some(Ty::from_hir(
464 } else { 485 db,
465 return Ok(Ty::Unknown); 486 &module,
466 }; 487 impl_block.as_ref(),
467 Ty::from_hir(db, &module, impl_block.as_ref(), &type_ref) 488 &type_ref,
489 )?))
468} 490}
469 491
470/// The result of type inference: A mapping from expressions and patterns to types. 492/// The result of type inference: A mapping from expressions and patterns to types.
@@ -802,7 +824,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
802 let (ty, def_id) = self.resolve_variant(path.as_ref())?; 824 let (ty, def_id) = self.resolve_variant(path.as_ref())?;
803 for field in fields { 825 for field in fields {
804 let field_ty = if let Some(def_id) = def_id { 826 let field_ty = if let Some(def_id) = def_id {
805 self.db.type_for_field(def_id, field.name.clone())? 827 self.db
828 .type_for_field(def_id, field.name.clone())?
829 .unwrap_or(Ty::Unknown)
806 } else { 830 } else {
807 Ty::Unknown 831 Ty::Unknown
808 }; 832 };
@@ -815,15 +839,20 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
815 } 839 }
816 Expr::Field { expr, name } => { 840 Expr::Field { expr, name } => {
817 let receiver_ty = self.infer_expr(*expr, &Expectation::none())?; 841 let receiver_ty = self.infer_expr(*expr, &Expectation::none())?;
818 let ty = match receiver_ty { 842 let ty = receiver_ty
819 Ty::Tuple(fields) => { 843 .autoderef(self.db)
820 let i = name.to_string().parse::<usize>().ok(); 844 .find_map(|derefed_ty| match derefed_ty {
821 i.and_then(|i| fields.get(i).cloned()) 845 // this is more complicated than necessary because type_for_field is cancelable
822 .unwrap_or(Ty::Unknown) 846 Ty::Tuple(fields) => {
823 } 847 let i = name.to_string().parse::<usize>().ok();
824 Ty::Adt { def_id, .. } => self.db.type_for_field(def_id, name.clone())?, 848 i.and_then(|i| fields.get(i).cloned()).map(Ok)
825 _ => Ty::Unknown, 849 }
826 }; 850 Ty::Adt { def_id, .. } => {
851 transpose(self.db.type_for_field(def_id, name.clone()))
852 }
853 _ => None,
854 })
855 .unwrap_or(Ok(Ty::Unknown))?;
827 self.insert_type_vars(ty) 856 self.insert_type_vars(ty)
828 } 857 }
829 Expr::Try { expr } => { 858 Expr::Try { expr } => {
@@ -848,12 +877,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
848 let inner_ty = self.infer_expr(*expr, &Expectation::none())?; 877 let inner_ty = self.infer_expr(*expr, &Expectation::none())?;
849 match op { 878 match op {
850 Some(UnaryOp::Deref) => { 879 Some(UnaryOp::Deref) => {
851 match inner_ty { 880 if let Some(derefed_ty) = inner_ty.builtin_deref() {
852 // builtin deref: 881 derefed_ty
853 Ty::Ref(ref_inner, _) => (*ref_inner).clone(), 882 } else {
854 Ty::RawPtr(ptr_inner, _) => (*ptr_inner).clone(),
855 // TODO Deref::deref 883 // TODO Deref::deref
856 _ => Ty::Unknown, 884 Ty::Unknown
857 } 885 }
858 } 886 }
859 _ => Ty::Unknown, 887 _ => Ty::Unknown,
diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs
new file mode 100644
index 000000000..24a386558
--- /dev/null
+++ b/crates/ra_hir/src/ty/autoderef.rs
@@ -0,0 +1,21 @@
1//! In certain situations, rust automatically inserts derefs as necessary: For
2//! example, field accesses `foo.bar` still work when `foo` is actually a
3//! reference to a type with the field `bar`. This is an approximation of the
4//! logic in rustc (which lives in librustc_typeck/check/autoderef.rs).
5
6use ra_syntax::algo::generate;
7
8use crate::HirDatabase;
9use super::Ty;
10
11impl Ty {
12 /// Iterates over the possible derefs of `ty`.
13 pub fn autoderef<'a>(self, db: &'a impl HirDatabase) -> impl Iterator<Item = Ty> + 'a {
14 generate(Some(self), move |ty| ty.autoderef_step(db))
15 }
16
17 fn autoderef_step(&self, _db: &impl HirDatabase) -> Option<Ty> {
18 // TODO Deref::deref
19 self.builtin_deref()
20 }
21}
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 83aedaa00..e6c7e225b 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -95,7 +95,7 @@ fn test() {
95} 95}
96 96
97#[test] 97#[test]
98fn infer_refs_and_ptrs() { 98fn infer_refs() {
99 check_inference( 99 check_inference(
100 r#" 100 r#"
101fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { 101fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) {
@@ -180,6 +180,37 @@ fn test() {
180 ); 180 );
181} 181}
182 182
183#[test]
184fn infer_field_autoderef() {
185 check_inference(
186 r#"
187struct A {
188 b: B,
189}
190struct B;
191
192fn test1(a: A) {
193 let a1 = a;
194 a1.b;
195 let a2 = &a;
196 a2.b;
197 let a3 = &mut a;
198 a3.b;
199 let a4 = &&&&&&&a;
200 a4.b;
201 let a5 = &mut &&mut &&mut a;
202 a5.b;
203}
204
205fn test2(a1: *const A, a2: *mut A) {
206 a1.b;
207 a2.b;
208}
209"#,
210 "field_autoderef.txt",
211 );
212}
213
183fn infer(content: &str) -> String { 214fn infer(content: &str) -> String {
184 let (db, _, file_id) = MockDatabase::with_single_file(content); 215 let (db, _, file_id) = MockDatabase::with_single_file(content);
185 let source_file = db.source_file(file_id); 216 let source_file = db.source_file(file_id);
diff --git a/crates/ra_hir/src/ty/tests/data/field_autoderef.txt b/crates/ra_hir/src/ty/tests/data/field_autoderef.txt
new file mode 100644
index 000000000..e1db1db40
--- /dev/null
+++ b/crates/ra_hir/src/ty/tests/data/field_autoderef.txt
@@ -0,0 +1,43 @@
1[44; 45) 'a': A
2[50; 213) '{ ...5.b; }': ()
3[60; 62) 'a1': A
4[65; 66) 'a': A
5[72; 74) 'a1': A
6[72; 76) 'a1.b': B
7[86; 88) 'a2': &A
8[91; 93) '&a': &A
9[92; 93) 'a': A
10[99; 101) 'a2': &A
11[99; 103) 'a2.b': B
12[113; 115) 'a3': &mut A
13[118; 124) '&mut a': &mut A
14[123; 124) 'a': A
15[130; 132) 'a3': &mut A
16[130; 134) 'a3.b': B
17[144; 146) 'a4': &&&&&&&A
18[149; 157) '&&&&&&&a': &&&&&&&A
19[150; 157) '&&&&&&a': &&&&&&A
20[151; 157) '&&&&&a': &&&&&A
21[152; 157) '&&&&a': &&&&A
22[153; 157) '&&&a': &&&A
23[154; 157) '&&a': &&A
24[155; 157) '&a': &A
25[156; 157) 'a': A
26[163; 165) 'a4': &&&&&&&A
27[163; 167) 'a4.b': B
28[177; 179) 'a5': &mut &&mut &&mut A
29[182; 200) '&mut &...&mut a': &mut &&mut &&mut A
30[187; 200) '&&mut &&mut a': &&mut &&mut A
31[188; 200) '&mut &&mut a': &mut &&mut A
32[193; 200) '&&mut a': &&mut A
33[194; 200) '&mut a': &mut A
34[199; 200) 'a': A
35[206; 208) 'a5': &mut &&mut &&mut A
36[206; 210) 'a5.b': B
37[224; 226) 'a1': *const A
38[238; 240) 'a2': *mut A
39[250; 273) '{ ...2.b; }': ()
40[256; 258) 'a1': *const A
41[256; 260) 'a1.b': B
42[266; 268) 'a2': *mut A
43[266; 270) 'a2.b': B