Skip to content

Commit 1dd1aae

Browse files
authored
Merge pull request #88 from dtolnay/precedence
Synthesize parentheses for precedence
2 parents b359ab0 + 5b476df commit 1dd1aae

14 files changed

+1525
-222
lines changed

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ verbatim = ["syn/parsing"]
1919

2020
[dependencies]
2121
proc-macro2 = { version = "1.0.80", default-features = false }
22-
syn = { version = "2.0.81", default-features = false, features = ["full"] }
22+
syn = { version = "2.0.95", default-features = false, features = ["full"] }
2323

2424
[dev-dependencies]
2525
indoc = "2"
2626
proc-macro2 = { version = "1.0.80", default-features = false }
2727
quote = { version = "1.0.35", default-features = false }
28-
syn = { version = "2.0.81", default-features = false, features = ["parsing"] }
28+
syn = { version = "2.0.95", default-features = false, features = ["parsing"] }
2929

3030
[lib]
3131
doc-scrape-examples = false

src/attr.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::algorithm::Printer;
2+
use crate::fixup::FixupContext;
23
use crate::path::PathKind;
34
use crate::INDENT;
45
use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
@@ -102,7 +103,7 @@ impl Printer {
102103
fn meta_name_value(&mut self, meta: &MetaNameValue) {
103104
self.path(&meta.path, PathKind::Simple);
104105
self.word(" = ");
105-
self.expr(&meta.value);
106+
self.expr(&meta.value, FixupContext::NONE);
106107
}
107108

108109
fn attr_tokens(&mut self, tokens: TokenStream) {

src/classify.rs

+324
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
use proc_macro2::{Delimiter, TokenStream, TokenTree};
2+
use std::ops::ControlFlow;
3+
use syn::punctuated::Punctuated;
4+
use syn::{Expr, MacroDelimiter, Path, PathArguments, ReturnType, Token, Type, TypeParamBound};
5+
6+
pub(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+
13+
pub(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(_) // both under ExprKind::Block in rustc
20+
| 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+
64+
pub(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+
126+
/// Whether the expression's first token is the label of a loop/block.
127+
pub(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+
188+
/// Whether the expression's last token is `}`.
189+
pub(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+
}

src/data.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::algorithm::Printer;
2+
use crate::fixup::FixupContext;
23
use crate::iter::IterDelimited;
34
use crate::path::PathKind;
45
use crate::INDENT;
@@ -31,7 +32,7 @@ impl Printer {
3132
}
3233
if let Some((_eq_token, discriminant)) = &variant.discriminant {
3334
self.word(" = ");
34-
self.expr(discriminant);
35+
self.expr(discriminant, FixupContext::NONE);
3536
}
3637
}
3738

0 commit comments

Comments
 (0)