1use crate::classify;
2use crate::precedence::Precedence;
3use syn::{
4 Expr, ExprBreak, ExprRange, ExprRawAddr, ExprReference, ExprReturn, ExprUnary, ExprYield,
5 ReturnType,
6};
7
8#[derive(Copy, Clone)]
9pub struct FixupContext {
10 previous_operator: Precedence,
11 next_operator: Precedence,
12
13 stmt: bool,
24
25 leftmost_subexpression_in_stmt: bool,
55
56 match_arm: bool,
70
71 leftmost_subexpression_in_match_arm: bool,
85
86 condition: bool,
95
96 rightmost_subexpression_in_condition: bool,
103
104 leftmost_subexpression_in_optional_operand: bool,
111
112 next_operator_can_begin_expr: bool,
119
120 next_operator_can_continue_expr: bool,
127
128 next_operator_can_begin_generics: bool,
136}
137
138impl FixupContext {
139 pub const NONE: Self = FixupContext {
142 previous_operator: Precedence::MIN,
143 next_operator: Precedence::MIN,
144 stmt: false,
145 leftmost_subexpression_in_stmt: false,
146 match_arm: false,
147 leftmost_subexpression_in_match_arm: false,
148 condition: false,
149 rightmost_subexpression_in_condition: false,
150 leftmost_subexpression_in_optional_operand: false,
151 next_operator_can_begin_expr: false,
152 next_operator_can_continue_expr: false,
153 next_operator_can_begin_generics: false,
154 };
155
156 pub fn new_stmt() -> Self {
159 FixupContext {
160 stmt: true,
161 ..FixupContext::NONE
162 }
163 }
164
165 pub fn new_match_arm() -> Self {
168 FixupContext {
169 match_arm: true,
170 ..FixupContext::NONE
171 }
172 }
173
174 pub fn new_condition() -> Self {
179 FixupContext {
180 condition: true,
181 rightmost_subexpression_in_condition: true,
182 ..FixupContext::NONE
183 }
184 }
185
186 pub fn leftmost_subexpression_with_operator(
198 self,
199 expr: &Expr,
200 next_operator_can_begin_expr: bool,
201 next_operator_can_begin_generics: bool,
202 precedence: Precedence,
203 ) -> (Precedence, Self) {
204 let fixup = FixupContext {
205 next_operator: precedence,
206 stmt: false,
207 leftmost_subexpression_in_stmt: self.stmt || self.leftmost_subexpression_in_stmt,
208 match_arm: false,
209 leftmost_subexpression_in_match_arm: self.match_arm
210 || self.leftmost_subexpression_in_match_arm,
211 rightmost_subexpression_in_condition: false,
212 next_operator_can_begin_expr,
213 next_operator_can_continue_expr: true,
214 next_operator_can_begin_generics,
215 ..self
216 };
217
218 (fixup.leftmost_subexpression_precedence(expr), fixup)
219 }
220
221 pub fn leftmost_subexpression_with_dot(self, expr: &Expr) -> (Precedence, Self) {
226 let fixup = FixupContext {
227 next_operator: Precedence::Unambiguous,
228 stmt: self.stmt || self.leftmost_subexpression_in_stmt,
229 leftmost_subexpression_in_stmt: false,
230 match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm,
231 leftmost_subexpression_in_match_arm: false,
232 rightmost_subexpression_in_condition: false,
233 next_operator_can_begin_expr: false,
234 next_operator_can_continue_expr: true,
235 next_operator_can_begin_generics: false,
236 ..self
237 };
238
239 (fixup.leftmost_subexpression_precedence(expr), fixup)
240 }
241
242 fn leftmost_subexpression_precedence(self, expr: &Expr) -> Precedence {
243 if !self.next_operator_can_begin_expr || self.next_operator == Precedence::Range {
244 if let Scan::Bailout = scan_right(expr, self, false, 0, 0) {
245 if scan_left(expr, self) {
246 return Precedence::Unambiguous;
247 }
248 }
249 }
250
251 self.precedence(expr)
252 }
253
254 pub fn rightmost_subexpression(
266 self,
267 expr: &Expr,
268 precedence: Precedence,
269 ) -> (Precedence, Self) {
270 let fixup = self.rightmost_subexpression_fixup(false, false, precedence);
271 (fixup.rightmost_subexpression_precedence(expr), fixup)
272 }
273
274 pub fn rightmost_subexpression_fixup(
275 self,
276 reset_allow_struct: bool,
277 optional_operand: bool,
278 precedence: Precedence,
279 ) -> Self {
280 FixupContext {
281 previous_operator: precedence,
282 stmt: false,
283 leftmost_subexpression_in_stmt: false,
284 match_arm: false,
285 leftmost_subexpression_in_match_arm: false,
286 condition: self.condition && !reset_allow_struct,
287 leftmost_subexpression_in_optional_operand: self.condition && optional_operand,
288 ..self
289 }
290 }
291
292 pub fn rightmost_subexpression_precedence(self, expr: &Expr) -> Precedence {
293 let default_prec = self.precedence(expr);
294
295 if default_prec < Precedence::Prefix
296 && (!self.next_operator_can_begin_expr || self.next_operator == Precedence::Range)
297 {
298 if let Scan::Bailout | Scan::Fail = scan_right(
299 expr,
300 self,
301 self.previous_operator == Precedence::Range,
302 1,
303 0,
304 ) {
305 if scan_left(expr, self) {
306 return Precedence::Prefix;
307 }
308 }
309 }
310
311 default_prec
312 }
313
314 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 fn precedence(self, expr: &Expr) -> Precedence {
343 if self.next_operator_can_begin_expr {
344 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 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)]
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::Range(e) => e.start.is_none() || fixup.previous_operator < Precedence::Assign,
398 _ => true,
399 }
400}
401
402fn scan_right(
403 expr: &Expr,
404 fixup: FixupContext,
405 range: bool,
406 fail_offset: u8,
407 bailout_offset: u8,
408) -> Scan {
409 if fixup.parenthesize(expr) {
410 return Scan::Consume;
411 }
412 match expr {
413 #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
414 Expr::Assign(e) => {
415 if match fixup.next_operator {
416 Precedence::Unambiguous => fail_offset >= 2,
417 _ => bailout_offset >= 1,
418 } {
419 return Scan::Consume;
420 }
421 let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Assign);
422 let scan = scan_right(
423 &e.right,
424 right_fixup,
425 false,
426 match fixup.next_operator {
427 Precedence::Unambiguous => fail_offset,
428 _ => 1,
429 },
430 1,
431 );
432 if let Scan::Bailout | Scan::Consume = scan {
433 return Scan::Consume;
434 }
435 if right_fixup.rightmost_subexpression_precedence(&e.right) < Precedence::Assign {
436 Scan::Consume
437 } else if let Precedence::Unambiguous = fixup.next_operator {
438 Scan::Fail
439 } else {
440 Scan::Bailout
441 }
442 }
443 Expr::Binary(e) => {
444 if match fixup.next_operator {
445 Precedence::Unambiguous => fail_offset >= 2,
446 _ => bailout_offset >= 1,
447 } {
448 return Scan::Consume;
449 }
450 let binop_prec = Precedence::of_binop(&e.op);
451 let right_fixup = fixup.rightmost_subexpression_fixup(false, false, binop_prec);
452 let scan = scan_right(
453 &e.right,
454 right_fixup,
455 range && binop_prec != Precedence::Assign,
456 match fixup.next_operator {
457 Precedence::Unambiguous => fail_offset,
458 _ => 1,
459 },
460 match (binop_prec, fixup.next_operator) {
461 (Precedence::Assign, _) => 1,
462 (_, Precedence::Assign | Precedence::Range) if range => 0,
463 _ => 1,
464 },
465 );
466 if match (scan, fixup.next_operator) {
467 (Scan::Fail, _) => false,
468 (Scan::Bailout, _) if binop_prec == Precedence::Assign => true,
469 (Scan::Bailout, Precedence::Assign | Precedence::Range) => !range,
470 (Scan::Bailout | Scan::Consume, _) => true,
471 } {
472 return Scan::Consume;
473 }
474 let right_prec = right_fixup.rightmost_subexpression_precedence(&e.right);
475 let right_needs_group = match binop_prec {
476 Precedence::Assign => right_prec < binop_prec,
477 _ => right_prec <= binop_prec,
478 };
479 if right_needs_group {
480 Scan::Consume
481 } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) {
482 Scan::Fail
483 } else {
484 Scan::Bailout
485 }
486 }
487 Expr::RawAddr(ExprRawAddr { expr, .. })
488 | Expr::Reference(ExprReference { expr, .. })
489 | Expr::Unary(ExprUnary { expr, .. }) => {
490 if match fixup.next_operator {
491 Precedence::Unambiguous => fail_offset >= 2,
492 _ => bailout_offset >= 1,
493 } {
494 return Scan::Consume;
495 }
496 let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Prefix);
497 let scan = scan_right(
498 expr,
499 right_fixup,
500 range,
501 match fixup.next_operator {
502 Precedence::Unambiguous => fail_offset,
503 _ => 1,
504 },
505 match fixup.next_operator {
506 Precedence::Assign | Precedence::Range if range => 0,
507 _ => 1,
508 },
509 );
510 if match (scan, fixup.next_operator) {
511 (Scan::Fail, _) => false,
512 (Scan::Bailout, Precedence::Assign | Precedence::Range) => !range,
513 (Scan::Bailout | Scan::Consume, _) => true,
514 } {
515 return Scan::Consume;
516 }
517 if right_fixup.rightmost_subexpression_precedence(expr) < Precedence::Prefix {
518 Scan::Consume
519 } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) {
520 Scan::Fail
521 } else {
522 Scan::Bailout
523 }
524 }
525 Expr::Range(e) => match &e.end {
526 Some(end) => {
527 if fail_offset >= 2 {
528 return Scan::Consume;
529 }
530 let right_fixup =
531 fixup.rightmost_subexpression_fixup(false, true, Precedence::Range);
532 let scan = scan_right(
533 end,
534 right_fixup,
535 true,
536 fail_offset,
537 match fixup.next_operator {
538 Precedence::Assign | Precedence::Range => 0,
539 _ => 1,
540 },
541 );
542 if match (scan, fixup.next_operator) {
543 (Scan::Fail, _) => false,
544 (Scan::Bailout, Precedence::Assign | Precedence::Range) => false,
545 (Scan::Bailout | Scan::Consume, _) => true,
546 } {
547 return Scan::Consume;
548 }
549 if right_fixup.rightmost_subexpression_precedence(end) <= Precedence::Range {
550 Scan::Consume
551 } else {
552 Scan::Fail
553 }
554 }
555 None => match fixup.next_operator {
556 Precedence::Range => Scan::Consume,
557 _ => Scan::Fail,
558 },
559 },
560 Expr::Break(e) => match &e.expr {
561 Some(value) => {
562 if bailout_offset >= 1 || e.label.is_none() && classify::expr_leading_label(value) {
563 return Scan::Consume;
564 }
565 let right_fixup = fixup.rightmost_subexpression_fixup(true, true, Precedence::Jump);
566 match scan_right(value, right_fixup, false, 1, 1) {
567 Scan::Fail => Scan::Bailout,
568 Scan::Bailout | Scan::Consume => Scan::Consume,
569 }
570 }
571 None => match fixup.next_operator {
572 Precedence::Assign if range => Scan::Fail,
573 _ => Scan::Consume,
574 },
575 },
576 Expr::Return(ExprReturn { expr, .. }) | Expr::Yield(ExprYield { expr, .. }) => match expr {
577 Some(e) => {
578 if bailout_offset >= 1 {
579 return Scan::Consume;
580 }
581 let right_fixup =
582 fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump);
583 match scan_right(e, right_fixup, false, 1, 1) {
584 Scan::Fail => Scan::Bailout,
585 Scan::Bailout | Scan::Consume => Scan::Consume,
586 }
587 }
588 None => match fixup.next_operator {
589 Precedence::Assign if range => Scan::Fail,
590 _ => Scan::Consume,
591 },
592 },
593 #[cfg_attr(all(test, exhaustive), allow(non_exhaustive_omitted_patterns))]
595 Expr::Closure(e) => {
596 if matches!(e.output, ReturnType::Default)
597 || matches!(&*e.body, Expr::Block(body) if body.attrs.is_empty() && body.label.is_none())
598 {
599 if bailout_offset >= 1 {
600 return Scan::Consume;
601 }
602 let right_fixup =
603 fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump);
604 match scan_right(&e.body, right_fixup, false, 1, 1) {
605 Scan::Fail => Scan::Bailout,
606 Scan::Bailout | Scan::Consume => Scan::Consume,
607 }
608 } else {
609 Scan::Consume
610 }
611 }
612 Expr::Group(e) => scan_right(&e.expr, fixup, range, fail_offset, bailout_offset),
613 Expr::Array(_)
614 | Expr::Async(_)
615 | Expr::Await(_)
616 | Expr::Block(_)
617 | Expr::Call(_)
618 | Expr::Cast(_)
619 | Expr::Const(_)
620 | Expr::Continue(_)
621 | Expr::Field(_)
622 | Expr::ForLoop(_)
623 | Expr::If(_)
624 | Expr::Index(_)
625 | Expr::Infer(_)
626 | Expr::Let(_)
627 | Expr::Lit(_)
628 | Expr::Loop(_)
629 | Expr::Macro(_)
630 | Expr::Match(_)
631 | Expr::MethodCall(_)
632 | Expr::Paren(_)
633 | Expr::Path(_)
634 | Expr::Repeat(_)
635 | Expr::Struct(_)
636 | Expr::Try(_)
637 | Expr::TryBlock(_)
638 | Expr::Tuple(_)
639 | Expr::Unsafe(_)
640 | Expr::Verbatim(_)
641 | Expr::While(_) => match fixup.next_operator {
642 Precedence::Assign | Precedence::Range if range => Scan::Fail,
643 _ => Scan::Consume,
644 },
645
646 _ => match fixup.next_operator {
647 Precedence::Assign | Precedence::Range if range => Scan::Fail,
648 _ => Scan::Consume,
649 },
650 }
651}