diff options
author | Florian Diebold <[email protected]> | 2019-01-06 18:51:42 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-01-07 13:54:23 +0000 |
commit | 7bb279b365e54ee0051e09ead5aa157ff6be917b (patch) | |
tree | 7a495d68453ea1e3c7a726e97bbbe19f9bc90532 | |
parent | a6071c9f4c8441b4b8f2e970bc055d66cc9be5f0 (diff) |
Implement autoderef for field accesses
-rw-r--r-- | crates/ra_analysis/src/completion/complete_dot.rs | 57 | ||||
-rw-r--r-- | crates/ra_hir/src/db.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 72 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/autoderef.rs | 21 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 33 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests/data/field_autoderef.txt | 43 |
6 files changed, 185 insertions, 43 deletions
diff --git a/crates/ra_analysis/src/completion/complete_dot.rs b/crates/ra_analysis/src/completion/complete_dot.rs index 54ce1b638..5d4e60dc5 100644 --- a/crates/ra_analysis/src/completion/complete_dot.rs +++ b/crates/ra_analysis/src/completion/complete_dot.rs | |||
@@ -23,31 +23,35 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Ca | |||
23 | } | 23 | } |
24 | 24 | ||
25 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) -> Cancelable<()> { | 25 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) -> Cancelable<()> { |
26 | // TODO: autoderef etc. | 26 | for receiver in receiver.autoderef(ctx.db) { |
27 | match receiver { | 27 | match receiver { |
28 | Ty::Adt { def_id, .. } => { | 28 | Ty::Adt { def_id, .. } => { |
29 | match def_id.resolve(ctx.db)? { | 29 | match def_id.resolve(ctx.db)? { |
30 | Def::Struct(s) => { | 30 | Def::Struct(s) => { |
31 | let variant_data = s.variant_data(ctx.db)?; | 31 | let variant_data = s.variant_data(ctx.db)?; |
32 | for field in variant_data.fields() { | 32 | for field in variant_data.fields() { |
33 | CompletionItem::new(CompletionKind::Reference, field.name().to_string()) | 33 | CompletionItem::new( |
34 | CompletionKind::Reference, | ||
35 | field.name().to_string(), | ||
36 | ) | ||
34 | .kind(CompletionItemKind::Field) | 37 | .kind(CompletionItemKind::Field) |
35 | .add_to(acc); | 38 | .add_to(acc); |
39 | } | ||
36 | } | 40 | } |
41 | // TODO unions | ||
42 | _ => {} | ||
37 | } | 43 | } |
38 | // TODO unions | ||
39 | _ => {} | ||
40 | } | 44 | } |
41 | } | 45 | Ty::Tuple(fields) => { |
42 | Ty::Tuple(fields) => { | 46 | for (i, _ty) in fields.iter().enumerate() { |
43 | for (i, _ty) in fields.iter().enumerate() { | 47 | CompletionItem::new(CompletionKind::Reference, i.to_string()) |
44 | CompletionItem::new(CompletionKind::Reference, i.to_string()) | 48 | .kind(CompletionItemKind::Field) |
45 | .kind(CompletionItemKind::Field) | 49 | .add_to(acc); |
46 | .add_to(acc); | 50 | } |
47 | } | 51 | } |
48 | } | 52 | _ => {} |
49 | _ => {} | 53 | }; |
50 | }; | 54 | } |
51 | Ok(()) | 55 | Ok(()) |
52 | } | 56 | } |
53 | 57 | ||
@@ -88,6 +92,21 @@ mod tests { | |||
88 | } | 92 | } |
89 | 93 | ||
90 | #[test] | 94 | #[test] |
95 | fn test_struct_field_completion_autoderef() { | ||
96 | check_ref_completion( | ||
97 | r" | ||
98 | struct A { the_field: u32 } | ||
99 | impl A { | ||
100 | fn foo(&self) { | ||
101 | self.<|> | ||
102 | } | ||
103 | } | ||
104 | ", | ||
105 | r#"the_field"#, | ||
106 | ); | ||
107 | } | ||
108 | |||
109 | #[test] | ||
91 | fn test_no_struct_field_completion_for_method_call() { | 110 | fn test_no_struct_field_completion_for_method_call() { |
92 | check_ref_completion( | 111 | check_ref_completion( |
93 | r" | 112 | r" |
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 | ||
16 | mod autoderef; | ||
16 | mod primitive; | 17 | mod primitive; |
17 | #[cfg(test)] | 18 | #[cfg(test)] |
18 | mod tests; | 19 | mod 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 | ||
40 | fn 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)] |
41 | pub struct TypeVarId(u32); | 50 | pub 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 | ||
362 | impl fmt::Display for Ty { | 379 | impl 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 | ||
446 | pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Cancelable<Ty> { | 463 | pub(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 | |||
6 | use ra_syntax::algo::generate; | ||
7 | |||
8 | use crate::HirDatabase; | ||
9 | use super::Ty; | ||
10 | |||
11 | impl 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] |
98 | fn infer_refs_and_ptrs() { | 98 | fn infer_refs() { |
99 | check_inference( | 99 | check_inference( |
100 | r#" | 100 | r#" |
101 | fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { | 101 | fn 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] | ||
184 | fn infer_field_autoderef() { | ||
185 | check_inference( | ||
186 | r#" | ||
187 | struct A { | ||
188 | b: B, | ||
189 | } | ||
190 | struct B; | ||
191 | |||
192 | fn 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 | |||
205 | fn test2(a1: *const A, a2: *mut A) { | ||
206 | a1.b; | ||
207 | a2.b; | ||
208 | } | ||
209 | "#, | ||
210 | "field_autoderef.txt", | ||
211 | ); | ||
212 | } | ||
213 | |||
183 | fn infer(content: &str) -> String { | 214 | fn 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 | ||