[flang] Revamp messaging. Add ""_err_en_US to denote fatal error messages.
authorpeter klausler <pklausler@nvidia.com>
Mon, 2 Apr 2018 22:51:04 +0000 (15:51 -0700)
committerpeter klausler <pklausler@nvidia.com>
Mon, 2 Apr 2018 22:51:04 +0000 (15:51 -0700)
Original-commit: flang-compiler/f18@7ddc807a1bb6b33613ebdfab9c089bb2eabdcea9
Reviewed-on: https://github.com/flang-compiler/f18/pull/38
Tree-same-pre-rewrite: false

16 files changed:
flang/lib/parser/basic-parsers.h
flang/lib/parser/characters.h
flang/lib/parser/debug-parser.h
flang/lib/parser/message.cc
flang/lib/parser/message.h
flang/lib/parser/parse-state.h
flang/lib/parser/parsing.cc
flang/lib/parser/parsing.h
flang/lib/parser/preprocessor.cc
flang/lib/parser/prescan.cc
flang/lib/parser/prescan.h
flang/lib/parser/token-parsers.h
flang/lib/parser/unparse.cc
flang/tools/f18/f18.cc
flang/tools/f18/test-sema.cc
flang/tools/f18/test-type.cc

index b7b1f4d..46e7809 100644 (file)
@@ -29,7 +29,7 @@
 namespace Fortran {
 namespace parser {
 
-// fail<A>("..."_en_US) returns a parser that never succeeds.  It reports an
+// fail<A>("..."_err_en_US) returns a parser that never succeeds.  It reports an
 // error message at the current position.  The result type is unused,
 // but might have to be specified at the point of call for satisfy
 // the type checker.  The state remains valid.
@@ -39,7 +39,7 @@ public:
   constexpr FailParser(const FailParser &) = default;
   constexpr explicit FailParser(MessageFixedText t) : text_{t} {}
   std::optional<A> Parse(ParseState *state) const {
-    state->PutMessage(text_);
+    state->Say(text_);
     return {};
   }
 
@@ -1188,7 +1188,7 @@ constexpr struct NextCh {
   constexpr NextCh() {}
   std::optional<const char *> Parse(ParseState *state) const {
     if (state->IsAtEnd()) {
-      state->PutMessage("end of file"_en_US);
+      state->Say("end of file"_err_en_US);
       return {};
     }
     const char *at{state->GetLocation()};
@@ -1214,7 +1214,7 @@ public:
     if (result.has_value()) {
       state->set_anyConformanceViolation();
       if (state->warnOnNonstandardUsage()) {
-        state->PutMessage(at, "nonstandard usage"_en_US);
+        state->Say(at, "nonstandard usage"_en_US);
       }
     }
     return result;
@@ -1245,7 +1245,7 @@ public:
     if (result) {
       state->set_anyConformanceViolation();
       if (state->warnOnDeprecatedUsage()) {
-        state->PutMessage(at, "deprecated usage"_en_US);
+        state->Say(at, "deprecated usage"_en_US);
       }
     }
     return result;
index b0985d8..4725811 100644 (file)
@@ -14,7 +14,7 @@ namespace parser {
 
 enum class Encoding { UTF8, EUC_JP };
 
-static inline constexpr bool IsUpperCaseLetter(char ch) {
+inline constexpr bool IsUpperCaseLetter(char ch) {
   if constexpr ('A' == static_cast<char>(0xc1)) {
     // EBCDIC
     return (ch >= 'A' && ch <= 'I') || (ch >= 'J' && ch <= 'R') ||
@@ -24,7 +24,7 @@ static inline constexpr bool IsUpperCaseLetter(char ch) {
   }
 }
 
-static inline constexpr bool IsLowerCaseLetter(char ch) {
+inline constexpr bool IsLowerCaseLetter(char ch) {
   if constexpr ('a' == static_cast<char>(0x81)) {
     // EBCDIC
     return (ch >= 'a' && ch <= 'i') || (ch >= 'j' && ch <= 'r') ||
@@ -34,40 +34,36 @@ static inline constexpr bool IsLowerCaseLetter(char ch) {
   }
 }
 
-static inline constexpr bool IsLetter(char ch) {
+inline constexpr bool IsLetter(char ch) {
   return IsUpperCaseLetter(ch) || IsLowerCaseLetter(ch);
 }
 
-static inline constexpr bool IsDecimalDigit(char ch) {
-  return ch >= '0' && ch <= '9';
-}
+inline constexpr bool IsDecimalDigit(char ch) { return ch >= '0' && ch <= '9'; }
 
-static inline constexpr bool IsHexadecimalDigit(char ch) {
+inline constexpr bool IsHexadecimalDigit(char ch) {
   return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') ||
       (ch >= 'a' && ch <= 'f');
 }
 
-static inline constexpr bool IsOctalDigit(char ch) {
-  return ch >= '0' && ch <= '7';
-}
+inline constexpr bool IsOctalDigit(char ch) { return ch >= '0' && ch <= '7'; }
 
-static inline constexpr bool IsLegalIdentifierStart(char ch) {
+inline constexpr bool IsLegalIdentifierStart(char ch) {
   return IsLetter(ch) || ch == '_' || ch == '@' || ch == '$';
 }
 
-static inline constexpr bool IsLegalInIdentifier(char ch) {
+inline constexpr bool IsLegalInIdentifier(char ch) {
   return IsLegalIdentifierStart(ch) || IsDecimalDigit(ch);
 }
 
-static inline constexpr char ToLowerCaseLetter(char ch) {
+inline constexpr char ToLowerCaseLetter(char ch) {
   return IsUpperCaseLetter(ch) ? ch - 'A' + 'a' : ch;
 }
 
-static inline constexpr char ToLowerCaseLetter(char &&ch) {
+inline constexpr char ToLowerCaseLetter(char &&ch) {
   return IsUpperCaseLetter(ch) ? ch - 'A' + 'a' : ch;
 }
 
-static inline std::string ToLowerCaseLetters(const std::string &str) {
+inline std::string ToLowerCaseLetters(const std::string &str) {
   std::string lowered{str};
   for (char &ch : lowered) {
     ch = ToLowerCaseLetter(ch);
@@ -75,11 +71,11 @@ static inline std::string ToLowerCaseLetters(const std::string &str) {
   return lowered;
 }
 
-static inline constexpr char ToUpperCaseLetter(char ch) {
+inline constexpr char ToUpperCaseLetter(char ch) {
   return IsLowerCaseLetter(ch) ? ch - 'a' + 'A' : ch;
 }
 
-static inline constexpr char ToUpperCaseLetter(char &&ch) {
+inline constexpr char ToUpperCaseLetter(char &&ch) {
   return IsLowerCaseLetter(ch) ? ch - 'a' + 'A' : ch;
 }
 
@@ -91,19 +87,19 @@ static inline std::string ToUpperCaseLetters(const std::string &str) {
   return raised;
 }
 
-static inline constexpr bool IsSameApartFromCase(char x, char y) {
+inline constexpr bool IsSameApartFromCase(char x, char y) {
   return ToLowerCaseLetter(x) == ToLowerCaseLetter(y);
 }
 
-static inline constexpr char DecimalDigitValue(char ch) { return ch - '0'; }
+inline constexpr char DecimalDigitValue(char ch) { return ch - '0'; }
 
-static inline constexpr char HexadecimalDigitValue(char ch) {
+inline constexpr char HexadecimalDigitValue(char ch) {
   return IsUpperCaseLetter(ch)
       ? ch - 'A' + 10
       : IsLowerCaseLetter(ch) ? ch - 'a' + 10 : DecimalDigitValue(ch);
 }
 
-static constexpr std::optional<char> BackslashEscapeValue(char ch) {
+constexpr std::optional<char> BackslashEscapeValue(char ch) {
   switch (ch) {
   // case 'a': return {'\a'};  pgf90 doesn't know about \a
   case 'b': return {'\b'};
@@ -119,7 +115,7 @@ static constexpr std::optional<char> BackslashEscapeValue(char ch) {
   }
 }
 
-static constexpr std::optional<char> BackslashEscapeChar(char ch) {
+constexpr std::optional<char> BackslashEscapeChar(char ch) {
   switch (ch) {
   // case '\a': return {'a'};  pgf90 doesn't know about \a
   case '\b': return {'b'};
index 0ff96c4..4f3d675 100644 (file)
@@ -27,8 +27,8 @@ public:
       context->Emit(std::cout, cooked);
     }
     Provenance p{cooked.GetProvenance(state->GetLocation()).start()};
-    cooked.allSources().Identify(std::cout, p, "");
-    std::cout << "   parser debug: " << std::string{str_, length_} << '\n';
+    cooked.allSources().Identify(std::cout, p, "", true);
+    std::cout << "   parser debug: " << std::string{str_, length_} << "\n\n";
     return {Success{}};
   }
 
index cd2546d..54bc16d 100644 (file)
@@ -18,7 +18,8 @@ std::string MessageFixedText::ToString() const {
   return std::string{str_, /*not in std::*/ strnlen(str_, bytes_)};
 }
 
-MessageFormattedText::MessageFormattedText(MessageFixedText text, ...) {
+MessageFormattedText::MessageFormattedText(MessageFixedText text, ...)
+  : isFatal_{text.isFatal()} {
   const char *p{text.str()};
   std::string asString;
   if (p[text.size()] != '\0') {
@@ -59,13 +60,16 @@ Provenance Message::Emit(
     cooked.allSources().Identify(o, provenance, "", echoSourceLine);
   }
   o << "   ";
+  if (isFatal_) {
+    o << "ERROR: ";
+  }
   if (string_.empty()) {
     if (isExpectedText_) {
       std::string goal{text_.ToString()};
       if (goal == "\n") {
-        o << "expected end of line"_en_US;
+        o << "expected end of line"_err_en_US;
       } else {
-        o << MessageFormattedText("expected '%s'"_en_US, goal.data())
+        o << MessageFormattedText("expected '%s'"_err_en_US, goal.data())
                  .MoveString();
       }
     } else {
@@ -90,5 +94,14 @@ void Messages::Emit(
     msg.Emit(o, cooked_, echoSourceLines);
   }
 }
+
+bool Messages::AnyFatalError() const {
+  for (const auto &msg : messages_) {
+    if (msg.isFatal()) {
+      return true;
+    }
+  }
+  return false;
+}
 }  // namespace parser
 }  // namespace Fortran
index b140dba..583507c 100644 (file)
 namespace Fortran {
 namespace parser {
 
-// Use "..."_en_US literals to define the static text of a message.
+// Use "..."_err_en_US and "..."_en_US literals to define the static
+// text and fatality of a message.
 class MessageFixedText {
 public:
   MessageFixedText() {}
-  constexpr MessageFixedText(const char str[], std::size_t n)
-    : str_{str}, bytes_{n} {}
+  constexpr MessageFixedText(
+      const char str[], std::size_t n, bool isFatal = false)
+    : str_{str}, bytes_{n}, isFatal_{isFatal} {}
   constexpr MessageFixedText(const MessageFixedText &) = default;
   MessageFixedText(MessageFixedText &&) = default;
   constexpr MessageFixedText &operator=(const MessageFixedText &) = default;
@@ -30,16 +32,23 @@ public:
   const char *str() const { return str_; }
   std::size_t size() const { return bytes_; }
   bool empty() const { return bytes_ == 0; }
+  bool isFatal() const { return isFatal_; }
 
   std::string ToString() const;
 
 private:
   const char *str_{nullptr};
   std::size_t bytes_{0};
+  bool isFatal_{false};
 };
 
 constexpr MessageFixedText operator""_en_US(const char str[], std::size_t n) {
-  return MessageFixedText{str, n};
+  return MessageFixedText{str, n, false /* not fatal */};
+}
+
+constexpr MessageFixedText operator""_err_en_US(
+    const char str[], std::size_t n) {
+  return MessageFixedText{str, n, true /* fatal */};
 }
 
 std::ostream &operator<<(std::ostream &, const MessageFixedText &);
@@ -48,12 +57,15 @@ class MessageFormattedText {
 public:
   MessageFormattedText(MessageFixedText, ...);
   std::string MoveString() { return std::move(string_); }
+  bool isFatal() const { return isFatal_; }
 
 private:
   std::string string_;
+  bool isFatal_{false};
 };
 
-// Represents a formatted rendition of "expected '%s'"_en_US on a constant text.
+// Represents a formatted rendition of "expected '%s'"_err_en_US
+// on a constant text.
 class MessageExpectedText {
 public:
   MessageExpectedText(const char *s, std::size_t n) : str_{s}, bytes_{n} {}
@@ -78,20 +90,23 @@ public:
   Message &operator=(Message &&that) = default;
 
   Message(Provenance p, MessageFixedText t, MessageContext c = nullptr)
-    : provenance_{p}, text_{t}, context_{c} {}
+    : provenance_{p}, text_{t}, context_{c}, isFatal_{t.isFatal()} {}
   Message(Provenance p, MessageFormattedText &&s, MessageContext c = nullptr)
-    : provenance_{p}, string_{s.MoveString()}, context_{c} {}
+    : provenance_{p}, string_{s.MoveString()}, context_{c}, isFatal_{
+                                                                s.isFatal()} {}
   Message(Provenance p, MessageExpectedText t, MessageContext c = nullptr)
     : provenance_{p}, text_{t.AsMessageFixedText()},
-      isExpectedText_{true}, context_{c} {}
+      isExpectedText_{true}, context_{c}, isFatal_{true} {}
 
   Message(const char *csl, MessageFixedText t, MessageContext c = nullptr)
-    : cookedSourceLocation_{csl}, text_{t}, context_{c} {}
+    : cookedSourceLocation_{csl}, text_{t}, context_{c}, isFatal_{t.isFatal()} {
+  }
   Message(const char *csl, MessageFormattedText &&s, MessageContext c = nullptr)
-    : cookedSourceLocation_{csl}, string_{s.MoveString()}, context_{c} {}
+    : cookedSourceLocation_{csl}, string_{s.MoveString()}, context_{c},
+      isFatal_{s.isFatal()} {}
   Message(const char *csl, MessageExpectedText t, MessageContext c = nullptr)
     : cookedSourceLocation_{csl}, text_{t.AsMessageFixedText()},
-      isExpectedText_{true}, context_{c} {}
+      isExpectedText_{true}, context_{c}, isFatal_{true} {}
 
   bool operator<(const Message &that) const {
     if (cookedSourceLocation_ != nullptr) {
@@ -106,6 +121,7 @@ public:
   Provenance provenance() const { return provenance_; }
   const char *cookedSourceLocation() const { return cookedSourceLocation_; }
   MessageContext context() const { return context_; }
+  bool isFatal() const { return isFatal_; }
 
   Provenance Emit(
       std::ostream &, const CookedSource &, bool echoSourceLine = true) const;
@@ -114,9 +130,10 @@ private:
   Provenance provenance_;
   const char *cookedSourceLocation_{nullptr};
   MessageFixedText text_;
-  bool isExpectedText_{false};  // implies "expected '%s'"_en_US
+  bool isExpectedText_{false};  // implies "expected '%s'"_err_en_US
   std::string string_;
   MessageContext context_;
+  bool isFatal_{false};
 };
 
 class Messages {
@@ -183,6 +200,8 @@ public:
   void Emit(std::ostream &, const char *prefix = nullptr,
       bool echoSourceLines = true) const;
 
+  bool AnyFatalError() const;
+
 private:
   const CookedSource &cooked_;
   list_type messages_;
index c5e6625..2ff16bd 100644 (file)
@@ -118,21 +118,17 @@ public:
     }
   }
 
-  Message &PutMessage(MessageFixedText t) { return PutMessage(p_, t); }
-  Message &PutMessage(MessageFormattedText &&t) {
-    return PutMessage(p_, std::move(t));
-  }
-  Message &PutMessage(MessageExpectedText &&t) {
-    return PutMessage(p_, std::move(t));
-  }
+  Message &Say(MessageFixedText t) { return Say(p_, t); }
+  Message &Say(MessageFormattedText &&t) { return Say(p_, std::move(t)); }
+  Message &Say(MessageExpectedText &&t) { return Say(p_, std::move(t)); }
 
-  Message &PutMessage(const char *at, MessageFixedText t) {
+  Message &Say(const char *at, MessageFixedText t) {
     return messages_.Put(Message{at, t, context_});
   }
-  Message &PutMessage(const char *at, MessageFormattedText &&t) {
+  Message &Say(const char *at, MessageFormattedText &&t) {
     return messages_.Put(Message{at, std::move(t), context_});
   }
-  Message &PutMessage(const char *at, MessageExpectedText &&t) {
+  Message &Say(const char *at, MessageExpectedText &&t) {
     return messages_.Put(Message{at, std::move(t), context_});
   }
 
index 8e8a859..71ff71f 100644 (file)
 namespace Fortran {
 namespace parser {
 
-bool Parsing::Prescan(const std::string &path, Options options) {
+void Parsing::Prescan(const std::string &path, Options options) {
   options_ = options;
 
   std::stringstream fileError;
   const auto *sourceFile = allSources_.Open(path, &fileError);
   if (sourceFile == nullptr) {
     ProvenanceRange range{allSources_.AddCompilerInsertion(path)};
-    MessageFormattedText msg("%s"_en_US, fileError.str().data());
+    MessageFormattedText msg("%s"_err_en_US, fileError.str().data());
     messages_.Put(Message(range.start(), std::move(msg)));
-    anyFatalError_ = true;
-    return false;
+    return;
   }
   if (sourceFile->bytes() == 0) {
     ProvenanceRange range{allSources_.AddCompilerInsertion(path)};
-    messages_.Put(Message{range.start(), "file is empty"_en_US});
-    anyFatalError_ = true;
-    return false;
+    messages_.Put(Message{range.start(), "file is empty"_err_en_US});
+    return;
   }
 
   for (const auto &path : options.searchDirectories) {
@@ -51,19 +49,11 @@ bool Parsing::Prescan(const std::string &path, Options options) {
       .AddCompilerDirectiveSentinel("dir$");
   ProvenanceRange range{
       allSources_.AddIncludedFile(*sourceFile, ProvenanceRange{})};
-  anyFatalError_ = !prescanner.Prescan(range);
-  if (anyFatalError_) {
-    return false;
-  }
-
+  prescanner.Prescan(range);
   cooked_.Marshal();
-  return true;
 }
 
 void Parsing::DumpCookedChars(std::ostream &out) const {
-  if (anyFatalError_) {
-    return;
-  }
   UserState userState;
   ParseState parseState{cooked_};
   parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
@@ -74,10 +64,7 @@ void Parsing::DumpCookedChars(std::ostream &out) const {
 
 void Parsing::DumpProvenance(std::ostream &out) const { cooked_.Dump(out); }
 
-bool Parsing::Parse() {
-  if (anyFatalError_) {
-    return false;
-  }
+void Parsing::Parse() {
   UserState userState;
   ParseState parseState{cooked_};
   parseState.set_inFixedForm(options_.isFixedForm)
@@ -86,11 +73,34 @@ bool Parsing::Parse() {
       .set_warnOnDeprecatedUsage(options_.isStrictlyStandard)
       .set_userState(&userState);
   parseTree_ = program.Parse(&parseState);
-  anyFatalError_ = parseState.anyErrorRecovery();
+  CHECK(
+      !parseState.anyErrorRecovery() || parseState.messages()->AnyFatalError());
   consumedWholeFile_ = parseState.IsAtEnd();
   finalRestingPlace_ = parseState.GetLocation();
   messages_.Annex(parseState.messages());
-  return parseTree_.has_value() && !anyFatalError_;
+}
+
+std::optional<Program> Parsing::ForTesting(
+    std::string path, std::ostream &err) {
+  Parsing parsing;
+  parsing.Prescan(path, Options{});
+  if (parsing.messages().AnyFatalError()) {
+    parsing.messages().Emit(err);
+    err << "could not scan " << path << '\n';
+    return {};
+  }
+  parsing.Parse();
+  parsing.messages().Emit(err);
+  if (!parsing.consumedWholeFile()) {
+    err << "f18 parser FAIL; final position: ";
+    parsing.Identify(err, parsing.finalRestingPlace(), "   ");
+    return {};
+  }
+  if (parsing.messages().AnyFatalError()) {
+    err << "could not parse " << path << '\n';
+    return {};
+  }
+  return std::move(parsing.parseTree());
 }
 }  // namespace parser
 }  // namespace Fortran
index a3fbf6a..655d461 100644 (file)
@@ -36,12 +36,12 @@ public:
   bool consumedWholeFile() const { return consumedWholeFile_; }
   const char *finalRestingPlace() const { return finalRestingPlace_; }
   Messages &messages() { return messages_; }
-  Program &parseTree() { return *parseTree_; }
+  std::optional<Program> &parseTree() { return parseTree_; }
 
-  bool Prescan(const std::string &path, Options);
+  void Prescan(const std::string &path, Options);
   void DumpCookedChars(std::ostream &) const;
   void DumpProvenance(std::ostream &) const;
-  bool Parse();
+  void Parse();
 
   void Identify(std::ostream &o, const char *at, const std::string &prefix,
       bool echoSourceLine = false) const {
@@ -49,12 +49,13 @@ public:
         o, cooked_.GetProvenance(at).start(), prefix, echoSourceLine);
   }
 
+  static std::optional<Program> ForTesting(std::string path, std::ostream &);
+
 private:
   Options options_;
   AllSources allSources_;
   CookedSource cooked_{allSources_};
   Messages messages_{cooked_};
-  bool anyFatalError_{false};
   bool consumedWholeFile_{false};
   const char *finalRestingPlace_{nullptr};
   std::optional<Program> parseTree_;
index 30634be..913e0da 100644 (file)
@@ -369,7 +369,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
     return;
   }
   if (dir.TokenAt(j).ToString() != "#") {
-    prescanner->Error("missing '#'"_en_US, dir.GetTokenProvenance(j));
+    prescanner->Say("missing '#'"_err_en_US, dir.GetTokenProvenance(j));
     return;
   }
   j = SkipBlanks(dir, j + 1, tokens);
@@ -390,7 +390,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
     // TODO: implement #line
   } else if (dirName == "define") {
     if (nameToken.empty()) {
-      prescanner->Error("#define: missing or invalid name"_en_US,
+      prescanner->Say("#define: missing or invalid name"_err_en_US,
           dir.GetTokenProvenance(j < tokens ? j : tokens - 1));
       return;
     }
@@ -408,8 +408,8 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
             isVariadic = true;
           } else {
             if (an.empty() || !IsLegalIdentifierStart(an[0])) {
-              prescanner->Error(
-                  "#define: missing or invalid argument name"_en_US,
+              prescanner->Say(
+                  "#define: missing or invalid argument name"_err_en_US,
                   dir.GetTokenProvenance(j));
               return;
             }
@@ -417,7 +417,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
           }
           j = SkipBlanks(dir, j + 1, tokens);
           if (j == tokens) {
-            prescanner->Error("#define: malformed argument list"_en_US,
+            prescanner->Say("#define: malformed argument list"_err_en_US,
                 dir.GetTokenProvenance(tokens - 1));
             return;
           }
@@ -426,20 +426,20 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
             break;
           }
           if (isVariadic || punc != ",") {
-            prescanner->Error("#define: malformed argument list"_en_US,
+            prescanner->Say("#define: malformed argument list"_err_en_US,
                 dir.GetTokenProvenance(j));
             return;
           }
           j = SkipBlanks(dir, j + 1, tokens);
           if (j == tokens) {
-            prescanner->Error("#define: malformed argument list"_en_US,
+            prescanner->Say("#define: malformed argument list"_err_en_US,
                 dir.GetTokenProvenance(tokens - 1));
             return;
           }
         }
         if (std::set<std::string>(argName.begin(), argName.end()).size() !=
             argName.size()) {
-          prescanner->Error("#define: argument names are not distinct"_en_US,
+          prescanner->Say("#define: argument names are not distinct"_err_en_US,
               dir.GetTokenProvenance(dirOffset));
           return;
         }
@@ -454,12 +454,12 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
     }
   } else if (dirName == "undef") {
     if (nameToken.empty()) {
-      prescanner->Error("# missing or invalid name"_en_US,
+      prescanner->Say("# missing or invalid name"_err_en_US,
           dir.GetTokenProvenance(tokens - 1));
     } else {
       j = SkipBlanks(dir, j + 1, tokens);
       if (j != tokens) {
-        prescanner->Error("#undef: excess tokens at end of directive"_en_US,
+        prescanner->Say("#undef: excess tokens at end of directive"_err_en_US,
             dir.GetTokenProvenance(j));
       } else {
         definitions_.erase(nameToken);
@@ -467,16 +467,16 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
     }
   } else if (dirName == "ifdef" || dirName == "ifndef") {
     if (nameToken.empty()) {
-      prescanner->Error(
-          MessageFormattedText("#%s: missing name"_en_US, dirName.data()),
+      prescanner->Say(
+          MessageFormattedText("#%s: missing name"_err_en_US, dirName.data()),
           dir.GetTokenProvenance(tokens - 1));
       return;
     }
     j = SkipBlanks(dir, j + 1, tokens);
     if (j != tokens) {
-      prescanner->Error(
-          MessageFormattedText(
-              "#%s: excess tokens at end of directive"_en_US, dirName.data()),
+      prescanner->Say(MessageFormattedText(
+                          "#%s: excess tokens at end of directive"_err_en_US,
+                          dirName.data()),
           dir.GetTokenProvenance(j));
     } else if (IsNameDefined(nameToken) == (dirName == "ifdef")) {
       ifStack_.push(CanDeadElseAppear::Yes);
@@ -493,15 +493,15 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
     }
   } else if (dirName == "else") {
     if (j != tokens) {
-      prescanner->Error("#else: excess tokens at end of directive"_en_US,
+      prescanner->Say("#else: excess tokens at end of directive"_err_en_US,
           dir.GetTokenProvenance(j));
     } else if (ifStack_.empty()) {
-      prescanner->Error(
-          "#else: not nested within #if, #ifdef, or #ifndef"_en_US,
+      prescanner->Say(
+          "#else: not nested within #if, #ifdef, or #ifndef"_err_en_US,
           dir.GetTokenProvenance(tokens - 1));
     } else if (ifStack_.top() != CanDeadElseAppear::Yes) {
-      prescanner->Error(
-          "#else: already appeared within this #if, #ifdef, or #ifndef"_en_US,
+      prescanner->Say(
+          "#else: already appeared within this #if, #ifdef, or #ifndef"_err_en_US,
           dir.GetTokenProvenance(tokens - 1));
     } else {
       ifStack_.pop();
@@ -510,12 +510,12 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
     }
   } else if (dirName == "elif") {
     if (ifStack_.empty()) {
-      prescanner->Error(
-          "#elif: not nested within #if, #ifdef, or #ifndef"_en_US,
+      prescanner->Say(
+          "#elif: not nested within #if, #ifdef, or #ifndef"_err_en_US,
           dir.GetTokenProvenance(tokens - 1));
     } else if (ifStack_.top() != CanDeadElseAppear::Yes) {
-      prescanner->Error("#elif: #else previously appeared within this "
-                        "#if, #ifdef, or #ifndef"_en_US,
+      prescanner->Say("#elif: #else previously appeared within this "
+                      "#if, #ifdef, or #ifndef"_err_en_US,
           dir.GetTokenProvenance(tokens - 1));
     } else {
       ifStack_.pop();
@@ -524,32 +524,32 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
     }
   } else if (dirName == "endif") {
     if (j != tokens) {
-      prescanner->Error("#endif: excess tokens at end of directive"_en_US,
+      prescanner->Say("#endif: excess tokens at end of directive"_err_en_US,
           dir.GetTokenProvenance(j));
     } else if (ifStack_.empty()) {
-      prescanner->Error("#endif: no #if, #ifdef, or #ifndef"_en_US,
+      prescanner->Say("#endif: no #if, #ifdef, or #ifndef"_err_en_US,
           dir.GetTokenProvenance(tokens - 1));
     } else {
       ifStack_.pop();
     }
   } else if (dirName == "error") {
-    prescanner->Error(
-        MessageFormattedText("#error: %s"_en_US, dir.ToString().data()),
+    prescanner->Say(
+        MessageFormattedText("#error: %s"_err_en_US, dir.ToString().data()),
         dir.GetTokenProvenance(dirOffset));
   } else if (dirName == "warning") {
-    prescanner->Complain(
+    prescanner->Say(
         MessageFormattedText("#warning: %s"_en_US, dir.ToString().data()),
         dir.GetTokenProvenance(dirOffset));
   } else if (dirName == "include") {
     if (j == tokens) {
-      prescanner->Error("#include: missing name of file to include"_en_US,
+      prescanner->Say("#include: missing name of file to include"_err_en_US,
           dir.GetTokenProvenance(tokens - 1));
       return;
     }
     std::string include;
     if (dir.TokenAt(j).ToString() == "<") {
       if (dir.TokenAt(tokens - 1).ToString() != ">") {
-        prescanner->Error("#include: expected '>' at end of directive"_en_US,
+        prescanner->Say("#include: expected '>' at end of directive"_err_en_US,
             dir.GetTokenProvenance(tokens - 1));
         return;
       }
@@ -560,20 +560,20 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
         include.substr(include.size() - 1, 1) == "\"") {
       include = include.substr(1, include.size() - 2);
     } else {
-      prescanner->Error("#include: expected name of file to include"_en_US,
+      prescanner->Say("#include: expected name of file to include"_err_en_US,
           dir.GetTokenProvenance(j < tokens ? j : tokens - 1));
       return;
     }
     if (include.empty()) {
-      prescanner->Error("#include: empty include file name"_en_US,
+      prescanner->Say("#include: empty include file name"_err_en_US,
           dir.GetTokenProvenance(dirOffset));
       return;
     }
     std::stringstream error;
     const SourceFile *included{allSources_.Open(include, &error)};
     if (included == nullptr) {
-      prescanner->Error(
-          MessageFormattedText("#include: %s"_en_US, error.str().data()),
+      prescanner->Say(
+          MessageFormattedText("#include: %s"_err_en_US, error.str().data()),
           dir.GetTokenProvenance(dirOffset));
       return;
     }
@@ -582,13 +582,11 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
     }
     ProvenanceRange fileRange{
         allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())};
-    if (!Prescanner{*prescanner}.Prescan(fileRange)) {
-      prescanner->set_anyFatalErrors();
-    }
+    Prescanner{*prescanner}.Prescan(fileRange);
   } else {
-    prescanner->Error(
-        MessageFormattedText(
-            "#%s: unknown or unimplemented directive"_en_US, dirName.data()),
+    prescanner->Say(MessageFormattedText(
+                        "#%s: unknown or unimplemented directive"_err_en_US,
+                        dirName.data()),
         dir.GetTokenProvenance(dirOffset));
   }
 }
@@ -649,8 +647,8 @@ void Preprocessor::SkipDisabledConditionalCode(const std::string &dirName,
       }
     }
   }
-  prescanner->Error(
-      MessageFormattedText("#%s: missing #endif"_en_US, dirName.data()),
+  prescanner->Say(
+      MessageFormattedText("#%s: missing #endif"_err_en_US, dirName.data()),
       provenance);
 }
 
@@ -751,8 +749,8 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
 
   std::size_t tokens{token.SizeInTokens()};
   if (*atToken >= tokens) {
-    *error = Message{
-        token.GetTokenProvenance(tokens - 1), "incomplete expression"_en_US};
+    *error = Message{token.GetTokenProvenance(tokens - 1),
+        "incomplete expression"_err_en_US};
     return 0;
   }
 
@@ -770,7 +768,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
     if (consumed < t.size()) {
       *error = Message{token.GetTokenProvenance(opAt),
           MessageFormattedText(
-              "uninterpretable numeric constant '%s'"_en_US, t.data())};
+              "uninterpretable numeric constant '%s'"_err_en_US, t.data())};
       return 0;
     }
   } else if (IsLegalIdentifierStart(t[0])) {
@@ -792,13 +790,13 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
       op = it->second;
     } else {
       *error = Message{token.GetTokenProvenance(tokens - 1),
-          "operand expected in expression"_en_US};
+          "operand expected in expression"_err_en_US};
       return 0;
     }
   }
   if (precedence[op] < minimumPrecedence) {
     *error = Message{
-        token.GetTokenProvenance(opAt), "operator precedence error"_en_US};
+        token.GetTokenProvenance(opAt), "operator precedence error"_err_en_US};
     return 0;
   }
   ++*atToken;
@@ -813,7 +811,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
         ++*atToken;
       } else {
         *error = Message{token.GetTokenProvenance(tokens - 1),
-            "')' missing from expression"_en_US};
+            "')' missing from expression"_err_en_US};
         return 0;
       }
       break;
@@ -855,8 +853,8 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
   switch (op) {
   case POWER:
     if (left == 0 && right < 0) {
-      *error =
-          Message{token.GetTokenProvenance(opAt), "0 ** negative power"_en_US};
+      *error = Message{
+          token.GetTokenProvenance(opAt), "0 ** negative power"_err_en_US};
       return 0;
     }
     if (left == 0 || left == 1 || right == 1) {
@@ -870,7 +868,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
       for (; right > 0; --right) {
         if ((power * left) / left != power) {
           *error = Message{token.GetTokenProvenance(opAt),
-              "overflow in exponentation"_en_US};
+              "overflow in exponentation"_err_en_US};
           return 0;
         }
         power *= left;
@@ -882,45 +880,46 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
       return 0;
     }
     if ((left * right) / left != right) {
-      *error = Message{
-          token.GetTokenProvenance(opAt), "overflow in multiplication"_en_US};
+      *error = Message{token.GetTokenProvenance(opAt),
+          "overflow in multiplication"_err_en_US};
     }
     return left * right;
   case DIVIDE:
     if (right == 0) {
       *error =
-          Message{token.GetTokenProvenance(opAt), "division by zero"_en_US};
+          Message{token.GetTokenProvenance(opAt), "division by zero"_err_en_US};
       return 0;
     }
     return left / right;
   case MODULUS:
     if (right == 0) {
-      *error = Message{token.GetTokenProvenance(opAt), "modulus by zero"_en_US};
+      *error =
+          Message{token.GetTokenProvenance(opAt), "modulus by zero"_err_en_US};
       return 0;
     }
     return left % right;
   case ADD:
     if ((left < 0) == (right < 0) && (left < 0) != (left + right < 0)) {
-      *error =
-          Message{token.GetTokenProvenance(opAt), "overflow in addition"_en_US};
+      *error = Message{
+          token.GetTokenProvenance(opAt), "overflow in addition"_err_en_US};
     }
     return left + right;
   case SUBTRACT:
     if ((left < 0) != (right < 0) && (left < 0) == (left - right < 0)) {
       *error = Message{
-          token.GetTokenProvenance(opAt), "overflow in subtraction"_en_US};
+          token.GetTokenProvenance(opAt), "overflow in subtraction"_err_en_US};
     }
     return left - right;
   case LEFTSHIFT:
     if (right < 0 || right > 64) {
-      *error =
-          Message{token.GetTokenProvenance(opAt), "bad left shift count"_en_US};
+      *error = Message{
+          token.GetTokenProvenance(opAt), "bad left shift count"_err_en_US};
     }
     return right >= 64 ? 0 : left << right;
   case RIGHTSHIFT:
     if (right < 0 || right > 64) {
       *error = Message{
-          token.GetTokenProvenance(opAt), "bad right shift count"_en_US};
+          token.GetTokenProvenance(opAt), "bad right shift count"_err_en_US};
     }
     return right >= 64 ? 0 : left >> right;
   case BITAND:
@@ -939,7 +938,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
   case SELECT:
     if (*atToken >= tokens || token.TokenAt(*atToken).ToString() != ":") {
       *error = Message{token.GetTokenProvenance(opAt),
-          "':' required in selection expression"_en_US};
+          "':' required in selection expression"_err_en_US};
       return left;
     } else {
       ++*atToken;
@@ -983,10 +982,11 @@ bool Preprocessor::IsIfPredicateTrue(const TokenSequence &expr,
   std::optional<Message> error;
   bool result{ExpressionValue(expr4, 0, &atToken, &error) != 0};
   if (error.has_value()) {
-    prescanner->Error(std::move(*error));
+    prescanner->Say(std::move(*error));
   } else if (atToken < expr4.SizeInTokens()) {
-    prescanner->Error(atToken == 0 ? "could not parse any expression"_en_US
-                                   : "excess characters after expression"_en_US,
+    prescanner->Say(atToken == 0
+            ? "could not parse any expression"_err_en_US
+            : "excess characters after expression"_err_en_US,
         expr4.GetTokenProvenance(atToken));
   }
   return result;
index 1fcd0c2..429de34 100644 (file)
@@ -42,7 +42,7 @@ static void NormalizeCompilerDirectiveCommentMarker(TokenSequence *dir) {
   CHECK(!"compiler directive all blank");
 }
 
-bool Prescanner::Prescan(ProvenanceRange range) {
+void Prescanner::Prescan(ProvenanceRange range) {
   AllSources &allSources{cooked_.allSources()};
   ProvenanceRange around{allSources.GetContiguousRangeAround(range)};
   startProvenance_ = range.start();
@@ -67,7 +67,6 @@ bool Prescanner::Prescan(ProvenanceRange range) {
     TokenSequence tokens{dir, allSources.AddCompilerInsertion(dir).start()};
     tokens.Emit(&cooked_);
   }
-  return !anyFatalErrors_;
 }
 
 void Prescanner::Statement() {
@@ -131,7 +130,7 @@ void Prescanner::Statement() {
       FortranInclude(ppd + ppl.payloadOffset);
       break;
     case LineClassification::Kind::PreprocessorDirective:
-      Complain("preprocessed line looks like a preprocessor directive"_en_US,
+      Say("preprocessed line looks like a preprocessor directive"_en_US,
           preprocessed->GetProvenanceRange().start());
       preprocessed->ToLowerCase().Emit(&cooked_);
       break;
@@ -169,31 +168,14 @@ TokenSequence Prescanner::TokenizePreprocessorDirective() {
   return {std::move(tokens)};
 }
 
-Message &Prescanner::Error(Message &&message) {
-  anyFatalErrors_ = true;
-  return messages_.Put(std::move(message));
-}
-
-Message &Prescanner::Error(MessageFixedText text, Provenance p) {
-  anyFatalErrors_ = true;
-  return messages_.Put({p, text});
-}
-
-Message &Prescanner::Error(MessageFormattedText &&text, Provenance p) {
-  anyFatalErrors_ = true;
-  return messages_.Put({p, std::move(text)});
-}
-
-Message &Prescanner::Complain(Message &&message) {
-  return messages_.Put(std::move(message));
-}
+void Prescanner::Say(Message &&message) { messages_.Put(std::move(message)); }
 
-Message &Prescanner::Complain(MessageFixedText text, Provenance p) {
-  return messages_.Put({p, text});
+void Prescanner::Say(MessageFixedText text, Provenance p) {
+  messages_.Put({p, text});
 }
 
-Message &Prescanner::Complain(MessageFormattedText &&text, Provenance p) {
-  return messages_.Put({p, std::move(text)});
+void Prescanner::Say(MessageFormattedText &&text, Provenance p) {
+  messages_.Put({p, std::move(text)});
 }
 
 void Prescanner::NextLine() {
@@ -424,7 +406,7 @@ void Prescanner::QuotedCharacterLiteral(TokenSequence *tokens) {
     }
     if (*at_ == '\n') {
       if (!inPreprocessorDirective_) {
-        Error("incomplete character literal"_en_US, GetProvenance(start));
+        Say("incomplete character literal"_err_en_US, GetProvenance(start));
       }
       break;
     }
@@ -474,7 +456,7 @@ void Prescanner::Hollerith(TokenSequence *tokens, int count) {
   }
   if (*at_ == '\n') {
     if (!inPreprocessorDirective_) {
-      Error("incomplete Hollerith literal"_en_US, GetProvenance(start));
+      Say("incomplete Hollerith literal"_err_en_US, GetProvenance(start));
     }
   } else {
     NextChar();
@@ -575,13 +557,13 @@ bool Prescanner::FortranInclude(const char *firstQuote) {
     path += *p;
   }
   if (*p != quote) {
-    Error("malformed path name string"_en_US, GetProvenance(p));
+    Say("malformed path name string"_err_en_US, GetProvenance(p));
     return true;
   }
   for (++p; *p == ' ' || *p == '\t'; ++p) {
   }
   if (*p != '\n' && *p != '!') {
-    Complain("excess characters after path name"_en_US, GetProvenance(p));
+    Say("excess characters after path name"_en_US, GetProvenance(p));
   }
   std::stringstream error;
   Provenance provenance{GetProvenance(lineStart_)};
@@ -595,7 +577,7 @@ bool Prescanner::FortranInclude(const char *firstQuote) {
     allSources.PopSearchPathDirectory();
   }
   if (included == nullptr) {
-    Error(MessageFormattedText("INCLUDE: %s"_en_US, error.str().data()),
+    Say(MessageFormattedText("INCLUDE: %s"_err_en_US, error.str().data()),
         provenance);
     return true;
   }
@@ -606,7 +588,7 @@ bool Prescanner::FortranInclude(const char *firstQuote) {
       provenance, static_cast<std::size_t>(p - lineStart_)};
   ProvenanceRange fileRange{
       allSources.AddIncludedFile(*included, includeLineRange)};
-  anyFatalErrors_ |= !Prescanner{*this}.Prescan(fileRange);
+  Prescanner{*this}.Prescan(fileRange);
   return true;
 }
 
@@ -672,7 +654,7 @@ const char *Prescanner::FixedFormContinuationLine() {
   if (col1 == '&') {
     // Extension: '&' as continuation marker
     if (warnOnNonstandardUsage_) {
-      Complain("nonstandard usage"_en_US, GetProvenance(lineStart_));
+      Say("nonstandard usage"_en_US, GetProvenance(lineStart_));
     }
     return lineStart_ + 1;
   }
index 9630e85..9dc14dd 100644 (file)
@@ -28,8 +28,6 @@ public:
   Prescanner(Messages &, CookedSource &, Preprocessor &);
   Prescanner(const Prescanner &);
 
-  bool anyFatalErrors() const { return anyFatalErrors_; }
-  void set_anyFatalErrors() { anyFatalErrors_ = true; }
   Messages &messages() const { return messages_; }
 
   Prescanner &set_fixedForm(bool yes) {
@@ -59,7 +57,7 @@ public:
 
   Prescanner &AddCompilerDirectiveSentinel(const std::string &);
 
-  bool Prescan(ProvenanceRange);
+  void Prescan(ProvenanceRange);
   void Statement();
   void NextLine();
 
@@ -69,12 +67,9 @@ public:
   TokenSequence TokenizePreprocessorDirective();
   Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
 
-  Message &Error(Message &&);
-  Message &Error(MessageFixedText, Provenance);
-  Message &Error(MessageFormattedText &&, Provenance);
-  Message &Complain(Message &&);
-  Message &Complain(MessageFixedText, Provenance);
-  Message &Complain(MessageFormattedText &&, Provenance);
+  void Say(Message &&);
+  void Say(MessageFixedText, Provenance);
+  void Say(MessageFormattedText &&, Provenance);
 
 private:
   struct LineClassification {
index 2995464..9c38cfd 100644 (file)
@@ -33,7 +33,7 @@ public:
         return {at};
       }
     }
-    state->PutMessage(at, messageText_);
+    state->Say(at, messageText_);
     return {};
   }
 
@@ -42,9 +42,10 @@ private:
   const MessageFixedText messageText_;
 };
 
-constexpr auto letter = CharPredicateGuard{IsLetter, "expected letter"_en_US};
+constexpr auto letter =
+    CharPredicateGuard{IsLetter, "expected letter"_err_en_US};
 constexpr auto digit =
-    CharPredicateGuard{IsDecimalDigit, "expected digit"_en_US};
+    CharPredicateGuard{IsDecimalDigit, "expected digit"_err_en_US};
 
 // "xyz"_ch matches one instance of the characters x, y, or z without skipping
 // any spaces before or after.  The parser returns the location of the character
@@ -66,7 +67,7 @@ public:
         }
       }
     }
-    state->PutMessage(at, MessageExpectedText{chars_, bytes_});
+    state->Say(at, MessageExpectedText{chars_, bytes_});
     return {};
   }
 
@@ -100,7 +101,7 @@ static inline void MissingSpace(ParseState *state) {
   if (!state->inFixedForm()) {
     state->set_anyConformanceViolation();
     if (state->warnOnNonstandardUsage()) {
-      state->PutMessage("expected space"_en_US);
+      state->Say("expected space"_err_en_US);
     }
   }
 }
@@ -171,7 +172,7 @@ public:
       } else if (**at == ToLowerCaseLetter(*p)) {
         at.reset();
       } else {
-        state->PutMessage(start, MessageExpectedText{str_, bytes_});
+        state->Say(start, MessageExpectedText{str_, bytes_});
         return {};
       }
     }
@@ -241,7 +242,7 @@ struct CharLiteralChar {
     }
     char ch{**och};
     if (ch == '\n') {
-      state->PutMessage(at, "unclosed character constant"_en_US);
+      state->Say(at, "unclosed character constant"_err_en_US);
       return {};
     }
     if (ch != '\\') {
@@ -252,7 +253,7 @@ struct CharLiteralChar {
     }
     ch = **och;
     if (ch == '\n') {
-      state->PutMessage(at, "unclosed character constant"_en_US);
+      state->Say(at, "unclosed character constant"_err_en_US);
       return {};
     }
     if (std::optional<char> escChar{BackslashEscapeValue(ch)}) {
@@ -262,7 +263,7 @@ struct CharLiteralChar {
       ch -= '0';
       for (int j = (ch > 3 ? 1 : 2); j-- > 0;) {
         static constexpr auto octalDigit =
-            CharPredicateGuard{IsOctalDigit, "expected octal digit"_en_US};
+            CharPredicateGuard{IsOctalDigit, "expected octal digit"_err_en_US};
         och = octalDigit.Parse(state);
         if (och.has_value()) {
           ch = 8 * ch + **och - '0';
@@ -274,7 +275,7 @@ struct CharLiteralChar {
       ch = 0;
       for (int j = 0; j++ < 2;) {
         static constexpr auto hexDigit = CharPredicateGuard{
-            IsHexadecimalDigit, "expected hexadecimal digit"_en_US};
+            IsHexadecimalDigit, "expected hexadecimal digit"_err_en_US};
         och = hexDigit.Parse(state);
         if (och.has_value()) {
           ch = 16 * ch + HexadecimalDigitValue(**och);
@@ -283,7 +284,7 @@ struct CharLiteralChar {
         }
       }
     } else {
-      state->PutMessage(at, "bad escaped character"_en_US);
+      state->Say(at, "bad escaped character"_err_en_US);
     }
     return {Result::Escaped(ch)};
   }
@@ -314,7 +315,7 @@ static bool IsNonstandardUsageOk(ParseState *state) {
   }
   state->set_anyConformanceViolation();
   if (state->warnOnNonstandardUsage()) {
-    state->PutMessage("nonstandard usage"_en_US);
+    state->Say("nonstandard usage"_en_US);
   }
   return true;
 }
@@ -385,7 +386,7 @@ struct BOZLiteral {
     }
 
     if (content.empty()) {
-      state->PutMessage(start, "no digit in BOZ literal"_en_US);
+      state->Say(start, "no digit in BOZ literal"_err_en_US);
       return {};
     }
 
@@ -393,13 +394,13 @@ struct BOZLiteral {
     for (auto digit : content) {
       digit = HexadecimalDigitValue(digit);
       if ((digit >> *shift) > 0) {
-        state->PutMessage(start, "bad digit in BOZ literal"_en_US);
+        state->Say(start, "bad digit in BOZ literal"_err_en_US);
         return {};
       }
       std::uint64_t was{value};
       value <<= *shift;
       if ((value >> *shift) != was) {
-        state->PutMessage(start, "excessive digits in BOZ literal"_en_US);
+        state->Say(start, "excessive digits in BOZ literal"_err_en_US);
         return {};
       }
       value |= digit;
@@ -431,7 +432,7 @@ struct DigitString {
       value += digitValue;
     }
     if (overflow) {
-      state->PutMessage(*firstDigit, "overflow in decimal literal"_en_US);
+      state->Say(*firstDigit, "overflow in decimal literal"_err_en_US);
     }
     return {value};
   }
@@ -458,14 +459,14 @@ struct HollerithLiteral {
       if (state->encoding() == Encoding::EUC_JP) {
         std::optional<int> chBytes{EUC_JPCharacterBytes(p)};
         if (!chBytes.has_value()) {
-          state->PutMessage(start, "bad EUC_JP characters in Hollerith"_en_US);
+          state->Say(start, "bad EUC_JP characters in Hollerith"_err_en_US);
           return {};
         }
         bytes = *chBytes;
       } else if (state->encoding() == Encoding::UTF8) {
         std::optional<int> chBytes{UTF8CharacterBytes(p)};
         if (!chBytes.has_value()) {
-          state->PutMessage(start, "bad UTF-8 characters in Hollerith"_en_US);
+          state->Say(start, "bad UTF-8 characters in Hollerith"_err_en_US);
           return {};
         }
         bytes = *chBytes;
@@ -473,8 +474,8 @@ struct HollerithLiteral {
       if (bytes == 1) {
         std::optional<const char *> at{nextCh.Parse(state)};
         if (!at.has_value() || !isprint(**at)) {
-          state->PutMessage(
-              start, "insufficient or bad characters in Hollerith"_en_US);
+          state->Say(
+              start, "insufficient or bad characters in Hollerith"_err_en_US);
           return {};
         }
         content += **at;
index cb8a04c..e9d01e7 100644 (file)
@@ -2124,9 +2124,7 @@ private:
     WalkTupleElements(tuple, separator);
   }
 
-  void EndSubprogram() {
-    structureComponents_.clear();
-  }
+  void EndSubprogram() { structureComponents_.clear(); }
 
   std::ostream &out_;
   int indent_{0};
index 4b73d18..1a7bc32 100644 (file)
@@ -141,7 +141,10 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options,
     }
   }
   Fortran::parser::Parsing parsing;
-  if (!parsing.Prescan(path, options)) {
+  parsing.Prescan(path, options);
+  if (!parsing.messages().empty() &&
+      (driver.warningsAreErrors || parsing.messages().AnyFatalError())) {
+    std::cerr << driver.prefix << "could not scan " << path << '\n';
     parsing.messages().Emit(std::cerr, driver.prefix);
     exit(EXIT_FAILURE);
   }
@@ -153,29 +156,27 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options,
     parsing.DumpCookedChars(std::cout);
     return {};
   }
-  if (!parsing.Parse()) {
-    if (!parsing.consumedWholeFile()) {
-      std::cerr << "f18 FAIL; final position: ";
-      parsing.Identify(std::cerr, parsing.finalRestingPlace(), "   ");
-    }
+  parsing.Parse();
+  parsing.messages().Emit(std::cerr, driver.prefix);
+  if (!parsing.consumedWholeFile()) {
+    std::cerr << "f18 parser FAIL; final position: ";
+    parsing.Identify(std::cerr, parsing.finalRestingPlace(), "   ");
+    exit(EXIT_FAILURE);
+  }
+  if (!parsing.messages().empty() &&
+      (driver.warningsAreErrors || parsing.messages().AnyFatalError()) ||
+      !parsing.parseTree().has_value()) {
     std::cerr << driver.prefix << "could not parse " << path << '\n';
-    parsing.messages().Emit(std::cerr, driver.prefix);
     exit(EXIT_FAILURE);
   }
   if (driver.measureTree) {
-    MeasureParseTree(parsing.parseTree());
+    MeasureParseTree(*parsing.parseTree());
   }
   if (driver.dumpUnparse) {
-    Unparse(std::cout, parsing.parseTree(), driver.encoding,
+    Unparse(std::cout, *parsing.parseTree(), driver.encoding,
             true /*capitalize*/);
     return {};
   }
-
-  parsing.messages().Emit(std::cerr, driver.prefix);
-  if (driver.warningsAreErrors &&
-      !parsing.messages().empty()) {
-    exit(EXIT_FAILURE);
-  }
   if (driver.parseOnly) {
     return {};
   }
@@ -187,7 +188,7 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options,
                 static_cast<unsigned long>(getpid()));
   { std::ofstream tmpSource;
     tmpSource.open(tmpSourcePath);
-    Unparse(tmpSource, parsing.parseTree(), driver.encoding);
+    Unparse(tmpSource, *parsing.parseTree(), driver.encoding);
   }
 
   if (ParentProcess()) {
index b156fbb..38c98bf 100644 (file)
@@ -22,12 +22,9 @@ int main(int argc, char *const argv[]) {
     return EXIT_FAILURE;
   }
   std::string path{argv[1]};
-  Parsing parsing;
-  if (!parsing.Prescan(path, Options{}) || !parsing.Parse()) {
-    std::cerr << "parse FAILED\n";
-    parsing.messages().Emit(std::cerr);
-    return EXIT_FAILURE;
+  if (std::optional<Program> parseTree{Parsing::ForTesting(path, std::cerr)}) {
+    DoSemanticAnalysis(parsing.messages().cooked(), *parseTree);
+    return EXIT_SUCCESS;
   }
-  DoSemanticAnalysis(parsing.messages().cooked(), parsing.parseTree());
-  return EXIT_SUCCESS;
+  return EXIT_FAILURE;
 }
index ec5a7ef..c584618 100644 (file)
@@ -15,12 +15,9 @@ int main(int argc, char *const argv[]) {
     return EXIT_FAILURE;
   }
   std::string path{argv[1]};
-  Parsing parsing;
-  if (!parsing.Prescan(path, Options{}) || !parsing.Parse()) {
-    std::cerr << "parse FAILED\n";
-    parsing.messages().Emit(std::cerr);
-    return EXIT_FAILURE;
+  if (std::optional<Program> parseTree{Parsing::ForTesting(path, std::cerr)}) {
+    semantics::MakeTypes(std::cout, *parseTree);
+    return EXIT_SUCCESS;
   }
-  semantics::MakeTypes(std::cout, parsing.parseTree());
-  return EXIT_SUCCESS;
+  return EXIT_FAILURE;
 }