|
1 | 1 | use crate::expr::Expr; |
| 2 | +#[cfg(feature = "parsing")] |
| 3 | +use crate::generics::TypeParamBound; |
| 4 | +#[cfg(feature = "parsing")] |
| 5 | +use crate::path::{Path, PathArguments}; |
| 6 | +#[cfg(feature = "parsing")] |
| 7 | +use crate::punctuated::Punctuated; |
| 8 | +#[cfg(feature = "parsing")] |
| 9 | +use crate::ty::{ReturnType, Type}; |
| 10 | +#[cfg(feature = "parsing")] |
| 11 | +use proc_macro2::{Delimiter, TokenStream, TokenTree}; |
| 12 | +#[cfg(feature = "parsing")] |
| 13 | +use std::ops::ControlFlow; |
2 | 14 |
|
3 | 15 | pub(crate) fn requires_terminator(expr: &Expr) -> bool { |
4 | 16 | // see https://github.com/rust-lang/rust/blob/9a19e7604/compiler/rustc_ast/src/util/classify.rs#L7-L26 |
@@ -43,3 +55,130 @@ pub(crate) fn requires_terminator(expr: &Expr) -> bool { |
43 | 55 | | Expr::Verbatim(_) => true |
44 | 56 | } |
45 | 57 | } |
| 58 | + |
| 59 | +/// Whether the expression's last token is `}`. |
| 60 | +#[cfg(feature = "parsing")] |
| 61 | +pub(crate) fn expr_trailing_brace(mut expr: &Expr) -> bool { |
| 62 | + loop { |
| 63 | + match expr { |
| 64 | + Expr::Array(_) => return false, |
| 65 | + Expr::Assign(e) => expr = &e.right, |
| 66 | + Expr::Async(_) => return true, |
| 67 | + Expr::Await(_) => return false, |
| 68 | + Expr::Binary(e) => expr = &e.right, |
| 69 | + Expr::Block(_) => return true, |
| 70 | + Expr::Break(e) => match &e.expr { |
| 71 | + Some(e) => expr = e, |
| 72 | + None => return false, |
| 73 | + }, |
| 74 | + Expr::Call(_) => return false, |
| 75 | + Expr::Cast(e) => return type_trailing_brace(&e.ty), |
| 76 | + Expr::Closure(e) => expr = &e.body, |
| 77 | + Expr::Const(_) => return true, |
| 78 | + Expr::Continue(_) => return false, |
| 79 | + Expr::Field(_) => return false, |
| 80 | + Expr::ForLoop(_) => return true, |
| 81 | + Expr::Group(_) => return false, |
| 82 | + Expr::If(_) => return true, |
| 83 | + Expr::Index(_) => return false, |
| 84 | + Expr::Infer(_) => return false, |
| 85 | + Expr::Let(e) => expr = &e.expr, |
| 86 | + Expr::Lit(_) => return false, |
| 87 | + Expr::Loop(_) => return true, |
| 88 | + Expr::Macro(e) => return e.mac.delimiter.is_brace(), |
| 89 | + Expr::Match(_) => return true, |
| 90 | + Expr::MethodCall(_) => return false, |
| 91 | + Expr::Paren(_) => return false, |
| 92 | + Expr::Path(_) => return false, |
| 93 | + Expr::Range(e) => match &e.end { |
| 94 | + Some(end) => expr = end, |
| 95 | + None => return false, |
| 96 | + }, |
| 97 | + Expr::Reference(e) => expr = &e.expr, |
| 98 | + Expr::Repeat(_) => return false, |
| 99 | + Expr::Return(e) => match &e.expr { |
| 100 | + Some(e) => expr = e, |
| 101 | + None => return false, |
| 102 | + }, |
| 103 | + Expr::Struct(_) => return true, |
| 104 | + Expr::Try(_) => return false, |
| 105 | + Expr::TryBlock(_) => return true, |
| 106 | + Expr::Tuple(_) => return false, |
| 107 | + Expr::Unary(e) => expr = &e.expr, |
| 108 | + Expr::Unsafe(_) => return true, |
| 109 | + Expr::Verbatim(e) => return tokens_trailing_brace(e), |
| 110 | + Expr::While(_) => return true, |
| 111 | + Expr::Yield(e) => match &e.expr { |
| 112 | + Some(e) => expr = e, |
| 113 | + None => return false, |
| 114 | + }, |
| 115 | + } |
| 116 | + } |
| 117 | +} |
| 118 | + |
| 119 | +#[cfg(feature = "parsing")] |
| 120 | +fn type_trailing_brace(mut ty: &Type) -> bool { |
| 121 | + fn last_type_in_path(path: &Path) -> Option<&Type> { |
| 122 | + match &path.segments.last().unwrap().arguments { |
| 123 | + PathArguments::None | PathArguments::AngleBracketed(_) => None, |
| 124 | + PathArguments::Parenthesized(arg) => match &arg.output { |
| 125 | + ReturnType::Default => None, |
| 126 | + ReturnType::Type(_, ret) => Some(ret), |
| 127 | + }, |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + fn last_type_in_bounds( |
| 132 | + bounds: &Punctuated<TypeParamBound, Token![+]>, |
| 133 | + ) -> ControlFlow<bool, &Type> { |
| 134 | + match bounds.last().unwrap() { |
| 135 | + TypeParamBound::Trait(t) => match last_type_in_path(&t.path) { |
| 136 | + Some(t) => ControlFlow::Continue(t), |
| 137 | + None => ControlFlow::Break(false), |
| 138 | + }, |
| 139 | + TypeParamBound::Lifetime(_) => ControlFlow::Break(false), |
| 140 | + TypeParamBound::Verbatim(t) => ControlFlow::Break(tokens_trailing_brace(t)), |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + loop { |
| 145 | + match ty { |
| 146 | + Type::Array(_) => return false, |
| 147 | + Type::BareFn(t) => match &t.output { |
| 148 | + ReturnType::Default => return false, |
| 149 | + ReturnType::Type(_, ret) => ty = ret, |
| 150 | + }, |
| 151 | + Type::Group(_) => return false, |
| 152 | + Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) { |
| 153 | + ControlFlow::Break(trailing_brace) => return trailing_brace, |
| 154 | + ControlFlow::Continue(t) => ty = t, |
| 155 | + }, |
| 156 | + Type::Infer(_) => return false, |
| 157 | + Type::Macro(t) => return t.mac.delimiter.is_brace(), |
| 158 | + Type::Never(_) => return false, |
| 159 | + Type::Paren(_) => return false, |
| 160 | + Type::Path(t) => match last_type_in_path(&t.path) { |
| 161 | + Some(t) => ty = t, |
| 162 | + None => return false, |
| 163 | + }, |
| 164 | + Type::Ptr(t) => ty = &t.elem, |
| 165 | + Type::Reference(t) => ty = &t.elem, |
| 166 | + Type::Slice(_) => return false, |
| 167 | + Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) { |
| 168 | + ControlFlow::Break(trailing_brace) => return trailing_brace, |
| 169 | + ControlFlow::Continue(t) => ty = t, |
| 170 | + }, |
| 171 | + Type::Tuple(_) => return false, |
| 172 | + Type::Verbatim(t) => return tokens_trailing_brace(t), |
| 173 | + } |
| 174 | + } |
| 175 | +} |
| 176 | + |
| 177 | +#[cfg(feature = "parsing")] |
| 178 | +fn tokens_trailing_brace(tokens: &TokenStream) -> bool { |
| 179 | + if let Some(TokenTree::Group(last)) = tokens.clone().into_iter().last() { |
| 180 | + last.delimiter() == Delimiter::Brace |
| 181 | + } else { |
| 182 | + false |
| 183 | + } |
| 184 | +} |
0 commit comments