Skip to content

Commit 99a0a12

Browse files
authored
[flang][parser] Better error recovery for SUBROUTINE/FUNCTION statements (#100664)
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 51f7283 commit 99a0a12

File tree

3 files changed

+62
-22
lines changed

3 files changed

+62
-22
lines changed

flang/lib/Parser/program-parsers.cpp

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,31 @@
1919

2020
namespace Fortran::parser {
2121

22+
// R1530 function-stmt ->
23+
// [prefix] FUNCTION function-name ( [dummy-arg-name-list] ) [suffix]
24+
// R1526 prefix -> prefix-spec [prefix-spec]...
25+
// R1531 dummy-arg-name -> name
26+
27+
static constexpr auto validFunctionStmt{
28+
construct<FunctionStmt>(many(prefixSpec), "FUNCTION" >> name,
29+
parenthesized(optionalList(name)), maybe(suffix)) /
30+
atEndOfStmt ||
31+
construct<FunctionStmt>(many(prefixSpec), "FUNCTION" >> name / atEndOfStmt,
32+
// PGI & Intel accept "FUNCTION F"
33+
extension<LanguageFeature::OmitFunctionDummies>(
34+
"nonstandard usage: FUNCTION statement without dummy argument list"_port_en_US,
35+
pure<std::list<Name>>()),
36+
pure<std::optional<Suffix>>())};
37+
38+
// function-stmt with error recovery -- used in interfaces and internal
39+
// subprograms, but not at the top level, where REALFUNCTIONF and
40+
// INTEGERPUREELEMENTALFUNCTIONG(10) might appear as the first statement
41+
// of a main program.
42+
TYPE_PARSER(validFunctionStmt ||
43+
construct<FunctionStmt>(many(prefixSpec), "FUNCTION" >> name,
44+
defaulted(parenthesized(optionalList(name))), maybe(suffix)) /
45+
checkEndOfKnownStmt)
46+
2247
// R502 program-unit ->
2348
// main-program | external-subprogram | module | submodule | block-data
2449
// R503 external-subprogram -> function-subprogram | subroutine-subprogram
@@ -36,10 +61,11 @@ namespace Fortran::parser {
3661
// Enforcing C1547 is done in semantics.
3762
static constexpr auto programUnit{
3863
construct<ProgramUnit>(indirect(Parser<Module>{})) ||
39-
construct<ProgramUnit>(indirect(functionSubprogram)) ||
4064
construct<ProgramUnit>(indirect(subroutineSubprogram)) ||
4165
construct<ProgramUnit>(indirect(Parser<Submodule>{})) ||
4266
construct<ProgramUnit>(indirect(Parser<BlockData>{})) ||
67+
lookAhead(validFunctionStmt) >>
68+
construct<ProgramUnit>(indirect(functionSubprogram)) ||
4369
construct<ProgramUnit>(indirect(Parser<MainProgram>{}))};
4470
static constexpr auto normalProgramUnit{StartNewSubprogram{} >> programUnit /
4571
skipMany(";"_tok) / space / recovery(endOfLine, SkipPast<'\n'>{})};
@@ -529,20 +555,6 @@ TYPE_CONTEXT_PARSER("FUNCTION subprogram"_en_US,
529555
executionPart, maybe(internalSubprogramPart),
530556
unterminatedStatement(endFunctionStmt)))
531557

532-
// R1530 function-stmt ->
533-
// [prefix] FUNCTION function-name ( [dummy-arg-name-list] ) [suffix]
534-
// R1526 prefix -> prefix-spec [prefix-spec]...
535-
// R1531 dummy-arg-name -> name
536-
TYPE_CONTEXT_PARSER("FUNCTION statement"_en_US,
537-
construct<FunctionStmt>(many(prefixSpec), "FUNCTION" >> name,
538-
parenthesized(optionalList(name)), maybe(suffix)) ||
539-
extension<LanguageFeature::OmitFunctionDummies>(
540-
"nonstandard usage: FUNCTION statement without dummy argument list"_port_en_US,
541-
construct<FunctionStmt>( // PGI & Intel accept "FUNCTION F"
542-
many(prefixSpec), "FUNCTION" >> name,
543-
construct<std::list<Name>>(),
544-
construct<std::optional<Suffix>>())))
545-
546558
// R1532 suffix ->
547559
// proc-language-binding-spec [RESULT ( result-name )] |
548560
// RESULT ( result-name ) [proc-language-binding-spec]
@@ -567,11 +579,13 @@ TYPE_CONTEXT_PARSER("SUBROUTINE subprogram"_en_US,
567579
// [prefix] SUBROUTINE subroutine-name [( [dummy-arg-list] )
568580
// [proc-language-binding-spec]]
569581
TYPE_PARSER(
570-
construct<SubroutineStmt>(many(prefixSpec), "SUBROUTINE" >> name,
571-
parenthesized(optionalList(dummyArg)), maybe(languageBindingSpec)) ||
572-
construct<SubroutineStmt>(many(prefixSpec), "SUBROUTINE" >> name,
573-
pure<std::list<DummyArg>>(),
574-
pure<std::optional<LanguageBindingSpec>>()))
582+
(construct<SubroutineStmt>(many(prefixSpec), "SUBROUTINE" >> name,
583+
!"("_tok >> pure<std::list<DummyArg>>(),
584+
pure<std::optional<LanguageBindingSpec>>()) ||
585+
construct<SubroutineStmt>(many(prefixSpec), "SUBROUTINE" >> name,
586+
defaulted(parenthesized(optionalList(dummyArg))),
587+
maybe(languageBindingSpec))) /
588+
checkEndOfKnownStmt)
575589
576590
// R1536 dummy-arg -> dummy-arg-name | *
577591
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)