Skip to content

Commit 10f2ed4

Browse files
committed
[flang][parser] Better error recovery for SUBROUTINE/FUNCTION statements
When there's an error in a SUBROUTINE or FUNCTION statement, errors cascade quickly because the body of the subprogram or interface isn't in the right context. So, if a SUBROUTINE or FUNCTION statement is expected, and contains a SUBROUTINE or FUNCTION keyword, it counts as one -- retain and emit any errors pertaining to the arguments or suffix, recover to the end of the line if needed, and proceed.
1 parent cfb92be commit 10f2ed4

File tree

3 files changed

+63
-15
lines changed

3 files changed

+63
-15
lines changed

flang/lib/Parser/program-parsers.cpp

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,27 @@ namespace Fortran::parser {
3434
// for several productions; giving the "module" production priority here is a
3535
// cleaner solution, though regrettably subtle.
3636
// Enforcing C1547 is done in semantics.
37+
static constexpr auto notATopLevelFunctionStmt{
38+
// REAL FUNCTION F(10) at the top level is the first declaration
39+
// of a main program
40+
// REAL FUNCTION is too
41+
// REAL FUNCTION F/1./ is too
42+
// REAL FUNCTION F,G is too
43+
// REAL FUNCTION F(X) is a FunctionStmt
44+
// REAL FUNCTION F can be a FunctionStmt
45+
declarationTypeSpec >>
46+
many("PURE"_tok || "IMPURE"_tok || "ELEMENTAL"_tok || "RECURSIVE"_tok ||
47+
"NON_RECURSIVE"_tok || "MODULE"_tok) >>
48+
"FUNCTION"_tok >>
49+
!(name >> (extension<LanguageFeature::OmitFunctionDummies>(atEndOfStmt) ||
50+
parenthesized(optionalList(name)) >> ok))};
3751
static constexpr auto programUnit{
3852
construct<ProgramUnit>(indirect(Parser<Module>{})) ||
39-
construct<ProgramUnit>(indirect(functionSubprogram)) ||
4053
construct<ProgramUnit>(indirect(subroutineSubprogram)) ||
4154
construct<ProgramUnit>(indirect(Parser<Submodule>{})) ||
4255
construct<ProgramUnit>(indirect(Parser<BlockData>{})) ||
56+
!notATopLevelFunctionStmt >>
57+
construct<ProgramUnit>(indirect(functionSubprogram)) ||
4358
construct<ProgramUnit>(indirect(Parser<MainProgram>{}))};
4459
static constexpr auto normalProgramUnit{StartNewSubprogram{} >> programUnit /
4560
skipMany(";"_tok) / space / recovery(endOfLine, SkipPast<'\n'>{})};
@@ -532,15 +547,20 @@ TYPE_CONTEXT_PARSER("FUNCTION subprogram"_en_US,
532547
// [prefix] FUNCTION function-name ( [dummy-arg-name-list] ) [suffix]
533548
// R1526 prefix -> prefix-spec [prefix-spec]...
534549
// R1531 dummy-arg-name -> name
535-
TYPE_CONTEXT_PARSER("FUNCTION statement"_en_US,
536-
construct<FunctionStmt>(many(prefixSpec), "FUNCTION" >> name,
537-
parenthesized(optionalList(name)), maybe(suffix)) ||
550+
551+
TYPE_PARSER(construct<FunctionStmt>(many(prefixSpec), "FUNCTION" >> name,
552+
parenthesized(optionalList(name)), maybe(suffix)) /
553+
atEndOfStmt ||
554+
construct<FunctionStmt>(many(prefixSpec), "FUNCTION" >> name / atEndOfStmt,
555+
// PGI & Intel accept "FUNCTION F"
538556
extension<LanguageFeature::OmitFunctionDummies>(
539557
"nonstandard usage: FUNCTION statement without dummy argument list"_port_en_US,
540-
construct<FunctionStmt>( // PGI & Intel accept "FUNCTION F"
541-
many(prefixSpec), "FUNCTION" >> name,
542-
construct<std::list<Name>>(),
543-
construct<std::optional<Suffix>>())))
558+
pure<std::list<Name>>()),
559+
pure<std::optional<Suffix>>()) ||
560+
// error recovery
561+
construct<FunctionStmt>(many(prefixSpec), "FUNCTION" >> name,
562+
defaulted(parenthesized(optionalList(name))), maybe(suffix)) /
563+
checkEndOfKnownStmt)
544564

545565
// R1532 suffix ->
546566
// proc-language-binding-spec [RESULT ( result-name )] |
@@ -566,11 +586,13 @@ TYPE_CONTEXT_PARSER("SUBROUTINE subprogram"_en_US,
566586
// [prefix] SUBROUTINE subroutine-name [( [dummy-arg-list] )
567587
// [proc-language-binding-spec]]
568588
TYPE_PARSER(
569-
construct<SubroutineStmt>(many(prefixSpec), "SUBROUTINE" >> name,
570-
parenthesized(optionalList(dummyArg)), maybe(languageBindingSpec)) ||
571-
construct<SubroutineStmt>(many(prefixSpec), "SUBROUTINE" >> name,
572-
pure<std::list<DummyArg>>(),
573-
pure<std::optional<LanguageBindingSpec>>()))
589+
(construct<SubroutineStmt>(many(prefixSpec), "SUBROUTINE" >> name,
590+
!"("_tok >> pure<std::list<DummyArg>>(),
591+
pure<std::optional<LanguageBindingSpec>>()) ||
592+
construct<SubroutineStmt>(many(prefixSpec), "SUBROUTINE" >> name,
593+
defaulted(parenthesized(optionalList(dummyArg))),
594+
maybe(languageBindingSpec))) /
595+
checkEndOfKnownStmt)
574596
575597
// R1536 dummy-arg -> dummy-arg-name | *
576598
TYPE_PARSER(construct<DummyArg>(name) || construct<DummyArg>(star))

flang/lib/Parser/stmt-parser.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ inline constexpr auto unterminatedStatement(const PA &p) {
3030
maybe(label), space >> p));
3131
}
3232

33+
constexpr auto atEndOfStmt{space >>
34+
withMessage("expected end of statement"_err_en_US, lookAhead(";\n"_ch))};
35+
constexpr auto checkEndOfKnownStmt{recovery(atEndOfStmt, SkipTo<'\n'>{})};
36+
3337
constexpr auto endOfLine{
3438
"\n"_ch >> ok || fail("expected end of line"_err_en_US)};
3539

@@ -86,8 +90,6 @@ constexpr auto executionPartErrorRecovery{stmtErrorRecoveryStart >>
8690
// END statement error recovery
8791
constexpr auto missingOptionalName{pure<std::optional<Name>>()};
8892
constexpr auto noNameEnd{"END" >> missingOptionalName};
89-
constexpr auto atEndOfStmt{space >>
90-
withMessage("expected end of statement"_err_en_US, lookAhead(";\n"_ch))};
9193
constexpr auto bareEnd{noNameEnd / recovery(atEndOfStmt, SkipTo<'\n'>{})};
9294

9395
// For unrecognizable construct END statements. Be sure to not consume

flang/test/Parser/recovery04.f90

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
2+
module m
3+
contains
4+
!CHECK: expected end of statement
5+
!CHECK: subroutine s1(var i, j)
6+
subroutine s1(var i, j)
7+
end subroutine
8+
!CHECK: expected end of statement
9+
!CHECK: subroutine s2[b]
10+
subroutine s2[b]
11+
end subroutine
12+
!CHECK: expected end of statement
13+
!CHECK: function f1(var i, j)
14+
function f1(var i, j)
15+
end function
16+
!CHECK: expected end of statement
17+
!CHECK: function f2[b]
18+
function f2[b]
19+
end function
20+
!CHECK: expected end of statement
21+
!CHECK: function f3(a,*)
22+
function f3(a,*)
23+
end function
24+
end

0 commit comments

Comments
 (0)