Handle ::{self} imports

This commit is contained in:
Jonas Schievink 2021-06-28 20:11:58 +02:00
parent fa7bb38450
commit 3ebceb71e3
3 changed files with 173 additions and 130 deletions

View file

@ -747,11 +747,21 @@ impl Import {
} }
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ImportKind {
/// The `ModPath` is imported normally.
Plain,
/// This is a glob-import of all names in the `ModPath`.
Glob,
/// This is a `some::path::self` import, which imports `some::path` only in type namespace.
TypeOnly,
}
impl UseTree { impl UseTree {
/// Expands the `UseTree` into individually imported `ModPath`s. /// Expands the `UseTree` into individually imported `ModPath`s.
pub fn expand( pub fn expand(
&self, &self,
mut cb: impl FnMut(Idx<ast::UseTree>, ModPath, /* is_glob */ bool, Option<ImportAlias>), mut cb: impl FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
) { ) {
self.expand_impl(None, &mut cb) self.expand_impl(None, &mut cb)
} }
@ -759,26 +769,24 @@ impl UseTree {
fn expand_impl( fn expand_impl(
&self, &self,
prefix: Option<ModPath>, prefix: Option<ModPath>,
cb: &mut dyn FnMut( cb: &mut dyn FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
Idx<ast::UseTree>,
ModPath,
/* is_glob */ bool,
Option<ImportAlias>,
),
) { ) {
fn concat_mod_paths(prefix: Option<ModPath>, path: &ModPath) -> Option<ModPath> { fn concat_mod_paths(
prefix: Option<ModPath>,
path: &ModPath,
) -> Option<(ModPath, ImportKind)> {
match (prefix, &path.kind) { match (prefix, &path.kind) {
(None, _) => Some(path.clone()), (None, _) => Some((path.clone(), ImportKind::Plain)),
(Some(mut prefix), PathKind::Plain) => { (Some(mut prefix), PathKind::Plain) => {
for segment in path.segments() { for segment in path.segments() {
prefix.push_segment(segment.clone()); prefix.push_segment(segment.clone());
} }
Some(prefix) Some((prefix, ImportKind::Plain))
} }
(Some(prefix), PathKind::Super(0)) => { (Some(prefix), PathKind::Super(0)) => {
// `some::path::self` == `some::path` // `some::path::self` == `some::path`
if path.segments().is_empty() { if path.segments().is_empty() {
Some(prefix) Some((prefix, ImportKind::TypeOnly))
} else { } else {
None None
} }
@ -789,25 +797,25 @@ impl UseTree {
match &self.kind { match &self.kind {
UseTreeKind::Single { path, alias } => { UseTreeKind::Single { path, alias } => {
if let Some(path) = concat_mod_paths(prefix, path) { if let Some((path, kind)) = concat_mod_paths(prefix, path) {
cb(self.index, path, false, alias.clone()); cb(self.index, path, kind, alias.clone());
} }
} }
UseTreeKind::Glob { path: Some(path) } => { UseTreeKind::Glob { path: Some(path) } => {
if let Some(path) = concat_mod_paths(prefix, path) { if let Some((path, _)) = concat_mod_paths(prefix, path) {
cb(self.index, path, true, None); cb(self.index, path, ImportKind::Glob, None);
} }
} }
UseTreeKind::Glob { path: None } => { UseTreeKind::Glob { path: None } => {
if let Some(prefix) = prefix { if let Some(prefix) = prefix {
cb(self.index, prefix, true, None); cb(self.index, prefix, ImportKind::Glob, None);
} }
} }
UseTreeKind::Prefixed { prefix: additional_prefix, list } => { UseTreeKind::Prefixed { prefix: additional_prefix, list } => {
let prefix = match additional_prefix { let prefix = match additional_prefix {
Some(path) => match concat_mod_paths(prefix, path) { Some(path) => match concat_mod_paths(prefix, path) {
Some(path) => Some(path), Some((path, ImportKind::Plain)) => Some(path),
None => return, _ => return,
}, },
None => prefix, None => prefix,
}; };

View file

@ -30,8 +30,8 @@ use crate::{
intern::Interned, intern::Interned,
item_scope::{ImportType, PerNsGlobImports}, item_scope::{ImportType, PerNsGlobImports},
item_tree::{ item_tree::{
self, Fields, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod, self, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, MacroCall, MacroDef,
ModItem, ModKind, MacroRules, Mod, ModItem, ModKind,
}, },
macro_call_as_call_id, macro_call_as_call_id,
nameres::{ nameres::{
@ -146,7 +146,7 @@ struct Import {
path: Interned<ModPath>, path: Interned<ModPath>,
alias: Option<ImportAlias>, alias: Option<ImportAlias>,
visibility: RawVisibility, visibility: RawVisibility,
is_glob: bool, kind: ImportKind,
is_prelude: bool, is_prelude: bool,
is_extern_crate: bool, is_extern_crate: bool,
is_macro_use: bool, is_macro_use: bool,
@ -166,12 +166,12 @@ impl Import {
let is_prelude = attrs.by_key("prelude_import").exists(); let is_prelude = attrs.by_key("prelude_import").exists();
let mut res = Vec::new(); let mut res = Vec::new();
it.use_tree.expand(|idx, path, is_glob, alias| { it.use_tree.expand(|idx, path, kind, alias| {
res.push(Self { res.push(Self {
path: Interned::new(path), // FIXME this makes little sense path: Interned::new(path), // FIXME this makes little sense
alias, alias,
visibility: visibility.clone(), visibility: visibility.clone(),
is_glob, kind,
is_prelude, is_prelude,
is_extern_crate: false, is_extern_crate: false,
is_macro_use: false, is_macro_use: false,
@ -197,7 +197,7 @@ impl Import {
)), )),
alias: it.alias.clone(), alias: it.alias.clone(),
visibility: visibility.clone(), visibility: visibility.clone(),
is_glob: false, kind: ImportKind::Plain,
is_prelude: false, is_prelude: false,
is_extern_crate: true, is_extern_crate: true,
is_macro_use: attrs.by_key("macro_use").exists(), is_macro_use: attrs.by_key("macro_use").exists(),
@ -767,127 +767,139 @@ impl DefCollector<'_> {
fn record_resolved_import(&mut self, directive: &ImportDirective) { fn record_resolved_import(&mut self, directive: &ImportDirective) {
let module_id = directive.module_id; let module_id = directive.module_id;
let import = &directive.import; let import = &directive.import;
let def = directive.status.namespaces(); let mut def = directive.status.namespaces();
let vis = self let vis = self
.def_map .def_map
.resolve_visibility(self.db, module_id, &directive.import.visibility) .resolve_visibility(self.db, module_id, &directive.import.visibility)
.unwrap_or(Visibility::Public); .unwrap_or(Visibility::Public);
if import.is_glob { match import.kind {
log::debug!("glob import: {:?}", import); ImportKind::Plain | ImportKind::TypeOnly => {
match def.take_types() { let name = match &import.alias {
Some(ModuleDefId::ModuleId(m)) => { Some(ImportAlias::Alias(name)) => Some(name.clone()),
if import.is_prelude { Some(ImportAlias::Underscore) => None,
// Note: This dodgily overrides the injected prelude. The rustc None => match import.path.segments().last() {
// implementation seems to work the same though. Some(last_segment) => Some(last_segment.clone()),
cov_mark::hit!(std_prelude); None => {
self.def_map.prelude = Some(m); cov_mark::hit!(bogus_paths);
} else if m.krate != self.def_map.krate { return;
cov_mark::hit!(glob_across_crates); }
// glob import from other crate => we can just import everything once },
let item_map = m.def_map(self.db); };
let scope = &item_map[m.local_id].scope;
// Module scoped macros is included if import.kind == ImportKind::TypeOnly {
let items = scope def.values = None;
.resolutions() def.macros = None;
// only keep visible names... }
.map(|(n, res)| {
(n, res.filter_visibility(|v| v.is_visible_from_other_crate()))
})
.filter(|(_, res)| !res.is_none())
.collect::<Vec<_>>();
self.update(module_id, &items, vis, ImportType::Glob); log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
} else {
// glob import from same crate => we do an initial // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
// import, and then need to propagate any further if import.is_extern_crate && module_id == self.def_map.root {
// additions if let (Some(def), Some(name)) = (def.take_types(), name.as_ref()) {
let def_map; self.def_map.extern_prelude.insert(name.clone(), def);
let scope = if m.block == self.def_map.block_id() { }
&self.def_map[m.local_id].scope }
self.update(module_id, &[(name, def)], vis, ImportType::Named);
}
ImportKind::Glob => {
log::debug!("glob import: {:?}", import);
match def.take_types() {
Some(ModuleDefId::ModuleId(m)) => {
if import.is_prelude {
// Note: This dodgily overrides the injected prelude. The rustc
// implementation seems to work the same though.
cov_mark::hit!(std_prelude);
self.def_map.prelude = Some(m);
} else if m.krate != self.def_map.krate {
cov_mark::hit!(glob_across_crates);
// glob import from other crate => we can just import everything once
let item_map = m.def_map(self.db);
let scope = &item_map[m.local_id].scope;
// Module scoped macros is included
let items = scope
.resolutions()
// only keep visible names...
.map(|(n, res)| {
(n, res.filter_visibility(|v| v.is_visible_from_other_crate()))
})
.filter(|(_, res)| !res.is_none())
.collect::<Vec<_>>();
self.update(module_id, &items, vis, ImportType::Glob);
} else { } else {
def_map = m.def_map(self.db); // glob import from same crate => we do an initial
&def_map[m.local_id].scope // import, and then need to propagate any further
}; // additions
let def_map;
let scope = if m.block == self.def_map.block_id() {
&self.def_map[m.local_id].scope
} else {
def_map = m.def_map(self.db);
&def_map[m.local_id].scope
};
// Module scoped macros is included // Module scoped macros is included
let items = scope let items = scope
.resolutions() .resolutions()
// only keep visible names... // only keep visible names...
.map(|(n, res)| { .map(|(n, res)| {
( (
n, n,
res.filter_visibility(|v| { res.filter_visibility(|v| {
v.is_visible_from_def_map(self.db, &self.def_map, module_id) v.is_visible_from_def_map(
}), self.db,
) &self.def_map,
}) module_id,
.filter(|(_, res)| !res.is_none()) )
.collect::<Vec<_>>(); }),
)
})
.filter(|(_, res)| !res.is_none())
.collect::<Vec<_>>();
self.update(module_id, &items, vis, ImportType::Glob); self.update(module_id, &items, vis, ImportType::Glob);
// record the glob import in case we add further items // record the glob import in case we add further items
let glob = self.glob_imports.entry(m.local_id).or_default(); let glob = self.glob_imports.entry(m.local_id).or_default();
if !glob.iter().any(|(mid, _)| *mid == module_id) { if !glob.iter().any(|(mid, _)| *mid == module_id) {
glob.push((module_id, vis)); glob.push((module_id, vis));
}
} }
} }
} Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { cov_mark::hit!(glob_enum);
cov_mark::hit!(glob_enum); // glob import from enum => just import all the variants
// glob import from enum => just import all the variants
// XXX: urgh, so this works by accident! Here, we look at // XXX: urgh, so this works by accident! Here, we look at
// the enum data, and, in theory, this might require us to // the enum data, and, in theory, this might require us to
// look back at the crate_def_map, creating a cycle. For // look back at the crate_def_map, creating a cycle. For
// example, `enum E { crate::some_macro!(); }`. Luckily, the // example, `enum E { crate::some_macro!(); }`. Luckily, the
// only kind of macro that is allowed inside enum is a // only kind of macro that is allowed inside enum is a
// `cfg_macro`, and we don't need to run name resolution for // `cfg_macro`, and we don't need to run name resolution for
// it, but this is sheer luck! // it, but this is sheer luck!
let enum_data = self.db.enum_data(e); let enum_data = self.db.enum_data(e);
let resolutions = enum_data let resolutions = enum_data
.variants .variants
.iter() .iter()
.map(|(local_id, variant_data)| { .map(|(local_id, variant_data)| {
let name = variant_data.name.clone(); let name = variant_data.name.clone();
let variant = EnumVariantId { parent: e, local_id }; let variant = EnumVariantId { parent: e, local_id };
let res = PerNs::both(variant.into(), variant.into(), vis); let res = PerNs::both(variant.into(), variant.into(), vis);
(Some(name), res) (Some(name), res)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
self.update(module_id, &resolutions, vis, ImportType::Glob); self.update(module_id, &resolutions, vis, ImportType::Glob);
} }
Some(d) => { Some(d) => {
log::debug!("glob import {:?} from non-module/enum {:?}", import, d); log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
} }
None => { None => {
log::debug!("glob import {:?} didn't resolve as type", import); log::debug!("glob import {:?} didn't resolve as type", import);
}
}
} else {
let name = match &import.alias {
Some(ImportAlias::Alias(name)) => Some(name.clone()),
Some(ImportAlias::Underscore) => None,
None => match import.path.segments().last() {
Some(last_segment) => Some(last_segment.clone()),
None => {
cov_mark::hit!(bogus_paths);
return;
} }
},
};
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 import.is_extern_crate && module_id == self.def_map.root {
if let (Some(def), Some(name)) = (def.take_types(), name.as_ref()) {
self.def_map.extern_prelude.insert(name.clone(), def);
} }
} }
self.update(module_id, &[(name, def)], vis, ImportType::Named);
} }
} }

View file

@ -329,7 +329,7 @@ pub struct Baz;
"#, "#,
expect![[r#" expect![[r#"
crate crate
Baz: t v Baz: t
foo: t foo: t
crate::foo crate::foo
@ -816,3 +816,26 @@ fn bar() {}
"#]], "#]],
); );
} }
#[test]
fn self_imports_only_types() {
check(
r#"
//- /main.rs
mod m {
pub macro S() {}
pub struct S;
}
use self::m::S::{self};
"#,
expect![[r#"
crate
S: t
m: t
crate::m
S: t v m
"#]],
);
}