prettyplease/
classify.rs

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