Skip to content

Commit 27ea0c4

Browse files
committed
[Parse] Use empty RecoveryExpr when if/while/do/switch conditions fail to parse
This allows the body to be parsed. An special-case that would replace a missing if condition with OpaqueValueExpr was removed as it's now redundant (unless recovery-expr is disabled). For loops are not handled at this point, as the parsing is more complicated. Differential Revision: https://reviews.llvm.org/D113752
1 parent ad1b877 commit 27ea0c4

File tree

14 files changed

+168
-45
lines changed

14 files changed

+168
-45
lines changed

clang/include/clang/Parse/Parser.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,6 +1975,7 @@ class Parser : public CodeCompletionHandler {
19751975
Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt,
19761976
SourceLocation Loc,
19771977
Sema::ConditionKind CK,
1978+
bool MissingOK,
19781979
ForRangeInfo *FRI = nullptr,
19791980
bool EnterForConditionScope = false);
19801981
DeclGroupPtrTy
@@ -2079,8 +2080,8 @@ class Parser : public CodeCompletionHandler {
20792080
bool ParseParenExprOrCondition(StmtResult *InitStmt,
20802081
Sema::ConditionResult &CondResult,
20812082
SourceLocation Loc, Sema::ConditionKind CK,
2082-
SourceLocation *LParenLoc = nullptr,
2083-
SourceLocation *RParenLoc = nullptr);
2083+
bool MissingOK, SourceLocation *LParenLoc,
2084+
SourceLocation *RParenLoc);
20842085
StmtResult ParseIfStatement(SourceLocation *TrailingElseLoc);
20852086
StmtResult ParseSwitchStatement(SourceLocation *TrailingElseLoc);
20862087
StmtResult ParseWhileStatement(SourceLocation *TrailingElseLoc);

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12080,9 +12080,12 @@ class Sema final {
1208012080
ConstexprIf, ///< A constant boolean condition from 'if constexpr'.
1208112081
Switch ///< An integral condition for a 'switch' statement.
1208212082
};
12083+
QualType PreferredConditionType(ConditionKind K) const {
12084+
return K == ConditionKind::Switch ? Context.IntTy : Context.BoolTy;
12085+
}
1208312086

12084-
ConditionResult ActOnCondition(Scope *S, SourceLocation Loc,
12085-
Expr *SubExpr, ConditionKind CK);
12087+
ConditionResult ActOnCondition(Scope *S, SourceLocation Loc, Expr *SubExpr,
12088+
ConditionKind CK, bool MissingOK = false);
1208612089

1208712090
ConditionResult ActOnConditionVariable(Decl *ConditionVar,
1208812091
SourceLocation StmtLoc,

clang/lib/AST/ExprConstant.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4933,8 +4933,13 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
49334933
if (SS->getConditionVariable() &&
49344934
!EvaluateDecl(Info, SS->getConditionVariable()))
49354935
return ESR_Failed;
4936-
if (!EvaluateInteger(SS->getCond(), Value, Info))
4937-
return ESR_Failed;
4936+
if (SS->getCond()->isValueDependent()) {
4937+
if (!EvaluateDependentExpr(SS->getCond(), Info))
4938+
return ESR_Failed;
4939+
} else {
4940+
if (!EvaluateInteger(SS->getCond(), Value, Info))
4941+
return ESR_Failed;
4942+
}
49384943
if (!CondScope.destroy())
49394944
return ESR_Failed;
49404945
}

clang/lib/Parse/ParseExprCXX.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,18 +1953,20 @@ Parser::ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
19531953
/// \param Loc The location of the start of the statement that requires this
19541954
/// condition, e.g., the "for" in a for loop.
19551955
///
1956+
/// \param MissingOK Whether an empty condition is acceptable here. Otherwise
1957+
/// it is considered an error to be recovered from.
1958+
///
19561959
/// \param FRI If non-null, a for range declaration is permitted, and if
19571960
/// present will be parsed and stored here, and a null result will be returned.
19581961
///
19591962
/// \param EnterForConditionScope If true, enter a continue/break scope at the
19601963
/// appropriate moment for a 'for' loop.
19611964
///
19621965
/// \returns The parsed condition.
1963-
Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt,
1964-
SourceLocation Loc,
1965-
Sema::ConditionKind CK,
1966-
ForRangeInfo *FRI,
1967-
bool EnterForConditionScope) {
1966+
Sema::ConditionResult
1967+
Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
1968+
Sema::ConditionKind CK, bool MissingOK,
1969+
ForRangeInfo *FRI, bool EnterForConditionScope) {
19681970
// Helper to ensure we always enter a continue/break scope if requested.
19691971
struct ForConditionScopeRAII {
19701972
Scope *S;
@@ -2019,7 +2021,7 @@ Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt,
20192021
}
20202022
ConsumeToken();
20212023
*InitStmt = Actions.ActOnNullStmt(SemiLoc);
2022-
return ParseCXXCondition(nullptr, Loc, CK);
2024+
return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
20232025
}
20242026

20252027
// Parse the expression.
@@ -2031,10 +2033,11 @@ Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt,
20312033
WarnOnInit();
20322034
*InitStmt = Actions.ActOnExprStmt(Expr.get());
20332035
ConsumeToken();
2034-
return ParseCXXCondition(nullptr, Loc, CK);
2036+
return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
20352037
}
20362038

2037-
return Actions.ActOnCondition(getCurScope(), Loc, Expr.get(), CK);
2039+
return Actions.ActOnCondition(getCurScope(), Loc, Expr.get(), CK,
2040+
MissingOK);
20382041
}
20392042

20402043
case ConditionOrInitStatement::InitStmtDecl: {
@@ -2048,7 +2051,7 @@ Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt,
20482051
DG = ParseSimpleDeclaration(DeclaratorContext::SelectionInit, DeclEnd,
20492052
attrs, /*RequireSemi=*/true);
20502053
*InitStmt = Actions.ActOnDeclStmt(DG, DeclStart, DeclEnd);
2051-
return ParseCXXCondition(nullptr, Loc, CK);
2054+
return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
20522055
}
20532056

20542057
case ConditionOrInitStatement::ForRangeDecl: {

clang/lib/Parse/ParseStmt.cpp

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,22 +1191,24 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
11911191
bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
11921192
Sema::ConditionResult &Cond,
11931193
SourceLocation Loc,
1194-
Sema::ConditionKind CK,
1194+
Sema::ConditionKind CK, bool MissingOK,
11951195
SourceLocation *LParenLoc,
11961196
SourceLocation *RParenLoc) {
11971197
BalancedDelimiterTracker T(*this, tok::l_paren);
11981198
T.consumeOpen();
1199+
SourceLocation Start = Tok.getLocation();
11991200

1200-
if (getLangOpts().CPlusPlus)
1201-
Cond = ParseCXXCondition(InitStmt, Loc, CK);
1202-
else {
1201+
if (getLangOpts().CPlusPlus) {
1202+
Cond = ParseCXXCondition(InitStmt, Loc, CK, MissingOK);
1203+
} else {
12031204
ExprResult CondExpr = ParseExpression();
12041205

12051206
// If required, convert to a boolean value.
12061207
if (CondExpr.isInvalid())
12071208
Cond = Sema::ConditionError();
12081209
else
1209-
Cond = Actions.ActOnCondition(getCurScope(), Loc, CondExpr.get(), CK);
1210+
Cond = Actions.ActOnCondition(getCurScope(), Loc, CondExpr.get(), CK,
1211+
MissingOK);
12101212
}
12111213

12121214
// If the parser was confused by the condition and we don't have a ')', try to
@@ -1220,7 +1222,16 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
12201222
return true;
12211223
}
12221224

1223-
// Otherwise the condition is valid or the rparen is present.
1225+
if (Cond.isInvalid()) {
1226+
ExprResult CondExpr = Actions.CreateRecoveryExpr(
1227+
Start, Tok.getLocation() == Start ? Start : PrevTokLocation, {},
1228+
Actions.PreferredConditionType(CK));
1229+
if (!CondExpr.isInvalid())
1230+
Cond = Actions.ActOnCondition(getCurScope(), Loc, CondExpr.get(), CK,
1231+
MissingOK);
1232+
}
1233+
1234+
// Either the condition is valid or the rparen is present.
12241235
T.consumeClose();
12251236

12261237
if (LParenLoc != nullptr) {
@@ -1404,7 +1415,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
14041415
if (ParseParenExprOrCondition(&InitStmt, Cond, IfLoc,
14051416
IsConstexpr ? Sema::ConditionKind::ConstexprIf
14061417
: Sema::ConditionKind::Boolean,
1407-
&LParen, &RParen))
1418+
/*MissingOK=*/false, &LParen, &RParen))
14081419
return StmtError();
14091420

14101421
if (IsConstexpr)
@@ -1599,7 +1610,8 @@ StmtResult Parser::ParseSwitchStatement(SourceLocation *TrailingElseLoc) {
15991610
SourceLocation LParen;
16001611
SourceLocation RParen;
16011612
if (ParseParenExprOrCondition(&InitStmt, Cond, SwitchLoc,
1602-
Sema::ConditionKind::Switch, &LParen, &RParen))
1613+
Sema::ConditionKind::Switch,
1614+
/*MissingOK=*/false, &LParen, &RParen))
16031615
return StmtError();
16041616

16051617
StmtResult Switch = Actions.ActOnStartOfSwitchStmt(
@@ -1689,7 +1701,8 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) {
16891701
SourceLocation LParen;
16901702
SourceLocation RParen;
16911703
if (ParseParenExprOrCondition(nullptr, Cond, WhileLoc,
1692-
Sema::ConditionKind::Boolean, &LParen, &RParen))
1704+
Sema::ConditionKind::Boolean,
1705+
/*MissingOK=*/false, &LParen, &RParen))
16931706
return StmtError();
16941707

16951708
// C99 6.8.5p5 - In C99, the body of the while statement is a scope, even if
@@ -1780,10 +1793,18 @@ StmtResult Parser::ParseDoStatement() {
17801793
// A do-while expression is not a condition, so can't have attributes.
17811794
DiagnoseAndSkipCXX11Attributes();
17821795

1796+
SourceLocation Start = Tok.getLocation();
17831797
ExprResult Cond = ParseExpression();
17841798
// Correct the typos in condition before closing the scope.
17851799
if (Cond.isUsable())
17861800
Cond = Actions.CorrectDelayedTyposInExpr(Cond);
1801+
else {
1802+
if (!Tok.isOneOf(tok::r_paren, tok::r_square, tok::r_brace))
1803+
SkipUntil(tok::semi);
1804+
Cond = Actions.CreateRecoveryExpr(
1805+
Start, Start == Tok.getLocation() ? Start : PrevTokLocation, {},
1806+
Actions.getASTContext().BoolTy);
1807+
}
17871808
T.consumeClose();
17881809
DoScope.Exit();
17891810

@@ -2038,10 +2059,11 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
20382059
// for-range-declaration next.
20392060
bool MightBeForRangeStmt = !ForRangeInfo.ParsedForRangeDecl();
20402061
ColonProtectionRAIIObject ColonProtection(*this, MightBeForRangeStmt);
2041-
SecondPart =
2042-
ParseCXXCondition(nullptr, ForLoc, Sema::ConditionKind::Boolean,
2043-
MightBeForRangeStmt ? &ForRangeInfo : nullptr,
2044-
/*EnterForConditionScope*/ true);
2062+
SecondPart = ParseCXXCondition(
2063+
nullptr, ForLoc, Sema::ConditionKind::Boolean,
2064+
// FIXME: recovery if we don't see another semi!
2065+
/*MissingOK=*/true, MightBeForRangeStmt ? &ForRangeInfo : nullptr,
2066+
/*EnterForConditionScope*/ true);
20452067

20462068
if (ForRangeInfo.ParsedForRangeDecl()) {
20472069
Diag(FirstPart.get() ? FirstPart.get()->getBeginLoc()
@@ -2065,9 +2087,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
20652087
if (SecondExpr.isInvalid())
20662088
SecondPart = Sema::ConditionError();
20672089
else
2068-
SecondPart =
2069-
Actions.ActOnCondition(getCurScope(), ForLoc, SecondExpr.get(),
2070-
Sema::ConditionKind::Boolean);
2090+
SecondPart = Actions.ActOnCondition(
2091+
getCurScope(), ForLoc, SecondExpr.get(),
2092+
Sema::ConditionKind::Boolean, /*MissingOK=*/true);
20712093
}
20722094
}
20732095
}

clang/lib/Sema/SemaExpr.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19209,10 +19209,12 @@ ExprResult Sema::CheckBooleanCondition(SourceLocation Loc, Expr *E,
1920919209
}
1921019210

1921119211
Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc,
19212-
Expr *SubExpr, ConditionKind CK) {
19213-
// Empty conditions are valid in for-statements.
19212+
Expr *SubExpr, ConditionKind CK,
19213+
bool MissingOK) {
19214+
// MissingOK indicates whether having no condition expression is valid
19215+
// (for loop) or invalid (e.g. while loop).
1921419216
if (!SubExpr)
19215-
return ConditionResult();
19217+
return MissingOK ? ConditionResult() : ConditionError();
1921619218

1921719219
ExprResult Cond;
1921819220
switch (CK) {
@@ -19230,7 +19232,7 @@ Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc,
1923019232
}
1923119233
if (Cond.isInvalid()) {
1923219234
Cond = CreateRecoveryExpr(SubExpr->getBeginLoc(), SubExpr->getEndLoc(),
19233-
{SubExpr});
19235+
{SubExpr}, PreferredConditionType(CK));
1923419236
if (!Cond.get())
1923519237
return ConditionError();
1923619238
}

clang/lib/Sema/SemaStmt.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -869,12 +869,7 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc,
869869
Stmt *thenStmt, SourceLocation ElseLoc,
870870
Stmt *elseStmt) {
871871
if (Cond.isInvalid())
872-
Cond = ConditionResult(
873-
*this, nullptr,
874-
MakeFullExpr(new (Context) OpaqueValueExpr(SourceLocation(),
875-
Context.BoolTy, VK_PRValue),
876-
IfLoc),
877-
false);
872+
return StmtError();
878873

879874
bool ConstevalOrNegatedConsteval =
880875
StatementKind == IfStatementKind::ConstevalNonNegated ||
@@ -2468,6 +2463,7 @@ StmtResult Sema::ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
24682463
Stmt *First, SourceLocation ColonLoc,
24692464
Expr *Range, SourceLocation RParenLoc,
24702465
BuildForRangeKind Kind) {
2466+
// FIXME: recover in order to allow the body to be parsed.
24712467
if (!First)
24722468
return StmtError();
24732469

clang/lib/Sema/TreeTransform.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4076,7 +4076,8 @@ Sema::ConditionResult TreeTransform<Derived>::TransformCondition(
40764076
if (CondExpr.isInvalid())
40774077
return Sema::ConditionError();
40784078

4079-
return getSema().ActOnCondition(nullptr, Loc, CondExpr.get(), Kind);
4079+
return getSema().ActOnCondition(nullptr, Loc, CondExpr.get(), Kind,
4080+
/*MissingOK=*/true);
40804081
}
40814082

40824083
return Sema::ConditionResult();

clang/test/AST/ast-dump-invalid.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ int g(int i) {
3333
// CHECK-NEXT: |-ParmVarDecl
3434
// CHECK-NEXT: `-CompoundStmt
3535
// CHECK-NEXT: `-IfStmt {{.*}} <line:25:3, line:28:12>
36-
// CHECK-NEXT: |-OpaqueValueExpr {{.*}} <<invalid sloc>> 'bool'
36+
// CHECK-NEXT: |-RecoveryExpr {{.*}} <line:25:7> 'bool'
3737
// CHECK-NEXT: |-ReturnStmt {{.*}} <line:26:5, col:12>
3838
// CHECK-NEXT: | `-IntegerLiteral {{.*}} <col:12> 'int' 4
3939
// CHECK-NEXT: `-ReturnStmt {{.*}} <line:28:5, col:12>

clang/test/AST/loop-recovery.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s
2+
// RUN: not %clang_cc1 -fsyntax-only -ast-dump %s -std=c++17 | FileCheck %s
3+
4+
void test() {
5+
while(!!!) // expected-error {{expected expression}}
6+
int whileBody;
7+
// CHECK: WhileStmt
8+
// CHECK: RecoveryExpr {{.*}} <line:{{.*}}:9, col:11> 'bool'
9+
// CHECK: whileBody 'int'
10+
11+
for(!!!) // expected-error {{expected expression}} expected-error {{expected ';'}}
12+
int forBody;
13+
// CHECK: ForStmt
14+
// FIXME: the AST should have a RecoveryExpr to distinguish from for(;;)
15+
// CHECK-NOT: RecoveryExpr
16+
// CHECK: forBody 'int'
17+
18+
for(auto c : !!!) // expected-error {{expected expression}}
19+
int forEachBody;
20+
// FIXME: parse the foreach body
21+
// CHECK-NOT: CXXForRangeStmt
22+
// CHECK-NOT: forEachBody 'int'
23+
24+
do
25+
int doBody;
26+
while(!!!); // expected-error {{expected expression}}
27+
// CHECK: DoStmt
28+
// CHECK: doBody 'int'
29+
// CHECK: RecoveryExpr {{.*}} <line:{{.*}}:9, col:11> 'bool'
30+
31+
if(!!!) // expected-error {{expected expression}}
32+
int ifBody;
33+
else
34+
int elseBody;
35+
// CHECK: IfStmt
36+
// CHECK: RecoveryExpr {{.*}} <line:{{.*}}:6, col:8> 'bool'
37+
// CHECK: ifBody 'int'
38+
// CHECK: elseBody 'int'
39+
40+
switch(!!!) // expected-error {{expected expression}}
41+
int switchBody;
42+
// CHECK: SwitchStmt
43+
// CHECK: RecoveryExpr {{.*}} <line:{{.*}}:10, col:12> 'int'
44+
// CHECK: switchBody 'int'
45+
46+
switch (;) // expected-error {{expected expression}}
47+
int switchBody;
48+
// CHECK: SwitchStmt
49+
// CHECK: NullStmt
50+
// CHECK: RecoveryExpr {{.*}} <col:11> 'int'
51+
// CHECK: switchBody 'int'
52+
53+
switch (;;) // expected-error {{expected expression}}
54+
int switchBody;
55+
// CHECK: SwitchStmt
56+
// CHECK: NullStmt
57+
// CHECK: RecoveryExpr {{.*}} <col:11, col:12> 'int'
58+
// CHECK: switchBody 'int'
59+
60+
switch (!!!;) // expected-error {{expected expression}}
61+
int switchBody;
62+
// CHECK: SwitchStmt
63+
// CHECK: RecoveryExpr {{.*}} <line:{{.*}}:11, col:14> 'int'
64+
// CHECK: switchBody 'int'
65+
}

clang/test/Parser/cxx0x-attributes.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ void bad_attributes_in_do_while() {
152152
[[ab]ab] ns::i); // expected-error {{an attribute list cannot appear here}}
153153
do {} while ( // expected-note {{to match this '('}}
154154
alignas(4 ns::i; // expected-note {{to match this '('}}
155+
// expected-error@-1 {{expected ';' after do/while}}
155156
} // expected-error 2{{expected ')'}} expected-error {{expected expression}}
156157

157158
[[]] using T = int; // expected-error {{an attribute list cannot appear here}}

clang/test/Sema/complex-int.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ result = arr*brr;
1818
result = xx*yy;
1919

2020
switch (arr) { // expected-error{{statement requires expression of integer type ('_Complex int' invalid)}}
21-
case brr: ;
22-
case xx: ;
21+
case brr: ; // expected-error{{integer constant expression must have integer type}}
22+
case xx: ; // expected-error{{integer constant expression must have integer type}}
2323
}
2424
switch (ii) {
2525
case brr: ; // expected-error{{integer constant expression must have integer type}}

clang/test/SemaCXX/condition.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ void test() {
2020
while (struct S {} *x=0) ; // expected-error {{'S' cannot be defined in a condition}}
2121
while (struct {} *x=0) ; // expected-error-re {{'(unnamed struct at {{.*}})' cannot be defined in a condition}}
2222
switch (enum {E} x=0) ; // expected-error-re {{'(unnamed enum at {{.*}})' cannot be defined in a condition}}
23+
// expected-warning@-1 {{switch statement has empty body}}
24+
// expected-note@-2 {{put the semicolon on a separate line}}
2325

2426
if (int x=0) { // expected-note 2 {{previous definition is here}}
2527
int x; // expected-error {{redefinition of 'x'}}

0 commit comments

Comments
 (0)