Skip to content

Commit 619b5bf

Browse files
committed
[flang] Improve error recovery for bad/missing construct END statements
When a multi-statement construct should end with a particular END statement like "END SELECT", and that construct's END statement is missing or unrecognizable, the error recovery productions should not misinterpret a program unit END statement that follows and consume it as a misspelled construct END statement. Doing so leads to cascading errors or a failed parse. Differential Revision: https://reviews.llvm.org/D136896
1 parent b203511 commit 619b5bf

File tree

6 files changed

+32
-26
lines changed

6 files changed

+32
-26
lines changed

flang/lib/Parser/Fortran-parsers.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ TYPE_PARSER(construct<PrivateOrSequence>(Parser<PrivateStmt>{}) ||
384384

385385
// R730 end-type-stmt -> END TYPE [type-name]
386386
TYPE_PARSER(construct<EndTypeStmt>(
387-
recovery("END TYPE" >> maybe(name), endStmtErrorRecovery)))
387+
recovery("END TYPE" >> maybe(name), namedConstructEndStmtErrorRecovery)))
388388

389389
// R731 sequence-stmt -> SEQUENCE
390390
TYPE_PARSER(construct<SequenceStmt>("SEQUENCE"_tok))
@@ -607,7 +607,7 @@ TYPE_PARSER(
607607
construct<Enumerator>(namedConstant, maybe("=" >> scalarIntConstantExpr)))
608608

609609
// R763 end-enum-stmt -> END ENUM
610-
TYPE_PARSER(recovery("END ENUM"_tok, "END" >> SkipPast<'\n'>{}) >>
610+
TYPE_PARSER(recovery("END ENUM"_tok, constructEndStmtErrorRecovery) >>
611611
construct<EndEnumStmt>())
612612

613613
// R801 type-declaration-stmt ->

flang/lib/Parser/executable-parsers.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
// Per-type parsers for executable statements
1010

1111
#include "basic-parsers.h"
12-
#include "debug-parser.h"
1312
#include "expr-parsers.h"
1413
#include "misc-parsers.h"
1514
#include "stmt-parser.h"
@@ -159,8 +158,8 @@ TYPE_PARSER(construct<Selector>(variable) / lookAhead(","_tok || ")"_tok) ||
159158
construct<Selector>(expr))
160159

161160
// R1106 end-associate-stmt -> END ASSOCIATE [associate-construct-name]
162-
TYPE_PARSER(construct<EndAssociateStmt>(
163-
recovery("END ASSOCIATE" >> maybe(name), endStmtErrorRecovery)))
161+
TYPE_PARSER(construct<EndAssociateStmt>(recovery(
162+
"END ASSOCIATE" >> maybe(name), namedConstructEndStmtErrorRecovery)))
164163

165164
// R1107 block-construct ->
166165
// block-stmt [block-specification-part] block end-block-stmt
@@ -186,7 +185,7 @@ TYPE_PARSER(construct<BlockSpecificationPart>(specificationPart))
186185

187186
// R1110 end-block-stmt -> END BLOCK [block-construct-name]
188187
TYPE_PARSER(construct<EndBlockStmt>(
189-
recovery("END BLOCK" >> maybe(name), endStmtErrorRecovery)))
188+
recovery("END BLOCK" >> maybe(name), namedConstructEndStmtErrorRecovery)))
190189

191190
// R1111 change-team-construct -> change-team-stmt block end-change-team-stmt
192191
TYPE_CONTEXT_PARSER("CHANGE TEAM construct"_en_US,
@@ -226,8 +225,8 @@ TYPE_CONTEXT_PARSER("CRITICAL construct"_en_US,
226225
statement(Parser<EndCriticalStmt>{})))
227226

228227
// R1118 end-critical-stmt -> END CRITICAL [critical-construct-name]
229-
TYPE_PARSER(construct<EndCriticalStmt>(
230-
recovery("END CRITICAL" >> maybe(name), endStmtErrorRecovery)))
228+
TYPE_PARSER(construct<EndCriticalStmt>(recovery(
229+
"END CRITICAL" >> maybe(name), namedConstructEndStmtErrorRecovery)))
231230

232231
// R1119 do-construct -> do-stmt block end-do
233232
// R1120 do-stmt -> nonlabel-do-stmt | label-do-stmt
@@ -289,7 +288,7 @@ TYPE_CONTEXT_PARSER("nonlabel DO statement"_en_US,
289288
// R1132 end-do-stmt -> END DO [do-construct-name]
290289
TYPE_CONTEXT_PARSER("END DO statement"_en_US,
291290
construct<EndDoStmt>(
292-
recovery("END DO" >> maybe(name), endStmtErrorRecovery)))
291+
recovery("END DO" >> maybe(name), namedConstructEndStmtErrorRecovery)))
293292

294293
// R1133 cycle-stmt -> CYCLE [do-construct-name]
295294
TYPE_CONTEXT_PARSER(
@@ -315,8 +314,8 @@ TYPE_CONTEXT_PARSER("IF construct"_en_US,
315314
block)),
316315
maybe(construct<IfConstruct::ElseBlock>(
317316
statement(construct<ElseStmt>("ELSE" >> maybe(name))), block)),
318-
statement(construct<EndIfStmt>(
319-
recovery("END IF" >> maybe(name), endStmtErrorRecovery)))))
317+
statement(construct<EndIfStmt>(recovery(
318+
"END IF" >> maybe(name), namedConstructEndStmtErrorRecovery)))))
320319

321320
// R1139 if-stmt -> IF ( scalar-logical-expr ) action-stmt
322321
TYPE_CONTEXT_PARSER("IF statement"_en_US,
@@ -345,7 +344,7 @@ TYPE_CONTEXT_PARSER("CASE statement"_en_US,
345344
// R1151 end-select-rank-stmt -> END SELECT [select-construct-name]
346345
// R1155 end-select-type-stmt -> END SELECT [select-construct-name]
347346
TYPE_PARSER(construct<EndSelectStmt>(
348-
recovery("END SELECT" >> maybe(name), endStmtErrorRecovery)))
347+
recovery("END SELECT" >> maybe(name), namedConstructEndStmtErrorRecovery)))
349348

350349
// R1145 case-selector -> ( case-value-range-list ) | DEFAULT
351350
constexpr auto defaultKeyword{construct<Default>("DEFAULT"_tok)};

flang/lib/Parser/expr-parsers.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
#include "expr-parsers.h"
1212
#include "basic-parsers.h"
13-
#include "debug-parser.h"
1413
#include "misc-parsers.h"
1514
#include "stmt-parser.h"
1615
#include "token-parsers.h"
@@ -496,8 +495,8 @@ TYPE_CONTEXT_PARSER("ELSEWHERE statement"_en_US,
496495

497496
// R1049 end-where-stmt -> ENDWHERE [where-construct-name]
498497
TYPE_CONTEXT_PARSER("END WHERE statement"_en_US,
499-
construct<EndWhereStmt>(
500-
recovery("END WHERE" >> maybe(name), endStmtErrorRecovery)))
498+
construct<EndWhereStmt>(recovery(
499+
"END WHERE" >> maybe(name), namedConstructEndStmtErrorRecovery)))
501500

502501
// R1050 forall-construct ->
503502
// forall-construct-stmt [forall-body-construct]... end-forall-stmt
@@ -527,8 +526,8 @@ TYPE_PARSER(construct<ForallAssignmentStmt>(assignmentStmt) ||
527526

528527
// R1054 end-forall-stmt -> END FORALL [forall-construct-name]
529528
TYPE_CONTEXT_PARSER("END FORALL statement"_en_US,
530-
construct<EndForallStmt>(
531-
recovery("END FORALL" >> maybe(name), endStmtErrorRecovery)))
529+
construct<EndForallStmt>(recovery(
530+
"END FORALL" >> maybe(name), namedConstructEndStmtErrorRecovery)))
532531

533532
// R1055 forall-stmt -> FORALL concurrent-header forall-assignment-stmt
534533
TYPE_CONTEXT_PARSER("FORALL statement"_en_US,

flang/lib/Parser/io-parsers.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
// Per-type parsers for I/O statements and FORMAT
1010

1111
#include "basic-parsers.h"
12-
#include "debug-parser.h"
1312
#include "expr-parsers.h"
1413
#include "misc-parsers.h"
1514
#include "stmt-parser.h"

flang/lib/Parser/program-parsers.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
// Per-type parsers for program units
1010

1111
#include "basic-parsers.h"
12-
#include "debug-parser.h"
1312
#include "expr-parsers.h"
1413
#include "misc-parsers.h"
1514
#include "stmt-parser.h"
@@ -31,10 +30,10 @@ namespace Fortran::parser {
3130
// statement without an otherwise empty list of dummy arguments. That
3231
// MODULE prefix is disallowed by a constraint (C1547) in this context,
3332
// so the standard language is not ambiguous, but disabling its misrecognition
34-
// here would require context-sensitive keyword recognition or (or via)
35-
// variant parsers for several productions; giving the "module" production
36-
// priority here is a cleaner solution, though regrettably subtle. Enforcing
37-
// C1547 is done in semantics.
33+
// here would require context-sensitive keyword recognition or variant parsers
34+
// for several productions; giving the "module" production priority here is a
35+
// cleaner solution, though regrettably subtle.
36+
// Enforcing C1547 is done in semantics.
3837
static constexpr auto programUnit{
3938
construct<ProgramUnit>(indirect(Parser<Module>{})) ||
4039
construct<ProgramUnit>(indirect(functionSubprogram)) ||
@@ -329,7 +328,9 @@ TYPE_PARSER(construct<InterfaceStmt>("INTERFACE" >> maybe(genericSpec)) ||
329328
construct<InterfaceStmt>(construct<Abstract>("ABSTRACT INTERFACE"_sptok)))
330329

331330
// R1504 end-interface-stmt -> END INTERFACE [generic-spec]
332-
TYPE_PARSER(construct<EndInterfaceStmt>("END INTERFACE" >> maybe(genericSpec)))
331+
TYPE_PARSER(
332+
construct<EndInterfaceStmt>(recovery("END INTERFACE" >> maybe(genericSpec),
333+
constructEndStmtErrorRecovery >> pure<std::optional<GenericSpec>>())))
333334

334335
// R1505 interface-body ->
335336
// function-stmt [specification-part] end-function-stmt |

flang/lib/Parser/stmt-parser.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,16 @@ constexpr auto atEndOfStmt{space >>
9090
withMessage("expected end of statement"_err_en_US, lookAhead(";\n"_ch))};
9191
constexpr auto bareEnd{noNameEnd / recovery(atEndOfStmt, SkipTo<'\n'>{})};
9292

93-
constexpr auto endStmtErrorRecovery{
94-
("END"_tok >> SkipTo<'\n'>{} || ok) >> missingOptionalName};
93+
// For unrecognizable construct END statements. Be sure to not consume
94+
// a program unit's END statement.
95+
constexpr auto progUnitEndStmt{
96+
"END" >> (lookAhead("\n"_ch) || "SUBROUTINE"_tok || "FUNCTION"_tok ||
97+
"PROCEDURE"_tok || "MODULE"_tok || "SUBMODULE"_tok ||
98+
"PROGRAM"_tok || "BLOCK DATA"_tok)};
99+
constexpr auto constructEndStmtErrorRecovery{
100+
!progUnitEndStmt >> ("END"_tok >> SkipTo<'\n'>{} || ok)};
101+
constexpr auto namedConstructEndStmtErrorRecovery{
102+
constructEndStmtErrorRecovery >> missingOptionalName};
95103

96104
constexpr auto progUnitEndStmtErrorRecovery{
97105
(many(!"END"_tok >> SkipPast<'\n'>{}) >>

0 commit comments

Comments
 (0)