Skip to main content

syn/
fixup.rs

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