Skip to content

Commit 4d4c46a

Browse files
bors[bot]flodiebold
andcommitted
Merge #742
742: Extern crate r=matklad a=flodiebold This implements `extern crate` declarations by lowering them to (absolute) imports, and adds support for absolute paths. It also extracts the extern prelude from the per-module item map, and handles the special case of extern crates in the crate root adding to the extern prelude. This means we finally resolve `Arc`, so it fixes #523 😄 Co-authored-by: Florian Diebold <[email protected]>
2 parents 94d5d0d + de4c5e3 commit 4d4c46a

File tree

14 files changed

+190
-48
lines changed

14 files changed

+190
-48
lines changed

Diff for: crates/ra_hir/src/code_model_api.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ impl Module {
134134
}
135135

136136
/// Returns the crate this module is part of.
137-
pub fn krate(&self, _db: &impl HirDatabase) -> Option<Crate> {
137+
pub fn krate(&self, _db: &impl PersistentHirDatabase) -> Option<Crate> {
138138
Some(self.krate)
139139
}
140140

Diff for: crates/ra_hir/src/nameres.rs

+60-30
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use crate::{
3434
/// module, the set of visible items.
3535
#[derive(Default, Debug, PartialEq, Eq)]
3636
pub struct ItemMap {
37+
pub(crate) extern_prelude: FxHashMap<Name, ModuleDef>,
3738
per_module: ArenaMap<ModuleId, ModuleScope>,
3839
}
3940

@@ -204,6 +205,7 @@ where
204205
}
205206

206207
pub(crate) fn resolve(mut self) -> ItemMap {
208+
self.populate_extern_prelude();
207209
for (&module_id, items) in self.input.iter() {
208210
self.populate_module(module_id, Arc::clone(items));
209211
}
@@ -227,29 +229,19 @@ where
227229
self.result
228230
}
229231

232+
fn populate_extern_prelude(&mut self) {
233+
for dep in self.krate.dependencies(self.db) {
234+
log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate);
235+
if let Some(module) = dep.krate.root_module(self.db) {
236+
self.result
237+
.extern_prelude
238+
.insert(dep.name.clone(), module.into());
239+
}
240+
}
241+
}
242+
230243
fn populate_module(&mut self, module_id: ModuleId, input: Arc<LoweredModule>) {
231244
let mut module_items = ModuleScope::default();
232-
233-
// Populate extern crates prelude
234-
{
235-
let root_id = module_id.crate_root(&self.module_tree);
236-
let file_id = root_id.file_id(&self.module_tree);
237-
let crate_graph = self.db.crate_graph();
238-
if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file())
239-
{
240-
let krate = Crate { crate_id };
241-
for dep in krate.dependencies(self.db) {
242-
if let Some(module) = dep.krate.root_module(self.db) {
243-
let def = module.into();
244-
self.add_module_item(
245-
&mut module_items,
246-
dep.name.clone(),
247-
PerNs::types(def),
248-
);
249-
}
250-
}
251-
};
252-
}
253245
for (import_id, import_data) in input.imports.iter() {
254246
if let Some(last_segment) = import_data.path.segments.iter().last() {
255247
if !import_data.is_glob {
@@ -327,7 +319,16 @@ where
327319
.alias
328320
.clone()
329321
.unwrap_or_else(|| last_segment.name.clone());
330-
log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def,);
322+
log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
323+
324+
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
325+
if let Some(root_module) = self.krate.root_module(self.db) {
326+
if import.is_extern_crate && module_id == root_module.module_id {
327+
if let Some(def) = def.take_types() {
328+
self.result.extern_prelude.insert(name.clone(), def);
329+
}
330+
}
331+
}
331332
self.update(module_id, |items| {
332333
let res = Resolution {
333334
def,
@@ -389,24 +390,53 @@ impl ItemMap {
389390
original_module: Module,
390391
path: &Path,
391392
) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
392-
let mut curr_per_ns: PerNs<ModuleDef> = PerNs::types(match path.kind {
393-
PathKind::Crate => original_module.crate_root(db).into(),
394-
PathKind::Self_ | PathKind::Plain => original_module.into(),
393+
let mut segments = path.segments.iter().enumerate();
394+
let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
395+
PathKind::Crate => PerNs::types(original_module.crate_root(db).into()),
396+
PathKind::Self_ => PerNs::types(original_module.into()),
397+
PathKind::Plain => {
398+
let segment = match segments.next() {
399+
Some((_, segment)) => segment,
400+
None => return (PerNs::none(), ReachedFixedPoint::Yes),
401+
};
402+
// Resolve in:
403+
// - current module / scope
404+
// - extern prelude
405+
match self[original_module.module_id].items.get(&segment.name) {
406+
Some(res) if !res.def.is_none() => res.def,
407+
_ => {
408+
if let Some(def) = self.extern_prelude.get(&segment.name) {
409+
PerNs::types(*def)
410+
} else {
411+
return (PerNs::none(), ReachedFixedPoint::No);
412+
}
413+
}
414+
}
415+
}
395416
PathKind::Super => {
396417
if let Some(p) = original_module.parent(db) {
397-
p.into()
418+
PerNs::types(p.into())
398419
} else {
399420
log::debug!("super path in root module");
400421
return (PerNs::none(), ReachedFixedPoint::Yes);
401422
}
402423
}
403424
PathKind::Abs => {
404-
// TODO: absolute use is not supported
405-
return (PerNs::none(), ReachedFixedPoint::Yes);
425+
// 2018-style absolute path -- only extern prelude
426+
let segment = match segments.next() {
427+
Some((_, segment)) => segment,
428+
None => return (PerNs::none(), ReachedFixedPoint::Yes),
429+
};
430+
if let Some(def) = self.extern_prelude.get(&segment.name) {
431+
log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
432+
PerNs::types(*def)
433+
} else {
434+
return (PerNs::none(), ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
435+
}
406436
}
407-
});
437+
};
408438

409-
for (i, segment) in path.segments.iter().enumerate() {
439+
for (i, segment) in segments {
410440
let curr = match curr_per_ns.as_ref().take_types() {
411441
Some(r) => r,
412442
None => {

Diff for: crates/ra_hir/src/nameres/lower.rs

+19-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
88
use rustc_hash::FxHashMap;
99

1010
use crate::{
11-
SourceItemId, Path, ModuleSource, Name,
11+
SourceItemId, Path, PathKind, ModuleSource, Name,
1212
HirFileId, MacroCallLoc, AsName, PerNs, Function,
1313
ModuleDef, Module, Struct, Enum, Const, Static, Trait, Type,
1414
ids::LocationCtx, PersistentHirDatabase,
@@ -23,6 +23,7 @@ pub(super) struct ImportData {
2323
pub(super) path: Path,
2424
pub(super) alias: Option<Name>,
2525
pub(super) is_glob: bool,
26+
pub(super) is_extern_crate: bool,
2627
}
2728

2829
/// A set of items and imports declared inside a module, without relation to
@@ -186,8 +187,22 @@ impl LoweredModule {
186187
ast::ModuleItemKind::UseItem(it) => {
187188
self.add_use_item(source_map, it);
188189
}
189-
ast::ModuleItemKind::ExternCrateItem(_) => {
190-
// TODO
190+
ast::ModuleItemKind::ExternCrateItem(it) => {
191+
// Lower `extern crate x` to `use ::x`. This is kind of cheating
192+
// and only works if we always interpret absolute paths in the
193+
// 2018 style; otherwise `::x` could also refer to a module in
194+
// the crate root.
195+
if let Some(name_ref) = it.name_ref() {
196+
let mut path = Path::from_name_ref(name_ref);
197+
path.kind = PathKind::Abs;
198+
let alias = it.alias().and_then(|a| a.name()).map(AsName::as_name);
199+
self.imports.alloc(ImportData {
200+
path,
201+
alias,
202+
is_glob: false,
203+
is_extern_crate: true,
204+
});
205+
}
191206
}
192207
ast::ModuleItemKind::ConstDef(it) => {
193208
if let Some(name) = it.name() {
@@ -215,6 +230,7 @@ impl LoweredModule {
215230
path,
216231
alias,
217232
is_glob: segment.is_none(),
233+
is_extern_crate: false,
218234
});
219235
if let Some(segment) = segment {
220236
source_map.insert(import, segment)

Diff for: crates/ra_hir/src/nameres/tests.rs

+43-5
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,49 @@ fn item_map_across_crates() {
329329
module.module_id,
330330
"
331331
Baz: t v
332-
test_crate: t
332+
",
333+
);
334+
}
335+
336+
#[test]
337+
fn extern_crate_rename() {
338+
let (mut db, sr) = MockDatabase::with_files(
339+
"
340+
//- /main.rs
341+
extern crate alloc as alloc_crate;
342+
343+
mod alloc;
344+
mod sync;
345+
346+
//- /sync.rs
347+
use alloc_crate::Arc;
348+
349+
//- /lib.rs
350+
struct Arc;
351+
",
352+
);
353+
let main_id = sr.files[RelativePath::new("/main.rs")];
354+
let sync_id = sr.files[RelativePath::new("/sync.rs")];
355+
let lib_id = sr.files[RelativePath::new("/lib.rs")];
356+
357+
let mut crate_graph = CrateGraph::default();
358+
let main_crate = crate_graph.add_crate_root(main_id);
359+
let lib_crate = crate_graph.add_crate_root(lib_id);
360+
crate_graph
361+
.add_dep(main_crate, "alloc".into(), lib_crate)
362+
.unwrap();
363+
364+
db.set_crate_graph(Arc::new(crate_graph));
365+
366+
let module = crate::source_binder::module_from_file_id(&db, sync_id).unwrap();
367+
let krate = module.krate(&db).unwrap();
368+
let item_map = db.item_map(krate);
369+
370+
check_module_item_map(
371+
&item_map,
372+
module.module_id,
373+
"
374+
Arc: t v
333375
",
334376
);
335377
}
@@ -361,8 +403,6 @@ fn import_across_source_roots() {
361403

362404
let main_id = sr2.files[RelativePath::new("/main.rs")];
363405

364-
eprintln!("lib = {:?}, main = {:?}", lib_id, main_id);
365-
366406
let mut crate_graph = CrateGraph::default();
367407
let main_crate = crate_graph.add_crate_root(main_id);
368408
let lib_crate = crate_graph.add_crate_root(lib_id);
@@ -381,7 +421,6 @@ fn import_across_source_roots() {
381421
module.module_id,
382422
"
383423
C: t v
384-
test_crate: t
385424
",
386425
);
387426
}
@@ -423,7 +462,6 @@ fn reexport_across_crates() {
423462
module.module_id,
424463
"
425464
Baz: t v
426-
test_crate: t
427465
",
428466
);
429467
}

Diff for: crates/ra_hir/src/resolve.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,10 @@ impl Scope {
197197
.entries()
198198
.for_each(|(name, res)| {
199199
f(name.clone(), res.def.map(Resolution::Def));
200-
})
200+
});
201+
m.item_map.extern_prelude.iter().for_each(|(name, def)| {
202+
f(name.clone(), PerNs::types(Resolution::Def(*def)));
203+
});
201204
}
202205
Scope::GenericParams(gp) => {
203206
for param in &gp.params {

Diff for: crates/ra_ide_api/src/completion/complete_scope.rs

+14
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,20 @@ mod tests {
110110
);
111111
}
112112

113+
#[test]
114+
fn completes_extern_prelude() {
115+
check_reference_completion(
116+
"extern_prelude",
117+
r"
118+
//- /lib.rs
119+
use <|>;
120+
121+
//- /other_crate/lib.rs
122+
// nothing here
123+
",
124+
);
125+
}
126+
113127
#[test]
114128
fn completes_module_items_in_nested_modules() {
115129
check_reference_completion(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
created: "2019-02-04T21:08:32.615556587+00:00"
3+
creator: insta@0.5.3
4+
expression: kind_completions
5+
source: crates/ra_ide_api/src/completion/completion_item.rs
6+
---
7+
[
8+
CompletionItem {
9+
completion_kind: Reference,
10+
label: "other_crate",
11+
kind: Some(
12+
Module
13+
),
14+
detail: None,
15+
documentation: None,
16+
lookup: None,
17+
insert_text: None,
18+
insert_text_format: PlainText,
19+
source_range: [4; 4),
20+
text_edit: None
21+
}
22+
]

Diff for: crates/ra_ide_api/src/mock_analysis.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,25 @@ impl MockAnalysis {
8686
let mut change = AnalysisChange::new();
8787
change.add_root(source_root, true);
8888
let mut crate_graph = CrateGraph::default();
89+
let mut root_crate = None;
8990
for (i, (path, contents)) in self.files.into_iter().enumerate() {
9091
assert!(path.starts_with('/'));
9192
let path = RelativePathBuf::from_path(&path[1..]).unwrap();
9293
let file_id = FileId(i as u32 + 1);
9394
if path == "/lib.rs" || path == "/main.rs" {
94-
crate_graph.add_crate_root(file_id);
95+
root_crate = Some(crate_graph.add_crate_root(file_id));
96+
} else if path.ends_with("/lib.rs") {
97+
let other_crate = crate_graph.add_crate_root(file_id);
98+
let crate_name = path.parent().unwrap().file_name().unwrap();
99+
if let Some(root_crate) = root_crate {
100+
crate_graph
101+
.add_dep(root_crate, crate_name.into(), other_crate)
102+
.unwrap();
103+
}
95104
}
96105
change.add_file(source_root, file_id, path, Arc::new(contents));
97106
}
98107
change.set_crate_graph(crate_graph);
99-
// change.set_file_resolver(Arc::new(file_map));
100108
host.apply_change(change);
101109
host
102110
}

Diff for: crates/ra_lsp_server/src/project_model/sysroot.rs

+1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ rustc_tsan
127127
syntax";
128128

129129
const STD_DEPS: &str = "
130+
alloc
130131
alloc_jemalloc
131132
alloc_system
132133
core

Diff for: crates/ra_syntax/src/ast/generated.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -970,7 +970,15 @@ impl ToOwned for ExternCrateItem {
970970
}
971971

972972

973-
impl ExternCrateItem {}
973+
impl ExternCrateItem {
974+
pub fn name_ref(&self) -> Option<&NameRef> {
975+
super::child_opt(self)
976+
}
977+
978+
pub fn alias(&self) -> Option<&Alias> {
979+
super::child_opt(self)
980+
}
981+
}
974982

975983
// FalseKw
976984
#[derive(Debug, PartialEq, Eq, Hash)]

Diff for: crates/ra_syntax/src/grammar.ron

+3-1
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,9 @@ Grammar(
601601
"UseTreeList": (
602602
collections: [["use_trees", "UseTree"]]
603603
),
604-
"ExternCrateItem": (),
604+
"ExternCrateItem": (
605+
options: ["NameRef", "Alias"],
606+
),
605607
"ArgList": (
606608
collections: [
607609
["args", "Expr"]

0 commit comments

Comments
 (0)