Skip to main content

syn/
classify.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3#[cfg(feature = "full")]
4use crate::expr::Expr;
5#[cfg(any(feature = "printing", feature = "full"))]
6use crate::generics::TypeParamBound;
7#[cfg(any(feature = "printing", feature = "full"))]
8use crate::path::{Path, PathArguments};
9#[cfg(any(feature = "printing", feature = "full"))]
10use crate::punctuated::Punctuated;
11#[cfg(any(feature = "printing", feature = "full"))]
12use crate::ty::{ReturnType, Type};
13#[cfg(feature = "full")]
14use proc_macro2::{Delimiter, TokenStream, TokenTree};
15#[cfg(any(feature = "printing", feature = "full"))]
16use std::ops::ControlFlow;
17
18#[cfg(feature = "full")]
19pub(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")]
27pub(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")]
73pub(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"))]
132pub(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")]
186pub(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}