Skip to content

Commit 83f9bee

Browse files
committed
Fix eager token mapping panics
1 parent ff15634 commit 83f9bee

File tree

5 files changed

+96
-51
lines changed

5 files changed

+96
-51
lines changed

crates/hir-expand/src/db.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ fn macro_arg(
308308
) -> Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>> {
309309
let loc = db.lookup_intern_macro_call(id);
310310

311-
if let Some(EagerCallInfo { arg, arg_id: Some(_), error: _ }) = loc.eager.as_deref() {
311+
if let Some(EagerCallInfo { arg, arg_id: _, error: _ }) = loc.eager.as_deref() {
312312
return Some(Arc::new((arg.0.clone(), arg.1.clone(), Default::default())));
313313
}
314314

@@ -371,7 +371,7 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
371371

372372
fn macro_arg_text(db: &dyn ExpandDatabase, id: MacroCallId) -> Option<GreenNode> {
373373
let loc = db.lookup_intern_macro_call(id);
374-
let arg = loc.kind.arg(db)?;
374+
let arg = loc.kind.arg(db)?.value;
375375
if matches!(loc.kind, MacroCallKind::FnLike { .. }) {
376376
let first = arg.first_child_or_token().map_or(T![.], |it| it.kind());
377377
let last = arg.last_child_or_token().map_or(T![.], |it| it.kind());
@@ -446,8 +446,14 @@ fn macro_def(
446446
fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
447447
let _p = profile::span("macro_expand");
448448
let loc = db.lookup_intern_macro_call(id);
449+
450+
// This might look a bit odd, but we do not expand the inputs to eager macros here.
451+
// Eager macros inputs are expanded, well, eagerly when we collect the macro calls.
452+
// That kind of expansion uses the ast id map of an eager macros input though which goes through
453+
// the HirFileId machinery. As eager macro inputs are assigned a macro file id that query
454+
// will end up going through here again, whereas we want to just want to inspect the raw input.
455+
// As such we just return the input subtree here.
449456
if let Some(EagerCallInfo { arg, arg_id: None, error }) = loc.eager.as_deref() {
450-
// This is an input expansion for an eager macro. These are already pre-expanded
451457
return ExpandResult { value: Arc::new(arg.0.clone()), err: error.clone() };
452458
}
453459

crates/hir-expand/src/eager.rs

+27-17
Original file line numberDiff line numberDiff line change
@@ -67,33 +67,43 @@ pub fn expand_eager_macro_input(
6767
})),
6868
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
6969
});
70-
let arg_as_expr = match db.macro_arg_text(arg_id) {
71-
Some(it) => it,
72-
None => {
73-
return Ok(ExpandResult {
74-
value: None,
75-
err: Some(ExpandError::other("invalid token tree")),
76-
})
77-
}
70+
71+
let ExpandResult { value: expanded_eager_input, err } = {
72+
let arg_as_expr = match db.macro_arg_text(arg_id) {
73+
Some(it) => it,
74+
None => {
75+
return Ok(ExpandResult {
76+
value: None,
77+
err: Some(ExpandError::other("invalid token tree")),
78+
})
79+
}
80+
};
81+
82+
eager_macro_recur(
83+
db,
84+
&Hygiene::new(db, macro_call.file_id),
85+
InFile::new(arg_id.as_file(), SyntaxNode::new_root(arg_as_expr)),
86+
krate,
87+
resolver,
88+
)?
7889
};
79-
let ExpandResult { value: expanded_eager_input, err } = eager_macro_recur(
80-
db,
81-
&Hygiene::new(db, macro_call.file_id),
82-
InFile::new(arg_id.as_file(), SyntaxNode::new_root(arg_as_expr)),
83-
krate,
84-
resolver,
85-
)?;
90+
8691
let Some(expanded_eager_input) = expanded_eager_input else {
8792
return Ok(ExpandResult { value: None, err });
8893
};
89-
let (mut subtree, token_map) = mbe::syntax_node_to_token_tree(&expanded_eager_input);
94+
// FIXME: This token map is pointless, it points into the expanded eager syntax node, but that
95+
// node doesn't exist outside this function so we can't use this tokenmap.
96+
// Ideally we'd need to patch the tokenmap of the pre-expanded input and then put that here
97+
// or even better, forego expanding into a SyntaxNode altogether and instead construct a subtree
98+
// in place! But that is kind of difficult.
99+
let (mut subtree, _token_map) = mbe::syntax_node_to_token_tree(&expanded_eager_input);
90100
subtree.delimiter = crate::tt::Delimiter::unspecified();
91101

92102
let loc = MacroCallLoc {
93103
def,
94104
krate,
95105
eager: Some(Box::new(EagerCallInfo {
96-
arg: Arc::new((subtree, token_map)),
106+
arg: Arc::new((subtree, Default::default())),
97107
arg_id: Some(arg_id),
98108
error: err.clone(),
99109
})),

crates/hir-expand/src/hygiene.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -149,16 +149,12 @@ impl HygieneInfo {
149149
token_id = unshifted;
150150
(&attr_args.1, self.attr_input_or_mac_def_start?)
151151
}
152-
None => (
153-
&self.macro_arg.1,
154-
InFile::new(loc.kind.file_id(), loc.kind.arg(db)?.text_range().start()),
155-
),
152+
None => (&self.macro_arg.1, loc.kind.arg(db)?.map(|it| it.text_range().start())),
156153
},
157154
_ => match origin {
158-
mbe::Origin::Call => (
159-
&self.macro_arg.1,
160-
InFile::new(loc.kind.file_id(), loc.kind.arg(db)?.text_range().start()),
161-
),
155+
mbe::Origin::Call => {
156+
(&self.macro_arg.1, loc.kind.arg(db)?.map(|it| it.text_range().start()))
157+
}
162158
mbe::Origin::Def => match (&*self.macro_def, &self.attr_input_or_mac_def_start) {
163159
(TokenExpander::DeclarativeMacro { def_site_token_map, .. }, Some(tt)) => {
164160
(def_site_token_map, *tt)

crates/hir-expand/src/lib.rs

+55-22
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,9 @@ pub enum MacroDefKind {
154154
struct EagerCallInfo {
155155
/// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
156156
arg: Arc<(tt::Subtree, TokenMap)>,
157-
/// call id of the eager macro's input file. If this is none, macro call containing this call info
158-
/// is an eager macro's input, otherwise it is its output.
157+
/// Call id of the eager macro's input file (this is the macro file for its fully expanded input).
158+
/// If this is none, `arg` contains the pre-expanded input, otherwise arg contains the
159+
/// post-expanded input.
159160
arg_id: Option<MacroCallId>,
160161
error: Option<ExpandError>,
161162
}
@@ -276,13 +277,20 @@ impl HirFileId {
276277

277278
let macro_def = db.macro_def(loc.def).ok()?;
278279
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
279-
let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| {
280-
Arc::new((
281-
tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
282-
Default::default(),
283-
Default::default(),
284-
))
285-
});
280+
let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
281+
282+
let macro_arg = db
283+
.macro_arg(match loc.eager.as_deref() {
284+
Some(&EagerCallInfo { arg_id: Some(arg_id), .. }) => arg_id,
285+
_ => macro_file.macro_call_id,
286+
})
287+
.unwrap_or_else(|| {
288+
Arc::new((
289+
tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
290+
Default::default(),
291+
Default::default(),
292+
))
293+
});
286294

287295
let def = loc.def.ast_id().left().and_then(|id| {
288296
let def_tt = match id.to_node(db) {
@@ -309,8 +317,8 @@ impl HirFileId {
309317
});
310318

311319
Some(ExpansionInfo {
312-
expanded: InFile::new(self, parse.syntax_node()),
313-
arg: InFile::new(loc.kind.file_id(), arg_tt),
320+
expanded,
321+
arg: arg_tt,
314322
attr_input_or_mac_def,
315323
macro_arg_shift: mbe::Shift::new(&macro_arg.0),
316324
macro_arg,
@@ -603,13 +611,18 @@ impl MacroCallKind {
603611
FileRange { range, file_id }
604612
}
605613

606-
fn arg(&self, db: &dyn db::ExpandDatabase) -> Option<SyntaxNode> {
614+
fn arg(&self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> {
607615
match self {
608-
MacroCallKind::FnLike { ast_id, .. } => {
609-
Some(ast_id.to_node(db).token_tree()?.syntax().clone())
616+
MacroCallKind::FnLike { ast_id, .. } => ast_id
617+
.to_in_file_node(db)
618+
.map(|it| Some(it.token_tree()?.syntax().clone()))
619+
.transpose(),
620+
MacroCallKind::Derive { ast_id, .. } => {
621+
Some(ast_id.to_in_file_node(db).syntax().cloned())
622+
}
623+
MacroCallKind::Attr { ast_id, .. } => {
624+
Some(ast_id.to_in_file_node(db).syntax().cloned())
610625
}
611-
MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
612-
MacroCallKind::Attr { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
613626
}
614627
}
615628
}
@@ -627,7 +640,7 @@ impl MacroCallId {
627640
/// ExpansionInfo mainly describes how to map text range between src and expanded macro
628641
#[derive(Debug, Clone, PartialEq, Eq)]
629642
pub struct ExpansionInfo {
630-
expanded: InFile<SyntaxNode>,
643+
expanded: InMacroFile<SyntaxNode>,
631644
/// The argument TokenTree or item for attributes
632645
arg: InFile<SyntaxNode>,
633646
/// The `macro_rules!` or attribute input.
@@ -643,7 +656,7 @@ pub struct ExpansionInfo {
643656

644657
impl ExpansionInfo {
645658
pub fn expanded(&self) -> InFile<SyntaxNode> {
646-
self.expanded.clone()
659+
self.expanded.clone().into()
647660
}
648661

649662
pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
@@ -674,7 +687,7 @@ impl ExpansionInfo {
674687
let token_id_in_attr_input = if let Some(item) = item {
675688
// check if we are mapping down in an attribute input
676689
// this is a special case as attributes can have two inputs
677-
let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
690+
let call_id = self.expanded.file_id.macro_call_id;
678691
let loc = db.lookup_intern_macro_call(call_id);
679692

680693
let token_range = token.value.text_range();
@@ -730,7 +743,7 @@ impl ExpansionInfo {
730743
.ranges_by_token(token_id, token.value.kind())
731744
.flat_map(move |range| self.expanded.value.covering_element(range).into_token());
732745

733-
Some(tokens.map(move |token| self.expanded.with_value(token)))
746+
Some(tokens.map(move |token| InFile::new(self.expanded.file_id.into(), token)))
734747
}
735748

736749
/// Map a token up out of the expansion it resides in into the arguments of the macro call of the expansion.
@@ -739,12 +752,14 @@ impl ExpansionInfo {
739752
db: &dyn db::ExpandDatabase,
740753
token: InFile<&SyntaxToken>,
741754
) -> Option<(InFile<SyntaxToken>, Origin)> {
755+
assert_eq!(token.file_id, self.expanded.file_id.into());
756+
dbg!(&token);
742757
// Fetch the id through its text range,
743758
let token_id = self.exp_map.token_by_range(token.value.text_range())?;
744759
// conditionally unshifting the id to accommodate for macro-rules def site
745760
let (mut token_id, origin) = self.macro_def.map_id_up(token_id);
746761

747-
let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
762+
let call_id = self.expanded.file_id.macro_call_id;
748763
let loc = db.lookup_intern_macro_call(call_id);
749764

750765
// Special case: map tokens from `include!` expansions to the included file
@@ -778,7 +793,7 @@ impl ExpansionInfo {
778793
}
779794
}
780795
}
781-
_ => match origin {
796+
_ => match dbg!(origin) {
782797
mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
783798
mbe::Origin::Def => match (&*self.macro_def, &self.attr_input_or_mac_def) {
784799
(TokenExpander::DeclarativeMacro { def_site_token_map, .. }, Some(tt)) => {
@@ -790,6 +805,8 @@ impl ExpansionInfo {
790805
};
791806

792807
let range = token_map.first_range_by_token(token_id, token.value.kind())?;
808+
dbg!(&tt);
809+
dbg!(range);
793810
let token =
794811
tt.value.covering_element(range + tt.value.text_range().start()).into_token()?;
795812
Some((tt.with_value(token), origin))
@@ -805,6 +822,9 @@ impl<N: AstIdNode> AstId<N> {
805822
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N {
806823
self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
807824
}
825+
pub fn to_in_file_node(&self, db: &dyn db::ExpandDatabase) -> InFile<N> {
826+
InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)))
827+
}
808828
pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> AstPtr<N> {
809829
db.ast_id_map(self.file_id).get(self.value)
810830
}
@@ -820,6 +840,7 @@ impl ErasedAstId {
820840
db.ast_id_map(self.file_id).get_raw(self.value)
821841
}
822842
}
843+
823844
/// `InFile<T>` stores a value of `T` inside a particular file/syntax tree.
824845
///
825846
/// Typical usages are:
@@ -1038,6 +1059,18 @@ impl InFile<SyntaxToken> {
10381059
}
10391060
}
10401061

1062+
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
1063+
pub struct InMacroFile<T> {
1064+
pub file_id: MacroFile,
1065+
pub value: T,
1066+
}
1067+
1068+
impl<T> From<InMacroFile<T>> for InFile<T> {
1069+
fn from(macro_file: InMacroFile<T>) -> Self {
1070+
InFile { file_id: macro_file.file_id.into(), value: macro_file.value }
1071+
}
1072+
}
1073+
10411074
fn ascend_node_border_tokens(
10421075
db: &dyn db::ExpandDatabase,
10431076
InFile { file_id, value: node }: InFile<&SyntaxNode>,

crates/mbe/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ impl Shift {
206206
}
207207
}
208208

209-
#[derive(Debug, Eq, PartialEq)]
209+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
210210
pub enum Origin {
211211
Def,
212212
Call,

0 commit comments

Comments
 (0)