aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-11-21 15:35:51 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-11-21 15:35:51 +0000
commitac874b64550c103249e2b951d92e2f1a8c9a5828 (patch)
tree50fdd5d9681ff57a5cd15de3c401cd478e6f3ab5
parent031bc868293539714157e3d93cc338b011f5661d (diff)
parentedeec6a41487e6458a9d96b328c9b784525d8f06 (diff)
Merge #237
237: This moves parts of completion from ad-hockery to descriptors-based resolve r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r--crates/ra_analysis/src/completion/mod.rs14
-rw-r--r--crates/ra_analysis/src/completion/reference_completion.rs65
-rw-r--r--crates/ra_analysis/src/descriptors/mod.rs5
-rw-r--r--crates/ra_analysis/src/descriptors/module/mod.rs18
-rw-r--r--crates/ra_analysis/src/descriptors/module/nameres.rs162
-rw-r--r--crates/ra_analysis/src/descriptors/path.rs153
-rw-r--r--crates/ra_editor/src/lib.rs7
-rw-r--r--crates/ra_syntax/src/grammar/items/mod.rs2
-rw-r--r--crates/ra_syntax/src/grammar/paths.rs2
9 files changed, 262 insertions, 166 deletions
diff --git a/crates/ra_analysis/src/completion/mod.rs b/crates/ra_analysis/src/completion/mod.rs
index a8a752fc7..c7717ab61 100644
--- a/crates/ra_analysis/src/completion/mod.rs
+++ b/crates/ra_analysis/src/completion/mod.rs
@@ -221,6 +221,20 @@ mod tests {
221 } 221 }
222 222
223 #[test] 223 #[test]
224 fn test_completion_self_path() {
225 check_scope_completion(
226 r"
227 use self::m::<|>;
228
229 mod m {
230 struct Bar;
231 }
232 ",
233 r#"[CompletionItem { label: "Bar", lookup: None, snippet: None }]"#,
234 );
235 }
236
237 #[test]
224 fn test_completion_mod_scope_nested() { 238 fn test_completion_mod_scope_nested() {
225 check_scope_completion( 239 check_scope_completion(
226 r" 240 r"
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs
index 84383b547..d301a3c02 100644
--- a/crates/ra_analysis/src/completion/reference_completion.rs
+++ b/crates/ra_analysis/src/completion/reference_completion.rs
@@ -10,8 +10,11 @@ use ra_syntax::{
10use crate::{ 10use crate::{
11 db::RootDatabase, 11 db::RootDatabase,
12 completion::CompletionItem, 12 completion::CompletionItem,
13 descriptors::module::{ModuleDescriptor}, 13 descriptors::{
14 descriptors::function::FnScopes, 14 module::{ModuleDescriptor},
15 function::FnScopes,
16 Path,
17 },
15 Cancelable 18 Cancelable
16}; 19};
17 20
@@ -55,7 +58,7 @@ pub(super) fn completions(
55 }), 58 }),
56 ); 59 );
57 } 60 }
58 NameRefKind::CratePath(path) => complete_path(acc, db, module, path)?, 61 NameRefKind::Path(path) => complete_path(acc, db, module, path)?,
59 NameRefKind::BareIdentInMod => { 62 NameRefKind::BareIdentInMod => {
60 let name_range = name_ref.syntax().range(); 63 let name_range = name_ref.syntax().range();
61 let top_node = name_ref 64 let top_node = name_ref
@@ -79,8 +82,8 @@ enum NameRefKind<'a> {
79 LocalRef { 82 LocalRef {
80 enclosing_fn: Option<ast::FnDef<'a>>, 83 enclosing_fn: Option<ast::FnDef<'a>>,
81 }, 84 },
82 /// NameRef is the last segment in crate:: path 85 /// NameRef is the last segment in some path
83 CratePath(Vec<ast::NameRef<'a>>), 86 Path(Path),
84 /// NameRef is bare identifier at the module's root. 87 /// NameRef is bare identifier at the module's root.
85 /// Used for keyword completion 88 /// Used for keyword completion
86 BareIdentInMod, 89 BareIdentInMod,
@@ -102,8 +105,10 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
102 let parent = name_ref.syntax().parent()?; 105 let parent = name_ref.syntax().parent()?;
103 if let Some(segment) = ast::PathSegment::cast(parent) { 106 if let Some(segment) = ast::PathSegment::cast(parent) {
104 let path = segment.parent_path(); 107 let path = segment.parent_path();
105 if let Some(crate_path) = crate_path(path) { 108 if let Some(path) = Path::from_ast(path) {
106 return Some(NameRefKind::CratePath(crate_path)); 109 if !path.is_ident() {
110 return Some(NameRefKind::Path(path));
111 }
107 } 112 }
108 if path.qualifier().is_none() { 113 if path.qualifier().is_none() {
109 let enclosing_fn = name_ref 114 let enclosing_fn = name_ref
@@ -117,32 +122,6 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
117 None 122 None
118} 123}
119 124
120fn crate_path(mut path: ast::Path) -> Option<Vec<ast::NameRef>> {
121 let mut res = Vec::new();
122 loop {
123 let segment = path.segment()?;
124 match segment.kind()? {
125 ast::PathSegmentKind::Name(name) => res.push(name),
126 ast::PathSegmentKind::CrateKw => break,
127 ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => return None,
128 }
129 path = qualifier(path)?;
130 }
131 res.reverse();
132 return Some(res);
133
134 fn qualifier(path: ast::Path) -> Option<ast::Path> {
135 if let Some(q) = path.qualifier() {
136 return Some(q);
137 }
138 // TODO: this bottom up traversal is not too precise.
139 // Should we handle do a top-down analysiss, recording results?
140 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
141 let use_tree = use_tree_list.parent_use_tree();
142 use_tree.path()
143 }
144}
145
146fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { 125fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
147 let mut shadowed = FxHashSet::default(); 126 let mut shadowed = FxHashSet::default();
148 acc.extend( 127 acc.extend(
@@ -169,9 +148,13 @@ fn complete_path(
169 acc: &mut Vec<CompletionItem>, 148 acc: &mut Vec<CompletionItem>,
170 db: &RootDatabase, 149 db: &RootDatabase,
171 module: &ModuleDescriptor, 150 module: &ModuleDescriptor,
172 crate_path: Vec<ast::NameRef>, 151 mut path: Path,
173) -> Cancelable<()> { 152) -> Cancelable<()> {
174 let target_module = match find_target_module(module, crate_path) { 153 if path.segments.is_empty() {
154 return Ok(());
155 }
156 path.segments.pop();
157 let target_module = match module.resolve_path(path) {
175 None => return Ok(()), 158 None => return Ok(()),
176 Some(it) => it, 159 Some(it) => it,
177 }; 160 };
@@ -188,18 +171,6 @@ fn complete_path(
188 Ok(()) 171 Ok(())
189} 172}
190 173
191fn find_target_module(
192 module: &ModuleDescriptor,
193 mut crate_path: Vec<ast::NameRef>,
194) -> Option<ModuleDescriptor> {
195 crate_path.pop();
196 let mut target_module = module.crate_root();
197 for name in crate_path {
198 target_module = target_module.child(name.text().as_str())?;
199 }
200 Some(target_module)
201}
202
203fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) { 174fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) {
204 acc.push(CompletionItem { 175 acc.push(CompletionItem {
205 label: "tfn".to_string(), 176 label: "tfn".to_string(),
diff --git a/crates/ra_analysis/src/descriptors/mod.rs b/crates/ra_analysis/src/descriptors/mod.rs
index 6b56d92e1..97750ea64 100644
--- a/crates/ra_analysis/src/descriptors/mod.rs
+++ b/crates/ra_analysis/src/descriptors/mod.rs
@@ -1,10 +1,11 @@
1pub(crate) mod function; 1pub(crate) mod function;
2pub(crate) mod module; 2pub(crate) mod module;
3mod path;
3 4
4use std::sync::Arc; 5use std::sync::Arc;
5 6
6use ra_syntax::{ 7use ra_syntax::{
7 ast::{self, AstNode, FnDefNode}, 8 ast::{self, FnDefNode, AstNode},
8 TextRange, 9 TextRange,
9}; 10};
10 11
@@ -18,6 +19,8 @@ use crate::{
18 Cancelable, 19 Cancelable,
19}; 20};
20 21
22pub(crate) use self::path::{Path, PathKind};
23
21salsa::query_group! { 24salsa::query_group! {
22 pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase { 25 pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase {
23 fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> { 26 fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> {
diff --git a/crates/ra_analysis/src/descriptors/module/mod.rs b/crates/ra_analysis/src/descriptors/module/mod.rs
index cfdffcdbc..acc6c1c5a 100644
--- a/crates/ra_analysis/src/descriptors/module/mod.rs
+++ b/crates/ra_analysis/src/descriptors/module/mod.rs
@@ -14,11 +14,11 @@ use relative_path::RelativePathBuf;
14 14
15use crate::{ 15use crate::{
16 db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId, FilePosition, Cancelable, 16 db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId, FilePosition, Cancelable,
17 descriptors::DescriptorDatabase, 17 descriptors::{Path, PathKind, DescriptorDatabase},
18 input::SourceRootId 18 input::SourceRootId
19}; 19};
20 20
21pub(crate) use self::{nameres::ModuleScope}; 21pub(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.
@@ -110,6 +110,7 @@ impl ModuleDescriptor {
110 } 110 }
111 111
112 /// `name` is `None` for the crate's root module 112 /// `name` is `None` for the crate's root module
113 #[allow(unused)]
113 pub fn name(&self) -> Option<SmolStr> { 114 pub fn name(&self) -> Option<SmolStr> {
114 let link = self.module_id.parent_link(&self.tree)?; 115 let link = self.module_id.parent_link(&self.tree)?;
115 Some(link.name(&self.tree)) 116 Some(link.name(&self.tree))
@@ -131,6 +132,19 @@ impl ModuleDescriptor {
131 Ok(res) 132 Ok(res)
132 } 133 }
133 134
135 pub(crate) fn resolve_path(&self, path: Path) -> Option<ModuleDescriptor> {
136 let mut curr = match path.kind {
137 PathKind::Crate => self.crate_root(),
138 PathKind::Self_ | PathKind::Plain => self.clone(),
139 PathKind::Super => self.parent()?,
140 };
141 let segments = path.segments;
142 for name in segments {
143 curr = curr.child(&name)?;
144 }
145 Some(curr)
146 }
147
134 pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> { 148 pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> {
135 self.module_id.problems(&self.tree, db) 149 self.module_id.problems(&self.tree, db)
136 } 150 }
diff --git a/crates/ra_analysis/src/descriptors/module/nameres.rs b/crates/ra_analysis/src/descriptors/module/nameres.rs
index c5bf467ca..4c555421d 100644
--- a/crates/ra_analysis/src/descriptors/module/nameres.rs
+++ b/crates/ra_analysis/src/descriptors/module/nameres.rs
@@ -1,4 +1,19 @@
1//! Name resolution algorithm 1//! Name resolution algorithm. The end result of the algorithm is `ItemMap`: a
2//! map with maps each module to it's scope: the set of items, visible in the
3//! module. That is, we only resolve imports here, name resolution of item
4//! bodies will be done in a separate step.
5//!
6//! Like Rustc, we use an interative per-crate algorithm: we start with scopes
7//! containing only directly defined items, and then iteratively resolve
8//! imports.
9//!
10//! To make this work nicely in the IDE scenarios, we place `InputModuleItems`
11//! in between raw syntax and name resolution. `InputModuleItems` are computed
12//! using only the module's syntax, and it is all directly defined items plus
13//! imports. The plain is to make `InputModuleItems` independent of local
14//! modifications (that is, typing inside a function shold not change IMIs),
15//! such that the results of name resolution can be preserved unless the module
16//! structure itself is modified.
2use std::{ 17use std::{
3 sync::Arc, 18 sync::Arc,
4 time::Instant, 19 time::Instant,
@@ -8,13 +23,14 @@ use rustc_hash::FxHashMap;
8 23
9use ra_syntax::{ 24use ra_syntax::{
10 SmolStr, SyntaxKind::{self, *}, 25 SmolStr, SyntaxKind::{self, *},
11 ast::{self, AstNode, ModuleItemOwner} 26 ast::{self, ModuleItemOwner}
12}; 27};
13 28
14use crate::{ 29use crate::{
15 Cancelable, 30 Cancelable,
16 loc2id::{DefId, DefLoc}, 31 loc2id::{DefId, DefLoc},
17 descriptors::{ 32 descriptors::{
33 Path, PathKind,
18 DescriptorDatabase, 34 DescriptorDatabase,
19 module::{ModuleId, ModuleTree, ModuleSourceNode}, 35 module::{ModuleId, ModuleTree, ModuleSourceNode},
20 }, 36 },
@@ -32,7 +48,6 @@ pub(crate) struct ItemMap {
32#[derive(Debug, Default, PartialEq, Eq, Clone)] 48#[derive(Debug, Default, PartialEq, Eq, Clone)]
33pub(crate) struct ModuleScope { 49pub(crate) struct ModuleScope {
34 pub(crate) items: FxHashMap<SmolStr, Resolution>, 50 pub(crate) items: FxHashMap<SmolStr, Resolution>,
35 pub(crate) import_resolutions: FxHashMap<LocalSyntaxPtr, DefId>,
36} 51}
37 52
38/// A set of items and imports declared inside a module, without relation to 53/// A set of items and imports declared inside a module, without relation to
@@ -44,22 +59,20 @@ pub(crate) struct ModuleScope {
44#[derive(Debug, Default, PartialEq, Eq)] 59#[derive(Debug, Default, PartialEq, Eq)]
45pub(crate) struct InputModuleItems { 60pub(crate) struct InputModuleItems {
46 items: Vec<ModuleItem>, 61 items: Vec<ModuleItem>,
47 glob_imports: Vec<Path>, 62 imports: Vec<Import>,
48 imports: Vec<Path>,
49} 63}
50 64
51#[derive(Debug, Clone, PartialEq, Eq)] 65#[derive(Debug, Clone, PartialEq, Eq)]
52struct Path { 66struct Import {
53 kind: PathKind, 67 path: Path,
54 segments: Vec<(LocalSyntaxPtr, SmolStr)>, 68 kind: ImportKind,
55} 69}
56 70
57#[derive(Debug, Clone, Copy, PartialEq, Eq)] 71#[derive(Debug, Clone, PartialEq, Eq)]
58enum PathKind { 72enum ImportKind {
59 Abs, 73 Glob,
60 Self_, 74 // TODO: make offset independent
61 Super, 75 Named(LocalSyntaxPtr),
62 Crate,
63} 76}
64 77
65pub(crate) fn input_module_items( 78pub(crate) fn input_module_items(
@@ -182,86 +195,16 @@ impl InputModuleItems {
182 } 195 }
183 196
184 fn add_use_item(&mut self, item: ast::UseItem) { 197 fn add_use_item(&mut self, item: ast::UseItem) {
185 if let Some(tree) = item.use_tree() { 198 Path::expand_use_item(item, |path, ptr| {
186 self.add_use_tree(None, tree); 199 let kind = match ptr {
187 } 200 None => ImportKind::Glob,
188 } 201 Some(ptr) => ImportKind::Named(ptr),
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 }; 202 };
199 for tree in use_tree_list.use_trees() { 203 self.imports.push(Import { kind, path })
200 self.add_use_tree(prefix.clone(), tree); 204 })
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 } 205 }
215} 206}
216 207
217fn 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
265impl ModuleItem { 208impl ModuleItem {
266 fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> { 209 fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> {
267 let name = item.name()?.text(); 210 let name = item.name()?.text();
@@ -308,14 +251,16 @@ where
308 let mut module_items = ModuleScope::default(); 251 let mut module_items = ModuleScope::default();
309 252
310 for import in input.imports.iter() { 253 for import in input.imports.iter() {
311 if let Some((ptr, name)) = import.segments.last() { 254 if let Some(name) = import.path.segments.iter().last() {
312 module_items.items.insert( 255 if let ImportKind::Named(ptr) = import.kind {
313 name.clone(), 256 module_items.items.insert(
314 Resolution { 257 name.clone(),
315 def_id: None, 258 Resolution {
316 import_name: Some(*ptr), 259 def_id: None,
317 }, 260 import_name: Some(ptr),
318 ); 261 },
262 );
263 }
319 } 264 }
320 } 265 }
321 266
@@ -356,10 +301,15 @@ where
356 } 301 }
357 } 302 }
358 303
359 fn resolve_import(&mut self, module_id: ModuleId, import: &Path) { 304 fn resolve_import(&mut self, module_id: ModuleId, import: &Import) {
360 let mut curr = match import.kind { 305 let ptr = match import.kind {
306 ImportKind::Glob => return,
307 ImportKind::Named(ptr) => ptr,
308 };
309
310 let mut curr = match import.path.kind {
361 // TODO: handle extern crates 311 // TODO: handle extern crates
362 PathKind::Abs => return, 312 PathKind::Plain => return,
363 PathKind::Self_ => module_id, 313 PathKind::Self_ => module_id,
364 PathKind::Super => { 314 PathKind::Super => {
365 match module_id.parent(&self.module_tree) { 315 match module_id.parent(&self.module_tree) {
@@ -371,8 +321,8 @@ where
371 PathKind::Crate => module_id.crate_root(&self.module_tree), 321 PathKind::Crate => module_id.crate_root(&self.module_tree),
372 }; 322 };
373 323
374 for (i, (ptr, name)) in import.segments.iter().enumerate() { 324 for (i, name) in import.path.segments.iter().enumerate() {
375 let is_last = i == import.segments.len() - 1; 325 let is_last = i == import.path.segments.len() - 1;
376 326
377 let def_id = match self.result.per_module[&curr].items.get(name) { 327 let def_id = match self.result.per_module[&curr].items.get(name) {
378 None => return, 328 None => return,
@@ -382,10 +332,6 @@ where
382 }, 332 },
383 }; 333 };
384 334
385 self.update(module_id, |items| {
386 items.import_resolutions.insert(*ptr, def_id);
387 });
388
389 if !is_last { 335 if !is_last {
390 curr = match self.db.id_maps().def_loc(def_id) { 336 curr = match self.db.id_maps().def_loc(def_id) {
391 DefLoc::Module { id, .. } => id, 337 DefLoc::Module { id, .. } => id,
@@ -395,7 +341,7 @@ where
395 self.update(module_id, |items| { 341 self.update(module_id, |items| {
396 let res = Resolution { 342 let res = Resolution {
397 def_id: Some(def_id), 343 def_id: Some(def_id),
398 import_name: Some(*ptr), 344 import_name: Some(ptr),
399 }; 345 };
400 items.items.insert(name.clone(), res); 346 items.items.insert(name.clone(), res);
401 }) 347 })
diff --git a/crates/ra_analysis/src/descriptors/path.rs b/crates/ra_analysis/src/descriptors/path.rs
new file mode 100644
index 000000000..99fca18b1
--- /dev/null
+++ b/crates/ra_analysis/src/descriptors/path.rs
@@ -0,0 +1,153 @@
1use ra_syntax::{SmolStr, ast, AstNode};
2
3use crate::syntax_ptr::LocalSyntaxPtr;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub(crate) struct Path {
7 pub(crate) kind: PathKind,
8 pub(crate) segments: Vec<SmolStr>,
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub(crate) enum PathKind {
13 Plain,
14 Self_,
15 Super,
16 Crate,
17}
18
19impl Path {
20 /// Calls `cb` with all paths, represented by this use item.
21 pub(crate) fn expand_use_item(
22 item: ast::UseItem,
23 mut cb: impl FnMut(Path, Option<LocalSyntaxPtr>),
24 ) {
25 if let Some(tree) = item.use_tree() {
26 expand_use_tree(None, tree, &mut cb);
27 }
28 }
29
30 /// Converts an `ast::Path` to `Path`. Works with use trees.
31 pub(crate) fn from_ast(mut path: ast::Path) -> Option<Path> {
32 let mut kind = PathKind::Plain;
33 let mut segments = Vec::new();
34 loop {
35 let segment = path.segment()?;
36 match segment.kind()? {
37 ast::PathSegmentKind::Name(name) => segments.push(name.text()),
38 ast::PathSegmentKind::CrateKw => {
39 kind = PathKind::Crate;
40 break;
41 }
42 ast::PathSegmentKind::SelfKw => {
43 kind = PathKind::Self_;
44 break;
45 }
46 ast::PathSegmentKind::SuperKw => {
47 kind = PathKind::Super;
48 break;
49 }
50 }
51 path = match qualifier(path) {
52 Some(it) => it,
53 None => break,
54 };
55 }
56 segments.reverse();
57 return Some(Path { kind, segments });
58
59 fn qualifier(path: ast::Path) -> Option<ast::Path> {
60 if let Some(q) = path.qualifier() {
61 return Some(q);
62 }
63 // TODO: this bottom up traversal is not too precise.
64 // Should we handle do a top-down analysiss, recording results?
65 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
66 let use_tree = use_tree_list.parent_use_tree();
67 use_tree.path()
68 }
69 }
70
71 /// `true` is this path is a single identifier, like `foo`
72 pub(crate) fn is_ident(&self) -> bool {
73 self.kind == PathKind::Plain && self.segments.len() == 1
74 }
75}
76
77fn expand_use_tree(
78 prefix: Option<Path>,
79 tree: ast::UseTree,
80 cb: &mut impl FnMut(Path, Option<LocalSyntaxPtr>),
81) {
82 if let Some(use_tree_list) = tree.use_tree_list() {
83 let prefix = match tree.path() {
84 None => prefix,
85 Some(path) => match convert_path(prefix, path) {
86 Some(it) => Some(it),
87 None => return, // TODO: report errors somewhere
88 },
89 };
90 for tree in use_tree_list.use_trees() {
91 expand_use_tree(prefix.clone(), tree, cb);
92 }
93 } else {
94 if let Some(ast_path) = tree.path() {
95 if let Some(path) = convert_path(prefix, ast_path) {
96 let ptr = if tree.has_star() {
97 None
98 } else {
99 let ptr = LocalSyntaxPtr::new(ast_path.segment().unwrap().syntax());
100 Some(ptr)
101 };
102 cb(path, ptr)
103 }
104 }
105 }
106}
107
108fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
109 let prefix = if let Some(qual) = path.qualifier() {
110 Some(convert_path(prefix, qual)?)
111 } else {
112 None
113 };
114 let segment = path.segment()?;
115 let res = match segment.kind()? {
116 ast::PathSegmentKind::Name(name) => {
117 let mut res = prefix.unwrap_or_else(|| Path {
118 kind: PathKind::Plain,
119 segments: Vec::with_capacity(1),
120 });
121 res.segments.push(name.text());
122 res
123 }
124 ast::PathSegmentKind::CrateKw => {
125 if prefix.is_some() {
126 return None;
127 }
128 Path {
129 kind: PathKind::Crate,
130 segments: Vec::new(),
131 }
132 }
133 ast::PathSegmentKind::SelfKw => {
134 if prefix.is_some() {
135 return None;
136 }
137 Path {
138 kind: PathKind::Self_,
139 segments: Vec::new(),
140 }
141 }
142 ast::PathSegmentKind::SuperKw => {
143 if prefix.is_some() {
144 return None;
145 }
146 Path {
147 kind: PathKind::Super,
148 segments: Vec::new(),
149 }
150 }
151 };
152 Some(res)
153}
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs
index ff4e8303d..c6b116159 100644
--- a/crates/ra_editor/src/lib.rs
+++ b/crates/ra_editor/src/lib.rs
@@ -148,12 +148,7 @@ pub fn find_node_at_offset<'a, N: AstNode<'a>>(
148 syntax: SyntaxNodeRef<'a>, 148 syntax: SyntaxNodeRef<'a>,
149 offset: TextUnit, 149 offset: TextUnit,
150) -> Option<N> { 150) -> Option<N> {
151 let leaves = find_leaf_at_offset(syntax, offset); 151 find_leaf_at_offset(syntax, offset).find_map(|leaf| leaf.ancestors().find_map(N::cast))
152 let leaf = leaves
153 .clone()
154 .find(|leaf| !leaf.kind().is_trivia())
155 .or_else(|| leaves.right_biased())?;
156 leaf.ancestors().filter_map(N::cast).next()
157} 152}
158 153
159#[cfg(test)] 154#[cfg(test)]
diff --git a/crates/ra_syntax/src/grammar/items/mod.rs b/crates/ra_syntax/src/grammar/items/mod.rs
index 06c6b5e6e..682266908 100644
--- a/crates/ra_syntax/src/grammar/items/mod.rs
+++ b/crates/ra_syntax/src/grammar/items/mod.rs
@@ -29,7 +29,7 @@ pub(super) enum ItemFlavor {
29 Trait, 29 Trait,
30} 30}
31 31
32const ITEM_RECOVERY_SET: TokenSet = token_set![ 32pub(super) const ITEM_RECOVERY_SET: TokenSet = token_set![
33 FN_KW, STRUCT_KW, ENUM_KW, IMPL_KW, TRAIT_KW, CONST_KW, STATIC_KW, LET_KW, MOD_KW, PUB_KW, 33 FN_KW, STRUCT_KW, ENUM_KW, IMPL_KW, TRAIT_KW, CONST_KW, STATIC_KW, LET_KW, MOD_KW, PUB_KW,
34 CRATE_KW 34 CRATE_KW
35]; 35];
diff --git a/crates/ra_syntax/src/grammar/paths.rs b/crates/ra_syntax/src/grammar/paths.rs
index a35a339cc..33a11886c 100644
--- a/crates/ra_syntax/src/grammar/paths.rs
+++ b/crates/ra_syntax/src/grammar/paths.rs
@@ -78,7 +78,7 @@ fn path_segment(p: &mut Parser, mode: Mode, first: bool) {
78 // use crate::foo; 78 // use crate::foo;
79 SELF_KW | SUPER_KW | CRATE_KW => p.bump(), 79 SELF_KW | SUPER_KW | CRATE_KW => p.bump(),
80 _ => { 80 _ => {
81 p.err_and_bump("expected identifier"); 81 p.err_recover("expected identifier", items::ITEM_RECOVERY_SET);
82 } 82 }
83 }; 83 };
84 } 84 }