[flang-commits] [flang] 619b5bf - [flang] Improve error recovery for bad/missing construct END statements

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Sat Oct 29 13:26:16 PDT 2022


Author: Peter Klausler
Date: 2022-10-29T12:45:29-07:00
New Revision: 619b5bfc8dfa90fff327956cc8cf5bb9d4029bab

URL: https://github.com/llvm/llvm-project/commit/619b5bfc8dfa90fff327956cc8cf5bb9d4029bab
DIFF: https://github.com/llvm/llvm-project/commit/619b5bfc8dfa90fff327956cc8cf5bb9d4029bab.diff

LOG: [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

Added: 
    

Modified: 
    flang/lib/Parser/Fortran-parsers.cpp
    flang/lib/Parser/executable-parsers.cpp
    flang/lib/Parser/expr-parsers.cpp
    flang/lib/Parser/io-parsers.cpp
    flang/lib/Parser/program-parsers.cpp
    flang/lib/Parser/stmt-parser.h

Removed: 
    


################################################################################
diff  --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index a508ac2027ad0..86c3d55e5c1c6 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -384,7 +384,7 @@ TYPE_PARSER(construct<PrivateOrSequence>(Parser<PrivateStmt>{}) ||
 
 // R730 end-type-stmt -> END TYPE [type-name]
 TYPE_PARSER(construct<EndTypeStmt>(
-    recovery("END TYPE" >> maybe(name), endStmtErrorRecovery)))
+    recovery("END TYPE" >> maybe(name), namedConstructEndStmtErrorRecovery)))
 
 // R731 sequence-stmt -> SEQUENCE
 TYPE_PARSER(construct<SequenceStmt>("SEQUENCE"_tok))
@@ -607,7 +607,7 @@ TYPE_PARSER(
     construct<Enumerator>(namedConstant, maybe("=" >> scalarIntConstantExpr)))
 
 // R763 end-enum-stmt -> END ENUM
-TYPE_PARSER(recovery("END ENUM"_tok, "END" >> SkipPast<'\n'>{}) >>
+TYPE_PARSER(recovery("END ENUM"_tok, constructEndStmtErrorRecovery) >>
     construct<EndEnumStmt>())
 
 // R801 type-declaration-stmt ->

diff  --git a/flang/lib/Parser/executable-parsers.cpp b/flang/lib/Parser/executable-parsers.cpp
index 04fe1849a6fd5..fc67d11a7ed75 100644
--- a/flang/lib/Parser/executable-parsers.cpp
+++ b/flang/lib/Parser/executable-parsers.cpp
@@ -9,7 +9,6 @@
 // Per-type parsers for executable statements
 
 #include "basic-parsers.h"
-#include "debug-parser.h"
 #include "expr-parsers.h"
 #include "misc-parsers.h"
 #include "stmt-parser.h"
@@ -159,8 +158,8 @@ TYPE_PARSER(construct<Selector>(variable) / lookAhead(","_tok || ")"_tok) ||
     construct<Selector>(expr))
 
 // R1106 end-associate-stmt -> END ASSOCIATE [associate-construct-name]
-TYPE_PARSER(construct<EndAssociateStmt>(
-    recovery("END ASSOCIATE" >> maybe(name), endStmtErrorRecovery)))
+TYPE_PARSER(construct<EndAssociateStmt>(recovery(
+    "END ASSOCIATE" >> maybe(name), namedConstructEndStmtErrorRecovery)))
 
 // R1107 block-construct ->
 //         block-stmt [block-specification-part] block end-block-stmt
@@ -186,7 +185,7 @@ TYPE_PARSER(construct<BlockSpecificationPart>(specificationPart))
 
 // R1110 end-block-stmt -> END BLOCK [block-construct-name]
 TYPE_PARSER(construct<EndBlockStmt>(
-    recovery("END BLOCK" >> maybe(name), endStmtErrorRecovery)))
+    recovery("END BLOCK" >> maybe(name), namedConstructEndStmtErrorRecovery)))
 
 // R1111 change-team-construct -> change-team-stmt block end-change-team-stmt
 TYPE_CONTEXT_PARSER("CHANGE TEAM construct"_en_US,
@@ -226,8 +225,8 @@ TYPE_CONTEXT_PARSER("CRITICAL construct"_en_US,
         statement(Parser<EndCriticalStmt>{})))
 
 // R1118 end-critical-stmt -> END CRITICAL [critical-construct-name]
-TYPE_PARSER(construct<EndCriticalStmt>(
-    recovery("END CRITICAL" >> maybe(name), endStmtErrorRecovery)))
+TYPE_PARSER(construct<EndCriticalStmt>(recovery(
+    "END CRITICAL" >> maybe(name), namedConstructEndStmtErrorRecovery)))
 
 // R1119 do-construct -> do-stmt block end-do
 // R1120 do-stmt -> nonlabel-do-stmt | label-do-stmt
@@ -289,7 +288,7 @@ TYPE_CONTEXT_PARSER("nonlabel DO statement"_en_US,
 // R1132 end-do-stmt -> END DO [do-construct-name]
 TYPE_CONTEXT_PARSER("END DO statement"_en_US,
     construct<EndDoStmt>(
-        recovery("END DO" >> maybe(name), endStmtErrorRecovery)))
+        recovery("END DO" >> maybe(name), namedConstructEndStmtErrorRecovery)))
 
 // R1133 cycle-stmt -> CYCLE [do-construct-name]
 TYPE_CONTEXT_PARSER(
@@ -315,8 +314,8 @@ TYPE_CONTEXT_PARSER("IF construct"_en_US,
             block)),
         maybe(construct<IfConstruct::ElseBlock>(
             statement(construct<ElseStmt>("ELSE" >> maybe(name))), block)),
-        statement(construct<EndIfStmt>(
-            recovery("END IF" >> maybe(name), endStmtErrorRecovery)))))
+        statement(construct<EndIfStmt>(recovery(
+            "END IF" >> maybe(name), namedConstructEndStmtErrorRecovery)))))
 
 // R1139 if-stmt -> IF ( scalar-logical-expr ) action-stmt
 TYPE_CONTEXT_PARSER("IF statement"_en_US,
@@ -345,7 +344,7 @@ TYPE_CONTEXT_PARSER("CASE statement"_en_US,
 // R1151 end-select-rank-stmt -> END SELECT [select-construct-name]
 // R1155 end-select-type-stmt -> END SELECT [select-construct-name]
 TYPE_PARSER(construct<EndSelectStmt>(
-    recovery("END SELECT" >> maybe(name), endStmtErrorRecovery)))
+    recovery("END SELECT" >> maybe(name), namedConstructEndStmtErrorRecovery)))
 
 // R1145 case-selector -> ( case-value-range-list ) | DEFAULT
 constexpr auto defaultKeyword{construct<Default>("DEFAULT"_tok)};

diff  --git a/flang/lib/Parser/expr-parsers.cpp b/flang/lib/Parser/expr-parsers.cpp
index 6b53866e820a0..45e6b2869c02b 100644
--- a/flang/lib/Parser/expr-parsers.cpp
+++ b/flang/lib/Parser/expr-parsers.cpp
@@ -10,7 +10,6 @@
 
 #include "expr-parsers.h"
 #include "basic-parsers.h"
-#include "debug-parser.h"
 #include "misc-parsers.h"
 #include "stmt-parser.h"
 #include "token-parsers.h"
@@ -496,8 +495,8 @@ TYPE_CONTEXT_PARSER("ELSEWHERE statement"_en_US,
 
 // R1049 end-where-stmt -> ENDWHERE [where-construct-name]
 TYPE_CONTEXT_PARSER("END WHERE statement"_en_US,
-    construct<EndWhereStmt>(
-        recovery("END WHERE" >> maybe(name), endStmtErrorRecovery)))
+    construct<EndWhereStmt>(recovery(
+        "END WHERE" >> maybe(name), namedConstructEndStmtErrorRecovery)))
 
 // R1050 forall-construct ->
 //         forall-construct-stmt [forall-body-construct]... end-forall-stmt
@@ -527,8 +526,8 @@ TYPE_PARSER(construct<ForallAssignmentStmt>(assignmentStmt) ||
 
 // R1054 end-forall-stmt -> END FORALL [forall-construct-name]
 TYPE_CONTEXT_PARSER("END FORALL statement"_en_US,
-    construct<EndForallStmt>(
-        recovery("END FORALL" >> maybe(name), endStmtErrorRecovery)))
+    construct<EndForallStmt>(recovery(
+        "END FORALL" >> maybe(name), namedConstructEndStmtErrorRecovery)))
 
 // R1055 forall-stmt -> FORALL concurrent-header forall-assignment-stmt
 TYPE_CONTEXT_PARSER("FORALL statement"_en_US,

diff  --git a/flang/lib/Parser/io-parsers.cpp b/flang/lib/Parser/io-parsers.cpp
index c7ef96e677465..538f03dfdbdcf 100644
--- a/flang/lib/Parser/io-parsers.cpp
+++ b/flang/lib/Parser/io-parsers.cpp
@@ -9,7 +9,6 @@
 // Per-type parsers for I/O statements and FORMAT
 
 #include "basic-parsers.h"
-#include "debug-parser.h"
 #include "expr-parsers.h"
 #include "misc-parsers.h"
 #include "stmt-parser.h"

diff  --git a/flang/lib/Parser/program-parsers.cpp b/flang/lib/Parser/program-parsers.cpp
index 476be856fc713..9a74b3b35318b 100644
--- a/flang/lib/Parser/program-parsers.cpp
+++ b/flang/lib/Parser/program-parsers.cpp
@@ -9,7 +9,6 @@
 // Per-type parsers for program units
 
 #include "basic-parsers.h"
-#include "debug-parser.h"
 #include "expr-parsers.h"
 #include "misc-parsers.h"
 #include "stmt-parser.h"
@@ -31,10 +30,10 @@ namespace Fortran::parser {
 // statement without an otherwise empty list of dummy arguments.  That
 // MODULE prefix is disallowed by a constraint (C1547) in this context,
 // so the standard language is not ambiguous, but disabling its misrecognition
-// here would require context-sensitive keyword recognition or (or via)
-// variant parsers for several productions; giving the "module" production
-// priority here is a cleaner solution, though regrettably subtle.  Enforcing
-// C1547 is done in semantics.
+// here would require context-sensitive keyword recognition or variant parsers
+// for several productions; giving the "module" production priority here is a
+// cleaner solution, though regrettably subtle.
+// Enforcing C1547 is done in semantics.
 static constexpr auto programUnit{
     construct<ProgramUnit>(indirect(Parser<Module>{})) ||
     construct<ProgramUnit>(indirect(functionSubprogram)) ||
@@ -329,7 +328,9 @@ TYPE_PARSER(construct<InterfaceStmt>("INTERFACE" >> maybe(genericSpec)) ||
     construct<InterfaceStmt>(construct<Abstract>("ABSTRACT INTERFACE"_sptok)))
 
 // R1504 end-interface-stmt -> END INTERFACE [generic-spec]
-TYPE_PARSER(construct<EndInterfaceStmt>("END INTERFACE" >> maybe(genericSpec)))
+TYPE_PARSER(
+    construct<EndInterfaceStmt>(recovery("END INTERFACE" >> maybe(genericSpec),
+        constructEndStmtErrorRecovery >> pure<std::optional<GenericSpec>>())))
 
 // R1505 interface-body ->
 //         function-stmt [specification-part] end-function-stmt |

diff  --git a/flang/lib/Parser/stmt-parser.h b/flang/lib/Parser/stmt-parser.h
index cd1c69beedd4a..bc0073f487f46 100644
--- a/flang/lib/Parser/stmt-parser.h
+++ b/flang/lib/Parser/stmt-parser.h
@@ -90,8 +90,16 @@ constexpr auto atEndOfStmt{space >>
     withMessage("expected end of statement"_err_en_US, lookAhead(";\n"_ch))};
 constexpr auto bareEnd{noNameEnd / recovery(atEndOfStmt, SkipTo<'\n'>{})};
 
-constexpr auto endStmtErrorRecovery{
-    ("END"_tok >> SkipTo<'\n'>{} || ok) >> missingOptionalName};
+// For unrecognizable construct END statements.  Be sure to not consume
+// a program unit's END statement.
+constexpr auto progUnitEndStmt{
+    "END" >> (lookAhead("\n"_ch) || "SUBROUTINE"_tok || "FUNCTION"_tok ||
+                 "PROCEDURE"_tok || "MODULE"_tok || "SUBMODULE"_tok ||
+                 "PROGRAM"_tok || "BLOCK DATA"_tok)};
+constexpr auto constructEndStmtErrorRecovery{
+    !progUnitEndStmt >> ("END"_tok >> SkipTo<'\n'>{} || ok)};
+constexpr auto namedConstructEndStmtErrorRecovery{
+    constructEndStmtErrorRecovery >> missingOptionalName};
 
 constexpr auto progUnitEndStmtErrorRecovery{
     (many(!"END"_tok >> SkipPast<'\n'>{}) >>


        


More information about the flang-commits mailing list