Skip to content

Commit b35e514

Browse files
Rollup merge of rust-lang#159849 - petrochenkov:estmt, r=JonathanBrouwer
rustc_parse: Stop returning `Option` from statement parsing `parse_stmt_without_recovery` had one corner case in which it returned `Ok(None)` - when parsing immediately encountered a closing brace `}`, possibly after parsing outer attributes. It is simpler to never call `parse_stmt_without_recovery` in such contexts than deal with a possibility of no statement being returned without an error. So this PR changes the function's return type from `PResult<'a, Option<Stmt>>` to `PResult<'a, Stmt>` and adjusts one call site to check for a closing brace.
2 parents d5b7448 + 75529d4 commit b35e514

7 files changed

Lines changed: 58 additions & 76 deletions

File tree

compiler/rustc_attr_parsing/src/parser.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -660,9 +660,8 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
660660
// don't `uninterpolate` the token to avoid suggesting anything butchered or questionable
661661
// when macro metavariables are involved.
662662
let snapshot = self.parser.create_snapshot_for_diagnostic();
663-
let stmt = self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false);
664-
match stmt {
665-
Ok(Some(stmt)) => {
663+
match self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false) {
664+
Ok(stmt) => {
666665
// The user tried to write something like
667666
// `#[deprecated(note = concat!("a", "b"))]`.
668667
err.descr = stmt.kind.descr().to_string();
@@ -692,7 +691,6 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
692691
});
693692
}
694693
}
695-
Ok(None) => {}
696694
Err(e) => {
697695
e.cancel();
698696
self.parser.restore_snapshot(snapshot);

compiler/rustc_builtin_macros/src/cfg_eval.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,8 @@ impl CfgEval<'_> {
126126
Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap())
127127
}
128128
Annotatable::Stmt(_) => {
129-
let stmt = parser
130-
.parse_stmt_without_recovery(false, ForceCollect::Yes, false)?
131-
.unwrap();
129+
let stmt =
130+
parser.parse_stmt_without_recovery(false, ForceCollect::Yes, false)?;
132131
Annotatable::Stmt(Box::new(self.flat_map_stmt(stmt).pop().unwrap()))
133132
}
134133
Annotatable::Expr(_) => {

compiler/rustc_expand/src/expand.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,9 +1120,7 @@ pub fn parse_ast_fragment<'a>(
11201120
let mut stmts = SmallVec::new();
11211121
// Won't make progress on a `}`.
11221122
while this.token != token::Eof && this.token != token::CloseBrace {
1123-
if let Some(stmt) = this.parse_full_stmt(AttemptLocalParseRecovery::Yes)? {
1124-
stmts.push(stmt);
1125-
}
1123+
stmts.push(this.parse_full_stmt(AttemptLocalParseRecovery::Yes)?);
11261124
}
11271125
AstFragment::Stmts(stmts)
11281126
}

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3331,13 +3331,9 @@ impl<'a> Parser<'a> {
33313331
self.restore_snapshot(pre_pat_snapshot);
33323332
match self.parse_stmt_without_recovery(true, ForceCollect::No, false) {
33333333
// Consume statements for as long as possible.
3334-
Ok(Some(stmt)) => {
3334+
Ok(stmt) => {
33353335
stmts.push(stmt);
33363336
}
3337-
Ok(None) => {
3338-
self.restore_snapshot(start_snapshot);
3339-
break;
3340-
}
33413337
// We couldn't parse either yet another statement missing it's
33423338
// enclosing block nor the next arm's pattern or closing brace.
33433339
Err(stmt_err) => {

compiler/rustc_parse/src/parser/mod.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -987,12 +987,9 @@ impl<'a> Parser<'a> {
987987
let initial_semicolon = self.token.span;
988988

989989
while self.eat(exp!(Semi)) {
990-
let _ = self
991-
.parse_stmt_without_recovery(false, ForceCollect::No, false)
992-
.unwrap_or_else(|e| {
993-
e.cancel();
994-
None
995-
});
990+
if let Err(e) = self.parse_stmt_without_recovery(false, ForceCollect::No, false) {
991+
e.cancel();
992+
}
996993
}
997994

998995
expect_err

compiler/rustc_parse/src/parser/nonterminal.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ impl<'a> Parser<'a> {
139139
this.parse_block().map(|block| WithTokens::new(block))
140140
})?))
141141
}
142-
NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? {
142+
NonterminalKind::Stmt => match self.parse_stmt_nonterminal(ForceCollect::Yes) {
143143
Some(stmt) => Ok(ParseNtResult::Stmt(Box::new(stmt))),
144144
None => {
145145
Err(self.dcx().create_err(UnexpectedNonterminal::Statement(self.token.span)))

compiler/rustc_parse/src/parser/stmt.rs

Lines changed: 48 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,22 @@ use crate::diagnostics::{self, MalformedLoopLabel};
2727
use crate::exp;
2828

2929
impl<'a> Parser<'a> {
30-
/// Parses a statement. This stops just before trailing semicolons on everything but items.
30+
/// Parses a statement nonterminal, which has a peculiar syntax preserved for backward
31+
/// compatibility. The parsing stops just before trailing semicolons on everything but items.
3132
/// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
3233
///
3334
/// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of
3435
/// whether or not we have attributes.
3536
// Public for rustfmt usage.
36-
pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> {
37-
Ok(self.parse_stmt_without_recovery(false, force_collect, false).unwrap_or_else(|e| {
38-
e.emit();
39-
self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
40-
None
41-
}))
37+
pub fn parse_stmt_nonterminal(&mut self, force_collect: ForceCollect) -> Option<Stmt> {
38+
match self.parse_stmt_without_recovery(false, force_collect, false) {
39+
Ok(stmt) => Some(stmt),
40+
Err(e) => {
41+
e.emit();
42+
self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
43+
None
44+
}
45+
}
4246
}
4347

4448
/// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of
@@ -49,19 +53,18 @@ impl<'a> Parser<'a> {
4953
capture_semi: bool,
5054
force_collect: ForceCollect,
5155
force_full_expr: bool,
52-
) -> PResult<'a, Option<Stmt>> {
56+
) -> PResult<'a, Stmt> {
5357
let pre_attr_pos = self.collect_pos();
5458
let attrs = self.parse_outer_attributes()?;
5559
let lo = self.token.span;
5660

57-
if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
61+
if let Some(mut stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
5862
this.parse_stmt_without_recovery(false, ForceCollect::Yes, false)
5963
}) {
60-
let mut stmt = stmt.expect("an actual statement");
6164
stmt.visit_attrs(|stmt_attrs| {
6265
attrs.prepend_to_nt_inner(stmt_attrs);
6366
});
64-
return Ok(Some(stmt));
67+
return Ok(stmt);
6568
}
6669

6770
if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) {
@@ -161,9 +164,12 @@ impl<'a> Parser<'a> {
161164
self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item)))
162165
} else if self.eat(exp!(Semi)) {
163166
// Do not attempt to parse an expression if we're done here.
164-
self.error_outer_attrs(attrs);
167+
self.error_outer_attrs(attrs)?;
165168
self.mk_stmt(lo, StmtKind::Empty)
166-
} else if self.token != token::CloseBrace {
169+
} else if self.token == token::CloseBrace {
170+
self.error_outer_attrs(attrs)?;
171+
self.dcx().span_bug(self.token.span, "don't parse a statement if you see `}`");
172+
} else {
167173
// Remainder are line-expr stmts. This is similar to the `parse_stmt_path_start` case
168174
// above.
169175
let restrictions =
@@ -185,13 +191,10 @@ impl<'a> Parser<'a> {
185191
.emit_err(diagnostics::AssignmentElseNotAllowed { span: e.span.to(bl.span) });
186192
}
187193
self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
188-
} else {
189-
self.error_outer_attrs(attrs);
190-
return Ok(None);
191194
};
192195

193196
self.maybe_augment_stashed_expr_in_pats_with_suggestions(&stmt);
194-
Ok(Some(stmt))
197+
Ok(stmt)
195198
}
196199

197200
fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
@@ -272,20 +275,21 @@ impl<'a> Parser<'a> {
272275

273276
/// Error on outer attributes in this context.
274277
/// Also error if the previous token was a doc comment.
275-
fn error_outer_attrs(&self, attrs: AttrWrapper) {
276-
if !attrs.is_empty()
277-
&& let attrs @ [.., last] = &*attrs.take_for_recovery(self.psess)
278-
{
279-
if last.is_doc_comment() {
280-
self.dcx().emit_err(diagnostics::DocCommentDoesNotDocumentAnything {
281-
span: last.span,
282-
missing_comma: None,
283-
});
284-
} else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
285-
self.dcx()
286-
.emit_err(diagnostics::ExpectedStatementAfterOuterAttr { span: last.span });
287-
}
278+
fn error_outer_attrs(&self, attrs: AttrWrapper) -> PResult<'a, ()> {
279+
if attrs.is_empty() {
280+
return Ok(());
288281
}
282+
let attrs = attrs.take_for_recovery(self.psess);
283+
let last = attrs.last().unwrap();
284+
Err(if last.is_doc_comment() {
285+
self.dcx().create_err(diagnostics::DocCommentDoesNotDocumentAnything {
286+
span: last.span,
287+
missing_comma: None,
288+
})
289+
} else {
290+
assert_eq!(last.style, AttrStyle::Outer);
291+
self.dcx().create_err(diagnostics::ExpectedStatementAfterOuterAttr { span: last.span })
292+
})
289293
}
290294

291295
fn recover_stmt_local_after_let(
@@ -521,6 +525,10 @@ impl<'a> Parser<'a> {
521525
let sp = self.token.span;
522526
let mut err = self.dcx().struct_span_err(sp, msg);
523527
self.label_expected_raw_ref(&mut err);
528+
err.span_label(sp, "expected `{`");
529+
if self.token == token::CloseBrace {
530+
return err;
531+
}
524532

525533
let do_not_suggest_help = self.token.is_keyword(kw::In)
526534
|| self.token == token::Colon
@@ -547,13 +555,13 @@ impl<'a> Parser<'a> {
547555
// since we want to protect against:
548556
// `if 1 1 + 1 {` being suggested as `if { 1 } 1 + 1 {`
549557
// + +
550-
Ok(Some(_))
558+
Ok(_)
551559
if (!self.token.is_keyword(kw::Else)
552560
&& self.look_ahead(1, |t| t == &token::OpenBrace))
553561
|| do_not_suggest_help => {}
554562
// Do not suggest `if foo println!("") {;}` (as would be seen in test for #46836).
555-
Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {}
556-
Ok(Some(stmt)) => {
563+
Ok(Stmt { kind: StmtKind::Empty, .. }) => {}
564+
Ok(stmt) => {
557565
let stmt_own_line = self.psess.source_map().is_line_before_span_empty(sp);
558566
let stmt_span = if stmt_own_line && self.eat(exp!(Semi)) {
559567
// Expand the span to include the semicolon.
@@ -571,9 +579,7 @@ impl<'a> Parser<'a> {
571579
Err(e) => {
572580
e.delay_as_bug();
573581
}
574-
_ => {}
575582
}
576-
err.span_label(sp, "expected `{`");
577583
err
578584
}
579585

@@ -773,17 +779,12 @@ impl<'a> Parser<'a> {
773779

774780
let guar = err.emit();
775781
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
776-
Some(self.mk_stmt_err(self.token.span, guar))
782+
self.mk_stmt_err(self.token.span, guar)
777783
}
778784
Ok(stmt) => stmt,
779785
Err(err) => return Err(err),
780786
};
781-
if let Some(stmt) = stmt {
782-
stmts.push(stmt);
783-
} else {
784-
// Found only `;` or `}`.
785-
continue;
786-
};
787+
stmts.push(stmt);
787788
}
788789
Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
789790
}
@@ -943,10 +944,7 @@ impl<'a> Parser<'a> {
943944
}
944945

945946
/// Parses a statement, including the trailing semicolon.
946-
pub fn parse_full_stmt(
947-
&mut self,
948-
recover: AttemptLocalParseRecovery,
949-
) -> PResult<'a, Option<Stmt>> {
947+
pub fn parse_full_stmt(&mut self, recover: AttemptLocalParseRecovery) -> PResult<'a, Stmt> {
950948
// Skip looking for a trailing semicolon when we have a metavar seq.
951949
if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
952950
// Why pass `true` for `force_full_expr`? Statement expressions are less expressive
@@ -959,14 +957,10 @@ impl<'a> Parser<'a> {
959957
// will reparse successfully.
960958
this.parse_stmt_without_recovery(false, ForceCollect::No, true)
961959
}) {
962-
let stmt = stmt.expect("an actual statement");
963-
return Ok(Some(stmt));
960+
return Ok(stmt);
964961
}
965962

966-
let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No, false)?
967-
else {
968-
return Ok(None);
969-
};
963+
let mut stmt = self.parse_stmt_without_recovery(true, ForceCollect::No, false)?;
970964

971965
let mut eat_semi = true;
972966
let mut add_semi_to_stmt = false;
@@ -1086,7 +1080,7 @@ impl<'a> Parser<'a> {
10861080
StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
10871081
StmtKind::Let(local) => {
10881082
if self.try_recover_let_missing_semi(local).is_some() {
1089-
return Ok(Some(stmt));
1083+
return Ok(stmt);
10901084
}
10911085
if let Err(mut e) = self.expect_semi() {
10921086
// We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
@@ -1159,7 +1153,7 @@ impl<'a> Parser<'a> {
11591153
}
11601154

11611155
stmt.span = stmt.span.to(self.prev_token.span);
1162-
Ok(Some(stmt))
1156+
Ok(stmt)
11631157
}
11641158

11651159
pub(super) fn mk_block(

0 commit comments

Comments
 (0)