From e163c908acff9260eff347541f0f3f57db551622 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 2 Feb 2019 00:33:54 +0100 Subject: Add AST for extern crate Also change it to parse the crate name as a NAME_REF, not a NAME. --- crates/ra_syntax/src/ast/generated.rs | 10 +++++++++- crates/ra_syntax/src/grammar.ron | 4 +++- crates/ra_syntax/src/grammar/items.rs | 2 +- .../tests/data/parser/inline/ok/0060_extern_crate.txt | 2 +- crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt | 4 ++-- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 60480c699..a8d60e882 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -970,7 +970,15 @@ impl ToOwned for ExternCrateItem { } -impl ExternCrateItem {} +impl ExternCrateItem { + pub fn name_ref(&self) -> Option<&NameRef> { + super::child_opt(self) + } + + pub fn alias(&self) -> Option<&Alias> { + super::child_opt(self) + } +} // FalseKw #[derive(Debug, PartialEq, Eq, Hash)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index a2ccd7cb9..2ed1fd1b8 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -601,7 +601,9 @@ Grammar( "UseTreeList": ( collections: [["use_trees", "UseTree"]] ), - "ExternCrateItem": (), + "ExternCrateItem": ( + options: ["NameRef", "Alias"], + ), "ArgList": ( collections: [ ["args", "Expr"] diff --git a/crates/ra_syntax/src/grammar/items.rs b/crates/ra_syntax/src/grammar/items.rs index 18039cd3f..84c18a293 100644 --- a/crates/ra_syntax/src/grammar/items.rs +++ b/crates/ra_syntax/src/grammar/items.rs @@ -247,7 +247,7 @@ fn extern_crate_item(p: &mut Parser) { p.bump(); assert!(p.at(CRATE_KW)); p.bump(); - name(p); + name_ref(p); opt_alias(p); p.expect(SEMI); } diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0060_extern_crate.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0060_extern_crate.txt index 7ffdc7fbd..a7b428787 100644 --- a/crates/ra_syntax/tests/data/parser/inline/ok/0060_extern_crate.txt +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0060_extern_crate.txt @@ -4,7 +4,7 @@ SOURCE_FILE@[0; 18) WHITESPACE@[6; 7) CRATE_KW@[7; 12) WHITESPACE@[12; 13) - NAME@[13; 16) + NAME_REF@[13; 16) IDENT@[13; 16) "foo" SEMI@[16; 17) WHITESPACE@[17; 18) diff --git a/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt b/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt index 5adf3f276..5558d952e 100644 --- a/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt +++ b/crates/ra_syntax/tests/data/parser/ok/0007_extern_crate.txt @@ -4,7 +4,7 @@ SOURCE_FILE@[0; 43) WHITESPACE@[6; 7) CRATE_KW@[7; 12) WHITESPACE@[12; 13) - NAME@[13; 16) + NAME_REF@[13; 16) IDENT@[13; 16) "foo" SEMI@[16; 17) WHITESPACE@[17; 18) @@ -13,7 +13,7 @@ SOURCE_FILE@[0; 43) WHITESPACE@[24; 25) CRATE_KW@[25; 30) WHITESPACE@[30; 31) - NAME@[31; 34) + NAME_REF@[31; 34) IDENT@[31; 34) "foo" WHITESPACE@[34; 35) ALIAS@[35; 41) -- cgit v1.2.3 From 397d84ee292295ce1d19033e586467ef9148e5bc Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 2 Feb 2019 00:36:48 +0100 Subject: Add test for extern crate renames --- crates/ra_hir/src/nameres/tests.rs | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 81c8a4f12..42c59f76f 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs @@ -334,6 +334,48 @@ fn item_map_across_crates() { ); } +#[test] +fn extern_crate_rename() { + let (mut db, sr) = MockDatabase::with_files( + " + //- /main.rs + extern crate alloc as alloc_crate; + + mod alloc; + + use alloc_crate::Arc; + + //- /lib.rs + struct Arc; + ", + ); + let main_id = sr.files[RelativePath::new("/main.rs")]; + let lib_id = sr.files[RelativePath::new("/lib.rs")]; + + let mut crate_graph = CrateGraph::default(); + let main_crate = crate_graph.add_crate_root(main_id); + let lib_crate = crate_graph.add_crate_root(lib_id); + crate_graph + .add_dep(main_crate, "alloc".into(), lib_crate) + .unwrap(); + + db.set_crate_graph(Arc::new(crate_graph)); + + let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); + let krate = module.krate(&db).unwrap(); + let item_map = db.item_map(krate); + + check_module_item_map( + &item_map, + module.module_id, + " + Arc: t v + alloc: t + alloc_crate: t + ", + ); +} + #[test] fn import_across_source_roots() { let (mut db, sr) = MockDatabase::with_files( -- cgit v1.2.3 From d69023fc72b26e64ebf1f96fc322a2f7377a5f4d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 2 Feb 2019 00:49:20 +0100 Subject: Lower extern crates to imports This is probably not completely correct, but it kind of works. --- crates/ra_hir/src/nameres/lower.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs index df87f520f..db898a782 100644 --- a/crates/ra_hir/src/nameres/lower.rs +++ b/crates/ra_hir/src/nameres/lower.rs @@ -8,7 +8,7 @@ use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; use rustc_hash::FxHashMap; use crate::{ - SourceItemId, Path, ModuleSource, Name, + SourceItemId, Path, PathKind, ModuleSource, Name, HirFileId, MacroCallLoc, AsName, PerNs, Function, ModuleDef, Module, Struct, Enum, Const, Static, Trait, Type, ids::LocationCtx, PersistentHirDatabase, @@ -186,8 +186,21 @@ impl LoweredModule { ast::ModuleItemKind::UseItem(it) => { self.add_use_item(source_map, it); } - ast::ModuleItemKind::ExternCrateItem(_) => { - // TODO + ast::ModuleItemKind::ExternCrateItem(it) => { + // Lower `extern crate x` to `use ::x`. This is kind of cheating + // and only works if we always interpret absolute paths in the + // 2018 style; otherwise `::x` could also refer to a module in + // the crate root. + if let Some(name_ref) = it.name_ref() { + let mut path = Path::from_name_ref(name_ref); + path.kind = PathKind::Abs; + let alias = it.alias().and_then(|a| a.name()).map(AsName::as_name); + self.imports.alloc(ImportData { + path, + alias, + is_glob: false, + }); + } } ast::ModuleItemKind::ConstDef(it) => { if let Some(name) = it.name() { -- cgit v1.2.3 From 1056b480d6235ee72849a416b84e13180f84307c Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 3 Feb 2019 23:23:22 +0100 Subject: Make extern crates in the root module add to the extern prelude To accomplish this, separate the extern prelude from the per-module item maps. --- crates/ra_hir/src/code_model_api.rs | 2 +- crates/ra_hir/src/nameres.rs | 90 ++++++++++++++++++++++++------------- crates/ra_hir/src/nameres/lower.rs | 3 ++ crates/ra_hir/src/nameres/tests.rs | 12 ++--- 4 files changed, 68 insertions(+), 39 deletions(-) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 92ab0f692..a58bf8f87 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -134,7 +134,7 @@ impl Module { } /// Returns the crate this module is part of. - pub fn krate(&self, _db: &impl HirDatabase) -> Option { + pub fn krate(&self, _db: &impl PersistentHirDatabase) -> Option { Some(self.krate) } diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 04cc693b3..68df083d8 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -34,6 +34,7 @@ use crate::{ /// module, the set of visible items. #[derive(Default, Debug, PartialEq, Eq)] pub struct ItemMap { + extern_prelude: FxHashMap, per_module: ArenaMap, } @@ -204,6 +205,7 @@ where } pub(crate) fn resolve(mut self) -> ItemMap { + self.populate_extern_prelude(); for (&module_id, items) in self.input.iter() { self.populate_module(module_id, Arc::clone(items)); } @@ -227,29 +229,19 @@ where self.result } + fn populate_extern_prelude(&mut self) { + for dep in self.krate.dependencies(self.db) { + log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate); + if let Some(module) = dep.krate.root_module(self.db) { + self.result + .extern_prelude + .insert(dep.name.clone(), module.into()); + } + } + } + fn populate_module(&mut self, module_id: ModuleId, input: Arc) { let mut module_items = ModuleScope::default(); - - // Populate extern crates prelude - { - let root_id = module_id.crate_root(&self.module_tree); - let file_id = root_id.file_id(&self.module_tree); - let crate_graph = self.db.crate_graph(); - if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file()) - { - let krate = Crate { crate_id }; - for dep in krate.dependencies(self.db) { - if let Some(module) = dep.krate.root_module(self.db) { - let def = module.into(); - self.add_module_item( - &mut module_items, - dep.name.clone(), - PerNs::types(def), - ); - } - } - }; - } for (import_id, import_data) in input.imports.iter() { if let Some(last_segment) = import_data.path.segments.iter().last() { if !import_data.is_glob { @@ -327,7 +319,16 @@ where .alias .clone() .unwrap_or_else(|| last_segment.name.clone()); - log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def,); + log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); + + // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 + if let Some(root_module) = self.krate.root_module(self.db) { + if import.is_extern_crate && module_id == root_module.module_id { + if let Some(def) = def.take_types() { + self.result.extern_prelude.insert(name.clone(), def); + } + } + } self.update(module_id, |items| { let res = Resolution { def, @@ -389,24 +390,53 @@ impl ItemMap { original_module: Module, path: &Path, ) -> (PerNs, ReachedFixedPoint) { - let mut curr_per_ns: PerNs = PerNs::types(match path.kind { - PathKind::Crate => original_module.crate_root(db).into(), - PathKind::Self_ | PathKind::Plain => original_module.into(), + let mut segments = path.segments.iter().enumerate(); + let mut curr_per_ns: PerNs = match path.kind { + PathKind::Crate => PerNs::types(original_module.crate_root(db).into()), + PathKind::Self_ => PerNs::types(original_module.into()), + PathKind::Plain => { + let segment = match segments.next() { + Some((_, segment)) => segment, + None => return (PerNs::none(), ReachedFixedPoint::Yes), + }; + // Resolve in: + // - current module / scope + // - extern prelude + match self[original_module.module_id].items.get(&segment.name) { + Some(res) if !res.def.is_none() => res.def, + _ => { + if let Some(def) = self.extern_prelude.get(&segment.name) { + PerNs::types(*def) + } else { + return (PerNs::none(), ReachedFixedPoint::No); + } + } + } + } PathKind::Super => { if let Some(p) = original_module.parent(db) { - p.into() + PerNs::types(p.into()) } else { log::debug!("super path in root module"); return (PerNs::none(), ReachedFixedPoint::Yes); } } PathKind::Abs => { - // TODO: absolute use is not supported - return (PerNs::none(), ReachedFixedPoint::Yes); + // 2018-style absolute path -- only extern prelude + let segment = match segments.next() { + Some((_, segment)) => segment, + None => return (PerNs::none(), ReachedFixedPoint::Yes), + }; + if let Some(def) = self.extern_prelude.get(&segment.name) { + log::debug!("absolute path {:?} resolved to crate {:?}", path, def); + PerNs::types(*def) + } else { + return (PerNs::none(), ReachedFixedPoint::Yes); + } } - }); + }; - for (i, segment) in path.segments.iter().enumerate() { + for (i, segment) in segments { let curr = match curr_per_ns.as_ref().take_types() { Some(r) => r, None => { diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs index db898a782..7e6e48ae0 100644 --- a/crates/ra_hir/src/nameres/lower.rs +++ b/crates/ra_hir/src/nameres/lower.rs @@ -23,6 +23,7 @@ pub(super) struct ImportData { pub(super) path: Path, pub(super) alias: Option, pub(super) is_glob: bool, + pub(super) is_extern_crate: bool, } /// A set of items and imports declared inside a module, without relation to @@ -199,6 +200,7 @@ impl LoweredModule { path, alias, is_glob: false, + is_extern_crate: true, }); } } @@ -228,6 +230,7 @@ impl LoweredModule { path, alias, is_glob: segment.is_none(), + is_extern_crate: false, }); if let Some(segment) = segment { source_map.insert(import, segment) diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 42c59f76f..0654dbaa1 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs @@ -329,7 +329,6 @@ fn item_map_across_crates() { module.module_id, " Baz: t v - test_crate: t ", ); } @@ -342,7 +341,9 @@ fn extern_crate_rename() { extern crate alloc as alloc_crate; mod alloc; + mod sync; + //- /sync.rs use alloc_crate::Arc; //- /lib.rs @@ -350,6 +351,7 @@ fn extern_crate_rename() { ", ); let main_id = sr.files[RelativePath::new("/main.rs")]; + let sync_id = sr.files[RelativePath::new("/sync.rs")]; let lib_id = sr.files[RelativePath::new("/lib.rs")]; let mut crate_graph = CrateGraph::default(); @@ -361,7 +363,7 @@ fn extern_crate_rename() { db.set_crate_graph(Arc::new(crate_graph)); - let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); + let module = crate::source_binder::module_from_file_id(&db, sync_id).unwrap(); let krate = module.krate(&db).unwrap(); let item_map = db.item_map(krate); @@ -370,8 +372,6 @@ fn extern_crate_rename() { module.module_id, " Arc: t v - alloc: t - alloc_crate: t ", ); } @@ -403,8 +403,6 @@ fn import_across_source_roots() { let main_id = sr2.files[RelativePath::new("/main.rs")]; - eprintln!("lib = {:?}, main = {:?}", lib_id, main_id); - let mut crate_graph = CrateGraph::default(); let main_crate = crate_graph.add_crate_root(main_id); let lib_crate = crate_graph.add_crate_root(lib_id); @@ -423,7 +421,6 @@ fn import_across_source_roots() { module.module_id, " C: t v - test_crate: t ", ); } @@ -465,7 +462,6 @@ fn reexport_across_crates() { module.module_id, " Baz: t v - test_crate: t ", ); } -- cgit v1.2.3 From 0c2fedc2cb5f086b1f6c2942de0e76427dfe9b00 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 3 Feb 2019 23:23:59 +0100 Subject: Add alloc to std deps --- crates/ra_lsp_server/src/project_model/sysroot.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ra_lsp_server/src/project_model/sysroot.rs b/crates/ra_lsp_server/src/project_model/sysroot.rs index 1e0604bf5..fb4685671 100644 --- a/crates/ra_lsp_server/src/project_model/sysroot.rs +++ b/crates/ra_lsp_server/src/project_model/sysroot.rs @@ -127,6 +127,7 @@ rustc_tsan syntax"; const STD_DEPS: &str = " +alloc alloc_jemalloc alloc_system core -- cgit v1.2.3 From ccef48b6c3a04b0104a68a947558994ea3e6a069 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 4 Feb 2019 21:42:37 +0100 Subject: Make it possible to have multiple crate in a mock analysis fixture --- crates/ra_ide_api/src/mock_analysis.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/ra_ide_api/src/mock_analysis.rs b/crates/ra_ide_api/src/mock_analysis.rs index 0f2d22ab2..834b30541 100644 --- a/crates/ra_ide_api/src/mock_analysis.rs +++ b/crates/ra_ide_api/src/mock_analysis.rs @@ -86,17 +86,25 @@ impl MockAnalysis { let mut change = AnalysisChange::new(); change.add_root(source_root, true); let mut crate_graph = CrateGraph::default(); + let mut root_crate = None; for (i, (path, contents)) in self.files.into_iter().enumerate() { assert!(path.starts_with('/')); let path = RelativePathBuf::from_path(&path[1..]).unwrap(); let file_id = FileId(i as u32 + 1); if path == "/lib.rs" || path == "/main.rs" { - crate_graph.add_crate_root(file_id); + root_crate = Some(crate_graph.add_crate_root(file_id)); + } else if path.ends_with("/lib.rs") { + let other_crate = crate_graph.add_crate_root(file_id); + let crate_name = path.parent().unwrap().file_name().unwrap(); + if let Some(root_crate) = root_crate { + crate_graph + .add_dep(root_crate, crate_name.into(), other_crate) + .unwrap(); + } } change.add_file(source_root, file_id, path, Arc::new(contents)); } change.set_crate_graph(crate_graph); - // change.set_file_resolver(Arc::new(file_map)); host.apply_change(change); host } -- cgit v1.2.3 From 699ce06ac3d857c7d4eaf01c523964f01d1b35f2 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 4 Feb 2019 22:09:56 +0100 Subject: Complete extern prelude (again) --- crates/ra_hir/src/nameres.rs | 2 +- crates/ra_hir/src/resolve.rs | 5 ++++- crates/ra_ide_api/src/completion/complete_scope.rs | 14 ++++++++++++++ .../snapshots/completion_item__extern_prelude.snap | 22 ++++++++++++++++++++++ 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 crates/ra_ide_api/src/completion/snapshots/completion_item__extern_prelude.snap diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 68df083d8..f9da6bc83 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -34,7 +34,7 @@ use crate::{ /// module, the set of visible items. #[derive(Default, Debug, PartialEq, Eq)] pub struct ItemMap { - extern_prelude: FxHashMap, + pub(crate) extern_prelude: FxHashMap, per_module: ArenaMap, } diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 6c87d0df7..5ca7bacb5 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -197,7 +197,10 @@ impl Scope { .entries() .for_each(|(name, res)| { f(name.clone(), res.def.map(Resolution::Def)); - }) + }); + m.item_map.extern_prelude.iter().for_each(|(name, def)| { + f(name.clone(), PerNs::types(Resolution::Def(*def))); + }); } Scope::GenericParams(gp) => { for param in &gp.params { diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index 44514ab2b..8674b1e66 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs @@ -110,6 +110,20 @@ mod tests { ); } + #[test] + fn completes_extern_prelude() { + check_reference_completion( + "extern_prelude", + r" + //- /lib.rs + use <|>; + + //- /other_crate/lib.rs + // nothing here + ", + ); + } + #[test] fn completes_module_items_in_nested_modules() { check_reference_completion( diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__extern_prelude.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__extern_prelude.snap new file mode 100644 index 000000000..d0e3a6188 --- /dev/null +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__extern_prelude.snap @@ -0,0 +1,22 @@ +--- +created: "2019-02-04T21:08:32.615556587+00:00" +creator: insta@0.5.3 +expression: kind_completions +source: crates/ra_ide_api/src/completion/completion_item.rs +--- +[ + CompletionItem { + completion_kind: Reference, + label: "other_crate", + kind: Some( + Module + ), + detail: None, + documentation: None, + lookup: None, + insert_text: None, + insert_text_format: PlainText, + source_range: [4; 4), + text_edit: None + } +] -- cgit v1.2.3 From de4c5e381fb1adc25143dcd67af6c87f6d9789ae Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 4 Feb 2019 23:26:25 +0100 Subject: Fix ReachedFixedPoint value for unresolved external prelude items --- crates/ra_hir/src/nameres.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index f9da6bc83..681aa9a67 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -431,7 +431,7 @@ impl ItemMap { log::debug!("absolute path {:?} resolved to crate {:?}", path, def); PerNs::types(*def) } else { - return (PerNs::none(), ReachedFixedPoint::Yes); + return (PerNs::none(), ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude } } }; -- cgit v1.2.3