Skip to content

internal: stop expanding UseTrees during ItemTree lowering #8997

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 6 additions & 20 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,27 +472,13 @@ impl Module {
});
}

DefDiagnosticKind::UnresolvedImport { ast, index } => {
let use_item = ast.to_node(db.upcast());
let hygiene = Hygiene::new(db.upcast(), ast.file_id);
let mut cur = 0;
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;
},
);
DefDiagnosticKind::UnresolvedImport { id, index } => {
let file_id = id.file_id();
let item_tree = id.item_tree(db.upcast());
let import = &item_tree[id.value];

if let Some(tree) = tree {
sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
}
let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index);
sink.push(UnresolvedImport { file: file_id, node: AstPtr::new(&use_tree) });
}

DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
Expand Down
130 changes: 119 additions & 11 deletions crates/hir_def/src/item_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,21 +523,38 @@ impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
}
}

/// A desugared `use` import.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Import {
pub path: Interned<ModPath>,
pub alias: Option<ImportAlias>,
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>,
/// Index of this `Import` when the containing `Use` is visited via `ModPath::expand_use_item`.
///
/// This can be used to get the `UseTree` this `Import` corresponds to and allows emitting
/// precise diagnostics.
pub index: usize,
pub use_tree: UseTree,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct UseTree {
pub index: Idx<ast::UseTree>,
kind: UseTreeKind,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum UseTreeKind {
/// ```
/// use path::to::Item;
/// use path::to::Item as Renamed;
/// use path::to::Trait as _;
/// ```
Single { path: Interned<ModPath>, alias: Option<ImportAlias> },

/// ```
/// use *; // (invalid, but can occur in nested tree)
/// use path::*;
/// ```
Glob { path: Option<Interned<ModPath>> },

/// ```
/// use prefix::{self, Item, ...};
/// ```
Prefixed { prefix: Option<Interned<ModPath>>, list: Box<[UseTree]> },
}

#[derive(Debug, Clone, Eq, PartialEq)]
Expand Down Expand Up @@ -711,6 +728,97 @@ pub struct 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 {
($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
$(
Expand Down
Loading