mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 07:04:22 +00:00
Shuffle hir-expand things around
This commit is contained in:
parent
e320004dad
commit
880baa9e56
8 changed files with 295 additions and 258 deletions
|
@ -155,7 +155,14 @@ impl PartialEq for AstIdMap {
|
||||||
impl Eq for AstIdMap {}
|
impl Eq for AstIdMap {}
|
||||||
|
|
||||||
impl AstIdMap {
|
impl AstIdMap {
|
||||||
pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap {
|
pub(crate) fn ast_id_map(
|
||||||
|
db: &dyn ExpandDatabase,
|
||||||
|
file_id: span::HirFileId,
|
||||||
|
) -> triomphe::Arc<AstIdMap> {
|
||||||
|
triomphe::Arc::new(AstIdMap::from_source(&db.parse_or_expand(file_id)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_source(node: &SyntaxNode) -> AstIdMap {
|
||||||
assert!(node.parent().is_none());
|
assert!(node.parent().is_none());
|
||||||
let mut res = AstIdMap::default();
|
let mut res = AstIdMap::default();
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
//! Defines database & queries for macro expansion.
|
//! Defines database & queries for macro expansion.
|
||||||
|
|
||||||
use std::sync::OnceLock;
|
|
||||||
|
|
||||||
use base_db::{
|
use base_db::{
|
||||||
salsa::{self, debug::DebugQueryTable},
|
salsa::{self, debug::DebugQueryTable},
|
||||||
CrateId, Edition, FileId, SourceDatabase, VersionReq,
|
CrateId, FileId, SourceDatabase,
|
||||||
};
|
};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use limit::Limit;
|
use limit::Limit;
|
||||||
use mbe::{syntax_node_to_token_tree, ValueResult};
|
use mbe::{syntax_node_to_token_tree, ValueResult};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use span::{Span, SyntaxContextId};
|
use span::SyntaxContextId;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, HasAttrs},
|
ast::{self, HasAttrs},
|
||||||
AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
|
AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
|
||||||
|
@ -19,13 +17,14 @@ use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast_id_map::AstIdMap,
|
ast_id_map::AstIdMap,
|
||||||
attrs::{collect_attrs, RawAttrs},
|
attrs::collect_attrs,
|
||||||
builtin_attr_macro::pseudo_derive_attr_expansion,
|
builtin_attr_macro::pseudo_derive_attr_expansion,
|
||||||
builtin_fn_macro::EagerExpander,
|
builtin_fn_macro::EagerExpander,
|
||||||
|
declarative::DeclarativeMacroExpander,
|
||||||
fixup::{self, reverse_fixups, SyntaxFixupUndoInfo},
|
fixup::{self, reverse_fixups, SyntaxFixupUndoInfo},
|
||||||
hygiene::{
|
hygiene::{
|
||||||
apply_mark, span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt,
|
span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt,
|
||||||
SyntaxContextData, Transparency,
|
SyntaxContextData,
|
||||||
},
|
},
|
||||||
proc_macro::ProcMacros,
|
proc_macro::ProcMacros,
|
||||||
span_map::{RealSpanMap, SpanMap, SpanMapRef},
|
span_map::{RealSpanMap, SpanMap, SpanMapRef},
|
||||||
|
@ -43,82 +42,6 @@ use crate::{
|
||||||
/// Actual max for `analysis-stats .` at some point: 30672.
|
/// Actual max for `analysis-stats .` at some point: 30672.
|
||||||
static TOKEN_LIMIT: Limit = Limit::new(1_048_576);
|
static TOKEN_LIMIT: Limit = Limit::new(1_048_576);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
||||||
/// Old-style `macro_rules` or the new macros 2.0
|
|
||||||
pub struct DeclarativeMacroExpander {
|
|
||||||
pub mac: mbe::DeclarativeMacro<span::Span>,
|
|
||||||
pub transparency: Transparency,
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Remove this once we drop support for 1.76
|
|
||||||
static REQUIREMENT: OnceLock<VersionReq> = OnceLock::new();
|
|
||||||
|
|
||||||
impl DeclarativeMacroExpander {
|
|
||||||
pub fn expand(
|
|
||||||
&self,
|
|
||||||
db: &dyn ExpandDatabase,
|
|
||||||
tt: tt::Subtree,
|
|
||||||
call_id: MacroCallId,
|
|
||||||
) -> ExpandResult<tt::Subtree> {
|
|
||||||
let loc = db.lookup_intern_macro_call(call_id);
|
|
||||||
let toolchain = &db.crate_graph()[loc.def.krate].toolchain;
|
|
||||||
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
|
|
||||||
REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches(
|
|
||||||
&base_db::Version {
|
|
||||||
pre: base_db::Prerelease::EMPTY,
|
|
||||||
build: base_db::BuildMetadata::EMPTY,
|
|
||||||
major: version.major,
|
|
||||||
minor: version.minor,
|
|
||||||
patch: version.patch,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
});
|
|
||||||
match self.mac.err() {
|
|
||||||
Some(e) => ExpandResult::new(
|
|
||||||
tt::Subtree::empty(tt::DelimSpan { open: loc.call_site, close: loc.call_site }),
|
|
||||||
ExpandError::other(format!("invalid macro definition: {e}")),
|
|
||||||
),
|
|
||||||
None => self
|
|
||||||
.mac
|
|
||||||
.expand(
|
|
||||||
&tt,
|
|
||||||
|s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency),
|
|
||||||
new_meta_vars,
|
|
||||||
loc.call_site,
|
|
||||||
)
|
|
||||||
.map_err(Into::into),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expand_unhygienic(
|
|
||||||
&self,
|
|
||||||
db: &dyn ExpandDatabase,
|
|
||||||
tt: tt::Subtree,
|
|
||||||
krate: CrateId,
|
|
||||||
call_site: Span,
|
|
||||||
) -> ExpandResult<tt::Subtree> {
|
|
||||||
let toolchain = &db.crate_graph()[krate].toolchain;
|
|
||||||
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
|
|
||||||
REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches(
|
|
||||||
&base_db::Version {
|
|
||||||
pre: base_db::Prerelease::EMPTY,
|
|
||||||
build: base_db::BuildMetadata::EMPTY,
|
|
||||||
major: version.major,
|
|
||||||
minor: version.minor,
|
|
||||||
patch: version.patch,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
});
|
|
||||||
match self.mac.err() {
|
|
||||||
Some(e) => ExpandResult::new(
|
|
||||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
|
||||||
ExpandError::other(format!("invalid macro definition: {e}")),
|
|
||||||
),
|
|
||||||
None => self.mac.expand(&tt, |_| (), new_meta_vars, call_site).map_err(Into::into),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum TokenExpander {
|
pub enum TokenExpander {
|
||||||
/// Old-style `macro_rules` or the new macros 2.0
|
/// Old-style `macro_rules` or the new macros 2.0
|
||||||
|
@ -141,6 +64,7 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||||
#[salsa::input]
|
#[salsa::input]
|
||||||
fn proc_macros(&self) -> Arc<ProcMacros>;
|
fn proc_macros(&self) -> Arc<ProcMacros>;
|
||||||
|
|
||||||
|
#[salsa::invoke(AstIdMap::ast_id_map)]
|
||||||
fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
|
fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
|
||||||
|
|
||||||
/// Main public API -- parses a hir file, not caring whether it's a real
|
/// Main public API -- parses a hir file, not caring whether it's a real
|
||||||
|
@ -156,8 +80,10 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||||
macro_file: MacroFileId,
|
macro_file: MacroFileId,
|
||||||
) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)>;
|
) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)>;
|
||||||
#[salsa::transparent]
|
#[salsa::transparent]
|
||||||
|
#[salsa::invoke(SpanMap::new)]
|
||||||
fn span_map(&self, file_id: HirFileId) -> SpanMap;
|
fn span_map(&self, file_id: HirFileId) -> SpanMap;
|
||||||
|
|
||||||
|
#[salsa::invoke(crate::span_map::real_span_map)]
|
||||||
fn real_span_map(&self, file_id: FileId) -> Arc<RealSpanMap>;
|
fn real_span_map(&self, file_id: FileId) -> Arc<RealSpanMap>;
|
||||||
|
|
||||||
/// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
|
/// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
|
||||||
|
@ -173,6 +99,7 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||||
#[salsa::transparent]
|
#[salsa::transparent]
|
||||||
fn setup_syntax_context_root(&self) -> ();
|
fn setup_syntax_context_root(&self) -> ();
|
||||||
#[salsa::transparent]
|
#[salsa::transparent]
|
||||||
|
#[salsa::invoke(crate::hygiene::dump_syntax_contexts)]
|
||||||
fn dump_syntax_contexts(&self) -> String;
|
fn dump_syntax_contexts(&self) -> String;
|
||||||
|
|
||||||
/// Lowers syntactic macro call to a token tree representation. That's a firewall
|
/// Lowers syntactic macro call to a token tree representation. That's a firewall
|
||||||
|
@ -184,8 +111,10 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||||
) -> ValueResult<Option<(Arc<tt::Subtree>, SyntaxFixupUndoInfo)>, Arc<Box<[SyntaxError]>>>;
|
) -> ValueResult<Option<(Arc<tt::Subtree>, SyntaxFixupUndoInfo)>, Arc<Box<[SyntaxError]>>>;
|
||||||
/// Fetches the expander for this macro.
|
/// Fetches the expander for this macro.
|
||||||
#[salsa::transparent]
|
#[salsa::transparent]
|
||||||
|
#[salsa::invoke(TokenExpander::macro_expander)]
|
||||||
fn macro_expander(&self, id: MacroDefId) -> TokenExpander;
|
fn macro_expander(&self, id: MacroDefId) -> TokenExpander;
|
||||||
/// Fetches (and compiles) the expander of this decl macro.
|
/// Fetches (and compiles) the expander of this decl macro.
|
||||||
|
#[salsa::invoke(DeclarativeMacroExpander::expander)]
|
||||||
fn decl_macro_expander(
|
fn decl_macro_expander(
|
||||||
&self,
|
&self,
|
||||||
def_crate: CrateId,
|
def_crate: CrateId,
|
||||||
|
@ -203,36 +132,6 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||||
) -> ExpandResult<Box<[SyntaxError]>>;
|
) -> ExpandResult<Box<[SyntaxError]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn span_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> SpanMap {
|
|
||||||
match file_id.repr() {
|
|
||||||
HirFileIdRepr::FileId(file_id) => SpanMap::RealSpanMap(db.real_span_map(file_id)),
|
|
||||||
HirFileIdRepr::MacroFile(m) => {
|
|
||||||
SpanMap::ExpansionSpanMap(db.parse_macro_expansion(m).value.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn real_span_map(db: &dyn ExpandDatabase, file_id: FileId) -> Arc<RealSpanMap> {
|
|
||||||
use syntax::ast::HasModuleItem;
|
|
||||||
let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)];
|
|
||||||
let ast_id_map = db.ast_id_map(file_id.into());
|
|
||||||
let tree = db.parse(file_id).tree();
|
|
||||||
// FIXME: Descend into modules and other item containing items that are not annotated with attributes
|
|
||||||
// and allocate pairs for those as well. This gives us finer grained span anchors resulting in
|
|
||||||
// better incrementality
|
|
||||||
pairs.extend(
|
|
||||||
tree.items()
|
|
||||||
.map(|item| (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase())),
|
|
||||||
);
|
|
||||||
|
|
||||||
Arc::new(RealSpanMap::from_file(
|
|
||||||
file_id,
|
|
||||||
pairs.into_boxed_slice(),
|
|
||||||
tree.syntax().text_range().end(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This expands the given macro call, but with different arguments. This is
|
/// This expands the given macro call, but with different arguments. This is
|
||||||
/// used for completion, where we want to see what 'would happen' if we insert a
|
/// used for completion, where we want to see what 'would happen' if we insert a
|
||||||
/// token. The `token_to_map` mapped down into the expansion, with the mapped
|
/// token. The `token_to_map` mapped down into the expansion, with the mapped
|
||||||
|
@ -357,10 +256,6 @@ pub fn expand_speculative(
|
||||||
Some((node.syntax_node(), token))
|
Some((node.syntax_node(), token))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
|
|
||||||
Arc::new(AstIdMap::from_source(&db.parse_or_expand(file_id)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
|
fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
|
||||||
match file_id.repr() {
|
match file_id.repr() {
|
||||||
HirFileIdRepr::FileId(file_id) => db.parse(file_id).syntax_node(),
|
HirFileIdRepr::FileId(file_id) => db.parse(file_id).syntax_node(),
|
||||||
|
@ -412,7 +307,10 @@ fn parse_macro_expansion_error(
|
||||||
.map(|it| it.0.errors().to_vec().into_boxed_slice())
|
.map(|it| it.0.errors().to_vec().into_boxed_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_with_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> (Parse<SyntaxNode>, SpanMap) {
|
pub(crate) fn parse_with_map(
|
||||||
|
db: &dyn ExpandDatabase,
|
||||||
|
file_id: HirFileId,
|
||||||
|
) -> (Parse<SyntaxNode>, SpanMap) {
|
||||||
match file_id.repr() {
|
match file_id.repr() {
|
||||||
HirFileIdRepr::FileId(file_id) => {
|
HirFileIdRepr::FileId(file_id) => {
|
||||||
(db.parse(file_id).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id)))
|
(db.parse(file_id).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id)))
|
||||||
|
@ -581,100 +479,18 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decl_macro_expander(
|
impl TokenExpander {
|
||||||
db: &dyn ExpandDatabase,
|
fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander {
|
||||||
def_crate: CrateId,
|
match id.kind {
|
||||||
id: AstId<ast::Macro>,
|
MacroDefKind::Declarative(ast_id) => {
|
||||||
) -> Arc<DeclarativeMacroExpander> {
|
TokenExpander::DeclarativeMacro(db.decl_macro_expander(id.krate, ast_id))
|
||||||
let crate_data = &db.crate_graph()[def_crate];
|
}
|
||||||
let is_2021 = crate_data.edition >= Edition::Edition2021;
|
MacroDefKind::BuiltIn(expander, _) => TokenExpander::BuiltIn(expander),
|
||||||
let (root, map) = parse_with_map(db, id.file_id);
|
MacroDefKind::BuiltInAttr(expander, _) => TokenExpander::BuiltInAttr(expander),
|
||||||
let root = root.syntax_node();
|
MacroDefKind::BuiltInDerive(expander, _) => TokenExpander::BuiltInDerive(expander),
|
||||||
|
MacroDefKind::BuiltInEager(expander, ..) => TokenExpander::BuiltInEager(expander),
|
||||||
let transparency = |node| {
|
MacroDefKind::ProcMacro(expander, ..) => TokenExpander::ProcMacro(expander),
|
||||||
// ... would be nice to have the item tree here
|
|
||||||
let attrs = RawAttrs::new(db, node, map.as_ref()).filter(db, def_crate);
|
|
||||||
match &*attrs
|
|
||||||
.iter()
|
|
||||||
.find(|it| {
|
|
||||||
it.path.as_ident().and_then(|it| it.as_str()) == Some("rustc_macro_transparency")
|
|
||||||
})?
|
|
||||||
.token_tree_value()?
|
|
||||||
.token_trees
|
|
||||||
{
|
|
||||||
[tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &*i.text {
|
|
||||||
"transparent" => Some(Transparency::Transparent),
|
|
||||||
"semitransparent" => Some(Transparency::SemiTransparent),
|
|
||||||
"opaque" => Some(Transparency::Opaque),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
let toolchain = crate_data.toolchain.as_ref();
|
|
||||||
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
|
|
||||||
REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches(
|
|
||||||
&base_db::Version {
|
|
||||||
pre: base_db::Prerelease::EMPTY,
|
|
||||||
build: base_db::BuildMetadata::EMPTY,
|
|
||||||
major: version.major,
|
|
||||||
minor: version.minor,
|
|
||||||
patch: version.patch,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let (mac, transparency) = match id.to_ptr(db).to_node(&root) {
|
|
||||||
ast::Macro::MacroRules(macro_rules) => (
|
|
||||||
match macro_rules.token_tree() {
|
|
||||||
Some(arg) => {
|
|
||||||
let tt = mbe::syntax_node_to_token_tree(
|
|
||||||
arg.syntax(),
|
|
||||||
map.as_ref(),
|
|
||||||
map.span_for_range(macro_rules.macro_rules_token().unwrap().text_range()),
|
|
||||||
);
|
|
||||||
|
|
||||||
mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars)
|
|
||||||
}
|
|
||||||
None => mbe::DeclarativeMacro::from_err(
|
|
||||||
mbe::ParseError::Expected("expected a token tree".into()),
|
|
||||||
is_2021,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent),
|
|
||||||
),
|
|
||||||
ast::Macro::MacroDef(macro_def) => (
|
|
||||||
match macro_def.body() {
|
|
||||||
Some(arg) => {
|
|
||||||
let tt = mbe::syntax_node_to_token_tree(
|
|
||||||
arg.syntax(),
|
|
||||||
map.as_ref(),
|
|
||||||
map.span_for_range(macro_def.macro_token().unwrap().text_range()),
|
|
||||||
);
|
|
||||||
|
|
||||||
mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars)
|
|
||||||
}
|
|
||||||
None => mbe::DeclarativeMacro::from_err(
|
|
||||||
mbe::ParseError::Expected("expected a token tree".into()),
|
|
||||||
is_2021,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
transparency(¯o_def).unwrap_or(Transparency::Opaque),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
Arc::new(DeclarativeMacroExpander { mac, transparency })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander {
|
|
||||||
match id.kind {
|
|
||||||
MacroDefKind::Declarative(ast_id) => {
|
|
||||||
TokenExpander::DeclarativeMacro(db.decl_macro_expander(id.krate, ast_id))
|
|
||||||
}
|
|
||||||
MacroDefKind::BuiltIn(expander, _) => TokenExpander::BuiltIn(expander),
|
|
||||||
MacroDefKind::BuiltInAttr(expander, _) => TokenExpander::BuiltInAttr(expander),
|
|
||||||
MacroDefKind::BuiltInDerive(expander, _) => TokenExpander::BuiltInDerive(expander),
|
|
||||||
MacroDefKind::BuiltInEager(expander, ..) => TokenExpander::BuiltInEager(expander),
|
|
||||||
MacroDefKind::ProcMacro(expander, ..) => TokenExpander::ProcMacro(expander),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -862,40 +678,3 @@ fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> {
|
||||||
fn setup_syntax_context_root(db: &dyn ExpandDatabase) {
|
fn setup_syntax_context_root(db: &dyn ExpandDatabase) {
|
||||||
db.intern_syntax_context(SyntaxContextData::root());
|
db.intern_syntax_context(SyntaxContextData::root());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String {
|
|
||||||
let mut s = String::from("Expansions:");
|
|
||||||
let mut entries = InternMacroCallLookupQuery.in_db(db).entries::<Vec<_>>();
|
|
||||||
entries.sort_by_key(|e| e.key);
|
|
||||||
for e in entries {
|
|
||||||
let id = e.key;
|
|
||||||
let expn_data = e.value.as_ref().unwrap();
|
|
||||||
s.push_str(&format!(
|
|
||||||
"\n{:?}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}",
|
|
||||||
id,
|
|
||||||
expn_data.kind.file_id(),
|
|
||||||
expn_data.call_site,
|
|
||||||
SyntaxContextId::ROOT, // FIXME expn_data.def_site,
|
|
||||||
expn_data.kind.descr(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
s.push_str("\n\nSyntaxContexts:\n");
|
|
||||||
let mut entries = InternSyntaxContextLookupQuery.in_db(db).entries::<Vec<_>>();
|
|
||||||
entries.sort_by_key(|e| e.key);
|
|
||||||
for e in entries {
|
|
||||||
struct SyntaxContextDebug<'a>(
|
|
||||||
&'a dyn ExpandDatabase,
|
|
||||||
SyntaxContextId,
|
|
||||||
&'a SyntaxContextData,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<'a> std::fmt::Debug for SyntaxContextDebug<'a> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.2.fancy_debug(self.1, self.0, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stdx::format_to!(s, "{:?}\n", SyntaxContextDebug(db, e.key, &e.value.unwrap()));
|
|
||||||
}
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
176
crates/hir-expand/src/declarative.rs
Normal file
176
crates/hir-expand/src/declarative.rs
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use base_db::{CrateId, Edition, VersionReq};
|
||||||
|
use span::{MacroCallId, Span};
|
||||||
|
use syntax::{ast, AstNode};
|
||||||
|
use triomphe::Arc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
attrs::RawAttrs,
|
||||||
|
db::ExpandDatabase,
|
||||||
|
hygiene::{apply_mark, Transparency},
|
||||||
|
tt, AstId, ExpandError, ExpandResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Old-style `macro_rules` or the new macros 2.0
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub struct DeclarativeMacroExpander {
|
||||||
|
pub mac: mbe::DeclarativeMacro<span::Span>,
|
||||||
|
pub transparency: Transparency,
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Remove this once we drop support for 1.76
|
||||||
|
static REQUIREMENT: OnceLock<VersionReq> = OnceLock::new();
|
||||||
|
|
||||||
|
impl DeclarativeMacroExpander {
|
||||||
|
pub fn expand(
|
||||||
|
&self,
|
||||||
|
db: &dyn ExpandDatabase,
|
||||||
|
tt: tt::Subtree,
|
||||||
|
call_id: MacroCallId,
|
||||||
|
) -> ExpandResult<tt::Subtree> {
|
||||||
|
let loc = db.lookup_intern_macro_call(call_id);
|
||||||
|
let toolchain = &db.crate_graph()[loc.def.krate].toolchain;
|
||||||
|
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
|
||||||
|
REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches(
|
||||||
|
&base_db::Version {
|
||||||
|
pre: base_db::Prerelease::EMPTY,
|
||||||
|
build: base_db::BuildMetadata::EMPTY,
|
||||||
|
major: version.major,
|
||||||
|
minor: version.minor,
|
||||||
|
patch: version.patch,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
});
|
||||||
|
match self.mac.err() {
|
||||||
|
Some(e) => ExpandResult::new(
|
||||||
|
tt::Subtree::empty(tt::DelimSpan { open: loc.call_site, close: loc.call_site }),
|
||||||
|
ExpandError::other(format!("invalid macro definition: {e}")),
|
||||||
|
),
|
||||||
|
None => self
|
||||||
|
.mac
|
||||||
|
.expand(
|
||||||
|
&tt,
|
||||||
|
|s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency),
|
||||||
|
new_meta_vars,
|
||||||
|
loc.call_site,
|
||||||
|
)
|
||||||
|
.map_err(Into::into),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand_unhygienic(
|
||||||
|
&self,
|
||||||
|
db: &dyn ExpandDatabase,
|
||||||
|
tt: tt::Subtree,
|
||||||
|
krate: CrateId,
|
||||||
|
call_site: Span,
|
||||||
|
) -> ExpandResult<tt::Subtree> {
|
||||||
|
let toolchain = &db.crate_graph()[krate].toolchain;
|
||||||
|
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
|
||||||
|
REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches(
|
||||||
|
&base_db::Version {
|
||||||
|
pre: base_db::Prerelease::EMPTY,
|
||||||
|
build: base_db::BuildMetadata::EMPTY,
|
||||||
|
major: version.major,
|
||||||
|
minor: version.minor,
|
||||||
|
patch: version.patch,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
});
|
||||||
|
match self.mac.err() {
|
||||||
|
Some(e) => ExpandResult::new(
|
||||||
|
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||||
|
ExpandError::other(format!("invalid macro definition: {e}")),
|
||||||
|
),
|
||||||
|
None => self.mac.expand(&tt, |_| (), new_meta_vars, call_site).map_err(Into::into),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn expander(
|
||||||
|
db: &dyn ExpandDatabase,
|
||||||
|
def_crate: CrateId,
|
||||||
|
id: AstId<ast::Macro>,
|
||||||
|
) -> Arc<DeclarativeMacroExpander> {
|
||||||
|
let crate_data = &db.crate_graph()[def_crate];
|
||||||
|
let is_2021 = crate_data.edition >= Edition::Edition2021;
|
||||||
|
let (root, map) = crate::db::parse_with_map(db, id.file_id);
|
||||||
|
let root = root.syntax_node();
|
||||||
|
|
||||||
|
let transparency = |node| {
|
||||||
|
// ... would be nice to have the item tree here
|
||||||
|
let attrs = RawAttrs::new(db, node, map.as_ref()).filter(db, def_crate);
|
||||||
|
match &*attrs
|
||||||
|
.iter()
|
||||||
|
.find(|it| {
|
||||||
|
it.path.as_ident().and_then(|it| it.as_str())
|
||||||
|
== Some("rustc_macro_transparency")
|
||||||
|
})?
|
||||||
|
.token_tree_value()?
|
||||||
|
.token_trees
|
||||||
|
{
|
||||||
|
[tt::TokenTree::Leaf(tt::Leaf::Ident(i)), ..] => match &*i.text {
|
||||||
|
"transparent" => Some(Transparency::Transparent),
|
||||||
|
"semitransparent" => Some(Transparency::SemiTransparent),
|
||||||
|
"opaque" => Some(Transparency::Opaque),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let toolchain = crate_data.toolchain.as_ref();
|
||||||
|
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
|
||||||
|
REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches(
|
||||||
|
&base_db::Version {
|
||||||
|
pre: base_db::Prerelease::EMPTY,
|
||||||
|
build: base_db::BuildMetadata::EMPTY,
|
||||||
|
major: version.major,
|
||||||
|
minor: version.minor,
|
||||||
|
patch: version.patch,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let (mac, transparency) = match id.to_ptr(db).to_node(&root) {
|
||||||
|
ast::Macro::MacroRules(macro_rules) => (
|
||||||
|
match macro_rules.token_tree() {
|
||||||
|
Some(arg) => {
|
||||||
|
let tt = mbe::syntax_node_to_token_tree(
|
||||||
|
arg.syntax(),
|
||||||
|
map.as_ref(),
|
||||||
|
map.span_for_range(
|
||||||
|
macro_rules.macro_rules_token().unwrap().text_range(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars)
|
||||||
|
}
|
||||||
|
None => mbe::DeclarativeMacro::from_err(
|
||||||
|
mbe::ParseError::Expected("expected a token tree".into()),
|
||||||
|
is_2021,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent),
|
||||||
|
),
|
||||||
|
ast::Macro::MacroDef(macro_def) => (
|
||||||
|
match macro_def.body() {
|
||||||
|
Some(arg) => {
|
||||||
|
let tt = mbe::syntax_node_to_token_tree(
|
||||||
|
arg.syntax(),
|
||||||
|
map.as_ref(),
|
||||||
|
map.span_for_range(macro_def.macro_token().unwrap().text_range()),
|
||||||
|
);
|
||||||
|
|
||||||
|
mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars)
|
||||||
|
}
|
||||||
|
None => mbe::DeclarativeMacro::from_err(
|
||||||
|
mbe::ParseError::Expected("expected a token tree".into()),
|
||||||
|
is_2021,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
transparency(¯o_def).unwrap_or(Transparency::Opaque),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
Arc::new(DeclarativeMacroExpander { mac, transparency })
|
||||||
|
}
|
||||||
|
}
|
|
@ -245,3 +245,43 @@ pub fn marks_rev(
|
||||||
})
|
})
|
||||||
.map(|ctx| ctx.outer_mark(db))
|
.map(|ctx| ctx.outer_mark(db))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String {
|
||||||
|
use crate::db::{InternMacroCallLookupQuery, InternSyntaxContextLookupQuery};
|
||||||
|
use base_db::salsa::debug::DebugQueryTable;
|
||||||
|
|
||||||
|
let mut s = String::from("Expansions:");
|
||||||
|
let mut entries = InternMacroCallLookupQuery.in_db(db).entries::<Vec<_>>();
|
||||||
|
entries.sort_by_key(|e| e.key);
|
||||||
|
for e in entries {
|
||||||
|
let id = e.key;
|
||||||
|
let expn_data = e.value.as_ref().unwrap();
|
||||||
|
s.push_str(&format!(
|
||||||
|
"\n{:?}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}",
|
||||||
|
id,
|
||||||
|
expn_data.kind.file_id(),
|
||||||
|
expn_data.call_site,
|
||||||
|
SyntaxContextId::ROOT, // FIXME expn_data.def_site,
|
||||||
|
expn_data.kind.descr(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
s.push_str("\n\nSyntaxContexts:\n");
|
||||||
|
let mut entries = InternSyntaxContextLookupQuery.in_db(db).entries::<Vec<_>>();
|
||||||
|
entries.sort_by_key(|e| e.key);
|
||||||
|
for e in entries {
|
||||||
|
struct SyntaxContextDebug<'a>(
|
||||||
|
&'a dyn ExpandDatabase,
|
||||||
|
SyntaxContextId,
|
||||||
|
&'a SyntaxContextData,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<'a> std::fmt::Debug for SyntaxContextDebug<'a> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.2.fancy_debug(self.1, self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stdx::format_to!(s, "{:?}\n", SyntaxContextDebug(db, e.key, &e.value.unwrap()));
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
|
@ -11,16 +11,18 @@ pub mod attrs;
|
||||||
pub mod builtin_attr_macro;
|
pub mod builtin_attr_macro;
|
||||||
pub mod builtin_derive_macro;
|
pub mod builtin_derive_macro;
|
||||||
pub mod builtin_fn_macro;
|
pub mod builtin_fn_macro;
|
||||||
|
pub mod change;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
|
pub mod declarative;
|
||||||
pub mod eager;
|
pub mod eager;
|
||||||
pub mod files;
|
pub mod files;
|
||||||
pub mod change;
|
|
||||||
pub mod hygiene;
|
pub mod hygiene;
|
||||||
pub mod mod_path;
|
pub mod mod_path;
|
||||||
pub mod name;
|
pub mod name;
|
||||||
pub mod proc_macro;
|
pub mod proc_macro;
|
||||||
pub mod quote;
|
pub mod quote;
|
||||||
pub mod span_map;
|
pub mod span_map;
|
||||||
|
|
||||||
mod fixup;
|
mod fixup;
|
||||||
|
|
||||||
use attrs::collect_attrs;
|
use attrs::collect_attrs;
|
||||||
|
@ -167,7 +169,8 @@ pub struct MacroCallLoc {
|
||||||
pub krate: CrateId,
|
pub krate: CrateId,
|
||||||
/// Some if this is a macro call for an eager macro. Note that this is `None`
|
/// Some if this is a macro call for an eager macro. Note that this is `None`
|
||||||
/// for the eager input macro file.
|
/// for the eager input macro file.
|
||||||
// FIXME: This seems bad to save in an interned structure
|
// FIXME: This is being interned, subtrees can vary quickly differ just slightly causing
|
||||||
|
// leakage problems here
|
||||||
eager: Option<Arc<EagerCallInfo>>,
|
eager: Option<Arc<EagerCallInfo>>,
|
||||||
pub kind: MacroCallKind,
|
pub kind: MacroCallKind,
|
||||||
pub call_site: Span,
|
pub call_site: Span,
|
||||||
|
@ -220,7 +223,7 @@ pub enum MacroCallKind {
|
||||||
},
|
},
|
||||||
Attr {
|
Attr {
|
||||||
ast_id: AstId<ast::Item>,
|
ast_id: AstId<ast::Item>,
|
||||||
// FIXME: This is being interned, subtrees can very quickly differ just slightly causing
|
// FIXME: This is being interned, subtrees can vary quickly differ just slightly causing
|
||||||
// leakage problems here
|
// leakage problems here
|
||||||
attr_args: Option<Arc<tt::Subtree>>,
|
attr_args: Option<Arc<tt::Subtree>>,
|
||||||
/// Syntactical index of the invoking `#[attribute]`.
|
/// Syntactical index of the invoking `#[attribute]`.
|
||||||
|
|
|
@ -40,7 +40,7 @@ pub enum PathKind {
|
||||||
Crate,
|
Crate,
|
||||||
/// Absolute path (::foo)
|
/// Absolute path (::foo)
|
||||||
Abs,
|
Abs,
|
||||||
// FIXME: Remove this
|
// FIXME: Can we remove this somehow?
|
||||||
/// `$crate` from macro expansion
|
/// `$crate` from macro expansion
|
||||||
DollarCrate(CrateId),
|
DollarCrate(CrateId),
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,8 @@ impl ModPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
|
pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
|
||||||
let segments = segments.into_iter().collect();
|
let mut segments: SmallVec<_> = segments.into_iter().collect();
|
||||||
|
segments.shrink_to_fit();
|
||||||
ModPath { kind, segments }
|
ModPath { kind, segments }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
//! Span maps for real files and macro expansions.
|
//! Span maps for real files and macro expansions.
|
||||||
use span::Span;
|
use span::{FileId, HirFileId, HirFileIdRepr, Span};
|
||||||
use syntax::TextRange;
|
use syntax::{AstNode, TextRange};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
pub use span::RealSpanMap;
|
pub use span::RealSpanMap;
|
||||||
|
|
||||||
|
use crate::db::ExpandDatabase;
|
||||||
|
|
||||||
pub type ExpansionSpanMap = span::SpanMap<Span>;
|
pub type ExpansionSpanMap = span::SpanMap<Span>;
|
||||||
|
|
||||||
/// Spanmap for a macro file or a real file
|
/// Spanmap for a macro file or a real file
|
||||||
|
@ -34,7 +36,6 @@ impl mbe::SpanMapper<Span> for SpanMapRef<'_> {
|
||||||
self.span_for_range(range)
|
self.span_for_range(range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpanMap {
|
impl SpanMap {
|
||||||
pub fn span_for_range(&self, range: TextRange) -> Span {
|
pub fn span_for_range(&self, range: TextRange) -> Span {
|
||||||
match self {
|
match self {
|
||||||
|
@ -53,6 +54,16 @@ impl SpanMap {
|
||||||
Self::RealSpanMap(span_map) => SpanMapRef::RealSpanMap(span_map),
|
Self::RealSpanMap(span_map) => SpanMapRef::RealSpanMap(span_map),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn new(db: &dyn ExpandDatabase, file_id: HirFileId) -> SpanMap {
|
||||||
|
match file_id.repr() {
|
||||||
|
HirFileIdRepr::FileId(file_id) => SpanMap::RealSpanMap(db.real_span_map(file_id)),
|
||||||
|
HirFileIdRepr::MacroFile(m) => {
|
||||||
|
SpanMap::ExpansionSpanMap(db.parse_macro_expansion(m).value.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpanMapRef<'_> {
|
impl SpanMapRef<'_> {
|
||||||
|
@ -63,3 +74,23 @@ impl SpanMapRef<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn real_span_map(db: &dyn ExpandDatabase, file_id: FileId) -> Arc<RealSpanMap> {
|
||||||
|
use syntax::ast::HasModuleItem;
|
||||||
|
let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)];
|
||||||
|
let ast_id_map = db.ast_id_map(file_id.into());
|
||||||
|
let tree = db.parse(file_id).tree();
|
||||||
|
// FIXME: Descend into modules and other item containing items that are not annotated with attributes
|
||||||
|
// and allocate pairs for those as well. This gives us finer grained span anchors resulting in
|
||||||
|
// better incrementality
|
||||||
|
pairs.extend(
|
||||||
|
tree.items()
|
||||||
|
.map(|item| (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase())),
|
||||||
|
);
|
||||||
|
|
||||||
|
Arc::new(RealSpanMap::from_file(
|
||||||
|
file_id,
|
||||||
|
pairs.into_boxed_slice(),
|
||||||
|
tree.syntax().text_range().end(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
reorder_modules = false
|
reorder_modules = true
|
||||||
use_small_heuristics = "Max"
|
use_small_heuristics = "Max"
|
||||||
|
|
Loading…
Reference in a new issue