Skip to content

Commit 458383d

Browse files
committed
parse_if_expr: recover on attributes
1 parent ba3ae46 commit 458383d

9 files changed

+98
-64
lines changed

src/librustc_parse/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
#![feature(bool_to_option)]
44
#![feature(crate_visibility_modifier)]
5+
#![feature(bindings_after_at)]
56

67
use rustc_ast::ast;
78
use rustc_ast::token::{self, Nonterminal};

src/librustc_parse/parser/expr.rs

+37-6
Original file line numberDiff line numberDiff line change
@@ -1514,13 +1514,16 @@ impl<'a> Parser<'a> {
15141514
let thn = if self.eat_keyword(kw::Else) || !cond.returns() {
15151515
self.error_missing_if_cond(lo, cond.span)
15161516
} else {
1517+
let attrs = self.parse_outer_attributes()?; // For recovery.
15171518
let not_block = self.token != token::OpenDelim(token::Brace);
1518-
self.parse_block().map_err(|mut err| {
1519+
let block = self.parse_block().map_err(|mut err| {
15191520
if not_block {
15201521
err.span_label(lo, "this `if` expression has a condition, but no block");
15211522
}
15221523
err
1523-
})?
1524+
})?;
1525+
self.error_on_if_block_attrs(lo, false, block.span, &attrs);
1526+
block
15241527
};
15251528
let els = if self.eat_keyword(kw::Else) { Some(self.parse_else_expr()?) } else { None };
15261529
Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::If(cond, thn, els), attrs))
@@ -1562,12 +1565,40 @@ impl<'a> Parser<'a> {
15621565

15631566
/// Parses an `else { ... }` expression (`else` token already eaten).
15641567
fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> {
1565-
if self.eat_keyword(kw::If) {
1566-
self.parse_if_expr(AttrVec::new())
1568+
let ctx_span = self.prev_token.span; // `else`
1569+
let attrs = self.parse_outer_attributes()?; // For recovery.
1570+
let expr = if self.eat_keyword(kw::If) {
1571+
self.parse_if_expr(AttrVec::new())?
15671572
} else {
15681573
let blk = self.parse_block()?;
1569-
Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new()))
1570-
}
1574+
self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new())
1575+
};
1576+
self.error_on_if_block_attrs(ctx_span, true, expr.span, &attrs);
1577+
Ok(expr)
1578+
}
1579+
1580+
fn error_on_if_block_attrs(
1581+
&self,
1582+
ctx_span: Span,
1583+
is_ctx_else: bool,
1584+
branch_span: Span,
1585+
attrs: &[ast::Attribute],
1586+
) {
1587+
let (span, last) = match attrs {
1588+
[] => return,
1589+
[x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span),
1590+
};
1591+
let ctx = if is_ctx_else { "else" } else { "if" };
1592+
self.struct_span_err(last, "outer attributes are not allowed on `if` and `else` branches")
1593+
.span_label(branch_span, "the attributes are attached to this branch")
1594+
.span_label(ctx_span, format!("the branch belongs to this `{}`", ctx))
1595+
.span_suggestion(
1596+
span,
1597+
"remove the attributes",
1598+
String::new(),
1599+
Applicability::MachineApplicable,
1600+
)
1601+
.emit();
15711602
}
15721603

15731604
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).

src/librustc_parse/parser/item.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,7 @@ impl<'a> Parser<'a> {
352352
fn recover_attrs_no_item(&mut self, attrs: &[Attribute]) -> PResult<'a, ()> {
353353
let (start, end) = match attrs {
354354
[] => return Ok(()),
355-
[x0] => (x0, x0),
356-
[x0, .., xn] => (x0, xn),
355+
[x0 @ xn] | [x0, .., xn] => (x0, xn),
357356
};
358357
let msg = if end.is_doc_comment() {
359358
"expected item after doc comment"

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fn if_else_parse_error() {
88
#[cfg(FALSE)]
99
fn else_attr_ifparse_error() {
1010
if true {
11-
} else #[attr] if false { //~ ERROR expected
11+
} else #[attr] if false { //~ ERROR outer attributes are not allowed
1212
} else {
1313
}
1414
}

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

+9-10
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,17 @@ error: expected expression, found keyword `else`
44
LL | } #[attr] else if false {
55
| ^^^^ expected expression
66

7-
error: expected `{`, found `#`
7+
error: outer attributes are not allowed on `if` and `else` branches
88
--> $DIR/else-attrs.rs:11:12
99
|
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-
|
10+
LL | } else #[attr] if false {
11+
| _______----_^^^^^^^_-
12+
| | | |
13+
| | | help: remove the attributes
14+
| | the branch belongs to this `else`
15+
LL | | } else {
16+
LL | | }
17+
| |_____- the attributes are attached to this branch
1918

2019
error: expected expression, found keyword `else`
2120
--> $DIR/else-attrs.rs:20:15

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

+8-8
Original file line numberDiff line numberDiff line change
@@ -39,35 +39,35 @@ fn main() {}
3939
#[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; }
4040
//~^ ERROR an inner attribute is not permitted in this context
4141
#[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; }
42-
//~^ ERROR expected `{`, found `#`
42+
//~^ ERROR outer attributes are not allowed on `if`
4343
#[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; }
4444
//~^ ERROR an inner attribute is not permitted in this context
4545
#[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; }
4646
//~^ ERROR expected one of
4747
#[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; }
48-
//~^ ERROR expected `{`, found `#`
48+
//~^ ERROR outer attributes are not allowed on `if`
4949
#[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; }
5050
//~^ ERROR an inner attribute is not permitted in this context
5151
#[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; }
52-
//~^ ERROR expected `{`, found `#`
52+
//~^ ERROR outer attributes are not allowed on `if`
5353
#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; }
54-
//~^ ERROR expected `{`, found `#`
54+
//~^ ERROR outer attributes are not allowed on `if`
5555
#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; }
5656
//~^ ERROR an inner attribute is not permitted in this context
5757
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; }
58-
//~^ ERROR expected `{`, found `#`
58+
//~^ ERROR outer attributes are not allowed on `if`
5959
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; }
6060
//~^ ERROR an inner attribute is not permitted in this context
6161
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; }
6262
//~^ ERROR expected one of
6363
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; }
64-
//~^ ERROR expected `{`, found `#`
64+
//~^ ERROR outer attributes are not allowed on `if`
6565
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; }
6666
//~^ ERROR an inner attribute is not permitted in this context
6767
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; }
68-
//~^ ERROR expected `{`, found `#`
68+
//~^ ERROR outer attributes are not allowed on `if`
6969
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; }
70-
//~^ ERROR expected `{`, found `#`
70+
//~^ ERROR outer attributes are not allowed on `if`
7171
#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; }
7272
//~^ ERROR an inner attribute is not permitted in this context
7373

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

+36-32
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,14 @@ LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; }
136136
|
137137
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
138138

139-
error: expected `{`, found `#`
139+
error: outer attributes are not allowed on `if` and `else` branches
140140
--> $DIR/attr-stmt-expr-attr-bad.rs:41:37
141141
|
142142
LL | #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; }
143-
| -- ^ -- help: try placing this code inside a block: `{ {} }`
143+
| -- ^^^^^^^ -- the attributes are attached to this branch
144144
| | |
145-
| | expected `{`
146-
| this `if` expression has a condition, but no block
145+
| | help: remove the attributes
146+
| the branch belongs to this `if`
147147

148148
error: an inner attribute is not permitted in this context
149149
--> $DIR/attr-stmt-expr-attr-bad.rs:43:38
@@ -159,13 +159,14 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
159159
LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; }
160160
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
161161

162-
error: expected `{`, found `#`
162+
error: outer attributes are not allowed on `if` and `else` branches
163163
--> $DIR/attr-stmt-expr-attr-bad.rs:47:45
164164
|
165165
LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; }
166-
| ^ -- help: try placing this code inside a block: `{ {} }`
167-
| |
168-
| expected `{`
166+
| ---- ^^^^^^^ -- the attributes are attached to this branch
167+
| | |
168+
| | help: remove the attributes
169+
| the branch belongs to this `else`
169170

170171
error: an inner attribute is not permitted in this context
171172
--> $DIR/attr-stmt-expr-attr-bad.rs:49:46
@@ -175,22 +176,23 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; }
175176
|
176177
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
177178

178-
error: expected `{`, found `#`
179+
error: outer attributes are not allowed on `if` and `else` branches
179180
--> $DIR/attr-stmt-expr-attr-bad.rs:51:45
180181
|
181182
LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; }
182-
| ^ ------- help: try placing this code inside a block: `{ if 0 {} }`
183-
| |
184-
| expected `{`
183+
| ---- ^^^^^^^ ------- the attributes are attached to this branch
184+
| | |
185+
| | help: remove the attributes
186+
| the branch belongs to this `else`
185187

186-
error: expected `{`, found `#`
188+
error: outer attributes are not allowed on `if` and `else` branches
187189
--> $DIR/attr-stmt-expr-attr-bad.rs:53:50
188190
|
189191
LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; }
190-
| -- ^ -- help: try placing this code inside a block: `{ {} }`
192+
| -- ^^^^^^^ -- the attributes are attached to this branch
191193
| | |
192-
| | expected `{`
193-
| this `if` expression has a condition, but no block
194+
| | help: remove the attributes
195+
| the branch belongs to this `if`
194196

195197
error: an inner attribute is not permitted in this context
196198
--> $DIR/attr-stmt-expr-attr-bad.rs:55:51
@@ -200,14 +202,14 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; }
200202
|
201203
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
202204

203-
error: expected `{`, found `#`
205+
error: outer attributes are not allowed on `if` and `else` branches
204206
--> $DIR/attr-stmt-expr-attr-bad.rs:57:45
205207
|
206208
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; }
207-
| -- ^ -- help: try placing this code inside a block: `{ {} }`
209+
| -- ^^^^^^^ -- the attributes are attached to this branch
208210
| | |
209-
| | expected `{`
210-
| this `if` expression has a condition, but no block
211+
| | help: remove the attributes
212+
| the branch belongs to this `if`
211213

212214
error: an inner attribute is not permitted in this context
213215
--> $DIR/attr-stmt-expr-attr-bad.rs:59:46
@@ -223,13 +225,14 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
223225
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; }
224226
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
225227

226-
error: expected `{`, found `#`
228+
error: outer attributes are not allowed on `if` and `else` branches
227229
--> $DIR/attr-stmt-expr-attr-bad.rs:63:53
228230
|
229231
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; }
230-
| ^ -- help: try placing this code inside a block: `{ {} }`
231-
| |
232-
| expected `{`
232+
| ---- ^^^^^^^ -- the attributes are attached to this branch
233+
| | |
234+
| | help: remove the attributes
235+
| the branch belongs to this `else`
233236

234237
error: an inner attribute is not permitted in this context
235238
--> $DIR/attr-stmt-expr-attr-bad.rs:65:54
@@ -239,22 +242,23 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; }
239242
|
240243
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
241244

242-
error: expected `{`, found `#`
245+
error: outer attributes are not allowed on `if` and `else` branches
243246
--> $DIR/attr-stmt-expr-attr-bad.rs:67:53
244247
|
245248
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; }
246-
| ^ --------------- help: try placing this code inside a block: `{ if let _ = 0 {} }`
247-
| |
248-
| expected `{`
249+
| ---- ^^^^^^^ --------------- the attributes are attached to this branch
250+
| | |
251+
| | help: remove the attributes
252+
| the branch belongs to this `else`
249253

250-
error: expected `{`, found `#`
254+
error: outer attributes are not allowed on `if` and `else` branches
251255
--> $DIR/attr-stmt-expr-attr-bad.rs:69:66
252256
|
253257
LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; }
254-
| -- ^ -- help: try placing this code inside a block: `{ {} }`
258+
| -- ^^^^^^^ -- the attributes are attached to this branch
255259
| | |
256-
| | expected `{`
257-
| this `if` expression has a condition, but no block
260+
| | help: remove the attributes
261+
| the branch belongs to this `if`
258262

259263
error: an inner attribute is not permitted in this context
260264
--> $DIR/attr-stmt-expr-attr-bad.rs:71:67
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
fn main() {
22
if true /*!*/ {}
3-
//~^ ERROR expected `{`, found doc comment `/*!*/`
3+
//~^ ERROR outer attributes are not allowed on
44
//~| ERROR expected outer doc comment
55
}

src/test/ui/parser/doc-comment-in-if-statement.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ LL | if true /*!*/ {}
66
|
77
= note: inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
88

9-
error: expected `{`, found doc comment `/*!*/`
9+
error: outer attributes are not allowed on `if` and `else` branches
1010
--> $DIR/doc-comment-in-if-statement.rs:2:13
1111
|
1212
LL | if true /*!*/ {}
13-
| -- ^^^^^ -- help: try placing this code inside a block: `{ {} }`
13+
| -- ^^^^^ -- the attributes are attached to this branch
1414
| | |
15-
| | expected `{`
16-
| this `if` expression has a condition, but no block
15+
| | help: remove the attributes
16+
| the branch belongs to this `if`
1717

1818
error: aborting due to 2 previous errors
1919

0 commit comments

Comments
 (0)