aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/nameres/crate_def_map.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-03-13 13:04:28 +0000
committerAleksey Kladov <[email protected]>2019-03-17 09:46:13 +0000
commit182c05a96c25321ac3ff262cea098e0c4d7ed6f8 (patch)
tree151c81b7b5f55dda12a555c69993a86a61b84243 /crates/ra_hir/src/nameres/crate_def_map.rs
parent0d8d9186563637f493ac7691268319373251b18a (diff)
add name resolution from the old impl
unlike the old impl, this also handles macro imports across crates
Diffstat (limited to 'crates/ra_hir/src/nameres/crate_def_map.rs')
-rw-r--r--crates/ra_hir/src/nameres/crate_def_map.rs310
1 files changed, 187 insertions, 123 deletions
diff --git a/crates/ra_hir/src/nameres/crate_def_map.rs b/crates/ra_hir/src/nameres/crate_def_map.rs
index ea9b4fb50..483878c78 100644
--- a/crates/ra_hir/src/nameres/crate_def_map.rs
+++ b/crates/ra_hir/src/nameres/crate_def_map.rs
@@ -40,16 +40,21 @@
40/// syntax. 40/// syntax.
41/// 41///
42/// TBD; 42/// TBD;
43
43mod raw; 44mod raw;
44mod collector; 45mod collector;
46#[cfg(test)]
47mod tests;
45 48
46use rustc_hash::FxHashMap; 49use rustc_hash::FxHashMap;
47use ra_arena::{Arena}; 50use test_utils::tested_by;
51use ra_arena::Arena;
48 52
49use crate::{ 53use crate::{
50 Name, 54 Name, Module, Path, PathKind, ModuleDef, Crate,
55 PersistentHirDatabase,
51 module_tree::ModuleId, 56 module_tree::ModuleId,
52 nameres::ModuleScope, 57 nameres::{ModuleScope, ResolveMode, ResolvePathResult, PerNs, Edition, ReachedFixedPoint},
53}; 58};
54 59
55#[derive(Default, Debug)] 60#[derive(Default, Debug)]
@@ -62,143 +67,202 @@ struct ModuleData {
62/// Contans all top-level defs from a macro-expanded crate 67/// Contans all top-level defs from a macro-expanded crate
63#[derive(Debug)] 68#[derive(Debug)]
64pub(crate) struct CrateDefMap { 69pub(crate) struct CrateDefMap {
70 krate: Crate,
71 edition: Edition,
72 /// The prelude module for this crate. This either comes from an import
73 /// marked with the `prelude_import` attribute, or (in the normal case) from
74 /// a dependency (`std` or `core`).
75 prelude: Option<Module>,
76 extern_prelude: FxHashMap<Name, ModuleDef>,
65 root: ModuleId, 77 root: ModuleId,
66 modules: Arena<ModuleId, ModuleData>, 78 modules: Arena<ModuleId, ModuleData>,
79 public_macros: FxHashMap<Name, mbe::MacroRules>,
67} 80}
68 81
69#[cfg(test)] 82impl std::ops::Index<ModuleId> for CrateDefMap {
70mod tests { 83 type Output = ModuleScope;
71 use std::sync::Arc; 84 fn index(&self, id: ModuleId) -> &ModuleScope {
72 85 &self.modules[id].scope
73 use ra_db::SourceDatabase;
74 use insta::assert_snapshot_matches;
75
76 use crate::{Crate, mock::MockDatabase, nameres::Resolution};
77
78 use super::*;
79
80 fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> {
81 let db = MockDatabase::with_files(fixture);
82 let crate_id = db.crate_graph().iter().next().unwrap();
83 let krate = Crate { crate_id };
84 collector::crate_def_map_query(&db, krate)
85 } 86 }
87}
86 88
87 fn render_crate_def_map(map: &CrateDefMap) -> String { 89impl CrateDefMap {
88 let mut buf = String::new(); 90 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
89 go(&mut buf, map, "\ncrate", map.root); 91 // the result.
90 return buf; 92 #[allow(unused)]
91 93 fn resolve_path_fp(
92 fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: ModuleId) { 94 &self,
93 *buf += path; 95 db: &impl PersistentHirDatabase,
94 *buf += "\n"; 96 mode: ResolveMode,
95 for (name, res) in map.modules[module].scope.items.iter() { 97 original_module: ModuleId,
96 *buf += &format!("{}: {}\n", name, dump_resolution(res)) 98 path: &Path,
99 ) -> ResolvePathResult {
100 let mut segments = path.segments.iter().enumerate();
101 let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
102 PathKind::Crate => {
103 PerNs::types(Module { krate: self.krate, module_id: self.root }.into())
97 } 104 }
98 for (name, child) in map.modules[module].children.iter() { 105 PathKind::Self_ => {
99 let path = path.to_string() + &format!("::{}", name); 106 PerNs::types(Module { krate: self.krate, module_id: original_module }.into())
100 go(buf, map, &path, *child);
101 } 107 }
102 } 108 // plain import or absolute path in 2015: crate-relative with
103 109 // fallback to extern prelude (with the simplification in
104 fn dump_resolution(resolution: &Resolution) -> &'static str { 110 // rust-lang/rust#57745)
105 match (resolution.def.types.is_some(), resolution.def.values.is_some()) { 111 // TODO there must be a nicer way to write this condition
106 (true, true) => "t v", 112 PathKind::Plain | PathKind::Abs
107 (true, false) => "t", 113 if self.edition == Edition::Edition2015
108 (false, true) => "v", 114 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
109 (false, false) => "_", 115 {
116 let segment = match segments.next() {
117 Some((_, segment)) => segment,
118 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
119 };
120 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
121 self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
122 }
123 PathKind::Plain => {
124 let segment = match segments.next() {
125 Some((_, segment)) => segment,
126 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
127 };
128 log::debug!("resolving {:?} in module", segment);
129 self.resolve_name_in_module(db, original_module, &segment.name)
130 }
131 PathKind::Super => {
132 if let Some(p) = self.modules[original_module].parent {
133 PerNs::types(Module { krate: self.krate, module_id: p }.into())
134 } else {
135 log::debug!("super path in root module");
136 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
137 }
138 }
139 PathKind::Abs => {
140 // 2018-style absolute path -- only extern prelude
141 let segment = match segments.next() {
142 Some((_, segment)) => segment,
143 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
144 };
145 if let Some(def) = self.extern_prelude.get(&segment.name) {
146 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
147 PerNs::types(*def)
148 } else {
149 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
150 }
110 } 151 }
152 };
153
154 for (i, segment) in segments {
155 let curr = match curr_per_ns.as_ref().take_types() {
156 Some(r) => r,
157 None => {
158 // we still have path segments left, but the path so far
159 // didn't resolve in the types namespace => no resolution
160 // (don't break here because `curr_per_ns` might contain
161 // something in the value namespace, and it would be wrong
162 // to return that)
163 return ResolvePathResult::empty(ReachedFixedPoint::No);
164 }
165 };
166 // resolve segment in curr
167
168 curr_per_ns = match curr {
169 ModuleDef::Module(module) => {
170 if module.krate != self.krate {
171 let path = Path {
172 segments: path.segments[i..].iter().cloned().collect(),
173 kind: PathKind::Self_,
174 };
175 log::debug!("resolving {:?} in other crate", path);
176 let item_map = db.item_map(module.krate);
177 let (def, s) = item_map.resolve_path(db, *module, &path);
178 return ResolvePathResult::with(
179 def,
180 ReachedFixedPoint::Yes,
181 s.map(|s| s + i),
182 );
183 }
184
185 match self[module.module_id].items.get(&segment.name) {
186 Some(res) if !res.def.is_none() => res.def,
187 _ => {
188 log::debug!("path segment {:?} not found", segment.name);
189 return ResolvePathResult::empty(ReachedFixedPoint::No);
190 }
191 }
192 }
193 ModuleDef::Enum(e) => {
194 // enum variant
195 tested_by!(item_map_enum_importing);
196 match e.variant(db, &segment.name) {
197 Some(variant) => PerNs::both(variant.into(), variant.into()),
198 None => {
199 return ResolvePathResult::with(
200 PerNs::types((*e).into()),
201 ReachedFixedPoint::Yes,
202 Some(i),
203 );
204 }
205 }
206 }
207 s => {
208 // could be an inherent method call in UFCS form
209 // (`Struct::method`), or some other kind of associated item
210 log::debug!(
211 "path segment {:?} resolved to non-module {:?}, but is not last",
212 segment.name,
213 curr,
214 );
215
216 return ResolvePathResult::with(
217 PerNs::types((*s).into()),
218 ReachedFixedPoint::Yes,
219 Some(i),
220 );
221 }
222 };
111 } 223 }
224 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
112 } 225 }
113 226
114 fn def_map(fixtute: &str) -> String { 227 fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
115 let dm = compute_crate_def_map(fixtute); 228 let from_crate_root = self[self.root].items.get(name).map_or(PerNs::none(), |it| it.def);
116 render_crate_def_map(&dm) 229 let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
230
231 from_crate_root.or(from_extern_prelude)
117 } 232 }
118 233
119 #[test] 234 fn resolve_name_in_module(
120 fn crate_def_map_smoke_test() { 235 &self,
121 let map = def_map( 236 db: &impl PersistentHirDatabase,
122 " 237 module: ModuleId,
123 //- /lib.rs 238 name: &Name,
124 mod foo; 239 ) -> PerNs<ModuleDef> {
125 struct S; 240 // Resolve in:
126 241 // - current module / scope
127 //- /foo/mod.rs 242 // - extern prelude
128 pub mod bar; 243 // - std prelude
129 fn f() {} 244 let from_scope = self[module].items.get(name).map_or(PerNs::none(), |it| it.def);
130 245 let from_extern_prelude =
131 //- /foo/bar.rs 246 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
132 pub struct Baz; 247 let from_prelude = self.resolve_in_prelude(db, name);
133 enum E { V } 248
134 ", 249 from_scope.or(from_extern_prelude).or(from_prelude)
135 );
136 assert_snapshot_matches!(
137 map,
138 @r###"
139crate
140S: t v
141
142crate::foo
143f: v
144
145crate::foo::bar
146Baz: t v
147E: t
148"###
149 )
150 } 250 }
151 251
152 #[test] 252 fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
153 fn macro_rules_are_globally_visible() { 253 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
154 let map = def_map(
155 "
156 //- /lib.rs
157 macro_rules! structs {
158 ($($i:ident),*) => {
159 $(struct $i { field: u32 } )*
160 }
161 }
162 structs!(Foo);
163 mod nested;
164
165 //- /nested.rs
166 structs!(Bar, Baz);
167 ",
168 );
169 assert_snapshot_matches!(map, @r###"
170crate
171Foo: t v
172
173crate::nested
174Bar: t v
175Baz: t v
176"###);
177 } 254 }
178 255
179 #[test] 256 fn resolve_in_prelude(&self, db: &impl PersistentHirDatabase, name: &Name) -> PerNs<ModuleDef> {
180 fn macro_rules_can_define_modules() { 257 if let Some(prelude) = self.prelude {
181 let map = def_map( 258 let resolution = if prelude.krate == self.krate {
182 " 259 self[prelude.module_id].items.get(name).cloned()
183 //- /lib.rs 260 } else {
184 macro_rules! m { 261 db.item_map(prelude.krate)[prelude.module_id].items.get(name).cloned()
185 ($name:ident) => { mod $name; } 262 };
186 } 263 resolution.map(|r| r.def).unwrap_or_else(PerNs::none)
187 m!(n1); 264 } else {
188 265 PerNs::none()
189 //- /n1.rs 266 }
190 m!(n2)
191 //- /n1/n2.rs
192 struct X;
193 ",
194 );
195 assert_snapshot_matches!(map, @r###"
196crate
197
198crate::n1
199
200crate::n1::n2
201X: t v
202"###);
203 } 267 }
204} 268}