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 | 139 |
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, |