diff options
30 files changed, 1518 insertions, 318 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs index d742d6295..fe580700f 100644 --- a/crates/ra_analysis/src/completion.rs +++ b/crates/ra_analysis/src/completion.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | mod completion_item; | 1 | mod completion_item; |
2 | mod completion_context; | 2 | mod completion_context; |
3 | 3 | ||
4 | mod complete_dot; | ||
4 | mod complete_fn_param; | 5 | mod complete_fn_param; |
5 | mod complete_keyword; | 6 | mod complete_keyword; |
6 | mod complete_snippet; | 7 | mod complete_snippet; |
@@ -20,13 +21,13 @@ use crate::{ | |||
20 | 21 | ||
21 | pub use crate::completion::completion_item::{CompletionItem, InsertText, CompletionItemKind}; | 22 | pub use crate::completion::completion_item::{CompletionItem, InsertText, CompletionItemKind}; |
22 | 23 | ||
23 | /// Main entry point for copmletion. We run comletion as a two-phase process. | 24 | /// Main entry point for completion. We run completion as a two-phase process. |
24 | /// | 25 | /// |
25 | /// First, we look at the position and collect a so-called `CompletionContext. | 26 | /// First, we look at the position and collect a so-called `CompletionContext. |
26 | /// This is a somewhat messy process, because, during completion, syntax tree is | 27 | /// This is a somewhat messy process, because, during completion, syntax tree is |
27 | /// incomplete and can look readlly weired. | 28 | /// incomplete and can look really weird. |
28 | /// | 29 | /// |
29 | /// Once the context is collected, we run a series of completion routines whihc | 30 | /// Once the context is collected, we run a series of completion routines which |
30 | /// look at the context and produce completion items. | 31 | /// look at the context and produce completion items. |
31 | pub(crate) fn completions( | 32 | pub(crate) fn completions( |
32 | db: &db::RootDatabase, | 33 | db: &db::RootDatabase, |
@@ -43,6 +44,7 @@ pub(crate) fn completions( | |||
43 | complete_snippet::complete_item_snippet(&mut acc, &ctx); | 44 | complete_snippet::complete_item_snippet(&mut acc, &ctx); |
44 | complete_path::complete_path(&mut acc, &ctx)?; | 45 | complete_path::complete_path(&mut acc, &ctx)?; |
45 | complete_scope::complete_scope(&mut acc, &ctx)?; | 46 | complete_scope::complete_scope(&mut acc, &ctx)?; |
47 | complete_dot::complete_dot(&mut acc, &ctx)?; | ||
46 | 48 | ||
47 | Ok(Some(acc)) | 49 | Ok(Some(acc)) |
48 | } | 50 | } |
diff --git a/crates/ra_analysis/src/completion/complete_dot.rs b/crates/ra_analysis/src/completion/complete_dot.rs new file mode 100644 index 000000000..93d657576 --- /dev/null +++ b/crates/ra_analysis/src/completion/complete_dot.rs | |||
@@ -0,0 +1,98 @@ | |||
1 | use ra_syntax::ast::AstNode; | ||
2 | use hir::{Ty, Def}; | ||
3 | |||
4 | use crate::Cancelable; | ||
5 | use crate::completion::{CompletionContext, Completions, CompletionKind, CompletionItem, CompletionItemKind}; | ||
6 | |||
7 | /// Complete dot accesses, i.e. fields or methods (currently only fields). | ||
8 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { | ||
9 | let module = if let Some(module) = &ctx.module { | ||
10 | module | ||
11 | } else { | ||
12 | return Ok(()); | ||
13 | }; | ||
14 | let function = if let Some(fn_def) = ctx.enclosing_fn { | ||
15 | hir::source_binder::function_from_module(ctx.db, module, fn_def) | ||
16 | } else { | ||
17 | return Ok(()); | ||
18 | }; | ||
19 | let receiver = if let Some(receiver) = ctx.dot_receiver { | ||
20 | receiver | ||
21 | } else { | ||
22 | return Ok(()); | ||
23 | }; | ||
24 | let infer_result = function.infer(ctx.db)?; | ||
25 | let receiver_ty = if let Some(ty) = infer_result.type_of_node(receiver.syntax()) { | ||
26 | ty | ||
27 | } else { | ||
28 | return Ok(()); | ||
29 | }; | ||
30 | if !ctx.is_method_call { | ||
31 | complete_fields(acc, ctx, receiver_ty)?; | ||
32 | } | ||
33 | Ok(()) | ||
34 | } | ||
35 | |||
36 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) -> Cancelable<()> { | ||
37 | // TODO: autoderef etc. | ||
38 | match receiver { | ||
39 | Ty::Adt { def_id, .. } => { | ||
40 | match def_id.resolve(ctx.db)? { | ||
41 | Def::Struct(s) => { | ||
42 | let variant_data = s.variant_data(ctx.db)?; | ||
43 | for field in variant_data.fields() { | ||
44 | CompletionItem::new(CompletionKind::Reference, field.name().to_string()) | ||
45 | .kind(CompletionItemKind::Field) | ||
46 | .add_to(acc); | ||
47 | } | ||
48 | } | ||
49 | // TODO unions | ||
50 | _ => {} | ||
51 | } | ||
52 | } | ||
53 | Ty::Tuple(fields) => { | ||
54 | for (i, _ty) in fields.iter().enumerate() { | ||
55 | CompletionItem::new(CompletionKind::Reference, i.to_string()) | ||
56 | .kind(CompletionItemKind::Field) | ||
57 | .add_to(acc); | ||
58 | } | ||
59 | } | ||
60 | _ => {} | ||
61 | }; | ||
62 | Ok(()) | ||
63 | } | ||
64 | |||
65 | #[cfg(test)] | ||
66 | mod tests { | ||
67 | use crate::completion::*; | ||
68 | |||
69 | fn check_ref_completion(code: &str, expected_completions: &str) { | ||
70 | check_completion(code, expected_completions, CompletionKind::Reference); | ||
71 | } | ||
72 | |||
73 | #[test] | ||
74 | fn test_struct_field_completion() { | ||
75 | check_ref_completion( | ||
76 | r" | ||
77 | struct A { the_field: u32 } | ||
78 | fn foo(a: A) { | ||
79 | a.<|> | ||
80 | } | ||
81 | ", | ||
82 | r#"the_field"#, | ||
83 | ); | ||
84 | } | ||
85 | |||
86 | #[test] | ||
87 | fn test_no_struct_field_completion_for_method_call() { | ||
88 | check_ref_completion( | ||
89 | r" | ||
90 | struct A { the_field: u32 } | ||
91 | fn foo(a: A) { | ||
92 | a.<|>() | ||
93 | } | ||
94 | ", | ||
95 | r#""#, | ||
96 | ); | ||
97 | } | ||
98 | } | ||
diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs index ad4d68a33..aaa2c7cee 100644 --- a/crates/ra_analysis/src/completion/complete_path.rs +++ b/crates/ra_analysis/src/completion/complete_path.rs | |||
@@ -8,7 +8,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C | |||
8 | (Some(path), Some(module)) => (path.clone(), module), | 8 | (Some(path), Some(module)) => (path.clone(), module), |
9 | _ => return Ok(()), | 9 | _ => return Ok(()), |
10 | }; | 10 | }; |
11 | let def_id = match module.resolve_path(ctx.db, path)? { | 11 | let def_id = match module.resolve_path(ctx.db, &path)?.take_types() { |
12 | Some(it) => it, | 12 | Some(it) => it, |
13 | None => return Ok(()), | 13 | None => return Ok(()), |
14 | }; | 14 | }; |
diff --git a/crates/ra_analysis/src/completion/completion_context.rs b/crates/ra_analysis/src/completion/completion_context.rs index 064fbc6f7..978772fd4 100644 --- a/crates/ra_analysis/src/completion/completion_context.rs +++ b/crates/ra_analysis/src/completion/completion_context.rs | |||
@@ -1,12 +1,13 @@ | |||
1 | use ra_editor::find_node_at_offset; | 1 | use ra_editor::find_node_at_offset; |
2 | use ra_text_edit::AtomTextEdit; | 2 | use ra_text_edit::AtomTextEdit; |
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::find_leaf_at_offset, | 4 | algo::{find_leaf_at_offset, find_covering_node}, |
5 | ast, | 5 | ast, |
6 | AstNode, | 6 | AstNode, |
7 | SyntaxNodeRef, | 7 | SyntaxNodeRef, |
8 | SourceFileNode, | 8 | SourceFileNode, |
9 | TextUnit, | 9 | TextUnit, |
10 | TextRange, | ||
10 | SyntaxKind::*, | 11 | SyntaxKind::*, |
11 | }; | 12 | }; |
12 | use hir::source_binder; | 13 | use hir::source_binder; |
@@ -31,6 +32,10 @@ pub(super) struct CompletionContext<'a> { | |||
31 | pub(super) is_stmt: bool, | 32 | pub(super) is_stmt: bool, |
32 | /// Something is typed at the "top" level, in module or impl/trait. | 33 | /// Something is typed at the "top" level, in module or impl/trait. |
33 | pub(super) is_new_item: bool, | 34 | pub(super) is_new_item: bool, |
35 | /// The receiver if this is a field or method access, i.e. writing something.<|> | ||
36 | pub(super) dot_receiver: Option<ast::Expr<'a>>, | ||
37 | /// If this is a method call in particular, i.e. the () are already there. | ||
38 | pub(super) is_method_call: bool, | ||
34 | } | 39 | } |
35 | 40 | ||
36 | impl<'a> CompletionContext<'a> { | 41 | impl<'a> CompletionContext<'a> { |
@@ -54,12 +59,14 @@ impl<'a> CompletionContext<'a> { | |||
54 | after_if: false, | 59 | after_if: false, |
55 | is_stmt: false, | 60 | is_stmt: false, |
56 | is_new_item: false, | 61 | is_new_item: false, |
62 | dot_receiver: None, | ||
63 | is_method_call: false, | ||
57 | }; | 64 | }; |
58 | ctx.fill(original_file, position.offset); | 65 | ctx.fill(original_file, position.offset); |
59 | Ok(Some(ctx)) | 66 | Ok(Some(ctx)) |
60 | } | 67 | } |
61 | 68 | ||
62 | fn fill(&mut self, original_file: &SourceFileNode, offset: TextUnit) { | 69 | fn fill(&mut self, original_file: &'a SourceFileNode, offset: TextUnit) { |
63 | // Insert a fake ident to get a valid parse tree. We will use this file | 70 | // Insert a fake ident to get a valid parse tree. We will use this file |
64 | // to determine context, though the original_file will be used for | 71 | // to determine context, though the original_file will be used for |
65 | // actual completion. | 72 | // actual completion. |
@@ -76,7 +83,7 @@ impl<'a> CompletionContext<'a> { | |||
76 | self.is_param = true; | 83 | self.is_param = true; |
77 | return; | 84 | return; |
78 | } | 85 | } |
79 | self.classify_name_ref(&file, name_ref); | 86 | self.classify_name_ref(original_file, name_ref); |
80 | } | 87 | } |
81 | 88 | ||
82 | // Otherwise, see if this is a declaration. We can use heuristics to | 89 | // Otherwise, see if this is a declaration. We can use heuristics to |
@@ -88,7 +95,7 @@ impl<'a> CompletionContext<'a> { | |||
88 | } | 95 | } |
89 | } | 96 | } |
90 | } | 97 | } |
91 | fn classify_name_ref(&mut self, file: &SourceFileNode, name_ref: ast::NameRef) { | 98 | fn classify_name_ref(&mut self, original_file: &'a SourceFileNode, name_ref: ast::NameRef) { |
92 | let name_range = name_ref.syntax().range(); | 99 | let name_range = name_ref.syntax().range(); |
93 | let top_node = name_ref | 100 | let top_node = name_ref |
94 | .syntax() | 101 | .syntax() |
@@ -105,6 +112,12 @@ impl<'a> CompletionContext<'a> { | |||
105 | _ => (), | 112 | _ => (), |
106 | } | 113 | } |
107 | 114 | ||
115 | self.enclosing_fn = self | ||
116 | .leaf | ||
117 | .ancestors() | ||
118 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | ||
119 | .find_map(ast::FnDef::cast); | ||
120 | |||
108 | let parent = match name_ref.syntax().parent() { | 121 | let parent = match name_ref.syntax().parent() { |
109 | Some(it) => it, | 122 | Some(it) => it, |
110 | None => return, | 123 | None => return, |
@@ -120,11 +133,6 @@ impl<'a> CompletionContext<'a> { | |||
120 | } | 133 | } |
121 | if path.qualifier().is_none() { | 134 | if path.qualifier().is_none() { |
122 | self.is_trivial_path = true; | 135 | self.is_trivial_path = true; |
123 | self.enclosing_fn = self | ||
124 | .leaf | ||
125 | .ancestors() | ||
126 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | ||
127 | .find_map(ast::FnDef::cast); | ||
128 | 136 | ||
129 | self.is_stmt = match name_ref | 137 | self.is_stmt = match name_ref |
130 | .syntax() | 138 | .syntax() |
@@ -137,7 +145,9 @@ impl<'a> CompletionContext<'a> { | |||
137 | }; | 145 | }; |
138 | 146 | ||
139 | if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { | 147 | if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { |
140 | if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) { | 148 | if let Some(if_expr) = |
149 | find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off) | ||
150 | { | ||
141 | if if_expr.syntax().range().end() < name_ref.syntax().range().start() { | 151 | if if_expr.syntax().range().end() < name_ref.syntax().range().start() { |
142 | self.after_if = true; | 152 | self.after_if = true; |
143 | } | 153 | } |
@@ -145,9 +155,33 @@ impl<'a> CompletionContext<'a> { | |||
145 | } | 155 | } |
146 | } | 156 | } |
147 | } | 157 | } |
158 | if let Some(field_expr) = ast::FieldExpr::cast(parent) { | ||
159 | // The receiver comes before the point of insertion of the fake | ||
160 | // ident, so it should have the same range in the non-modified file | ||
161 | self.dot_receiver = field_expr | ||
162 | .expr() | ||
163 | .map(|e| e.syntax().range()) | ||
164 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | ||
165 | } | ||
166 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { | ||
167 | // As above | ||
168 | self.dot_receiver = method_call_expr | ||
169 | .expr() | ||
170 | .map(|e| e.syntax().range()) | ||
171 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | ||
172 | self.is_method_call = true; | ||
173 | } | ||
148 | } | 174 | } |
149 | } | 175 | } |
150 | 176 | ||
177 | fn find_node_with_range<'a, N: AstNode<'a>>( | ||
178 | syntax: SyntaxNodeRef<'a>, | ||
179 | range: TextRange, | ||
180 | ) -> Option<N> { | ||
181 | let node = find_covering_node(syntax, range); | ||
182 | node.ancestors().find_map(N::cast) | ||
183 | } | ||
184 | |||
151 | fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool { | 185 | fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool { |
152 | match node.ancestors().filter_map(N::cast).next() { | 186 | match node.ancestors().filter_map(N::cast).next() { |
153 | None => false, | 187 | None => false, |
diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs index 911f08468..c9f9f495d 100644 --- a/crates/ra_analysis/src/completion/completion_item.rs +++ b/crates/ra_analysis/src/completion/completion_item.rs | |||
@@ -1,5 +1,7 @@ | |||
1 | use crate::db; | 1 | use crate::db; |
2 | 2 | ||
3 | use hir::PerNs; | ||
4 | |||
3 | /// `CompletionItem` describes a single completion variant in the editor pop-up. | 5 | /// `CompletionItem` describes a single completion variant in the editor pop-up. |
4 | /// It is basically a POD with various properties. To construct a | 6 | /// It is basically a POD with various properties. To construct a |
5 | /// `CompletionItem`, use `new` method and the `Builder` struct. | 7 | /// `CompletionItem`, use `new` method and the `Builder` struct. |
@@ -25,7 +27,10 @@ pub enum CompletionItemKind { | |||
25 | Keyword, | 27 | Keyword, |
26 | Module, | 28 | Module, |
27 | Function, | 29 | Function, |
30 | Struct, | ||
31 | Enum, | ||
28 | Binding, | 32 | Binding, |
33 | Field, | ||
29 | } | 34 | } |
30 | 35 | ||
31 | #[derive(Debug, PartialEq, Eq)] | 36 | #[derive(Debug, PartialEq, Eq)] |
@@ -117,16 +122,27 @@ impl Builder { | |||
117 | db: &db::RootDatabase, | 122 | db: &db::RootDatabase, |
118 | resolution: &hir::Resolution, | 123 | resolution: &hir::Resolution, |
119 | ) -> Builder { | 124 | ) -> Builder { |
120 | if let Some(def_id) = resolution.def_id { | 125 | let resolved = resolution.def_id.and_then(|d| d.resolve(db).ok()); |
121 | if let Ok(def) = def_id.resolve(db) { | 126 | let kind = match resolved { |
122 | let kind = match def { | 127 | PerNs { |
123 | hir::Def::Module(..) => CompletionItemKind::Module, | 128 | types: Some(hir::Def::Module(..)), |
124 | hir::Def::Function(..) => CompletionItemKind::Function, | 129 | .. |
125 | _ => return self, | 130 | } => CompletionItemKind::Module, |
126 | }; | 131 | PerNs { |
127 | self.kind = Some(kind); | 132 | types: Some(hir::Def::Struct(..)), |
128 | } | 133 | .. |
129 | } | 134 | } => CompletionItemKind::Struct, |
135 | PerNs { | ||
136 | types: Some(hir::Def::Enum(..)), | ||
137 | .. | ||
138 | } => CompletionItemKind::Enum, | ||
139 | PerNs { | ||
140 | values: Some(hir::Def::Function(..)), | ||
141 | .. | ||
142 | } => CompletionItemKind::Function, | ||
143 | _ => return self, | ||
144 | }; | ||
145 | self.kind = Some(kind); | ||
130 | self | 146 | self |
131 | } | 147 | } |
132 | } | 148 | } |
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index 780a84291..036e284bf 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs | |||
@@ -95,6 +95,9 @@ salsa::database_storage! { | |||
95 | fn submodules() for hir::db::SubmodulesQuery; | 95 | fn submodules() for hir::db::SubmodulesQuery; |
96 | fn infer() for hir::db::InferQuery; | 96 | fn infer() for hir::db::InferQuery; |
97 | fn type_for_def() for hir::db::TypeForDefQuery; | 97 | fn type_for_def() for hir::db::TypeForDefQuery; |
98 | fn type_for_field() for hir::db::TypeForFieldQuery; | ||
99 | fn struct_data() for hir::db::StructDataQuery; | ||
100 | fn enum_data() for hir::db::EnumDataQuery; | ||
98 | } | 101 | } |
99 | } | 102 | } |
100 | } | 103 | } |
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs new file mode 100644 index 000000000..65c461148 --- /dev/null +++ b/crates/ra_hir/src/adt.rs | |||
@@ -0,0 +1,194 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use ra_syntax::{SmolStr, ast::{self, NameOwner, StructFlavor}}; | ||
4 | |||
5 | use crate::{ | ||
6 | DefId, Cancelable, | ||
7 | db::{HirDatabase}, | ||
8 | type_ref::TypeRef, | ||
9 | }; | ||
10 | |||
11 | pub struct Struct { | ||
12 | def_id: DefId, | ||
13 | } | ||
14 | |||
15 | impl Struct { | ||
16 | pub(crate) fn new(def_id: DefId) -> Self { | ||
17 | Struct { def_id } | ||
18 | } | ||
19 | |||
20 | pub fn def_id(&self) -> DefId { | ||
21 | self.def_id | ||
22 | } | ||
23 | |||
24 | pub fn variant_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<VariantData>> { | ||
25 | Ok(db.struct_data(self.def_id)?.variant_data.clone()) | ||
26 | } | ||
27 | |||
28 | pub fn struct_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<StructData>> { | ||
29 | Ok(db.struct_data(self.def_id)?) | ||
30 | } | ||
31 | |||
32 | pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<SmolStr>> { | ||
33 | Ok(db.struct_data(self.def_id)?.name.clone()) | ||
34 | } | ||
35 | } | ||
36 | |||
37 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
38 | pub struct StructData { | ||
39 | name: Option<SmolStr>, | ||
40 | variant_data: Arc<VariantData>, | ||
41 | } | ||
42 | |||
43 | impl StructData { | ||
44 | pub(crate) fn new(struct_def: ast::StructDef) -> StructData { | ||
45 | let name = struct_def.name().map(|n| n.text()); | ||
46 | let variant_data = VariantData::new(struct_def.flavor()); | ||
47 | let variant_data = Arc::new(variant_data); | ||
48 | StructData { name, variant_data } | ||
49 | } | ||
50 | |||
51 | pub fn name(&self) -> Option<&SmolStr> { | ||
52 | self.name.as_ref() | ||
53 | } | ||
54 | |||
55 | pub fn variant_data(&self) -> &Arc<VariantData> { | ||
56 | &self.variant_data | ||
57 | } | ||
58 | } | ||
59 | |||
60 | pub struct Enum { | ||
61 | def_id: DefId, | ||
62 | } | ||
63 | |||
64 | impl Enum { | ||
65 | pub(crate) fn new(def_id: DefId) -> Self { | ||
66 | Enum { def_id } | ||
67 | } | ||
68 | |||
69 | pub fn def_id(&self) -> DefId { | ||
70 | self.def_id | ||
71 | } | ||
72 | |||
73 | pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<SmolStr>> { | ||
74 | Ok(db.enum_data(self.def_id)?.name.clone()) | ||
75 | } | ||
76 | } | ||
77 | |||
78 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
79 | pub struct EnumData { | ||
80 | name: Option<SmolStr>, | ||
81 | variants: Vec<(SmolStr, Arc<VariantData>)>, | ||
82 | } | ||
83 | |||
84 | impl EnumData { | ||
85 | pub(crate) fn new(enum_def: ast::EnumDef) -> Self { | ||
86 | let name = enum_def.name().map(|n| n.text()); | ||
87 | let variants = if let Some(evl) = enum_def.variant_list() { | ||
88 | evl.variants() | ||
89 | .map(|v| { | ||
90 | ( | ||
91 | v.name() | ||
92 | .map(|n| n.text()) | ||
93 | .unwrap_or_else(|| SmolStr::new("[error]")), | ||
94 | Arc::new(VariantData::new(v.flavor())), | ||
95 | ) | ||
96 | }) | ||
97 | .collect() | ||
98 | } else { | ||
99 | Vec::new() | ||
100 | }; | ||
101 | EnumData { name, variants } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | /// A single field of an enum variant or struct | ||
106 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
107 | pub struct StructField { | ||
108 | name: SmolStr, | ||
109 | type_ref: TypeRef, | ||
110 | } | ||
111 | |||
112 | impl StructField { | ||
113 | pub fn name(&self) -> SmolStr { | ||
114 | self.name.clone() | ||
115 | } | ||
116 | pub fn type_ref(&self) -> &TypeRef { | ||
117 | &self.type_ref | ||
118 | } | ||
119 | } | ||
120 | |||
121 | /// Fields of an enum variant or struct | ||
122 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
123 | pub enum VariantData { | ||
124 | Struct(Vec<StructField>), | ||
125 | Tuple(Vec<StructField>), | ||
126 | Unit, | ||
127 | } | ||
128 | |||
129 | impl VariantData { | ||
130 | pub fn new(flavor: StructFlavor) -> Self { | ||
131 | match flavor { | ||
132 | StructFlavor::Tuple(fl) => { | ||
133 | let fields = fl | ||
134 | .fields() | ||
135 | .enumerate() | ||
136 | .map(|(i, fd)| StructField { | ||
137 | name: SmolStr::new(i.to_string()), | ||
138 | type_ref: TypeRef::from_ast_opt(fd.type_ref()), | ||
139 | }) | ||
140 | .collect(); | ||
141 | VariantData::Tuple(fields) | ||
142 | } | ||
143 | StructFlavor::Named(fl) => { | ||
144 | let fields = fl | ||
145 | .fields() | ||
146 | .map(|fd| StructField { | ||
147 | name: fd | ||
148 | .name() | ||
149 | .map(|n| n.text()) | ||
150 | .unwrap_or_else(|| SmolStr::new("[error]")), | ||
151 | type_ref: TypeRef::from_ast_opt(fd.type_ref()), | ||
152 | }) | ||
153 | .collect(); | ||
154 | VariantData::Struct(fields) | ||
155 | } | ||
156 | StructFlavor::Unit => VariantData::Unit, | ||
157 | } | ||
158 | } | ||
159 | |||
160 | pub(crate) fn get_field_type_ref(&self, field_name: &str) -> Option<&TypeRef> { | ||
161 | self.fields() | ||
162 | .iter() | ||
163 | .find(|f| f.name == field_name) | ||
164 | .map(|f| &f.type_ref) | ||
165 | } | ||
166 | |||
167 | pub fn fields(&self) -> &[StructField] { | ||
168 | match *self { | ||
169 | VariantData::Struct(ref fields) | VariantData::Tuple(ref fields) => fields, | ||
170 | _ => &[], | ||
171 | } | ||
172 | } | ||
173 | pub fn is_struct(&self) -> bool { | ||
174 | if let VariantData::Struct(..) = *self { | ||
175 | true | ||
176 | } else { | ||
177 | false | ||
178 | } | ||
179 | } | ||
180 | pub fn is_tuple(&self) -> bool { | ||
181 | if let VariantData::Tuple(..) = *self { | ||
182 | true | ||
183 | } else { | ||
184 | false | ||
185 | } | ||
186 | } | ||
187 | pub fn is_unit(&self) -> bool { | ||
188 | if let VariantData::Unit = *self { | ||
189 | true | ||
190 | } else { | ||
191 | false | ||
192 | } | ||
193 | } | ||
194 | } | ||
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index d94f75857..e7f9afa77 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | SmolStr, | ||
4 | SyntaxNode, | 5 | SyntaxNode, |
5 | ast::FnDefNode, | 6 | ast::FnDefNode, |
6 | }; | 7 | }; |
@@ -15,6 +16,7 @@ use crate::{ | |||
15 | module::{ModuleId, ModuleTree, ModuleSource, | 16 | module::{ModuleId, ModuleTree, ModuleSource, |
16 | nameres::{ItemMap, InputModuleItems}}, | 17 | nameres::{ItemMap, InputModuleItems}}, |
17 | ty::{InferenceResult, Ty}, | 18 | ty::{InferenceResult, Ty}, |
19 | adt::{StructData, EnumData}, | ||
18 | }; | 20 | }; |
19 | 21 | ||
20 | salsa::query_group! { | 22 | salsa::query_group! { |
@@ -31,6 +33,16 @@ pub trait HirDatabase: SyntaxDatabase | |||
31 | use fn query_definitions::fn_syntax; | 33 | use fn query_definitions::fn_syntax; |
32 | } | 34 | } |
33 | 35 | ||
36 | fn struct_data(def_id: DefId) -> Cancelable<Arc<StructData>> { | ||
37 | type StructDataQuery; | ||
38 | use fn query_definitions::struct_data; | ||
39 | } | ||
40 | |||
41 | fn enum_data(def_id: DefId) -> Cancelable<Arc<EnumData>> { | ||
42 | type EnumDataQuery; | ||
43 | use fn query_definitions::enum_data; | ||
44 | } | ||
45 | |||
34 | fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> { | 46 | fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> { |
35 | type InferQuery; | 47 | type InferQuery; |
36 | use fn query_definitions::infer; | 48 | use fn query_definitions::infer; |
@@ -41,6 +53,11 @@ pub trait HirDatabase: SyntaxDatabase | |||
41 | use fn query_definitions::type_for_def; | 53 | use fn query_definitions::type_for_def; |
42 | } | 54 | } |
43 | 55 | ||
56 | fn type_for_field(def_id: DefId, field: SmolStr) -> Cancelable<Ty> { | ||
57 | type TypeForFieldQuery; | ||
58 | use fn query_definitions::type_for_field; | ||
59 | } | ||
60 | |||
44 | fn file_items(file_id: FileId) -> Arc<SourceFileItems> { | 61 | fn file_items(file_id: FileId) -> Arc<SourceFileItems> { |
45 | type SourceFileItemsQuery; | 62 | type SourceFileItemsQuery; |
46 | use fn query_definitions::file_items; | 63 | use fn query_definitions::file_items; |
diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs index d36477b48..01f0f3a66 100644 --- a/crates/ra_hir/src/function.rs +++ b/crates/ra_hir/src/function.rs | |||
@@ -46,8 +46,7 @@ impl Function { | |||
46 | } | 46 | } |
47 | 47 | ||
48 | pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> { | 48 | pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> { |
49 | let loc = self.fn_id.0.loc(db); | 49 | self.fn_id.0.module(db) |
50 | Module::new(db, loc.source_root_id, loc.module_id) | ||
51 | } | 50 | } |
52 | } | 51 | } |
53 | 52 | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index a0d99a84d..f1cc0ccd0 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -25,6 +25,8 @@ pub mod source_binder; | |||
25 | mod krate; | 25 | mod krate; |
26 | mod module; | 26 | mod module; |
27 | mod function; | 27 | mod function; |
28 | mod adt; | ||
29 | mod type_ref; | ||
28 | mod ty; | 30 | mod ty; |
29 | 31 | ||
30 | use std::ops::Index; | 32 | use std::ops::Index; |
@@ -40,8 +42,10 @@ use crate::{ | |||
40 | pub use self::{ | 42 | pub use self::{ |
41 | path::{Path, PathKind}, | 43 | path::{Path, PathKind}, |
42 | krate::Crate, | 44 | krate::Crate, |
43 | module::{Module, ModuleId, Problem, nameres::ItemMap, ModuleScope, Resolution}, | 45 | module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, |
44 | function::{Function, FnScopes}, | 46 | function::{Function, FnScopes}, |
47 | adt::{Struct, Enum}, | ||
48 | ty::Ty, | ||
45 | }; | 49 | }; |
46 | 50 | ||
47 | pub use self::function::FnSignatureInfo; | 51 | pub use self::function::FnSignatureInfo; |
@@ -56,7 +60,11 @@ ra_db::impl_numeric_id!(DefId); | |||
56 | pub(crate) enum DefKind { | 60 | pub(crate) enum DefKind { |
57 | Module, | 61 | Module, |
58 | Function, | 62 | Function, |
63 | Struct, | ||
64 | Enum, | ||
59 | Item, | 65 | Item, |
66 | |||
67 | StructCtor, | ||
60 | } | 68 | } |
61 | 69 | ||
62 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | 70 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] |
@@ -68,18 +76,18 @@ pub struct DefLoc { | |||
68 | } | 76 | } |
69 | 77 | ||
70 | impl DefKind { | 78 | impl DefKind { |
71 | pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> Option<DefKind> { | 79 | pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> { |
72 | match kind { | 80 | match kind { |
73 | SyntaxKind::FN_DEF => Some(DefKind::Function), | 81 | SyntaxKind::FN_DEF => PerNs::values(DefKind::Function), |
74 | SyntaxKind::MODULE => Some(DefKind::Module), | 82 | SyntaxKind::MODULE => PerNs::types(DefKind::Module), |
83 | SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor), | ||
84 | SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum), | ||
75 | // These define items, but don't have their own DefKinds yet: | 85 | // These define items, but don't have their own DefKinds yet: |
76 | SyntaxKind::STRUCT_DEF => Some(DefKind::Item), | 86 | SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item), |
77 | SyntaxKind::ENUM_DEF => Some(DefKind::Item), | 87 | SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item), |
78 | SyntaxKind::TRAIT_DEF => Some(DefKind::Item), | 88 | SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item), |
79 | SyntaxKind::TYPE_DEF => Some(DefKind::Item), | 89 | SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item), |
80 | SyntaxKind::CONST_DEF => Some(DefKind::Item), | 90 | _ => PerNs::none(), |
81 | SyntaxKind::STATIC_DEF => Some(DefKind::Item), | ||
82 | _ => None, | ||
83 | } | 91 | } |
84 | } | 92 | } |
85 | } | 93 | } |
@@ -99,6 +107,8 @@ impl DefLoc { | |||
99 | pub enum Def { | 107 | pub enum Def { |
100 | Module(Module), | 108 | Module(Module), |
101 | Function(Function), | 109 | Function(Function), |
110 | Struct(Struct), | ||
111 | Enum(Enum), | ||
102 | Item, | 112 | Item, |
103 | } | 113 | } |
104 | 114 | ||
@@ -114,10 +124,25 @@ impl DefId { | |||
114 | let function = Function::new(self); | 124 | let function = Function::new(self); |
115 | Def::Function(function) | 125 | Def::Function(function) |
116 | } | 126 | } |
127 | DefKind::Struct => { | ||
128 | let struct_def = Struct::new(self); | ||
129 | Def::Struct(struct_def) | ||
130 | } | ||
131 | DefKind::Enum => { | ||
132 | let enum_def = Enum::new(self); | ||
133 | Def::Enum(enum_def) | ||
134 | } | ||
135 | DefKind::StructCtor => Def::Item, | ||
117 | DefKind::Item => Def::Item, | 136 | DefKind::Item => Def::Item, |
118 | }; | 137 | }; |
119 | Ok(res) | 138 | Ok(res) |
120 | } | 139 | } |
140 | |||
141 | /// For a module, returns that module; for any other def, returns the containing module. | ||
142 | pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> { | ||
143 | let loc = self.loc(db); | ||
144 | Module::new(db, loc.source_root_id, loc.module_id) | ||
145 | } | ||
121 | } | 146 | } |
122 | 147 | ||
123 | /// Identifier of item within a specific file. This is stable over reparses, so | 148 | /// Identifier of item within a specific file. This is stable over reparses, so |
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index b5a997170..f6882cb77 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs | |||
@@ -193,6 +193,9 @@ salsa::database_storage! { | |||
193 | fn submodules() for db::SubmodulesQuery; | 193 | fn submodules() for db::SubmodulesQuery; |
194 | fn infer() for db::InferQuery; | 194 | fn infer() for db::InferQuery; |
195 | fn type_for_def() for db::TypeForDefQuery; | 195 | fn type_for_def() for db::TypeForDefQuery; |
196 | fn type_for_field() for db::TypeForFieldQuery; | ||
197 | fn struct_data() for db::StructDataQuery; | ||
198 | fn enum_data() for db::EnumDataQuery; | ||
196 | } | 199 | } |
197 | } | 200 | } |
198 | } | 201 | } |
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index 891119953..b9d36f01f 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs | |||
@@ -17,7 +17,7 @@ use crate::{ | |||
17 | arena::{Arena, Id}, | 17 | arena::{Arena, Id}, |
18 | }; | 18 | }; |
19 | 19 | ||
20 | pub use self::nameres::{ModuleScope, Resolution}; | 20 | pub use self::nameres::{ModuleScope, Resolution, Namespace, PerNs}; |
21 | 21 | ||
22 | /// `Module` is API entry point to get all the information | 22 | /// `Module` is API entry point to get all the information |
23 | /// about a particular module. | 23 | /// about a particular module. |
@@ -115,16 +115,29 @@ impl Module { | |||
115 | Ok(res) | 115 | Ok(res) |
116 | } | 116 | } |
117 | 117 | ||
118 | pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable<Option<DefId>> { | 118 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> Cancelable<PerNs<DefId>> { |
119 | let mut curr = match path.kind { | 119 | let mut curr_per_ns = PerNs::types( |
120 | PathKind::Crate => self.crate_root(), | 120 | match path.kind { |
121 | PathKind::Self_ | PathKind::Plain => self.clone(), | 121 | PathKind::Crate => self.crate_root(), |
122 | PathKind::Super => ctry!(self.parent()), | 122 | PathKind::Self_ | PathKind::Plain => self.clone(), |
123 | } | 123 | PathKind::Super => { |
124 | .def_id(db); | 124 | if let Some(p) = self.parent() { |
125 | p | ||
126 | } else { | ||
127 | return Ok(PerNs::none()); | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | .def_id(db), | ||
132 | ); | ||
125 | 133 | ||
126 | let segments = path.segments; | 134 | let segments = &path.segments; |
127 | for name in segments.iter() { | 135 | for name in segments.iter() { |
136 | let curr = if let Some(r) = curr_per_ns.as_ref().take(Namespace::Types) { | ||
137 | r | ||
138 | } else { | ||
139 | return Ok(PerNs::none()); | ||
140 | }; | ||
128 | let module = match curr.loc(db) { | 141 | let module = match curr.loc(db) { |
129 | DefLoc { | 142 | DefLoc { |
130 | kind: DefKind::Module, | 143 | kind: DefKind::Module, |
@@ -132,12 +145,17 @@ impl Module { | |||
132 | module_id, | 145 | module_id, |
133 | .. | 146 | .. |
134 | } => Module::new(db, source_root_id, module_id)?, | 147 | } => Module::new(db, source_root_id, module_id)?, |
135 | _ => return Ok(None), | 148 | // TODO here would be the place to handle enum variants... |
149 | _ => return Ok(PerNs::none()), | ||
136 | }; | 150 | }; |
137 | let scope = module.scope(db)?; | 151 | let scope = module.scope(db)?; |
138 | curr = ctry!(ctry!(scope.get(&name)).def_id); | 152 | curr_per_ns = if let Some(r) = scope.get(&name) { |
153 | r.def_id | ||
154 | } else { | ||
155 | return Ok(PerNs::none()); | ||
156 | }; | ||
139 | } | 157 | } |
140 | Ok(Some(curr)) | 158 | Ok(curr_per_ns) |
141 | } | 159 | } |
142 | 160 | ||
143 | pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { | 161 | pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { |
@@ -145,7 +163,7 @@ impl Module { | |||
145 | } | 163 | } |
146 | } | 164 | } |
147 | 165 | ||
148 | /// Phisically, rust source is organized as a set of files, but logically it is | 166 | /// Physically, rust source is organized as a set of files, but logically it is |
149 | /// organized as a tree of modules. Usually, a single file corresponds to a | 167 | /// organized as a tree of modules. Usually, a single file corresponds to a |
150 | /// single module, but it is not nessary the case. | 168 | /// single module, but it is not nessary the case. |
151 | /// | 169 | /// |
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs index 0b152a406..98cd225dd 100644 --- a/crates/ra_hir/src/module/nameres.rs +++ b/crates/ra_hir/src/module/nameres.rs | |||
@@ -118,22 +118,96 @@ enum ImportKind { | |||
118 | #[derive(Debug, Clone, PartialEq, Eq)] | 118 | #[derive(Debug, Clone, PartialEq, Eq)] |
119 | pub struct Resolution { | 119 | pub struct Resolution { |
120 | /// None for unresolved | 120 | /// None for unresolved |
121 | pub def_id: Option<DefId>, | 121 | pub def_id: PerNs<DefId>, |
122 | /// ident by whitch this is imported into local scope. | 122 | /// ident by whitch this is imported into local scope. |
123 | pub import: Option<NamedImport>, | 123 | pub import: Option<NamedImport>, |
124 | } | 124 | } |
125 | 125 | ||
126 | // #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | 126 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
127 | // enum Namespace { | 127 | pub enum Namespace { |
128 | // Types, | 128 | Types, |
129 | // Values, | 129 | Values, |
130 | // } | 130 | } |
131 | |||
132 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
133 | pub struct PerNs<T> { | ||
134 | pub types: Option<T>, | ||
135 | pub values: Option<T>, | ||
136 | } | ||
137 | |||
138 | impl<T> PerNs<T> { | ||
139 | pub fn none() -> PerNs<T> { | ||
140 | PerNs { | ||
141 | types: None, | ||
142 | values: None, | ||
143 | } | ||
144 | } | ||
145 | |||
146 | pub fn values(t: T) -> PerNs<T> { | ||
147 | PerNs { | ||
148 | types: None, | ||
149 | values: Some(t), | ||
150 | } | ||
151 | } | ||
152 | |||
153 | pub fn types(t: T) -> PerNs<T> { | ||
154 | PerNs { | ||
155 | types: Some(t), | ||
156 | values: None, | ||
157 | } | ||
158 | } | ||
159 | |||
160 | pub fn both(types: T, values: T) -> PerNs<T> { | ||
161 | PerNs { | ||
162 | types: Some(types), | ||
163 | values: Some(values), | ||
164 | } | ||
165 | } | ||
166 | |||
167 | pub fn is_none(&self) -> bool { | ||
168 | self.types.is_none() && self.values.is_none() | ||
169 | } | ||
170 | |||
171 | pub fn take(self, namespace: Namespace) -> Option<T> { | ||
172 | match namespace { | ||
173 | Namespace::Types => self.types, | ||
174 | Namespace::Values => self.values, | ||
175 | } | ||
176 | } | ||
177 | |||
178 | pub fn take_types(self) -> Option<T> { | ||
179 | self.types | ||
180 | } | ||
181 | |||
182 | pub fn take_values(self) -> Option<T> { | ||
183 | self.values | ||
184 | } | ||
131 | 185 | ||
132 | // #[derive(Debug)] | 186 | pub fn get(&self, namespace: Namespace) -> Option<&T> { |
133 | // struct PerNs<T> { | 187 | self.as_ref().take(namespace) |
134 | // types: Option<T>, | 188 | } |
135 | // values: Option<T>, | 189 | |
136 | // } | 190 | pub fn as_ref(&self) -> PerNs<&T> { |
191 | PerNs { | ||
192 | types: self.types.as_ref(), | ||
193 | values: self.values.as_ref(), | ||
194 | } | ||
195 | } | ||
196 | |||
197 | pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> { | ||
198 | PerNs { | ||
199 | types: self.types.and_then(&f), | ||
200 | values: self.values.and_then(&f), | ||
201 | } | ||
202 | } | ||
203 | |||
204 | pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> { | ||
205 | PerNs { | ||
206 | types: self.types.map(&f), | ||
207 | values: self.values.map(&f), | ||
208 | } | ||
209 | } | ||
210 | } | ||
137 | 211 | ||
138 | impl InputModuleItems { | 212 | impl InputModuleItems { |
139 | pub(crate) fn new<'a>( | 213 | pub(crate) fn new<'a>( |
@@ -254,7 +328,7 @@ where | |||
254 | for dep in krate.dependencies(self.db) { | 328 | for dep in krate.dependencies(self.db) { |
255 | if let Some(module) = dep.krate.root_module(self.db)? { | 329 | if let Some(module) = dep.krate.root_module(self.db)? { |
256 | let def_id = module.def_id(self.db); | 330 | let def_id = module.def_id(self.db); |
257 | self.add_module_item(&mut module_items, dep.name, def_id); | 331 | self.add_module_item(&mut module_items, dep.name, PerNs::types(def_id)); |
258 | } | 332 | } |
259 | } | 333 | } |
260 | }; | 334 | }; |
@@ -265,7 +339,7 @@ where | |||
265 | module_items.items.insert( | 339 | module_items.items.insert( |
266 | name.clone(), | 340 | name.clone(), |
267 | Resolution { | 341 | Resolution { |
268 | def_id: None, | 342 | def_id: PerNs::none(), |
269 | import: Some(import), | 343 | import: Some(import), |
270 | }, | 344 | }, |
271 | ); | 345 | ); |
@@ -277,18 +351,23 @@ where | |||
277 | if item.kind == MODULE { | 351 | if item.kind == MODULE { |
278 | continue; | 352 | continue; |
279 | } | 353 | } |
280 | let def_loc = DefLoc { | 354 | // depending on the item kind, the location can define something in |
281 | kind: DefKind::for_syntax_kind(item.kind).unwrap_or(DefKind::Item), | 355 | // the values namespace, the types namespace, or both |
282 | source_root_id: self.source_root, | 356 | let kind = DefKind::for_syntax_kind(item.kind); |
283 | module_id, | 357 | let def_id = kind.map(|k| { |
284 | source_item_id: SourceItemId { | 358 | let def_loc = DefLoc { |
285 | file_id, | 359 | kind: k, |
286 | item_id: Some(item.id), | 360 | source_root_id: self.source_root, |
287 | }, | 361 | module_id, |
288 | }; | 362 | source_item_id: SourceItemId { |
289 | let def_id = def_loc.id(self.db); | 363 | file_id, |
364 | item_id: Some(item.id), | ||
365 | }, | ||
366 | }; | ||
367 | def_loc.id(self.db) | ||
368 | }); | ||
290 | let resolution = Resolution { | 369 | let resolution = Resolution { |
291 | def_id: Some(def_id), | 370 | def_id, |
292 | import: None, | 371 | import: None, |
293 | }; | 372 | }; |
294 | module_items.items.insert(item.name.clone(), resolution); | 373 | module_items.items.insert(item.name.clone(), resolution); |
@@ -303,16 +382,16 @@ where | |||
303 | source_item_id: module_id.source(&self.module_tree).0, | 382 | source_item_id: module_id.source(&self.module_tree).0, |
304 | }; | 383 | }; |
305 | let def_id = def_loc.id(self.db); | 384 | let def_id = def_loc.id(self.db); |
306 | self.add_module_item(&mut module_items, name, def_id); | 385 | self.add_module_item(&mut module_items, name, PerNs::types(def_id)); |
307 | } | 386 | } |
308 | 387 | ||
309 | self.result.per_module.insert(module_id, module_items); | 388 | self.result.per_module.insert(module_id, module_items); |
310 | Ok(()) | 389 | Ok(()) |
311 | } | 390 | } |
312 | 391 | ||
313 | fn add_module_item(&self, module_items: &mut ModuleScope, name: SmolStr, def_id: DefId) { | 392 | fn add_module_item(&self, module_items: &mut ModuleScope, name: SmolStr, def_id: PerNs<DefId>) { |
314 | let resolution = Resolution { | 393 | let resolution = Resolution { |
315 | def_id: Some(def_id), | 394 | def_id, |
316 | import: None, | 395 | import: None, |
317 | }; | 396 | }; |
318 | module_items.items.insert(name, resolution); | 397 | module_items.items.insert(name, resolution); |
@@ -347,15 +426,17 @@ where | |||
347 | let is_last = i == import.path.segments.len() - 1; | 426 | let is_last = i == import.path.segments.len() - 1; |
348 | 427 | ||
349 | let def_id = match self.result.per_module[&curr].items.get(name) { | 428 | let def_id = match self.result.per_module[&curr].items.get(name) { |
350 | None => return Ok(()), | 429 | Some(res) if !res.def_id.is_none() => res.def_id, |
351 | Some(res) => match res.def_id { | 430 | _ => return Ok(()), |
352 | Some(it) => it, | ||
353 | None => return Ok(()), | ||
354 | }, | ||
355 | }; | 431 | }; |
356 | 432 | ||
357 | if !is_last { | 433 | if !is_last { |
358 | curr = match def_id.loc(self.db) { | 434 | let type_def_id = if let Some(d) = def_id.take(Namespace::Types) { |
435 | d | ||
436 | } else { | ||
437 | return Ok(()); | ||
438 | }; | ||
439 | curr = match type_def_id.loc(self.db) { | ||
359 | DefLoc { | 440 | DefLoc { |
360 | kind: DefKind::Module, | 441 | kind: DefKind::Module, |
361 | module_id: target_module_id, | 442 | module_id: target_module_id, |
@@ -370,10 +451,11 @@ where | |||
370 | segments: import.path.segments[i + 1..].iter().cloned().collect(), | 451 | segments: import.path.segments[i + 1..].iter().cloned().collect(), |
371 | kind: PathKind::Crate, | 452 | kind: PathKind::Crate, |
372 | }; | 453 | }; |
373 | if let Some(def_id) = module.resolve_path(self.db, path)? { | 454 | let def_id = module.resolve_path(self.db, &path)?; |
455 | if !def_id.is_none() { | ||
374 | self.update(module_id, |items| { | 456 | self.update(module_id, |items| { |
375 | let res = Resolution { | 457 | let res = Resolution { |
376 | def_id: Some(def_id), | 458 | def_id: def_id, |
377 | import: Some(ptr), | 459 | import: Some(ptr), |
378 | }; | 460 | }; |
379 | items.items.insert(name.clone(), res); | 461 | items.items.insert(name.clone(), res); |
@@ -387,7 +469,7 @@ where | |||
387 | } else { | 469 | } else { |
388 | self.update(module_id, |items| { | 470 | self.update(module_id, |items| { |
389 | let res = Resolution { | 471 | let res = Resolution { |
390 | def_id: Some(def_id), | 472 | def_id: def_id, |
391 | import: Some(ptr), | 473 | import: Some(ptr), |
392 | }; | 474 | }; |
393 | items.items.insert(name.clone(), res); | 475 | items.items.insert(name.clone(), res); |
diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs index 3e29c3954..03ea5c1d6 100644 --- a/crates/ra_hir/src/module/nameres/tests.rs +++ b/crates/ra_hir/src/module/nameres/tests.rs | |||
@@ -40,7 +40,7 @@ fn item_map_smoke_test() { | |||
40 | ); | 40 | ); |
41 | let name = SmolStr::from("Baz"); | 41 | let name = SmolStr::from("Baz"); |
42 | let resolution = &item_map.per_module[&module_id].items[&name]; | 42 | let resolution = &item_map.per_module[&module_id].items[&name]; |
43 | assert!(resolution.def_id.is_some()); | 43 | assert!(resolution.def_id.take_types().is_some()); |
44 | } | 44 | } |
45 | 45 | ||
46 | #[test] | 46 | #[test] |
@@ -59,7 +59,7 @@ fn test_self() { | |||
59 | ); | 59 | ); |
60 | let name = SmolStr::from("Baz"); | 60 | let name = SmolStr::from("Baz"); |
61 | let resolution = &item_map.per_module[&module_id].items[&name]; | 61 | let resolution = &item_map.per_module[&module_id].items[&name]; |
62 | assert!(resolution.def_id.is_some()); | 62 | assert!(resolution.def_id.take_types().is_some()); |
63 | } | 63 | } |
64 | 64 | ||
65 | #[test] | 65 | #[test] |
@@ -92,7 +92,7 @@ fn item_map_across_crates() { | |||
92 | 92 | ||
93 | let name = SmolStr::from("Baz"); | 93 | let name = SmolStr::from("Baz"); |
94 | let resolution = &item_map.per_module[&module_id].items[&name]; | 94 | let resolution = &item_map.per_module[&module_id].items[&name]; |
95 | assert!(resolution.def_id.is_some()); | 95 | assert!(resolution.def_id.take_types().is_some()); |
96 | } | 96 | } |
97 | 97 | ||
98 | #[test] | 98 | #[test] |
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index e04d00900..0b260072c 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs | |||
@@ -1,12 +1,12 @@ | |||
1 | use ra_syntax::{SmolStr, ast, AstNode, TextRange}; | 1 | use ra_syntax::{SmolStr, ast, AstNode, TextRange}; |
2 | 2 | ||
3 | #[derive(Debug, Clone, PartialEq, Eq)] | 3 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
4 | pub struct Path { | 4 | pub struct Path { |
5 | pub kind: PathKind, | 5 | pub kind: PathKind, |
6 | pub segments: Vec<SmolStr>, | 6 | pub segments: Vec<SmolStr>, |
7 | } | 7 | } |
8 | 8 | ||
9 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 9 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
10 | pub enum PathKind { | 10 | pub enum PathKind { |
11 | Plain, | 11 | Plain, |
12 | Self_, | 12 | Self_, |
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs index b654af920..4a7958a12 100644 --- a/crates/ra_hir/src/query_definitions.rs +++ b/crates/ra_hir/src/query_definitions.rs | |||
@@ -19,7 +19,8 @@ use crate::{ | |||
19 | imp::Submodule, | 19 | imp::Submodule, |
20 | nameres::{InputModuleItems, ItemMap, Resolver}, | 20 | nameres::{InputModuleItems, ItemMap, Resolver}, |
21 | }, | 21 | }, |
22 | ty::{self, InferenceResult, Ty} | 22 | ty::{self, InferenceResult, Ty}, |
23 | adt::{StructData, EnumData}, | ||
23 | }; | 24 | }; |
24 | 25 | ||
25 | /// Resolve `FnId` to the corresponding `SyntaxNode` | 26 | /// Resolve `FnId` to the corresponding `SyntaxNode` |
@@ -45,6 +46,32 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<T | |||
45 | ty::type_for_def(db, def_id) | 46 | ty::type_for_def(db, def_id) |
46 | } | 47 | } |
47 | 48 | ||
49 | pub(super) fn type_for_field( | ||
50 | db: &impl HirDatabase, | ||
51 | def_id: DefId, | ||
52 | field: SmolStr, | ||
53 | ) -> Cancelable<Ty> { | ||
54 | ty::type_for_field(db, def_id, field) | ||
55 | } | ||
56 | |||
57 | pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> { | ||
58 | let def_loc = def_id.loc(db); | ||
59 | assert!(def_loc.kind == DefKind::Struct); | ||
60 | let syntax = db.file_item(def_loc.source_item_id); | ||
61 | let struct_def = | ||
62 | ast::StructDef::cast(syntax.borrowed()).expect("struct def should point to StructDef node"); | ||
63 | Ok(Arc::new(StructData::new(struct_def.borrowed()))) | ||
64 | } | ||
65 | |||
66 | pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<EnumData>> { | ||
67 | let def_loc = def_id.loc(db); | ||
68 | assert!(def_loc.kind == DefKind::Enum); | ||
69 | let syntax = db.file_item(def_loc.source_item_id); | ||
70 | let enum_def = | ||
71 | ast::EnumDef::cast(syntax.borrowed()).expect("enum def should point to EnumDef node"); | ||
72 | Ok(Arc::new(EnumData::new(enum_def.borrowed()))) | ||
73 | } | ||
74 | |||
48 | pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { | 75 | pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { |
49 | let mut res = SourceFileItems::new(file_id); | 76 | let mut res = SourceFileItems::new(file_id); |
50 | let source_file = db.source_file(file_id); | 77 | let source_file = db.source_file(file_id); |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index c759d4c8b..67b523c2c 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -11,13 +11,18 @@ use rustc_hash::{FxHashMap}; | |||
11 | use ra_db::{LocalSyntaxPtr, Cancelable}; | 11 | use ra_db::{LocalSyntaxPtr, Cancelable}; |
12 | use ra_syntax::{ | 12 | use ra_syntax::{ |
13 | SmolStr, | 13 | SmolStr, |
14 | ast::{self, AstNode, LoopBodyOwner, ArgListOwner}, | 14 | ast::{self, AstNode, LoopBodyOwner, ArgListOwner, PrefixOp}, |
15 | SyntaxNodeRef | 15 | SyntaxNodeRef |
16 | }; | 16 | }; |
17 | 17 | ||
18 | use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase}; | 18 | use crate::{ |
19 | Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, | ||
20 | db::HirDatabase, | ||
21 | adt::VariantData, | ||
22 | type_ref::{TypeRef, Mutability}, | ||
23 | }; | ||
19 | 24 | ||
20 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | 25 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] |
21 | pub enum Ty { | 26 | pub enum Ty { |
22 | /// The primitive boolean type. Written as `bool`. | 27 | /// The primitive boolean type. Written as `bool`. |
23 | Bool, | 28 | Bool, |
@@ -35,8 +40,15 @@ pub enum Ty { | |||
35 | /// A primitive floating-point type. For example, `f64`. | 40 | /// A primitive floating-point type. For example, `f64`. |
36 | Float(primitive::FloatTy), | 41 | Float(primitive::FloatTy), |
37 | 42 | ||
38 | // Structures, enumerations and unions. | 43 | /// Structures, enumerations and unions. |
39 | // Adt(AdtDef, Substs), | 44 | Adt { |
45 | /// The DefId of the struct/enum. | ||
46 | def_id: DefId, | ||
47 | /// The name, for displaying. | ||
48 | name: SmolStr, | ||
49 | // later we'll need generic substitutions here | ||
50 | }, | ||
51 | |||
40 | /// The pointee of a string slice. Written as `str`. | 52 | /// The pointee of a string slice. Written as `str`. |
41 | Str, | 53 | Str, |
42 | 54 | ||
@@ -45,12 +57,13 @@ pub enum Ty { | |||
45 | /// The pointee of an array slice. Written as `[T]`. | 57 | /// The pointee of an array slice. Written as `[T]`. |
46 | Slice(TyRef), | 58 | Slice(TyRef), |
47 | 59 | ||
48 | // A raw pointer. Written as `*mut T` or `*const T` | 60 | /// A raw pointer. Written as `*mut T` or `*const T` |
49 | // RawPtr(TypeAndMut<'tcx>), | 61 | RawPtr(TyRef, Mutability), |
62 | |||
63 | /// A reference; a pointer with an associated lifetime. Written as | ||
64 | /// `&'a mut T` or `&'a T`. | ||
65 | Ref(TyRef, Mutability), | ||
50 | 66 | ||
51 | // A reference; a pointer with an associated lifetime. Written as | ||
52 | // `&'a mut T` or `&'a T`. | ||
53 | // Ref(Ty<'tcx>, hir::Mutability), | ||
54 | /// A pointer to a function. Written as `fn() -> i32`. | 67 | /// A pointer to a function. Written as `fn() -> i32`. |
55 | /// | 68 | /// |
56 | /// For example the type of `bar` here: | 69 | /// For example the type of `bar` here: |
@@ -107,58 +120,104 @@ pub enum Ty { | |||
107 | 120 | ||
108 | type TyRef = Arc<Ty>; | 121 | type TyRef = Arc<Ty>; |
109 | 122 | ||
110 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | 123 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] |
111 | pub struct FnSig { | 124 | pub struct FnSig { |
112 | input: Vec<Ty>, | 125 | input: Vec<Ty>, |
113 | output: Ty, | 126 | output: Ty, |
114 | } | 127 | } |
115 | 128 | ||
116 | impl Ty { | 129 | impl Ty { |
117 | pub fn new(_db: &impl HirDatabase, node: ast::TypeRef) -> Cancelable<Self> { | 130 | pub(crate) fn from_hir( |
118 | use ra_syntax::ast::TypeRef::*; | 131 | db: &impl HirDatabase, |
119 | Ok(match node { | 132 | module: &Module, |
120 | ParenType(_inner) => Ty::Unknown, // TODO | 133 | type_ref: &TypeRef, |
121 | TupleType(_inner) => Ty::Unknown, // TODO | 134 | ) -> Cancelable<Self> { |
122 | NeverType(..) => Ty::Never, | 135 | Ok(match type_ref { |
123 | PathType(inner) => { | 136 | TypeRef::Never => Ty::Never, |
124 | let path = if let Some(p) = inner.path() { | 137 | TypeRef::Tuple(inner) => { |
125 | p | 138 | let inner_tys = inner |
126 | } else { | 139 | .iter() |
127 | return Ok(Ty::Unknown); | 140 | .map(|tr| Ty::from_hir(db, module, tr)) |
141 | .collect::<Cancelable<_>>()?; | ||
142 | Ty::Tuple(inner_tys) | ||
143 | } | ||
144 | TypeRef::Path(path) => Ty::from_hir_path(db, module, path)?, | ||
145 | TypeRef::RawPtr(inner, mutability) => { | ||
146 | let inner_ty = Ty::from_hir(db, module, inner)?; | ||
147 | Ty::RawPtr(Arc::new(inner_ty), *mutability) | ||
148 | } | ||
149 | TypeRef::Array(_inner) => Ty::Unknown, // TODO | ||
150 | TypeRef::Slice(inner) => { | ||
151 | let inner_ty = Ty::from_hir(db, module, inner)?; | ||
152 | Ty::Slice(Arc::new(inner_ty)) | ||
153 | } | ||
154 | TypeRef::Reference(inner, mutability) => { | ||
155 | let inner_ty = Ty::from_hir(db, module, inner)?; | ||
156 | Ty::Ref(Arc::new(inner_ty), *mutability) | ||
157 | } | ||
158 | TypeRef::Placeholder => Ty::Unknown, // TODO | ||
159 | TypeRef::Fn(params) => { | ||
160 | let mut inner_tys = params | ||
161 | .iter() | ||
162 | .map(|tr| Ty::from_hir(db, module, tr)) | ||
163 | .collect::<Cancelable<Vec<_>>>()?; | ||
164 | let return_ty = inner_tys | ||
165 | .pop() | ||
166 | .expect("TypeRef::Fn should always have at least return type"); | ||
167 | let sig = FnSig { | ||
168 | input: inner_tys, | ||
169 | output: return_ty, | ||
128 | }; | 170 | }; |
129 | if path.qualifier().is_none() { | 171 | Ty::FnPtr(Arc::new(sig)) |
130 | let name = path | ||
131 | .segment() | ||
132 | .and_then(|s| s.name_ref()) | ||
133 | .map(|n| n.text()) | ||
134 | .unwrap_or(SmolStr::new("")); | ||
135 | if let Some(int_ty) = primitive::IntTy::from_string(&name) { | ||
136 | Ty::Int(int_ty) | ||
137 | } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) { | ||
138 | Ty::Uint(uint_ty) | ||
139 | } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) { | ||
140 | Ty::Float(float_ty) | ||
141 | } else { | ||
142 | // TODO | ||
143 | Ty::Unknown | ||
144 | } | ||
145 | } else { | ||
146 | // TODO | ||
147 | Ty::Unknown | ||
148 | } | ||
149 | } | 172 | } |
150 | PointerType(_inner) => Ty::Unknown, // TODO | 173 | TypeRef::Error => Ty::Unknown, |
151 | ArrayType(_inner) => Ty::Unknown, // TODO | ||
152 | SliceType(_inner) => Ty::Unknown, // TODO | ||
153 | ReferenceType(_inner) => Ty::Unknown, // TODO | ||
154 | PlaceholderType(_inner) => Ty::Unknown, // TODO | ||
155 | FnPointerType(_inner) => Ty::Unknown, // TODO | ||
156 | ForType(_inner) => Ty::Unknown, // TODO | ||
157 | ImplTraitType(_inner) => Ty::Unknown, // TODO | ||
158 | DynTraitType(_inner) => Ty::Unknown, // TODO | ||
159 | }) | 174 | }) |
160 | } | 175 | } |
161 | 176 | ||
177 | pub(crate) fn from_hir_path( | ||
178 | db: &impl HirDatabase, | ||
179 | module: &Module, | ||
180 | path: &Path, | ||
181 | ) -> Cancelable<Self> { | ||
182 | if path.is_ident() { | ||
183 | let name = &path.segments[0]; | ||
184 | if let Some(int_ty) = primitive::IntTy::from_string(&name) { | ||
185 | return Ok(Ty::Int(int_ty)); | ||
186 | } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) { | ||
187 | return Ok(Ty::Uint(uint_ty)); | ||
188 | } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) { | ||
189 | return Ok(Ty::Float(float_ty)); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | // Resolve in module (in type namespace) | ||
194 | let resolved = if let Some(r) = module.resolve_path(db, path)?.take_types() { | ||
195 | r | ||
196 | } else { | ||
197 | return Ok(Ty::Unknown); | ||
198 | }; | ||
199 | let ty = db.type_for_def(resolved)?; | ||
200 | Ok(ty) | ||
201 | } | ||
202 | |||
203 | // TODO: These should not be necessary long-term, since everything will work on HIR | ||
204 | pub(crate) fn from_ast_opt( | ||
205 | db: &impl HirDatabase, | ||
206 | module: &Module, | ||
207 | node: Option<ast::TypeRef>, | ||
208 | ) -> Cancelable<Self> { | ||
209 | node.map(|n| Ty::from_ast(db, module, n)) | ||
210 | .unwrap_or(Ok(Ty::Unknown)) | ||
211 | } | ||
212 | |||
213 | pub(crate) fn from_ast( | ||
214 | db: &impl HirDatabase, | ||
215 | module: &Module, | ||
216 | node: ast::TypeRef, | ||
217 | ) -> Cancelable<Self> { | ||
218 | Ty::from_hir(db, module, &TypeRef::from_ast(node)) | ||
219 | } | ||
220 | |||
162 | pub fn unit() -> Self { | 221 | pub fn unit() -> Self { |
163 | Ty::Tuple(Vec::new()) | 222 | Ty::Tuple(Vec::new()) |
164 | } | 223 | } |
@@ -174,6 +233,8 @@ impl fmt::Display for Ty { | |||
174 | Ty::Float(t) => write!(f, "{}", t.ty_to_string()), | 233 | Ty::Float(t) => write!(f, "{}", t.ty_to_string()), |
175 | Ty::Str => write!(f, "str"), | 234 | Ty::Str => write!(f, "str"), |
176 | Ty::Slice(t) => write!(f, "[{}]", t), | 235 | Ty::Slice(t) => write!(f, "[{}]", t), |
236 | Ty::RawPtr(t, m) => write!(f, "*{}{}", m.as_keyword_for_ptr(), t), | ||
237 | Ty::Ref(t, m) => write!(f, "&{}{}", m.as_keyword_for_ref(), t), | ||
177 | Ty::Never => write!(f, "!"), | 238 | Ty::Never => write!(f, "!"), |
178 | Ty::Tuple(ts) => { | 239 | Ty::Tuple(ts) => { |
179 | write!(f, "(")?; | 240 | write!(f, "(")?; |
@@ -189,6 +250,7 @@ impl fmt::Display for Ty { | |||
189 | } | 250 | } |
190 | write!(f, ") -> {}", sig.output) | 251 | write!(f, ") -> {}", sig.output) |
191 | } | 252 | } |
253 | Ty::Adt { name, .. } => write!(f, "{}", name), | ||
192 | Ty::Unknown => write!(f, "[unknown]"), | 254 | Ty::Unknown => write!(f, "[unknown]"), |
193 | } | 255 | } |
194 | } | 256 | } |
@@ -196,34 +258,40 @@ impl fmt::Display for Ty { | |||
196 | 258 | ||
197 | pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> { | 259 | pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> { |
198 | let syntax = f.syntax(db); | 260 | let syntax = f.syntax(db); |
261 | let module = f.module(db)?; | ||
199 | let node = syntax.borrowed(); | 262 | let node = syntax.borrowed(); |
200 | // TODO we ignore type parameters for now | 263 | // TODO we ignore type parameters for now |
201 | let input = node | 264 | let input = node |
202 | .param_list() | 265 | .param_list() |
203 | .map(|pl| { | 266 | .map(|pl| { |
204 | pl.params() | 267 | pl.params() |
205 | .map(|p| { | 268 | .map(|p| Ty::from_ast_opt(db, &module, p.type_ref())) |
206 | p.type_ref() | ||
207 | .map(|t| Ty::new(db, t)) | ||
208 | .unwrap_or(Ok(Ty::Unknown)) | ||
209 | }) | ||
210 | .collect() | 269 | .collect() |
211 | }) | 270 | }) |
212 | .unwrap_or_else(|| Ok(Vec::new()))?; | 271 | .unwrap_or_else(|| Ok(Vec::new()))?; |
213 | let output = node | 272 | let output = Ty::from_ast_opt(db, &module, node.ret_type().and_then(|rt| rt.type_ref()))?; |
214 | .ret_type() | ||
215 | .and_then(|rt| rt.type_ref()) | ||
216 | .map(|t| Ty::new(db, t)) | ||
217 | .unwrap_or(Ok(Ty::Unknown))?; | ||
218 | let sig = FnSig { input, output }; | 273 | let sig = FnSig { input, output }; |
219 | Ok(Ty::FnPtr(Arc::new(sig))) | 274 | Ok(Ty::FnPtr(Arc::new(sig))) |
220 | } | 275 | } |
221 | 276 | ||
222 | // TODO this should probably be per namespace (i.e. types vs. values), since for | 277 | pub fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable<Ty> { |
223 | // a tuple struct `struct Foo(Bar)`, Foo has function type as a value, but | 278 | Ok(Ty::Adt { |
224 | // defines the struct type Foo when used in the type namespace. rustc has a | 279 | def_id: s.def_id(), |
225 | // separate DefId for the constructor, but with the current DefId approach, that | 280 | name: s |
226 | // seems complicated. | 281 | .name(db)? |
282 | .unwrap_or_else(|| SmolStr::new("[unnamed struct]")), | ||
283 | }) | ||
284 | } | ||
285 | |||
286 | pub fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable<Ty> { | ||
287 | Ok(Ty::Adt { | ||
288 | def_id: s.def_id(), | ||
289 | name: s | ||
290 | .name(db)? | ||
291 | .unwrap_or_else(|| SmolStr::new("[unnamed enum]")), | ||
292 | }) | ||
293 | } | ||
294 | |||
227 | pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { | 295 | pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { |
228 | let def = def_id.resolve(db)?; | 296 | let def = def_id.resolve(db)?; |
229 | match def { | 297 | match def { |
@@ -232,6 +300,8 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { | |||
232 | Ok(Ty::Unknown) | 300 | Ok(Ty::Unknown) |
233 | } | 301 | } |
234 | Def::Function(f) => type_for_fn(db, f), | 302 | Def::Function(f) => type_for_fn(db, f), |
303 | Def::Struct(s) => type_for_struct(db, s), | ||
304 | Def::Enum(e) => type_for_enum(db, e), | ||
235 | Def::Item => { | 305 | Def::Item => { |
236 | log::debug!("trying to get type for item of unknown type {:?}", def_id); | 306 | log::debug!("trying to get type for item of unknown type {:?}", def_id); |
237 | Ok(Ty::Unknown) | 307 | Ok(Ty::Unknown) |
@@ -239,6 +309,33 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { | |||
239 | } | 309 | } |
240 | } | 310 | } |
241 | 311 | ||
312 | pub(super) fn type_for_field( | ||
313 | db: &impl HirDatabase, | ||
314 | def_id: DefId, | ||
315 | field: SmolStr, | ||
316 | ) -> Cancelable<Ty> { | ||
317 | let def = def_id.resolve(db)?; | ||
318 | let variant_data = match def { | ||
319 | Def::Struct(s) => { | ||
320 | let variant_data = s.variant_data(db)?; | ||
321 | variant_data | ||
322 | } | ||
323 | // TODO: unions | ||
324 | // TODO: enum variants | ||
325 | _ => panic!( | ||
326 | "trying to get type for field in non-struct/variant {:?}", | ||
327 | def_id | ||
328 | ), | ||
329 | }; | ||
330 | let module = def_id.module(db)?; | ||
331 | let type_ref = if let Some(tr) = variant_data.get_field_type_ref(&field) { | ||
332 | tr | ||
333 | } else { | ||
334 | return Ok(Ty::Unknown); | ||
335 | }; | ||
336 | Ty::from_hir(db, &module, &type_ref) | ||
337 | } | ||
338 | |||
242 | #[derive(Clone, PartialEq, Eq, Debug)] | 339 | #[derive(Clone, PartialEq, Eq, Debug)] |
243 | pub struct InferenceResult { | 340 | pub struct InferenceResult { |
244 | type_of: FxHashMap<LocalSyntaxPtr, Ty>, | 341 | type_of: FxHashMap<LocalSyntaxPtr, Ty>, |
@@ -305,32 +402,54 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
305 | }; | 402 | }; |
306 | 403 | ||
307 | // resolve in module | 404 | // resolve in module |
308 | let resolved = ctry!(self.module.resolve_path(self.db, path)?); | 405 | let resolved = ctry!(self.module.resolve_path(self.db, &path)?.take_values()); |
309 | let ty = self.db.type_for_def(resolved)?; | 406 | let ty = self.db.type_for_def(resolved)?; |
310 | // TODO we will need to add type variables for type parameters etc. here | 407 | // TODO we will need to add type variables for type parameters etc. here |
311 | Ok(Some(ty)) | 408 | Ok(Some(ty)) |
312 | } | 409 | } |
313 | 410 | ||
411 | fn resolve_variant( | ||
412 | &self, | ||
413 | path: Option<ast::Path>, | ||
414 | ) -> Cancelable<(Ty, Option<Arc<VariantData>>)> { | ||
415 | let path = if let Some(path) = path.and_then(Path::from_ast) { | ||
416 | path | ||
417 | } else { | ||
418 | return Ok((Ty::Unknown, None)); | ||
419 | }; | ||
420 | let def_id = if let Some(def_id) = self.module.resolve_path(self.db, &path)?.take_types() { | ||
421 | def_id | ||
422 | } else { | ||
423 | return Ok((Ty::Unknown, None)); | ||
424 | }; | ||
425 | Ok(match def_id.resolve(self.db)? { | ||
426 | Def::Struct(s) => { | ||
427 | let struct_data = self.db.struct_data(def_id)?; | ||
428 | let ty = type_for_struct(self.db, s)?; | ||
429 | (ty, Some(struct_data.variant_data().clone())) | ||
430 | } | ||
431 | _ => (Ty::Unknown, None), | ||
432 | }) | ||
433 | } | ||
434 | |||
435 | fn infer_expr_opt(&mut self, expr: Option<ast::Expr>) -> Cancelable<Ty> { | ||
436 | if let Some(e) = expr { | ||
437 | self.infer_expr(e) | ||
438 | } else { | ||
439 | Ok(Ty::Unknown) | ||
440 | } | ||
441 | } | ||
442 | |||
314 | fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> { | 443 | fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> { |
315 | let ty = match expr { | 444 | let ty = match expr { |
316 | ast::Expr::IfExpr(e) => { | 445 | ast::Expr::IfExpr(e) => { |
317 | if let Some(condition) = e.condition() { | 446 | if let Some(condition) = e.condition() { |
318 | if let Some(e) = condition.expr() { | 447 | // TODO if no pat, this should be bool |
319 | // TODO if no pat, this should be bool | 448 | self.infer_expr_opt(condition.expr())?; |
320 | self.infer_expr(e)?; | ||
321 | } | ||
322 | // TODO write type for pat | 449 | // TODO write type for pat |
323 | }; | 450 | }; |
324 | let if_ty = if let Some(block) = e.then_branch() { | 451 | let if_ty = self.infer_block_opt(e.then_branch())?; |
325 | self.infer_block(block)? | 452 | let else_ty = self.infer_block_opt(e.else_branch())?; |
326 | } else { | ||
327 | Ty::Unknown | ||
328 | }; | ||
329 | let else_ty = if let Some(block) = e.else_branch() { | ||
330 | self.infer_block(block)? | ||
331 | } else { | ||
332 | Ty::Unknown | ||
333 | }; | ||
334 | if let Some(ty) = self.unify(&if_ty, &else_ty) { | 453 | if let Some(ty) = self.unify(&if_ty, &else_ty) { |
335 | ty | 454 | ty |
336 | } else { | 455 | } else { |
@@ -338,62 +457,37 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
338 | Ty::Unknown | 457 | Ty::Unknown |
339 | } | 458 | } |
340 | } | 459 | } |
341 | ast::Expr::BlockExpr(e) => { | 460 | ast::Expr::BlockExpr(e) => self.infer_block_opt(e.block())?, |
342 | if let Some(block) = e.block() { | ||
343 | self.infer_block(block)? | ||
344 | } else { | ||
345 | Ty::Unknown | ||
346 | } | ||
347 | } | ||
348 | ast::Expr::LoopExpr(e) => { | 461 | ast::Expr::LoopExpr(e) => { |
349 | if let Some(block) = e.loop_body() { | 462 | self.infer_block_opt(e.loop_body())?; |
350 | self.infer_block(block)?; | ||
351 | }; | ||
352 | // TODO never, or the type of the break param | 463 | // TODO never, or the type of the break param |
353 | Ty::Unknown | 464 | Ty::Unknown |
354 | } | 465 | } |
355 | ast::Expr::WhileExpr(e) => { | 466 | ast::Expr::WhileExpr(e) => { |
356 | if let Some(condition) = e.condition() { | 467 | if let Some(condition) = e.condition() { |
357 | if let Some(e) = condition.expr() { | 468 | // TODO if no pat, this should be bool |
358 | // TODO if no pat, this should be bool | 469 | self.infer_expr_opt(condition.expr())?; |
359 | self.infer_expr(e)?; | ||
360 | } | ||
361 | // TODO write type for pat | 470 | // TODO write type for pat |
362 | }; | 471 | }; |
363 | if let Some(block) = e.loop_body() { | 472 | self.infer_block_opt(e.loop_body())?; |
364 | // TODO | ||
365 | self.infer_block(block)?; | ||
366 | }; | ||
367 | // TODO always unit? | 473 | // TODO always unit? |
368 | Ty::Unknown | 474 | Ty::Unknown |
369 | } | 475 | } |
370 | ast::Expr::ForExpr(e) => { | 476 | ast::Expr::ForExpr(e) => { |
371 | if let Some(expr) = e.iterable() { | 477 | let _iterable_ty = self.infer_expr_opt(e.iterable()); |
372 | self.infer_expr(expr)?; | ||
373 | } | ||
374 | if let Some(_pat) = e.pat() { | 478 | if let Some(_pat) = e.pat() { |
375 | // TODO write type for pat | 479 | // TODO write type for pat |
376 | } | 480 | } |
377 | if let Some(block) = e.loop_body() { | 481 | self.infer_block_opt(e.loop_body())?; |
378 | self.infer_block(block)?; | ||
379 | } | ||
380 | // TODO always unit? | 482 | // TODO always unit? |
381 | Ty::Unknown | 483 | Ty::Unknown |
382 | } | 484 | } |
383 | ast::Expr::LambdaExpr(e) => { | 485 | ast::Expr::LambdaExpr(e) => { |
384 | let _body_ty = if let Some(body) = e.body() { | 486 | let _body_ty = self.infer_expr_opt(e.body())?; |
385 | self.infer_expr(body)? | ||
386 | } else { | ||
387 | Ty::Unknown | ||
388 | }; | ||
389 | Ty::Unknown | 487 | Ty::Unknown |
390 | } | 488 | } |
391 | ast::Expr::CallExpr(e) => { | 489 | ast::Expr::CallExpr(e) => { |
392 | let callee_ty = if let Some(e) = e.expr() { | 490 | let callee_ty = self.infer_expr_opt(e.expr())?; |
393 | self.infer_expr(e)? | ||
394 | } else { | ||
395 | Ty::Unknown | ||
396 | }; | ||
397 | if let Some(arg_list) = e.arg_list() { | 491 | if let Some(arg_list) = e.arg_list() { |
398 | for arg in arg_list.args() { | 492 | for arg in arg_list.args() { |
399 | // TODO unify / expect argument type | 493 | // TODO unify / expect argument type |
@@ -410,11 +504,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
410 | } | 504 | } |
411 | } | 505 | } |
412 | ast::Expr::MethodCallExpr(e) => { | 506 | ast::Expr::MethodCallExpr(e) => { |
413 | let _receiver_ty = if let Some(e) = e.expr() { | 507 | let _receiver_ty = self.infer_expr_opt(e.expr())?; |
414 | self.infer_expr(e)? | ||
415 | } else { | ||
416 | Ty::Unknown | ||
417 | }; | ||
418 | if let Some(arg_list) = e.arg_list() { | 508 | if let Some(arg_list) = e.arg_list() { |
419 | for arg in arg_list.args() { | 509 | for arg in arg_list.args() { |
420 | // TODO unify / expect argument type | 510 | // TODO unify / expect argument type |
@@ -424,20 +514,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
424 | Ty::Unknown | 514 | Ty::Unknown |
425 | } | 515 | } |
426 | ast::Expr::MatchExpr(e) => { | 516 | ast::Expr::MatchExpr(e) => { |
427 | let _ty = if let Some(match_expr) = e.expr() { | 517 | let _ty = self.infer_expr_opt(e.expr())?; |
428 | self.infer_expr(match_expr)? | ||
429 | } else { | ||
430 | Ty::Unknown | ||
431 | }; | ||
432 | if let Some(match_arm_list) = e.match_arm_list() { | 518 | if let Some(match_arm_list) = e.match_arm_list() { |
433 | for arm in match_arm_list.arms() { | 519 | for arm in match_arm_list.arms() { |
434 | // TODO type the bindings in pat | 520 | // TODO type the bindings in pat |
435 | // TODO type the guard | 521 | // TODO type the guard |
436 | let _ty = if let Some(e) = arm.expr() { | 522 | let _ty = self.infer_expr_opt(arm.expr())?; |
437 | self.infer_expr(e)? | ||
438 | } else { | ||
439 | Ty::Unknown | ||
440 | }; | ||
441 | } | 523 | } |
442 | // TODO unify all the match arm types | 524 | // TODO unify all the match arm types |
443 | Ty::Unknown | 525 | Ty::Unknown |
@@ -450,68 +532,78 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
450 | ast::Expr::PathExpr(e) => self.infer_path_expr(e)?.unwrap_or(Ty::Unknown), | 532 | ast::Expr::PathExpr(e) => self.infer_path_expr(e)?.unwrap_or(Ty::Unknown), |
451 | ast::Expr::ContinueExpr(_e) => Ty::Never, | 533 | ast::Expr::ContinueExpr(_e) => Ty::Never, |
452 | ast::Expr::BreakExpr(_e) => Ty::Never, | 534 | ast::Expr::BreakExpr(_e) => Ty::Never, |
453 | ast::Expr::ParenExpr(e) => { | 535 | ast::Expr::ParenExpr(e) => self.infer_expr_opt(e.expr())?, |
454 | if let Some(e) = e.expr() { | ||
455 | self.infer_expr(e)? | ||
456 | } else { | ||
457 | Ty::Unknown | ||
458 | } | ||
459 | } | ||
460 | ast::Expr::Label(_e) => Ty::Unknown, | 536 | ast::Expr::Label(_e) => Ty::Unknown, |
461 | ast::Expr::ReturnExpr(e) => { | 537 | ast::Expr::ReturnExpr(e) => { |
462 | if let Some(e) = e.expr() { | 538 | self.infer_expr_opt(e.expr())?; |
463 | // TODO unify with return type | ||
464 | self.infer_expr(e)?; | ||
465 | }; | ||
466 | Ty::Never | 539 | Ty::Never |
467 | } | 540 | } |
468 | ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => { | 541 | ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => { |
469 | // Can this even occur outside of a match expression? | 542 | // Can this even occur outside of a match expression? |
470 | Ty::Unknown | 543 | Ty::Unknown |
471 | } | 544 | } |
472 | ast::Expr::StructLit(_e) => Ty::Unknown, | 545 | ast::Expr::StructLit(e) => { |
546 | let (ty, _variant_data) = self.resolve_variant(e.path())?; | ||
547 | if let Some(nfl) = e.named_field_list() { | ||
548 | for field in nfl.fields() { | ||
549 | // TODO unify with / expect field type | ||
550 | self.infer_expr_opt(field.expr())?; | ||
551 | } | ||
552 | } | ||
553 | ty | ||
554 | } | ||
473 | ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => { | 555 | ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => { |
474 | // Can this even occur outside of a struct literal? | 556 | // Can this even occur outside of a struct literal? |
475 | Ty::Unknown | 557 | Ty::Unknown |
476 | } | 558 | } |
477 | ast::Expr::IndexExpr(_e) => Ty::Unknown, | 559 | ast::Expr::IndexExpr(_e) => Ty::Unknown, |
478 | ast::Expr::FieldExpr(_e) => Ty::Unknown, | 560 | ast::Expr::FieldExpr(e) => { |
479 | ast::Expr::TryExpr(e) => { | 561 | let receiver_ty = self.infer_expr_opt(e.expr())?; |
480 | let _inner_ty = if let Some(e) = e.expr() { | 562 | if let Some(nr) = e.name_ref() { |
481 | self.infer_expr(e)? | 563 | let text = nr.text(); |
564 | match receiver_ty { | ||
565 | Ty::Tuple(fields) => { | ||
566 | let i = text.parse::<usize>().ok(); | ||
567 | i.and_then(|i| fields.get(i).cloned()) | ||
568 | .unwrap_or(Ty::Unknown) | ||
569 | } | ||
570 | Ty::Adt { def_id, .. } => self.db.type_for_field(def_id, text)?, | ||
571 | _ => Ty::Unknown, | ||
572 | } | ||
482 | } else { | 573 | } else { |
483 | Ty::Unknown | 574 | Ty::Unknown |
484 | }; | 575 | } |
576 | } | ||
577 | ast::Expr::TryExpr(e) => { | ||
578 | let _inner_ty = self.infer_expr_opt(e.expr())?; | ||
485 | Ty::Unknown | 579 | Ty::Unknown |
486 | } | 580 | } |
487 | ast::Expr::CastExpr(e) => { | 581 | ast::Expr::CastExpr(e) => { |
488 | let _inner_ty = if let Some(e) = e.expr() { | 582 | let _inner_ty = self.infer_expr_opt(e.expr())?; |
489 | self.infer_expr(e)? | 583 | let cast_ty = Ty::from_ast_opt(self.db, &self.module, e.type_ref())?; |
490 | } else { | ||
491 | Ty::Unknown | ||
492 | }; | ||
493 | let cast_ty = e | ||
494 | .type_ref() | ||
495 | .map(|t| Ty::new(self.db, t)) | ||
496 | .unwrap_or(Ok(Ty::Unknown))?; | ||
497 | // TODO do the coercion... | 584 | // TODO do the coercion... |
498 | cast_ty | 585 | cast_ty |
499 | } | 586 | } |
500 | ast::Expr::RefExpr(e) => { | 587 | ast::Expr::RefExpr(e) => { |
501 | let _inner_ty = if let Some(e) = e.expr() { | 588 | let inner_ty = self.infer_expr_opt(e.expr())?; |
502 | self.infer_expr(e)? | 589 | let m = Mutability::from_mutable(e.is_mut()); |
503 | } else { | 590 | // TODO reference coercions etc. |
504 | Ty::Unknown | 591 | Ty::Ref(Arc::new(inner_ty), m) |
505 | }; | ||
506 | Ty::Unknown | ||
507 | } | 592 | } |
508 | ast::Expr::PrefixExpr(e) => { | 593 | ast::Expr::PrefixExpr(e) => { |
509 | let _inner_ty = if let Some(e) = e.expr() { | 594 | let inner_ty = self.infer_expr_opt(e.expr())?; |
510 | self.infer_expr(e)? | 595 | match e.op() { |
511 | } else { | 596 | Some(PrefixOp::Deref) => { |
512 | Ty::Unknown | 597 | match inner_ty { |
513 | }; | 598 | // builtin deref: |
514 | Ty::Unknown | 599 | Ty::Ref(ref_inner, _) => (*ref_inner).clone(), |
600 | Ty::RawPtr(ptr_inner, _) => (*ptr_inner).clone(), | ||
601 | // TODO Deref::deref | ||
602 | _ => Ty::Unknown, | ||
603 | } | ||
604 | } | ||
605 | _ => Ty::Unknown, | ||
606 | } | ||
515 | } | 607 | } |
516 | ast::Expr::RangeExpr(_e) => Ty::Unknown, | 608 | ast::Expr::RangeExpr(_e) => Ty::Unknown, |
517 | ast::Expr::BinExpr(_e) => Ty::Unknown, | 609 | ast::Expr::BinExpr(_e) => Ty::Unknown, |
@@ -521,15 +613,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
521 | Ok(ty) | 613 | Ok(ty) |
522 | } | 614 | } |
523 | 615 | ||
616 | fn infer_block_opt(&mut self, node: Option<ast::Block>) -> Cancelable<Ty> { | ||
617 | if let Some(b) = node { | ||
618 | self.infer_block(b) | ||
619 | } else { | ||
620 | Ok(Ty::Unknown) | ||
621 | } | ||
622 | } | ||
623 | |||
524 | fn infer_block(&mut self, node: ast::Block) -> Cancelable<Ty> { | 624 | fn infer_block(&mut self, node: ast::Block) -> Cancelable<Ty> { |
525 | for stmt in node.statements() { | 625 | for stmt in node.statements() { |
526 | match stmt { | 626 | match stmt { |
527 | ast::Stmt::LetStmt(stmt) => { | 627 | ast::Stmt::LetStmt(stmt) => { |
528 | let decl_ty = if let Some(type_ref) = stmt.type_ref() { | 628 | let decl_ty = Ty::from_ast_opt(self.db, &self.module, stmt.type_ref())?; |
529 | Ty::new(self.db, type_ref)? | ||
530 | } else { | ||
531 | Ty::Unknown | ||
532 | }; | ||
533 | let ty = if let Some(expr) = stmt.initializer() { | 629 | let ty = if let Some(expr) = stmt.initializer() { |
534 | // TODO pass expectation | 630 | // TODO pass expectation |
535 | let expr_ty = self.infer_expr(expr)?; | 631 | let expr_ty = self.infer_expr(expr)?; |
@@ -544,9 +640,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
544 | }; | 640 | }; |
545 | } | 641 | } |
546 | ast::Stmt::ExprStmt(expr_stmt) => { | 642 | ast::Stmt::ExprStmt(expr_stmt) => { |
547 | if let Some(expr) = expr_stmt.expr() { | 643 | self.infer_expr_opt(expr_stmt.expr())?; |
548 | self.infer_expr(expr)?; | ||
549 | } | ||
550 | } | 644 | } |
551 | } | 645 | } |
552 | } | 646 | } |
@@ -576,7 +670,7 @@ pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceR | |||
576 | continue; | 670 | continue; |
577 | }; | 671 | }; |
578 | if let Some(type_ref) = param.type_ref() { | 672 | if let Some(type_ref) = param.type_ref() { |
579 | let ty = Ty::new(db, type_ref)?; | 673 | let ty = Ty::from_ast(db, &ctx.module, type_ref)?; |
580 | ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); | 674 | ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); |
581 | } else { | 675 | } else { |
582 | // TODO self param | 676 | // TODO self param |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index b6c02cd80..a76925b58 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -68,6 +68,51 @@ fn test() { | |||
68 | ); | 68 | ); |
69 | } | 69 | } |
70 | 70 | ||
71 | #[test] | ||
72 | fn infer_struct() { | ||
73 | check_inference( | ||
74 | r#" | ||
75 | struct A { | ||
76 | b: B, | ||
77 | c: C, | ||
78 | } | ||
79 | struct B; | ||
80 | struct C(usize); | ||
81 | |||
82 | fn test() { | ||
83 | let c = C(1); | ||
84 | B; | ||
85 | let a: A = A { b: B, c: C(1) }; | ||
86 | a.b; | ||
87 | a.c; | ||
88 | } | ||
89 | "#, | ||
90 | "0004_struct.txt", | ||
91 | ); | ||
92 | } | ||
93 | |||
94 | #[test] | ||
95 | fn infer_refs_and_ptrs() { | ||
96 | check_inference( | ||
97 | r#" | ||
98 | fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { | ||
99 | a; | ||
100 | *a; | ||
101 | &a; | ||
102 | &mut a; | ||
103 | b; | ||
104 | *b; | ||
105 | &b; | ||
106 | c; | ||
107 | *c; | ||
108 | d; | ||
109 | *d; | ||
110 | } | ||
111 | "#, | ||
112 | "0005_refs.txt", | ||
113 | ); | ||
114 | } | ||
115 | |||
71 | fn infer(content: &str) -> String { | 116 | fn infer(content: &str) -> String { |
72 | let (db, _, file_id) = MockDatabase::with_single_file(content); | 117 | let (db, _, file_id) = MockDatabase::with_single_file(content); |
73 | let source_file = db.source_file(file_id); | 118 | let source_file = db.source_file(file_id); |
diff --git a/crates/ra_hir/src/ty/tests/data/0001_basics.txt b/crates/ra_hir/src/ty/tests/data/0001_basics.txt index 0c46f243a..212e92e00 100644 --- a/crates/ra_hir/src/ty/tests/data/0001_basics.txt +++ b/crates/ra_hir/src/ty/tests/data/0001_basics.txt | |||
@@ -1,4 +1,4 @@ | |||
1 | [33; 34) 'd': [unknown] | 1 | [33; 34) 'd': &[unknown] |
2 | [88; 94) '1isize': [unknown] | 2 | [88; 94) '1isize': [unknown] |
3 | [48; 49) 'a': u32 | 3 | [48; 49) 'a': u32 |
4 | [55; 56) 'b': isize | 4 | [55; 56) 'b': isize |
@@ -10,4 +10,4 @@ | |||
10 | [17; 18) 'b': isize | 10 | [17; 18) 'b': isize |
11 | [100; 106) '"test"': [unknown] | 11 | [100; 106) '"test"': [unknown] |
12 | [42; 121) '{ ...f32; }': () | 12 | [42; 121) '{ ...f32; }': () |
13 | [69; 70) 'd': [unknown] | 13 | [69; 70) 'd': &[unknown] |
diff --git a/crates/ra_hir/src/ty/tests/data/0004_struct.txt b/crates/ra_hir/src/ty/tests/data/0004_struct.txt new file mode 100644 index 000000000..cc8f3665b --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/0004_struct.txt | |||
@@ -0,0 +1,16 @@ | |||
1 | [86; 90) 'C(1)': [unknown] | ||
2 | [121; 122) 'B': [unknown] | ||
3 | [86; 87) 'C': [unknown] | ||
4 | [129; 130) '1': [unknown] | ||
5 | [107; 108) 'a': A | ||
6 | [127; 128) 'C': [unknown] | ||
7 | [139; 142) 'a.b': B | ||
8 | [114; 133) 'A { b:...C(1) }': A | ||
9 | [148; 151) 'a.c': C | ||
10 | [148; 149) 'a': A | ||
11 | [139; 140) 'a': A | ||
12 | [72; 154) '{ ...a.c; }': () | ||
13 | [96; 97) 'B': [unknown] | ||
14 | [88; 89) '1': [unknown] | ||
15 | [82; 83) 'c': [unknown] | ||
16 | [127; 131) 'C(1)': [unknown] | ||
diff --git a/crates/ra_hir/src/ty/tests/data/0005_refs.txt b/crates/ra_hir/src/ty/tests/data/0005_refs.txt new file mode 100644 index 000000000..296e955c1 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/0005_refs.txt | |||
@@ -0,0 +1,23 @@ | |||
1 | [115; 117) '&b': &&mut u32 | ||
2 | [88; 94) '&mut a': &mut &u32 | ||
3 | [146; 147) 'd': *mut u32 | ||
4 | [145; 147) '*d': u32 | ||
5 | [65; 66) 'a': &u32 | ||
6 | [46; 47) 'd': *mut u32 | ||
7 | [59; 150) '{ ... *d; }': () | ||
8 | [116; 117) 'b': &mut u32 | ||
9 | [131; 132) 'c': *const u32 | ||
10 | [130; 132) '*c': u32 | ||
11 | [72; 74) '*a': u32 | ||
12 | [107; 109) '*b': u32 | ||
13 | [108; 109) 'b': &mut u32 | ||
14 | [9; 10) 'a': &u32 | ||
15 | [18; 19) 'b': &mut u32 | ||
16 | [93; 94) 'a': &u32 | ||
17 | [100; 101) 'b': &mut u32 | ||
18 | [81; 82) 'a': &u32 | ||
19 | [80; 82) '&a': &&u32 | ||
20 | [73; 74) 'a': &u32 | ||
21 | [123; 124) 'c': *const u32 | ||
22 | [31; 32) 'c': *const u32 | ||
23 | [138; 139) 'd': *mut u32 | ||
diff --git a/crates/ra_hir/src/type_ref.rs b/crates/ra_hir/src/type_ref.rs new file mode 100644 index 000000000..b36bb35d8 --- /dev/null +++ b/crates/ra_hir/src/type_ref.rs | |||
@@ -0,0 +1,110 @@ | |||
1 | //! HIR for references to types. Paths in these are not yet resolved. They can | ||
2 | //! be directly created from an ast::TypeRef, without further queries. | ||
3 | |||
4 | use ra_syntax::ast; | ||
5 | |||
6 | use crate::Path; | ||
7 | |||
8 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||
9 | pub enum Mutability { | ||
10 | Shared, | ||
11 | Mut, | ||
12 | } | ||
13 | |||
14 | impl Mutability { | ||
15 | pub fn from_mutable(mutable: bool) -> Mutability { | ||
16 | if mutable { | ||
17 | Mutability::Mut | ||
18 | } else { | ||
19 | Mutability::Shared | ||
20 | } | ||
21 | } | ||
22 | |||
23 | pub fn as_keyword_for_ref(self) -> &'static str { | ||
24 | match self { | ||
25 | Mutability::Shared => "", | ||
26 | Mutability::Mut => "mut ", | ||
27 | } | ||
28 | } | ||
29 | |||
30 | pub fn as_keyword_for_ptr(self) -> &'static str { | ||
31 | match self { | ||
32 | Mutability::Shared => "const ", | ||
33 | Mutability::Mut => "mut ", | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | |||
38 | /// Compare ty::Ty | ||
39 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | ||
40 | pub enum TypeRef { | ||
41 | Never, | ||
42 | Placeholder, | ||
43 | Tuple(Vec<TypeRef>), | ||
44 | Path(Path), | ||
45 | RawPtr(Box<TypeRef>, Mutability), | ||
46 | Reference(Box<TypeRef>, Mutability), | ||
47 | Array(Box<TypeRef> /*, Expr*/), | ||
48 | Slice(Box<TypeRef>), | ||
49 | /// A fn pointer. Last element of the vector is the return type. | ||
50 | Fn(Vec<TypeRef>), | ||
51 | // For | ||
52 | // ImplTrait, | ||
53 | // DynTrait, | ||
54 | Error, | ||
55 | } | ||
56 | |||
57 | impl TypeRef { | ||
58 | /// Converts an `ast::TypeRef` to a `hir::TypeRef`. | ||
59 | pub(crate) fn from_ast(node: ast::TypeRef) -> Self { | ||
60 | use ra_syntax::ast::TypeRef::*; | ||
61 | match node { | ||
62 | ParenType(inner) => TypeRef::from_ast_opt(inner.type_ref()), | ||
63 | TupleType(inner) => TypeRef::Tuple(inner.fields().map(TypeRef::from_ast).collect()), | ||
64 | NeverType(..) => TypeRef::Never, | ||
65 | PathType(inner) => inner | ||
66 | .path() | ||
67 | .and_then(Path::from_ast) | ||
68 | .map(TypeRef::Path) | ||
69 | .unwrap_or(TypeRef::Error), | ||
70 | PointerType(inner) => { | ||
71 | let inner_ty = TypeRef::from_ast_opt(inner.type_ref()); | ||
72 | let mutability = Mutability::from_mutable(inner.is_mut()); | ||
73 | TypeRef::RawPtr(Box::new(inner_ty), mutability) | ||
74 | } | ||
75 | ArrayType(inner) => TypeRef::Array(Box::new(TypeRef::from_ast_opt(inner.type_ref()))), | ||
76 | SliceType(inner) => TypeRef::Slice(Box::new(TypeRef::from_ast_opt(inner.type_ref()))), | ||
77 | ReferenceType(inner) => { | ||
78 | let inner_ty = TypeRef::from_ast_opt(inner.type_ref()); | ||
79 | let mutability = Mutability::from_mutable(inner.is_mut()); | ||
80 | TypeRef::Reference(Box::new(inner_ty), mutability) | ||
81 | } | ||
82 | PlaceholderType(_inner) => TypeRef::Placeholder, | ||
83 | FnPointerType(inner) => { | ||
84 | let ret_ty = TypeRef::from_ast_opt(inner.ret_type().and_then(|rt| rt.type_ref())); | ||
85 | let mut params = if let Some(pl) = inner.param_list() { | ||
86 | pl.params() | ||
87 | .map(|p| p.type_ref()) | ||
88 | .map(TypeRef::from_ast_opt) | ||
89 | .collect() | ||
90 | } else { | ||
91 | Vec::new() | ||
92 | }; | ||
93 | params.push(ret_ty); | ||
94 | TypeRef::Fn(params) | ||
95 | } | ||
96 | // for types are close enough for our purposes to the inner type for now... | ||
97 | ForType(inner) => TypeRef::from_ast_opt(inner.type_ref()), | ||
98 | ImplTraitType(_inner) => TypeRef::Error, | ||
99 | DynTraitType(_inner) => TypeRef::Error, | ||
100 | } | ||
101 | } | ||
102 | |||
103 | pub(crate) fn from_ast_opt(node: Option<ast::TypeRef>) -> Self { | ||
104 | if let Some(node) = node { | ||
105 | TypeRef::from_ast(node) | ||
106 | } else { | ||
107 | TypeRef::Error | ||
108 | } | ||
109 | } | ||
110 | } | ||
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs index 560f64989..5f7038f63 100644 --- a/crates/ra_lsp_server/src/caps.rs +++ b/crates/ra_lsp_server/src/caps.rs | |||
@@ -19,7 +19,7 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
19 | hover_provider: Some(true), | 19 | hover_provider: Some(true), |
20 | completion_provider: Some(CompletionOptions { | 20 | completion_provider: Some(CompletionOptions { |
21 | resolve_provider: None, | 21 | resolve_provider: None, |
22 | trigger_characters: Some(vec![":".to_string()]), | 22 | trigger_characters: Some(vec![":".to_string(), ".".to_string()]), |
23 | }), | 23 | }), |
24 | signature_help_provider: Some(SignatureHelpOptions { | 24 | signature_help_provider: Some(SignatureHelpOptions { |
25 | trigger_characters: Some(vec!["(".to_string(), ",".to_string(), ")".to_string()]), | 25 | trigger_characters: Some(vec!["(".to_string(), ",".to_string(), ")".to_string()]), |
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 051f1f995..c0e4e3a36 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -55,7 +55,10 @@ impl Conv for CompletionItemKind { | |||
55 | CompletionItemKind::Snippet => Snippet, | 55 | CompletionItemKind::Snippet => Snippet, |
56 | CompletionItemKind::Module => Module, | 56 | CompletionItemKind::Module => Module, |
57 | CompletionItemKind::Function => Function, | 57 | CompletionItemKind::Function => Function, |
58 | CompletionItemKind::Struct => Struct, | ||
59 | CompletionItemKind::Enum => Enum, | ||
58 | CompletionItemKind::Binding => Variable, | 60 | CompletionItemKind::Binding => Variable, |
61 | CompletionItemKind::Field => Field, | ||
59 | } | 62 | } |
60 | } | 63 | } |
61 | } | 64 | } |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index f12479fb4..8fb6b6408 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -363,3 +363,73 @@ impl<'a, N: AstNode<'a>> Iterator for AstChildren<'a, N> { | |||
363 | } | 363 | } |
364 | } | 364 | } |
365 | } | 365 | } |
366 | |||
367 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
368 | pub enum StructFlavor<'a> { | ||
369 | Tuple(PosFieldList<'a>), | ||
370 | Named(NamedFieldDefList<'a>), | ||
371 | Unit, | ||
372 | } | ||
373 | |||
374 | impl<'a> StructFlavor<'a> { | ||
375 | fn from_node<N: AstNode<'a>>(node: N) -> StructFlavor<'a> { | ||
376 | if let Some(nfdl) = child_opt::<_, NamedFieldDefList>(node) { | ||
377 | StructFlavor::Named(nfdl) | ||
378 | } else if let Some(pfl) = child_opt::<_, PosFieldList>(node) { | ||
379 | StructFlavor::Tuple(pfl) | ||
380 | } else { | ||
381 | StructFlavor::Unit | ||
382 | } | ||
383 | } | ||
384 | } | ||
385 | |||
386 | impl<'a> StructDef<'a> { | ||
387 | pub fn flavor(self) -> StructFlavor<'a> { | ||
388 | StructFlavor::from_node(self) | ||
389 | } | ||
390 | } | ||
391 | |||
392 | impl<'a> EnumVariant<'a> { | ||
393 | pub fn flavor(self) -> StructFlavor<'a> { | ||
394 | StructFlavor::from_node(self) | ||
395 | } | ||
396 | } | ||
397 | |||
398 | impl<'a> PointerType<'a> { | ||
399 | pub fn is_mut(&self) -> bool { | ||
400 | self.syntax().children().any(|n| n.kind() == MUT_KW) | ||
401 | } | ||
402 | } | ||
403 | |||
404 | impl<'a> ReferenceType<'a> { | ||
405 | pub fn is_mut(&self) -> bool { | ||
406 | self.syntax().children().any(|n| n.kind() == MUT_KW) | ||
407 | } | ||
408 | } | ||
409 | |||
410 | impl<'a> RefExpr<'a> { | ||
411 | pub fn is_mut(&self) -> bool { | ||
412 | self.syntax().children().any(|n| n.kind() == MUT_KW) | ||
413 | } | ||
414 | } | ||
415 | |||
416 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
417 | pub enum PrefixOp { | ||
418 | /// The `*` operator for dereferencing | ||
419 | Deref, | ||
420 | /// The `!` operator for logical inversion | ||
421 | Not, | ||
422 | /// The `-` operator for negation | ||
423 | Neg, | ||
424 | } | ||
425 | |||
426 | impl<'a> PrefixExpr<'a> { | ||
427 | pub fn op(&self) -> Option<PrefixOp> { | ||
428 | match self.syntax().first_child()?.kind() { | ||
429 | STAR => Some(PrefixOp::Deref), | ||
430 | EXCL => Some(PrefixOp::Not), | ||
431 | MINUS => Some(PrefixOp::Neg), | ||
432 | _ => None, | ||
433 | } | ||
434 | } | ||
435 | } | ||
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index c73533861..c22e026cf 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -131,7 +131,15 @@ impl<R: TreeRoot<RaTypes>> ArrayTypeNode<R> { | |||
131 | } | 131 | } |
132 | 132 | ||
133 | 133 | ||
134 | impl<'a> ArrayType<'a> {} | 134 | impl<'a> ArrayType<'a> { |
135 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | ||
136 | super::child_opt(self) | ||
137 | } | ||
138 | |||
139 | pub fn expr(self) -> Option<Expr<'a>> { | ||
140 | super::child_opt(self) | ||
141 | } | ||
142 | } | ||
135 | 143 | ||
136 | // Attr | 144 | // Attr |
137 | #[derive(Debug, Clone, Copy,)] | 145 | #[derive(Debug, Clone, Copy,)] |
@@ -806,7 +814,94 @@ impl<'a> ast::NameOwner<'a> for EnumDef<'a> {} | |||
806 | impl<'a> ast::TypeParamsOwner<'a> for EnumDef<'a> {} | 814 | impl<'a> ast::TypeParamsOwner<'a> for EnumDef<'a> {} |
807 | impl<'a> ast::AttrsOwner<'a> for EnumDef<'a> {} | 815 | impl<'a> ast::AttrsOwner<'a> for EnumDef<'a> {} |
808 | impl<'a> ast::DocCommentsOwner<'a> for EnumDef<'a> {} | 816 | impl<'a> ast::DocCommentsOwner<'a> for EnumDef<'a> {} |
809 | impl<'a> EnumDef<'a> {} | 817 | impl<'a> EnumDef<'a> { |
818 | pub fn variant_list(self) -> Option<EnumVariantList<'a>> { | ||
819 | super::child_opt(self) | ||
820 | } | ||
821 | } | ||
822 | |||
823 | // EnumVariant | ||
824 | #[derive(Debug, Clone, Copy,)] | ||
825 | pub struct EnumVariantNode<R: TreeRoot<RaTypes> = OwnedRoot> { | ||
826 | pub(crate) syntax: SyntaxNode<R>, | ||
827 | } | ||
828 | pub type EnumVariant<'a> = EnumVariantNode<RefRoot<'a>>; | ||
829 | |||
830 | impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<EnumVariantNode<R1>> for EnumVariantNode<R2> { | ||
831 | fn eq(&self, other: &EnumVariantNode<R1>) -> bool { self.syntax == other.syntax } | ||
832 | } | ||
833 | impl<R: TreeRoot<RaTypes>> Eq for EnumVariantNode<R> {} | ||
834 | impl<R: TreeRoot<RaTypes>> Hash for EnumVariantNode<R> { | ||
835 | fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) } | ||
836 | } | ||
837 | |||
838 | impl<'a> AstNode<'a> for EnumVariant<'a> { | ||
839 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
840 | match syntax.kind() { | ||
841 | ENUM_VARIANT => Some(EnumVariant { syntax }), | ||
842 | _ => None, | ||
843 | } | ||
844 | } | ||
845 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | ||
846 | } | ||
847 | |||
848 | impl<R: TreeRoot<RaTypes>> EnumVariantNode<R> { | ||
849 | pub fn borrowed(&self) -> EnumVariant { | ||
850 | EnumVariantNode { syntax: self.syntax.borrowed() } | ||
851 | } | ||
852 | pub fn owned(&self) -> EnumVariantNode { | ||
853 | EnumVariantNode { syntax: self.syntax.owned() } | ||
854 | } | ||
855 | } | ||
856 | |||
857 | |||
858 | impl<'a> ast::NameOwner<'a> for EnumVariant<'a> {} | ||
859 | impl<'a> EnumVariant<'a> { | ||
860 | pub fn expr(self) -> Option<Expr<'a>> { | ||
861 | super::child_opt(self) | ||
862 | } | ||
863 | } | ||
864 | |||
865 | // EnumVariantList | ||
866 | #[derive(Debug, Clone, Copy,)] | ||
867 | pub struct EnumVariantListNode<R: TreeRoot<RaTypes> = OwnedRoot> { | ||
868 | pub(crate) syntax: SyntaxNode<R>, | ||
869 | } | ||
870 | pub type EnumVariantList<'a> = EnumVariantListNode<RefRoot<'a>>; | ||
871 | |||
872 | impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<EnumVariantListNode<R1>> for EnumVariantListNode<R2> { | ||
873 | fn eq(&self, other: &EnumVariantListNode<R1>) -> bool { self.syntax == other.syntax } | ||
874 | } | ||
875 | impl<R: TreeRoot<RaTypes>> Eq for EnumVariantListNode<R> {} | ||
876 | impl<R: TreeRoot<RaTypes>> Hash for EnumVariantListNode<R> { | ||
877 | fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) } | ||
878 | } | ||
879 | |||
880 | impl<'a> AstNode<'a> for EnumVariantList<'a> { | ||
881 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
882 | match syntax.kind() { | ||
883 | ENUM_VARIANT_LIST => Some(EnumVariantList { syntax }), | ||
884 | _ => None, | ||
885 | } | ||
886 | } | ||
887 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | ||
888 | } | ||
889 | |||
890 | impl<R: TreeRoot<RaTypes>> EnumVariantListNode<R> { | ||
891 | pub fn borrowed(&self) -> EnumVariantList { | ||
892 | EnumVariantListNode { syntax: self.syntax.borrowed() } | ||
893 | } | ||
894 | pub fn owned(&self) -> EnumVariantListNode { | ||
895 | EnumVariantListNode { syntax: self.syntax.owned() } | ||
896 | } | ||
897 | } | ||
898 | |||
899 | |||
900 | impl<'a> EnumVariantList<'a> { | ||
901 | pub fn variants(self) -> impl Iterator<Item = EnumVariant<'a>> + 'a { | ||
902 | super::children(self) | ||
903 | } | ||
904 | } | ||
810 | 905 | ||
811 | // Expr | 906 | // Expr |
812 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 907 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
@@ -1036,7 +1131,15 @@ impl<R: TreeRoot<RaTypes>> FieldExprNode<R> { | |||
1036 | } | 1131 | } |
1037 | 1132 | ||
1038 | 1133 | ||
1039 | impl<'a> FieldExpr<'a> {} | 1134 | impl<'a> FieldExpr<'a> { |
1135 | pub fn expr(self) -> Option<Expr<'a>> { | ||
1136 | super::child_opt(self) | ||
1137 | } | ||
1138 | |||
1139 | pub fn name_ref(self) -> Option<NameRef<'a>> { | ||
1140 | super::child_opt(self) | ||
1141 | } | ||
1142 | } | ||
1040 | 1143 | ||
1041 | // FieldPatList | 1144 | // FieldPatList |
1042 | #[derive(Debug, Clone, Copy,)] | 1145 | #[derive(Debug, Clone, Copy,)] |
@@ -1163,7 +1266,15 @@ impl<R: TreeRoot<RaTypes>> FnPointerTypeNode<R> { | |||
1163 | } | 1266 | } |
1164 | 1267 | ||
1165 | 1268 | ||
1166 | impl<'a> FnPointerType<'a> {} | 1269 | impl<'a> FnPointerType<'a> { |
1270 | pub fn param_list(self) -> Option<ParamList<'a>> { | ||
1271 | super::child_opt(self) | ||
1272 | } | ||
1273 | |||
1274 | pub fn ret_type(self) -> Option<RetType<'a>> { | ||
1275 | super::child_opt(self) | ||
1276 | } | ||
1277 | } | ||
1167 | 1278 | ||
1168 | // ForExpr | 1279 | // ForExpr |
1169 | #[derive(Debug, Clone, Copy,)] | 1280 | #[derive(Debug, Clone, Copy,)] |
@@ -1246,7 +1357,11 @@ impl<R: TreeRoot<RaTypes>> ForTypeNode<R> { | |||
1246 | } | 1357 | } |
1247 | 1358 | ||
1248 | 1359 | ||
1249 | impl<'a> ForType<'a> {} | 1360 | impl<'a> ForType<'a> { |
1361 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | ||
1362 | super::child_opt(self) | ||
1363 | } | ||
1364 | } | ||
1250 | 1365 | ||
1251 | // IfExpr | 1366 | // IfExpr |
1252 | #[derive(Debug, Clone, Copy,)] | 1367 | #[derive(Debug, Clone, Copy,)] |
@@ -1935,6 +2050,10 @@ impl<'a> MethodCallExpr<'a> { | |||
1935 | pub fn expr(self) -> Option<Expr<'a>> { | 2050 | pub fn expr(self) -> Option<Expr<'a>> { |
1936 | super::child_opt(self) | 2051 | super::child_opt(self) |
1937 | } | 2052 | } |
2053 | |||
2054 | pub fn name_ref(self) -> Option<NameRef<'a>> { | ||
2055 | super::child_opt(self) | ||
2056 | } | ||
1938 | } | 2057 | } |
1939 | 2058 | ||
1940 | // Module | 2059 | // Module |
@@ -2142,7 +2261,15 @@ impl<R: TreeRoot<RaTypes>> NamedFieldNode<R> { | |||
2142 | } | 2261 | } |
2143 | 2262 | ||
2144 | 2263 | ||
2145 | impl<'a> NamedField<'a> {} | 2264 | impl<'a> NamedField<'a> { |
2265 | pub fn name_ref(self) -> Option<NameRef<'a>> { | ||
2266 | super::child_opt(self) | ||
2267 | } | ||
2268 | |||
2269 | pub fn expr(self) -> Option<Expr<'a>> { | ||
2270 | super::child_opt(self) | ||
2271 | } | ||
2272 | } | ||
2146 | 2273 | ||
2147 | // NamedFieldDef | 2274 | // NamedFieldDef |
2148 | #[derive(Debug, Clone, Copy,)] | 2275 | #[derive(Debug, Clone, Copy,)] |
@@ -2181,7 +2308,52 @@ impl<R: TreeRoot<RaTypes>> NamedFieldDefNode<R> { | |||
2181 | 2308 | ||
2182 | impl<'a> ast::NameOwner<'a> for NamedFieldDef<'a> {} | 2309 | impl<'a> ast::NameOwner<'a> for NamedFieldDef<'a> {} |
2183 | impl<'a> ast::AttrsOwner<'a> for NamedFieldDef<'a> {} | 2310 | impl<'a> ast::AttrsOwner<'a> for NamedFieldDef<'a> {} |
2184 | impl<'a> NamedFieldDef<'a> {} | 2311 | impl<'a> NamedFieldDef<'a> { |
2312 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | ||
2313 | super::child_opt(self) | ||
2314 | } | ||
2315 | } | ||
2316 | |||
2317 | // NamedFieldDefList | ||
2318 | #[derive(Debug, Clone, Copy,)] | ||
2319 | pub struct NamedFieldDefListNode<R: TreeRoot<RaTypes> = OwnedRoot> { | ||
2320 | pub(crate) syntax: SyntaxNode<R>, | ||
2321 | } | ||
2322 | pub type NamedFieldDefList<'a> = NamedFieldDefListNode<RefRoot<'a>>; | ||
2323 | |||
2324 | impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<NamedFieldDefListNode<R1>> for NamedFieldDefListNode<R2> { | ||
2325 | fn eq(&self, other: &NamedFieldDefListNode<R1>) -> bool { self.syntax == other.syntax } | ||
2326 | } | ||
2327 | impl<R: TreeRoot<RaTypes>> Eq for NamedFieldDefListNode<R> {} | ||
2328 | impl<R: TreeRoot<RaTypes>> Hash for NamedFieldDefListNode<R> { | ||
2329 | fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) } | ||
2330 | } | ||
2331 | |||
2332 | impl<'a> AstNode<'a> for NamedFieldDefList<'a> { | ||
2333 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
2334 | match syntax.kind() { | ||
2335 | NAMED_FIELD_DEF_LIST => Some(NamedFieldDefList { syntax }), | ||
2336 | _ => None, | ||
2337 | } | ||
2338 | } | ||
2339 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | ||
2340 | } | ||
2341 | |||
2342 | impl<R: TreeRoot<RaTypes>> NamedFieldDefListNode<R> { | ||
2343 | pub fn borrowed(&self) -> NamedFieldDefList { | ||
2344 | NamedFieldDefListNode { syntax: self.syntax.borrowed() } | ||
2345 | } | ||
2346 | pub fn owned(&self) -> NamedFieldDefListNode { | ||
2347 | NamedFieldDefListNode { syntax: self.syntax.owned() } | ||
2348 | } | ||
2349 | } | ||
2350 | |||
2351 | |||
2352 | impl<'a> NamedFieldDefList<'a> { | ||
2353 | pub fn fields(self) -> impl Iterator<Item = NamedFieldDef<'a>> + 'a { | ||
2354 | super::children(self) | ||
2355 | } | ||
2356 | } | ||
2185 | 2357 | ||
2186 | // NamedFieldList | 2358 | // NamedFieldList |
2187 | #[derive(Debug, Clone, Copy,)] | 2359 | #[derive(Debug, Clone, Copy,)] |
@@ -2218,7 +2390,11 @@ impl<R: TreeRoot<RaTypes>> NamedFieldListNode<R> { | |||
2218 | } | 2390 | } |
2219 | 2391 | ||
2220 | 2392 | ||
2221 | impl<'a> NamedFieldList<'a> {} | 2393 | impl<'a> NamedFieldList<'a> { |
2394 | pub fn fields(self) -> impl Iterator<Item = NamedField<'a>> + 'a { | ||
2395 | super::children(self) | ||
2396 | } | ||
2397 | } | ||
2222 | 2398 | ||
2223 | // NeverType | 2399 | // NeverType |
2224 | #[derive(Debug, Clone, Copy,)] | 2400 | #[derive(Debug, Clone, Copy,)] |
@@ -2451,7 +2627,11 @@ impl<R: TreeRoot<RaTypes>> ParenTypeNode<R> { | |||
2451 | } | 2627 | } |
2452 | 2628 | ||
2453 | 2629 | ||
2454 | impl<'a> ParenType<'a> {} | 2630 | impl<'a> ParenType<'a> { |
2631 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | ||
2632 | super::child_opt(self) | ||
2633 | } | ||
2634 | } | ||
2455 | 2635 | ||
2456 | // Pat | 2636 | // Pat |
2457 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 2637 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
@@ -2816,7 +2996,94 @@ impl<R: TreeRoot<RaTypes>> PointerTypeNode<R> { | |||
2816 | } | 2996 | } |
2817 | 2997 | ||
2818 | 2998 | ||
2819 | impl<'a> PointerType<'a> {} | 2999 | impl<'a> PointerType<'a> { |
3000 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | ||
3001 | super::child_opt(self) | ||
3002 | } | ||
3003 | } | ||
3004 | |||
3005 | // PosField | ||
3006 | #[derive(Debug, Clone, Copy,)] | ||
3007 | pub struct PosFieldNode<R: TreeRoot<RaTypes> = OwnedRoot> { | ||
3008 | pub(crate) syntax: SyntaxNode<R>, | ||
3009 | } | ||
3010 | pub type PosField<'a> = PosFieldNode<RefRoot<'a>>; | ||
3011 | |||
3012 | impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<PosFieldNode<R1>> for PosFieldNode<R2> { | ||
3013 | fn eq(&self, other: &PosFieldNode<R1>) -> bool { self.syntax == other.syntax } | ||
3014 | } | ||
3015 | impl<R: TreeRoot<RaTypes>> Eq for PosFieldNode<R> {} | ||
3016 | impl<R: TreeRoot<RaTypes>> Hash for PosFieldNode<R> { | ||
3017 | fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) } | ||
3018 | } | ||
3019 | |||
3020 | impl<'a> AstNode<'a> for PosField<'a> { | ||
3021 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
3022 | match syntax.kind() { | ||
3023 | POS_FIELD => Some(PosField { syntax }), | ||
3024 | _ => None, | ||
3025 | } | ||
3026 | } | ||
3027 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | ||
3028 | } | ||
3029 | |||
3030 | impl<R: TreeRoot<RaTypes>> PosFieldNode<R> { | ||
3031 | pub fn borrowed(&self) -> PosField { | ||
3032 | PosFieldNode { syntax: self.syntax.borrowed() } | ||
3033 | } | ||
3034 | pub fn owned(&self) -> PosFieldNode { | ||
3035 | PosFieldNode { syntax: self.syntax.owned() } | ||
3036 | } | ||
3037 | } | ||
3038 | |||
3039 | |||
3040 | impl<'a> ast::AttrsOwner<'a> for PosField<'a> {} | ||
3041 | impl<'a> PosField<'a> { | ||
3042 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | ||
3043 | super::child_opt(self) | ||
3044 | } | ||
3045 | } | ||
3046 | |||
3047 | // PosFieldList | ||
3048 | #[derive(Debug, Clone, Copy,)] | ||
3049 | pub struct PosFieldListNode<R: TreeRoot<RaTypes> = OwnedRoot> { | ||
3050 | pub(crate) syntax: SyntaxNode<R>, | ||
3051 | } | ||
3052 | pub type PosFieldList<'a> = PosFieldListNode<RefRoot<'a>>; | ||
3053 | |||
3054 | impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<PosFieldListNode<R1>> for PosFieldListNode<R2> { | ||
3055 | fn eq(&self, other: &PosFieldListNode<R1>) -> bool { self.syntax == other.syntax } | ||
3056 | } | ||
3057 | impl<R: TreeRoot<RaTypes>> Eq for PosFieldListNode<R> {} | ||
3058 | impl<R: TreeRoot<RaTypes>> Hash for PosFieldListNode<R> { | ||
3059 | fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) } | ||
3060 | } | ||
3061 | |||
3062 | impl<'a> AstNode<'a> for PosFieldList<'a> { | ||
3063 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
3064 | match syntax.kind() { | ||
3065 | POS_FIELD_LIST => Some(PosFieldList { syntax }), | ||
3066 | _ => None, | ||
3067 | } | ||
3068 | } | ||
3069 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | ||
3070 | } | ||
3071 | |||
3072 | impl<R: TreeRoot<RaTypes>> PosFieldListNode<R> { | ||
3073 | pub fn borrowed(&self) -> PosFieldList { | ||
3074 | PosFieldListNode { syntax: self.syntax.borrowed() } | ||
3075 | } | ||
3076 | pub fn owned(&self) -> PosFieldListNode { | ||
3077 | PosFieldListNode { syntax: self.syntax.owned() } | ||
3078 | } | ||
3079 | } | ||
3080 | |||
3081 | |||
3082 | impl<'a> PosFieldList<'a> { | ||
3083 | pub fn fields(self) -> impl Iterator<Item = PosField<'a>> + 'a { | ||
3084 | super::children(self) | ||
3085 | } | ||
3086 | } | ||
2820 | 3087 | ||
2821 | // PrefixExpr | 3088 | // PrefixExpr |
2822 | #[derive(Debug, Clone, Copy,)] | 3089 | #[derive(Debug, Clone, Copy,)] |
@@ -3046,7 +3313,11 @@ impl<R: TreeRoot<RaTypes>> ReferenceTypeNode<R> { | |||
3046 | } | 3313 | } |
3047 | 3314 | ||
3048 | 3315 | ||
3049 | impl<'a> ReferenceType<'a> {} | 3316 | impl<'a> ReferenceType<'a> { |
3317 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | ||
3318 | super::child_opt(self) | ||
3319 | } | ||
3320 | } | ||
3050 | 3321 | ||
3051 | // RetType | 3322 | // RetType |
3052 | #[derive(Debug, Clone, Copy,)] | 3323 | #[derive(Debug, Clone, Copy,)] |
@@ -3239,7 +3510,11 @@ impl<R: TreeRoot<RaTypes>> SliceTypeNode<R> { | |||
3239 | } | 3510 | } |
3240 | 3511 | ||
3241 | 3512 | ||
3242 | impl<'a> SliceType<'a> {} | 3513 | impl<'a> SliceType<'a> { |
3514 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | ||
3515 | super::child_opt(self) | ||
3516 | } | ||
3517 | } | ||
3243 | 3518 | ||
3244 | // SourceFile | 3519 | // SourceFile |
3245 | #[derive(Debug, Clone, Copy,)] | 3520 | #[derive(Debug, Clone, Copy,)] |
@@ -3426,11 +3701,7 @@ impl<'a> ast::NameOwner<'a> for StructDef<'a> {} | |||
3426 | impl<'a> ast::TypeParamsOwner<'a> for StructDef<'a> {} | 3701 | impl<'a> ast::TypeParamsOwner<'a> for StructDef<'a> {} |
3427 | impl<'a> ast::AttrsOwner<'a> for StructDef<'a> {} | 3702 | impl<'a> ast::AttrsOwner<'a> for StructDef<'a> {} |
3428 | impl<'a> ast::DocCommentsOwner<'a> for StructDef<'a> {} | 3703 | impl<'a> ast::DocCommentsOwner<'a> for StructDef<'a> {} |
3429 | impl<'a> StructDef<'a> { | 3704 | impl<'a> StructDef<'a> {} |
3430 | pub fn fields(self) -> impl Iterator<Item = NamedFieldDef<'a>> + 'a { | ||
3431 | super::children(self) | ||
3432 | } | ||
3433 | } | ||
3434 | 3705 | ||
3435 | // StructLit | 3706 | // StructLit |
3436 | #[derive(Debug, Clone, Copy,)] | 3707 | #[derive(Debug, Clone, Copy,)] |
@@ -3467,7 +3738,15 @@ impl<R: TreeRoot<RaTypes>> StructLitNode<R> { | |||
3467 | } | 3738 | } |
3468 | 3739 | ||
3469 | 3740 | ||
3470 | impl<'a> StructLit<'a> {} | 3741 | impl<'a> StructLit<'a> { |
3742 | pub fn path(self) -> Option<Path<'a>> { | ||
3743 | super::child_opt(self) | ||
3744 | } | ||
3745 | |||
3746 | pub fn named_field_list(self) -> Option<NamedFieldList<'a>> { | ||
3747 | super::child_opt(self) | ||
3748 | } | ||
3749 | } | ||
3471 | 3750 | ||
3472 | // StructPat | 3751 | // StructPat |
3473 | #[derive(Debug, Clone, Copy,)] | 3752 | #[derive(Debug, Clone, Copy,)] |
@@ -3770,7 +4049,11 @@ impl<R: TreeRoot<RaTypes>> TupleTypeNode<R> { | |||
3770 | } | 4049 | } |
3771 | 4050 | ||
3772 | 4051 | ||
3773 | impl<'a> TupleType<'a> {} | 4052 | impl<'a> TupleType<'a> { |
4053 | pub fn fields(self) -> impl Iterator<Item = TypeRef<'a>> + 'a { | ||
4054 | super::children(self) | ||
4055 | } | ||
4056 | } | ||
3774 | 4057 | ||
3775 | // TypeDef | 4058 | // TypeDef |
3776 | #[derive(Debug, Clone, Copy,)] | 4059 | #[derive(Debug, Clone, Copy,)] |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index e3b9032a0..4bcff4e14 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -261,18 +261,20 @@ Grammar( | |||
261 | "TypeParamsOwner", | 261 | "TypeParamsOwner", |
262 | "AttrsOwner", | 262 | "AttrsOwner", |
263 | "DocCommentsOwner" | 263 | "DocCommentsOwner" |
264 | ], | ||
265 | collections: [ | ||
266 | ["fields", "NamedFieldDef"] | ||
267 | ] | 264 | ] |
268 | ), | 265 | ), |
269 | "NamedFieldDef": ( traits: ["NameOwner", "AttrsOwner"] ), | 266 | "NamedFieldDefList": (collections: [["fields", "NamedFieldDef"]]), |
267 | "NamedFieldDef": ( traits: ["NameOwner", "AttrsOwner"], options: ["TypeRef"] ), | ||
268 | "PosFieldList": (collections: [["fields", "PosField"]]), | ||
269 | "PosField": ( traits: ["AttrsOwner"], options: ["TypeRef"]), | ||
270 | "EnumDef": ( traits: [ | 270 | "EnumDef": ( traits: [ |
271 | "NameOwner", | 271 | "NameOwner", |
272 | "TypeParamsOwner", | 272 | "TypeParamsOwner", |
273 | "AttrsOwner", | 273 | "AttrsOwner", |
274 | "DocCommentsOwner" | 274 | "DocCommentsOwner" |
275 | ] ), | 275 | ], options: [["variant_list", "EnumVariantList"]] ), |
276 | "EnumVariantList": ( collections: [["variants", "EnumVariant"]] ), | ||
277 | "EnumVariant": ( traits: ["NameOwner"], options: ["Expr"] ), | ||
276 | "TraitDef": ( traits: ["NameOwner", "AttrsOwner", "DocCommentsOwner"] ), | 278 | "TraitDef": ( traits: ["NameOwner", "AttrsOwner", "DocCommentsOwner"] ), |
277 | "Module": ( | 279 | "Module": ( |
278 | traits: ["NameOwner", "AttrsOwner", "DocCommentsOwner" ], | 280 | traits: ["NameOwner", "AttrsOwner", "DocCommentsOwner" ], |
@@ -301,17 +303,17 @@ Grammar( | |||
301 | ] ), | 303 | ] ), |
302 | "ImplItem": (), | 304 | "ImplItem": (), |
303 | 305 | ||
304 | "ParenType": (), | 306 | "ParenType": (options: ["TypeRef"]), |
305 | "TupleType": (), | 307 | "TupleType": ( collections: [["fields", "TypeRef"]] ), |
306 | "NeverType": (), | 308 | "NeverType": (), |
307 | "PathType": (options: ["Path"]), | 309 | "PathType": (options: ["Path"]), |
308 | "PointerType": (), | 310 | "PointerType": (options: ["TypeRef"]), |
309 | "ArrayType": (), | 311 | "ArrayType": ( options: ["TypeRef", "Expr"] ), |
310 | "SliceType": (), | 312 | "SliceType": ( options: ["TypeRef"] ), |
311 | "ReferenceType": (), | 313 | "ReferenceType": (options: ["TypeRef"]), |
312 | "PlaceholderType": (), | 314 | "PlaceholderType": (), |
313 | "FnPointerType": (), | 315 | "FnPointerType": (options: ["ParamList", "RetType"]), |
314 | "ForType": (), | 316 | "ForType": (options: ["TypeRef"]), |
315 | "ImplTraitType": (), | 317 | "ImplTraitType": (), |
316 | "DynTraitType": (), | 318 | "DynTraitType": (), |
317 | 319 | ||
@@ -392,19 +394,19 @@ Grammar( | |||
392 | collections: [ [ "pats", "Pat" ] ] | 394 | collections: [ [ "pats", "Pat" ] ] |
393 | ), | 395 | ), |
394 | "MatchGuard": (), | 396 | "MatchGuard": (), |
395 | "StructLit": (), | 397 | "StructLit": (options: ["Path", "NamedFieldList"]), |
396 | "NamedFieldList": (), | 398 | "NamedFieldList": (collections: [ ["fields", "NamedField"] ]), |
397 | "NamedField": (), | 399 | "NamedField": (options: ["NameRef", "Expr"]), |
398 | "CallExpr": ( | 400 | "CallExpr": ( |
399 | traits: ["ArgListOwner"], | 401 | traits: ["ArgListOwner"], |
400 | options: [ "Expr" ], | 402 | options: [ "Expr" ], |
401 | ), | 403 | ), |
402 | "MethodCallExpr": ( | 404 | "MethodCallExpr": ( |
403 | traits: ["ArgListOwner"], | 405 | traits: ["ArgListOwner"], |
404 | options: [ "Expr" ], | 406 | options: [ "Expr", "NameRef" ], |
405 | ), | 407 | ), |
406 | "IndexExpr": (), | 408 | "IndexExpr": (), |
407 | "FieldExpr": (), | 409 | "FieldExpr": (options: ["Expr", "NameRef"]), |
408 | "TryExpr": (options: ["Expr"]), | 410 | "TryExpr": (options: ["Expr"]), |
409 | "CastExpr": (options: ["Expr", "TypeRef"]), | 411 | "CastExpr": (options: ["Expr", "TypeRef"]), |
410 | "RefExpr": (options: ["Expr"]), | 412 | "RefExpr": (options: ["Expr"]), |
diff --git a/crates/ra_syntax/src/grammar/expressions.rs b/crates/ra_syntax/src/grammar/expressions.rs index da78d85a2..2d1f17491 100644 --- a/crates/ra_syntax/src/grammar/expressions.rs +++ b/crates/ra_syntax/src/grammar/expressions.rs | |||
@@ -283,14 +283,10 @@ fn postfix_expr( | |||
283 | // } | 283 | // } |
284 | L_PAREN if allow_calls => call_expr(p, lhs), | 284 | L_PAREN if allow_calls => call_expr(p, lhs), |
285 | L_BRACK if allow_calls => index_expr(p, lhs), | 285 | L_BRACK if allow_calls => index_expr(p, lhs), |
286 | DOT if p.nth(1) == IDENT => { | 286 | DOT if p.nth(1) == IDENT && (p.nth(2) == L_PAREN || p.nth(2) == COLONCOLON) => { |
287 | if p.nth(2) == L_PAREN || p.nth(2) == COLONCOLON { | 287 | method_call_expr(p, lhs) |
288 | method_call_expr(p, lhs) | ||
289 | } else { | ||
290 | field_expr(p, lhs) | ||
291 | } | ||
292 | } | 288 | } |
293 | DOT if p.nth(1) == INT_NUMBER => field_expr(p, lhs), | 289 | DOT => field_expr(p, lhs), |
294 | // test postfix_range | 290 | // test postfix_range |
295 | // fn foo() { let x = 1..; } | 291 | // fn foo() { let x = 1..; } |
296 | DOTDOT | DOTDOTEQ if !EXPR_FIRST.contains(p.nth(1)) => { | 292 | DOTDOT | DOTDOTEQ if !EXPR_FIRST.contains(p.nth(1)) => { |
@@ -355,13 +351,15 @@ fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { | |||
355 | // x.0.bar; | 351 | // x.0.bar; |
356 | // } | 352 | // } |
357 | fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { | 353 | fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { |
358 | assert!(p.at(DOT) && (p.nth(1) == IDENT || p.nth(1) == INT_NUMBER)); | 354 | assert!(p.at(DOT)); |
359 | let m = lhs.precede(p); | 355 | let m = lhs.precede(p); |
360 | p.bump(); | 356 | p.bump(); |
361 | if p.at(IDENT) { | 357 | if p.at(IDENT) { |
362 | name_ref(p) | 358 | name_ref(p) |
363 | } else { | 359 | } else if p.at(INT_NUMBER) { |
364 | p.bump() | 360 | p.bump() |
361 | } else { | ||
362 | p.error("expected field name or number") | ||
365 | } | 363 | } |
366 | m.complete(p, FIELD_EXPR) | 364 | m.complete(p, FIELD_EXPR) |
367 | } | 365 | } |
diff --git a/crates/ra_syntax/tests/data/parser/err/0029_field_completion.rs b/crates/ra_syntax/tests/data/parser/err/0029_field_completion.rs new file mode 100644 index 000000000..a7cdc17bb --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/err/0029_field_completion.rs | |||
@@ -0,0 +1,3 @@ | |||
1 | fn foo(a: A) { | ||
2 | a. | ||
3 | } | ||
diff --git a/crates/ra_syntax/tests/data/parser/err/0029_field_completion.txt b/crates/ra_syntax/tests/data/parser/err/0029_field_completion.txt new file mode 100644 index 000000000..fd2a3f37b --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/err/0029_field_completion.txt | |||
@@ -0,0 +1,35 @@ | |||
1 | SOURCE_FILE@[0; 24) | ||
2 | FN_DEF@[0; 23) | ||
3 | FN_KW@[0; 2) | ||
4 | WHITESPACE@[2; 3) | ||
5 | NAME@[3; 6) | ||
6 | IDENT@[3; 6) "foo" | ||
7 | PARAM_LIST@[6; 12) | ||
8 | L_PAREN@[6; 7) | ||
9 | PARAM@[7; 11) | ||
10 | BIND_PAT@[7; 8) | ||
11 | NAME@[7; 8) | ||
12 | IDENT@[7; 8) "a" | ||
13 | COLON@[8; 9) | ||
14 | WHITESPACE@[9; 10) | ||
15 | PATH_TYPE@[10; 11) | ||
16 | PATH@[10; 11) | ||
17 | PATH_SEGMENT@[10; 11) | ||
18 | NAME_REF@[10; 11) | ||
19 | IDENT@[10; 11) "A" | ||
20 | R_PAREN@[11; 12) | ||
21 | WHITESPACE@[12; 13) | ||
22 | BLOCK@[13; 23) | ||
23 | L_CURLY@[13; 14) | ||
24 | WHITESPACE@[14; 19) | ||
25 | FIELD_EXPR@[19; 21) | ||
26 | PATH_EXPR@[19; 20) | ||
27 | PATH@[19; 20) | ||
28 | PATH_SEGMENT@[19; 20) | ||
29 | NAME_REF@[19; 20) | ||
30 | IDENT@[19; 20) "a" | ||
31 | DOT@[20; 21) | ||
32 | err: `expected field name or number` | ||
33 | WHITESPACE@[21; 22) | ||
34 | R_CURLY@[22; 23) | ||
35 | WHITESPACE@[23; 24) | ||