1 // SPDX-License-Identifier: Apache-2.0 OR MIT 2 3 #[cfg(feature = "full")] 4 use crate::expr::Expr; 5 #[cfg(any(feature = "printing", feature = "full"))] 6 use crate::generics::TypeParamBound; 7 #[cfg(any(feature = "printing", feature = "full"))] 8 use crate::path::{Path, PathArguments}; 9 #[cfg(any(feature = "printing", feature = "full"))] 10 use crate::punctuated::Punctuated; 11 #[cfg(any(feature = "printing", feature = "full"))] 12 use crate::ty::{ReturnType, Type}; 13 #[cfg(feature = "full")] 14 use proc_macro2::{Delimiter, TokenStream, TokenTree}; 15 #[cfg(any(feature = "printing", feature = "full"))] 16 use std::ops::ControlFlow; 17 18 #[cfg(feature = "full")] 19 pub(crate) fn requires_semi_to_be_stmt(expr: &Expr) -> bool { 20 match expr { 21 Expr::Macro(expr) => !expr.mac.delimiter.is_brace(), 22 _ => requires_comma_to_be_match_arm(expr), 23 } 24 } 25 26 #[cfg(feature = "full")] 27 pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool { 28 match expr { 29 Expr::If(_) 30 | Expr::Match(_) 31 | Expr::Block(_) | Expr::Unsafe(_) // both under ExprKind::Block in rustc 32 | Expr::While(_) 33 | Expr::Loop(_) 34 | Expr::ForLoop(_) 35 | Expr::TryBlock(_) 36 | Expr::Const(_) => false, 37 38 Expr::Array(_) 39 | Expr::Assign(_) 40 | Expr::Async(_) 41 | Expr::Await(_) 42 | Expr::Binary(_) 43 | Expr::Break(_) 44 | Expr::Call(_) 45 | Expr::Cast(_) 46 | Expr::Closure(_) 47 | Expr::Continue(_) 48 | Expr::Field(_) 49 | Expr::Group(_) 50 | Expr::Index(_) 51 | Expr::Infer(_) 52 | Expr::Let(_) 53 | Expr::Lit(_) 54 | Expr::Macro(_) 55 | Expr::MethodCall(_) 56 | Expr::Paren(_) 57 | Expr::Path(_) 58 | Expr::Range(_) 59 | Expr::RawAddr(_) 60 | Expr::Reference(_) 61 | Expr::Repeat(_) 62 | Expr::Return(_) 63 | Expr::Struct(_) 64 | Expr::Try(_) 65 | Expr::Tuple(_) 66 | Expr::Unary(_) 67 | Expr::Yield(_) 68 | Expr::Verbatim(_) => true, 69 } 70 } 71 72 #[cfg(feature = "printing")] 73 pub(crate) fn trailing_unparameterized_path(mut ty: &Type) -> bool { 74 loop { 75 match ty { 76 Type::BareFn(t) => match &t.output { 77 ReturnType::Default => return false, 78 ReturnType::Type(_, ret) => ty = ret, 79 }, 80 Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) { 81 ControlFlow::Break(trailing_path) => return trailing_path, 82 ControlFlow::Continue(t) => ty = t, 83 }, 84 Type::Path(t) => match last_type_in_path(&t.path) { 85 ControlFlow::Break(trailing_path) => return trailing_path, 86 ControlFlow::Continue(t) => ty = t, 87 }, 88 Type::Ptr(t) => ty = &t.elem, 89 Type::Reference(t) => ty = &t.elem, 90 Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) { 91 ControlFlow::Break(trailing_path) => return trailing_path, 92 ControlFlow::Continue(t) => ty = t, 93 }, 94 95 Type::Array(_) 96 | Type::Group(_) 97 | Type::Infer(_) 98 | Type::Macro(_) 99 | Type::Never(_) 100 | Type::Paren(_) 101 | Type::Slice(_) 102 | Type::Tuple(_) 103 | Type::Verbatim(_) => return false, 104 } 105 } 106 107 fn last_type_in_path(path: &Path) -> ControlFlow<bool, &Type> { 108 match &path.segments.last().unwrap().arguments { 109 PathArguments::None => ControlFlow::Break(true), 110 PathArguments::AngleBracketed(_) => ControlFlow::Break(false), 111 PathArguments::Parenthesized(arg) => match &arg.output { 112 ReturnType::Default => ControlFlow::Break(false), 113 ReturnType::Type(_, ret) => ControlFlow::Continue(ret), 114 }, 115 } 116 } 117 118 fn last_type_in_bounds( 119 bounds: &Punctuated<TypeParamBound, Token![+]>, 120 ) -> ControlFlow<bool, &Type> { 121 match bounds.last().unwrap() { 122 TypeParamBound::Trait(t) => last_type_in_path(&t.path), 123 TypeParamBound::Lifetime(_) 124 | TypeParamBound::PreciseCapture(_) 125 | TypeParamBound::Verbatim(_) => ControlFlow::Break(false), 126 } 127 } 128 } 129 130 /// Whether the expression's first token is the label of a loop/block. 131 #[cfg(all(feature = "printing", feature = "full"))] 132 pub(crate) fn expr_leading_label(mut expr: &Expr) -> bool { 133 loop { 134 match expr { 135 Expr::Block(e) => return e.label.is_some(), 136 Expr::ForLoop(e) => return e.label.is_some(), 137 Expr::Loop(e) => return e.label.is_some(), 138 Expr::While(e) => return e.label.is_some(), 139 140 Expr::Assign(e) => expr = &e.left, 141 Expr::Await(e) => expr = &e.base, 142 Expr::Binary(e) => expr = &e.left, 143 Expr::Call(e) => expr = &e.func, 144 Expr::Cast(e) => expr = &e.expr, 145 Expr::Field(e) => expr = &e.base, 146 Expr::Index(e) => expr = &e.expr, 147 Expr::MethodCall(e) => expr = &e.receiver, 148 Expr::Range(e) => match &e.start { 149 Some(start) => expr = start, 150 None => return false, 151 }, 152 Expr::Try(e) => expr = &e.expr, 153 154 Expr::Array(_) 155 | Expr::Async(_) 156 | Expr::Break(_) 157 | Expr::Closure(_) 158 | Expr::Const(_) 159 | Expr::Continue(_) 160 | Expr::Group(_) 161 | Expr::If(_) 162 | Expr::Infer(_) 163 | Expr::Let(_) 164 | Expr::Lit(_) 165 | Expr::Macro(_) 166 | Expr::Match(_) 167 | Expr::Paren(_) 168 | Expr::Path(_) 169 | Expr::RawAddr(_) 170 | Expr::Reference(_) 171 | Expr::Repeat(_) 172 | Expr::Return(_) 173 | Expr::Struct(_) 174 | Expr::TryBlock(_) 175 | Expr::Tuple(_) 176 | Expr::Unary(_) 177 | Expr::Unsafe(_) 178 | Expr::Verbatim(_) 179 | Expr::Yield(_) => return false, 180 } 181 } 182 } 183 184 /// Whether the expression's last token is `}`. 185 #[cfg(feature = "full")] 186 pub(crate) fn expr_trailing_brace(mut expr: &Expr) -> bool { 187 loop { 188 match expr { 189 Expr::Async(_) 190 | Expr::Block(_) 191 | Expr::Const(_) 192 | Expr::ForLoop(_) 193 | Expr::If(_) 194 | Expr::Loop(_) 195 | Expr::Match(_) 196 | Expr::Struct(_) 197 | Expr::TryBlock(_) 198 | Expr::Unsafe(_) 199 | Expr::While(_) => return true, 200 201 Expr::Assign(e) => expr = &e.right, 202 Expr::Binary(e) => expr = &e.right, 203 Expr::Break(e) => match &e.expr { 204 Some(e) => expr = e, 205 None => return false, 206 }, 207 Expr::Cast(e) => return type_trailing_brace(&e.ty), 208 Expr::Closure(e) => expr = &e.body, 209 Expr::Let(e) => expr = &e.expr, 210 Expr::Macro(e) => return e.mac.delimiter.is_brace(), 211 Expr::Range(e) => match &e.end { 212 Some(end) => expr = end, 213 None => return false, 214 }, 215 Expr::RawAddr(e) => expr = &e.expr, 216 Expr::Reference(e) => expr = &e.expr, 217 Expr::Return(e) => match &e.expr { 218 Some(e) => expr = e, 219 None => return false, 220 }, 221 Expr::Unary(e) => expr = &e.expr, 222 Expr::Verbatim(e) => return tokens_trailing_brace(e), 223 Expr::Yield(e) => match &e.expr { 224 Some(e) => expr = e, 225 None => return false, 226 }, 227 228 Expr::Array(_) 229 | Expr::Await(_) 230 | Expr::Call(_) 231 | Expr::Continue(_) 232 | Expr::Field(_) 233 | Expr::Group(_) 234 | Expr::Index(_) 235 | Expr::Infer(_) 236 | Expr::Lit(_) 237 | Expr::MethodCall(_) 238 | Expr::Paren(_) 239 | Expr::Path(_) 240 | Expr::Repeat(_) 241 | Expr::Try(_) 242 | Expr::Tuple(_) => return false, 243 } 244 } 245 246 fn type_trailing_brace(mut ty: &Type) -> bool { 247 loop { 248 match ty { 249 Type::BareFn(t) => match &t.output { 250 ReturnType::Default => return false, 251 ReturnType::Type(_, ret) => ty = ret, 252 }, 253 Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) { 254 ControlFlow::Break(trailing_brace) => return trailing_brace, 255 ControlFlow::Continue(t) => ty = t, 256 }, 257 Type::Macro(t) => return t.mac.delimiter.is_brace(), 258 Type::Path(t) => match last_type_in_path(&t.path) { 259 Some(t) => ty = t, 260 None => return false, 261 }, 262 Type::Ptr(t) => ty = &t.elem, 263 Type::Reference(t) => ty = &t.elem, 264 Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) { 265 ControlFlow::Break(trailing_brace) => return trailing_brace, 266 ControlFlow::Continue(t) => ty = t, 267 }, 268 Type::Verbatim(t) => return tokens_trailing_brace(t), 269 270 Type::Array(_) 271 | Type::Group(_) 272 | Type::Infer(_) 273 | Type::Never(_) 274 | Type::Paren(_) 275 | Type::Slice(_) 276 | Type::Tuple(_) => return false, 277 } 278 } 279 } 280 281 fn last_type_in_path(path: &Path) -> Option<&Type> { 282 match &path.segments.last().unwrap().arguments { 283 PathArguments::None | PathArguments::AngleBracketed(_) => None, 284 PathArguments::Parenthesized(arg) => match &arg.output { 285 ReturnType::Default => None, 286 ReturnType::Type(_, ret) => Some(ret), 287 }, 288 } 289 } 290 291 fn last_type_in_bounds( 292 bounds: &Punctuated<TypeParamBound, Token![+]>, 293 ) -> ControlFlow<bool, &Type> { 294 match bounds.last().unwrap() { 295 TypeParamBound::Trait(t) => match last_type_in_path(&t.path) { 296 Some(t) => ControlFlow::Continue(t), 297 None => ControlFlow::Break(false), 298 }, 299 TypeParamBound::Lifetime(_) | TypeParamBound::PreciseCapture(_) => { 300 ControlFlow::Break(false) 301 } 302 TypeParamBound::Verbatim(t) => ControlFlow::Break(tokens_trailing_brace(t)), 303 } 304 } 305 306 fn tokens_trailing_brace(tokens: &TokenStream) -> bool { 307 if let Some(TokenTree::Group(last)) = tokens.clone().into_iter().last() { 308 last.delimiter() == Delimiter::Brace 309 } else { 310 false 311 } 312 } 313 } 314