Skip to content

Commit 5af861c

Browse files
committed
Disallow ambiguous attributes on expressions
1 parent 6c6b302 commit 5af861c

15 files changed

+141
-49
lines changed

compiler/rustc_parse/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,8 @@ parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allo
620620
parse_out_of_range_hex_escape = out of range hex escape
621621
.label = must be a character in the range [\x00-\x7f]
622622
623+
parse_outer_attr_ambiguous = ambiguous outer attributes
624+
623625
parse_outer_attr_explanation = outer attributes, like `#[test]`, annotate the item following them
624626
625627
parse_outer_attribute_not_allowed_on_if_else = outer attributes are not allowed on `if` and `else` branches

compiler/rustc_parse/src/errors.rs

+9
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,15 @@ pub(crate) struct OuterAttributeNotAllowedOnIfElse {
495495
pub attributes: Span,
496496
}
497497

498+
#[derive(Diagnostic)]
499+
#[diag(parse_outer_attr_ambiguous)]
500+
pub(crate) struct AmbiguousOuterAttributes {
501+
#[primary_span]
502+
pub span: Span,
503+
#[subdiagnostic]
504+
pub sugg: WrapInParentheses,
505+
}
506+
498507
#[derive(Diagnostic)]
499508
#[diag(parse_missing_in_in_for_loop)]
500509
pub(crate) struct MissingInInForLoop {

compiler/rustc_parse/src/parser/expr.rs

+19-13
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,9 @@ impl<'a> Parser<'a> {
327327
this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::NotYetParsed)
328328
})?;
329329

330-
let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span);
330+
self.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span);
331+
let span = lhs_span.to(rhs.span);
332+
331333
lhs = match op {
332334
AssocOp::Add
333335
| AssocOp::Subtract
@@ -426,6 +428,18 @@ impl<'a> Parser<'a> {
426428
});
427429
}
428430

431+
fn error_ambiguous_outer_attrs(&self, lhs: &P<Expr>, lhs_span: Span, rhs_span: Span) {
432+
if let Some(attr) = lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer) {
433+
self.dcx().emit_err(errors::AmbiguousOuterAttributes {
434+
span: attr.span.to(rhs_span),
435+
sugg: errors::WrapInParentheses::Expression {
436+
left: attr.span.shrink_to_lo(),
437+
right: lhs_span.shrink_to_hi(),
438+
},
439+
});
440+
}
441+
}
442+
429443
/// Possibly translate the current token to an associative operator.
430444
/// The method does not advance the current token.
431445
///
@@ -506,7 +520,8 @@ impl<'a> Parser<'a> {
506520
None
507521
};
508522
let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span);
509-
let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span);
523+
self.error_ambiguous_outer_attrs(&lhs, lhs.span, rhs_span);
524+
let span = lhs.span.to(rhs_span);
510525
let limits =
511526
if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed };
512527
let range = self.mk_range(Some(lhs), rhs, limits);
@@ -722,7 +737,8 @@ impl<'a> Parser<'a> {
722737
expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind,
723738
) -> PResult<'a, P<Expr>> {
724739
let mk_expr = |this: &mut Self, lhs: P<Expr>, rhs: P<Ty>| {
725-
this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs))
740+
this.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span);
741+
this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs))
726742
};
727743

728744
// Save the state of the parser before parsing type normally, in case there is a
@@ -3807,16 +3823,6 @@ impl<'a> Parser<'a> {
38073823
self.mk_expr(span, ExprKind::Err(guar))
38083824
}
38093825

3810-
/// Create expression span ensuring the span of the parent node
3811-
/// is larger than the span of lhs and rhs, including the attributes.
3812-
fn mk_expr_sp(&self, lhs: &P<Expr>, lhs_span: Span, rhs_span: Span) -> Span {
3813-
lhs.attrs
3814-
.iter()
3815-
.find(|a| a.style == AttrStyle::Outer)
3816-
.map_or(lhs_span, |a| a.span)
3817-
.to(rhs_span)
3818-
}
3819-
38203826
fn collect_tokens_for_expr(
38213827
&mut self,
38223828
attrs: AttrWrapper,

src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn foo(
1616

1717
fn skip_on_statements() {
1818
#[rustfmt::skip]
19-
5+3;
19+
{ 5+3; }
2020
}
2121

2222
#[rustfmt::skip]
@@ -33,11 +33,11 @@ mod foo {
3333
#[clippy::msrv = "1.29"]
3434
fn msrv_1_29() {
3535
#[cfg_attr(rustfmt, rustfmt::skip)]
36-
1+29;
36+
{ 1+29; }
3737
}
3838

3939
#[clippy::msrv = "1.30"]
4040
fn msrv_1_30() {
4141
#[rustfmt::skip]
42-
1+30;
42+
{ 1+30; }
4343
}

src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn foo(
1616

1717
fn skip_on_statements() {
1818
#[cfg_attr(rustfmt, rustfmt::skip)]
19-
5+3;
19+
{ 5+3; }
2020
}
2121

2222
#[cfg_attr(rustfmt, rustfmt_skip)]
@@ -33,11 +33,11 @@ mod foo {
3333
#[clippy::msrv = "1.29"]
3434
fn msrv_1_29() {
3535
#[cfg_attr(rustfmt, rustfmt::skip)]
36-
1+29;
36+
{ 1+29; }
3737
}
3838

3939
#[clippy::msrv = "1.30"]
4040
fn msrv_1_30() {
4141
#[cfg_attr(rustfmt, rustfmt::skip)]
42-
1+30;
42+
{ 1+30; }
4343
}

src/tools/rustfmt/tests/source/attrib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ type Os = NoSource;
214214
// #3313
215215
fn stmt_expr_attributes() {
216216
let foo ;
217-
#[must_use]
218-
foo = false ;
217+
(#[must_use]
218+
foo) = false ;
219219
}
220220

221221
// #3509

src/tools/rustfmt/tests/target/attrib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ type Os = NoSource;
248248
// #3313
249249
fn stmt_expr_attributes() {
250250
let foo;
251-
#[must_use]
252-
foo = false;
251+
(#[must_use]
252+
foo) = false;
253253
}
254254

255255
// #3509

tests/pretty/ast-stmt-expr-attr.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ fn syntax() {
1313
let _ = #[attr] ();
1414
let _ = #[attr] (#[attr] 0,);
1515
let _ = #[attr] (#[attr] 0, 0);
16-
let _ = #[attr] 0 + #[attr] 0;
17-
let _ = #[attr] 0 / #[attr] 0;
18-
let _ = #[attr] 0 & #[attr] 0;
19-
let _ = #[attr] 0 % #[attr] 0;
16+
let _ = (#[attr] 0) + #[attr] 0;
17+
let _ = (#[attr] 0) / #[attr] 0;
18+
let _ = (#[attr] 0) & #[attr] 0;
19+
let _ = (#[attr] 0) % #[attr] 0;
2020
let _ = #[attr] (0 + 0);
2121
let _ = #[attr] !0;
2222
let _ = #[attr] -0;
2323
let _ = #[attr] false;
2424
let _ = #[attr] 0;
2525
let _ = #[attr] 'c';
26-
let _ = #[attr] x as Y;
26+
let _ = (#[attr] x) as Y;
2727
let _ = #[attr] (x as Y);
2828
let _ =
2929
#[attr] while true {
@@ -88,18 +88,18 @@ fn syntax() {
8888
let _ = ();
8989
foo
9090
};
91-
let _ = #[attr] x = y;
91+
let _ = (#[attr] x) = y;
9292
let _ = #[attr] (x = y);
93-
let _ = #[attr] x += y;
93+
let _ = (#[attr] x) += y;
9494
let _ = #[attr] (x += y);
9595
let _ = #[attr] foo.bar;
9696
let _ = (#[attr] foo).bar;
9797
let _ = #[attr] foo.0;
9898
let _ = (#[attr] foo).0;
9999
let _ = #[attr] foo[bar];
100100
let _ = (#[attr] foo)[bar];
101-
let _ = #[attr] 0..#[attr] 0;
102-
let _ = #[attr] 0..;
101+
let _ = (#[attr] 0)..#[attr] 0;
102+
let _ = (#[attr] 0)..;
103103
let _ = #[attr] (0..0);
104104
let _ = #[attr] (0..);
105105
let _ = #[attr] (..0);

tests/pretty/stmt_expr_attributes.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,13 @@ fn _11() {
148148
let _ = #[rustc_dummy] (0);
149149
let _ = #[rustc_dummy] (0,);
150150
let _ = #[rustc_dummy] (0, 0);
151-
let _ = #[rustc_dummy] 0 + #[rustc_dummy] 0;
151+
let _ = (#[rustc_dummy] 0) + #[rustc_dummy] 0;
152152
let _ = #[rustc_dummy] !0;
153153
let _ = #[rustc_dummy] -0i32;
154154
let _ = #[rustc_dummy] false;
155155
let _ = #[rustc_dummy] 'c';
156156
let _ = #[rustc_dummy] 0;
157-
let _ = #[rustc_dummy] 0 as usize;
157+
let _ = (#[rustc_dummy] 0) as usize;
158158
let _ =
159159
#[rustc_dummy] while false {
160160
#![rustc_dummy]
@@ -214,8 +214,8 @@ fn _11() {
214214
#![rustc_dummy]
215215
};
216216
let mut x = 0;
217-
let _ = #[rustc_dummy] x = 15;
218-
let _ = #[rustc_dummy] x += 15;
217+
let _ = (#[rustc_dummy] x) = 15;
218+
let _ = (#[rustc_dummy] x) += 15;
219219
let s = Foo { data: () };
220220
let _ = #[rustc_dummy] s.data;
221221
let _ = (#[rustc_dummy] s).data;
@@ -225,8 +225,8 @@ fn _11() {
225225
let v = vec!(0);
226226
let _ = #[rustc_dummy] v[0];
227227
let _ = (#[rustc_dummy] v)[0];
228-
let _ = #[rustc_dummy] 0..#[rustc_dummy] 0;
229-
let _ = #[rustc_dummy] 0..;
228+
let _ = (#[rustc_dummy] 0)..#[rustc_dummy] 0;
229+
let _ = (#[rustc_dummy] 0)..;
230230
let _ = #[rustc_dummy] (0..0);
231231
let _ = #[rustc_dummy] (0..);
232232
let _ = #[rustc_dummy] (..0);

tests/ui/lint/unused/unused-doc-comments-edge-cases.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ fn doc_comment_between_if_else(num: u8) -> bool {
2020
}
2121

2222
fn doc_comment_on_expr(num: u8) -> bool {
23-
/// useless doc comment
23+
(/// useless doc comment
2424
//~^ ERROR: attributes on expressions are experimental
2525
//~| ERROR: unused doc comment
26-
num == 3
26+
num) == 3
2727
}
2828

2929
fn doc_comment_on_expr_field() -> bool {

tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr

+7-7
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ LL | else {
55
| ^^^^ expected expression
66

77
error[E0658]: attributes on expressions are experimental
8-
--> $DIR/unused-doc-comments-edge-cases.rs:23:5
8+
--> $DIR/unused-doc-comments-edge-cases.rs:23:6
99
|
10-
LL | /// useless doc comment
11-
| ^^^^^^^^^^^^^^^^^^^^^^^
10+
LL | (/// useless doc comment
11+
| ^^^^^^^^^^^^^^^^^^^^^^^
1212
|
1313
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
1414
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
@@ -32,12 +32,12 @@ LL | #![deny(unused_doc_comments)]
3232
| ^^^^^^^^^^^^^^^^^^^
3333

3434
error: unused doc comment
35-
--> $DIR/unused-doc-comments-edge-cases.rs:23:5
35+
--> $DIR/unused-doc-comments-edge-cases.rs:23:6
3636
|
37-
LL | /// useless doc comment
38-
| ^^^^^^^^^^^^^^^^^^^^^^^
37+
LL | (/// useless doc comment
38+
| ^^^^^^^^^^^^^^^^^^^^^^^
3939
...
40-
LL | num == 3
40+
LL | num) == 3
4141
| --- rustdoc does not generate documentation for expressions
4242
|
4343
= help: use `//` for a plain comment
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ run-rustfix
2+
#![feature(stmt_expr_attributes)]
3+
#![allow(unused_assignments, unused_attributes)]
4+
5+
fn main() {
6+
let mut x = (#[deprecated] 1) + 2; //~ ERROR ambiguous
7+
8+
(#[deprecated] x) = 4; //~ ERROR ambiguous
9+
10+
x = (#[deprecated] 5) as i32; //~ ERROR ambiguous
11+
12+
let _r = (#[deprecated] 1)..6; //~ ERROR ambiguous
13+
14+
println!("{}", x);
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ run-rustfix
2+
#![feature(stmt_expr_attributes)]
3+
#![allow(unused_assignments, unused_attributes)]
4+
5+
fn main() {
6+
let mut x = #[deprecated] 1 + 2; //~ ERROR ambiguous
7+
8+
#[deprecated] x = 4; //~ ERROR ambiguous
9+
10+
x = #[deprecated] 5 as i32; //~ ERROR ambiguous
11+
12+
let _r = #[deprecated] 1..6; //~ ERROR ambiguous
13+
14+
println!("{}", x);
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
error: ambiguous outer attributes
2+
--> $DIR/attr-binary-expr-ambigous.rs:6:17
3+
|
4+
LL | let mut x = #[deprecated] 1 + 2;
5+
| ^^^^^^^^^^^^^^^^^^^
6+
|
7+
help: wrap the expression in parentheses
8+
|
9+
LL | let mut x = (#[deprecated] 1) + 2;
10+
| + +
11+
12+
error: ambiguous outer attributes
13+
--> $DIR/attr-binary-expr-ambigous.rs:8:5
14+
|
15+
LL | #[deprecated] x = 4;
16+
| ^^^^^^^^^^^^^^^^^^^
17+
|
18+
help: wrap the expression in parentheses
19+
|
20+
LL | (#[deprecated] x) = 4;
21+
| + +
22+
23+
error: ambiguous outer attributes
24+
--> $DIR/attr-binary-expr-ambigous.rs:10:9
25+
|
26+
LL | x = #[deprecated] 5 as i32;
27+
| ^^^^^^^^^^^^^^^^^^^^^^
28+
|
29+
help: wrap the expression in parentheses
30+
|
31+
LL | x = (#[deprecated] 5) as i32;
32+
| + +
33+
34+
error: ambiguous outer attributes
35+
--> $DIR/attr-binary-expr-ambigous.rs:12:14
36+
|
37+
LL | let _r = #[deprecated] 1..6;
38+
| ^^^^^^^^^^^^^^^^^^
39+
|
40+
help: wrap the expression in parentheses
41+
|
42+
LL | let _r = (#[deprecated] 1)..6;
43+
| + +
44+
45+
error: aborting due to 4 previous errors
46+

tests/ui/proc-macro/issue-81555.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,5 @@ use test_macros::identity_attr;
1010
fn main() {
1111
let _x;
1212
let y = ();
13-
#[identity_attr]
14-
_x = y;
13+
(#[identity_attr] _x) = y;
1514
}

0 commit comments

Comments
 (0)