xref: /linux/rust/syn/classify.rs (revision 54e3eae855629702c566bd2e130d9f40e7f35bde)
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