| #[cfg(feature = "full")] |
| use crate::expr::Expr; |
| #[cfg(any(feature = "printing", feature = "full"))] |
| use crate::generics::TypeParamBound; |
| #[cfg(any(feature = "printing", feature = "full"))] |
| use crate::path::{Path, PathArguments}; |
| #[cfg(any(feature = "printing", feature = "full"))] |
| use crate::punctuated::Punctuated; |
| #[cfg(any(feature = "printing", feature = "full"))] |
| use crate::ty::{ReturnType, Type}; |
| #[cfg(feature = "full")] |
| use proc_macro2::{Delimiter, TokenStream, TokenTree}; |
| #[cfg(any(feature = "printing", feature = "full"))] |
| 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(), |
| _ => requires_comma_to_be_match_arm(expr), |
| } |
| } |
| |
| #[cfg(feature = "full")] |
| pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool { |
| match expr { |
| Expr::If(_) |
| | Expr::Match(_) |
| | Expr::Block(_) | Expr::Unsafe(_) // both under ExprKind::Block in rustc |
| | Expr::While(_) |
| | Expr::Loop(_) |
| | Expr::ForLoop(_) |
| | Expr::TryBlock(_) |
| | Expr::Const(_) => false, |
| |
| Expr::Array(_) |
| | Expr::Assign(_) |
| | Expr::Async(_) |
| | Expr::Await(_) |
| | Expr::Binary(_) |
| | Expr::Break(_) |
| | Expr::Call(_) |
| | Expr::Cast(_) |
| | Expr::Closure(_) |
| | Expr::Continue(_) |
| | Expr::Field(_) |
| | Expr::Group(_) |
| | Expr::Index(_) |
| | Expr::Infer(_) |
| | Expr::Let(_) |
| | Expr::Lit(_) |
| | Expr::Macro(_) |
| | Expr::MethodCall(_) |
| | Expr::Paren(_) |
| | Expr::Path(_) |
| | Expr::Range(_) |
| | Expr::Reference(_) |
| | Expr::Repeat(_) |
| | Expr::Return(_) |
| | Expr::Struct(_) |
| | Expr::Try(_) |
| | Expr::Tuple(_) |
| | Expr::Unary(_) |
| | Expr::Yield(_) |
| | Expr::Verbatim(_) => true |
| } |
| } |
| |
| #[cfg(all(feature = "printing", feature = "full"))] |
| pub(crate) fn confusable_with_adjacent_block(mut expr: &Expr) -> bool { |
| let mut stack = Vec::new(); |
| |
| while let Some(next) = match expr { |
| Expr::Assign(e) => { |
| stack.push(&e.right); |
| Some(&e.left) |
| } |
| Expr::Await(e) => Some(&e.base), |
| Expr::Binary(e) => { |
| stack.push(&e.right); |
| Some(&e.left) |
| } |
| Expr::Break(e) => { |
| if let Some(Expr::Block(_)) = e.expr.as_deref() { |
| return true; |
| } |
| stack.pop() |
| } |
| Expr::Call(e) => Some(&e.func), |
| Expr::Cast(e) => Some(&e.expr), |
| Expr::Closure(e) => Some(&e.body), |
| Expr::Field(e) => Some(&e.base), |
| Expr::Index(e) => Some(&e.expr), |
| Expr::MethodCall(e) => Some(&e.receiver), |
| Expr::Range(e) => { |
| if let Some(Expr::Block(_)) = e.end.as_deref() { |
| return true; |
| } |
| match (&e.start, &e.end) { |
| (Some(start), end) => { |
| stack.extend(end); |
| Some(start) |
| } |
| (None, Some(end)) => Some(end), |
| (None, None) => stack.pop(), |
| } |
| } |
| Expr::Reference(e) => Some(&e.expr), |
| Expr::Return(e) => { |
| if e.expr.is_none() && stack.is_empty() { |
| return true; |
| } |
| stack.pop() |
| } |
| Expr::Struct(_) => return true, |
| Expr::Try(e) => Some(&e.expr), |
| Expr::Unary(e) => Some(&e.expr), |
| Expr::Yield(e) => { |
| if e.expr.is_none() && stack.is_empty() { |
| return true; |
| } |
| stack.pop() |
| } |
| |
| Expr::Array(_) |
| | Expr::Async(_) |
| | Expr::Block(_) |
| | Expr::Const(_) |
| | Expr::Continue(_) |
| | Expr::ForLoop(_) |
| | Expr::Group(_) |
| | Expr::If(_) |
| | Expr::Infer(_) |
| | Expr::Let(_) |
| | Expr::Lit(_) |
| | Expr::Loop(_) |
| | Expr::Macro(_) |
| | Expr::Match(_) |
| | Expr::Paren(_) |
| | Expr::Path(_) |
| | Expr::Repeat(_) |
| | Expr::TryBlock(_) |
| | Expr::Tuple(_) |
| | Expr::Unsafe(_) |
| | Expr::Verbatim(_) |
| | Expr::While(_) => stack.pop(), |
| } { |
| expr = next; |
| } |
| |
| false |
| } |
| |
| #[cfg(feature = "printing")] |
| pub(crate) 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, |
| }, |
| |
| Type::Array(_) |
| | Type::Group(_) |
| | Type::Infer(_) |
| | Type::Macro(_) |
| | Type::Never(_) |
| | Type::Paren(_) |
| | Type::Slice(_) |
| | Type::Tuple(_) |
| | Type::Verbatim(_) => return false, |
| } |
| } |
| |
| fn last_type_in_path(path: &Path) -> ControlFlow<bool, &Type> { |
| match &path.segments.last().unwrap().arguments { |
| PathArguments::None => ControlFlow::Break(true), |
| PathArguments::AngleBracketed(_) => ControlFlow::Break(false), |
| PathArguments::Parenthesized(arg) => match &arg.output { |
| ReturnType::Default => ControlFlow::Break(false), |
| ReturnType::Type(_, ret) => ControlFlow::Continue(ret), |
| }, |
| } |
| } |
| |
| fn last_type_in_bounds( |
| bounds: &Punctuated<TypeParamBound, Token![+]>, |
| ) -> ControlFlow<bool, &Type> { |
| match bounds.last().unwrap() { |
| TypeParamBound::Trait(t) => last_type_in_path(&t.path), |
| TypeParamBound::Lifetime(_) | TypeParamBound::Verbatim(_) => ControlFlow::Break(false), |
| } |
| } |
| } |
| |
| /// Whether the expression's first token is the label of a loop/block. |
| #[cfg(all(feature = "printing", feature = "full"))] |
| pub(crate) fn expr_leading_label(mut expr: &Expr) -> bool { |
| loop { |
| match expr { |
| Expr::Block(e) => return e.label.is_some(), |
| Expr::ForLoop(e) => return e.label.is_some(), |
| Expr::Loop(e) => return e.label.is_some(), |
| Expr::While(e) => return e.label.is_some(), |
| |
| Expr::Assign(e) => expr = &e.left, |
| Expr::Await(e) => expr = &e.base, |
| Expr::Binary(e) => expr = &e.left, |
| Expr::Call(e) => expr = &e.func, |
| Expr::Cast(e) => expr = &e.expr, |
| Expr::Field(e) => expr = &e.base, |
| Expr::Index(e) => expr = &e.expr, |
| Expr::MethodCall(e) => expr = &e.receiver, |
| Expr::Range(e) => match &e.start { |
| Some(start) => expr = start, |
| None => return false, |
| }, |
| Expr::Try(e) => expr = &e.expr, |
| |
| Expr::Array(_) |
| | Expr::Async(_) |
| | Expr::Break(_) |
| | Expr::Closure(_) |
| | Expr::Const(_) |
| | Expr::Continue(_) |
| | Expr::Group(_) |
| | Expr::If(_) |
| | Expr::Infer(_) |
| | Expr::Let(_) |
| | Expr::Lit(_) |
| | Expr::Macro(_) |
| | Expr::Match(_) |
| | Expr::Paren(_) |
| | Expr::Path(_) |
| | Expr::Reference(_) |
| | Expr::Repeat(_) |
| | Expr::Return(_) |
| | Expr::Struct(_) |
| | Expr::TryBlock(_) |
| | Expr::Tuple(_) |
| | Expr::Unary(_) |
| | Expr::Unsafe(_) |
| | Expr::Verbatim(_) |
| | Expr::Yield(_) => return false, |
| } |
| } |
| } |
| |
| /// Whether the expression's last token is `}`. |
| #[cfg(feature = "full")] |
| pub(crate) fn expr_trailing_brace(mut expr: &Expr) -> bool { |
| loop { |
| match expr { |
| Expr::Async(_) |
| | Expr::Block(_) |
| | Expr::Const(_) |
| | Expr::ForLoop(_) |
| | Expr::If(_) |
| | Expr::Loop(_) |
| | Expr::Match(_) |
| | Expr::Struct(_) |
| | Expr::TryBlock(_) |
| | Expr::Unsafe(_) |
| | Expr::While(_) => return true, |
| |
| Expr::Assign(e) => expr = &e.right, |
| Expr::Binary(e) => expr = &e.right, |
| Expr::Break(e) => match &e.expr { |
| Some(e) => expr = e, |
| None => return false, |
| }, |
| Expr::Cast(e) => return type_trailing_brace(&e.ty), |
| Expr::Closure(e) => expr = &e.body, |
| Expr::Let(e) => expr = &e.expr, |
| Expr::Macro(e) => return e.mac.delimiter.is_brace(), |
| Expr::Range(e) => match &e.end { |
| Some(end) => expr = end, |
| None => return false, |
| }, |
| Expr::Reference(e) => expr = &e.expr, |
| Expr::Return(e) => match &e.expr { |
| Some(e) => expr = e, |
| None => return false, |
| }, |
| Expr::Unary(e) => expr = &e.expr, |
| Expr::Verbatim(e) => return tokens_trailing_brace(e), |
| Expr::Yield(e) => match &e.expr { |
| Some(e) => expr = e, |
| None => return false, |
| }, |
| |
| Expr::Array(_) |
| | Expr::Await(_) |
| | Expr::Call(_) |
| | Expr::Continue(_) |
| | Expr::Field(_) |
| | Expr::Group(_) |
| | Expr::Index(_) |
| | Expr::Infer(_) |
| | Expr::Lit(_) |
| | Expr::MethodCall(_) |
| | Expr::Paren(_) |
| | Expr::Path(_) |
| | Expr::Repeat(_) |
| | Expr::Try(_) |
| | Expr::Tuple(_) => return false, |
| } |
| } |
| |
| fn type_trailing_brace(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_brace) => return trailing_brace, |
| ControlFlow::Continue(t) => ty = t, |
| }, |
| Type::Macro(t) => return t.mac.delimiter.is_brace(), |
| Type::Path(t) => match last_type_in_path(&t.path) { |
| Some(t) => ty = t, |
| None => return false, |
| }, |
| 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_brace) => return trailing_brace, |
| ControlFlow::Continue(t) => ty = t, |
| }, |
| Type::Verbatim(t) => return tokens_trailing_brace(t), |
| |
| Type::Array(_) |
| | Type::Group(_) |
| | Type::Infer(_) |
| | Type::Never(_) |
| | Type::Paren(_) |
| | Type::Slice(_) |
| | Type::Tuple(_) => return false, |
| } |
| } |
| } |
| |
| fn last_type_in_path(path: &Path) -> Option<&Type> { |
| match &path.segments.last().unwrap().arguments { |
| PathArguments::None | PathArguments::AngleBracketed(_) => None, |
| PathArguments::Parenthesized(arg) => match &arg.output { |
| ReturnType::Default => None, |
| ReturnType::Type(_, ret) => Some(ret), |
| }, |
| } |
| } |
| |
| fn last_type_in_bounds( |
| bounds: &Punctuated<TypeParamBound, Token![+]>, |
| ) -> ControlFlow<bool, &Type> { |
| match bounds.last().unwrap() { |
| TypeParamBound::Trait(t) => match last_type_in_path(&t.path) { |
| Some(t) => ControlFlow::Continue(t), |
| None => ControlFlow::Break(false), |
| }, |
| TypeParamBound::Lifetime(_) => ControlFlow::Break(false), |
| TypeParamBound::Verbatim(t) => ControlFlow::Break(tokens_trailing_brace(t)), |
| } |
| } |
| |
| fn tokens_trailing_brace(tokens: &TokenStream) -> bool { |
| if let Some(TokenTree::Group(last)) = tokens.clone().into_iter().last() { |
| last.delimiter() == Delimiter::Brace |
| } else { |
| false |
| } |
| } |
| } |