Commit f6ddfac4 authored by peter klausler's avatar peter klausler
Browse files

[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
parent 101c3de3
Loading
Loading
Loading
Loading
+64 −0
Original line number Diff line number Diff line
@@ -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_;
+1 −0
Original line number Diff line number Diff line
@@ -150,6 +150,7 @@ private:
  }

  void LabelField(TokenSequence &);
  void EnforceStupidEndStatementRules(const TokenSequence &);
  void SkipToEndOfLine();
  bool MustSkipToEndOfLine() const;
  void NextChar();
+6 −2
Original line number Diff line number Diff line
@@ -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(
+1 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
! 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