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.rs139
1 files changed, 119 insertions, 20 deletions
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 42dc8c12d6..5d4f7dc146 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -22,16 +22,19 @@ pub mod proc_macro;
pub mod quote;
pub mod span_map;
+mod cfg_process;
mod fixup;
-
use attrs::collect_attrs;
+use rustc_hash::FxHashMap;
use triomphe::Arc;
use std::{fmt, hash::Hash};
use base_db::{salsa::impl_intern_value_trivial, CrateId, Edition, FileId};
use either::Either;
-use span::{ErasedFileAstId, FileRange, HirFileIdRepr, Span, SyntaxContextData, SyntaxContextId};
+use span::{
+ ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, SyntaxContextId,
+};
use syntax::{
ast::{self, AstNode},
SyntaxNode, SyntaxToken, TextRange, TextSize,
@@ -167,13 +170,8 @@ impl fmt::Display for ExpandError {
pub struct MacroCallLoc {
pub def: MacroDefId,
pub krate: CrateId,
- /// Some if this is a macro call for an eager macro. Note that this is `None`
- /// for the eager input macro file.
- // FIXME: This is being interned, subtrees can vary quickly differ just slightly causing
- // leakage problems here
- eager: Option<Arc<EagerCallInfo>>,
pub kind: MacroCallKind,
- pub call_site: Span,
+ pub ctxt: SyntaxContextId,
}
impl_intern_value_trivial!(MacroCallLoc);
@@ -184,7 +182,6 @@ pub struct MacroDefId {
pub kind: MacroDefKind,
pub local_inner: bool,
pub allow_internal_unsafe: bool,
- pub span: Span,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -204,6 +201,8 @@ pub struct EagerCallInfo {
/// Call id of the eager macro's input file (this is the macro file for its fully expanded input).
arg_id: MacroCallId,
error: Option<ExpandError>,
+ /// TODO: Doc
+ span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -211,6 +210,11 @@ pub enum MacroCallKind {
FnLike {
ast_id: AstId<ast::MacroCall>,
expand_to: ExpandTo,
+ /// Some if this is a macro call for an eager macro. Note that this is `None`
+ /// for the eager input macro file.
+ // FIXME: This is being interned, subtrees can vary quickly differ just slightly causing
+ // leakage problems here
+ eager: Option<Arc<EagerCallInfo>>,
},
Derive {
ast_id: AstId<ast::Adt>,
@@ -272,7 +276,7 @@ impl HirFileIdExt for HirFileId {
HirFileIdRepr::MacroFile(file) => {
let loc = db.lookup_intern_macro_call(file.macro_call_id);
if loc.def.is_include() {
- if let Some(eager) = &loc.eager {
+ if let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind {
if let Ok(it) = builtin_fn_macro::include_input_to_file_id(
db,
file.macro_call_id,
@@ -319,6 +323,9 @@ impl HirFileIdExt for HirFileId {
}
pub trait MacroFileIdExt {
+ fn is_env_or_option_env(&self, db: &dyn ExpandDatabase) -> bool;
+ fn is_include_like_macro(&self, db: &dyn ExpandDatabase) -> bool;
+ fn eager_arg(&self, db: &dyn ExpandDatabase) -> Option<MacroCallId>;
fn expansion_level(self, db: &dyn ExpandDatabase) -> u32;
/// If this is a macro call, returns the syntax node of the call.
fn call_node(self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode>;
@@ -385,31 +392,47 @@ impl MacroFileIdExt for MacroFileId {
db.lookup_intern_macro_call(self.macro_call_id).def.is_include()
}
+ fn is_include_like_macro(&self, db: &dyn ExpandDatabase) -> bool {
+ db.lookup_intern_macro_call(self.macro_call_id).def.is_include_like()
+ }
+
+ fn is_env_or_option_env(&self, db: &dyn ExpandDatabase) -> bool {
+ db.lookup_intern_macro_call(self.macro_call_id).def.is_env_or_option_env()
+ }
+
fn is_eager(&self, db: &dyn ExpandDatabase) -> bool {
- let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id);
+ let loc = db.lookup_intern_macro_call(self.macro_call_id);
matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
}
+ fn eager_arg(&self, db: &dyn ExpandDatabase) -> Option<MacroCallId> {
+ let loc = db.lookup_intern_macro_call(self.macro_call_id);
+ match &loc.kind {
+ MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id),
+ _ => None,
+ }
+ }
+
fn is_attr_macro(&self, db: &dyn ExpandDatabase) -> bool {
- let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id);
+ let loc = db.lookup_intern_macro_call(self.macro_call_id);
matches!(loc.kind, MacroCallKind::Attr { .. })
}
fn is_derive_attr_pseudo_expansion(&self, db: &dyn ExpandDatabase) -> bool {
- let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id);
+ let loc = db.lookup_intern_macro_call(self.macro_call_id);
loc.def.is_attribute_derive()
}
}
impl MacroDefId {
- pub fn as_lazy_macro(
+ pub fn make_call(
self,
db: &dyn ExpandDatabase,
krate: CrateId,
kind: MacroCallKind,
- call_site: Span,
+ ctxt: SyntaxContextId,
) -> MacroCallId {
- MacroCallLoc { def: self, krate, eager: None, kind, call_site }.intern(db)
+ MacroCallLoc { def: self, krate, kind, ctxt }.intern(db)
}
pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile<TextRange> {
@@ -474,6 +497,14 @@ impl MacroDefId {
pub fn is_include(&self) -> bool {
matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include())
}
+
+ pub fn is_include_like(&self) -> bool {
+ matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include_like())
+ }
+
+ pub fn is_env_or_option_env(&self) -> bool {
+ matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_env_or_option_env())
+ }
}
impl MacroCallLoc {
@@ -531,7 +562,7 @@ impl MacroCallLoc {
macro_call_id: MacroCallId,
) -> Option<FileId> {
if self.def.is_include() {
- if let Some(eager) = &self.eager {
+ if let MacroCallKind::FnLike { eager: Some(eager), .. } = &self.kind {
if let Ok(it) =
builtin_fn_macro::include_input_to_file_id(db, macro_call_id, &eager.arg)
{
@@ -655,7 +686,7 @@ impl MacroCallKind {
/// ExpansionInfo mainly describes how to map text range between src and expanded macro
// FIXME: can be expensive to create, we should check the use sites and maybe replace them with
// simpler function calls if the map is only used once
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ExpansionInfo {
pub expanded: InMacroFile<SyntaxNode>,
/// The argument TokenTree or item for attributes
@@ -683,6 +714,24 @@ impl ExpansionInfo {
}
/// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
+ ///
+ /// Note this does a linear search through the entire backing vector of the spanmap.
+ pub fn map_range_down_exact(
+ &self,
+ span: Span,
+ ) -> Option<InMacroFile<impl Iterator<Item = SyntaxToken> + '_>> {
+ let tokens = self
+ .exp_map
+ .ranges_with_span_exact(span)
+ .flat_map(move |range| self.expanded.value.covering_element(range).into_token());
+
+ Some(InMacroFile::new(self.expanded.file_id, tokens))
+ }
+
+ /// 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.
+ ///
+ /// Note this does a linear search through the entire backing vector of the spanmap.
pub fn map_range_down(
&self,
span: Span,
@@ -739,7 +788,7 @@ impl ExpansionInfo {
InFile::new(
self.arg.file_id,
arg_map
- .ranges_with_span(span)
+ .ranges_with_span_exact(span)
.filter(|range| range.intersect(arg_range).is_some())
.collect(),
)
@@ -757,7 +806,7 @@ impl ExpansionInfo {
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
- let (macro_arg, _) = db.macro_arg(macro_file.macro_call_id).value;
+ let (macro_arg, _, _) = db.macro_arg(macro_file.macro_call_id);
let def = loc.def.ast_id().left().and_then(|id| {
let def_tt = match id.to_node(db) {
@@ -793,7 +842,34 @@ impl ExpansionInfo {
}
}
+/// Maps up the text range out of the expansion hierarchy back into the original file its from only
+/// considering the root spans contained.
+/// Unlike [`map_node_range_up`], this will not return `None` if any anchors or syntax contexts differ.
+pub fn map_node_range_up_rooted(
+ db: &dyn ExpandDatabase,
+ exp_map: &ExpansionSpanMap,
+ range: TextRange,
+) -> Option<FileRange> {
+ let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root());
+ let Span { range, anchor, ctx: _ } = spans.next()?;
+ let mut start = range.start();
+ let mut end = range.end();
+
+ for span in spans {
+ if span.anchor != anchor {
+ return None;
+ }
+ start = start.min(span.range.start());
+ end = end.max(span.range.end());
+ }
+ let anchor_offset =
+ db.ast_id_map(anchor.file_id.into()).get_erased(anchor.ast_id).text_range().start();
+ Some(FileRange { file_id: anchor.file_id, range: TextRange::new(start, end) + anchor_offset })
+}
+
/// Maps up the text range out of the expansion hierarchy back into the original file its from.
+///
+/// this will return `None` if any anchors or syntax contexts differ.
pub fn map_node_range_up(
db: &dyn ExpandDatabase,
exp_map: &ExpansionSpanMap,
@@ -819,6 +895,29 @@ pub fn map_node_range_up(
))
}
+/// 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, SyntaxContextId), 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 anchor_offset =
+ db.ast_id_map(anchor.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,