Skip to content

Commit

Permalink
Rollup merge of rust-lang#69201 - Aaron1011:feature/permit-if-attr, r…
Browse files Browse the repository at this point in the history
…=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
  • Loading branch information
Centril authored Mar 9, 2020
2 parents 2cb0b85 + e50fd5a commit 32c749e
Show file tree
Hide file tree
Showing 17 changed files with 248 additions and 120 deletions.
9 changes: 0 additions & 9 deletions src/librustc_parse/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -718,20 +718,11 @@ impl<'a> Parser<'a> {
expr.map(|mut expr| {
attrs.extend::<Vec<_>>(expr.attrs.into());
expr.attrs = attrs;
self.error_attr_on_if_expr(&expr);
expr
})
})
}

fn error_attr_on_if_expr(&self, expr: &Expr) {
if let (ExprKind::If(..), [a0, ..]) = (&expr.kind, &*expr.attrs) {
// Just point to the first attribute in there...
self.struct_span_err(a0.span, "attributes are not yet allowed on `if` expressions")
.emit();
}
}

fn parse_dot_or_call_expr_with_(&mut self, mut e: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
loop {
if self.eat(&token::Question) {
Expand Down
37 changes: 37 additions & 0 deletions src/test/pretty/if-attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// pp-exact

#[cfg(FALSE)]
fn simple_attr() {

#[attr]
if true { }

#[allow_warnings]
if true { }
}

#[cfg(FALSE)]
fn if_else_chain() {

#[first_attr]
if true { } else if false { } else { }
}

#[cfg(FALSE)]
fn if_let() {

#[attr]
if let Some(_) = Some(true) { }
}

#[cfg(FALSE)]
fn let_attr_if() {
let _ = #[attr] if let _ = 0 { };
let _ = #[attr] if true { };

let _ = #[attr] if let _ = 0 { } else { };
let _ = #[attr] if true { } else { };
}


fn main() { }
5 changes: 5 additions & 0 deletions src/test/ui/if-attrs/bad-cfg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#![feature(stmt_expr_attributes)]

fn main() {
let _ = #[cfg(FALSE)] if true {}; //~ ERROR removing an expression
}
8 changes: 8 additions & 0 deletions src/test/ui/if-attrs/bad-cfg.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: removing an expression is not supported in this position
--> $DIR/bad-cfg.rs:4:13
|
LL | let _ = #[cfg(FALSE)] if true {};
| ^^^^^^^^^^^^^

error: aborting due to previous error

12 changes: 12 additions & 0 deletions src/test/ui/if-attrs/builtin-if-attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// check-pass

fn main() {
#[allow(unused_variables)]
if true {
let a = 1;
} else if false {
let b = 1;
} else {
let c = 1;
}
}
43 changes: 43 additions & 0 deletions src/test/ui/if-attrs/cfg-false-if-attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// check-pass

#[cfg(FALSE)]
fn simple_attr() {
#[attr] if true {}
#[allow_warnings] if true {}
}

#[cfg(FALSE)]
fn if_else_chain() {
#[first_attr] if true {
} else if false {
} else {
}
}

#[cfg(FALSE)]
fn if_let() {
#[attr] if let Some(_) = Some(true) {}
}

fn bar() {
#[cfg(FALSE)]
if true {
let x: () = true; // Should not error due to the #[cfg(FALSE)]
}

#[cfg_attr(not(unset_attr), cfg(FALSE))]
if true {
let a: () = true; // Should not error due to the applied #[cfg(FALSE)]
}
}

macro_rules! custom_macro {
($expr:expr) => {}
}

custom_macro! {
#[attr] if true {}
}


fn main() {}
25 changes: 25 additions & 0 deletions src/test/ui/if-attrs/else-attrs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#[cfg(FALSE)]
fn if_else_parse_error() {
if true {
} #[attr] else if false { //~ ERROR expected
}
}

#[cfg(FALSE)]
fn else_attr_ifparse_error() {
if true {
} else #[attr] if false { //~ ERROR expected
} else {
}
}

#[cfg(FALSE)]
fn else_parse_error() {
if true {
} else if false {
} #[attr] else { //~ ERROR expected
}
}

fn main() {
}
27 changes: 27 additions & 0 deletions src/test/ui/if-attrs/else-attrs.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
error: expected expression, found keyword `else`
--> $DIR/else-attrs.rs:4:15
|
LL | } #[attr] else if false {
| ^^^^ expected expression

error: expected `{`, found `#`
--> $DIR/else-attrs.rs:11:12
|
LL | } else #[attr] if false {
| ^ expected `{`
|
help: try placing this code inside a block
|
LL | } else #[attr] { if false {
LL | } else {
LL | } }
|

error: expected expression, found keyword `else`
--> $DIR/else-attrs.rs:20:15
|
LL | } #[attr] else {
| ^^^^ expected expression

error: aborting due to 3 previous errors

15 changes: 15 additions & 0 deletions src/test/ui/if-attrs/gate-whole-expr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// run-pass

fn main() {
let x = 1;

#[cfg(FALSE)]
if false {
x = 2;
} else if true {
x = 3;
} else {
x = 4;
}
assert_eq!(x, 1);
}
13 changes: 13 additions & 0 deletions src/test/ui/if-attrs/let-chains-attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// check-pass

#![feature(let_chains)] //~ WARN the feature `let_chains` is incomplete

#[cfg(FALSE)]
fn foo() {
#[attr]
if let Some(_) = Some(true) && let Ok(_) = Ok(1) {
} else if let Some(false) = Some(true) {
}
}

fn main() {}
8 changes: 8 additions & 0 deletions src/test/ui/if-attrs/let-chains-attr.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
warning: the feature `let_chains` is incomplete and may cause the compiler to crash
--> $DIR/let-chains-attr.rs:3:12
|
LL | #![feature(let_chains)]
| ^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

6 changes: 6 additions & 0 deletions src/test/ui/if-attrs/stmt-expr-gated.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fn main() {
let _ = #[deny(warnings)] if true { //~ ERROR attributes on expressions
} else if false {
} else {
};
}
12 changes: 12 additions & 0 deletions src/test/ui/if-attrs/stmt-expr-gated.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/stmt-expr-gated.rs:2:13
|
LL | let _ = #[deny(warnings)] if true {
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable

error: aborting due to previous error

For more information about this error, try `rustc --explain E0658`.
10 changes: 2 additions & 8 deletions src/test/ui/parser/attr-stmt-expr-attr-bad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ fn main() {}
//~^ ERROR an inner attribute is not permitted in this context
#[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; }
//~^ ERROR an inner attribute is not permitted in this context
#[cfg(FALSE)] fn e() { let _ = #[attr] if 0 {}; }
//~^ ERROR attributes are not yet allowed on `if` expressions
#[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; }
//~^ ERROR expected `{`, found `#`
#[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; }
Expand All @@ -51,14 +49,11 @@ fn main() {}
#[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; }
//~^ ERROR an inner attribute is not permitted in this context
#[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; }
//~^ ERROR attributes are not yet allowed on `if` expressions
//~| ERROR expected `{`, found `#`
//~^ ERROR expected `{`, found `#`
#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; }
//~^ ERROR expected `{`, found `#`
#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; }
//~^ ERROR an inner attribute is not permitted in this context
#[cfg(FALSE)] fn e() { let _ = #[attr] if let _ = 0 {}; }
//~^ ERROR attributes are not yet allowed on `if` expressions
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; }
//~^ ERROR expected `{`, found `#`
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; }
Expand All @@ -70,8 +65,7 @@ fn main() {}
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; }
//~^ ERROR an inner attribute is not permitted in this context
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; }
//~^ ERROR attributes are not yet allowed on `if` expressions
//~| ERROR expected `{`, found `#`
//~^ ERROR expected `{`, found `#`
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; }
//~^ ERROR expected `{`, found `#`
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; }
Expand Down
Loading

0 comments on commit 32c749e

Please sign in to comment.