diff --git a/src/classify.rs b/src/classify.rs index cc176d06d..ab735f142 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -3,11 +3,9 @@ use crate::generics::TypeParamBound; use crate::path::{Path, PathArguments}; use crate::punctuated::Punctuated; use crate::ty::{ReturnType, Type}; -#[cfg(feature = "full")] use proc_macro2::{Delimiter, TokenStream, TokenTree}; use std::ops::ControlFlow; -#[cfg(feature = "full")] pub(crate) fn requires_semi_to_be_stmt(expr: &Expr) -> bool { match expr { Expr::Macro(expr) => !expr.mac.delimiter.is_brace(), @@ -15,7 +13,6 @@ pub(crate) fn requires_semi_to_be_stmt(expr: &Expr) -> bool { } } -#[cfg(feature = "full")] pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool { match expr { Expr::If(_) @@ -60,7 +57,7 @@ pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool { } } -#[cfg(all(feature = "printing", feature = "full"))] +#[cfg(feature = "printing")] pub(crate) fn confusable_with_adjacent_block(mut expr: &Expr) -> bool { let mut stack = Vec::new(); @@ -146,84 +143,37 @@ pub(crate) fn confusable_with_adjacent_block(mut expr: &Expr) -> bool { } #[cfg(feature = "printing")] -pub(crate) fn confusable_with_adjacent_lt(mut expr: &Expr) -> bool { +pub(crate) fn trailing_unparameterized_path(mut ty: &Type) -> bool { loop { - match expr { - Expr::Binary(e) => expr = &e.right, - Expr::Cast(e) => return trailing_unparameterized_path(&e.ty), - Expr::Reference(e) => expr = &e.expr, - Expr::Unary(e) => expr = &e.expr, - - Expr::Array(_) - | Expr::Assign(_) - | Expr::Async(_) - | Expr::Await(_) - | Expr::Block(_) - | Expr::Break(_) - | Expr::Call(_) - | Expr::Closure(_) - | Expr::Const(_) - | Expr::Continue(_) - | Expr::Field(_) - | Expr::ForLoop(_) - | Expr::Group(_) - | Expr::If(_) - | Expr::Index(_) - | Expr::Infer(_) - | Expr::Let(_) - | Expr::Lit(_) - | Expr::Loop(_) - | Expr::Macro(_) - | Expr::Match(_) - | Expr::MethodCall(_) - | Expr::Paren(_) - | Expr::Path(_) - | Expr::Range(_) - | Expr::Repeat(_) - | Expr::Return(_) - | Expr::Struct(_) - | Expr::Try(_) - | Expr::TryBlock(_) - | Expr::Tuple(_) - | Expr::Unsafe(_) - | Expr::Verbatim(_) - | Expr::While(_) - | Expr::Yield(_) => return false, - } - } - - fn trailing_unparameterized_path(mut ty: &Type) -> bool { - loop { - match ty { - Type::BareFn(t) => match &t.output { - ReturnType::Default => return false, - ReturnType::Type(_, ret) => ty = ret, - }, - Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) { - ControlFlow::Break(trailing_path) => return trailing_path, - ControlFlow::Continue(t) => ty = t, - }, - Type::Path(t) => match last_type_in_path(&t.path) { - ControlFlow::Break(trailing_path) => return trailing_path, - ControlFlow::Continue(t) => ty = t, - }, - Type::Ptr(t) => ty = &t.elem, - Type::Reference(t) => ty = &t.elem, - Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) { - ControlFlow::Break(trailing_path) => return trailing_path, - ControlFlow::Continue(t) => ty = t, - }, + match ty { + Type::BareFn(t) => match &t.output { + ReturnType::Default => return false, + ReturnType::Type(_, ret) => ty = ret, + }, + Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) { + ControlFlow::Break(trailing_path) => return trailing_path, + ControlFlow::Continue(t) => ty = t, + }, + Type::Path(t) => match last_type_in_path(&t.path) { + ControlFlow::Break(trailing_path) => return trailing_path, + ControlFlow::Continue(t) => ty = t, + }, + Type::Ptr(t) => ty = &t.elem, + Type::Reference(t) => ty = &t.elem, + Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) { + ControlFlow::Break(trailing_path) => return trailing_path, + ControlFlow::Continue(t) => ty = t, + }, - Type::Array(_) - | Type::Group(_) - | Type::Infer(_) - | Type::Macro(_) - | Type::Never(_) - | Type::Paren(_) - | Type::Slice(_) - | Type::Tuple(_) - | Type::Verbatim(_) => return false, - } + Type::Array(_) + | Type::Group(_) + | Type::Infer(_) + | Type::Macro(_) + | Type::Never(_) + | Type::Paren(_) + | Type::Slice(_) + | Type::Tuple(_) + | Type::Verbatim(_) => return false, } } @@ -249,7 +199,7 @@ pub(crate) fn confusable_with_adjacent_lt(mut expr: &Expr) -> bool { } /// Whether the expression's first token is the label of a loop/block. -#[cfg(all(feature = "printing", feature = "full"))] +#[cfg(feature = "printing")] pub(crate) fn expr_leading_label(mut expr: &Expr) -> bool { loop { match expr { @@ -302,7 +252,6 @@ pub(crate) fn expr_leading_label(mut expr: &Expr) -> bool { } /// Whether the expression's last token is `}`. -#[cfg(feature = "full")] pub(crate) fn expr_trailing_brace(mut expr: &Expr) -> bool { loop { match expr { diff --git a/src/expr.rs b/src/expr.rs index 80a4b1a1e..c130fa928 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -2981,6 +2981,7 @@ pub(crate) mod printing { use crate::attr::Attribute; #[cfg(feature = "full")] use crate::attr::FilterAttrs; + #[cfg(feature = "full")] use crate::classify; #[cfg(feature = "full")] use crate::expr::{ @@ -2996,6 +2997,7 @@ pub(crate) mod printing { }; #[cfg(feature = "full")] use crate::fixup::FixupContext; + #[cfg(feature = "full")] use crate::op::BinOp; use crate::path; use crate::precedence::Precedence; @@ -3222,17 +3224,23 @@ pub(crate) mod printing { outer_attrs_to_tokens(&e.attrs, tokens); #[cfg(feature = "full")] - let left_fixup = fixup.leftmost_subexpression_with_begin_operator(match &e.op { - BinOp::Sub(_) - | BinOp::Mul(_) - | BinOp::And(_) - | BinOp::Or(_) - | BinOp::BitAnd(_) - | BinOp::BitOr(_) - | BinOp::Shl(_) - | BinOp::Lt(_) => true, - _ => false, - }); + let left_fixup = fixup.leftmost_subexpression_with_begin_operator( + match &e.op { + BinOp::Sub(_) + | BinOp::Mul(_) + | BinOp::And(_) + | BinOp::Or(_) + | BinOp::BitAnd(_) + | BinOp::BitOr(_) + | BinOp::Shl(_) + | BinOp::Lt(_) => true, + _ => false, + }, + match &e.op { + BinOp::Shl(_) | BinOp::Lt(_) => true, + _ => false, + }, + ); let left_prec = leading_precedence( &e.left, #[cfg(feature = "full")] @@ -3246,21 +3254,12 @@ pub(crate) mod printing { ); let binop_prec = Precedence::of_binop(&e.op); - let (mut left_needs_group, right_needs_group) = match binop_prec { + let (left_needs_group, right_needs_group) = match binop_prec { Precedence::Assign => (left_prec <= Precedence::Range, right_prec < binop_prec), Precedence::Compare => (left_prec <= binop_prec, right_prec <= binop_prec), _ => (left_prec < binop_prec, right_prec <= binop_prec), }; - // These cases require parenthesization independently of precedence. - if let BinOp::Lt(_) | BinOp::Shl(_) = &e.op { - // `x as i32 < y` has the parser thinking that `i32 < y` is the - // beginning of a path type. It starts trying to parse `x as (i32 < - // y ...` instead of `(x as i32) < ...`. We need to convince it - // _not_ to do that. - left_needs_group |= classify::confusable_with_adjacent_lt(&e.left); - } - print_subexpression( &e.left, left_needs_group, @@ -3341,7 +3340,7 @@ pub(crate) mod printing { Precedence::Unambiguous }; #[cfg(feature = "full")] - let func_fixup = fixup.leftmost_subexpression_with_begin_operator(true); + let func_fixup = fixup.leftmost_subexpression_with_begin_operator(true, false); let func_precedence = leading_precedence( &e.func, #[cfg(feature = "full")] @@ -3550,7 +3549,7 @@ pub(crate) mod printing { ) { outer_attrs_to_tokens(&e.attrs, tokens); #[cfg(feature = "full")] - let obj_fixup = fixup.leftmost_subexpression_with_begin_operator(true); + let obj_fixup = fixup.leftmost_subexpression_with_begin_operator(true, false); let obj_precedence = leading_precedence( &e.expr, #[cfg(feature = "full")] diff --git a/src/fixup.rs b/src/fixup.rs index 536da4e49..b8605a7d1 100644 --- a/src/fixup.rs +++ b/src/fixup.rs @@ -101,6 +101,15 @@ pub(crate) struct FixupContext { // let _ = return + 1; // no paren because '+' cannot begin expr // next_operator_can_begin_expr: bool, + + // This is the difference between: + // + // let _ = x as u8 + T; + // + // let _ = (x as u8) < T; + // + // Without parens, the latter would want to parse `u8 Self { FixupContext { next_operator_can_begin_expr, + next_operator_can_begin_generics, ..self.leftmost_subexpression() } } @@ -246,15 +258,14 @@ impl FixupContext { /// expressions have lower precedence when adjacent to particular operators. pub fn leading_precedence(self, expr: &Expr) -> Precedence { if self.next_operator_can_begin_expr { - match expr { - // Decrease precedence of value-less jumps when followed by an - // operator that would otherwise get interpreted as beginning a - // value for the jump. - Expr::Break(_) | Expr::Return(_) | Expr::Yield(_) => return Precedence::Jump, - _ => {} + // Decrease precedence of value-less jumps when followed by an + // operator that would otherwise get interpreted as beginning a + // value for the jump. + if let Expr::Break(_) | Expr::Return(_) | Expr::Yield(_) = expr { + return Precedence::Jump; } } - Precedence::of(expr) + self.precedence(expr) } /// Determines the effective precedence of a right subexpression. Some @@ -276,6 +287,17 @@ impl FixupContext { _ => {} } } + self.precedence(expr) + } + + fn precedence(self, expr: &Expr) -> Precedence { + if self.next_operator_can_begin_generics { + if let Expr::Cast(cast) = expr { + if classify::trailing_unparameterized_path(&cast.ty) { + return Precedence::MIN; + } + } + } Precedence::of(expr) } } diff --git a/src/lib.rs b/src/lib.rs index 96596844a..1ecf817f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -329,10 +329,7 @@ mod bigint; #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] pub mod buffer; -#[cfg(any( - all(feature = "parsing", feature = "full"), - all(feature = "printing", any(feature = "full", feature = "derive")), -))] +#[cfg(all(any(feature = "parsing", feature = "printing"), feature = "full"))] mod classify; mod custom_keyword; diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 11d0f86c5..d20cce8d6 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -660,7 +660,7 @@ fn test_fixup() { quote! { 0 + (0 + 0) }, quote! { (a = b) = c }, quote! { (x as i32) < 0 }, - quote! { (1 + x as i32) < 0 }, + quote! { 1 + (x as i32) < 0 }, quote! { (1 + 1).abs() }, quote! { (lo..hi)[..] }, quote! { (a..b)..(c..d) },