Skip to content

Commit 010c55b

Browse files
authored
[flang] Improve error recovery in tricky situation (#95168)
When the very first statement of the executable part has syntax errors, it's not at all obvious whether the error messages that are reported to the user should be those from its failure to be the last statement of the specification part or its failure to be the first executable statement when both failures are at the same character in the cooked character stream. Fortran makes this problem more exciting by allowing statement function definitions look a lot like several executable statements. The current error recovery scheme for declaration constructs depends on a look-ahead test to see whether the failed construct is actually the first executable statement. This works fine when the first executable statement is not in error, but should also allow for some error cases that begin with the tokens of an executable statement. This can obviously still go wrong for declaration constructs that are unparseable and also have ambiguity in their leading tokens with executable statements, but that seems to be a less likely case. Also improves error recovery for parenthesized items.
1 parent 3dd73dc commit 010c55b

File tree

5 files changed

+63
-10
lines changed

5 files changed

+63
-10
lines changed

flang/lib/Parser/expr-parsers.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ TYPE_PARSER(construct<AcImpliedDoControl>(
7070
constexpr auto primary{instrumented("primary"_en_US,
7171
first(construct<Expr>(indirect(Parser<CharLiteralConstantSubstring>{})),
7272
construct<Expr>(literalConstant),
73-
construct<Expr>(construct<Expr::Parentheses>(parenthesized(expr))),
73+
construct<Expr>(construct<Expr::Parentheses>("(" >>
74+
expr / !","_tok / recovery(")"_tok, SkipPastNested<'(', ')'>{}))),
7475
construct<Expr>(indirect(functionReference) / !"("_tok / !"%"_tok),
7576
construct<Expr>(designator / !"("_tok / !"%"_tok),
7677
construct<Expr>(indirect(Parser<SubstringInquiry>{})), // %LEN or %KIND

flang/lib/Parser/program-parsers.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,15 @@ TYPE_CONTEXT_PARSER("specification part"_en_US,
8686
// are in contexts that impose constraints on the kinds of statements that
8787
// are allowed, and so we have a variant production for declaration-construct
8888
// that implements those constraints.
89-
constexpr auto execPartLookAhead{first(actionStmt >> ok, openaccConstruct >> ok,
90-
openmpConstruct >> ok, "ASSOCIATE ("_tok, "BLOCK"_tok, "SELECT"_tok,
91-
"CHANGE TEAM"_sptok, "CRITICAL"_tok, "DO"_tok, "IF ("_tok, "WHERE ("_tok,
92-
"FORALL ("_tok, "!$CUF"_tok)};
89+
constexpr auto actionStmtLookAhead{first(actionStmt >> ok,
90+
// Also accept apparent action statements with errors if they might be
91+
// first in the execution part
92+
"ALLOCATE ("_tok, "CALL" >> name >> "("_tok, "GO TO"_tok, "OPEN ("_tok,
93+
"PRINT"_tok / space / !"("_tok, "READ ("_tok, "WRITE ("_tok)};
94+
constexpr auto execPartLookAhead{first(actionStmtLookAhead >> ok,
95+
openaccConstruct >> ok, openmpConstruct >> ok, "ASSOCIATE ("_tok,
96+
"BLOCK"_tok, "SELECT"_tok, "CHANGE TEAM"_sptok, "CRITICAL"_tok, "DO"_tok,
97+
"IF ("_tok, "WHERE ("_tok, "FORALL ("_tok, "!$CUF"_tok)};
9398
constexpr auto declErrorRecovery{
9499
stmtErrorRecoveryStart >> !execPartLookAhead >> skipStmtErrorRecovery};
95100
constexpr auto misplacedSpecificationStmt{Parser<UseStmt>{} >>
@@ -446,10 +451,13 @@ TYPE_PARSER(extension<LanguageFeature::CUDA>(
446451
"<<<" >> construct<CallStmt::Chevrons>(scalarExpr, "," >> scalarExpr,
447452
maybe("," >> scalarIntExpr), maybe("," >> scalarIntExpr)) /
448453
">>>"))
449-
TYPE_PARSER(construct<CallStmt>(
450-
sourced(construct<CallStmt>("CALL" >> Parser<ProcedureDesignator>{},
451-
maybe(Parser<CallStmt::Chevrons>{}),
452-
defaulted(parenthesized(optionalList(actualArgSpec)))))))
454+
constexpr auto actualArgSpecList{optionalList(actualArgSpec)};
455+
TYPE_CONTEXT_PARSER("CALL statement"_en_US,
456+
construct<CallStmt>(
457+
sourced(construct<CallStmt>("CALL" >> Parser<ProcedureDesignator>{},
458+
maybe(Parser<CallStmt::Chevrons>{}) / space,
459+
"(" >> actualArgSpecList / ")" ||
460+
lookAhead(endOfStmt) >> defaulted(actualArgSpecList)))))
453461

454462
// R1522 procedure-designator ->
455463
// procedure-name | proc-component-ref | data-ref % binding-name

flang/lib/Parser/token-parsers.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,8 @@ template <char goal> struct SkipPast {
560560
while (std::optional<const char *> p{state.GetNextChar()}) {
561561
if (**p == goal) {
562562
return {Success{}};
563+
} else if (**p == '\n') {
564+
break;
563565
}
564566
}
565567
return std::nullopt;
@@ -574,8 +576,32 @@ template <char goal> struct SkipTo {
574576
while (std::optional<const char *> p{state.PeekAtNextChar()}) {
575577
if (**p == goal) {
576578
return {Success{}};
579+
} else if (**p == '\n') {
580+
break;
581+
} else {
582+
state.UncheckedAdvance();
583+
}
584+
}
585+
return std::nullopt;
586+
}
587+
};
588+
589+
template <char left, char right> struct SkipPastNested {
590+
using resultType = Success;
591+
constexpr SkipPastNested() {}
592+
constexpr SkipPastNested(const SkipPastNested &) {}
593+
static std::optional<Success> Parse(ParseState &state) {
594+
int nesting{1};
595+
while (std::optional<const char *> p{state.GetNextChar()}) {
596+
if (**p == right) {
597+
if (!--nesting) {
598+
return {Success{}};
599+
}
600+
} else if (**p == left) {
601+
++nesting;
602+
} else if (**p == '\n') {
603+
break;
577604
}
578-
state.UncheckedAdvance();
579605
}
580606
return std::nullopt;
581607
}

flang/test/Parser/recovery01.f90

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
2+
program main
3+
call foo(i, &
4+
j, &
5+
k, &
6+
1$)
7+
end
8+
9+
!CHECK: error: expected ')'
10+
!CHECK: in the context: CALL statement

flang/test/Parser/recovery02.f90

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
2+
continue ! force executable part
3+
CALL ADD_HASH_BLOCK(d_c,f_c,dimc, &
4+
(h2b-1+noab*(h1b-1+noab*(p4b-noab-1+nvab*(p3b-noab-1$)))))
5+
end
6+
7+
!CHECK: error: expected ')'
8+
!CHECK: in the context: CALL statement

0 commit comments

Comments
 (0)