Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit ecf3cff

Browse files
committed
Replace expressions with errors in them
1 parent 30287e6 commit ecf3cff

File tree

7 files changed

+84
-33
lines changed

7 files changed

+84
-33
lines changed

crates/hir_def/src/macro_expansion_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
186186
let range: Range<usize> = range.into();
187187

188188
if show_token_ids {
189-
if let Some((tree, map)) = arg.as_deref() {
189+
if let Some((tree, map, _)) = arg.as_deref() {
190190
let tt_range = call.token_tree().unwrap().syntax().text_range();
191191
let mut ranges = Vec::new();
192192
extract_id_ranges(&mut ranges, &map, &tree);

crates/hir_expand/src/db.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,10 @@ pub trait AstDatabase: SourceDatabase {
108108

109109
/// Lowers syntactic macro call to a token tree representation.
110110
#[salsa::transparent]
111-
fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
111+
fn macro_arg(
112+
&self,
113+
id: MacroCallId,
114+
) -> Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupMap)>>;
112115
/// Extracts syntax node, corresponding to a macro call. That's a firewall
113116
/// query, only typing in the macro call itself changes the returned
114117
/// subtree.
@@ -291,29 +294,27 @@ fn parse_macro_expansion(
291294
}
292295
}
293296

294-
fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
297+
fn macro_arg(
298+
db: &dyn AstDatabase,
299+
id: MacroCallId,
300+
) -> Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupMap)>> {
295301
let arg = db.macro_arg_text(id)?;
296302
let loc = db.lookup_intern_macro_call(id);
297303

298304
let node = SyntaxNode::new_root(arg);
299-
eprintln!("input text:\n{node}");
300-
eprintln!("input syntax:\n{node:#?}");
301305
let censor = censor_for_macro_input(&loc, &node);
302306
// TODO only fixup for attribute macro input
303307
let mut fixups = fixup::fixup_syntax(&node);
304308
fixups.replace.extend(censor.into_iter().map(|node| (node, Vec::new())));
305-
eprintln!("fixups: {fixups:?}");
306309
let (mut tt, tmap) =
307310
mbe::syntax_node_to_token_tree_censored(&node, fixups.replace, fixups.append);
308311

309-
eprintln!("fixed-up input: {}", tt);
310-
311312
if loc.def.is_proc_macro() {
312313
// proc macros expect their inputs without parentheses, MBEs expect it with them included
313314
tt.delimiter = None;
314315
}
315316

316-
Some(Arc::new((tt, tmap)))
317+
Some(Arc::new((tt, tmap, fixups.map)))
317318
}
318319

319320
fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<SyntaxNode> {
@@ -433,7 +434,6 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
433434
let ExpandResult { value: mut tt, err } = expander.expand(db, id, &macro_arg.0);
434435
// Set a hard limit for the expanded tt
435436
let count = tt.count();
436-
// XXX: Make ExpandResult a real error and use .map_err instead?
437437
if TOKEN_LIMIT.check(count).is_err() {
438438
return ExpandResult::str_err(format!(
439439
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
@@ -442,7 +442,7 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
442442
));
443443
}
444444

445-
fixup::reverse_fixups(&mut tt, &macro_arg.1);
445+
fixup::reverse_fixups(&mut tt, &macro_arg.1, &macro_arg.2);
446446

447447
ExpandResult { value: Some(Arc::new(tt)), err }
448448
}

crates/hir_expand/src/fixup.rs

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,39 @@ use tt::Subtree;
1010
pub struct SyntaxFixups {
1111
pub append: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
1212
pub replace: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
13+
pub map: SyntaxFixupMap,
1314
}
1415

16+
#[derive(Debug, PartialEq, Eq)]
17+
pub struct SyntaxFixupMap {
18+
original: Vec<(Subtree, TokenMap)>,
19+
}
20+
21+
const EMPTY_ID: SyntheticTokenId = SyntheticTokenId(!0);
22+
1523
pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
1624
let mut append = FxHashMap::default();
1725
let mut replace = FxHashMap::default();
1826
let mut preorder = node.preorder();
19-
let empty_id = SyntheticTokenId(0);
27+
let mut original = Vec::new();
2028
while let Some(event) = preorder.next() {
2129
let node = match event {
2230
syntax::WalkEvent::Enter(node) => node,
2331
syntax::WalkEvent::Leave(_) => continue,
2432
};
25-
if node.kind() == SyntaxKind::ERROR {
26-
// TODO this might not be helpful
27-
replace.insert(node, Vec::new());
33+
if can_handle_error(&node) && has_error_to_handle(&node) {
34+
// the node contains an error node, we have to completely replace it by something valid
35+
let original_tree = mbe::syntax_node_to_token_tree(&node);
36+
// TODO handle token ids / token map
37+
let idx = original.len() as u32;
38+
original.push(original_tree);
39+
let replacement = SyntheticToken {
40+
kind: SyntaxKind::IDENT,
41+
text: "__ra_fixup".into(),
42+
range: node.text_range(),
43+
id: SyntheticTokenId(idx),
44+
};
45+
replace.insert(node.clone(), vec![replacement]);
2846
preorder.skip_subtree();
2947
continue;
3048
}
@@ -39,7 +57,7 @@ pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
3957
kind: SyntaxKind::IDENT,
4058
text: "__ra_fixup".into(),
4159
range: end_range,
42-
id: empty_id,
60+
id: EMPTY_ID,
4361
},
4462
]);
4563
}
@@ -51,7 +69,7 @@ pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
5169
kind: SyntaxKind::SEMICOLON,
5270
text: ";".into(),
5371
range: end_range,
54-
id: empty_id,
72+
id: EMPTY_ID,
5573
},
5674
]);
5775
}
@@ -60,18 +78,37 @@ pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
6078
}
6179
}
6280
}
63-
SyntaxFixups { append, replace }
81+
SyntaxFixups { append, replace, map: SyntaxFixupMap { original } }
82+
}
83+
84+
fn has_error(node: &SyntaxNode) -> bool {
85+
node.children().any(|c| c.kind() == SyntaxKind::ERROR)
86+
}
87+
88+
fn can_handle_error(node: &SyntaxNode) -> bool {
89+
ast::Expr::can_cast(node.kind())
90+
}
91+
92+
fn has_error_to_handle(node: &SyntaxNode) -> bool {
93+
has_error(node) || node.children().any(|c| !can_handle_error(&c) && has_error_to_handle(&c))
6494
}
6595

66-
pub fn reverse_fixups(tt: &mut Subtree, token_map: &TokenMap) {
67-
eprintln!("token_map: {:?}", token_map);
96+
pub fn reverse_fixups(tt: &mut Subtree, token_map: &TokenMap, fixup_map: &SyntaxFixupMap) {
6897
tt.token_trees.retain(|tt| match tt {
69-
tt::TokenTree::Leaf(leaf) => token_map.synthetic_token_id(leaf.id()).is_none(),
98+
tt::TokenTree::Leaf(leaf) => {
99+
token_map.synthetic_token_id(leaf.id()).is_none()
100+
|| token_map.synthetic_token_id(leaf.id()) != Some(EMPTY_ID)
101+
}
70102
_ => true,
71103
});
72104
tt.token_trees.iter_mut().for_each(|tt| match tt {
73-
tt::TokenTree::Subtree(tt) => reverse_fixups(tt, token_map),
74-
_ => {}
105+
tt::TokenTree::Subtree(tt) => reverse_fixups(tt, token_map, fixup_map),
106+
tt::TokenTree::Leaf(leaf) => {
107+
if let Some(id) = token_map.synthetic_token_id(leaf.id()) {
108+
let (original, _original_tmap) = &fixup_map.original[id.0 as usize];
109+
*tt = tt::TokenTree::Subtree(original.clone());
110+
}
111+
}
75112
});
76113
}
77114

@@ -84,6 +121,7 @@ mod tests {
84121
#[track_caller]
85122
fn check(ra_fixture: &str, mut expect: Expect) {
86123
let parsed = syntax::SourceFile::parse(ra_fixture);
124+
eprintln!("parse: {:#?}", parsed.syntax_node());
87125
let fixups = super::fixup_syntax(&parsed.syntax_node());
88126
let (mut tt, tmap) = mbe::syntax_node_to_token_tree_censored(
89127
&parsed.syntax_node(),
@@ -106,7 +144,7 @@ mod tests {
106144
parse.syntax_node()
107145
);
108146

109-
reverse_fixups(&mut tt, &tmap);
147+
reverse_fixups(&mut tt, &tmap, &fixups.map);
110148

111149
// the fixed-up + reversed version should be equivalent to the original input
112150
// (but token IDs don't matter)
@@ -169,6 +207,20 @@ fn foo() {
169207
"#,
170208
expect![[r#"
171209
fn foo () {a . b ; bar () ;}
210+
"#]],
211+
)
212+
}
213+
214+
#[test]
215+
fn extraneous_comma() {
216+
check(
217+
r#"
218+
fn foo() {
219+
bar(,);
220+
}
221+
"#,
222+
expect![[r#"
223+
fn foo () {__ra_fixup ;}
172224
"#]],
173225
)
174226
}

crates/hir_expand/src/hygiene.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use syntax::{
1515

1616
use crate::{
1717
db::{self, AstDatabase},
18+
fixup,
1819
name::{AsName, Name},
1920
HirFileId, HirFileIdRepr, InFile, MacroCallKind, MacroCallLoc, MacroDefKind, MacroFile,
2021
};
@@ -127,7 +128,7 @@ struct HygieneInfo {
127128
attr_input_or_mac_def_start: Option<InFile<TextSize>>,
128129

129130
macro_def: Arc<TokenExpander>,
130-
macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
131+
macro_arg: Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupMap)>,
131132
macro_arg_shift: mbe::Shift,
132133
exp_map: Arc<mbe::TokenMap>,
133134
}

crates/hir_expand/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ pub struct ExpansionInfo {
427427
attr_input_or_mac_def: Option<InFile<ast::TokenTree>>,
428428

429429
macro_def: Arc<TokenExpander>,
430-
macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
430+
macro_arg: Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupMap)>,
431431
/// A shift built from `macro_arg`'s subtree, relevant for attributes as the item is the macro arg
432432
/// and as such we need to shift tokens if they are part of an attributes input instead of their item.
433433
macro_arg_shift: mbe::Shift,

crates/mbe/src/syntax_bridge.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ pub fn syntax_node_to_token_tree_censored(
3030
let mut c = Convertor::new(node, global_offset, replace, append);
3131
let subtree = convert_tokens(&mut c);
3232
c.id_alloc.map.shrink_to_fit();
33-
always!(c.replace.is_empty());
34-
always!(c.append.is_empty());
33+
always!(c.replace.is_empty(), "replace: {:?}", c.replace);
34+
always!(c.append.is_empty(), "append: {:?}", c.append);
3535
(subtree, c.id_alloc.map)
3636
}
3737

@@ -539,7 +539,6 @@ impl Convertor {
539539
WalkEvent::Enter(ele) => ele,
540540
WalkEvent::Leave(SyntaxElement::Node(node)) => {
541541
if let Some(mut v) = append.remove(&node) {
542-
eprintln!("after {:?}, appending {:?}", node, v);
543542
if !v.is_empty() {
544543
v.reverse();
545544
return (None, v);
@@ -554,7 +553,6 @@ impl Convertor {
554553
SyntaxElement::Node(node) => {
555554
if let Some(mut v) = replace.remove(&node) {
556555
preorder.skip_subtree();
557-
eprintln!("replacing {:?} by {:?}", node, v);
558556
if !v.is_empty() {
559557
v.reverse();
560558
return (None, v);
@@ -640,8 +638,8 @@ impl TokenConvertor for Convertor {
640638
self.current = new_current;
641639
self.current_synthetic = new_synth;
642640
}
643-
// TODO fix range?
644-
return Some((SynToken::Synthetic(synth_token), self.range));
641+
let range = synth_token.range;
642+
return Some((SynToken::Synthetic(synth_token), range));
645643
}
646644

647645
let curr = self.current.clone()?;
@@ -675,7 +673,6 @@ impl TokenConvertor for Convertor {
675673
}
676674

677675
if let Some(synth_token) = self.current_synthetic.last() {
678-
// TODO fix range?
679676
return Some(SynToken::Synthetic(synth_token.clone()));
680677
}
681678

crates/mbe/src/token_map.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ impl TokenMap {
7474

7575
pub(crate) fn shrink_to_fit(&mut self) {
7676
self.entries.shrink_to_fit();
77+
self.synthetic_entries.shrink_to_fit();
7778
}
7879

7980
pub(crate) fn insert(&mut self, token_id: tt::TokenId, relative_range: TextRange) {

0 commit comments

Comments
 (0)