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