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