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

Commit 1b5cd03

Browse files
committed
Actually check in fixup.rs
1 parent c3601e9 commit 1b5cd03

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir_expand/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ profile = { path = "../profile", version = "0.0.0" }
2727
tt = { path = "../tt", version = "0.0.0" }
2828
mbe = { path = "../mbe", version = "0.0.0" }
2929
limit = { path = "../limit", version = "0.0.0" }
30+
31+
[dev-dependencies]
32+
expect-test = "1.2.0-pre.1"

crates/hir_expand/src/fixup.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
use mbe::SyntheticToken;
2+
use rustc_hash::FxHashMap;
3+
use syntax::{
4+
ast::{self, AstNode},
5+
match_ast, SyntaxKind, SyntaxNode, SyntaxToken,
6+
};
7+
use tt::{Leaf, Subtree};
8+
9+
#[derive(Debug)]
10+
pub struct SyntaxFixups {
11+
pub append: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
12+
pub replace: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
13+
}
14+
15+
pub fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
16+
let mut append = FxHashMap::default();
17+
let mut replace = FxHashMap::default();
18+
let mut preorder = node.preorder();
19+
while let Some(event) = preorder.next() {
20+
let node = match event {
21+
syntax::WalkEvent::Enter(node) => node,
22+
syntax::WalkEvent::Leave(_) => continue,
23+
};
24+
if node.kind() == SyntaxKind::ERROR {
25+
// TODO this might not be helpful
26+
replace.insert(node, Vec::new());
27+
preorder.skip_subtree();
28+
continue;
29+
}
30+
match_ast! {
31+
match node {
32+
ast::FieldExpr(it) => {
33+
if it.name_ref().is_none() {
34+
// incomplete field access: some_expr.|
35+
append.insert(node.clone(), vec![(SyntaxKind::IDENT, "__ra_fixup".into())]);
36+
}
37+
},
38+
_ => (),
39+
}
40+
}
41+
}
42+
SyntaxFixups { append, replace }
43+
}
44+
45+
pub fn reverse_fixups(tt: &mut Subtree) {
46+
tt.token_trees.retain(|tt| match tt {
47+
tt::TokenTree::Leaf(Leaf::Ident(ident)) => ident.text != "__ra_fixup",
48+
_ => true,
49+
});
50+
tt.token_trees.iter_mut().for_each(|tt| match tt {
51+
tt::TokenTree::Subtree(tt) => reverse_fixups(tt),
52+
_ => {}
53+
});
54+
}
55+
56+
#[cfg(test)]
57+
mod tests {
58+
use expect_test::{Expect, expect};
59+
60+
use super::reverse_fixups;
61+
62+
#[track_caller]
63+
fn check(ra_fixture: &str, mut expect: Expect) {
64+
let parsed = syntax::SourceFile::parse(ra_fixture);
65+
let fixups = super::fixup_syntax(&parsed.syntax_node());
66+
let (mut tt, _tmap) = mbe::syntax_node_to_token_tree_censored(
67+
&parsed.syntax_node(),
68+
fixups.replace,
69+
fixups.append,
70+
);
71+
72+
let mut actual = tt.to_string();
73+
actual.push_str("\n");
74+
75+
expect.indent(false);
76+
expect.assert_eq(&actual);
77+
78+
// the fixed-up tree should be syntactically valid
79+
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());
81+
82+
reverse_fixups(&mut tt);
83+
84+
// the fixed-up + reversed version should be equivalent to the original input
85+
// (but token IDs don't matter)
86+
let (original_as_tt, _) = mbe::syntax_node_to_token_tree(&parsed.syntax_node());
87+
assert_eq!(tt.to_string(), original_as_tt.to_string());
88+
}
89+
90+
#[test]
91+
fn incomplete_field_expr_1() {
92+
check(r#"
93+
fn foo() {
94+
a.
95+
}
96+
"#, expect![[r#"
97+
fn foo () {a . __ra_fixup}
98+
"#]])
99+
}
100+
101+
#[test]
102+
fn incomplete_field_expr_2() {
103+
check(r#"
104+
fn foo() {
105+
a. ;
106+
}
107+
"#, expect![[r#"
108+
fn foo () {a . __ra_fixup ;}
109+
"#]])
110+
}
111+
112+
#[test]
113+
fn incomplete_field_expr_3() {
114+
check(r#"
115+
fn foo() {
116+
a. ;
117+
bar();
118+
}
119+
"#, expect![[r#"
120+
fn foo () {a . __ra_fixup ; bar () ;}
121+
"#]])
122+
}
123+
124+
#[test]
125+
fn field_expr_before_call() {
126+
// another case that easily happens while typing
127+
check(r#"
128+
fn foo() {
129+
a.b
130+
bar();
131+
}
132+
"#, expect![[r#"
133+
fn foo () {a . b bar () ;}
134+
"#]])
135+
}
136+
}

0 commit comments

Comments
 (0)