diff options
Diffstat (limited to 'crates/ra_analysis/src/descriptors/module/scope.rs')
-rw-r--r-- | crates/ra_analysis/src/descriptors/module/scope.rs | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/crates/ra_analysis/src/descriptors/module/scope.rs b/crates/ra_analysis/src/descriptors/module/scope.rs new file mode 100644 index 000000000..da58ddce0 --- /dev/null +++ b/crates/ra_analysis/src/descriptors/module/scope.rs | |||
@@ -0,0 +1,128 @@ | |||
1 | //! Backend for module-level scope resolution & completion | ||
2 | |||
3 | |||
4 | use ra_syntax::{ | ||
5 | ast::{self, AstChildren, ModuleItemOwner}, | ||
6 | File, AstNode, SmolStr, SyntaxNode, SyntaxNodeRef, | ||
7 | }; | ||
8 | |||
9 | use crate::syntax_ptr::LocalSyntaxPtr; | ||
10 | |||
11 | /// `ModuleScope` contains all named items declared in the scope. | ||
12 | #[derive(Debug, PartialEq, Eq)] | ||
13 | pub(crate) struct ModuleScope { | ||
14 | entries: Vec<Entry>, | ||
15 | } | ||
16 | |||
17 | /// `Entry` is a single named declaration iside a module. | ||
18 | #[derive(Debug, PartialEq, Eq)] | ||
19 | pub(crate) struct Entry { | ||
20 | ptr: LocalSyntaxPtr, | ||
21 | kind: EntryKind, | ||
22 | name: SmolStr, | ||
23 | } | ||
24 | |||
25 | #[derive(Debug, PartialEq, Eq)] | ||
26 | enum EntryKind { | ||
27 | Item, | ||
28 | Import, | ||
29 | } | ||
30 | |||
31 | impl ModuleScope { | ||
32 | pub fn new(file: &File) -> ModuleScope { | ||
33 | let mut entries = Vec::new(); | ||
34 | for item in file.ast().items() { | ||
35 | let entry = match item { | ||
36 | ast::ModuleItem::StructDef(item) => Entry::new(item), | ||
37 | ast::ModuleItem::EnumDef(item) => Entry::new(item), | ||
38 | ast::ModuleItem::FnDef(item) => Entry::new(item), | ||
39 | ast::ModuleItem::ConstDef(item) => Entry::new(item), | ||
40 | ast::ModuleItem::StaticDef(item) => Entry::new(item), | ||
41 | ast::ModuleItem::TraitDef(item) => Entry::new(item), | ||
42 | ast::ModuleItem::TypeDef(item) => Entry::new(item), | ||
43 | ast::ModuleItem::Module(item) => Entry::new(item), | ||
44 | ast::ModuleItem::UseItem(item) => { | ||
45 | if let Some(tree) = item.use_tree() { | ||
46 | collect_imports(tree, &mut entries); | ||
47 | } | ||
48 | continue; | ||
49 | } | ||
50 | ast::ModuleItem::ExternCrateItem(_) | ast::ModuleItem::ImplItem(_) => continue, | ||
51 | }; | ||
52 | entries.extend(entry) | ||
53 | } | ||
54 | |||
55 | ModuleScope { entries } | ||
56 | } | ||
57 | |||
58 | pub fn entries(&self) -> &[Entry] { | ||
59 | self.entries.as_slice() | ||
60 | } | ||
61 | } | ||
62 | |||
63 | impl Entry { | ||
64 | fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<Entry> { | ||
65 | let name = item.name()?; | ||
66 | Some(Entry { | ||
67 | name: name.text(), | ||
68 | ptr: LocalSyntaxPtr::new(name.syntax()), | ||
69 | kind: EntryKind::Item, | ||
70 | }) | ||
71 | } | ||
72 | fn new_import(path: ast::Path) -> Option<Entry> { | ||
73 | let name_ref = path.segment()?.name_ref()?; | ||
74 | Some(Entry { | ||
75 | name: name_ref.text(), | ||
76 | ptr: LocalSyntaxPtr::new(name_ref.syntax()), | ||
77 | kind: EntryKind::Import, | ||
78 | }) | ||
79 | } | ||
80 | pub fn name(&self) -> &SmolStr { | ||
81 | &self.name | ||
82 | } | ||
83 | pub fn ptr(&self) -> LocalSyntaxPtr { | ||
84 | self.ptr | ||
85 | } | ||
86 | } | ||
87 | |||
88 | fn collect_imports(tree: ast::UseTree, acc: &mut Vec<Entry>) { | ||
89 | if let Some(use_tree_list) = tree.use_tree_list() { | ||
90 | return use_tree_list | ||
91 | .use_trees() | ||
92 | .for_each(|it| collect_imports(it, acc)); | ||
93 | } | ||
94 | if let Some(path) = tree.path() { | ||
95 | acc.extend(Entry::new_import(path)); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | #[cfg(test)] | ||
100 | mod tests { | ||
101 | use super::*; | ||
102 | use ra_syntax::{ast::ModuleItemOwner, File}; | ||
103 | |||
104 | fn do_check(code: &str, expected: &[&str]) { | ||
105 | let file = File::parse(&code); | ||
106 | let scope = ModuleScope::new(&file); | ||
107 | let actual = scope.entries.iter().map(|it| it.name()).collect::<Vec<_>>(); | ||
108 | assert_eq!(expected, actual.as_slice()); | ||
109 | } | ||
110 | |||
111 | #[test] | ||
112 | fn test_module_scope() { | ||
113 | do_check( | ||
114 | " | ||
115 | struct Foo; | ||
116 | enum Bar {} | ||
117 | mod baz {} | ||
118 | fn quux() {} | ||
119 | use x::{ | ||
120 | y::z, | ||
121 | t, | ||
122 | }; | ||
123 | type T = (); | ||
124 | ", | ||
125 | &["Foo", "Bar", "baz", "quux", "z", "t", "T"], | ||
126 | ) | ||
127 | } | ||
128 | } | ||