]> git.lizzy.rs Git - rust.git/blobdiff - crates/hir_def/src/item_tree.rs
fix: Don't duplicate attribute completions
[rust.git] / crates / hir_def / src / item_tree.rs
index 4a5f44027edca64e51728d1ba6a2fe048b9039e4..5e260880ffde71faffd4503eca21a7c65be6ede1 100644 (file)
 mod tests;
 
 use std::{
-    any::type_name,
     fmt::{self, Debug},
     hash::{Hash, Hasher},
     marker::PhantomData,
-    ops::{Index, Range},
+    ops::Index,
     sync::Arc,
 };
 
-use ast::{AstNode, NameOwner, StructKind};
+use ast::{AstNode, HasName, StructKind};
 use base_db::CrateId;
 use either::Either;
 use hir_expand::{
     ast_id_map::FileAstId,
     hygiene::Hygiene,
     name::{name, AsName, Name},
-    FragmentKind, HirFileId, InFile,
+    ExpandTo, HirFileId, InFile,
 };
-use la_arena::{Arena, Idx, RawIdx};
+use la_arena::{Arena, Idx, IdxRange, RawIdx};
 use profile::Count;
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
@@ -67,6 +66,7 @@
     path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
     type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
     visibility::RawVisibility,
+    BlockId,
 };
 
 #[derive(Copy, Clone, Eq, PartialEq)]
@@ -104,16 +104,15 @@ pub struct ItemTree {
 
 impl ItemTree {
     pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
-        let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id));
-        let syntax = if let Some(node) = db.parse_or_expand(file_id) {
-            if node.kind() == SyntaxKind::ERROR {
-                // FIXME: not 100% sure why these crop up, but return an empty tree to avoid a panic
-                return Default::default();
-            }
-            node
-        } else {
-            return Default::default();
+        let _p = profile::span("file_item_tree_query").detail(|| format!("{:?}", file_id));
+        let syntax = match db.parse_or_expand(file_id) {
+            Some(node) => node,
+            None => return Default::default(),
         };
+        if syntax.kind() == SyntaxKind::ERROR {
+            // FIXME: not 100% sure why these crop up, but return an empty tree to avoid a panic
+            return Default::default();
+        }
 
         let hygiene = Hygiene::new(db.upcast(), file_id);
         let ctx = lower::Ctx::new(db, hygiene.clone(), file_id);
@@ -132,21 +131,6 @@ pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) ->
                     // items.
                     ctx.lower_macro_stmts(stmts)
                 },
-                ast::Pat(_pat) => {
-                    // FIXME: This occurs because macros in pattern position are treated as inner
-                    // items and expanded during block DefMap computation
-                    return Default::default();
-                },
-                ast::Type(ty) => {
-                    // Types can contain inner items. We return an empty item tree in this case, but
-                    // still need to collect inner items.
-                    ctx.lower_inner_items(ty.syntax())
-                },
-                ast::Expr(e) => {
-                    // Macros can expand to expressions. We return an empty item tree in this case, but
-                    // still need to collect inner items.
-                    ctx.lower_inner_items(e.syntax())
-                },
                 _ => {
                     panic!("cannot create item tree from {:?} {}", syntax, syntax);
                 },
@@ -160,6 +144,14 @@ pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) ->
         Arc::new(item_tree)
     }
 
+    fn block_item_tree(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
+        let loc = db.lookup_intern_block(block);
+        let block = loc.ast_id.to_node(db.upcast());
+        let hygiene = Hygiene::new(db.upcast(), loc.ast_id.file_id);
+        let ctx = lower::Ctx::new(db, hygiene.clone(), loc.ast_id.file_id);
+        Arc::new(ctx.lower_block(&block))
+    }
+
     fn shrink_to_fit(&mut self) {
         if let Some(data) = &mut self.data {
             let ItemTreeData {
@@ -183,7 +175,6 @@ fn shrink_to_fit(&mut self) {
                 macro_rules,
                 macro_defs,
                 vis,
-                inner_items,
             } = &mut **data;
 
             imports.shrink_to_fit();
@@ -207,8 +198,6 @@ fn shrink_to_fit(&mut self) {
             macro_defs.shrink_to_fit();
 
             vis.arena.shrink_to_fit();
-
-            inner_items.shrink_to_fit();
         }
     }
 
@@ -227,17 +216,10 @@ pub(crate) fn raw_attrs(&self, of: AttrOwner) -> &RawAttrs {
         self.attrs.get(&of).unwrap_or(&RawAttrs::EMPTY)
     }
 
-    pub fn attrs(&self, db: &dyn DefDatabase, krate: CrateId, of: AttrOwner) -> Attrs {
+    pub(crate) fn attrs(&self, db: &dyn DefDatabase, krate: CrateId, of: AttrOwner) -> Attrs {
         self.raw_attrs(of).clone().filter(db, krate)
     }
 
-    pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] {
-        match &self.data {
-            Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]),
-            None => &[],
-        }
-    }
-
     pub fn pretty_print(&self) -> String {
         pretty::print_item_tree(self)
     }
@@ -297,8 +279,6 @@ struct ItemTreeData {
     macro_defs: Arena<MacroDef>,
 
     vis: ItemVisibilities,
-
-    inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>,
 }
 
 #[derive(Debug, Eq, PartialEq, Hash)]
@@ -374,23 +354,55 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
+/// Identifies a particular [`ItemTree`].
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
+pub struct TreeId {
+    file: HirFileId,
+    block: Option<BlockId>,
+}
+
+impl TreeId {
+    pub(crate) fn new(file: HirFileId, block: Option<BlockId>) -> Self {
+        Self { file, block }
+    }
+
+    pub(crate) fn item_tree(&self, db: &dyn DefDatabase) -> Arc<ItemTree> {
+        match self.block {
+            Some(block) => ItemTree::block_item_tree(db, block),
+            None => db.file_item_tree(self.file),
+        }
+    }
+
+    pub(crate) fn file_id(self) -> HirFileId {
+        self.file
+    }
+
+    pub(crate) fn is_block(self) -> bool {
+        self.block.is_some()
+    }
+}
+
 #[derive(Debug)]
 pub struct ItemTreeId<N: ItemTreeNode> {
-    file: HirFileId,
+    tree: TreeId,
     pub value: FileItemTreeId<N>,
 }
 
 impl<N: ItemTreeNode> ItemTreeId<N> {
-    pub fn new(file: HirFileId, idx: FileItemTreeId<N>) -> Self {
-        Self { file, value: idx }
+    pub fn new(tree: TreeId, idx: FileItemTreeId<N>) -> Self {
+        Self { tree, value: idx }
     }
 
     pub fn file_id(self) -> HirFileId {
-        self.file
+        self.tree.file
+    }
+
+    pub fn tree_id(self) -> TreeId {
+        self.tree
     }
 
     pub fn item_tree(self, db: &dyn DefDatabase) -> Arc<ItemTree> {
-        db.file_item_tree(self.file)
+        self.tree.item_tree(db)
     }
 }
 
@@ -403,7 +415,7 @@ fn clone(&self) -> Self {
 
 impl<N: ItemTreeNode> PartialEq for ItemTreeId<N> {
     fn eq(&self, other: &Self) -> bool {
-        self.file == other.file && self.value == other.value
+        self.tree == other.tree && self.value == other.value
     }
 }
 
@@ -411,7 +423,7 @@ impl<N: ItemTreeNode> Eq for ItemTreeId<N> {}
 
 impl<N: ItemTreeNode> Hash for ItemTreeId<N> {
     fn hash<H: Hasher>(&self, state: &mut H) {
-        self.file.hash(state);
+        self.tree.hash(state);
         self.value.hash(state);
     }
 }
@@ -446,10 +458,9 @@ fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
                 }
 
                 fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> {
-                    if let ModItem::$typ(id) = mod_item {
-                        Some(id)
-                    } else {
-                        None
+                    match mod_item {
+                        ModItem::$typ(id) => Some(id),
+                        _ => None,
                     }
                 }
 
@@ -523,21 +534,38 @@ fn index(&self, id: FileItemTreeId<N>) -> &N {
     }
 }
 
-/// 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)]
@@ -559,10 +587,11 @@ pub struct ExternBlock {
 pub struct Function {
     pub name: Name,
     pub visibility: RawVisibilityId,
-    pub generic_params: Interned<GenericParams>,
+    pub explicit_generic_params: Interned<GenericParams>,
     pub abi: Option<Interned<str>>,
-    pub params: IdRange<Param>,
+    pub params: IdxRange<Param>,
     pub ret_type: Interned<TypeRef>,
+    pub async_ret_type: Option<Interned<TypeRef>>,
     pub ast_id: FileAstId<ast::Fn>,
     pub(crate) flags: FnFlags,
 }
@@ -613,13 +642,13 @@ pub struct Enum {
     pub name: Name,
     pub visibility: RawVisibilityId,
     pub generic_params: Interned<GenericParams>,
-    pub variants: IdRange<Variant>,
+    pub variants: IdxRange<Variant>,
     pub ast_id: FileAstId<ast::Enum>,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Const {
-    /// const _: () = ();
+    /// `None` for `const _: () = ();`
     pub name: Option<Name>,
     pub visibility: RawVisibilityId,
     pub type_ref: Interned<TypeRef>,
@@ -631,8 +660,6 @@ pub struct Static {
     pub name: Name,
     pub visibility: RawVisibilityId,
     pub mutable: bool,
-    /// Whether the static is in an `extern` block.
-    pub is_extern: bool,
     pub type_ref: Interned<TypeRef>,
     pub ast_id: FileAstId<ast::Static>,
 }
@@ -644,7 +671,6 @@ pub struct Trait {
     pub generic_params: Interned<GenericParams>,
     pub is_auto: bool,
     pub is_unsafe: bool,
-    pub bounds: Box<[TypeBound]>,
     pub items: Box<[AssocItem]>,
     pub ast_id: FileAstId<ast::Trait>,
 }
@@ -664,10 +690,9 @@ pub struct TypeAlias {
     pub name: Name,
     pub visibility: RawVisibilityId,
     /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`.
-    pub bounds: Box<[TypeBound]>,
+    pub bounds: Box<[Interned<TypeBound>]>,
     pub generic_params: Interned<GenericParams>,
     pub type_ref: Option<Interned<TypeRef>>,
-    pub is_extern: bool,
     pub ast_id: FileAstId<ast::TypeAlias>,
 }
 
@@ -693,7 +718,7 @@ pub struct MacroCall {
     /// Path to the called macro.
     pub path: Interned<ModPath>,
     pub ast_id: FileAstId<ast::MacroCall>,
-    pub fragment: FragmentKind,
+    pub expand_to: ExpandTo,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
@@ -711,6 +736,105 @@ 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()
+    }
+}
+
+#[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 {
+    /// Expands the `UseTree` into individually imported `ModPath`s.
+    pub fn expand(
+        &self,
+        mut cb: impl FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
+    ) {
+        self.expand_impl(None, &mut cb)
+    }
+
+    fn expand_impl(
+        &self,
+        prefix: Option<ModPath>,
+        cb: &mut dyn FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
+    ) {
+        fn concat_mod_paths(
+            prefix: Option<ModPath>,
+            path: &ModPath,
+        ) -> Option<(ModPath, ImportKind)> {
+            match (prefix, &path.kind) {
+                (None, _) => Some((path.clone(), ImportKind::Plain)),
+                (Some(mut prefix), PathKind::Plain) => {
+                    for segment in path.segments() {
+                        prefix.push_segment(segment.clone());
+                    }
+                    Some((prefix, ImportKind::Plain))
+                }
+                (Some(prefix), PathKind::Super(0)) => {
+                    // `some::path::self` == `some::path`
+                    if path.segments().is_empty() {
+                        Some((prefix, ImportKind::TypeOnly))
+                    } else {
+                        None
+                    }
+                }
+                (Some(_), _) => None,
+            }
+        }
+
+        match &self.kind {
+            UseTreeKind::Single { path, alias } => {
+                if let Some((path, kind)) = concat_mod_paths(prefix, path) {
+                    cb(self.index, path, kind, alias.clone());
+                }
+            }
+            UseTreeKind::Glob { path: Some(path) } => {
+                if let Some((path, _)) = concat_mod_paths(prefix, path) {
+                    cb(self.index, path, ImportKind::Glob, None);
+                }
+            }
+            UseTreeKind::Glob { path: None } => {
+                if let Some(prefix) = prefix {
+                    cb(self.index, prefix, ImportKind::Glob, None);
+                }
+            }
+            UseTreeKind::Prefixed { prefix: additional_prefix, list } => {
+                let prefix = match additional_prefix {
+                    Some(path) => match concat_mod_paths(prefix, path) {
+                        Some((path, ImportKind::Plain)) => Some(path),
+                        _ => return,
+                    },
+                    None => prefix,
+                };
+                for tree in &**list {
+                    tree.expand_impl(prefix.clone(), cb);
+                }
+            }
+        }
+    }
+}
+
 macro_rules! impl_froms {
     ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
         $(
@@ -803,59 +927,10 @@ pub struct Variant {
     pub fields: Fields,
 }
 
-/// A range of densely allocated ItemTree IDs.
-pub struct IdRange<T> {
-    range: Range<u32>,
-    _p: PhantomData<T>,
-}
-
-impl<T> IdRange<T> {
-    fn new(range: Range<Idx<T>>) -> Self {
-        Self { range: range.start.into_raw().into()..range.end.into_raw().into(), _p: PhantomData }
-    }
-
-    fn is_empty(&self) -> bool {
-        self.range.is_empty()
-    }
-}
-
-impl<T> Iterator for IdRange<T> {
-    type Item = Idx<T>;
-    fn next(&mut self) -> Option<Self::Item> {
-        self.range.next().map(|raw| Idx::from_raw(raw.into()))
-    }
-}
-
-impl<T> DoubleEndedIterator for IdRange<T> {
-    fn next_back(&mut self) -> Option<Self::Item> {
-        self.range.next_back().map(|raw| Idx::from_raw(raw.into()))
-    }
-}
-
-impl<T> fmt::Debug for IdRange<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_tuple(&format!("IdRange::<{}>", type_name::<T>())).field(&self.range).finish()
-    }
-}
-
-impl<T> Clone for IdRange<T> {
-    fn clone(&self) -> Self {
-        Self { range: self.range.clone(), _p: PhantomData }
-    }
-}
-
-impl<T> PartialEq for IdRange<T> {
-    fn eq(&self, other: &Self) -> bool {
-        self.range == other.range
-    }
-}
-
-impl<T> Eq for IdRange<T> {}
-
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum Fields {
-    Record(IdRange<Field>),
-    Tuple(IdRange<Field>),
+    Record(IdxRange<Field>),
+    Tuple(IdxRange<Field>),
     Unit,
 }