syn/fixup.rs
1use crate::classify;
2use crate::expr::Expr;
3use crate::precedence::Precedence;
4
5pub(crate) struct FixupContext {
6 // Print expression such that it can be parsed back as a statement
7 // consisting of the original expression.
8 //
9 // The effect of this is for binary operators in statement position to set
10 // `leftmost_subexpression_in_stmt` when printing their left-hand operand.
11 //
12 // (match x {}) - 1; // match needs parens when LHS of binary operator
13 //
14 // match x {}; // not when its own statement
15 //
16 #[cfg(feature = "full")]
17 stmt: bool,
18
19 // This is the difference between:
20 //
21 // (match x {}) - 1; // subexpression needs parens
22 //
23 // let _ = match x {} - 1; // no parens
24 //
25 // There are 3 distinguishable contexts in which `print_expr` might be
26 // called with the expression `$match` as its argument, where `$match`
27 // represents an expression of kind `ExprKind::Match`:
28 //
29 // - stmt=false leftmost_subexpression_in_stmt=false
30 //
31 // Example: `let _ = $match - 1;`
32 //
33 // No parentheses required.
34 //
35 // - stmt=false leftmost_subexpression_in_stmt=true
36 //
37 // Example: `$match - 1;`
38 //
39 // Must parenthesize `($match)`, otherwise parsing back the output as a
40 // statement would terminate the statement after the closing brace of
41 // the match, parsing `-1;` as a separate statement.
42 //
43 // - stmt=true leftmost_subexpression_in_stmt=false
44 //
45 // Example: `$match;`
46 //
47 // No parentheses required.
48 #[cfg(feature = "full")]
49 leftmost_subexpression_in_stmt: bool,
50
51 // Print expression such that it can be parsed as a match arm.
52 //
53 // This is almost equivalent to `stmt`, but the grammar diverges a tiny bit
54 // between statements and match arms when it comes to braced macro calls.
55 // Macro calls with brace delimiter terminate a statement without a
56 // semicolon, but do not terminate a match-arm without comma.
57 //
58 // m! {} - 1; // two statements: a macro call followed by -1 literal
59 //
60 // match () {
61 // _ => m! {} - 1, // binary subtraction operator
62 // }
63 //
64 #[cfg(feature = "full")]
65 match_arm: bool,
66
67 // This is almost equivalent to `leftmost_subexpression_in_stmt`, other than
68 // for braced macro calls.
69 //
70 // If we have `m! {} - 1` as an expression, the leftmost subexpression
71 // `m! {}` will need to be parenthesized in the statement case but not the
72 // match-arm case.
73 //
74 // (m! {}) - 1; // subexpression needs parens
75 //
76 // match () {
77 // _ => m! {} - 1, // no parens
78 // }
79 //
80 #[cfg(feature = "full")]
81 leftmost_subexpression_in_match_arm: bool,
82
83 // This is the difference between:
84 //
85 // if let _ = (Struct {}) {} // needs parens
86 //
87 // match () {
88 // () if let _ = Struct {} => {} // no parens
89 // }
90 //
91 #[cfg(feature = "full")]
92 parenthesize_exterior_struct_lit: bool,
93
94 // This is the difference between:
95 //
96 // let _ = 1 + return 1; // no parens if rightmost subexpression
97 //
98 // let _ = 1 + (return 1) + 1; // needs parens
99 //
100 #[cfg(feature = "full")]
101 parenthesize_exterior_jump: bool,
102
103 // This is the difference between:
104 //
105 // let _ = (return) - 1; // without paren, this would return -1
106 //
107 // let _ = return + 1; // no paren because '+' cannot begin expr
108 //
109 #[cfg(feature = "full")]
110 next_operator_can_begin_expr: bool,
111
112 // This is the difference between:
113 //
114 // let _ = x as u8 + T;
115 //
116 // let _ = (x as u8) < T;
117 //
118 // Without parens, the latter would want to parse `u8<T...` as a type.
119 next_operator_can_begin_generics: bool,
120}
121
122impl FixupContext {
123 /// The default amount of fixing is minimal fixing. Fixups should be turned
124 /// on in a targeted fashion where needed.
125 pub const NONE: Self = FixupContext {
126 #[cfg(feature = "full")]
127 stmt: false,
128 #[cfg(feature = "full")]
129 leftmost_subexpression_in_stmt: false,
130 #[cfg(feature = "full")]
131 match_arm: false,
132 #[cfg(feature = "full")]
133 leftmost_subexpression_in_match_arm: false,
134 #[cfg(feature = "full")]
135 parenthesize_exterior_struct_lit: false,
136 #[cfg(feature = "full")]
137 parenthesize_exterior_jump: false,
138 #[cfg(feature = "full")]
139 next_operator_can_begin_expr: false,
140 next_operator_can_begin_generics: false,
141 };
142
143 /// Create the initial fixup for printing an expression in statement
144 /// position.
145 #[cfg(feature = "full")]
146 pub fn new_stmt() -> Self {
147 FixupContext {
148 stmt: true,
149 ..FixupContext::NONE
150 }
151 }
152
153 /// Create the initial fixup for printing an expression as the right-hand
154 /// side of a match arm.
155 #[cfg(feature = "full")]
156 pub fn new_match_arm() -> Self {
157 FixupContext {
158 match_arm: true,
159 ..FixupContext::NONE
160 }
161 }
162
163 /// Create the initial fixup for printing an expression as the "condition"
164 /// of an `if` or `while`. There are a few other positions which are
165 /// grammatically equivalent and also use this, such as the iterator
166 /// expression in `for` and the scrutinee in `match`.
167 #[cfg(feature = "full")]
168 pub fn new_condition() -> Self {
169 FixupContext {
170 parenthesize_exterior_struct_lit: true,
171 ..FixupContext::NONE
172 }
173 }
174
175 /// Transform this fixup into the one that should apply when printing the
176 /// leftmost subexpression of the current expression.
177 ///
178 /// The leftmost subexpression is any subexpression that has the same first
179 /// token as the current expression, but has a different last token.
180 ///
181 /// For example in `$a + $b` and `$a.method()`, the subexpression `$a` is a
182 /// leftmost subexpression.
183 ///
184 /// Not every expression has a leftmost subexpression. For example neither
185 /// `-$a` nor `[$a]` have one.
186 pub fn leftmost_subexpression(self) -> Self {
187 FixupContext {
188 #[cfg(feature = "full")]
189 stmt: false,
190 #[cfg(feature = "full")]
191 leftmost_subexpression_in_stmt: self.stmt || self.leftmost_subexpression_in_stmt,
192 #[cfg(feature = "full")]
193 match_arm: false,
194 #[cfg(feature = "full")]
195 leftmost_subexpression_in_match_arm: self.match_arm
196 || self.leftmost_subexpression_in_match_arm,
197 #[cfg(feature = "full")]
198 parenthesize_exterior_jump: true,
199 ..self
200 }
201 }
202
203 /// Transform this fixup into the one that should apply when printing a
204 /// leftmost subexpression followed by a `.` or `?` token, which confer
205 /// different statement boundary rules compared to other leftmost
206 /// subexpressions.
207 pub fn leftmost_subexpression_with_dot(self) -> Self {
208 FixupContext {
209 #[cfg(feature = "full")]
210 stmt: self.stmt || self.leftmost_subexpression_in_stmt,
211 #[cfg(feature = "full")]
212 leftmost_subexpression_in_stmt: false,
213 #[cfg(feature = "full")]
214 match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm,
215 #[cfg(feature = "full")]
216 leftmost_subexpression_in_match_arm: false,
217 #[cfg(feature = "full")]
218 parenthesize_exterior_jump: true,
219 ..self
220 }
221 }
222
223 /// Transform this fixup into the one that should apply when printing a
224 /// leftmost subexpression followed by punctuation that is legal as the
225 /// first token of an expression.
226 pub fn leftmost_subexpression_with_begin_operator(
227 self,
228 #[cfg(feature = "full")] next_operator_can_begin_expr: bool,
229 next_operator_can_begin_generics: bool,
230 ) -> Self {
231 FixupContext {
232 #[cfg(feature = "full")]
233 next_operator_can_begin_expr,
234 next_operator_can_begin_generics,
235 ..self.leftmost_subexpression()
236 }
237 }
238
239 /// Transform this fixup into the one that should apply when printing any
240 /// subexpression that is neither a leftmost subexpression nor surrounded in
241 /// delimiters.
242 ///
243 /// This is for any subexpression that has a different first token than the
244 /// current expression, and is not surrounded by a paren/bracket/brace. For
245 /// example the `$b` in `$a + $b` and `-$b`, but not the one in `[$b]` or
246 /// `$a.f($b)`.
247 pub fn subsequent_subexpression(self) -> Self {
248 FixupContext {
249 #[cfg(feature = "full")]
250 stmt: false,
251 #[cfg(feature = "full")]
252 leftmost_subexpression_in_stmt: false,
253 #[cfg(feature = "full")]
254 match_arm: false,
255 #[cfg(feature = "full")]
256 leftmost_subexpression_in_match_arm: false,
257 ..self
258 }
259 }
260
261 /// Determine whether parentheses are needed around the given expression to
262 /// head off an unintended statement boundary.
263 ///
264 /// The documentation on `FixupContext::leftmost_subexpression_in_stmt` has
265 /// examples.
266 #[cfg(feature = "full")]
267 pub fn would_cause_statement_boundary(self, expr: &Expr) -> bool {
268 (self.leftmost_subexpression_in_stmt && !classify::requires_semi_to_be_stmt(expr))
269 || ((self.stmt || self.leftmost_subexpression_in_stmt) && matches!(expr, Expr::Let(_)))
270 || (self.leftmost_subexpression_in_match_arm
271 && !classify::requires_comma_to_be_match_arm(expr))
272 }
273
274 /// Determine whether parentheses are needed around the given `let`
275 /// scrutinee.
276 ///
277 /// In `if let _ = $e {}`, some examples of `$e` that would need parentheses
278 /// are:
279 ///
280 /// - `Struct {}.f()`, because otherwise the `{` would be misinterpreted
281 /// as the opening of the if's then-block.
282 ///
283 /// - `true && false`, because otherwise this would be misinterpreted as a
284 /// "let chain".
285 #[cfg(feature = "full")]
286 pub fn needs_group_as_let_scrutinee(self, expr: &Expr) -> bool {
287 self.parenthesize_exterior_struct_lit && classify::confusable_with_adjacent_block(expr)
288 || self.trailing_precedence(expr) < Precedence::Let
289 }
290
291 /// Determines the effective precedence of a left subexpression. Some
292 /// expressions have lower precedence when adjacent to particular operators.
293 pub fn leading_precedence(self, expr: &Expr) -> Precedence {
294 #[cfg(feature = "full")]
295 if self.next_operator_can_begin_expr {
296 // Decrease precedence of value-less jumps when followed by an
297 // operator that would otherwise get interpreted as beginning a
298 // value for the jump.
299 if let Expr::Break(_) | Expr::Return(_) | Expr::Yield(_) = expr {
300 return Precedence::Jump;
301 }
302 }
303 self.precedence(expr)
304 }
305
306 /// Determines the effective precedence of a right subexpression. Some
307 /// expressions have higher precedence on the right side of a binary
308 /// operator than on the left.
309 pub fn trailing_precedence(self, expr: &Expr) -> Precedence {
310 #[cfg(feature = "full")]
311 if !self.parenthesize_exterior_jump {
312 match expr {
313 // Increase precedence of expressions that extend to the end of
314 // current statement or group.
315 Expr::Break(_)
316 | Expr::Closure(_)
317 | Expr::Let(_)
318 | Expr::Return(_)
319 | Expr::Yield(_) => {
320 return Precedence::Prefix;
321 }
322 Expr::Range(e) if e.start.is_none() => return Precedence::Prefix,
323 _ => {}
324 }
325 }
326 self.precedence(expr)
327 }
328
329 fn precedence(self, expr: &Expr) -> Precedence {
330 if self.next_operator_can_begin_generics {
331 if let Expr::Cast(cast) = expr {
332 if classify::trailing_unparameterized_path(&cast.ty) {
333 return Precedence::MIN;
334 }
335 }
336 }
337 Precedence::of(expr)
338 }
339}
340
341impl Copy for FixupContext {}
342
343impl Clone for FixupContext {
344 fn clone(&self) -> Self {
345 *self
346 }
347}