mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 01:17:27 +00:00
Import the prelude
This commit is contained in:
parent
65266c644a
commit
1526eb25c9
7 changed files with 99 additions and 12 deletions
|
@ -6,4 +6,5 @@ test_utils::marks!(
|
|||
type_var_resolves_to_int_var
|
||||
glob_enum
|
||||
glob_across_crates
|
||||
std_prelude
|
||||
);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -596,7 +596,8 @@ Grammar(
|
|||
options: [ "Pat", "TypeRef" ],
|
||||
),
|
||||
"UseItem": (
|
||||
options: [ "UseTree" ]
|
||||
traits: ["AttrsOwner"],
|
||||
options: [ "UseTree" ],
|
||||
),
|
||||
"UseTree": (
|
||||
options: [ "Path", "UseTreeList", "Alias" ]
|
||||
|
|
Loading…
Reference in a new issue