Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-expand/src/lib.rs')
| -rw-r--r-- | crates/hir-expand/src/lib.rs | 239 |
1 files changed, 126 insertions, 113 deletions
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 472ec83ffe..7b6a6135b3 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -4,6 +4,8 @@ //! tree originates not from the text of some `FileId`, but from some macro //! expansion. #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +// It's useful to refer to code that is private in doc comments. +#![allow(rustdoc::private_intra_doc_links)] pub use intern; @@ -25,18 +27,19 @@ mod cfg_process; mod fixup; mod prettify_macro_expansion_; -use attrs::collect_attrs; -use rustc_hash::FxHashMap; use salsa::plumbing::{AsId, FromId}; use stdx::TupleExt; +use thin_vec::ThinVec; use triomphe::Arc; use core::fmt; -use std::hash::Hash; +use std::{hash::Hash, ops}; use base_db::Crate; use either::Either; -use span::{Edition, ErasedFileAstId, FileAstId, Span, SpanAnchor, SyntaxContext}; +use span::{ + Edition, ErasedFileAstId, FileAstId, NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER, Span, SyntaxContext, +}; use syntax::{ SyntaxNode, SyntaxToken, TextRange, TextSize, ast::{self, AstNode}, @@ -61,27 +64,9 @@ pub use crate::{ }; pub use base_db::EditionedFileId; -pub use mbe::{DeclarativeMacro, ValueResult}; - -pub mod tt { - pub use span::Span; - pub use tt::{DelimiterKind, IdentIsRaw, LitKind, Spacing, token_to_literal}; - - pub type Delimiter = ::tt::Delimiter<Span>; - pub type DelimSpan = ::tt::DelimSpan<Span>; - pub type Subtree = ::tt::Subtree<Span>; - pub type Leaf = ::tt::Leaf<Span>; - pub type Literal = ::tt::Literal<Span>; - pub type Punct = ::tt::Punct<Span>; - pub type Ident = ::tt::Ident<Span>; - pub type TokenTree = ::tt::TokenTree<Span>; - pub type TopSubtree = ::tt::TopSubtree<Span>; - pub type TopSubtreeBuilder = ::tt::TopSubtreeBuilder<Span>; - pub type TokenTreesView<'a> = ::tt::TokenTreesView<'a, Span>; - pub type SubtreeView<'a> = ::tt::SubtreeView<'a, Span>; - pub type TtElement<'a> = ::tt::iter::TtElement<'a, Span>; - pub type TtIter<'a> = ::tt::iter::TtIter<'a, Span>; -} +pub use mbe::{DeclarativeMacro, MacroCallStyle, MacroCallStyles, ValueResult}; + +pub use tt; #[macro_export] macro_rules! impl_intern_lookup { @@ -266,7 +251,7 @@ pub struct MacroDefId { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum MacroDefKind { - Declarative(AstId<ast::Macro>), + Declarative(AstId<ast::Macro>, MacroCallStyles), BuiltIn(AstId<ast::Macro>, BuiltinFnLikeExpander), BuiltInAttr(AstId<ast::Macro>, BuiltinAttrExpander), BuiltInDerive(AstId<ast::Macro>, BuiltinDeriveExpander), @@ -317,9 +302,6 @@ pub enum MacroCallKind { Derive { ast_id: AstId<ast::Adt>, /// Syntactical index of the invoking `#[derive]` attribute. - /// - /// Outer attributes are counted first, then inner attributes. This does not support - /// out-of-line modules, which may have attributes spread across 2 files! derive_attr_index: AttrId, /// Index of the derive macro in the derive attribute derive_index: u32, @@ -329,17 +311,78 @@ pub enum MacroCallKind { }, Attr { ast_id: AstId<ast::Item>, - // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index` - // but we need to fix the `cfg_attr` handling first. + // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index`. attr_args: Option<Arc<tt::TopSubtree>>, - /// Syntactical index of the invoking `#[attribute]`. + /// This contains the list of all *active* attributes (derives and attr macros) preceding this + /// attribute, including this attribute. You can retrieve the [`AttrId`] of the current attribute + /// by calling [`invoc_attr()`] on this. + /// + /// The macro should not see the attributes here. /// - /// Outer attributes are counted first, then inner attributes. This does not support - /// out-of-line modules, which may have attributes spread across 2 files! - invoc_attr_index: AttrId, + /// [`invoc_attr()`]: AttrMacroAttrIds::invoc_attr + censored_attr_ids: AttrMacroAttrIds, }, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AttrMacroAttrIds(AttrMacroAttrIdsRepr); + +impl AttrMacroAttrIds { + #[inline] + pub fn from_one(id: AttrId) -> Self { + Self(AttrMacroAttrIdsRepr::One(id)) + } + + #[inline] + pub fn from_many(ids: &[AttrId]) -> Self { + if let &[id] = ids { + Self(AttrMacroAttrIdsRepr::One(id)) + } else { + Self(AttrMacroAttrIdsRepr::ManyDerives(ids.iter().copied().collect())) + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum AttrMacroAttrIdsRepr { + One(AttrId), + ManyDerives(ThinVec<AttrId>), +} + +impl ops::Deref for AttrMacroAttrIds { + type Target = [AttrId]; + + #[inline] + fn deref(&self) -> &Self::Target { + match &self.0 { + AttrMacroAttrIdsRepr::One(one) => std::slice::from_ref(one), + AttrMacroAttrIdsRepr::ManyDerives(many) => many, + } + } +} + +impl AttrMacroAttrIds { + #[inline] + pub fn invoc_attr(&self) -> AttrId { + match &self.0 { + AttrMacroAttrIdsRepr::One(it) => *it, + AttrMacroAttrIdsRepr::ManyDerives(it) => { + *it.last().expect("should always have at least one `AttrId`") + } + } + } +} + +impl MacroCallKind { + pub(crate) fn call_style(&self) -> MacroCallStyle { + match self { + MacroCallKind::FnLike { .. } => MacroCallStyle::FnLike, + MacroCallKind::Derive { .. } => MacroCallStyle::Derive, + MacroCallKind::Attr { .. } => MacroCallStyle::Attr, + } + } +} + impl HirFileId { pub fn edition(self, db: &dyn ExpandDatabase) -> Edition { match self { @@ -511,7 +554,7 @@ impl MacroDefId { pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile<TextRange> { match self.kind { - MacroDefKind::Declarative(id) + MacroDefKind::Declarative(id, _) | MacroDefKind::BuiltIn(id, _) | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) @@ -527,7 +570,7 @@ impl MacroDefId { pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> { match self.kind { MacroDefKind::ProcMacro(id, ..) => Either::Right(id), - MacroDefKind::Declarative(id) + MacroDefKind::Declarative(id, _) | MacroDefKind::BuiltIn(id, _) | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) @@ -540,18 +583,22 @@ impl MacroDefId { } pub fn is_attribute(&self) -> bool { - matches!( - self.kind, - MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) - ) + match self.kind { + MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => { + true + } + MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::ATTR), + _ => false, + } } pub fn is_derive(&self) -> bool { - matches!( - self.kind, + match self.kind { MacroDefKind::BuiltInDerive(..) - | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) - ) + | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => true, + MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::DERIVE), + _ => false, + } } pub fn is_fn_like(&self) -> bool { @@ -583,34 +630,20 @@ impl MacroDefId { impl MacroCallLoc { pub fn to_node(&self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode> { - match self.kind { + match &self.kind { MacroCallKind::FnLike { ast_id, .. } => { ast_id.with_value(ast_id.to_node(db).syntax().clone()) } MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // FIXME: handle `cfg_attr` - ast_id.with_value(ast_id.to_node(db)).map(|it| { - collect_attrs(&it) - .nth(derive_attr_index.ast_index()) - .and_then(|it| match it.1 { - Either::Left(attr) => Some(attr.syntax().clone()), - Either::Right(_) => None, - }) - .unwrap_or_else(|| it.syntax().clone()) - }) + let (attr, _, _, _) = derive_attr_index.find_attr_range(db, self.krate, *ast_id); + ast_id.with_value(attr.syntax().clone()) } - MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { + MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { if self.def.is_attribute_derive() { - // FIXME: handle `cfg_attr` - ast_id.with_value(ast_id.to_node(db)).map(|it| { - collect_attrs(&it) - .nth(invoc_attr_index.ast_index()) - .and_then(|it| match it.1 { - Either::Left(attr) => Some(attr.syntax().clone()), - Either::Right(_) => None, - }) - .unwrap_or_else(|| it.syntax().clone()) - }) + let (attr, _, _, _) = + attr_ids.invoc_attr().find_attr_range(db, self.krate, *ast_id); + ast_id.with_value(attr.syntax().clone()) } else { ast_id.with_value(ast_id.to_node(db).syntax().clone()) } @@ -715,7 +748,7 @@ impl MacroCallKind { /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros /// get the macro path (rustc shows the whole `ast::MacroCall`), attribute macros get the /// attribute's range, and derives get only the specific derive that is being referred to. - pub fn original_call_range(self, db: &dyn ExpandDatabase) -> FileRange { + pub fn original_call_range(self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange { let mut kind = self; let file_id = loop { match kind.file_id() { @@ -737,24 +770,11 @@ impl MacroCallKind { } MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // FIXME: should be the range of the macro name, not the whole derive - // FIXME: handle `cfg_attr` - collect_attrs(&ast_id.to_node(db)) - .nth(derive_attr_index.ast_index()) - .expect("missing derive") - .1 - .expect_left("derive is a doc comment?") - .syntax() - .text_range() + derive_attr_index.find_attr_range(db, krate, ast_id).2 } // FIXME: handle `cfg_attr` - MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { - collect_attrs(&ast_id.to_node(db)) - .nth(invoc_attr_index.ast_index()) - .expect("missing attribute") - .1 - .expect_left("attribute macro is a doc comment?") - .syntax() - .text_range() + MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { + attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).2 } }; @@ -818,6 +838,10 @@ impl ExpansionInfo { &self, span: Span, ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> { + if span.anchor.ast_id == NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER { + return None; + } + let tokens = self.exp_map.ranges_with_span_exact(span).flat_map(move |(range, ctx)| { self.expanded.value.covering_element(range).into_token().zip(Some(ctx)) }); @@ -826,13 +850,17 @@ impl ExpansionInfo { } /// Maps the passed in file range down into a macro expansion if it is the input to a macro call. - /// Unlike [`map_range_down_exact`], this will consider spans that contain the given span. + /// Unlike [`ExpansionInfo::map_range_down_exact`], this will consider spans that contain the given span. /// /// Note this does a linear search through the entire backing vector of the spanmap. pub fn map_range_down( &self, span: Span, ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> { + if span.anchor.ast_id == NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER { + return None; + } + let tokens = self.exp_map.ranges_with_span(span).flat_map(move |(range, ctx)| { self.expanded.value.covering_element(range).into_token().zip(Some(ctx)) }); @@ -873,7 +901,8 @@ impl ExpansionInfo { let span = self.exp_map.span_at(token.start()); match &self.arg_map { SpanMap::RealSpanMap(_) => { - let file_id = EditionedFileId::from_span(db, span.anchor.file_id).into(); + let file_id = + EditionedFileId::from_span_guess_origin(db, span.anchor.file_id).into(); let anchor_offset = db.ast_id_map(file_id).get_erased(span.anchor.ast_id).text_range().start(); InFile { file_id, value: smallvec::smallvec![span.range + anchor_offset] } @@ -929,7 +958,7 @@ pub fn map_node_range_up_rooted( start = start.min(span.range.start()); end = end.max(span.range.end()); } - let file_id = EditionedFileId::from_span(db, anchor.file_id); + let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id); let anchor_offset = db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start(); Some(FileRange { file_id, range: TextRange::new(start, end) + anchor_offset }) @@ -955,36 +984,12 @@ pub fn map_node_range_up( start = start.min(span.range.start()); end = end.max(span.range.end()); } - let file_id = EditionedFileId::from_span(db, anchor.file_id); + let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id); let anchor_offset = db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start(); Some((FileRange { file_id, range: TextRange::new(start, end) + anchor_offset }, ctx)) } -/// Maps up the text range out of the expansion hierarchy back into the original file its from. -/// This version will aggregate the ranges of all spans with the same anchor and syntax context. -pub fn map_node_range_up_aggregated( - db: &dyn ExpandDatabase, - exp_map: &ExpansionSpanMap, - range: TextRange, -) -> FxHashMap<(SpanAnchor, SyntaxContext), TextRange> { - let mut map = FxHashMap::default(); - for span in exp_map.spans_for_range(range) { - let range = map.entry((span.anchor, span.ctx)).or_insert_with(|| span.range); - *range = TextRange::new( - range.start().min(span.range.start()), - range.end().max(span.range.end()), - ); - } - for ((anchor, _), range) in &mut map { - let file_id = EditionedFileId::from_span(db, anchor.file_id); - let anchor_offset = - db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start(); - *range += anchor_offset; - } - map -} - /// Looks up the span at the given offset. pub fn span_for_offset( db: &dyn ExpandDatabase, @@ -992,7 +997,7 @@ pub fn span_for_offset( offset: TextSize, ) -> (FileRange, SyntaxContext) { let span = exp_map.span_at(offset); - let file_id = EditionedFileId::from_span(db, span.anchor.file_id); + let file_id = EditionedFileId::from_span_guess_origin(db, span.anchor.file_id); let anchor_offset = db.ast_id_map(file_id.into()).get_erased(span.anchor.ast_id).text_range().start(); (FileRange { file_id, range: span.range + anchor_offset }, span.ctx) @@ -1062,7 +1067,7 @@ impl ExpandTo { } } -intern::impl_internable!(ModPath, attrs::AttrInput); +intern::impl_internable!(ModPath); #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[doc(alias = "MacroFileId")] @@ -1125,6 +1130,14 @@ impl HirFileId { HirFileId::MacroFile(_) => None, } } + + #[inline] + pub fn krate(self, db: &dyn ExpandDatabase) -> Crate { + match self { + HirFileId::FileId(it) => it.krate(db), + HirFileId::MacroFile(it) => it.loc(db).krate, + } + } } impl PartialEq<EditionedFileId> for HirFileId { |