mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 15:15:24 +00:00
Handle ::{self}
imports
This commit is contained in:
parent
fa7bb38450
commit
3ebceb71e3
3 changed files with 173 additions and 130 deletions
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue