]> git.lizzy.rs Git - rust.git/blob - crates/hir-expand/src/fixup.rs
Auto merge of #12652 - lnicola:openvsx, r=lnicola
[rust.git] / crates / hir-expand / src / fixup.rs
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.
3 use std::mem;
4
5 use mbe::{SyntheticToken, SyntheticTokenId, TokenMap};
6 use rustc_hash::FxHashMap;
7 use syntax::{
8     ast::{self, AstNode},
9     match_ast, SyntaxKind, SyntaxNode, TextRange,
10 };
11 use tt::Subtree;
12
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.
16 #[derive(Debug)]
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,
23 }
24
25 /// This is the information needed to reverse the fixups.
26 #[derive(Debug, PartialEq, Eq)]
27 pub struct SyntaxFixupUndoInfo {
28     original: Vec<Subtree>,
29 }
30
31 const EMPTY_ID: SyntheticTokenId = SyntheticTokenId(!0);
32
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();
39     let mut next_id = 0;
40     while let Some(event) = preorder.next() {
41         let node = match event {
42             syntax::WalkEvent::Enter(node) => node,
43             syntax::WalkEvent::Leave(_) => continue,
44         };
45
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(
50                     &node,
51                     mem::take(&mut token_map),
52                     next_id,
53                     Default::default(),
54                     Default::default(),
55                 );
56             token_map = new_tmap;
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),
65             };
66             replace.insert(node.clone(), vec![replacement]);
67             preorder.skip_subtree();
68             continue;
69         }
70
71         // In some other situations, we can fix things by just appending some tokens.
72         let end_range = TextRange::empty(node.text_range().end());
73         match_ast! {
74             match node {
75                 ast::FieldExpr(it) => {
76                     if it.name_ref().is_none() {
77                         // incomplete field access: some_expr.|
78                         append.insert(node.clone(), vec![
79                             SyntheticToken {
80                                 kind: SyntaxKind::IDENT,
81                                 text: "__ra_fixup".into(),
82                                 range: end_range,
83                                 id: EMPTY_ID,
84                             },
85                         ]);
86                     }
87                 },
88                 ast::ExprStmt(it) => {
89                     if it.semicolon_token().is_none() {
90                         append.insert(node.clone(), vec![
91                             SyntheticToken {
92                                 kind: SyntaxKind::SEMICOLON,
93                                 text: ";".into(),
94                                 range: end_range,
95                                 id: EMPTY_ID,
96                             },
97                         ]);
98                     }
99                 },
100                 ast::LetStmt(it) => {
101                     if it.semicolon_token().is_none() {
102                         append.insert(node.clone(), vec![
103                             SyntheticToken {
104                                 kind: SyntaxKind::SEMICOLON,
105                                 text: ";".into(),
106                                 range: end_range,
107                                 id: EMPTY_ID,
108                             },
109                         ]);
110                     }
111                 },
112                 _ => (),
113             }
114         }
115     }
116     SyntaxFixups {
117         append,
118         replace,
119         token_map,
120         next_id,
121         undo_info: SyntaxFixupUndoInfo { original },
122     }
123 }
124
125 fn has_error(node: &SyntaxNode) -> bool {
126     node.children().any(|c| c.kind() == SyntaxKind::ERROR)
127 }
128
129 fn can_handle_error(node: &SyntaxNode) -> bool {
130     ast::Expr::can_cast(node.kind())
131 }
132
133 fn has_error_to_handle(node: &SyntaxNode) -> bool {
134     has_error(node) || node.children().any(|c| !can_handle_error(&c) && has_error_to_handle(&c))
135 }
136
137 pub(crate) fn reverse_fixups(
138     tt: &mut Subtree,
139     token_map: &TokenMap,
140     undo_info: &SyntaxFixupUndoInfo,
141 ) {
142     tt.token_trees.retain(|tt| match tt {
143         tt::TokenTree::Leaf(leaf) => {
144             token_map.synthetic_token_id(leaf.id()).is_none()
145                 || token_map.synthetic_token_id(leaf.id()) != Some(EMPTY_ID)
146         }
147         _ => true,
148     });
149     tt.token_trees.iter_mut().for_each(|tt| match tt {
150         tt::TokenTree::Subtree(tt) => reverse_fixups(tt, token_map, undo_info),
151         tt::TokenTree::Leaf(leaf) => {
152             if let Some(id) = token_map.synthetic_token_id(leaf.id()) {
153                 let original = &undo_info.original[id.0 as usize];
154                 *tt = tt::TokenTree::Subtree(original.clone());
155             }
156         }
157     });
158 }
159
160 #[cfg(test)]
161 mod tests {
162     use expect_test::{expect, Expect};
163
164     use super::reverse_fixups;
165
166     #[track_caller]
167     fn check(ra_fixture: &str, mut expect: Expect) {
168         let parsed = syntax::SourceFile::parse(ra_fixture);
169         let fixups = super::fixup_syntax(&parsed.syntax_node());
170         let (mut tt, tmap, _) = mbe::syntax_node_to_token_tree_with_modifications(
171             &parsed.syntax_node(),
172             fixups.token_map,
173             fixups.next_id,
174             fixups.replace,
175             fixups.append,
176         );
177
178         let mut actual = tt.to_string();
179         actual.push('\n');
180
181         expect.indent(false);
182         expect.assert_eq(&actual);
183
184         // the fixed-up tree should be syntactically valid
185         let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems);
186         assert_eq!(
187             parse.errors(),
188             &[],
189             "parse has syntax errors. parse tree:\n{:#?}",
190             parse.syntax_node()
191         );
192
193         reverse_fixups(&mut tt, &tmap, &fixups.undo_info);
194
195         // the fixed-up + reversed version should be equivalent to the original input
196         // (but token IDs don't matter)
197         let (original_as_tt, _) = mbe::syntax_node_to_token_tree(&parsed.syntax_node());
198         assert_eq!(tt.to_string(), original_as_tt.to_string());
199     }
200
201     #[test]
202     fn incomplete_field_expr_1() {
203         check(
204             r#"
205 fn foo() {
206     a.
207 }
208 "#,
209             expect![[r#"
210 fn foo () {a . __ra_fixup}
211 "#]],
212         )
213     }
214
215     #[test]
216     fn incomplete_field_expr_2() {
217         check(
218             r#"
219 fn foo() {
220     a. ;
221 }
222 "#,
223             expect![[r#"
224 fn foo () {a . __ra_fixup ;}
225 "#]],
226         )
227     }
228
229     #[test]
230     fn incomplete_field_expr_3() {
231         check(
232             r#"
233 fn foo() {
234     a. ;
235     bar();
236 }
237 "#,
238             expect![[r#"
239 fn foo () {a . __ra_fixup ; bar () ;}
240 "#]],
241         )
242     }
243
244     #[test]
245     fn incomplete_let() {
246         check(
247             r#"
248 fn foo() {
249     let x = a
250 }
251 "#,
252             expect![[r#"
253 fn foo () {let x = a ;}
254 "#]],
255         )
256     }
257
258     #[test]
259     fn incomplete_field_expr_in_let() {
260         check(
261             r#"
262 fn foo() {
263     let x = a.
264 }
265 "#,
266             expect![[r#"
267 fn foo () {let x = a . __ra_fixup ;}
268 "#]],
269         )
270     }
271
272     #[test]
273     fn field_expr_before_call() {
274         // another case that easily happens while typing
275         check(
276             r#"
277 fn foo() {
278     a.b
279     bar();
280 }
281 "#,
282             expect![[r#"
283 fn foo () {a . b ; bar () ;}
284 "#]],
285         )
286     }
287
288     #[test]
289     fn extraneous_comma() {
290         check(
291             r#"
292 fn foo() {
293     bar(,);
294 }
295 "#,
296             expect![[r#"
297 fn foo () {__ra_fixup ;}
298 "#]],
299         )
300     }
301 }