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(all(feature = "printing", feature = "full"))]
71pub(crate) fn confusable_with_adjacent_block(mut expr: &Expr) -> bool {
72    let mut stack = Vec::new();
73
74    while let Some(next) = match expr {
75        Expr::Assign(e) => {
76            stack.push(&e.right);
77            Some(&e.left)
78        }
79        Expr::Await(e) => Some(&e.base),
80        Expr::Binary(e) => {
81            stack.push(&e.right);
82            Some(&e.left)
83        }
84        Expr::Break(e) => {
85            if let Some(Expr::Block(_)) = e.expr.as_deref() {
86                return true;
87            }
88            stack.pop()
89        }
90        Expr::Call(e) => Some(&e.func),
91        Expr::Cast(e) => Some(&e.expr),
92        Expr::Closure(e) => Some(&e.body),
93        Expr::Field(e) => Some(&e.base),
94        Expr::Index(e) => Some(&e.expr),
95        Expr::MethodCall(e) => Some(&e.receiver),
96        Expr::Range(e) => {
97            if let Some(Expr::Block(_)) = e.end.as_deref() {
98                return true;
99            }
100            match (&e.start, &e.end) {
101                (Some(start), end) => {
102                    stack.extend(end);
103                    Some(start)
104                }
105                (None, Some(end)) => Some(end),
106                (None, None) => stack.pop(),
107            }
108        }
109        Expr::RawAddr(e) => Some(&e.expr),
110        Expr::Reference(e) => Some(&e.expr),
111        Expr::Return(e) => {
112            if e.expr.is_none() && stack.is_empty() {
113                return true;
114            }
115            stack.pop()
116        }
117        Expr::Struct(_) => return true,
118        Expr::Try(e) => Some(&e.expr),
119        Expr::Unary(e) => Some(&e.expr),
120        Expr::Yield(e) => {
121            if e.expr.is_none() && stack.is_empty() {
122                return true;
123            }
124            stack.pop()
125        }
126
127        Expr::Array(_)
128        | Expr::Async(_)
129        | Expr::Block(_)
130        | Expr::Const(_)
131        | Expr::Continue(_)
132        | Expr::ForLoop(_)
133        | Expr::Group(_)
134        | Expr::If(_)
135        | Expr::Infer(_)
136        | Expr::Let(_)
137        | Expr::Lit(_)
138        | Expr::Loop(_)
139        | Expr::Macro(_)
140        | Expr::Match(_)
141        | Expr::Paren(_)
142        | Expr::Path(_)
143        | Expr::Repeat(_)
144        | Expr::TryBlock(_)
145        | Expr::Tuple(_)
146        | Expr::Unsafe(_)
147        | Expr::Verbatim(_)
148        | Expr::While(_) => stack.pop(),
149    } {
150        expr = next;
151    }
152
153    false
154}
155
156#[cfg(feature = "printing")]
157pub(crate) fn trailing_unparameterized_path(mut ty: &Type) -> bool {
158    loop {
159        match ty {
160            Type::BareFn(t) => match &t.output {
161                ReturnType::Default => return false,
162                ReturnType::Type(_, ret) => ty = ret,
163            },
164            Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) {
165                ControlFlow::Break(trailing_path) => return trailing_path,
166                ControlFlow::Continue(t) => ty = t,
167            },
168            Type::Path(t) => match last_type_in_path(&t.path) {
169                ControlFlow::Break(trailing_path) => return trailing_path,
170                ControlFlow::Continue(t) => ty = t,
171            },
172            Type::Ptr(t) => ty = &t.elem,
173            Type::Reference(t) => ty = &t.elem,
174            Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) {
175                ControlFlow::Break(trailing_path) => return trailing_path,
176                ControlFlow::Continue(t) => ty = t,
177            },
178
179            Type::Array(_)
180            | Type::Group(_)
181            | Type::Infer(_)
182            | Type::Macro(_)
183            | Type::Never(_)
184            | Type::Paren(_)
185            | Type::Slice(_)
186            | Type::Tuple(_)
187            | Type::Verbatim(_) => return false,
188        }
189    }
190
191    fn last_type_in_path(path: &Path) -> ControlFlow<bool, &Type> {
192        match &path.segments.last().unwrap().arguments {
193            PathArguments::None => ControlFlow::Break(true),
194            PathArguments::AngleBracketed(_) => ControlFlow::Break(false),
195            PathArguments::Parenthesized(arg) => match &arg.output {
196                ReturnType::Default => ControlFlow::Break(false),
197                ReturnType::Type(_, ret) => ControlFlow::Continue(ret),
198            },
199        }
200    }
201
202    fn last_type_in_bounds(
203        bounds: &Punctuated<TypeParamBound, Token![+]>,
204    ) -> ControlFlow<bool, &Type> {
205        match bounds.last().unwrap() {
206            TypeParamBound::Trait(t) => last_type_in_path(&t.path),
207            TypeParamBound::Lifetime(_)
208            | TypeParamBound::PreciseCapture(_)
209            | TypeParamBound::Verbatim(_) => ControlFlow::Break(false),
210        }
211    }
212}
213
214/// Whether the expression's first token is the label of a loop/block.
215#[cfg(all(feature = "printing", feature = "full"))]
216pub(crate) fn expr_leading_label(mut expr: &Expr) -> bool {
217    loop {
218        match expr {
219            Expr::Block(e) => return e.label.is_some(),
220            Expr::ForLoop(e) => return e.label.is_some(),
221            Expr::Loop(e) => return e.label.is_some(),
222            Expr::While(e) => return e.label.is_some(),
223
224            Expr::Assign(e) => expr = &e.left,
225            Expr::Await(e) => expr = &e.base,
226            Expr::Binary(e) => expr = &e.left,
227            Expr::Call(e) => expr = &e.func,
228            Expr::Cast(e) => expr = &e.expr,
229            Expr::Field(e) => expr = &e.base,
230            Expr::Index(e) => expr = &e.expr,
231            Expr::MethodCall(e) => expr = &e.receiver,
232            Expr::Range(e) => match &e.start {
233                Some(start) => expr = start,
234                None => return false,
235            },
236            Expr::Try(e) => expr = &e.expr,
237
238            Expr::Array(_)
239            | Expr::Async(_)
240            | Expr::Break(_)
241            | Expr::Closure(_)
242            | Expr::Const(_)
243            | Expr::Continue(_)
244            | Expr::Group(_)
245            | Expr::If(_)
246            | Expr::Infer(_)
247            | Expr::Let(_)
248            | Expr::Lit(_)
249            | Expr::Macro(_)
250            | Expr::Match(_)
251            | Expr::Paren(_)
252            | Expr::Path(_)
253            | Expr::RawAddr(_)
254            | Expr::Reference(_)
255            | Expr::Repeat(_)
256            | Expr::Return(_)
257            | Expr::Struct(_)
258            | Expr::TryBlock(_)
259            | Expr::Tuple(_)
260            | Expr::Unary(_)
261            | Expr::Unsafe(_)
262            | Expr::Verbatim(_)
263            | Expr::Yield(_) => return false,
264        }
265    }
266}
267
268/// Whether the expression's last token is `}`.
269#[cfg(feature = "full")]
270pub(crate) fn expr_trailing_brace(mut expr: &Expr) -> bool {
271    loop {
272        match expr {
273            Expr::Async(_)
274            | Expr::Block(_)
275            | Expr::Const(_)
276            | Expr::ForLoop(_)
277            | Expr::If(_)
278            | Expr::Loop(_)
279            | Expr::Match(_)
280            | Expr::Struct(_)
281            | Expr::TryBlock(_)
282            | Expr::Unsafe(_)
283            | Expr::While(_) => return true,
284
285            Expr::Assign(e) => expr = &e.right,
286            Expr::Binary(e) => expr = &e.right,
287            Expr::Break(e) => match &e.expr {
288                Some(e) => expr = e,
289                None => return false,
290            },
291            Expr::Cast(e) => return type_trailing_brace(&e.ty),
292            Expr::Closure(e) => expr = &e.body,
293            Expr::Let(e) => expr = &e.expr,
294            Expr::Macro(e) => return e.mac.delimiter.is_brace(),
295            Expr::Range(e) => match &e.end {
296                Some(end) => expr = end,
297                None => return false,
298            },
299            Expr::RawAddr(e) => expr = &e.expr,
300            Expr::Reference(e) => expr = &e.expr,
301            Expr::Return(e) => match &e.expr {
302                Some(e) => expr = e,
303                None => return false,
304            },
305            Expr::Unary(e) => expr = &e.expr,
306            Expr::Verbatim(e) => return tokens_trailing_brace(e),
307            Expr::Yield(e) => match &e.expr {
308                Some(e) => expr = e,
309                None => return false,
310            },
311
312            Expr::Array(_)
313            | Expr::Await(_)
314            | Expr::Call(_)
315            | Expr::Continue(_)
316            | Expr::Field(_)
317            | Expr::Group(_)
318            | Expr::Index(_)
319            | Expr::Infer(_)
320            | Expr::Lit(_)
321            | Expr::MethodCall(_)
322            | Expr::Paren(_)
323            | Expr::Path(_)
324            | Expr::Repeat(_)
325            | Expr::Try(_)
326            | Expr::Tuple(_) => return false,
327        }
328    }
329
330    fn type_trailing_brace(mut ty: &Type) -> bool {
331        loop {
332            match ty {
333                Type::BareFn(t) => match &t.output {
334                    ReturnType::Default => return false,
335                    ReturnType::Type(_, ret) => ty = ret,
336                },
337                Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) {
338                    ControlFlow::Break(trailing_brace) => return trailing_brace,
339                    ControlFlow::Continue(t) => ty = t,
340                },
341                Type::Macro(t) => return t.mac.delimiter.is_brace(),
342                Type::Path(t) => match last_type_in_path(&t.path) {
343                    Some(t) => ty = t,
344                    None => return false,
345                },
346                Type::Ptr(t) => ty = &t.elem,
347                Type::Reference(t) => ty = &t.elem,
348                Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) {
349                    ControlFlow::Break(trailing_brace) => return trailing_brace,
350                    ControlFlow::Continue(t) => ty = t,
351                },
352                Type::Verbatim(t) => return tokens_trailing_brace(t),
353
354                Type::Array(_)
355                | Type::Group(_)
356                | Type::Infer(_)
357                | Type::Never(_)
358                | Type::Paren(_)
359                | Type::Slice(_)
360                | Type::Tuple(_) => return false,
361            }
362        }
363    }
364
365    fn last_type_in_path(path: &Path) -> Option<&Type> {
366        match &path.segments.last().unwrap().arguments {
367            PathArguments::None | PathArguments::AngleBracketed(_) => None,
368            PathArguments::Parenthesized(arg) => match &arg.output {
369                ReturnType::Default => None,
370                ReturnType::Type(_, ret) => Some(ret),
371            },
372        }
373    }
374
375    fn last_type_in_bounds(
376        bounds: &Punctuated<TypeParamBound, Token![+]>,
377    ) -> ControlFlow<bool, &Type> {
378        match bounds.last().unwrap() {
379            TypeParamBound::Trait(t) => match last_type_in_path(&t.path) {
380                Some(t) => ControlFlow::Continue(t),
381                None => ControlFlow::Break(false),
382            },
383            TypeParamBound::Lifetime(_) | TypeParamBound::PreciseCapture(_) => {
384                ControlFlow::Break(false)
385            }
386            TypeParamBound::Verbatim(t) => ControlFlow::Break(tokens_trailing_brace(t)),
387        }
388    }
389
390    fn tokens_trailing_brace(tokens: &TokenStream) -> bool {
391        if let Some(TokenTree::Group(last)) = tokens.clone().into_iter().last() {
392            last.delimiter() == Delimiter::Brace
393        } else {
394            false
395        }
396    }
397}