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;
path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
visibility::RawVisibility,
+ BlockId,
};
#[derive(Copy, Clone, Eq, PartialEq)]
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);
// 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);
},
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 {
macro_rules,
macro_defs,
vis,
- inner_items,
} = &mut **data;
imports.shrink_to_fit();
macro_defs.shrink_to_fit();
vis.arena.shrink_to_fit();
-
- inner_items.shrink_to_fit();
}
}
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)
}
macro_defs: Arena<MacroDef>,
vis: ItemVisibilities,
-
- inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>,
}
#[derive(Debug, Eq, PartialEq, Hash)]
}
}
+/// 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)
}
}
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
}
}
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);
}
}
}
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,
}
}
}
}
-/// 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)]
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,
}
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>,
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>,
}
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>,
}
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>,
}
/// 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)]
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)),* $(,)? }) => {
$(
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,
}