diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_analysis/src/completion/mod.rs | 10 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/reference_completion.rs | 26 | ||||
-rw-r--r-- | crates/ra_analysis/src/db.rs | 7 | ||||
-rw-r--r-- | crates/ra_analysis/src/descriptors/mod.rs | 14 | ||||
-rw-r--r-- | crates/ra_analysis/src/descriptors/module/imp.rs | 21 | ||||
-rw-r--r-- | crates/ra_analysis/src/descriptors/module/mod.rs | 37 | ||||
-rw-r--r-- | crates/ra_analysis/src/descriptors/module/nameres.rs | 453 | ||||
-rw-r--r-- | crates/ra_analysis/src/descriptors/module/scope.rs | 124 | ||||
-rw-r--r-- | crates/ra_analysis/src/loc2id.rs | 25 | ||||
-rw-r--r-- | crates/ra_analysis/tests/tests.rs | 8 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/mod.rs | 6 | ||||
-rw-r--r-- | crates/tools/src/lib.rs | 2 |
12 files changed, 555 insertions, 178 deletions
diff --git a/crates/ra_analysis/src/completion/mod.rs b/crates/ra_analysis/src/completion/mod.rs index 5e3ee79dd..a8a752fc7 100644 --- a/crates/ra_analysis/src/completion/mod.rs +++ b/crates/ra_analysis/src/completion/mod.rs | |||
@@ -204,9 +204,9 @@ mod tests { | |||
204 | <|> | 204 | <|> |
205 | } | 205 | } |
206 | ", | 206 | ", |
207 | r#"[CompletionItem { label: "Foo", lookup: None, snippet: None }, | 207 | r#"[CompletionItem { label: "quux", lookup: None, snippet: None }, |
208 | CompletionItem { label: "Baz", lookup: None, snippet: None }, | 208 | CompletionItem { label: "Foo", lookup: None, snippet: None }, |
209 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#, | 209 | CompletionItem { label: "Baz", lookup: None, snippet: None }]"#, |
210 | ); | 210 | ); |
211 | } | 211 | } |
212 | 212 | ||
@@ -230,8 +230,8 @@ mod tests { | |||
230 | fn quux() { <|> } | 230 | fn quux() { <|> } |
231 | } | 231 | } |
232 | ", | 232 | ", |
233 | r#"[CompletionItem { label: "Bar", lookup: None, snippet: None }, | 233 | r#"[CompletionItem { label: "quux", lookup: None, snippet: None }, |
234 | CompletionItem { label: "quux", lookup: None, snippet: None }]"#, | 234 | CompletionItem { label: "Bar", lookup: None, snippet: None }]"#, |
235 | ); | 235 | ); |
236 | } | 236 | } |
237 | 237 | ||
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs index c94d9af75..84383b547 100644 --- a/crates/ra_analysis/src/completion/reference_completion.rs +++ b/crates/ra_analysis/src/completion/reference_completion.rs | |||
@@ -39,14 +39,17 @@ pub(super) fn completions( | |||
39 | let module_scope = module.scope(db)?; | 39 | let module_scope = module.scope(db)?; |
40 | acc.extend( | 40 | acc.extend( |
41 | module_scope | 41 | module_scope |
42 | .entries() | 42 | .items |
43 | .iter() | 43 | .iter() |
44 | .filter(|entry| { | 44 | .filter(|(_name, res)| { |
45 | // Don't expose this item | 45 | // Don't expose this item |
46 | !entry.ptr().range().is_subrange(&name_ref.syntax().range()) | 46 | match res.import_name { |
47 | None => true, | ||
48 | Some(ptr) => !ptr.range().is_subrange(&name_ref.syntax().range()), | ||
49 | } | ||
47 | }) | 50 | }) |
48 | .map(|entry| CompletionItem { | 51 | .map(|(name, _res)| CompletionItem { |
49 | label: entry.name().to_string(), | 52 | label: name.to_string(), |
50 | lookup: None, | 53 | lookup: None, |
51 | snippet: None, | 54 | snippet: None, |
52 | }), | 55 | }), |
@@ -173,11 +176,14 @@ fn complete_path( | |||
173 | Some(it) => it, | 176 | Some(it) => it, |
174 | }; | 177 | }; |
175 | let module_scope = target_module.scope(db)?; | 178 | let module_scope = target_module.scope(db)?; |
176 | let completions = module_scope.entries().iter().map(|entry| CompletionItem { | 179 | let completions = module_scope |
177 | label: entry.name().to_string(), | 180 | .items |
178 | lookup: None, | 181 | .iter() |
179 | snippet: None, | 182 | .map(|(name, _res)| CompletionItem { |
180 | }); | 183 | label: name.to_string(), |
184 | lookup: None, | ||
185 | snippet: None, | ||
186 | }); | ||
181 | acc.extend(completions); | 187 | acc.extend(completions); |
182 | Ok(()) | 188 | Ok(()) |
183 | } | 189 | } |
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index 8133b7875..887d687ea 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs | |||
@@ -7,8 +7,8 @@ use salsa::{self, Database}; | |||
7 | use crate::{ | 7 | use crate::{ |
8 | db, | 8 | db, |
9 | descriptors::{ | 9 | descriptors::{ |
10 | DescriptorDatabase, FnScopesQuery, FnSyntaxQuery, ModuleScopeQuery, ModuleTreeQuery, | 10 | DescriptorDatabase, FnScopesQuery, FnSyntaxQuery, ModuleTreeQuery, |
11 | SubmodulesQuery, | 11 | SubmodulesQuery, ItemMapQuery, InputModuleItemsQuery, |
12 | }, | 12 | }, |
13 | symbol_index::SymbolIndex, | 13 | symbol_index::SymbolIndex, |
14 | syntax_ptr::SyntaxPtr, | 14 | syntax_ptr::SyntaxPtr, |
@@ -85,8 +85,9 @@ salsa::database_storage! { | |||
85 | } | 85 | } |
86 | impl DescriptorDatabase { | 86 | impl DescriptorDatabase { |
87 | fn module_tree() for ModuleTreeQuery; | 87 | fn module_tree() for ModuleTreeQuery; |
88 | fn module_scope() for ModuleScopeQuery; | ||
89 | fn fn_scopes() for FnScopesQuery; | 88 | fn fn_scopes() for FnScopesQuery; |
89 | fn _input_module_items() for InputModuleItemsQuery; | ||
90 | fn _item_map() for ItemMapQuery; | ||
90 | fn _fn_syntax() for FnSyntaxQuery; | 91 | fn _fn_syntax() for FnSyntaxQuery; |
91 | fn _submodules() for SubmodulesQuery; | 92 | fn _submodules() for SubmodulesQuery; |
92 | } | 93 | } |
diff --git a/crates/ra_analysis/src/descriptors/mod.rs b/crates/ra_analysis/src/descriptors/mod.rs index a8489f89c..6b56d92e1 100644 --- a/crates/ra_analysis/src/descriptors/mod.rs +++ b/crates/ra_analysis/src/descriptors/mod.rs | |||
@@ -11,7 +11,7 @@ use ra_syntax::{ | |||
11 | use crate::{ | 11 | use crate::{ |
12 | db::SyntaxDatabase, | 12 | db::SyntaxDatabase, |
13 | descriptors::function::{resolve_local_name, FnId, FnScopes}, | 13 | descriptors::function::{resolve_local_name, FnId, FnScopes}, |
14 | descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource}, | 14 | descriptors::module::{ModuleId, ModuleTree, ModuleSource, nameres::{ItemMap, InputModuleItems}}, |
15 | input::SourceRootId, | 15 | input::SourceRootId, |
16 | loc2id::IdDatabase, | 16 | loc2id::IdDatabase, |
17 | syntax_ptr::LocalSyntaxPtr, | 17 | syntax_ptr::LocalSyntaxPtr, |
@@ -25,14 +25,18 @@ salsa::query_group! { | |||
25 | use fn function::imp::fn_scopes; | 25 | use fn function::imp::fn_scopes; |
26 | } | 26 | } |
27 | 27 | ||
28 | fn _input_module_items(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<InputModuleItems>> { | ||
29 | type InputModuleItemsQuery; | ||
30 | use fn module::nameres::input_module_items; | ||
31 | } | ||
32 | fn _item_map(source_root_id: SourceRootId) -> Cancelable<Arc<ItemMap>> { | ||
33 | type ItemMapQuery; | ||
34 | use fn module::nameres::item_map; | ||
35 | } | ||
28 | fn _module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> { | 36 | fn _module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> { |
29 | type ModuleTreeQuery; | 37 | type ModuleTreeQuery; |
30 | use fn module::imp::module_tree; | 38 | use fn module::imp::module_tree; |
31 | } | 39 | } |
32 | fn _module_scope(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleScope>> { | ||
33 | type ModuleScopeQuery; | ||
34 | use fn module::imp::module_scope; | ||
35 | } | ||
36 | fn _fn_syntax(fn_id: FnId) -> FnDefNode { | 40 | fn _fn_syntax(fn_id: FnId) -> FnDefNode { |
37 | type FnSyntaxQuery; | 41 | type FnSyntaxQuery; |
38 | // Don't retain syntax trees in memory | 42 | // Don't retain syntax trees in memory |
diff --git a/crates/ra_analysis/src/descriptors/module/imp.rs b/crates/ra_analysis/src/descriptors/module/imp.rs index defe87216..d4dce861f 100644 --- a/crates/ra_analysis/src/descriptors/module/imp.rs +++ b/crates/ra_analysis/src/descriptors/module/imp.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast::{self, ModuleItemOwner, NameOwner}, | 4 | ast::{self, NameOwner}, |
5 | SmolStr, | 5 | SmolStr, |
6 | }; | 6 | }; |
7 | use relative_path::RelativePathBuf; | 7 | use relative_path::RelativePathBuf; |
@@ -15,7 +15,7 @@ use crate::{ | |||
15 | }; | 15 | }; |
16 | 16 | ||
17 | use super::{ | 17 | use super::{ |
18 | LinkData, LinkId, ModuleData, ModuleId, ModuleScope, ModuleSource, ModuleSourceNode, | 18 | LinkData, LinkId, ModuleData, ModuleId, ModuleSource, ModuleSourceNode, |
19 | ModuleTree, Problem, | 19 | ModuleTree, Problem, |
20 | }; | 20 | }; |
21 | 21 | ||
@@ -81,23 +81,6 @@ pub(crate) fn modules<'a>( | |||
81 | }) | 81 | }) |
82 | } | 82 | } |
83 | 83 | ||
84 | pub(crate) fn module_scope( | ||
85 | db: &impl DescriptorDatabase, | ||
86 | source_root_id: SourceRootId, | ||
87 | module_id: ModuleId, | ||
88 | ) -> Cancelable<Arc<ModuleScope>> { | ||
89 | let tree = db._module_tree(source_root_id)?; | ||
90 | let source = module_id.source(&tree).resolve(db); | ||
91 | let res = match source { | ||
92 | ModuleSourceNode::SourceFile(it) => ModuleScope::new(it.borrowed().items()), | ||
93 | ModuleSourceNode::Module(it) => match it.borrowed().item_list() { | ||
94 | Some(items) => ModuleScope::new(items.items()), | ||
95 | None => ModuleScope::new(std::iter::empty()), | ||
96 | }, | ||
97 | }; | ||
98 | Ok(Arc::new(res)) | ||
99 | } | ||
100 | |||
101 | pub(crate) fn module_tree( | 84 | pub(crate) fn module_tree( |
102 | db: &impl DescriptorDatabase, | 85 | db: &impl DescriptorDatabase, |
103 | source_root: SourceRootId, | 86 | source_root: SourceRootId, |
diff --git a/crates/ra_analysis/src/descriptors/module/mod.rs b/crates/ra_analysis/src/descriptors/module/mod.rs index 047454cff..cfdffcdbc 100644 --- a/crates/ra_analysis/src/descriptors/module/mod.rs +++ b/crates/ra_analysis/src/descriptors/module/mod.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | pub(super) mod imp; | 1 | pub(super) mod imp; |
2 | pub(crate) mod scope; | 2 | pub(super) mod nameres; |
3 | 3 | ||
4 | use std::sync::Arc; | 4 | use std::sync::Arc; |
5 | 5 | ||
@@ -18,7 +18,7 @@ use crate::{ | |||
18 | input::SourceRootId | 18 | input::SourceRootId |
19 | }; | 19 | }; |
20 | 20 | ||
21 | pub(crate) use self::scope::ModuleScope; | 21 | pub(crate) use self::{nameres::ModuleScope}; |
22 | 22 | ||
23 | /// `ModuleDescriptor` is API entry point to get all the information | 23 | /// `ModuleDescriptor` is API entry point to get all the information |
24 | /// about a particular module. | 24 | /// about a particular module. |
@@ -102,9 +102,11 @@ impl ModuleDescriptor { | |||
102 | 102 | ||
103 | /// The root of the tree this module is part of | 103 | /// The root of the tree this module is part of |
104 | pub fn crate_root(&self) -> ModuleDescriptor { | 104 | pub fn crate_root(&self) -> ModuleDescriptor { |
105 | generate(Some(self.clone()), |it| it.parent()) | 105 | let root_id = self.module_id.crate_root(&self.tree); |
106 | .last() | 106 | ModuleDescriptor { |
107 | .unwrap() | 107 | module_id: root_id, |
108 | ..self.clone() | ||
109 | } | ||
108 | } | 110 | } |
109 | 111 | ||
110 | /// `name` is `None` for the crate's root module | 112 | /// `name` is `None` for the crate's root module |
@@ -123,8 +125,10 @@ impl ModuleDescriptor { | |||
123 | } | 125 | } |
124 | 126 | ||
125 | /// Returns a `ModuleScope`: a set of items, visible in this module. | 127 | /// Returns a `ModuleScope`: a set of items, visible in this module. |
126 | pub fn scope(&self, db: &impl DescriptorDatabase) -> Cancelable<Arc<ModuleScope>> { | 128 | pub(crate) fn scope(&self, db: &impl DescriptorDatabase) -> Cancelable<ModuleScope> { |
127 | db._module_scope(self.source_root_id, self.module_id) | 129 | let item_map = db._item_map(self.source_root_id)?; |
130 | let res = item_map.per_module[&self.module_id].clone(); | ||
131 | Ok(res) | ||
128 | } | 132 | } |
129 | 133 | ||
130 | pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> { | 134 | pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> { |
@@ -146,6 +150,13 @@ pub(crate) struct ModuleTree { | |||
146 | } | 150 | } |
147 | 151 | ||
148 | impl ModuleTree { | 152 | impl ModuleTree { |
153 | fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a { | ||
154 | self.mods | ||
155 | .iter() | ||
156 | .enumerate() | ||
157 | .map(|(idx, _)| ModuleId(idx as u32)) | ||
158 | } | ||
159 | |||
149 | fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> { | 160 | fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> { |
150 | self.mods | 161 | self.mods |
151 | .iter() | 162 | .iter() |
@@ -204,6 +215,11 @@ impl ModuleId { | |||
204 | let link = self.parent_link(tree)?; | 215 | let link = self.parent_link(tree)?; |
205 | Some(tree.link(link).owner) | 216 | Some(tree.link(link).owner) |
206 | } | 217 | } |
218 | fn crate_root(self, tree: &ModuleTree) -> ModuleId { | ||
219 | generate(Some(self), move |it| it.parent(tree)) | ||
220 | .last() | ||
221 | .unwrap() | ||
222 | } | ||
207 | fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> { | 223 | fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> { |
208 | let link = tree | 224 | let link = tree |
209 | .module(self) | 225 | .module(self) |
@@ -213,6 +229,13 @@ impl ModuleId { | |||
213 | .find(|it| it.name == name)?; | 229 | .find(|it| it.name == name)?; |
214 | Some(*link.points_to.first()?) | 230 | Some(*link.points_to.first()?) |
215 | } | 231 | } |
232 | fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator<Item = (SmolStr, ModuleId)> + 'a { | ||
233 | tree.module(self).children.iter().filter_map(move |&it| { | ||
234 | let link = tree.link(it); | ||
235 | let module = *link.points_to.first()?; | ||
236 | Some((link.name.clone(), module)) | ||
237 | }) | ||
238 | } | ||
216 | fn problems(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> Vec<(SyntaxNode, Problem)> { | 239 | fn problems(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> Vec<(SyntaxNode, Problem)> { |
217 | tree.module(self) | 240 | tree.module(self) |
218 | .children | 241 | .children |
diff --git a/crates/ra_analysis/src/descriptors/module/nameres.rs b/crates/ra_analysis/src/descriptors/module/nameres.rs new file mode 100644 index 000000000..c5bf467ca --- /dev/null +++ b/crates/ra_analysis/src/descriptors/module/nameres.rs | |||
@@ -0,0 +1,453 @@ | |||
1 | //! Name resolution algorithm | ||
2 | use std::{ | ||
3 | sync::Arc, | ||
4 | time::Instant, | ||
5 | }; | ||
6 | |||
7 | use rustc_hash::FxHashMap; | ||
8 | |||
9 | use ra_syntax::{ | ||
10 | SmolStr, SyntaxKind::{self, *}, | ||
11 | ast::{self, AstNode, ModuleItemOwner} | ||
12 | }; | ||
13 | |||
14 | use crate::{ | ||
15 | Cancelable, | ||
16 | loc2id::{DefId, DefLoc}, | ||
17 | descriptors::{ | ||
18 | DescriptorDatabase, | ||
19 | module::{ModuleId, ModuleTree, ModuleSourceNode}, | ||
20 | }, | ||
21 | syntax_ptr::{LocalSyntaxPtr}, | ||
22 | input::SourceRootId, | ||
23 | }; | ||
24 | |||
25 | /// Item map is the result of the name resolution. Item map contains, for each | ||
26 | /// module, the set of visible items. | ||
27 | #[derive(Default, Debug, PartialEq, Eq)] | ||
28 | pub(crate) struct ItemMap { | ||
29 | pub(crate) per_module: FxHashMap<ModuleId, ModuleScope>, | ||
30 | } | ||
31 | |||
32 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | ||
33 | pub(crate) struct ModuleScope { | ||
34 | pub(crate) items: FxHashMap<SmolStr, Resolution>, | ||
35 | pub(crate) import_resolutions: FxHashMap<LocalSyntaxPtr, DefId>, | ||
36 | } | ||
37 | |||
38 | /// A set of items and imports declared inside a module, without relation to | ||
39 | /// other modules. | ||
40 | /// | ||
41 | /// This stands in-between raw syntax and name resolution and alow us to avoid | ||
42 | /// recomputing name res: if `InputModuleItems` are the same, we can avoid | ||
43 | /// running name resolution. | ||
44 | #[derive(Debug, Default, PartialEq, Eq)] | ||
45 | pub(crate) struct InputModuleItems { | ||
46 | items: Vec<ModuleItem>, | ||
47 | glob_imports: Vec<Path>, | ||
48 | imports: Vec<Path>, | ||
49 | } | ||
50 | |||
51 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
52 | struct Path { | ||
53 | kind: PathKind, | ||
54 | segments: Vec<(LocalSyntaxPtr, SmolStr)>, | ||
55 | } | ||
56 | |||
57 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
58 | enum PathKind { | ||
59 | Abs, | ||
60 | Self_, | ||
61 | Super, | ||
62 | Crate, | ||
63 | } | ||
64 | |||
65 | pub(crate) fn input_module_items( | ||
66 | db: &impl DescriptorDatabase, | ||
67 | source_root: SourceRootId, | ||
68 | module_id: ModuleId, | ||
69 | ) -> Cancelable<Arc<InputModuleItems>> { | ||
70 | let module_tree = db._module_tree(source_root)?; | ||
71 | let source = module_id.source(&module_tree); | ||
72 | let res = match source.resolve(db) { | ||
73 | ModuleSourceNode::SourceFile(it) => { | ||
74 | let items = it.borrowed().items(); | ||
75 | InputModuleItems::new(items) | ||
76 | } | ||
77 | ModuleSourceNode::Module(it) => { | ||
78 | let items = it | ||
79 | .borrowed() | ||
80 | .item_list() | ||
81 | .into_iter() | ||
82 | .flat_map(|it| it.items()); | ||
83 | InputModuleItems::new(items) | ||
84 | } | ||
85 | }; | ||
86 | Ok(Arc::new(res)) | ||
87 | } | ||
88 | |||
89 | pub(crate) fn item_map( | ||
90 | db: &impl DescriptorDatabase, | ||
91 | source_root: SourceRootId, | ||
92 | ) -> Cancelable<Arc<ItemMap>> { | ||
93 | let start = Instant::now(); | ||
94 | let module_tree = db._module_tree(source_root)?; | ||
95 | let input = module_tree | ||
96 | .modules() | ||
97 | .map(|id| { | ||
98 | let items = db._input_module_items(source_root, id)?; | ||
99 | Ok((id, items)) | ||
100 | }) | ||
101 | .collect::<Cancelable<FxHashMap<_, _>>>()?; | ||
102 | |||
103 | let mut resolver = Resolver { | ||
104 | db: db, | ||
105 | input: &input, | ||
106 | source_root, | ||
107 | module_tree, | ||
108 | result: ItemMap::default(), | ||
109 | }; | ||
110 | resolver.resolve()?; | ||
111 | let res = resolver.result; | ||
112 | let elapsed = start.elapsed(); | ||
113 | log::info!("item_map: {:?}", elapsed); | ||
114 | Ok(Arc::new(res)) | ||
115 | } | ||
116 | |||
117 | /// Resolution is basically `DefId` atm, but it should account for stuff like | ||
118 | /// multiple namespaces, ambiguity and errors. | ||
119 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
120 | pub(crate) struct Resolution { | ||
121 | /// None for unresolved | ||
122 | pub(crate) def_id: Option<DefId>, | ||
123 | /// ident by whitch this is imported into local scope. | ||
124 | /// TODO: make this offset-independent. | ||
125 | pub(crate) import_name: Option<LocalSyntaxPtr>, | ||
126 | } | ||
127 | |||
128 | // #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
129 | // enum Namespace { | ||
130 | // Types, | ||
131 | // Values, | ||
132 | // } | ||
133 | |||
134 | // #[derive(Debug)] | ||
135 | // struct PerNs<T> { | ||
136 | // types: Option<T>, | ||
137 | // values: Option<T>, | ||
138 | // } | ||
139 | |||
140 | #[derive(Debug, PartialEq, Eq)] | ||
141 | struct ModuleItem { | ||
142 | ptr: LocalSyntaxPtr, | ||
143 | name: SmolStr, | ||
144 | kind: SyntaxKind, | ||
145 | vis: Vis, | ||
146 | } | ||
147 | |||
148 | #[derive(Debug, PartialEq, Eq)] | ||
149 | enum Vis { | ||
150 | // Priv, | ||
151 | Other, | ||
152 | } | ||
153 | |||
154 | impl InputModuleItems { | ||
155 | fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> InputModuleItems { | ||
156 | let mut res = InputModuleItems::default(); | ||
157 | for item in items { | ||
158 | res.add_item(item); | ||
159 | } | ||
160 | res | ||
161 | } | ||
162 | |||
163 | fn add_item(&mut self, item: ast::ModuleItem) -> Option<()> { | ||
164 | match item { | ||
165 | ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(it)?), | ||
166 | ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(it)?), | ||
167 | ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(it)?), | ||
168 | ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(it)?), | ||
169 | ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(it)?), | ||
170 | ast::ModuleItem::ImplItem(_) => { | ||
171 | // impls don't define items | ||
172 | } | ||
173 | ast::ModuleItem::UseItem(it) => self.add_use_item(it), | ||
174 | ast::ModuleItem::ExternCrateItem(_) => { | ||
175 | // TODO | ||
176 | } | ||
177 | ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(it)?), | ||
178 | ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(it)?), | ||
179 | ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(it)?), | ||
180 | } | ||
181 | Some(()) | ||
182 | } | ||
183 | |||
184 | fn add_use_item(&mut self, item: ast::UseItem) { | ||
185 | if let Some(tree) = item.use_tree() { | ||
186 | self.add_use_tree(None, tree); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | fn add_use_tree(&mut self, prefix: Option<Path>, tree: ast::UseTree) { | ||
191 | if let Some(use_tree_list) = tree.use_tree_list() { | ||
192 | let prefix = match tree.path() { | ||
193 | None => prefix, | ||
194 | Some(path) => match convert_path(prefix, path) { | ||
195 | Some(it) => Some(it), | ||
196 | None => return, // TODO: report errors somewhere | ||
197 | }, | ||
198 | }; | ||
199 | for tree in use_tree_list.use_trees() { | ||
200 | self.add_use_tree(prefix.clone(), tree); | ||
201 | } | ||
202 | } else { | ||
203 | if let Some(path) = tree.path() { | ||
204 | if let Some(path) = convert_path(prefix, path) { | ||
205 | if tree.has_star() { | ||
206 | &mut self.glob_imports | ||
207 | } else { | ||
208 | &mut self.imports | ||
209 | } | ||
210 | .push(path); | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | |||
217 | fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> { | ||
218 | let prefix = if let Some(qual) = path.qualifier() { | ||
219 | Some(convert_path(prefix, qual)?) | ||
220 | } else { | ||
221 | None | ||
222 | }; | ||
223 | let segment = path.segment()?; | ||
224 | let res = match segment.kind()? { | ||
225 | ast::PathSegmentKind::Name(name) => { | ||
226 | let mut res = prefix.unwrap_or_else(|| Path { | ||
227 | kind: PathKind::Abs, | ||
228 | segments: Vec::with_capacity(1), | ||
229 | }); | ||
230 | let ptr = LocalSyntaxPtr::new(name.syntax()); | ||
231 | res.segments.push((ptr, name.text())); | ||
232 | res | ||
233 | } | ||
234 | ast::PathSegmentKind::CrateKw => { | ||
235 | if prefix.is_some() { | ||
236 | return None; | ||
237 | } | ||
238 | Path { | ||
239 | kind: PathKind::Crate, | ||
240 | segments: Vec::new(), | ||
241 | } | ||
242 | } | ||
243 | ast::PathSegmentKind::SelfKw => { | ||
244 | if prefix.is_some() { | ||
245 | return None; | ||
246 | } | ||
247 | Path { | ||
248 | kind: PathKind::Self_, | ||
249 | segments: Vec::new(), | ||
250 | } | ||
251 | } | ||
252 | ast::PathSegmentKind::SuperKw => { | ||
253 | if prefix.is_some() { | ||
254 | return None; | ||
255 | } | ||
256 | Path { | ||
257 | kind: PathKind::Super, | ||
258 | segments: Vec::new(), | ||
259 | } | ||
260 | } | ||
261 | }; | ||
262 | Some(res) | ||
263 | } | ||
264 | |||
265 | impl ModuleItem { | ||
266 | fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> { | ||
267 | let name = item.name()?.text(); | ||
268 | let ptr = LocalSyntaxPtr::new(item.syntax()); | ||
269 | let kind = item.syntax().kind(); | ||
270 | let vis = Vis::Other; | ||
271 | let res = ModuleItem { | ||
272 | ptr, | ||
273 | name, | ||
274 | kind, | ||
275 | vis, | ||
276 | }; | ||
277 | Some(res) | ||
278 | } | ||
279 | } | ||
280 | |||
281 | struct Resolver<'a, DB> { | ||
282 | db: &'a DB, | ||
283 | input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>, | ||
284 | source_root: SourceRootId, | ||
285 | module_tree: Arc<ModuleTree>, | ||
286 | result: ItemMap, | ||
287 | } | ||
288 | |||
289 | impl<'a, DB> Resolver<'a, DB> | ||
290 | where | ||
291 | DB: DescriptorDatabase, | ||
292 | { | ||
293 | fn resolve(&mut self) -> Cancelable<()> { | ||
294 | for (&module_id, items) in self.input.iter() { | ||
295 | self.populate_module(module_id, items) | ||
296 | } | ||
297 | |||
298 | for &module_id in self.input.keys() { | ||
299 | crate::db::check_canceled(self.db)?; | ||
300 | self.resolve_imports(module_id); | ||
301 | } | ||
302 | Ok(()) | ||
303 | } | ||
304 | |||
305 | fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) { | ||
306 | let file_id = module_id.source(&self.module_tree).file_id(); | ||
307 | |||
308 | let mut module_items = ModuleScope::default(); | ||
309 | |||
310 | for import in input.imports.iter() { | ||
311 | if let Some((ptr, name)) = import.segments.last() { | ||
312 | module_items.items.insert( | ||
313 | name.clone(), | ||
314 | Resolution { | ||
315 | def_id: None, | ||
316 | import_name: Some(*ptr), | ||
317 | }, | ||
318 | ); | ||
319 | } | ||
320 | } | ||
321 | |||
322 | for item in input.items.iter() { | ||
323 | if item.kind == MODULE { | ||
324 | // handle submodules separatelly | ||
325 | continue; | ||
326 | } | ||
327 | let ptr = item.ptr.into_global(file_id); | ||
328 | let def_loc = DefLoc::Item { ptr }; | ||
329 | let def_id = self.db.id_maps().def_id(def_loc); | ||
330 | let resolution = Resolution { | ||
331 | def_id: Some(def_id), | ||
332 | import_name: None, | ||
333 | }; | ||
334 | module_items.items.insert(item.name.clone(), resolution); | ||
335 | } | ||
336 | |||
337 | for (name, mod_id) in module_id.children(&self.module_tree) { | ||
338 | let def_loc = DefLoc::Module { | ||
339 | id: mod_id, | ||
340 | source_root: self.source_root, | ||
341 | }; | ||
342 | let def_id = self.db.id_maps().def_id(def_loc); | ||
343 | let resolution = Resolution { | ||
344 | def_id: Some(def_id), | ||
345 | import_name: None, | ||
346 | }; | ||
347 | module_items.items.insert(name, resolution); | ||
348 | } | ||
349 | |||
350 | self.result.per_module.insert(module_id, module_items); | ||
351 | } | ||
352 | |||
353 | fn resolve_imports(&mut self, module_id: ModuleId) { | ||
354 | for import in self.input[&module_id].imports.iter() { | ||
355 | self.resolve_import(module_id, import); | ||
356 | } | ||
357 | } | ||
358 | |||
359 | fn resolve_import(&mut self, module_id: ModuleId, import: &Path) { | ||
360 | let mut curr = match import.kind { | ||
361 | // TODO: handle extern crates | ||
362 | PathKind::Abs => return, | ||
363 | PathKind::Self_ => module_id, | ||
364 | PathKind::Super => { | ||
365 | match module_id.parent(&self.module_tree) { | ||
366 | Some(it) => it, | ||
367 | // TODO: error | ||
368 | None => return, | ||
369 | } | ||
370 | } | ||
371 | PathKind::Crate => module_id.crate_root(&self.module_tree), | ||
372 | }; | ||
373 | |||
374 | for (i, (ptr, name)) in import.segments.iter().enumerate() { | ||
375 | let is_last = i == import.segments.len() - 1; | ||
376 | |||
377 | let def_id = match self.result.per_module[&curr].items.get(name) { | ||
378 | None => return, | ||
379 | Some(res) => match res.def_id { | ||
380 | Some(it) => it, | ||
381 | None => return, | ||
382 | }, | ||
383 | }; | ||
384 | |||
385 | self.update(module_id, |items| { | ||
386 | items.import_resolutions.insert(*ptr, def_id); | ||
387 | }); | ||
388 | |||
389 | if !is_last { | ||
390 | curr = match self.db.id_maps().def_loc(def_id) { | ||
391 | DefLoc::Module { id, .. } => id, | ||
392 | _ => return, | ||
393 | } | ||
394 | } else { | ||
395 | self.update(module_id, |items| { | ||
396 | let res = Resolution { | ||
397 | def_id: Some(def_id), | ||
398 | import_name: Some(*ptr), | ||
399 | }; | ||
400 | items.items.insert(name.clone(), res); | ||
401 | }) | ||
402 | } | ||
403 | } | ||
404 | } | ||
405 | |||
406 | fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) { | ||
407 | let module_items = self.result.per_module.get_mut(&module_id).unwrap(); | ||
408 | f(module_items) | ||
409 | } | ||
410 | } | ||
411 | |||
412 | #[cfg(test)] | ||
413 | mod tests { | ||
414 | use crate::{ | ||
415 | mock_analysis::analysis_and_position, | ||
416 | descriptors::{DescriptorDatabase, module::ModuleDescriptor}, | ||
417 | input::FilesDatabase, | ||
418 | }; | ||
419 | use super::*; | ||
420 | |||
421 | fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) { | ||
422 | let (analysis, pos) = analysis_and_position(fixture); | ||
423 | let db = analysis.imp.db; | ||
424 | let source_root = db.file_source_root(pos.file_id); | ||
425 | let descr = ModuleDescriptor::guess_from_position(&*db, pos) | ||
426 | .unwrap() | ||
427 | .unwrap(); | ||
428 | let module_id = descr.module_id; | ||
429 | (db._item_map(source_root).unwrap(), module_id) | ||
430 | } | ||
431 | |||
432 | #[test] | ||
433 | fn test_item_map() { | ||
434 | let (item_map, module_id) = item_map( | ||
435 | " | ||
436 | //- /lib.rs | ||
437 | mod foo; | ||
438 | |||
439 | use crate::foo::bar::Baz; | ||
440 | <|> | ||
441 | |||
442 | //- /foo/mod.rs | ||
443 | pub mod bar; | ||
444 | |||
445 | //- /foo/bar.rs | ||
446 | pub struct Baz; | ||
447 | ", | ||
448 | ); | ||
449 | let name = SmolStr::from("Baz"); | ||
450 | let resolution = &item_map.per_module[&module_id].items[&name]; | ||
451 | assert!(resolution.def_id.is_some()); | ||
452 | } | ||
453 | } | ||
diff --git a/crates/ra_analysis/src/descriptors/module/scope.rs b/crates/ra_analysis/src/descriptors/module/scope.rs deleted file mode 100644 index 4490228e4..000000000 --- a/crates/ra_analysis/src/descriptors/module/scope.rs +++ /dev/null | |||
@@ -1,124 +0,0 @@ | |||
1 | //! Backend for module-level scope resolution & completion | ||
2 | |||
3 | use ra_syntax::{ast, AstNode, SmolStr}; | ||
4 | |||
5 | use crate::syntax_ptr::LocalSyntaxPtr; | ||
6 | |||
7 | /// `ModuleScope` contains all named items declared in the scope. | ||
8 | #[derive(Debug, PartialEq, Eq)] | ||
9 | pub(crate) struct ModuleScope { | ||
10 | entries: Vec<Entry>, | ||
11 | } | ||
12 | |||
13 | /// `Entry` is a single named declaration iside a module. | ||
14 | #[derive(Debug, PartialEq, Eq)] | ||
15 | pub(crate) struct Entry { | ||
16 | ptr: LocalSyntaxPtr, | ||
17 | kind: EntryKind, | ||
18 | name: SmolStr, | ||
19 | } | ||
20 | |||
21 | #[derive(Debug, PartialEq, Eq)] | ||
22 | enum EntryKind { | ||
23 | Item, | ||
24 | Import, | ||
25 | } | ||
26 | |||
27 | impl ModuleScope { | ||
28 | pub(super) fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> ModuleScope { | ||
29 | let mut entries = Vec::new(); | ||
30 | for item in items { | ||
31 | let entry = match item { | ||
32 | ast::ModuleItem::StructDef(item) => Entry::new(item), | ||
33 | ast::ModuleItem::EnumDef(item) => Entry::new(item), | ||
34 | ast::ModuleItem::FnDef(item) => Entry::new(item), | ||
35 | ast::ModuleItem::ConstDef(item) => Entry::new(item), | ||
36 | ast::ModuleItem::StaticDef(item) => Entry::new(item), | ||
37 | ast::ModuleItem::TraitDef(item) => Entry::new(item), | ||
38 | ast::ModuleItem::TypeDef(item) => Entry::new(item), | ||
39 | ast::ModuleItem::Module(item) => Entry::new(item), | ||
40 | ast::ModuleItem::UseItem(item) => { | ||
41 | if let Some(tree) = item.use_tree() { | ||
42 | collect_imports(tree, &mut entries); | ||
43 | } | ||
44 | continue; | ||
45 | } | ||
46 | ast::ModuleItem::ExternCrateItem(_) | ast::ModuleItem::ImplItem(_) => continue, | ||
47 | }; | ||
48 | entries.extend(entry) | ||
49 | } | ||
50 | |||
51 | ModuleScope { entries } | ||
52 | } | ||
53 | |||
54 | pub fn entries(&self) -> &[Entry] { | ||
55 | self.entries.as_slice() | ||
56 | } | ||
57 | } | ||
58 | |||
59 | impl Entry { | ||
60 | fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<Entry> { | ||
61 | let name = item.name()?; | ||
62 | Some(Entry { | ||
63 | name: name.text(), | ||
64 | ptr: LocalSyntaxPtr::new(name.syntax()), | ||
65 | kind: EntryKind::Item, | ||
66 | }) | ||
67 | } | ||
68 | fn new_import(path: ast::Path) -> Option<Entry> { | ||
69 | let name_ref = path.segment()?.name_ref()?; | ||
70 | Some(Entry { | ||
71 | name: name_ref.text(), | ||
72 | ptr: LocalSyntaxPtr::new(name_ref.syntax()), | ||
73 | kind: EntryKind::Import, | ||
74 | }) | ||
75 | } | ||
76 | pub fn name(&self) -> &SmolStr { | ||
77 | &self.name | ||
78 | } | ||
79 | pub fn ptr(&self) -> LocalSyntaxPtr { | ||
80 | self.ptr | ||
81 | } | ||
82 | } | ||
83 | |||
84 | fn collect_imports(tree: ast::UseTree, acc: &mut Vec<Entry>) { | ||
85 | if let Some(use_tree_list) = tree.use_tree_list() { | ||
86 | return use_tree_list | ||
87 | .use_trees() | ||
88 | .for_each(|it| collect_imports(it, acc)); | ||
89 | } | ||
90 | if let Some(path) = tree.path() { | ||
91 | acc.extend(Entry::new_import(path)); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | #[cfg(test)] | ||
96 | mod tests { | ||
97 | use super::*; | ||
98 | use ra_syntax::{ast::ModuleItemOwner, SourceFileNode}; | ||
99 | |||
100 | fn do_check(code: &str, expected: &[&str]) { | ||
101 | let file = SourceFileNode::parse(&code); | ||
102 | let scope = ModuleScope::new(file.ast().items()); | ||
103 | let actual = scope.entries.iter().map(|it| it.name()).collect::<Vec<_>>(); | ||
104 | assert_eq!(expected, actual.as_slice()); | ||
105 | } | ||
106 | |||
107 | #[test] | ||
108 | fn test_module_scope() { | ||
109 | do_check( | ||
110 | " | ||
111 | struct Foo; | ||
112 | enum Bar {} | ||
113 | mod baz {} | ||
114 | fn quux() {} | ||
115 | use x::{ | ||
116 | y::z, | ||
117 | t, | ||
118 | }; | ||
119 | type T = (); | ||
120 | ", | ||
121 | &["Foo", "Bar", "baz", "quux", "z", "t", "T"], | ||
122 | ) | ||
123 | } | ||
124 | } | ||
diff --git a/crates/ra_analysis/src/loc2id.rs b/crates/ra_analysis/src/loc2id.rs index 8c297156a..e4b55f9b0 100644 --- a/crates/ra_analysis/src/loc2id.rs +++ b/crates/ra_analysis/src/loc2id.rs | |||
@@ -8,7 +8,9 @@ use std::{ | |||
8 | use rustc_hash::FxHashMap; | 8 | use rustc_hash::FxHashMap; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | descriptors::module::ModuleId, | ||
11 | syntax_ptr::SyntaxPtr, | 12 | syntax_ptr::SyntaxPtr, |
13 | input::SourceRootId, | ||
12 | }; | 14 | }; |
13 | 15 | ||
14 | /// There are two principle ways to refer to things: | 16 | /// There are two principle ways to refer to things: |
@@ -89,6 +91,21 @@ macro_rules! impl_numeric_id { | |||
89 | pub(crate) struct FnId(u32); | 91 | pub(crate) struct FnId(u32); |
90 | impl_numeric_id!(FnId); | 92 | impl_numeric_id!(FnId); |
91 | 93 | ||
94 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
95 | pub(crate) struct DefId(u32); | ||
96 | impl_numeric_id!(DefId); | ||
97 | |||
98 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
99 | pub(crate) enum DefLoc { | ||
100 | Module { | ||
101 | id: ModuleId, | ||
102 | source_root: SourceRootId, | ||
103 | }, | ||
104 | Item { | ||
105 | ptr: SyntaxPtr, | ||
106 | }, | ||
107 | } | ||
108 | |||
92 | pub(crate) trait IdDatabase: salsa::Database { | 109 | pub(crate) trait IdDatabase: salsa::Database { |
93 | fn id_maps(&self) -> &IdMaps; | 110 | fn id_maps(&self) -> &IdMaps; |
94 | } | 111 | } |
@@ -105,9 +122,17 @@ impl IdMaps { | |||
105 | pub(crate) fn fn_ptr(&self, fn_id: FnId) -> SyntaxPtr { | 122 | pub(crate) fn fn_ptr(&self, fn_id: FnId) -> SyntaxPtr { |
106 | self.inner.fns.lock().id2loc(fn_id) | 123 | self.inner.fns.lock().id2loc(fn_id) |
107 | } | 124 | } |
125 | |||
126 | pub(crate) fn def_id(&self, loc: DefLoc) -> DefId { | ||
127 | self.inner.defs.lock().loc2id(&loc) | ||
128 | } | ||
129 | pub(crate) fn def_loc(&self, def_id: DefId) -> DefLoc { | ||
130 | self.inner.defs.lock().id2loc(def_id) | ||
131 | } | ||
108 | } | 132 | } |
109 | 133 | ||
110 | #[derive(Debug, Default)] | 134 | #[derive(Debug, Default)] |
111 | struct IdMapsInner { | 135 | struct IdMapsInner { |
112 | fns: Mutex<Loc2IdMap<SyntaxPtr, FnId>>, | 136 | fns: Mutex<Loc2IdMap<SyntaxPtr, FnId>>, |
137 | defs: Mutex<Loc2IdMap<DefLoc, DefId>>, | ||
113 | } | 138 | } |
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs index 719c166b5..72b2ebf97 100644 --- a/crates/ra_analysis/tests/tests.rs +++ b/crates/ra_analysis/tests/tests.rs | |||
@@ -447,8 +447,8 @@ fn test_complete_crate_path() { | |||
447 | ); | 447 | ); |
448 | let completions = analysis.completions(position).unwrap().unwrap(); | 448 | let completions = analysis.completions(position).unwrap().unwrap(); |
449 | assert_eq_dbg( | 449 | assert_eq_dbg( |
450 | r#"[CompletionItem { label: "foo", lookup: None, snippet: None }, | 450 | r#"[CompletionItem { label: "Spam", lookup: None, snippet: None }, |
451 | CompletionItem { label: "Spam", lookup: None, snippet: None }]"#, | 451 | CompletionItem { label: "foo", lookup: None, snippet: None }]"#, |
452 | &completions, | 452 | &completions, |
453 | ); | 453 | ); |
454 | } | 454 | } |
@@ -466,8 +466,8 @@ fn test_complete_crate_path_with_braces() { | |||
466 | ); | 466 | ); |
467 | let completions = analysis.completions(position).unwrap().unwrap(); | 467 | let completions = analysis.completions(position).unwrap().unwrap(); |
468 | assert_eq_dbg( | 468 | assert_eq_dbg( |
469 | r#"[CompletionItem { label: "foo", lookup: None, snippet: None }, | 469 | r#"[CompletionItem { label: "Spam", lookup: None, snippet: None }, |
470 | CompletionItem { label: "Spam", lookup: None, snippet: None }]"#, | 470 | CompletionItem { label: "foo", lookup: None, snippet: None }]"#, |
471 | &completions, | 471 | &completions, |
472 | ); | 472 | ); |
473 | } | 473 | } |
diff --git a/crates/ra_syntax/src/ast/mod.rs b/crates/ra_syntax/src/ast/mod.rs index 7077e3492..91c67119f 100644 --- a/crates/ra_syntax/src/ast/mod.rs +++ b/crates/ra_syntax/src/ast/mod.rs | |||
@@ -315,6 +315,12 @@ impl<'a> PathSegment<'a> { | |||
315 | } | 315 | } |
316 | } | 316 | } |
317 | 317 | ||
318 | impl<'a> UseTree<'a> { | ||
319 | pub fn has_star(self) -> bool { | ||
320 | self.syntax().children().any(|it| it.kind() == STAR) | ||
321 | } | ||
322 | } | ||
323 | |||
318 | impl<'a> UseTreeList<'a> { | 324 | impl<'a> UseTreeList<'a> { |
319 | pub fn parent_use_tree(self) -> UseTree<'a> { | 325 | pub fn parent_use_tree(self) -> UseTree<'a> { |
320 | self.syntax() | 326 | self.syntax() |
diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs index 8b8e9974e..e81761cf2 100644 --- a/crates/tools/src/lib.rs +++ b/crates/tools/src/lib.rs | |||
@@ -17,7 +17,7 @@ pub type Result<T> = ::std::result::Result<T, failure::Error>; | |||
17 | pub const GRAMMAR: &str = "crates/ra_syntax/src/grammar.ron"; | 17 | pub const GRAMMAR: &str = "crates/ra_syntax/src/grammar.ron"; |
18 | pub const SYNTAX_KINDS: &str = "crates/ra_syntax/src/syntax_kinds/generated.rs.tera"; | 18 | pub const SYNTAX_KINDS: &str = "crates/ra_syntax/src/syntax_kinds/generated.rs.tera"; |
19 | pub const AST: &str = "crates/ra_syntax/src/ast/generated.rs.tera"; | 19 | pub const AST: &str = "crates/ra_syntax/src/ast/generated.rs.tera"; |
20 | const TOOLCHAIN: &str = "beta-2018-10-30"; | 20 | const TOOLCHAIN: &str = "beta-2018-11-19"; |
21 | 21 | ||
22 | #[derive(Debug)] | 22 | #[derive(Debug)] |
23 | pub struct Test { | 23 | pub struct Test { |