diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 32a0df2f619ad..873095dca8722 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -624,6 +624,8 @@ parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allo parse_out_of_range_hex_escape = out of range hex escape .label = must be a character in the range [\x00-\x7f] +parse_outer_attr_ambiguous = ambiguous outer attributes + parse_outer_attr_explanation = outer attributes, like `#[test]`, annotate the item following them parse_outer_attribute_not_allowed_on_if_else = outer attributes are not allowed on `if` and `else` branches diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index ca8de4240f450..b77045ee34e0d 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -495,6 +495,15 @@ pub(crate) struct OuterAttributeNotAllowedOnIfElse { pub attributes: Span, } +#[derive(Diagnostic)] +#[diag(parse_outer_attr_ambiguous)] +pub(crate) struct AmbiguousOuterAttributes { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: WrapInParentheses, +} + #[derive(Diagnostic)] #[diag(parse_missing_in_in_for_loop)] pub(crate) struct MissingInInForLoop { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8a4546107186b..8ed2a6edf1aec 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -327,7 +327,9 @@ impl<'a> Parser<'a> { this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::NotYetParsed) })?; - let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); + self.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span); + let span = lhs_span.to(rhs.span); + lhs = match op { AssocOp::Add | AssocOp::Subtract @@ -426,6 +428,18 @@ impl<'a> Parser<'a> { }); } + fn error_ambiguous_outer_attrs(&self, lhs: &P, lhs_span: Span, rhs_span: Span) { + if let Some(attr) = lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer) { + self.dcx().emit_err(errors::AmbiguousOuterAttributes { + span: attr.span.to(rhs_span), + sugg: errors::WrapInParentheses::Expression { + left: attr.span.shrink_to_lo(), + right: lhs_span.shrink_to_hi(), + }, + }); + } + } + /// Possibly translate the current token to an associative operator. /// The method does not advance the current token. /// @@ -506,7 +520,8 @@ impl<'a> Parser<'a> { None }; let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span); - let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span); + self.error_ambiguous_outer_attrs(&lhs, lhs.span, rhs_span); + let span = lhs.span.to(rhs_span); let limits = if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; let range = self.mk_range(Some(lhs), rhs, limits); @@ -722,7 +737,8 @@ impl<'a> Parser<'a> { expr_kind: fn(P, P) -> ExprKind, ) -> PResult<'a, P> { let mk_expr = |this: &mut Self, lhs: P, rhs: P| { - this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs)) + this.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span); + this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs)) }; // Save the state of the parser before parsing type normally, in case there is a @@ -3813,16 +3829,6 @@ impl<'a> Parser<'a> { self.mk_expr(span, ExprKind::Err(guar)) } - /// Create expression span ensuring the span of the parent node - /// is larger than the span of lhs and rhs, including the attributes. - fn mk_expr_sp(&self, lhs: &P, lhs_span: Span, rhs_span: Span) -> Span { - lhs.attrs - .iter() - .find(|a| a.style == AttrStyle::Outer) - .map_or(lhs_span, |a| a.span) - .to(rhs_span) - } - fn collect_tokens_for_expr( &mut self, attrs: AttrWrapper, diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed index 05d5b3d10eaf8..84dac431169ab 100644 --- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed @@ -16,7 +16,7 @@ fn foo( fn skip_on_statements() { #[rustfmt::skip] - 5+3; + { 5+3; } } #[rustfmt::skip] @@ -33,11 +33,11 @@ mod foo { #[clippy::msrv = "1.29"] fn msrv_1_29() { #[cfg_attr(rustfmt, rustfmt::skip)] - 1+29; + { 1+29; } } #[clippy::msrv = "1.30"] fn msrv_1_30() { #[rustfmt::skip] - 1+30; + { 1+30; } } diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs index bc29e20210e8a..4ab5c70e13b5b 100644 --- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs @@ -16,7 +16,7 @@ fn foo( fn skip_on_statements() { #[cfg_attr(rustfmt, rustfmt::skip)] - 5+3; + { 5+3; } } #[cfg_attr(rustfmt, rustfmt_skip)] @@ -33,11 +33,11 @@ mod foo { #[clippy::msrv = "1.29"] fn msrv_1_29() { #[cfg_attr(rustfmt, rustfmt::skip)] - 1+29; + { 1+29; } } #[clippy::msrv = "1.30"] fn msrv_1_30() { #[cfg_attr(rustfmt, rustfmt::skip)] - 1+30; + { 1+30; } } diff --git a/src/tools/rustfmt/tests/source/attrib.rs b/src/tools/rustfmt/tests/source/attrib.rs index d45fba5522436..fc13cd02b03ef 100644 --- a/src/tools/rustfmt/tests/source/attrib.rs +++ b/src/tools/rustfmt/tests/source/attrib.rs @@ -214,8 +214,8 @@ type Os = NoSource; // #3313 fn stmt_expr_attributes() { let foo ; - #[must_use] - foo = false ; + (#[must_use] + foo) = false ; } // #3509 diff --git a/src/tools/rustfmt/tests/target/attrib.rs b/src/tools/rustfmt/tests/target/attrib.rs index 7e61f68d76a14..7b3309676de2f 100644 --- a/src/tools/rustfmt/tests/target/attrib.rs +++ b/src/tools/rustfmt/tests/target/attrib.rs @@ -248,8 +248,8 @@ type Os = NoSource; // #3313 fn stmt_expr_attributes() { let foo; - #[must_use] - foo = false; + (#[must_use] + foo) = false; } // #3509 diff --git a/tests/pretty/ast-stmt-expr-attr.rs b/tests/pretty/ast-stmt-expr-attr.rs index fd7272a1b1fd3..899f7173f704e 100644 --- a/tests/pretty/ast-stmt-expr-attr.rs +++ b/tests/pretty/ast-stmt-expr-attr.rs @@ -13,17 +13,17 @@ fn syntax() { let _ = #[attr] (); let _ = #[attr] (#[attr] 0,); let _ = #[attr] (#[attr] 0, 0); - let _ = #[attr] 0 + #[attr] 0; - let _ = #[attr] 0 / #[attr] 0; - let _ = #[attr] 0 & #[attr] 0; - let _ = #[attr] 0 % #[attr] 0; + let _ = (#[attr] 0) + #[attr] 0; + let _ = (#[attr] 0) / #[attr] 0; + let _ = (#[attr] 0) & #[attr] 0; + let _ = (#[attr] 0) % #[attr] 0; let _ = #[attr] (0 + 0); let _ = #[attr] !0; let _ = #[attr] -0; let _ = #[attr] false; let _ = #[attr] 0; let _ = #[attr] 'c'; - let _ = #[attr] x as Y; + let _ = (#[attr] x) as Y; let _ = #[attr] (x as Y); let _ = #[attr] while true { @@ -88,9 +88,9 @@ fn syntax() { let _ = (); foo }; - let _ = #[attr] x = y; + let _ = (#[attr] x) = y; let _ = #[attr] (x = y); - let _ = #[attr] x += y; + let _ = (#[attr] x) += y; let _ = #[attr] (x += y); let _ = #[attr] foo.bar; let _ = (#[attr] foo).bar; @@ -98,8 +98,8 @@ fn syntax() { let _ = (#[attr] foo).0; let _ = #[attr] foo[bar]; let _ = (#[attr] foo)[bar]; - let _ = #[attr] 0..#[attr] 0; - let _ = #[attr] 0..; + let _ = (#[attr] 0)..#[attr] 0; + let _ = (#[attr] 0)..; let _ = #[attr] (0..0); let _ = #[attr] (0..); let _ = #[attr] (..0); diff --git a/tests/pretty/stmt_expr_attributes.rs b/tests/pretty/stmt_expr_attributes.rs index 98ad98b863ac6..5076adf5aa47b 100644 --- a/tests/pretty/stmt_expr_attributes.rs +++ b/tests/pretty/stmt_expr_attributes.rs @@ -148,13 +148,13 @@ fn _11() { let _ = #[rustc_dummy] (0); let _ = #[rustc_dummy] (0,); let _ = #[rustc_dummy] (0, 0); - let _ = #[rustc_dummy] 0 + #[rustc_dummy] 0; + let _ = (#[rustc_dummy] 0) + #[rustc_dummy] 0; let _ = #[rustc_dummy] !0; let _ = #[rustc_dummy] -0i32; let _ = #[rustc_dummy] false; let _ = #[rustc_dummy] 'c'; let _ = #[rustc_dummy] 0; - let _ = #[rustc_dummy] 0 as usize; + let _ = (#[rustc_dummy] 0) as usize; let _ = #[rustc_dummy] while false { #![rustc_dummy] @@ -214,8 +214,8 @@ fn _11() { #![rustc_dummy] }; let mut x = 0; - let _ = #[rustc_dummy] x = 15; - let _ = #[rustc_dummy] x += 15; + let _ = (#[rustc_dummy] x) = 15; + let _ = (#[rustc_dummy] x) += 15; let s = Foo { data: () }; let _ = #[rustc_dummy] s.data; let _ = (#[rustc_dummy] s).data; @@ -225,8 +225,8 @@ fn _11() { let v = vec!(0); let _ = #[rustc_dummy] v[0]; let _ = (#[rustc_dummy] v)[0]; - let _ = #[rustc_dummy] 0..#[rustc_dummy] 0; - let _ = #[rustc_dummy] 0..; + let _ = (#[rustc_dummy] 0)..#[rustc_dummy] 0; + let _ = (#[rustc_dummy] 0)..; let _ = #[rustc_dummy] (0..0); let _ = #[rustc_dummy] (0..); let _ = #[rustc_dummy] (..0); diff --git a/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs b/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs index ba32fb566e83b..0f9eac93930bc 100644 --- a/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs +++ b/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs @@ -20,10 +20,10 @@ fn doc_comment_between_if_else(num: u8) -> bool { } fn doc_comment_on_expr(num: u8) -> bool { - /// useless doc comment + (/// useless doc comment //~^ ERROR: attributes on expressions are experimental //~| ERROR: unused doc comment - num == 3 + num) == 3 } fn doc_comment_on_expr_field() -> bool { diff --git a/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr b/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr index 55e4834e6707a..add85b2f5e043 100644 --- a/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr +++ b/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr @@ -5,10 +5,10 @@ LL | else { | ^^^^ expected expression error[E0658]: attributes on expressions are experimental - --> $DIR/unused-doc-comments-edge-cases.rs:23:5 + --> $DIR/unused-doc-comments-edge-cases.rs:23:6 | -LL | /// useless doc comment - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | (/// useless doc comment + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #15701 for more information = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable @@ -32,12 +32,12 @@ LL | #![deny(unused_doc_comments)] | ^^^^^^^^^^^^^^^^^^^ error: unused doc comment - --> $DIR/unused-doc-comments-edge-cases.rs:23:5 + --> $DIR/unused-doc-comments-edge-cases.rs:23:6 | -LL | /// useless doc comment - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | (/// useless doc comment + | ^^^^^^^^^^^^^^^^^^^^^^^ ... -LL | num == 3 +LL | num) == 3 | --- rustdoc does not generate documentation for expressions | = help: use `//` for a plain comment diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed b/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed new file mode 100644 index 0000000000000..aae71ede77132 --- /dev/null +++ b/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed @@ -0,0 +1,15 @@ +//@ run-rustfix +#![feature(stmt_expr_attributes)] +#![allow(unused_assignments, unused_attributes)] + +fn main() { + let mut x = (#[deprecated] 1) + 2; //~ ERROR ambiguous + + (#[deprecated] x) = 4; //~ ERROR ambiguous + + x = (#[deprecated] 5) as i32; //~ ERROR ambiguous + + let _r = (#[deprecated] 1)..6; //~ ERROR ambiguous + + println!("{}", x); +} diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs b/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs new file mode 100644 index 0000000000000..613e01d743bdd --- /dev/null +++ b/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs @@ -0,0 +1,15 @@ +//@ run-rustfix +#![feature(stmt_expr_attributes)] +#![allow(unused_assignments, unused_attributes)] + +fn main() { + let mut x = #[deprecated] 1 + 2; //~ ERROR ambiguous + + #[deprecated] x = 4; //~ ERROR ambiguous + + x = #[deprecated] 5 as i32; //~ ERROR ambiguous + + let _r = #[deprecated] 1..6; //~ ERROR ambiguous + + println!("{}", x); +} diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr b/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr new file mode 100644 index 0000000000000..0430570fd49d9 --- /dev/null +++ b/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr @@ -0,0 +1,46 @@ +error: ambiguous outer attributes + --> $DIR/attr-binary-expr-ambigous.rs:6:17 + | +LL | let mut x = #[deprecated] 1 + 2; + | ^^^^^^^^^^^^^^^^^^^ + | +help: wrap the expression in parentheses + | +LL | let mut x = (#[deprecated] 1) + 2; + | + + + +error: ambiguous outer attributes + --> $DIR/attr-binary-expr-ambigous.rs:8:5 + | +LL | #[deprecated] x = 4; + | ^^^^^^^^^^^^^^^^^^^ + | +help: wrap the expression in parentheses + | +LL | (#[deprecated] x) = 4; + | + + + +error: ambiguous outer attributes + --> $DIR/attr-binary-expr-ambigous.rs:10:9 + | +LL | x = #[deprecated] 5 as i32; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: wrap the expression in parentheses + | +LL | x = (#[deprecated] 5) as i32; + | + + + +error: ambiguous outer attributes + --> $DIR/attr-binary-expr-ambigous.rs:12:14 + | +LL | let _r = #[deprecated] 1..6; + | ^^^^^^^^^^^^^^^^^^ + | +help: wrap the expression in parentheses + | +LL | let _r = (#[deprecated] 1)..6; + | + + + +error: aborting due to 4 previous errors + diff --git a/tests/ui/proc-macro/issue-81555.rs b/tests/ui/proc-macro/issue-81555.rs index 7a61a31952f41..b337ab7ce37db 100644 --- a/tests/ui/proc-macro/issue-81555.rs +++ b/tests/ui/proc-macro/issue-81555.rs @@ -10,6 +10,5 @@ use test_macros::identity_attr; fn main() { let _x; let y = (); - #[identity_attr] - _x = y; + (#[identity_attr] _x) = y; }