prettyplease/
fixup.rs

1use crate::classify;
2use crate::precedence::Precedence;
3use syn::{
4    Expr, ExprBreak, ExprRange, ExprRawAddr, ExprReference, ExprReturn, ExprUnary, ExprYield,
5};
6
7#[derive(Copy, Clone)]
8pub struct FixupContext {
9    previous_operator: Precedence,
10    next_operator: Precedence,
11
12    // Print expression such that it can be parsed back as a statement
13    // consisting of the original expression.
14    //
15    // The effect of this is for binary operators in statement position to set
16    // `leftmost_subexpression_in_stmt` when printing their left-hand operand.
17    //
18    //     (match x {}) - 1;  // match needs parens when LHS of binary operator
19    //
20    //     match x {};  // not when its own statement
21    //
22    stmt: bool,
23
24    // This is the difference between:
25    //
26    //     (match x {}) - 1;  // subexpression needs parens
27    //
28    //     let _ = match x {} - 1;  // no parens
29    //
30    // There are 3 distinguishable contexts in which `print_expr` might be
31    // called with the expression `$match` as its argument, where `$match`
32    // represents an expression of kind `ExprKind::Match`:
33    //
34    //   - stmt=false leftmost_subexpression_in_stmt=false
35    //
36    //     Example: `let _ = $match - 1;`
37    //
38    //     No parentheses required.
39    //
40    //   - stmt=false leftmost_subexpression_in_stmt=true
41    //
42    //     Example: `$match - 1;`
43    //
44    //     Must parenthesize `($match)`, otherwise parsing back the output as a
45    //     statement would terminate the statement after the closing brace of
46    //     the match, parsing `-1;` as a separate statement.
47    //
48    //   - stmt=true leftmost_subexpression_in_stmt=false
49    //
50    //     Example: `$match;`
51    //
52    //     No parentheses required.
53    leftmost_subexpression_in_stmt: bool,
54
55    // Print expression such that it can be parsed as a match arm.
56    //
57    // This is almost equivalent to `stmt`, but the grammar diverges a tiny bit
58    // between statements and match arms when it comes to braced macro calls.
59    // Macro calls with brace delimiter terminate a statement without a
60    // semicolon, but do not terminate a match-arm without comma.
61    //
62    //     m! {} - 1;  // two statements: a macro call followed by -1 literal
63    //
64    //     match () {
65    //         _ => m! {} - 1,  // binary subtraction operator
66    //     }
67    //
68    match_arm: bool,
69
70    // This is almost equivalent to `leftmost_subexpression_in_stmt`, other than
71    // for braced macro calls.
72    //
73    // If we have `m! {} - 1` as an expression, the leftmost subexpression
74    // `m! {}` will need to be parenthesized in the statement case but not the
75    // match-arm case.
76    //
77    //     (m! {}) - 1;  // subexpression needs parens
78    //
79    //     match () {
80    //         _ => m! {} - 1,  // no parens
81    //     }
82    //
83    leftmost_subexpression_in_match_arm: bool,
84
85    // This is the difference between:
86    //
87    //     if let _ = (Struct {}) {}  // needs parens
88    //
89    //     match () {
90    //         () if let _ = Struct {} => {}  // no parens
91    //     }
92    //
93    condition: bool,
94
95    // This is the difference between:
96    //
97    //     if break Struct {} == (break) {}  // needs parens
98    //
99    //     if break break == Struct {} {}  // no parens
100    //
101    rightmost_subexpression_in_condition: bool,
102
103    // This is the difference between:
104    //
105    //     if break ({ x }).field + 1 {}  needs parens
106    //
107    //     if break 1 + { x }.field {}  // no parens
108    //
109    leftmost_subexpression_in_optional_operand: bool,
110
111    // This is the difference between:
112    //
113    //     let _ = (return) - 1;  // without paren, this would return -1
114    //
115    //     let _ = return + 1;  // no paren because '+' cannot begin expr
116    //
117    next_operator_can_begin_expr: bool,
118
119    // This is the difference between:
120    //
121    //     let _ = 1 + return 1;  // no parens if rightmost subexpression
122    //
123    //     let _ = 1 + (return 1) + 1;  // needs parens
124    //
125    next_operator_can_continue_expr: bool,
126
127    // This is the difference between:
128    //
129    //     let _ = x as u8 + T;
130    //
131    //     let _ = (x as u8) < T;
132    //
133    // Without parens, the latter would want to parse `u8<T...` as a type.
134    next_operator_can_begin_generics: bool,
135}
136
137impl FixupContext {
138    /// The default amount of fixing is minimal fixing. Fixups should be turned
139    /// on in a targeted fashion where needed.
140    pub const NONE: Self = FixupContext {
141        previous_operator: Precedence::MIN,
142        next_operator: Precedence::MIN,
143        stmt: false,
144        leftmost_subexpression_in_stmt: false,
145        match_arm: false,
146        leftmost_subexpression_in_match_arm: false,
147        condition: false,
148        rightmost_subexpression_in_condition: false,
149        leftmost_subexpression_in_optional_operand: false,
150        next_operator_can_begin_expr: false,
151        next_operator_can_continue_expr: false,
152        next_operator_can_begin_generics: false,
153    };
154
155    /// Create the initial fixup for printing an expression in statement
156    /// position.
157    pub fn new_stmt() -> Self {
158        FixupContext {
159            stmt: true,
160            ..FixupContext::NONE
161        }
162    }
163
164    /// Create the initial fixup for printing an expression as the right-hand
165    /// side of a match arm.
166    pub fn new_match_arm() -> Self {
167        FixupContext {
168            match_arm: true,
169            ..FixupContext::NONE
170        }
171    }
172
173    /// Create the initial fixup for printing an expression as the "condition"
174    /// of an `if` or `while`. There are a few other positions which are
175    /// grammatically equivalent and also use this, such as the iterator
176    /// expression in `for` and the scrutinee in `match`.
177    pub fn new_condition() -> Self {
178        FixupContext {
179            condition: true,
180            rightmost_subexpression_in_condition: true,
181            ..FixupContext::NONE
182        }
183    }
184
185    /// Transform this fixup into the one that should apply when printing the
186    /// leftmost subexpression of the current expression.
187    ///
188    /// The leftmost subexpression is any subexpression that has the same first
189    /// token as the current expression, but has a different last token.
190    ///
191    /// For example in `$a + $b` and `$a.method()`, the subexpression `$a` is a
192    /// leftmost subexpression.
193    ///
194    /// Not every expression has a leftmost subexpression. For example neither
195    /// `-$a` nor `[$a]` have one.
196    pub fn leftmost_subexpression_with_operator(
197        self,
198        expr: &Expr,
199        next_operator_can_begin_expr: bool,
200        next_operator_can_begin_generics: bool,
201        precedence: Precedence,
202    ) -> (Precedence, Self) {
203        let fixup = FixupContext {
204            next_operator: precedence,
205            stmt: false,
206            leftmost_subexpression_in_stmt: self.stmt || self.leftmost_subexpression_in_stmt,
207            match_arm: false,
208            leftmost_subexpression_in_match_arm: self.match_arm
209                || self.leftmost_subexpression_in_match_arm,
210            rightmost_subexpression_in_condition: false,
211            next_operator_can_begin_expr,
212            next_operator_can_continue_expr: true,
213            next_operator_can_begin_generics,
214            ..self
215        };
216
217        (fixup.leftmost_subexpression_precedence(expr), fixup)
218    }
219
220    /// Transform this fixup into the one that should apply when printing a
221    /// leftmost subexpression followed by a `.` or `?` token, which confer
222    /// different statement boundary rules compared to other leftmost
223    /// subexpressions.
224    pub fn leftmost_subexpression_with_dot(self, expr: &Expr) -> (Precedence, Self) {
225        let fixup = FixupContext {
226            next_operator: Precedence::Unambiguous,
227            stmt: self.stmt || self.leftmost_subexpression_in_stmt,
228            leftmost_subexpression_in_stmt: false,
229            match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm,
230            leftmost_subexpression_in_match_arm: false,
231            rightmost_subexpression_in_condition: false,
232            next_operator_can_begin_expr: false,
233            next_operator_can_continue_expr: true,
234            next_operator_can_begin_generics: false,
235            ..self
236        };
237
238        (fixup.leftmost_subexpression_precedence(expr), fixup)
239    }
240
241    fn leftmost_subexpression_precedence(self, expr: &Expr) -> Precedence {
242        if !self.next_operator_can_begin_expr || self.next_operator == Precedence::Range {
243            if let Scan::Bailout = scan_right(expr, self, Precedence::MIN, 0, 0) {
244                if scan_left(expr, self) {
245                    return Precedence::Unambiguous;
246                }
247            }
248        }
249
250        self.precedence(expr)
251    }
252
253    /// Transform this fixup into the one that should apply when printing the
254    /// rightmost subexpression of the current expression.
255    ///
256    /// The rightmost subexpression is any subexpression that has a different
257    /// first token than the current expression, but has the same last token.
258    ///
259    /// For example in `$a + $b` and `-$b`, the subexpression `$b` is a
260    /// rightmost subexpression.
261    ///
262    /// Not every expression has a rightmost subexpression. For example neither
263    /// `[$b]` nor `$a.f($b)` have one.
264    pub fn rightmost_subexpression(
265        self,
266        expr: &Expr,
267        precedence: Precedence,
268    ) -> (Precedence, Self) {
269        let fixup = self.rightmost_subexpression_fixup(false, false, precedence);
270        (fixup.rightmost_subexpression_precedence(expr), fixup)
271    }
272
273    pub fn rightmost_subexpression_fixup(
274        self,
275        reset_allow_struct: bool,
276        optional_operand: bool,
277        precedence: Precedence,
278    ) -> Self {
279        FixupContext {
280            previous_operator: precedence,
281            stmt: false,
282            leftmost_subexpression_in_stmt: false,
283            match_arm: false,
284            leftmost_subexpression_in_match_arm: false,
285            condition: self.condition && !reset_allow_struct,
286            leftmost_subexpression_in_optional_operand: self.condition && optional_operand,
287            ..self
288        }
289    }
290
291    pub fn rightmost_subexpression_precedence(self, expr: &Expr) -> Precedence {
292        let default_prec = self.precedence(expr);
293
294        if match self.previous_operator {
295            Precedence::Assign | Precedence::Let | Precedence::Prefix => {
296                default_prec < self.previous_operator
297            }
298            _ => default_prec <= self.previous_operator,
299        } && match self.next_operator {
300            Precedence::Range | Precedence::Or | Precedence::And => true,
301            _ => !self.next_operator_can_begin_expr,
302        } {
303            if let Scan::Bailout | Scan::Fail = scan_right(expr, self, self.previous_operator, 1, 0)
304            {
305                if scan_left(expr, self) {
306                    return Precedence::Prefix;
307                }
308            }
309        }
310
311        default_prec
312    }
313
314    /// Determine whether parentheses are needed around the given expression to
315    /// head off the early termination of a statement or condition.
316    pub fn parenthesize(self, expr: &Expr) -> bool {
317        (self.leftmost_subexpression_in_stmt && !classify::requires_semi_to_be_stmt(expr))
318            || ((self.stmt || self.leftmost_subexpression_in_stmt) && matches!(expr, Expr::Let(_)))
319            || (self.leftmost_subexpression_in_match_arm
320                && !classify::requires_comma_to_be_match_arm(expr))
321            || (self.condition && matches!(expr, Expr::Struct(_)))
322            || (self.rightmost_subexpression_in_condition
323                && matches!(
324                    expr,
325                    Expr::Return(ExprReturn { expr: None, .. })
326                        | Expr::Yield(ExprYield { expr: None, .. })
327                ))
328            || (self.rightmost_subexpression_in_condition
329                && !self.condition
330                && matches!(
331                    expr,
332                    Expr::Break(ExprBreak { expr: None, .. })
333                        | Expr::Path(_)
334                        | Expr::Range(ExprRange { end: None, .. })
335                ))
336            || (self.leftmost_subexpression_in_optional_operand
337                && matches!(expr, Expr::Block(expr) if expr.attrs.is_empty() && expr.label.is_none()))
338    }
339
340    /// Determines the effective precedence of a subexpression. Some expressions
341    /// have higher or lower precedence when adjacent to particular operators.
342    fn precedence(self, expr: &Expr) -> Precedence {
343        if self.next_operator_can_begin_expr {
344            // Decrease precedence of value-less jumps when followed by an
345            // operator that would otherwise get interpreted as beginning a
346            // value for the jump.
347            if let Expr::Break(ExprBreak { expr: None, .. })
348            | Expr::Return(ExprReturn { expr: None, .. })
349            | Expr::Yield(ExprYield { expr: None, .. }) = expr
350            {
351                return Precedence::Jump;
352            }
353        }
354
355        if !self.next_operator_can_continue_expr {
356            match expr {
357                // Increase precedence of expressions that extend to the end of
358                // current statement or group.
359                Expr::Break(_)
360                | Expr::Closure(_)
361                | Expr::Let(_)
362                | Expr::Return(_)
363                | Expr::Yield(_) => {
364                    return Precedence::Prefix;
365                }
366                Expr::Range(e) if e.start.is_none() => return Precedence::Prefix,
367                _ => {}
368            }
369        }
370
371        if self.next_operator_can_begin_generics {
372            if let Expr::Cast(cast) = expr {
373                if classify::trailing_unparameterized_path(&cast.ty) {
374                    return Precedence::MIN;
375                }
376            }
377        }
378
379        Precedence::of(expr)
380    }
381}
382
383#[derive(Copy, Clone, PartialEq)]
384enum Scan {
385    Fail,
386    Bailout,
387    Consume,
388}
389
390fn scan_left(expr: &Expr, fixup: FixupContext) -> bool {
391    match expr {
392        Expr::Assign(_) => fixup.previous_operator <= Precedence::Assign,
393        Expr::Binary(e) => match Precedence::of_binop(&e.op) {
394            Precedence::Assign => fixup.previous_operator <= Precedence::Assign,
395            binop_prec => fixup.previous_operator < binop_prec,
396        },
397        Expr::Cast(_) => fixup.previous_operator < Precedence::Cast,
398        Expr::Range(e) => e.start.is_none() || fixup.previous_operator < Precedence::Assign,
399        _ => true,
400    }
401}
402
403fn scan_right(
404    expr: &Expr,
405    fixup: FixupContext,
406    precedence: Precedence,
407    fail_offset: u8,
408    bailout_offset: u8,
409) -> Scan {
410    let consume_by_precedence = if match precedence {
411        Precedence::Assign | Precedence::Compare => precedence <= fixup.next_operator,
412        _ => precedence < fixup.next_operator,
413    } || fixup.next_operator == Precedence::MIN
414    {
415        Scan::Consume
416    } else {
417        Scan::Bailout
418    };
419    if fixup.parenthesize(expr) {
420        return consume_by_precedence;
421    }
422    match expr {
423        #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
424        Expr::Assign(e) => {
425            if match fixup.next_operator {
426                Precedence::Unambiguous => fail_offset >= 2,
427                _ => bailout_offset >= 1,
428            } {
429                return Scan::Consume;
430            }
431            let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Assign);
432            let scan = scan_right(
433                &e.right,
434                right_fixup,
435                Precedence::Assign,
436                match fixup.next_operator {
437                    Precedence::Unambiguous => fail_offset,
438                    _ => 1,
439                },
440                1,
441            );
442            if let Scan::Bailout | Scan::Consume = scan {
443                Scan::Consume
444            } else if let Precedence::Unambiguous = fixup.next_operator {
445                Scan::Fail
446            } else {
447                Scan::Bailout
448            }
449        }
450        Expr::Binary(e) => {
451            if match fixup.next_operator {
452                Precedence::Unambiguous => {
453                    fail_offset >= 2
454                        && (consume_by_precedence == Scan::Consume || bailout_offset >= 1)
455                }
456                _ => bailout_offset >= 1,
457            } {
458                return Scan::Consume;
459            }
460            let binop_prec = Precedence::of_binop(&e.op);
461            if binop_prec == Precedence::Compare && fixup.next_operator == Precedence::Compare {
462                return Scan::Consume;
463            }
464            let right_fixup = fixup.rightmost_subexpression_fixup(false, false, binop_prec);
465            let scan = scan_right(
466                &e.right,
467                right_fixup,
468                binop_prec,
469                match fixup.next_operator {
470                    Precedence::Unambiguous => fail_offset,
471                    _ => 1,
472                },
473                consume_by_precedence as u8 - Scan::Bailout as u8,
474            );
475            match scan {
476                Scan::Fail => {}
477                Scan::Bailout => return consume_by_precedence,
478                Scan::Consume => return Scan::Consume,
479            }
480            let right_needs_group = binop_prec != Precedence::Assign
481                && right_fixup.rightmost_subexpression_precedence(&e.right) <= binop_prec;
482            if right_needs_group {
483                consume_by_precedence
484            } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) {
485                Scan::Fail
486            } else {
487                Scan::Bailout
488            }
489        }
490        Expr::RawAddr(ExprRawAddr { expr, .. })
491        | Expr::Reference(ExprReference { expr, .. })
492        | Expr::Unary(ExprUnary { expr, .. }) => {
493            if match fixup.next_operator {
494                Precedence::Unambiguous => {
495                    fail_offset >= 2
496                        && (consume_by_precedence == Scan::Consume || bailout_offset >= 1)
497                }
498                _ => bailout_offset >= 1,
499            } {
500                return Scan::Consume;
501            }
502            let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Prefix);
503            let scan = scan_right(
504                expr,
505                right_fixup,
506                precedence,
507                match fixup.next_operator {
508                    Precedence::Unambiguous => fail_offset,
509                    _ => 1,
510                },
511                consume_by_precedence as u8 - Scan::Bailout as u8,
512            );
513            match scan {
514                Scan::Fail => {}
515                Scan::Bailout => return consume_by_precedence,
516                Scan::Consume => return Scan::Consume,
517            }
518            if right_fixup.rightmost_subexpression_precedence(expr) < Precedence::Prefix {
519                consume_by_precedence
520            } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) {
521                Scan::Fail
522            } else {
523                Scan::Bailout
524            }
525        }
526        Expr::Range(e) => match &e.end {
527            Some(end) => {
528                if fail_offset >= 2 {
529                    return Scan::Consume;
530                }
531                let right_fixup =
532                    fixup.rightmost_subexpression_fixup(false, true, Precedence::Range);
533                let scan = scan_right(
534                    end,
535                    right_fixup,
536                    Precedence::Range,
537                    fail_offset,
538                    match fixup.next_operator {
539                        Precedence::Assign | Precedence::Range => 0,
540                        _ => 1,
541                    },
542                );
543                if match (scan, fixup.next_operator) {
544                    (Scan::Fail, _) => false,
545                    (Scan::Bailout, Precedence::Assign | Precedence::Range) => false,
546                    (Scan::Bailout | Scan::Consume, _) => true,
547                } {
548                    return Scan::Consume;
549                }
550                if right_fixup.rightmost_subexpression_precedence(end) <= Precedence::Range {
551                    Scan::Consume
552                } else {
553                    Scan::Fail
554                }
555            }
556            None => {
557                if fixup.next_operator_can_begin_expr {
558                    Scan::Consume
559                } else {
560                    Scan::Fail
561                }
562            }
563        },
564        Expr::Break(e) => match &e.expr {
565            Some(value) => {
566                if bailout_offset >= 1 || e.label.is_none() && classify::expr_leading_label(value) {
567                    return Scan::Consume;
568                }
569                let right_fixup = fixup.rightmost_subexpression_fixup(true, true, Precedence::Jump);
570                match scan_right(value, right_fixup, Precedence::Jump, 1, 1) {
571                    Scan::Fail => Scan::Bailout,
572                    Scan::Bailout | Scan::Consume => Scan::Consume,
573                }
574            }
575            None => match fixup.next_operator {
576                Precedence::Assign if precedence > Precedence::Assign => Scan::Fail,
577                _ => Scan::Consume,
578            },
579        },
580        Expr::Return(ExprReturn { expr, .. }) | Expr::Yield(ExprYield { expr, .. }) => match expr {
581            Some(e) => {
582                if bailout_offset >= 1 {
583                    return Scan::Consume;
584                }
585                let right_fixup =
586                    fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump);
587                match scan_right(e, right_fixup, Precedence::Jump, 1, 1) {
588                    Scan::Fail => Scan::Bailout,
589                    Scan::Bailout | Scan::Consume => Scan::Consume,
590                }
591            }
592            None => match fixup.next_operator {
593                Precedence::Assign if precedence > Precedence::Assign => Scan::Fail,
594                _ => Scan::Consume,
595            },
596        },
597        Expr::Closure(_) => Scan::Consume,
598        Expr::Let(e) => {
599            if bailout_offset >= 1 {
600                return Scan::Consume;
601            }
602            let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Let);
603            let scan = scan_right(
604                &e.expr,
605                right_fixup,
606                Precedence::Let,
607                1,
608                if fixup.next_operator < Precedence::Let {
609                    0
610                } else {
611                    1
612                },
613            );
614            match scan {
615                Scan::Fail | Scan::Bailout if fixup.next_operator < Precedence::Let => {
616                    return Scan::Bailout;
617                }
618                Scan::Consume => return Scan::Consume,
619                _ => {}
620            }
621            if right_fixup.rightmost_subexpression_precedence(&e.expr) < Precedence::Let {
622                Scan::Consume
623            } else if let Scan::Fail = scan {
624                Scan::Bailout
625            } else {
626                Scan::Consume
627            }
628        }
629        Expr::Group(e) => scan_right(&e.expr, fixup, precedence, fail_offset, bailout_offset),
630        Expr::Array(_)
631        | Expr::Async(_)
632        | Expr::Await(_)
633        | Expr::Block(_)
634        | Expr::Call(_)
635        | Expr::Cast(_)
636        | Expr::Const(_)
637        | Expr::Continue(_)
638        | Expr::Field(_)
639        | Expr::ForLoop(_)
640        | Expr::If(_)
641        | Expr::Index(_)
642        | Expr::Infer(_)
643        | Expr::Lit(_)
644        | Expr::Loop(_)
645        | Expr::Macro(_)
646        | Expr::Match(_)
647        | Expr::MethodCall(_)
648        | Expr::Paren(_)
649        | Expr::Path(_)
650        | Expr::Repeat(_)
651        | Expr::Struct(_)
652        | Expr::Try(_)
653        | Expr::TryBlock(_)
654        | Expr::Tuple(_)
655        | Expr::Unsafe(_)
656        | Expr::Verbatim(_)
657        | Expr::While(_) => match fixup.next_operator {
658            Precedence::Assign | Precedence::Range if precedence == Precedence::Range => Scan::Fail,
659            _ if precedence == Precedence::Let && fixup.next_operator < Precedence::Let => {
660                Scan::Fail
661            }
662            _ => consume_by_precedence,
663        },
664
665        _ => match fixup.next_operator {
666            Precedence::Assign | Precedence::Range if precedence == Precedence::Range => Scan::Fail,
667            _ if precedence == Precedence::Let && fixup.next_operator < Precedence::Let => {
668                Scan::Fail
669            }
670            _ => consume_by_precedence,
671        },
672    }
673}