1 //! To make attribute macros work reliably when typing, we need to take care to
2 //! fix up syntax errors in the code we're passing to them.
5 use mbe::{SyntheticToken, SyntheticTokenId, TokenMap};
6 use rustc_hash::FxHashMap;
9 match_ast, SyntaxKind, SyntaxNode, TextRange,
13 /// The result of calculating fixes for a syntax node -- a bunch of changes
14 /// (appending to and replacing nodes), the information that is needed to
15 /// reverse those changes afterwards, and a token map.
17 pub(crate) struct SyntaxFixups {
18 pub(crate) append: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
19 pub(crate) replace: FxHashMap<SyntaxNode, Vec<SyntheticToken>>,
20 pub(crate) undo_info: SyntaxFixupUndoInfo,
21 pub(crate) token_map: TokenMap,
22 pub(crate) next_id: u32,
25 /// This is the information needed to reverse the fixups.
26 #[derive(Debug, PartialEq, Eq)]
27 pub struct SyntaxFixupUndoInfo {
28 original: Vec<Subtree>,
31 const EMPTY_ID: SyntheticTokenId = SyntheticTokenId(!0);
33 pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
34 let mut append = FxHashMap::default();
35 let mut replace = FxHashMap::default();
36 let mut preorder = node.preorder();
37 let mut original = Vec::new();
38 let mut token_map = TokenMap::default();
40 while let Some(event) = preorder.next() {
41 let node = match event {
42 syntax::WalkEvent::Enter(node) => node,
43 syntax::WalkEvent::Leave(_) => continue,
46 if can_handle_error(&node) && has_error_to_handle(&node) {
47 // the node contains an error node, we have to completely replace it by something valid
48 let (original_tree, new_tmap, new_next_id) =
49 mbe::syntax_node_to_token_tree_with_modifications(
51 mem::take(&mut token_map),
57 next_id = new_next_id;
58 let idx = original.len() as u32;
59 original.push(original_tree);
60 let replacement = SyntheticToken {
61 kind: SyntaxKind::IDENT,
62 text: "__ra_fixup".into(),
63 range: node.text_range(),
64 id: SyntheticTokenId(idx),
66 replace.insert(node.clone(), vec![replacement]);
67 preorder.skip_subtree();
71 // In some other situations, we can fix things by just appending some tokens.
72 let end_range = TextRange::empty(node.text_range().end());
75 ast::FieldExpr(it) => {
76 if it.name_ref().is_none() {
77 // incomplete field access: some_expr.|
78 append.insert(node.clone(), vec![
80 kind: SyntaxKind::IDENT,
81 text: "__ra_fixup".into(),
88 ast::ExprStmt(it) => {
89 if it.semicolon_token().is_none() {
90 append.insert(node.clone(), vec![
92 kind: SyntaxKind::SEMICOLON,
109 undo_info: SyntaxFixupUndoInfo { original },
113 fn has_error(node: &SyntaxNode) -> bool {
114 node.children().any(|c| c.kind() == SyntaxKind::ERROR)
117 fn can_handle_error(node: &SyntaxNode) -> bool {
118 ast::Expr::can_cast(node.kind())
121 fn has_error_to_handle(node: &SyntaxNode) -> bool {
122 has_error(node) || node.children().any(|c| !can_handle_error(&c) && has_error_to_handle(&c))
125 pub(crate) fn reverse_fixups(
127 token_map: &TokenMap,
128 undo_info: &SyntaxFixupUndoInfo,
130 tt.token_trees.retain(|tt| match tt {
131 tt::TokenTree::Leaf(leaf) => {
132 token_map.synthetic_token_id(leaf.id()).is_none()
133 || token_map.synthetic_token_id(leaf.id()) != Some(EMPTY_ID)
137 tt.token_trees.iter_mut().for_each(|tt| match tt {
138 tt::TokenTree::Subtree(tt) => reverse_fixups(tt, token_map, undo_info),
139 tt::TokenTree::Leaf(leaf) => {
140 if let Some(id) = token_map.synthetic_token_id(leaf.id()) {
141 let original = &undo_info.original[id.0 as usize];
142 *tt = tt::TokenTree::Subtree(original.clone());
150 use expect_test::{expect, Expect};
152 use super::reverse_fixups;
155 fn check(ra_fixture: &str, mut expect: Expect) {
156 let parsed = syntax::SourceFile::parse(ra_fixture);
157 let fixups = super::fixup_syntax(&parsed.syntax_node());
158 let (mut tt, tmap, _) = mbe::syntax_node_to_token_tree_with_modifications(
159 &parsed.syntax_node(),
166 let mut actual = tt.to_string();
167 actual.push_str("\n");
169 expect.indent(false);
170 expect.assert_eq(&actual);
172 // the fixed-up tree should be syntactically valid
173 let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems);
177 "parse has syntax errors. parse tree:\n{:#?}",
181 reverse_fixups(&mut tt, &tmap, &fixups.undo_info);
183 // the fixed-up + reversed version should be equivalent to the original input
184 // (but token IDs don't matter)
185 let (original_as_tt, _) = mbe::syntax_node_to_token_tree(&parsed.syntax_node());
186 assert_eq!(tt.to_string(), original_as_tt.to_string());
190 fn incomplete_field_expr_1() {
198 fn foo () {a . __ra_fixup}
204 fn incomplete_field_expr_2() {
212 fn foo () {a . __ra_fixup ;}
218 fn incomplete_field_expr_3() {
227 fn foo () {a . __ra_fixup ; bar () ;}
233 fn field_expr_before_call() {
234 // another case that easily happens while typing
243 fn foo () {a . b ; bar () ;}
249 fn extraneous_comma() {
257 fn foo () {__ra_fixup ;}