Skip to content

Commit 32c749e

Browse files
authored
Rollup merge of rust-lang#69201 - Aaron1011:feature/permit-if-attr, r=Centril
Permit attributes on 'if' expressions Previously, attributes on 'if' expressions (e.g. `#[attr] if true {}`) were disallowed during parsing. This made it impossible for macros to perform any custom handling of such attributes (e.g. stripping them away), since a compilation error would be emitted before they ever had a chance to run. This PR permits attributes on 'if' expressions ('if-attrs' from here on). Both built-in attributes (e.g. `#[allow]`, `#[cfg]`) and proc-macro attributes are supported. We still do *not* accept attributes on 'other parts' of an if-else chain. That is, the following code snippet still fails to parse: ```rust if true {} #[attr] else if false {} else #[attr] if false {} #[attr] else {} ``` Closes rust-lang#68618
2 parents 2cb0b85 + e50fd5a commit 32c749e

17 files changed

+248
-120
lines changed

src/librustc_parse/parser/expr.rs

-9
Original file line numberDiff line numberDiff line change
@@ -718,20 +718,11 @@ impl<'a> Parser<'a> {
718718
expr.map(|mut expr| {
719719
attrs.extend::<Vec<_>>(expr.attrs.into());
720720
expr.attrs = attrs;
721-
self.error_attr_on_if_expr(&expr);
722721
expr
723722
})
724723
})
725724
}
726725

727-
fn error_attr_on_if_expr(&self, expr: &Expr) {
728-
if let (ExprKind::If(..), [a0, ..]) = (&expr.kind, &*expr.attrs) {
729-
// Just point to the first attribute in there...
730-
self.struct_span_err(a0.span, "attributes are not yet allowed on `if` expressions")
731-
.emit();
732-
}
733-
}
734-
735726
fn parse_dot_or_call_expr_with_(&mut self, mut e: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
736727
loop {
737728
if self.eat(&token::Question) {

src/test/pretty/if-attr.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// pp-exact
2+
3+
#[cfg(FALSE)]
4+
fn simple_attr() {
5+
6+
#[attr]
7+
if true { }
8+
9+
#[allow_warnings]
10+
if true { }
11+
}
12+
13+
#[cfg(FALSE)]
14+
fn if_else_chain() {
15+
16+
#[first_attr]
17+
if true { } else if false { } else { }
18+
}
19+
20+
#[cfg(FALSE)]
21+
fn if_let() {
22+
23+
#[attr]
24+
if let Some(_) = Some(true) { }
25+
}
26+
27+
#[cfg(FALSE)]
28+
fn let_attr_if() {
29+
let _ = #[attr] if let _ = 0 { };
30+
let _ = #[attr] if true { };
31+
32+
let _ = #[attr] if let _ = 0 { } else { };
33+
let _ = #[attr] if true { } else { };
34+
}
35+
36+
37+
fn main() { }

src/test/ui/if-attrs/bad-cfg.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#![feature(stmt_expr_attributes)]
2+
3+
fn main() {
4+
let _ = #[cfg(FALSE)] if true {}; //~ ERROR removing an expression
5+
}

src/test/ui/if-attrs/bad-cfg.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: removing an expression is not supported in this position
2+
--> $DIR/bad-cfg.rs:4:13
3+
|
4+
LL | let _ = #[cfg(FALSE)] if true {};
5+
| ^^^^^^^^^^^^^
6+
7+
error: aborting due to previous error
8+
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// check-pass
2+
3+
fn main() {
4+
#[allow(unused_variables)]
5+
if true {
6+
let a = 1;
7+
} else if false {
8+
let b = 1;
9+
} else {
10+
let c = 1;
11+
}
12+
}
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// check-pass
2+
3+
#[cfg(FALSE)]
4+
fn simple_attr() {
5+
#[attr] if true {}
6+
#[allow_warnings] if true {}
7+
}
8+
9+
#[cfg(FALSE)]
10+
fn if_else_chain() {
11+
#[first_attr] if true {
12+
} else if false {
13+
} else {
14+
}
15+
}
16+
17+
#[cfg(FALSE)]
18+
fn if_let() {
19+
#[attr] if let Some(_) = Some(true) {}
20+
}
21+
22+
fn bar() {
23+
#[cfg(FALSE)]
24+
if true {
25+
let x: () = true; // Should not error due to the #[cfg(FALSE)]
26+
}
27+
28+
#[cfg_attr(not(unset_attr), cfg(FALSE))]
29+
if true {
30+
let a: () = true; // Should not error due to the applied #[cfg(FALSE)]
31+
}
32+
}
33+
34+
macro_rules! custom_macro {
35+
($expr:expr) => {}
36+
}
37+
38+
custom_macro! {
39+
#[attr] if true {}
40+
}
41+
42+
43+
fn main() {}

src/test/ui/if-attrs/else-attrs.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#[cfg(FALSE)]
2+
fn if_else_parse_error() {
3+
if true {
4+
} #[attr] else if false { //~ ERROR expected
5+
}
6+
}
7+
8+
#[cfg(FALSE)]
9+
fn else_attr_ifparse_error() {
10+
if true {
11+
} else #[attr] if false { //~ ERROR expected
12+
} else {
13+
}
14+
}
15+
16+
#[cfg(FALSE)]
17+
fn else_parse_error() {
18+
if true {
19+
} else if false {
20+
} #[attr] else { //~ ERROR expected
21+
}
22+
}
23+
24+
fn main() {
25+
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error: expected expression, found keyword `else`
2+
--> $DIR/else-attrs.rs:4:15
3+
|
4+
LL | } #[attr] else if false {
5+
| ^^^^ expected expression
6+
7+
error: expected `{`, found `#`
8+
--> $DIR/else-attrs.rs:11:12
9+
|
10+
LL | } else #[attr] if false {
11+
| ^ expected `{`
12+
|
13+
help: try placing this code inside a block
14+
|
15+
LL | } else #[attr] { if false {
16+
LL | } else {
17+
LL | } }
18+
|
19+
20+
error: expected expression, found keyword `else`
21+
--> $DIR/else-attrs.rs:20:15
22+
|
23+
LL | } #[attr] else {
24+
| ^^^^ expected expression
25+
26+
error: aborting due to 3 previous errors
27+
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// run-pass
2+
3+
fn main() {
4+
let x = 1;
5+
6+
#[cfg(FALSE)]
7+
if false {
8+
x = 2;
9+
} else if true {
10+
x = 3;
11+
} else {
12+
x = 4;
13+
}
14+
assert_eq!(x, 1);
15+
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// check-pass
2+
3+
#![feature(let_chains)] //~ WARN the feature `let_chains` is incomplete
4+
5+
#[cfg(FALSE)]
6+
fn foo() {
7+
#[attr]
8+
if let Some(_) = Some(true) && let Ok(_) = Ok(1) {
9+
} else if let Some(false) = Some(true) {
10+
}
11+
}
12+
13+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
warning: the feature `let_chains` is incomplete and may cause the compiler to crash
2+
--> $DIR/let-chains-attr.rs:3:12
3+
|
4+
LL | #![feature(let_chains)]
5+
| ^^^^^^^^^^
6+
|
7+
= note: `#[warn(incomplete_features)]` on by default
8+
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn main() {
2+
let _ = #[deny(warnings)] if true { //~ ERROR attributes on expressions
3+
} else if false {
4+
} else {
5+
};
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0658]: attributes on expressions are experimental
2+
--> $DIR/stmt-expr-gated.rs:2:13
3+
|
4+
LL | let _ = #[deny(warnings)] if true {
5+
| ^^^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
8+
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0658`.

src/test/ui/parser/attr-stmt-expr-attr-bad.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ fn main() {}
3838
//~^ ERROR an inner attribute is not permitted in this context
3939
#[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; }
4040
//~^ ERROR an inner attribute is not permitted in this context
41-
#[cfg(FALSE)] fn e() { let _ = #[attr] if 0 {}; }
42-
//~^ ERROR attributes are not yet allowed on `if` expressions
4341
#[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; }
4442
//~^ ERROR expected `{`, found `#`
4543
#[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; }
@@ -51,14 +49,11 @@ fn main() {}
5149
#[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; }
5250
//~^ ERROR an inner attribute is not permitted in this context
5351
#[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; }
54-
//~^ ERROR attributes are not yet allowed on `if` expressions
55-
//~| ERROR expected `{`, found `#`
52+
//~^ ERROR expected `{`, found `#`
5653
#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; }
5754
//~^ ERROR expected `{`, found `#`
5855
#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; }
5956
//~^ ERROR an inner attribute is not permitted in this context
60-
#[cfg(FALSE)] fn e() { let _ = #[attr] if let _ = 0 {}; }
61-
//~^ ERROR attributes are not yet allowed on `if` expressions
6257
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; }
6358
//~^ ERROR expected `{`, found `#`
6459
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; }
@@ -70,8 +65,7 @@ fn main() {}
7065
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; }
7166
//~^ ERROR an inner attribute is not permitted in this context
7267
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; }
73-
//~^ ERROR attributes are not yet allowed on `if` expressions
74-
//~| ERROR expected `{`, found `#`
68+
//~^ ERROR expected `{`, found `#`
7569
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; }
7670
//~^ ERROR expected `{`, found `#`
7771
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; }

0 commit comments

Comments
 (0)