syn/
classify.rs

1#[cfg(feature = "full")]
2use crate::expr::Expr;
3#[cfg(any(feature = "printing", feature = "full"))]
4use crate::generics::TypeParamBound;
5#[cfg(any(feature = "printing", feature = "full"))]
6use crate::path::{Path, PathArguments};
7#[cfg(any(feature = "printing", feature = "full"))]
8use crate::punctuated::Punctuated;
9#[cfg(any(feature = "printing", feature = "full"))]
10use crate::ty::{ReturnType, Type};
11#[cfg(feature = "full")]
12use proc_macro2::{Delimiter, TokenStream, TokenTree};
13#[cfg(any(feature = "printing", feature = "full"))]
14use std::ops::ControlFlow;
15
16#[cfg(feature = "full")]
17pub(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")]
25pub(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")]
71pub(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"))]
130pub(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")]
184pub(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}