• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::classify;
2 use crate::precedence::Precedence;
3 use syn::{
4     Expr, ExprBreak, ExprRange, ExprRawAddr, ExprReference, ExprReturn, ExprUnary, ExprYield,
5 };
6 
7 #[derive(Copy, Clone)]
8 pub 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 
137 impl 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.
new_stmt() -> Self157     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.
new_match_arm() -> Self166     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`.
new_condition() -> Self177     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.
leftmost_subexpression_with_operator( self, expr: &Expr, next_operator_can_begin_expr: bool, next_operator_can_begin_generics: bool, precedence: Precedence, ) -> (Precedence, Self)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.
leftmost_subexpression_with_dot(self, expr: &Expr) -> (Precedence, Self)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 
leftmost_subexpression_precedence(self, expr: &Expr) -> Precedence241     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.
rightmost_subexpression( self, expr: &Expr, precedence: Precedence, ) -> (Precedence, Self)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 
rightmost_subexpression_fixup( self, reset_allow_struct: bool, optional_operand: bool, precedence: Precedence, ) -> Self273     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 
rightmost_subexpression_precedence(self, expr: &Expr) -> Precedence291     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.
parenthesize(self, expr: &Expr) -> bool316     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.
precedence(self, expr: &Expr) -> Precedence342     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)]
384 enum Scan {
385     Fail,
386     Bailout,
387     Consume,
388 }
389 
scan_left(expr: &Expr, fixup: FixupContext) -> bool390 fn 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 
scan_right( expr: &Expr, fixup: FixupContext, precedence: Precedence, fail_offset: u8, bailout_offset: u8, ) -> Scan403 fn 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 }
674