mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 04:44:57 +00:00
Stop expanding UseTrees during ItemTree lowering
This commit is contained in:
parent
5587d0a3e3
commit
b52df91877
12 changed files with 320 additions and 188 deletions
|
@ -472,27 +472,13 @@ impl Module {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
DefDiagnosticKind::UnresolvedImport { ast, index } => {
|
DefDiagnosticKind::UnresolvedImport { id, index } => {
|
||||||
let use_item = ast.to_node(db.upcast());
|
let file_id = id.file_id();
|
||||||
let hygiene = Hygiene::new(db.upcast(), ast.file_id);
|
let item_tree = id.item_tree(db.upcast());
|
||||||
let mut cur = 0;
|
let import = &item_tree[id.value];
|
||||||
let mut tree = None;
|
|
||||||
ModPath::expand_use_item(
|
|
||||||
db.upcast(),
|
|
||||||
InFile::new(ast.file_id, use_item),
|
|
||||||
&hygiene,
|
|
||||||
|_mod_path, use_tree, _is_glob, _alias| {
|
|
||||||
if cur == *index {
|
|
||||||
tree = Some(use_tree.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
cur += 1;
|
let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index);
|
||||||
},
|
sink.push(UnresolvedImport { file: file_id, node: AstPtr::new(&use_tree) });
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(tree) = tree {
|
|
||||||
sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
|
DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
|
||||||
|
|
|
@ -523,21 +523,38 @@ impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A desugared `use` import.
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct Import {
|
pub struct Import {
|
||||||
pub path: Interned<ModPath>,
|
|
||||||
pub alias: Option<ImportAlias>,
|
|
||||||
pub visibility: RawVisibilityId,
|
pub visibility: RawVisibilityId,
|
||||||
pub is_glob: bool,
|
|
||||||
/// AST ID of the `use` item this import was derived from. Note that many `Import`s can map to
|
|
||||||
/// the same `use` item.
|
|
||||||
pub ast_id: FileAstId<ast::Use>,
|
pub ast_id: FileAstId<ast::Use>,
|
||||||
/// Index of this `Import` when the containing `Use` is visited via `ModPath::expand_use_item`.
|
pub use_tree: UseTree,
|
||||||
///
|
}
|
||||||
/// This can be used to get the `UseTree` this `Import` corresponds to and allows emitting
|
|
||||||
/// precise diagnostics.
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub index: usize,
|
pub struct UseTree {
|
||||||
|
pub index: Idx<ast::UseTree>,
|
||||||
|
kind: UseTreeKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub enum UseTreeKind {
|
||||||
|
/// ```ignore
|
||||||
|
/// use path::to::Item;
|
||||||
|
/// use path::to::Item as Renamed;
|
||||||
|
/// use path::to::Trait as _;
|
||||||
|
/// ```
|
||||||
|
Single { path: ModPath, alias: Option<ImportAlias> },
|
||||||
|
|
||||||
|
/// ```ignore
|
||||||
|
/// use *; // (invalid, but can occur in nested tree)
|
||||||
|
/// use path::*;
|
||||||
|
/// ```
|
||||||
|
Glob { path: Option<ModPath> },
|
||||||
|
|
||||||
|
/// ```ignore
|
||||||
|
/// use prefix::{self, Item, ...};
|
||||||
|
/// ```
|
||||||
|
Prefixed { prefix: Option<ModPath>, list: Vec<UseTree> },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
@ -711,6 +728,97 @@ pub struct MacroDef {
|
||||||
pub ast_id: FileAstId<ast::MacroDef>,
|
pub ast_id: FileAstId<ast::MacroDef>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Import {
|
||||||
|
/// Maps a `UseTree` contained in this import back to its AST node.
|
||||||
|
pub fn use_tree_to_ast(
|
||||||
|
&self,
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
file_id: HirFileId,
|
||||||
|
index: Idx<ast::UseTree>,
|
||||||
|
) -> ast::UseTree {
|
||||||
|
// Re-lower the AST item and get the source map.
|
||||||
|
// Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
|
||||||
|
let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
|
||||||
|
let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
|
||||||
|
let hygiene = Hygiene::new(db.upcast(), file_id);
|
||||||
|
let (_, source_map) =
|
||||||
|
lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree");
|
||||||
|
source_map[index].clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UseTree {
|
||||||
|
/// Expands the `UseTree` into individually imported `ModPath`s.
|
||||||
|
pub fn expand(
|
||||||
|
&self,
|
||||||
|
mut cb: impl FnMut(Idx<ast::UseTree>, ModPath, /* is_glob */ bool, Option<ImportAlias>),
|
||||||
|
) {
|
||||||
|
self.expand_impl(None, &mut cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_impl(
|
||||||
|
&self,
|
||||||
|
prefix: Option<ModPath>,
|
||||||
|
cb: &mut dyn FnMut(
|
||||||
|
Idx<ast::UseTree>,
|
||||||
|
ModPath,
|
||||||
|
/* is_glob */ bool,
|
||||||
|
Option<ImportAlias>,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
fn concat_mod_paths(prefix: Option<ModPath>, path: &ModPath) -> Option<ModPath> {
|
||||||
|
match (prefix, &path.kind) {
|
||||||
|
(None, _) => Some(path.clone()),
|
||||||
|
(Some(mut prefix), PathKind::Plain) => {
|
||||||
|
for segment in path.segments() {
|
||||||
|
prefix.push_segment(segment.clone());
|
||||||
|
}
|
||||||
|
Some(prefix)
|
||||||
|
}
|
||||||
|
(Some(prefix), PathKind::Super(0)) => {
|
||||||
|
// `some::path::self` == `some::path`
|
||||||
|
if path.segments().is_empty() {
|
||||||
|
Some(prefix)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Some(_), _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match &self.kind {
|
||||||
|
UseTreeKind::Single { path, alias } => {
|
||||||
|
if let Some(path) = concat_mod_paths(prefix, path) {
|
||||||
|
cb(self.index, path, false, alias.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UseTreeKind::Glob { path: Some(path) } => {
|
||||||
|
if let Some(path) = concat_mod_paths(prefix, path) {
|
||||||
|
cb(self.index, path, true, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UseTreeKind::Glob { path: None } => {
|
||||||
|
if let Some(prefix) = prefix {
|
||||||
|
cb(self.index, prefix, true, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UseTreeKind::Prefixed { prefix: additional_prefix, list } => {
|
||||||
|
let prefix = match additional_prefix {
|
||||||
|
Some(path) => match concat_mod_paths(prefix, path) {
|
||||||
|
Some(path) => Some(path),
|
||||||
|
None => return,
|
||||||
|
},
|
||||||
|
None => prefix,
|
||||||
|
};
|
||||||
|
for tree in list {
|
||||||
|
tree.expand_impl(prefix.clone(), cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impl_froms {
|
macro_rules! impl_froms {
|
||||||
($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
|
($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
|
||||||
$(
|
$(
|
||||||
|
|
|
@ -35,7 +35,6 @@ pub(super) struct Ctx<'a> {
|
||||||
db: &'a dyn DefDatabase,
|
db: &'a dyn DefDatabase,
|
||||||
tree: ItemTree,
|
tree: ItemTree,
|
||||||
hygiene: Hygiene,
|
hygiene: Hygiene,
|
||||||
file: HirFileId,
|
|
||||||
source_ast_id_map: Arc<AstIdMap>,
|
source_ast_id_map: Arc<AstIdMap>,
|
||||||
body_ctx: crate::body::LowerCtx<'a>,
|
body_ctx: crate::body::LowerCtx<'a>,
|
||||||
forced_visibility: Option<RawVisibilityId>,
|
forced_visibility: Option<RawVisibilityId>,
|
||||||
|
@ -47,7 +46,6 @@ impl<'a> Ctx<'a> {
|
||||||
db,
|
db,
|
||||||
tree: ItemTree::default(),
|
tree: ItemTree::default(),
|
||||||
hygiene,
|
hygiene,
|
||||||
file,
|
|
||||||
source_ast_id_map: db.ast_id_map(file),
|
source_ast_id_map: db.ast_id_map(file),
|
||||||
body_ctx: crate::body::LowerCtx::new(db, file),
|
body_ctx: crate::body::LowerCtx::new(db, file),
|
||||||
forced_visibility: None,
|
forced_visibility: None,
|
||||||
|
@ -561,30 +559,13 @@ impl<'a> Ctx<'a> {
|
||||||
Some(id(self.data().impls.alloc(res)))
|
Some(id(self.data().impls.alloc(res)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_use(&mut self, use_item: &ast::Use) -> Vec<FileItemTreeId<Import>> {
|
fn lower_use(&mut self, use_item: &ast::Use) -> Option<FileItemTreeId<Import>> {
|
||||||
let visibility = self.lower_visibility(use_item);
|
let visibility = self.lower_visibility(use_item);
|
||||||
let ast_id = self.source_ast_id_map.ast_id(use_item);
|
let ast_id = self.source_ast_id_map.ast_id(use_item);
|
||||||
|
let (use_tree, _) = lower_use_tree(self.db, &self.hygiene, use_item.use_tree()?)?;
|
||||||
|
|
||||||
// Every use item can expand to many `Import`s.
|
let res = Import { visibility, ast_id, use_tree };
|
||||||
let mut imports = Vec::new();
|
Some(id(self.data().imports.alloc(res)))
|
||||||
let tree = self.tree.data_mut();
|
|
||||||
ModPath::expand_use_item(
|
|
||||||
self.db,
|
|
||||||
InFile::new(self.file, use_item.clone()),
|
|
||||||
&self.hygiene,
|
|
||||||
|path, _use_tree, is_glob, alias| {
|
|
||||||
imports.push(id(tree.imports.alloc(Import {
|
|
||||||
path: Interned::new(path),
|
|
||||||
alias,
|
|
||||||
visibility,
|
|
||||||
is_glob,
|
|
||||||
ast_id,
|
|
||||||
index: imports.len(),
|
|
||||||
})));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
imports
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_extern_crate(
|
fn lower_extern_crate(
|
||||||
|
@ -884,3 +865,76 @@ fn lower_abi(abi: ast::Abi) -> Interned<str> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UseTreeLowering<'a> {
|
||||||
|
db: &'a dyn DefDatabase,
|
||||||
|
hygiene: &'a Hygiene,
|
||||||
|
mapping: Arena<ast::UseTree>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UseTreeLowering<'_> {
|
||||||
|
fn lower_use_tree(&mut self, tree: ast::UseTree) -> Option<UseTree> {
|
||||||
|
if let Some(use_tree_list) = tree.use_tree_list() {
|
||||||
|
let prefix = match tree.path() {
|
||||||
|
// E.g. use something::{{{inner}}};
|
||||||
|
None => None,
|
||||||
|
// E.g. `use something::{inner}` (prefix is `None`, path is `something`)
|
||||||
|
// or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
|
||||||
|
Some(path) => {
|
||||||
|
match ModPath::from_src(self.db, path, &self.hygiene) {
|
||||||
|
Some(it) => Some(it),
|
||||||
|
None => return None, // FIXME: report errors somewhere
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let list =
|
||||||
|
use_tree_list.use_trees().filter_map(|tree| self.lower_use_tree(tree)).collect();
|
||||||
|
|
||||||
|
Some(self.use_tree(UseTreeKind::Prefixed { prefix, list }, tree))
|
||||||
|
} else {
|
||||||
|
let is_glob = tree.star_token().is_some();
|
||||||
|
let path = match tree.path() {
|
||||||
|
Some(path) => Some(ModPath::from_src(self.db, path, &self.hygiene)?),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let alias = tree.rename().map(|a| {
|
||||||
|
a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
|
||||||
|
});
|
||||||
|
if alias.is_some() && is_glob {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match (path, alias, is_glob) {
|
||||||
|
(path, None, true) => {
|
||||||
|
if path.is_none() {
|
||||||
|
cov_mark::hit!(glob_enum_group);
|
||||||
|
}
|
||||||
|
Some(self.use_tree(UseTreeKind::Glob { path }, tree))
|
||||||
|
}
|
||||||
|
// Globs can't be renamed
|
||||||
|
(_, Some(_), true) | (None, None, false) => None,
|
||||||
|
// `bla::{ as Name}` is invalid
|
||||||
|
(None, Some(_), false) => None,
|
||||||
|
(Some(path), alias, false) => {
|
||||||
|
Some(self.use_tree(UseTreeKind::Single { path, alias }, tree))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_tree(&mut self, kind: UseTreeKind, ast: ast::UseTree) -> UseTree {
|
||||||
|
let index = self.mapping.alloc(ast);
|
||||||
|
UseTree { index, kind }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn lower_use_tree(
|
||||||
|
db: &dyn DefDatabase,
|
||||||
|
hygiene: &Hygiene,
|
||||||
|
tree: ast::UseTree,
|
||||||
|
) -> Option<(UseTree, Arena<ast::UseTree>)> {
|
||||||
|
let mut lowering = UseTreeLowering { db, hygiene, mapping: Arena::new() };
|
||||||
|
let tree = lowering.lower_use_tree(tree)?;
|
||||||
|
Some((tree, lowering.mapping))
|
||||||
|
}
|
||||||
|
|
|
@ -163,21 +163,46 @@ impl<'a> Printer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_use_tree(&mut self, use_tree: &UseTree) {
|
||||||
|
match &use_tree.kind {
|
||||||
|
UseTreeKind::Single { path, alias } => {
|
||||||
|
w!(self, "{}", path);
|
||||||
|
if let Some(alias) = alias {
|
||||||
|
w!(self, " as {}", alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UseTreeKind::Glob { path } => {
|
||||||
|
if let Some(path) = path {
|
||||||
|
w!(self, "{}::", path);
|
||||||
|
}
|
||||||
|
w!(self, "*");
|
||||||
|
}
|
||||||
|
UseTreeKind::Prefixed { prefix, list } => {
|
||||||
|
if let Some(prefix) = prefix {
|
||||||
|
w!(self, "{}::", prefix);
|
||||||
|
}
|
||||||
|
w!(self, "{{");
|
||||||
|
for (i, tree) in list.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
w!(self, ", ");
|
||||||
|
}
|
||||||
|
self.print_use_tree(tree);
|
||||||
|
}
|
||||||
|
w!(self, "}}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn print_mod_item(&mut self, item: ModItem) {
|
fn print_mod_item(&mut self, item: ModItem) {
|
||||||
self.print_attrs_of(item);
|
self.print_attrs_of(item);
|
||||||
|
|
||||||
match item {
|
match item {
|
||||||
ModItem::Import(it) => {
|
ModItem::Import(it) => {
|
||||||
let Import { visibility, path, is_glob, alias, ast_id: _, index } = &self.tree[it];
|
let Import { visibility, use_tree, ast_id: _ } = &self.tree[it];
|
||||||
self.print_visibility(*visibility);
|
self.print_visibility(*visibility);
|
||||||
w!(self, "use {}", path);
|
w!(self, "use ");
|
||||||
if *is_glob {
|
self.print_use_tree(use_tree);
|
||||||
w!(self, "::*");
|
wln!(self, ";");
|
||||||
}
|
|
||||||
if let Some(alias) = alias {
|
|
||||||
w!(self, " as {}", alias);
|
|
||||||
}
|
|
||||||
wln!(self, "; // {}", index);
|
|
||||||
}
|
}
|
||||||
ModItem::ExternCrate(it) => {
|
ModItem::ExternCrate(it) => {
|
||||||
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
|
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
|
||||||
|
|
|
@ -26,6 +26,8 @@ use globs::*;
|
||||||
|
|
||||||
/// docs on import
|
/// docs on import
|
||||||
use crate::{A, B};
|
use crate::{A, B};
|
||||||
|
|
||||||
|
use a::{c, d::{e}};
|
||||||
"#,
|
"#,
|
||||||
expect![[r##"
|
expect![[r##"
|
||||||
#![doc = " file comment"] // AttrId { is_doc_comment: true, ast_index: 0 }
|
#![doc = " file comment"] // AttrId { is_doc_comment: true, ast_index: 0 }
|
||||||
|
@ -36,19 +38,14 @@ use crate::{A, B};
|
||||||
|
|
||||||
pub(super) extern crate bli;
|
pub(super) extern crate bli;
|
||||||
|
|
||||||
pub use crate::path::nested; // 0
|
pub use crate::path::{nested, items as renamed, Trait as _};
|
||||||
|
|
||||||
pub use crate::path::items as renamed; // 1
|
pub(self) use globs::*;
|
||||||
|
|
||||||
pub use crate::path::Trait as _; // 2
|
|
||||||
|
|
||||||
pub(self) use globs::*; // 0
|
|
||||||
|
|
||||||
#[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 }
|
#[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 }
|
||||||
pub(self) use crate::A; // 0
|
pub(self) use crate::{A, B};
|
||||||
|
|
||||||
#[doc = " docs on import"] // AttrId { is_doc_comment: true, ast_index: 0 }
|
pub(self) use a::{c, d::{e}};
|
||||||
pub(self) use crate::B; // 1
|
|
||||||
"##]],
|
"##]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -218,7 +215,7 @@ mod outline;
|
||||||
#[doc = " outer"] // AttrId { is_doc_comment: true, ast_index: 0 }
|
#[doc = " outer"] // AttrId { is_doc_comment: true, ast_index: 0 }
|
||||||
#[doc = " inner"] // AttrId { is_doc_comment: true, ast_index: 1 }
|
#[doc = " inner"] // AttrId { is_doc_comment: true, ast_index: 1 }
|
||||||
pub(self) mod inline {
|
pub(self) mod inline {
|
||||||
pub(self) use super::*; // 0
|
pub(self) use super::*;
|
||||||
|
|
||||||
// flags = 0x2
|
// flags = 0x2
|
||||||
pub(self) fn fn_in_module() -> ();
|
pub(self) fn fn_in_module() -> ();
|
||||||
|
|
|
@ -17,6 +17,7 @@ use hir_expand::{
|
||||||
};
|
};
|
||||||
use hir_expand::{InFile, MacroCallLoc};
|
use hir_expand::{InFile, MacroCallLoc};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use la_arena::Idx;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
|
||||||
|
@ -143,7 +144,7 @@ impl PartialResolvedImport {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
enum ImportSource {
|
enum ImportSource {
|
||||||
Import(ItemTreeId<item_tree::Import>),
|
Import { id: ItemTreeId<item_tree::Import>, use_tree: Idx<ast::UseTree> },
|
||||||
ExternCrate(ItemTreeId<item_tree::ExternCrate>),
|
ExternCrate(ItemTreeId<item_tree::ExternCrate>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,20 +166,26 @@ impl Import {
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
tree: &ItemTree,
|
tree: &ItemTree,
|
||||||
id: ItemTreeId<item_tree::Import>,
|
id: ItemTreeId<item_tree::Import>,
|
||||||
) -> Self {
|
) -> Vec<Self> {
|
||||||
let it = &tree[id.value];
|
let it = &tree[id.value];
|
||||||
let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
|
let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
|
||||||
let visibility = &tree[it.visibility];
|
let visibility = &tree[it.visibility];
|
||||||
Self {
|
let is_prelude = attrs.by_key("prelude_import").exists();
|
||||||
path: it.path.clone(),
|
|
||||||
alias: it.alias.clone(),
|
let mut res = Vec::new();
|
||||||
visibility: visibility.clone(),
|
it.use_tree.expand(|idx, path, is_glob, alias| {
|
||||||
is_glob: it.is_glob,
|
res.push(Self {
|
||||||
is_prelude: attrs.by_key("prelude_import").exists(),
|
path: Interned::new(path), // FIXME this makes little sense
|
||||||
is_extern_crate: false,
|
alias,
|
||||||
is_macro_use: false,
|
visibility: visibility.clone(),
|
||||||
source: ImportSource::Import(id),
|
is_glob,
|
||||||
}
|
is_prelude,
|
||||||
|
is_extern_crate: false,
|
||||||
|
is_macro_use: false,
|
||||||
|
source: ImportSource::Import { id, use_tree: idx },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_extern_crate(
|
fn from_extern_crate(
|
||||||
|
@ -1130,11 +1137,8 @@ impl DefCollector<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
for directive in &self.unresolved_imports {
|
for directive in &self.unresolved_imports {
|
||||||
if let ImportSource::Import(import) = &directive.import.source {
|
if let ImportSource::Import { id: import, use_tree } = &directive.import.source {
|
||||||
let item_tree = import.item_tree(self.db);
|
match (directive.import.path.segments().first(), &directive.import.path.kind) {
|
||||||
let import_data = &item_tree[import.value];
|
|
||||||
|
|
||||||
match (import_data.path.segments().first(), &import_data.path.kind) {
|
|
||||||
(Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => {
|
(Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => {
|
||||||
if diagnosed_extern_crates.contains(krate) {
|
if diagnosed_extern_crates.contains(krate) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -1145,8 +1149,8 @@ impl DefCollector<'_> {
|
||||||
|
|
||||||
self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
|
self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
|
||||||
directive.module_id,
|
directive.module_id,
|
||||||
InFile::new(import.file_id(), import_data.ast_id),
|
*import,
|
||||||
import_data.index,
|
*use_tree,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1222,16 +1226,20 @@ impl ModCollector<'_, '_> {
|
||||||
match item {
|
match item {
|
||||||
ModItem::Mod(m) => self.collect_module(&self.item_tree[m], &attrs),
|
ModItem::Mod(m) => self.collect_module(&self.item_tree[m], &attrs),
|
||||||
ModItem::Import(import_id) => {
|
ModItem::Import(import_id) => {
|
||||||
self.def_collector.unresolved_imports.push(ImportDirective {
|
let module_id = self.module_id;
|
||||||
module_id: self.module_id,
|
let imports = Import::from_use(
|
||||||
import: Import::from_use(
|
self.def_collector.db,
|
||||||
self.def_collector.db,
|
krate,
|
||||||
krate,
|
&self.item_tree,
|
||||||
&self.item_tree,
|
ItemTreeId::new(self.file_id, import_id),
|
||||||
ItemTreeId::new(self.file_id, import_id),
|
);
|
||||||
),
|
self.def_collector.unresolved_imports.extend(imports.into_iter().map(
|
||||||
status: PartialResolvedImport::Unresolved,
|
|import| ImportDirective {
|
||||||
})
|
module_id,
|
||||||
|
import,
|
||||||
|
status: PartialResolvedImport::Unresolved,
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
ModItem::ExternCrate(import_id) => {
|
ModItem::ExternCrate(import_id) => {
|
||||||
self.def_collector.unresolved_imports.push(ImportDirective {
|
self.def_collector.unresolved_imports.push(ImportDirective {
|
||||||
|
|
|
@ -2,9 +2,15 @@
|
||||||
|
|
||||||
use cfg::{CfgExpr, CfgOptions};
|
use cfg::{CfgExpr, CfgOptions};
|
||||||
use hir_expand::MacroCallKind;
|
use hir_expand::MacroCallKind;
|
||||||
|
use la_arena::Idx;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
|
||||||
use crate::{nameres::LocalModuleId, path::ModPath, AstId};
|
use crate::{
|
||||||
|
item_tree::{self, ItemTreeId},
|
||||||
|
nameres::LocalModuleId,
|
||||||
|
path::ModPath,
|
||||||
|
AstId,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum DefDiagnosticKind {
|
pub enum DefDiagnosticKind {
|
||||||
|
@ -12,7 +18,7 @@ pub enum DefDiagnosticKind {
|
||||||
|
|
||||||
UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
|
UnresolvedExternCrate { ast: AstId<ast::ExternCrate> },
|
||||||
|
|
||||||
UnresolvedImport { ast: AstId<ast::Use>, index: usize },
|
UnresolvedImport { id: ItemTreeId<item_tree::Import>, index: Idx<ast::UseTree> },
|
||||||
|
|
||||||
UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
|
UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
|
||||||
|
|
||||||
|
@ -53,10 +59,10 @@ impl DefDiagnostic {
|
||||||
|
|
||||||
pub(super) fn unresolved_import(
|
pub(super) fn unresolved_import(
|
||||||
container: LocalModuleId,
|
container: LocalModuleId,
|
||||||
ast: AstId<ast::Use>,
|
id: ItemTreeId<item_tree::Import>,
|
||||||
index: usize,
|
index: Idx<ast::UseTree>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { ast, index } }
|
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedImport { id, index } }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn unconfigured_code(
|
pub(super) fn unconfigured_code(
|
||||||
|
|
|
@ -14,10 +14,7 @@ use hir_expand::{
|
||||||
};
|
};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
|
||||||
use crate::{
|
use crate::type_ref::{TypeBound, TypeRef};
|
||||||
type_ref::{TypeBound, TypeRef},
|
|
||||||
InFile,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct ModPath {
|
pub struct ModPath {
|
||||||
|
@ -56,8 +53,7 @@ impl Display for ImportAlias {
|
||||||
|
|
||||||
impl ModPath {
|
impl ModPath {
|
||||||
pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
|
pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
|
||||||
let ctx = LowerCtx::with_hygiene(db, hygiene);
|
lower::convert_path(db, None, path, hygiene)
|
||||||
lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
|
pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
|
||||||
|
@ -70,18 +66,6 @@ impl ModPath {
|
||||||
ModPath { kind, segments: Vec::new() }
|
ModPath { kind, segments: Vec::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls `cb` with all paths, represented by this use item.
|
|
||||||
pub fn expand_use_item(
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
item_src: InFile<ast::Use>,
|
|
||||||
hygiene: &Hygiene,
|
|
||||||
mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option<ImportAlias>),
|
|
||||||
) {
|
|
||||||
if let Some(tree) = item_src.value.use_tree() {
|
|
||||||
lower::lower_use_tree(db, None, tree, hygiene, &mut cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn segments(&self) -> &[Name] {
|
pub fn segments(&self) -> &[Name] {
|
||||||
&self.segments
|
&self.segments
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
type_ref::{LifetimeRef, TypeBound, TypeRef},
|
type_ref::{LifetimeRef, TypeBound, TypeRef},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) use lower_use::lower_use_tree;
|
pub(super) use lower_use::convert_path;
|
||||||
|
|
||||||
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
||||||
/// It correctly handles `$crate` based path from macro call.
|
/// It correctly handles `$crate` based path from macro call.
|
||||||
|
|
|
@ -4,68 +4,15 @@
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_expand::{hygiene::Hygiene, name::AsName};
|
use hir_expand::hygiene::Hygiene;
|
||||||
use syntax::ast::{self, NameOwner};
|
use syntax::{ast, AstNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
path::{ImportAlias, ModPath, PathKind},
|
path::{ModPath, PathKind},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn lower_use_tree(
|
pub(crate) fn convert_path(
|
||||||
db: &dyn DefDatabase,
|
|
||||||
prefix: Option<ModPath>,
|
|
||||||
tree: ast::UseTree,
|
|
||||||
hygiene: &Hygiene,
|
|
||||||
cb: &mut dyn FnMut(ModPath, &ast::UseTree, bool, Option<ImportAlias>),
|
|
||||||
) {
|
|
||||||
if let Some(use_tree_list) = tree.use_tree_list() {
|
|
||||||
let prefix = match tree.path() {
|
|
||||||
// E.g. use something::{{{inner}}};
|
|
||||||
None => prefix,
|
|
||||||
// E.g. `use something::{inner}` (prefix is `None`, path is `something`)
|
|
||||||
// or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
|
|
||||||
Some(path) => match convert_path(db, prefix, path, hygiene) {
|
|
||||||
Some(it) => Some(it),
|
|
||||||
None => return, // FIXME: report errors somewhere
|
|
||||||
},
|
|
||||||
};
|
|
||||||
for child_tree in use_tree_list.use_trees() {
|
|
||||||
lower_use_tree(db, prefix.clone(), child_tree, hygiene, cb);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let alias = tree.rename().map(|a| {
|
|
||||||
a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
|
|
||||||
});
|
|
||||||
let is_glob = tree.star_token().is_some();
|
|
||||||
if let Some(ast_path) = tree.path() {
|
|
||||||
// Handle self in a path.
|
|
||||||
// E.g. `use something::{self, <...>}`
|
|
||||||
if ast_path.qualifier().is_none() {
|
|
||||||
if let Some(segment) = ast_path.segment() {
|
|
||||||
if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
|
|
||||||
if let Some(prefix) = prefix {
|
|
||||||
cb(prefix, &tree, false, alias);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(path) = convert_path(db, prefix, ast_path, hygiene) {
|
|
||||||
cb(path, &tree, is_glob, alias)
|
|
||||||
}
|
|
||||||
// FIXME: report errors somewhere
|
|
||||||
// We get here if we do
|
|
||||||
} else if is_glob {
|
|
||||||
cov_mark::hit!(glob_enum_group);
|
|
||||||
if let Some(prefix) = prefix {
|
|
||||||
cb(prefix, &tree, is_glob, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_path(
|
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
prefix: Option<ModPath>,
|
prefix: Option<ModPath>,
|
||||||
path: ast::Path,
|
path: ast::Path,
|
||||||
|
@ -78,7 +25,7 @@ fn convert_path(
|
||||||
};
|
};
|
||||||
|
|
||||||
let segment = path.segment()?;
|
let segment = path.segment()?;
|
||||||
let res = match segment.kind()? {
|
let mut mod_path = match segment.kind()? {
|
||||||
ast::PathSegmentKind::Name(name_ref) => {
|
ast::PathSegmentKind::Name(name_ref) => {
|
||||||
match hygiene.name_ref_to_name(db.upcast(), name_ref) {
|
match hygiene.name_ref_to_name(db.upcast(), name_ref) {
|
||||||
Either::Left(name) => {
|
Either::Left(name) => {
|
||||||
|
@ -125,5 +72,18 @@ fn convert_path(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Some(res)
|
|
||||||
|
// handle local_inner_macros :
|
||||||
|
// Basically, even in rustc it is quite hacky:
|
||||||
|
// https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
|
||||||
|
// We follow what it did anyway :)
|
||||||
|
if mod_path.segments.len() == 1 && mod_path.kind == PathKind::Plain {
|
||||||
|
if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
|
||||||
|
if let Some(crate_id) = hygiene.local_inner_macros(db.upcast(), path) {
|
||||||
|
mod_path.kind = PathKind::DollarCrate(crate_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(mod_path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,9 +278,11 @@ impl TestDB {
|
||||||
let node = ast.to_node(self.upcast());
|
let node = ast.to_node(self.upcast());
|
||||||
(InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate")
|
(InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedExternCrate")
|
||||||
}
|
}
|
||||||
DefDiagnosticKind::UnresolvedImport { ast, .. } => {
|
DefDiagnosticKind::UnresolvedImport { id, .. } => {
|
||||||
let node = ast.to_node(self.upcast());
|
let item_tree = id.item_tree(self.upcast());
|
||||||
(InFile::new(ast.file_id, node.syntax().clone()), "UnresolvedImport")
|
let import = &item_tree[id.value];
|
||||||
|
let node = InFile::new(id.file_id(), import.ast_id).to_node(self.upcast());
|
||||||
|
(InFile::new(id.file_id(), node.syntax().clone()), "UnresolvedImport")
|
||||||
}
|
}
|
||||||
DefDiagnosticKind::UnconfiguredCode { ast, .. } => {
|
DefDiagnosticKind::UnconfiguredCode { ast, .. } => {
|
||||||
let node = ast.to_node(self.upcast());
|
let node = ast.to_node(self.upcast());
|
||||||
|
|
|
@ -311,6 +311,7 @@ mod tests {
|
||||||
/// * a diagnostic is produced
|
/// * a diagnostic is produced
|
||||||
/// * the first diagnostic fix trigger range touches the input cursor position
|
/// * the first diagnostic fix trigger range touches the input cursor position
|
||||||
/// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
|
/// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
|
||||||
|
#[track_caller]
|
||||||
pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
|
pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
|
||||||
check_nth_fix(0, ra_fixture_before, ra_fixture_after);
|
check_nth_fix(0, ra_fixture_before, ra_fixture_after);
|
||||||
}
|
}
|
||||||
|
@ -325,6 +326,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
|
fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
|
||||||
let after = trim_indent(ra_fixture_after);
|
let after = trim_indent(ra_fixture_after);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue