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")]
requires_semi_to_be_stmt(expr: &Expr) -> bool19 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")]
requires_comma_to_be_match_arm(expr: &Expr) -> bool27 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")]
trailing_unparameterized_path(mut ty: &Type) -> bool73 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"))]
expr_leading_label(mut expr: &Expr) -> bool132 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")]
expr_trailing_brace(mut expr: &Expr) -> bool186 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