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

Commit 1a5aa84

Browse files
committed
Track synthetic tokens, to be able to remove them again later
1 parent 1b5cd03 commit 1a5aa84

File tree

6 files changed

+133
-39
lines changed

6 files changed

+133
-39
lines changed

crates/hir_expand/src/db.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use std::sync::Arc;
55
use base_db::{salsa, SourceDatabase};
66
use either::Either;
77
use limit::Limit;
8-
use mbe::{syntax_node_to_token_tree, ExpandError, ExpandResult, SyntheticToken};
9-
use rustc_hash::{FxHashMap, FxHashSet};
8+
use mbe::{syntax_node_to_token_tree, ExpandError, ExpandResult};
9+
use rustc_hash::FxHashSet;
1010
use syntax::{
1111
algo::diff,
1212
ast::{self, HasAttrs, HasDocComments},
@@ -442,7 +442,7 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
442442
));
443443
}
444444

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

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

crates/hir_expand/src/fixup.rs

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use mbe::SyntheticToken;
1+
use mbe::{SyntheticToken, SyntheticTokenId, TokenMap};
22
use rustc_hash::FxHashMap;
33
use syntax::{
44
ast::{self, AstNode},
5-
match_ast, SyntaxKind, SyntaxNode, SyntaxToken,
5+
match_ast, SyntaxKind, SyntaxNode, TextRange,
66
};
7-
use tt::{Leaf, Subtree};
7+
use tt::Subtree;
88

99
#[derive(Debug)]
1010
pub struct SyntaxFixups {
@@ -16,6 +16,7 @@ pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
1616
let mut append = FxHashMap::default();
1717
let mut replace = FxHashMap::default();
1818
let mut preorder = node.preorder();
19+
let empty_id = SyntheticTokenId(0);
1920
while let Some(event) = preorder.next() {
2021
let node = match event {
2122
syntax::WalkEvent::Enter(node) => node,
@@ -27,12 +28,32 @@ pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
2728
preorder.skip_subtree();
2829
continue;
2930
}
31+
let end_range = TextRange::empty(node.text_range().end());
3032
match_ast! {
3133
match node {
3234
ast::FieldExpr(it) => {
3335
if it.name_ref().is_none() {
3436
// incomplete field access: some_expr.|
35-
append.insert(node.clone(), vec![(SyntaxKind::IDENT, "__ra_fixup".into())]);
37+
append.insert(node.clone(), vec![
38+
SyntheticToken {
39+
kind: SyntaxKind::IDENT,
40+
text: "__ra_fixup".into(),
41+
range: end_range,
42+
id: empty_id,
43+
},
44+
]);
45+
}
46+
},
47+
ast::ExprStmt(it) => {
48+
if it.semicolon_token().is_none() {
49+
append.insert(node.clone(), vec![
50+
SyntheticToken {
51+
kind: SyntaxKind::SEMICOLON,
52+
text: ";".into(),
53+
range: end_range,
54+
id: empty_id,
55+
},
56+
]);
3657
}
3758
},
3859
_ => (),
@@ -42,28 +63,29 @@ pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
4263
SyntaxFixups { append, replace }
4364
}
4465

45-
pub fn reverse_fixups(tt: &mut Subtree) {
66+
pub fn reverse_fixups(tt: &mut Subtree, token_map: &TokenMap) {
67+
eprintln!("token_map: {:?}", token_map);
4668
tt.token_trees.retain(|tt| match tt {
47-
tt::TokenTree::Leaf(Leaf::Ident(ident)) => ident.text != "__ra_fixup",
69+
tt::TokenTree::Leaf(leaf) => token_map.synthetic_token_id(leaf.id()).is_none(),
4870
_ => true,
4971
});
5072
tt.token_trees.iter_mut().for_each(|tt| match tt {
51-
tt::TokenTree::Subtree(tt) => reverse_fixups(tt),
73+
tt::TokenTree::Subtree(tt) => reverse_fixups(tt, token_map),
5274
_ => {}
5375
});
5476
}
5577

5678
#[cfg(test)]
5779
mod tests {
58-
use expect_test::{Expect, expect};
80+
use expect_test::{expect, Expect};
5981

6082
use super::reverse_fixups;
6183

6284
#[track_caller]
6385
fn check(ra_fixture: &str, mut expect: Expect) {
6486
let parsed = syntax::SourceFile::parse(ra_fixture);
6587
let fixups = super::fixup_syntax(&parsed.syntax_node());
66-
let (mut tt, _tmap) = mbe::syntax_node_to_token_tree_censored(
88+
let (mut tt, tmap) = mbe::syntax_node_to_token_tree_censored(
6789
&parsed.syntax_node(),
6890
fixups.replace,
6991
fixups.append,
@@ -77,9 +99,14 @@ mod tests {
7799

78100
// the fixed-up tree should be syntactically valid
79101
let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems);
80-
assert_eq!(parse.errors(), &[], "parse has syntax errors. parse tree:\n{:#?}", parse.syntax_node());
102+
assert_eq!(
103+
parse.errors(),
104+
&[],
105+
"parse has syntax errors. parse tree:\n{:#?}",
106+
parse.syntax_node()
107+
);
81108

82-
reverse_fixups(&mut tt);
109+
reverse_fixups(&mut tt, &tmap);
83110

84111
// the fixed-up + reversed version should be equivalent to the original input
85112
// (but token IDs don't matter)
@@ -89,48 +116,60 @@ mod tests {
89116

90117
#[test]
91118
fn incomplete_field_expr_1() {
92-
check(r#"
119+
check(
120+
r#"
93121
fn foo() {
94122
a.
95123
}
96-
"#, expect![[r#"
124+
"#,
125+
expect![[r#"
97126
fn foo () {a . __ra_fixup}
98-
"#]])
127+
"#]],
128+
)
99129
}
100130

101131
#[test]
102132
fn incomplete_field_expr_2() {
103-
check(r#"
133+
check(
134+
r#"
104135
fn foo() {
105136
a. ;
106137
}
107-
"#, expect![[r#"
138+
"#,
139+
expect![[r#"
108140
fn foo () {a . __ra_fixup ;}
109-
"#]])
141+
"#]],
142+
)
110143
}
111144

112145
#[test]
113146
fn incomplete_field_expr_3() {
114-
check(r#"
147+
check(
148+
r#"
115149
fn foo() {
116150
a. ;
117151
bar();
118152
}
119-
"#, expect![[r#"
153+
"#,
154+
expect![[r#"
120155
fn foo () {a . __ra_fixup ; bar () ;}
121-
"#]])
156+
"#]],
157+
)
122158
}
123159

124160
#[test]
125161
fn field_expr_before_call() {
126162
// another case that easily happens while typing
127-
check(r#"
163+
check(
164+
r#"
128165
fn foo() {
129166
a.b
130167
bar();
131168
}
132-
"#, expect![[r#"
133-
fn foo () {a . b bar () ;}
134-
"#]])
169+
"#,
170+
expect![[r#"
171+
fn foo () {a . b ; bar () ;}
172+
"#]],
173+
)
135174
}
136175
}

crates/mbe/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub use crate::{
3131
syntax_bridge::{
3232
parse_exprs_with_sep, parse_to_token_tree, syntax_node_to_token_tree,
3333
syntax_node_to_token_tree_censored, token_tree_to_syntax_node, SyntheticToken,
34+
SyntheticTokenId,
3435
},
3536
token_map::TokenMap,
3637
};

crates/mbe/src/syntax_bridge.rs

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Conversions between [`SyntaxNode`] and [`tt::TokenTree`].
22
3-
use rustc_hash::{FxHashMap, FxHashSet};
3+
use rustc_hash::FxHashMap;
44
use stdx::{always, non_empty_vec::NonEmptyVec};
55
use syntax::{
66
ast::{self, make::tokens::doc_comment},
@@ -35,7 +35,16 @@ pub fn syntax_node_to_token_tree_censored(
3535
(subtree, c.id_alloc.map)
3636
}
3737

38-
pub type SyntheticToken = (SyntaxKind, SmolStr);
38+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
39+
pub struct SyntheticTokenId(pub u32);
40+
41+
#[derive(Debug, Clone)]
42+
pub struct SyntheticToken {
43+
pub kind: SyntaxKind,
44+
pub text: SmolStr,
45+
pub range: TextRange,
46+
pub id: SyntheticTokenId,
47+
}
3948

4049
// The following items are what `rustc` macro can be parsed into :
4150
// link: https://github.com/rust-lang/rust/blob/9ebf47851a357faa4cd97f4b1dc7835f6376e639/src/libsyntax/ext/expand.rs#L141
@@ -153,13 +162,14 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
153162
Some(it) => it,
154163
None => break,
155164
};
165+
let synth_id = token.synthetic_id(&conv);
156166

157167
let kind = token.kind(&conv);
158168
if kind == COMMENT {
159169
if let Some(tokens) = conv.convert_doc_comment(&token) {
160170
// FIXME: There has to be a better way to do this
161171
// Add the comments token id to the converted doc string
162-
let id = conv.id_alloc().alloc(range);
172+
let id = conv.id_alloc().alloc(range, synth_id);
163173
result.extend(tokens.into_iter().map(|mut tt| {
164174
if let tt::TokenTree::Subtree(sub) = &mut tt {
165175
if let Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) =
@@ -174,7 +184,7 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
174184
continue;
175185
}
176186
let tt = if kind.is_punct() && kind != UNDERSCORE {
177-
assert_eq!(range.len(), TextSize::of('.'));
187+
// assert_eq!(range.len(), TextSize::of('.'));
178188

179189
if let Some(delim) = subtree.delimiter {
180190
let expected = match delim.kind {
@@ -226,11 +236,13 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
226236
panic!("Token from lexer must be single char: token = {:#?}", token);
227237
}
228238
};
229-
tt::Leaf::from(tt::Punct { char, spacing, id: conv.id_alloc().alloc(range) }).into()
239+
tt::Leaf::from(tt::Punct { char, spacing, id: conv.id_alloc().alloc(range, synth_id) })
240+
.into()
230241
} else {
231242
macro_rules! make_leaf {
232243
($i:ident) => {
233-
tt::$i { id: conv.id_alloc().alloc(range), text: token.to_text(conv) }.into()
244+
tt::$i { id: conv.id_alloc().alloc(range, synth_id), text: token.to_text(conv) }
245+
.into()
234246
};
235247
}
236248
let leaf: tt::Leaf = match kind {
@@ -245,14 +257,14 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
245257
let apostrophe = tt::Leaf::from(tt::Punct {
246258
char: '\'',
247259
spacing: tt::Spacing::Joint,
248-
id: conv.id_alloc().alloc(r),
260+
id: conv.id_alloc().alloc(r, synth_id),
249261
});
250262
result.push(apostrophe.into());
251263

252264
let r = TextRange::at(range.start() + char_unit, range.len() - char_unit);
253265
let ident = tt::Leaf::from(tt::Ident {
254266
text: SmolStr::new(&token.to_text(conv)[1..]),
255-
id: conv.id_alloc().alloc(r),
267+
id: conv.id_alloc().alloc(r, synth_id),
256268
});
257269
result.push(ident.into());
258270
continue;
@@ -273,7 +285,7 @@ fn convert_tokens<C: TokenConvertor>(conv: &mut C) -> tt::Subtree {
273285

274286
conv.id_alloc().close_delim(entry.idx, None);
275287
let leaf: tt::Leaf = tt::Punct {
276-
id: conv.id_alloc().alloc(entry.open_range),
288+
id: conv.id_alloc().alloc(entry.open_range, None),
277289
char: match entry.subtree.delimiter.unwrap().kind {
278290
tt::DelimiterKind::Parenthesis => '(',
279291
tt::DelimiterKind::Brace => '{',
@@ -367,11 +379,18 @@ struct TokenIdAlloc {
367379
}
368380

369381
impl TokenIdAlloc {
370-
fn alloc(&mut self, absolute_range: TextRange) -> tt::TokenId {
382+
fn alloc(
383+
&mut self,
384+
absolute_range: TextRange,
385+
synthetic_id: Option<SyntheticTokenId>,
386+
) -> tt::TokenId {
371387
let relative_range = absolute_range - self.global_offset;
372388
let token_id = tt::TokenId(self.next_id);
373389
self.next_id += 1;
374390
self.map.insert(token_id, relative_range);
391+
if let Some(id) = synthetic_id {
392+
self.map.insert_synthetic(token_id, id);
393+
}
375394
token_id
376395
}
377396

@@ -411,6 +430,8 @@ trait SrcToken<Ctx>: std::fmt::Debug {
411430
fn to_char(&self, ctx: &Ctx) -> Option<char>;
412431

413432
fn to_text(&self, ctx: &Ctx) -> SmolStr;
433+
434+
fn synthetic_id(&self, ctx: &Ctx) -> Option<SyntheticTokenId>;
414435
}
415436

416437
trait TokenConvertor: Sized {
@@ -437,6 +458,10 @@ impl<'a> SrcToken<RawConvertor<'a>> for usize {
437458
fn to_text(&self, ctx: &RawConvertor<'_>) -> SmolStr {
438459
ctx.lexed.text(*self).into()
439460
}
461+
462+
fn synthetic_id(&self, _ctx: &RawConvertor<'a>) -> Option<SyntheticTokenId> {
463+
None
464+
}
440465
}
441466

442467
impl<'a> TokenConvertor for RawConvertor<'a> {
@@ -564,21 +589,29 @@ impl SrcToken<Convertor> for SynToken {
564589
match self {
565590
SynToken::Ordinary(token) => token.kind(),
566591
SynToken::Punch(token, _) => token.kind(),
567-
SynToken::Synthetic((kind, _)) => *kind,
592+
SynToken::Synthetic(token) => token.kind,
568593
}
569594
}
570595
fn to_char(&self, _ctx: &Convertor) -> Option<char> {
571596
match self {
572597
SynToken::Ordinary(_) => None,
573598
SynToken::Punch(it, i) => it.text().chars().nth((*i).into()),
599+
SynToken::Synthetic(token) if token.text.len() == 1 => token.text.chars().next(),
574600
SynToken::Synthetic(_) => None,
575601
}
576602
}
577603
fn to_text(&self, _ctx: &Convertor) -> SmolStr {
578604
match self {
579605
SynToken::Ordinary(token) => token.text().into(),
580606
SynToken::Punch(token, _) => token.text().into(),
581-
SynToken::Synthetic((_, text)) => text.clone(),
607+
SynToken::Synthetic(token) => token.text.clone(),
608+
}
609+
}
610+
611+
fn synthetic_id(&self, _ctx: &Convertor) -> Option<SyntheticTokenId> {
612+
match self {
613+
SynToken::Synthetic(token) => Some(token.id),
614+
_ => None,
582615
}
583616
}
584617
}

0 commit comments

Comments
 (0)