Skip to content

Commit d0a4ba2

Browse files
Merge #8997
8997: internal: stop expanding UseTrees during ItemTree lowering r=jonas-schievink a=jonas-schievink Closes #8908 Messy diff, but `ItemTree` lowering got simpler, since we now have a strict 1-to-1 mapping between `ast::Item` and `ModItem`. The most messy part is mapping a single `UseTree` back to its `ast::UseTree` counterpart for diagnostics, but I think the ad-hoc source map built during lowering does the job. Co-authored-by: Jonas Schievink <[email protected]>
2 parents bfb06e1 + 196cb65 commit d0a4ba2

File tree

12 files changed

+351
-241
lines changed

12 files changed

+351
-241
lines changed

crates/hir/src/lib.rs

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -472,27 +472,13 @@ impl Module {
472472
});
473473
}
474474

475-
DefDiagnosticKind::UnresolvedImport { ast, index } => {
476-
let use_item = ast.to_node(db.upcast());
477-
let hygiene = Hygiene::new(db.upcast(), ast.file_id);
478-
let mut cur = 0;
479-
let mut tree = None;
480-
ModPath::expand_use_item(
481-
db.upcast(),
482-
InFile::new(ast.file_id, use_item),
483-
&hygiene,
484-
|_mod_path, use_tree, _is_glob, _alias| {
485-
if cur == *index {
486-
tree = Some(use_tree.clone());
487-
}
488-
489-
cur += 1;
490-
},
491-
);
475+
DefDiagnosticKind::UnresolvedImport { id, index } => {
476+
let file_id = id.file_id();
477+
let item_tree = id.item_tree(db.upcast());
478+
let import = &item_tree[id.value];
492479

493-
if let Some(tree) = tree {
494-
sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) });
495-
}
480+
let use_tree = import.use_tree_to_ast(db.upcast(), file_id, *index);
481+
sink.push(UnresolvedImport { file: file_id, node: AstPtr::new(&use_tree) });
496482
}
497483

498484
DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {

crates/hir_def/src/item_tree.rs

Lines changed: 119 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -523,21 +523,38 @@ impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
523523
}
524524
}
525525

526-
/// A desugared `use` import.
527526
#[derive(Debug, Clone, Eq, PartialEq)]
528527
pub struct Import {
529-
pub path: Interned<ModPath>,
530-
pub alias: Option<ImportAlias>,
531528
pub visibility: RawVisibilityId,
532-
pub is_glob: bool,
533-
/// AST ID of the `use` item this import was derived from. Note that many `Import`s can map to
534-
/// the same `use` item.
535529
pub ast_id: FileAstId<ast::Use>,
536-
/// Index of this `Import` when the containing `Use` is visited via `ModPath::expand_use_item`.
537-
///
538-
/// This can be used to get the `UseTree` this `Import` corresponds to and allows emitting
539-
/// precise diagnostics.
540-
pub index: usize,
530+
pub use_tree: UseTree,
531+
}
532+
533+
#[derive(Debug, Clone, Eq, PartialEq)]
534+
pub struct UseTree {
535+
pub index: Idx<ast::UseTree>,
536+
kind: UseTreeKind,
537+
}
538+
539+
#[derive(Debug, Clone, Eq, PartialEq)]
540+
pub enum UseTreeKind {
541+
/// ```
542+
/// use path::to::Item;
543+
/// use path::to::Item as Renamed;
544+
/// use path::to::Trait as _;
545+
/// ```
546+
Single { path: Interned<ModPath>, alias: Option<ImportAlias> },
547+
548+
/// ```
549+
/// use *; // (invalid, but can occur in nested tree)
550+
/// use path::*;
551+
/// ```
552+
Glob { path: Option<Interned<ModPath>> },
553+
554+
/// ```
555+
/// use prefix::{self, Item, ...};
556+
/// ```
557+
Prefixed { prefix: Option<Interned<ModPath>>, list: Box<[UseTree]> },
541558
}
542559

543560
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -711,6 +728,97 @@ pub struct MacroDef {
711728
pub ast_id: FileAstId<ast::MacroDef>,
712729
}
713730

731+
impl Import {
732+
/// Maps a `UseTree` contained in this import back to its AST node.
733+
pub fn use_tree_to_ast(
734+
&self,
735+
db: &dyn DefDatabase,
736+
file_id: HirFileId,
737+
index: Idx<ast::UseTree>,
738+
) -> ast::UseTree {
739+
// Re-lower the AST item and get the source map.
740+
// Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
741+
let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
742+
let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
743+
let hygiene = Hygiene::new(db.upcast(), file_id);
744+
let (_, source_map) =
745+
lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree");
746+
source_map[index].clone()
747+
}
748+
}
749+
750+
impl UseTree {
751+
/// Expands the `UseTree` into individually imported `ModPath`s.
752+
pub fn expand(
753+
&self,
754+
mut cb: impl FnMut(Idx<ast::UseTree>, ModPath, /* is_glob */ bool, Option<ImportAlias>),
755+
) {
756+
self.expand_impl(None, &mut cb)
757+
}
758+
759+
fn expand_impl(
760+
&self,
761+
prefix: Option<ModPath>,
762+
cb: &mut dyn FnMut(
763+
Idx<ast::UseTree>,
764+
ModPath,
765+
/* is_glob */ bool,
766+
Option<ImportAlias>,
767+
),
768+
) {
769+
fn concat_mod_paths(prefix: Option<ModPath>, path: &ModPath) -> Option<ModPath> {
770+
match (prefix, &path.kind) {
771+
(None, _) => Some(path.clone()),
772+
(Some(mut prefix), PathKind::Plain) => {
773+
for segment in path.segments() {
774+
prefix.push_segment(segment.clone());
775+
}
776+
Some(prefix)
777+
}
778+
(Some(prefix), PathKind::Super(0)) => {
779+
// `some::path::self` == `some::path`
780+
if path.segments().is_empty() {
781+
Some(prefix)
782+
} else {
783+
None
784+
}
785+
}
786+
(Some(_), _) => None,
787+
}
788+
}
789+
790+
match &self.kind {
791+
UseTreeKind::Single { path, alias } => {
792+
if let Some(path) = concat_mod_paths(prefix, path) {
793+
cb(self.index, path, false, alias.clone());
794+
}
795+
}
796+
UseTreeKind::Glob { path: Some(path) } => {
797+
if let Some(path) = concat_mod_paths(prefix, path) {
798+
cb(self.index, path, true, None);
799+
}
800+
}
801+
UseTreeKind::Glob { path: None } => {
802+
if let Some(prefix) = prefix {
803+
cb(self.index, prefix, true, None);
804+
}
805+
}
806+
UseTreeKind::Prefixed { prefix: additional_prefix, list } => {
807+
let prefix = match additional_prefix {
808+
Some(path) => match concat_mod_paths(prefix, path) {
809+
Some(path) => Some(path),
810+
None => return,
811+
},
812+
None => prefix,
813+
};
814+
for tree in &**list {
815+
tree.expand_impl(prefix.clone(), cb);
816+
}
817+
}
818+
}
819+
}
820+
}
821+
714822
macro_rules! impl_froms {
715823
($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
716824
$(

0 commit comments

Comments
 (0)