[flang] Enforce fixed form rules about END continuation

From subclause 6.3.3.5: a program unit END statement cannot be
continued in fixed form, and other statements cannot have initial
lines that look like program unit END statements.  I think this
is to avoid violating assumptions that are important to legacy
compilers' statement classification routines.

Differential Revision: https://reviews.llvm.org/D109933
This commit is contained in:
peter klausler 2021-09-15 15:46:43 -07:00
parent 101c3de39f
commit f6ddfac401
5 changed files with 101 additions and 2 deletions

View File

@ -217,6 +217,9 @@ void Prescanner::Statement() {
if (line.kind == LineClassification::Kind::CompilerDirective) {
SourceFormChange(tokens.ToString());
}
if (inFixedForm_ && line.kind == LineClassification::Kind::Source) {
EnforceStupidEndStatementRules(tokens);
}
tokens.CheckBadFortranCharacters(messages_).Emit(cooked_);
}
if (omitNewline_) {
@ -288,6 +291,67 @@ void Prescanner::LabelField(TokenSequence &token) {
}
}
// 6.3.3.5: A program unit END statement, or any other statement whose
// initial line resembles an END statement, shall not be continued in
// fixed form source.
void Prescanner::EnforceStupidEndStatementRules(const TokenSequence &tokens) {
CharBlock cBlock{tokens.ToCharBlock()};
const char *str{cBlock.begin()};
std::size_t n{cBlock.size()};
if (n < 3) {
return;
}
std::size_t j{0};
for (; j < n && (str[j] == ' ' || (str[j] >= '0' && str[j] <= '9')); ++j) {
}
if (j + 3 > n || std::memcmp(str + j, "end", 3) != 0) {
return;
}
// It starts with END, possibly after a label.
auto start{allSources_.GetSourcePosition(tokens.GetCharProvenance(j))};
auto end{allSources_.GetSourcePosition(tokens.GetCharProvenance(n - 1))};
if (!start || !end) {
return;
}
if (&start->file == &end->file && start->line == end->line) {
return; // no continuation
}
j += 3;
static const char *const prefixes[]{"program", "subroutine", "function",
"blockdata", "module", "submodule", nullptr};
CharBlock stmt{tokens.ToCharBlock()};
bool isPrefix{j == n || !IsLegalInIdentifier(str[j])}; // prefix is END
std::size_t endOfPrefix{j - 1};
for (const char *const *p{prefixes}; *p; ++p) {
std::size_t pLen{std::strlen(*p)};
if (j + pLen <= n && std::memcmp(str + j, *p, pLen) == 0) {
isPrefix = true; // END thing as prefix
j += pLen;
endOfPrefix = j - 1;
for (; j < n && IsLegalInIdentifier(str[j]); ++j) {
}
break;
}
}
if (isPrefix) {
auto range{tokens.GetTokenProvenanceRange(1)};
if (j == n) { // END or END thing [name]
Say(range,
"Program unit END statement may not be continued in fixed form source"_err_en_US);
} else {
auto endOfPrefixPos{
allSources_.GetSourcePosition(tokens.GetCharProvenance(endOfPrefix))};
auto next{allSources_.GetSourcePosition(tokens.GetCharProvenance(j))};
if (endOfPrefixPos && next && &endOfPrefixPos->file == &start->file &&
endOfPrefixPos->line == start->line &&
(&next->file != &start->file || next->line != start->line)) {
Say(range,
"Initial line of continued statement must not appear to be a program unit END in fixed form source"_err_en_US);
}
}
}
}
void Prescanner::SkipToEndOfLine() {
while (*at_ != '\n') {
++at_, ++column_;

View File

@ -150,6 +150,7 @@ private:
}
void LabelField(TokenSequence &);
void EnforceStupidEndStatementRules(const TokenSequence &);
void SkipToEndOfLine();
bool MustSkipToEndOfLine() const;
void NextChar();

View File

@ -286,10 +286,14 @@ llvm::raw_ostream &TokenSequence::Dump(llvm::raw_ostream &o) const {
return o;
}
Provenance TokenSequence::GetCharProvenance(std::size_t offset) const {
ProvenanceRange range{provenances_.Map(offset)};
return range.start();
}
Provenance TokenSequence::GetTokenProvenance(
std::size_t token, std::size_t offset) const {
ProvenanceRange range{provenances_.Map(start_[token] + offset)};
return range.start();
return GetCharProvenance(start_[token] + offset);
}
ProvenanceRange TokenSequence::GetTokenProvenanceRange(

View File

@ -102,6 +102,7 @@ public:
void Put(const std::string &, Provenance);
void Put(llvm::raw_string_ostream &, Provenance);
Provenance GetCharProvenance(std::size_t) const;
Provenance GetTokenProvenance(
std::size_t token, std::size_t offset = 0) const;
ProvenanceRange GetTokenProvenanceRange(

29
flang/test/Parser/end.f Normal file
View File

@ -0,0 +1,29 @@
! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
! CHECK: end.f:3:7: error: Program unit END statement may not be continued in fixed form source
e
+ nd
! CHECK: end.f:6:7: error: Program unit END statement may not be continued in fixed form source
end prog
+ ram
! CHECK: end.f:9:7: error: Program unit END statement may not be continued in fixed form source
end
+ program
! CHECK: end.f:12:7: error: Program unit END statement may not be continued in fixed form source
end
+ program
1 main
! CHECK: end.f:16:7: error: Program unit END statement may not be continued in fixed form source
end program
1 main
! CHECK: end.f:19:7: error: Initial line of continued statement must not appear to be a program unit END in fixed form source
end
+ = end + 1
! CHECK: end.f:22:7: error: Initial line of continued statement must not appear to be a program unit END in fixed form source
end module
+ = end module + 1
! CHECK-NOT: end.f:25:7: error: Initial line of continued statement must not appear to be a program unit END in fixed form source
end =
+ end + 1
! CHECK-NOT: end.f:28:7: error: Initial line of continued statement must not appear to be a program unit END in fixed form source
end block data (
+ 1) = 666