Import the prelude

This commit is contained in:
Florian Diebold 2019-02-10 20:44:34 +01:00
parent 65266c644a
commit 1526eb25c9
7 changed files with 99 additions and 12 deletions

View file

@ -6,4 +6,5 @@ test_utils::marks!(
type_var_resolves_to_int_var
glob_enum
glob_across_crates
std_prelude
);

View file

@ -34,6 +34,10 @@ use crate::{
/// module, the set of visible items.
#[derive(Default, Debug, PartialEq, Eq)]
pub struct ItemMap {
/// The prelude module for this crate. This either comes from an import
/// marked with the `prelude_import` attribute, or (in the normal case) from
/// a dependency (`std` or `core`).
prelude: Option<Module>,
pub(crate) extern_prelude: FxHashMap<Name, ModuleDef>,
per_module: ArenaMap<ModuleId, ModuleScope>,
}
@ -211,6 +215,13 @@ where
if let Some(module) = dep.krate.root_module(self.db) {
self.result.extern_prelude.insert(dep.name.clone(), module.into());
}
// look for the prelude
if self.result.prelude.is_none() {
let item_map = self.db.item_map(dep.krate);
if item_map.prelude.is_some() {
self.result.prelude = item_map.prelude;
}
}
}
}
@ -279,7 +290,10 @@ where
log::debug!("glob import: {:?}", import);
match def.take_types() {
Some(ModuleDef::Module(m)) => {
if m.krate != self.krate {
if import.is_prelude {
tested_by!(std_prelude);
self.result.prelude = Some(m);
} else if m.krate != self.krate {
tested_by!(glob_across_crates);
// glob import from other crate => we can just import everything once
let item_map = self.db.item_map(m.krate);
@ -434,12 +448,40 @@ impl ItemMap {
self.resolve_path_fp(db, original_module, path).0
}
pub(crate) fn resolve_name_in_module(&self, module: Module, name: &Name) -> PerNs<ModuleDef> {
fn resolve_in_prelude(
&self,
db: &impl PersistentHirDatabase,
original_module: Module,
name: &Name,
) -> PerNs<ModuleDef> {
if let Some(prelude) = self.prelude {
let resolution = if prelude.krate == original_module.krate {
self[prelude.module_id].items.get(name).cloned()
} else {
db.item_map(prelude.krate)[prelude.module_id].items.get(name).cloned()
};
resolution.map(|r| r.def).unwrap_or_else(PerNs::none)
} else {
PerNs::none()
}
}
pub(crate) fn resolve_name_in_module(
&self,
db: &impl PersistentHirDatabase,
module: Module,
name: &Name,
) -> PerNs<ModuleDef> {
// Resolve in:
// - current module / scope
// - extern prelude
// - std prelude
let from_scope = self[module.module_id].items.get(name).map_or(PerNs::none(), |it| it.def);
let from_extern_prelude =
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
let from_prelude = self.resolve_in_prelude(db, module, name);
from_scope.or(from_extern_prelude)
from_scope.or(from_extern_prelude).or(from_prelude)
}
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
@ -459,7 +501,7 @@ impl ItemMap {
Some((_, segment)) => segment,
None => return (PerNs::none(), ReachedFixedPoint::Yes),
};
self.resolve_name_in_module(original_module, &segment.name)
self.resolve_name_in_module(db, original_module, &segment.name)
}
PathKind::Super => {
if let Some(p) = original_module.parent(db) {

View file

@ -2,7 +2,7 @@ use std::sync::Arc;
use ra_syntax::{
AstNode, SourceFile, TreeArc, AstPtr,
ast::{self, ModuleItemOwner, NameOwner},
ast::{self, ModuleItemOwner, NameOwner, AttrsOwner},
};
use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
use rustc_hash::FxHashMap;
@ -23,6 +23,7 @@ pub(super) struct ImportData {
pub(super) path: Path,
pub(super) alias: Option<Name>,
pub(super) is_glob: bool,
pub(super) is_prelude: bool,
pub(super) is_extern_crate: bool,
}
@ -191,6 +192,7 @@ impl LoweredModule {
path,
alias,
is_glob: false,
is_prelude: false,
is_extern_crate: true,
});
}
@ -214,11 +216,14 @@ impl LoweredModule {
}
fn add_use_item(&mut self, source_map: &mut ImportSourceMap, item: &ast::UseItem) {
let is_prelude =
item.attrs().any(|attr| attr.as_atom().map(|s| s == "prelude_import").unwrap_or(false));
Path::expand_use_item(item, |path, segment, alias| {
let import = self.imports.alloc(ImportData {
path,
alias,
is_glob: segment.is_none(),
is_prelude,
is_extern_crate: false,
});
if let Some(segment) = segment {

View file

@ -296,6 +296,43 @@ fn module_resolution_works_for_non_standard_filenames() {
);
}
#[test]
fn std_prelude() {
covers!(std_prelude);
let mut db = MockDatabase::with_files(
"
//- /main.rs
use Foo::*;
//- /lib.rs
mod prelude;
#[prelude_import]
use prelude::*;
//- /prelude.rs
pub enum Foo { Bar, Baz };
",
);
db.set_crate_graph_from_fixture(crate_graph! {
"main": ("/main.rs", ["test_crate"]),
"test_crate": ("/lib.rs", []),
});
let main_id = db.file_id_of("/main.rs");
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,
"
Bar: t v
Baz: t v
",
);
}
#[test]
fn name_res_works_for_broken_modules() {
covers!(name_res_works_for_broken_modules);

View file

@ -56,10 +56,10 @@ pub enum Resolution {
}
impl Resolver {
pub fn resolve_name(&self, name: &Name) -> PerNs<Resolution> {
pub fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
let mut resolution = PerNs::none();
for scope in self.scopes.iter().rev() {
resolution = resolution.or(scope.resolve_name(name));
resolution = resolution.or(scope.resolve_name(db, name));
if resolution.is_both() {
return resolution;
}
@ -69,9 +69,9 @@ impl Resolver {
pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> {
if let Some(name) = path.as_ident() {
self.resolve_name(name)
self.resolve_name(db, name)
} else if path.is_self() {
self.resolve_name(&Name::self_param())
self.resolve_name(db, &Name::self_param())
} else {
let (item_map, module) = match self.module() {
Some(m) => m,
@ -143,13 +143,13 @@ impl Resolver {
}
impl Scope {
fn resolve_name(&self, name: &Name) -> PerNs<Resolution> {
fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
match self {
Scope::ModuleScope(m) => {
if let Some(KnownName::SelfParam) = name.as_known_name() {
PerNs::types(Resolution::Def(m.module.into()))
} else {
m.item_map.resolve_name_in_module(m.module, name).map(Resolution::Def)
m.item_map.resolve_name_in_module(db, m.module, name).map(Resolution::Def)
}
}
Scope::GenericParams(gp) => match gp.find_by_name(name) {

View file

@ -4210,6 +4210,7 @@ impl ToOwned for UseItem {
}
impl ast::AttrsOwner for UseItem {}
impl UseItem {
pub fn use_tree(&self) -> Option<&UseTree> {
super::child_opt(self)

View file

@ -596,7 +596,8 @@ Grammar(
options: [ "Pat", "TypeRef" ],
),
"UseItem": (
options: [ "UseTree" ]
traits: ["AttrsOwner"],
options: [ "UseTree" ],
),
"UseTree": (
options: [ "Path", "UseTreeList", "Alias" ]