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}