Skip to content

[flang][driver] -Werror promotes warnings to error and interopts with -Wfatal-errors #148748

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

akuhlens
Copy link
Contributor

@akuhlens akuhlens commented Jul 14, 2025

This PR changes how -Werror promotes warnings to errors so that it interoperates with -Wfatal-error. In doing so it changes how warnings are presented when they are promoted. They now say "error" instead of "warning" and are highlighted in red. This new presentation is consistent with gfortran and clang.

Flang:

$ build/bin/flang flang/test/Driver/color-diagnostics-scan.f && build/bin/flang -Werror flang/test/Driver/color-diagnostics-scan.f
./flang/test/Driver/color-diagnostics-scan.f:29:3: warning: Character in fixed-form label field must be a digit [-Wscanning]
  1 continue
    ^
./flang/test/Driver/color-diagnostics-scan.f:30:1: warning: Character in fixed-form label field must be a digit [-Wscanning]
  end
  ^
error: Could not scan flang/test/Driver/color-diagnostics-scan.f
./flang/test/Driver/color-diagnostics-scan.f:29:3: error: Character in fixed-form label field must be a digit [-Wscanning]
  1 continue
    ^
./flang/test/Driver/color-diagnostics-scan.f:30:1: error: Character in fixed-form label field must be a digit [-Wscanning]
  end
  ^

clang

$ touch t.c && clang -c -pedantic t.c && clang -c -pedantic -Werror t.c
t.c:1:1: warning: ISO C requires a translation unit to contain at
      least one declaration [-Wempty-translation-unit]
1 warning generated.
t.c:1:1: error: ISO C requires a translation unit to contain at
      least one declaration [-Werror,-Wempty-translation-unit]
1 error generated.

gfortran

λ cat ~/tmp/alias.f90 && gfortran -c -Wall ~/tmp/alias.f90 && gfortran -c -Wall -Werro
r ~/tmp/alias.f90
program p
interface
  subroutine bar(a,b)
    integer, intent(in) :: a
    integer, intent(out) :: b
  end subroutine
end interface
integer :: a

call bar(a,a)
end program p
/home/akuhlenschmi/tmp/alias.f90:10:9:

   10 | call bar(a,a)
      |         1
Warning: Same actual argument associated with INTENT(IN) argument ‘a’ and INTENT(OUT) a
rgument ‘b’ at (1)
/home/akuhlenschmi/tmp/alias.f90:10:9:

   10 | call bar(a,a)
      |         1
Error: Same actual argument associated with INTENT(IN) argument ‘a’ and INTENT(OUT) arg
ument ‘b’ at (1) [-Werror]
f951: all warnings being treated as errors

Note, it should be a simple change to switch the text formatting back to the previous behavior if we prefer that, but my intuition was that it would behave this way and I was surprised when it didn't.

@akuhlens akuhlens requested review from klausler, tarunprabhu and eugeneepshteyn and removed request for klausler July 14, 2025 23:26
@akuhlens akuhlens marked this pull request as ready for review July 14, 2025 23:29
@llvmbot llvmbot added flang:driver flang Flang issues not falling into any other category flang:semantics flang:parser labels Jul 14, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 14, 2025

@llvm/pr-subscribers-flang-parser

Author: Andre Kuhlenschmidt (akuhlens)

Changes

This PR changes how -Werror promotes warnings to errors so that it interoperates with -Wfatal-error. In doing so it changes how warnings are presented when they are promoted to be in-line with gfortran and clang.

Flang:

$ build/bin/flang flang/test/Driver/color-diagnostics-scan.f && build/bin/flang -Werror flang/test/Driver/color-diagnostics-scan.f
./flang/test/Driver/color-diagnostics-scan.f:29:3: warning: Character in fixed-form label field must be a digit [-Wscanning]
  1 continue
    ^
./flang/test/Driver/color-diagnostics-scan.f:30:1: warning: Character in fixed-form label field must be a digit [-Wscanning]
  end
  ^
error: Could not scan flang/test/Driver/color-diagnostics-scan.f
./flang/test/Driver/color-diagnostics-scan.f:29:3: error: Character in fixed-form label field must be a digit [-Wscanning]
  1 continue
    ^
./flang/test/Driver/color-diagnostics-scan.f:30:1: error: Character in fixed-form label field must be a digit [-Wscanning]
  end
  ^

clang

$ touch t.c && clang -c -pedantic t.c && clang -c -pedantic -Werror t.c
t.c:1:1: warning: ISO C requires a translation unit to contain at
      least one declaration [-Wempty-translation-unit]
1 warning generated.
t.c:1:1: error: ISO C requires a translation unit to contain at
      least one declaration [-Werror,-Wempty-translation-unit]
1 error generated.

gfortran

λ cat ~/tmp/alias.f90 && gfortran -c -Wall ~/tmp/alias.f90 && gfortran -c -Wall -Werro
r ~/tmp/alias.f90
program p
interface
  subroutine bar(a,b)
    integer, intent(in) :: a
    integer, intent(out) :: b
  end subroutine
end interface
integer :: a

call bar(a,a)
end program p
/home/akuhlenschmi/tmp/alias.f90:10:9:

   10 | call bar(a,a)
      |         1
Warning: Same actual argument associated with INTENT(IN) argument ‘a’ and INTENT(OUT) a
rgument ‘b’ at (1)
/home/akuhlenschmi/tmp/alias.f90:10:9:

   10 | call bar(a,a)
      |         1
Error: Same actual argument associated with INTENT(IN) argument ‘a’ and INTENT(OUT) arg
ument ‘b’ at (1) [-Werror]
f951: all warnings being treated as errors

Note, it should be a simple change to switch the text formatting back to the previous behavior if we prefer that, but my intuition was that it would behave this way and I was surprised when it didn't.


Full diff: https://github.com/llvm/llvm-project/pull/148748.diff

7 Files Affected:

  • (modified) flang/include/flang/Parser/message.h (+25-12)
  • (modified) flang/lib/Frontend/FrontendAction.cpp (+4-2)
  • (modified) flang/lib/Parser/message.cpp (+23-14)
  • (modified) flang/lib/Semantics/resolve-names.cpp (+2-1)
  • (modified) flang/lib/Semantics/semantics.cpp (+2-3)
  • (modified) flang/test/Driver/color-diagnostics-scan.f (+8-8)
  • (added) flang/test/Driver/fatal-errors-warnings.f90 (+32)
diff --git a/flang/include/flang/Parser/message.h b/flang/include/flang/Parser/message.h
index db1a0a65157e3..d1d313a70a016 100644
--- a/flang/include/flang/Parser/message.h
+++ b/flang/include/flang/Parser/message.h
@@ -56,13 +56,19 @@ class MessageFixedText {
 
   CharBlock text() const { return text_; }
   bool empty() const { return text_.empty(); }
-  Severity severity() const { return severity_; }
+  Severity severity(bool warningsAreErrors = false) const {
+    if (warningsAreErrors) {
+      return Severity::Error;
+    }
+    return severity_;
+  }
   MessageFixedText &set_severity(Severity severity) {
     severity_ = severity;
     return *this;
   }
-  bool IsFatal() const {
-    return severity_ == Severity::Error || severity_ == Severity::Todo;
+  bool IsFatal(bool warningsAreErrors = false) const {
+    Severity sev{severity(warningsAreErrors)};
+    return sev == Severity::Error || sev == Severity::Todo;
   }
 
 private:
@@ -105,7 +111,7 @@ class MessageFormattedText {
 public:
   template <typename... A>
   MessageFormattedText(const MessageFixedText &text, A &&...x)
-      : severity_{text.severity()} {
+      : severity_{text.severity(false)} {
     Format(&text, Convert(std::forward<A>(x))...);
   }
   MessageFormattedText(const MessageFormattedText &) = default;
@@ -113,14 +119,20 @@ class MessageFormattedText {
   MessageFormattedText &operator=(const MessageFormattedText &) = default;
   MessageFormattedText &operator=(MessageFormattedText &&) = default;
   const std::string &string() const { return string_; }
-  bool IsFatal() const {
-    return severity_ == Severity::Error || severity_ == Severity::Todo;
+  Severity severity(bool warningsAreErrors = false) const {
+    if (warningsAreErrors) {
+      return Severity::Error;
+    }
+    return severity_;
   }
-  Severity severity() const { return severity_; }
   MessageFormattedText &set_severity(Severity severity) {
     severity_ = severity;
     return *this;
   }
+  bool IsFatal(bool warningsAreErrors = false) const {
+    Severity sev{severity(warningsAreErrors)};
+    return sev == Severity::Error || sev == Severity::Todo;
+  }
   std::string MoveString() { return std::move(string_); }
   bool operator==(const MessageFormattedText &that) const {
     return severity_ == that.severity_ && string_ == that.string_;
@@ -281,8 +293,8 @@ class Message : public common::ReferenceCounted<Message> {
   }
 
   bool SortBefore(const Message &that) const;
-  bool IsFatal() const;
-  Severity severity() const;
+  bool IsFatal(bool warningsAreErrors = false) const;
+  Severity severity(bool warningsAreErrors = false) const;
   Message &set_severity(Severity);
   std::optional<common::LanguageFeature> languageFeature() const;
   Message &set_languageFeature(common::LanguageFeature);
@@ -293,7 +305,8 @@ class Message : public common::ReferenceCounted<Message> {
       const AllCookedSources &) const;
   void Emit(llvm::raw_ostream &, const AllCookedSources &,
       bool echoSourceLine = true,
-      const common::LanguageFeatureControl *hintFlags = nullptr) const;
+      const common::LanguageFeatureControl *hintFlags = nullptr,
+      bool warningsAreErrors = false) const;
 
   // If this Message or any of its attachments locates itself via a CharBlock,
   // replace its location with the corresponding ProvenanceRange.
@@ -355,9 +368,9 @@ class Messages {
   void Emit(llvm::raw_ostream &, const AllCookedSources &,
       bool echoSourceLines = true,
       const common::LanguageFeatureControl *hintFlags = nullptr,
-      std::size_t maxErrorsToEmit = 0) const;
+      std::size_t maxErrorsToEmit = 0, bool warningsAreErrors = false) const;
   void AttachTo(Message &, std::optional<Severity> = std::nullopt);
-  bool AnyFatalError() const;
+  bool AnyFatalError(bool warningsAreErrors = false) const;
 
 private:
   std::list<Message> messages_;
diff --git a/flang/lib/Frontend/FrontendAction.cpp b/flang/lib/Frontend/FrontendAction.cpp
index 2429e07e5b8c4..d32f477445f93 100644
--- a/flang/lib/Frontend/FrontendAction.cpp
+++ b/flang/lib/Frontend/FrontendAction.cpp
@@ -238,7 +238,8 @@ bool FrontendAction::reportFatalErrors(const char (&message)[N]) {
     instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
     instance->getParsing().messages().Emit(
         llvm::errs(), instance->getAllCookedSources(),
-        /*echoSourceLines=*/true, &features, maxErrors);
+        /*echoSourceLines=*/true, &features, maxErrors,
+        instance->getInvocation().getWarnAsErr());
     return true;
   }
   if (instance->getParsing().parseTree().has_value() &&
@@ -249,7 +250,8 @@ bool FrontendAction::reportFatalErrors(const char (&message)[N]) {
     instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
     instance->getParsing().messages().Emit(
         llvm::errs(), instance->getAllCookedSources(),
-        /*echoSourceLine=*/true, &features, maxErrors);
+        /*echoSourceLine=*/true, &features, maxErrors,
+        instance->getInvocation().getWarnAsErr());
     instance->getParsing().EmitMessage(
         llvm::errs(), instance->getParsing().finalRestingPlace(),
         "parser FAIL (final position)", "error: ", llvm::raw_ostream::RED);
diff --git a/flang/lib/Parser/message.cpp b/flang/lib/Parser/message.cpp
index 909fba948a45a..574e9f5d424f8 100644
--- a/flang/lib/Parser/message.cpp
+++ b/flang/lib/Parser/message.cpp
@@ -161,16 +161,21 @@ bool Message::SortBefore(const Message &that) const {
       location_, that.location_);
 }
 
-bool Message::IsFatal() const {
-  return severity() == Severity::Error || severity() == Severity::Todo;
+bool Message::IsFatal(bool warningsAreErrors) const {
+  Severity sev{severity(warningsAreErrors)};
+  return sev == Severity::Error || sev == Severity::Todo;
 }
 
-Severity Message::severity() const {
+Severity Message::severity(bool warningsAreErrors) const {
   return common::visit(
       common::visitors{
           [](const MessageExpectedText &) { return Severity::Error; },
-          [](const MessageFixedText &x) { return x.severity(); },
-          [](const MessageFormattedText &x) { return x.severity(); },
+          [=](const MessageFixedText &x) {
+            return x.severity(warningsAreErrors);
+          },
+          [=](const MessageFormattedText &x) {
+            return x.severity(warningsAreErrors);
+          },
       },
       text_);
 }
@@ -295,15 +300,16 @@ static constexpr int MAX_CONTEXTS_EMITTED{2};
 static constexpr bool OMIT_SHARED_CONTEXTS{true};
 
 void Message::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
-    bool echoSourceLine,
-    const common::LanguageFeatureControl *hintFlagPtr) const {
+    bool echoSourceLine, const common::LanguageFeatureControl *hintFlagPtr,
+    bool warningsAreErrors) const {
   std::optional<ProvenanceRange> provenanceRange{GetProvenanceRange(allCooked)};
   const AllSources &sources{allCooked.allSources()};
   const std::string text{ToString()};
+  Severity sev{severity(warningsAreErrors)};
   const std::string hint{
       HintLanguageControlFlag(hintFlagPtr, languageFeature_, usageWarning_)};
-  sources.EmitMessage(o, provenanceRange, text + hint, Prefix(severity()),
-      PrefixColor(severity()), echoSourceLine);
+  sources.EmitMessage(o, provenanceRange, text + hint, Prefix(sev),
+      PrefixColor(sev), echoSourceLine);
   // Refers to whether the attachment in the loop below is a context, but can't
   // be declared inside the loop because the previous iteration's
   // attachment->attachmentIsContext_ indicates this.
@@ -453,7 +459,7 @@ void Messages::ResolveProvenances(const AllCookedSources &allCooked) {
 
 void Messages::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
     bool echoSourceLines, const common::LanguageFeatureControl *hintFlagPtr,
-    std::size_t maxErrorsToEmit) const {
+    std::size_t maxErrorsToEmit, bool warningsAreErrors) const {
   std::vector<const Message *> sorted;
   for (const auto &msg : messages_) {
     sorted.push_back(&msg);
@@ -467,9 +473,9 @@ void Messages::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
       // Don't emit two identical messages for the same location
       continue;
     }
-    msg->Emit(o, allCooked, echoSourceLines, hintFlagPtr);
+    msg->Emit(o, allCooked, echoSourceLines, hintFlagPtr, warningsAreErrors);
     lastMsg = msg;
-    if (msg->IsFatal()) {
+    if (msg->IsFatal(warningsAreErrors)) {
       ++errorsEmitted;
     }
     // If maxErrorsToEmit is 0, emit all errors, otherwise break after
@@ -491,9 +497,12 @@ void Messages::AttachTo(Message &msg, std::optional<Severity> severity) {
   messages_.clear();
 }
 
-bool Messages::AnyFatalError() const {
+bool Messages::AnyFatalError(bool warningsAreErrors) const {
+  if (messages_.empty()) {
+    return false;
+  }
   for (const auto &msg : messages_) {
-    if (msg.IsFatal()) {
+    if (msg.IsFatal(warningsAreErrors)) {
       return true;
     }
   }
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 96faa5fd954cd..df898f6e5651e 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -7575,7 +7575,8 @@ bool DeclarationVisitor::OkToAddComponent(
       if (msg) {
         auto &said{Say2(name, std::move(*msg), *prev,
             "Previous declaration of '%s'"_en_US)};
-        if (msg->severity() == parser::Severity::Error) {
+        if (msg->severity(/*warningsAreErrors=*/false) ==
+            parser::Severity::Error) {
           Resolve(name, *prev);
           return false;
         }
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index ab78605d01f4c..b15ed057b52f2 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -376,8 +376,7 @@ const DeclTypeSpec &SemanticsContext::MakeLogicalType(int kind) {
 }
 
 bool SemanticsContext::AnyFatalError() const {
-  return !messages_.empty() &&
-      (warningsAreErrors_ || messages_.AnyFatalError());
+  return messages_.AnyFatalError(warningsAreErrors_);
 }
 bool SemanticsContext::HasError(const Symbol &symbol) {
   return errorSymbols_.count(symbol) > 0;
@@ -658,7 +657,7 @@ void Semantics::EmitMessages(llvm::raw_ostream &os) {
   context_.messages().ResolveProvenances(context_.allCookedSources());
   context_.messages().Emit(os, context_.allCookedSources(),
       /*echoSourceLine=*/true, &context_.languageFeatures(),
-      /*maxErrorsToEmit=*/context_.maxErrors());
+      context_.maxErrors(), context_.warningsAreErrors());
 }
 
 void SemanticsContext::DumpSymbols(llvm::raw_ostream &os) {
diff --git a/flang/test/Driver/color-diagnostics-scan.f b/flang/test/Driver/color-diagnostics-scan.f
index 29d4635b4fb03..2f647c923c2e6 100644
--- a/flang/test/Driver/color-diagnostics-scan.f
+++ b/flang/test/Driver/color-diagnostics-scan.f
@@ -3,24 +3,24 @@
 ! Windows command prompt doesn't support ANSI escape sequences.
 ! REQUIRES: shell
 
-! RUN: not %flang %s -E -Werror -fcolor-diagnostics 2>&1 \
+! RUN: %flang %s -E -fcolor-diagnostics 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
-! RUN: not %flang %s -E -Werror -fno-color-diagnostics 2>&1 \
+! RUN: %flang %s -E -fno-color-diagnostics 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_NCD
-! RUN: not %flang_fc1 -E -Werror %s -fcolor-diagnostics 2>&1 \
+! RUN: %flang_fc1 -E %s -fcolor-diagnostics 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
 
-! RUN: not %flang %s -E -Werror -fdiagnostics-color 2>&1 \
+! RUN: %flang %s -E -fdiagnostics-color 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
-! RUN: not %flang %s -E -Werror -fno-diagnostics-color 2>&1 \
+! RUN: %flang %s -E -fno-diagnostics-color 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_NCD
 
-! RUN: not %flang %s -E -Werror -fdiagnostics-color=always 2>&1 \
+! RUN: %flang %s -E -fdiagnostics-color=always 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
-! RUN: not %flang %s -E -Werror -fdiagnostics-color=never 2>&1 \
+! RUN: %flang %s -E -fdiagnostics-color=never 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_NCD
 
-! RUN: not %flang_fc1 -E -Werror %s 2>&1 | FileCheck %s --check-prefix=CHECK_NCD
+! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s --check-prefix=CHECK_NCD
 
 ! CHECK_CD: {{.*}}[0;1;35mwarning: {{.*}}[0mCharacter in fixed-form label field must be a digit
 
diff --git a/flang/test/Driver/fatal-errors-warnings.f90 b/flang/test/Driver/fatal-errors-warnings.f90
new file mode 100644
index 0000000000000..b4b23230587a4
--- /dev/null
+++ b/flang/test/Driver/fatal-errors-warnings.f90
@@ -0,0 +1,32 @@
+! RUN: not %flang_fc1 -Wfatal-errors -pedantic %s 2>&1 | FileCheck %s --check-prefix=CHECK1
+! RUN: not %flang_fc1 -pedantic -Werror %s 2>&1 | FileCheck %s --check-prefix=CHECK2
+! RUN: not %flang_fc1 -Wfatal-errors -pedantic -Werror %s 2>&1 | FileCheck %s --check-prefix=CHECK3
+
+module m
+    contains
+    subroutine foo(a)
+        real, intent(in), target :: a(:)
+    end subroutine
+end module
+
+program test
+    use m
+    real, target :: a(1)
+    real :: b(1)
+    call foo(a) ! ok
+    !CHECK1: fatal-errors-warnings.f90:{{.*}} warning:
+    !CHECK2: fatal-errors-warnings.f90:{{.*}} error:
+    !CHECK3: fatal-errors-warnings.f90:{{.*}} error:
+    call foo(b)
+    !CHECK1: fatal-errors-warnings.f90:{{.*}} warning:
+    !CHECK2: fatal-errors-warnings.f90:{{.*}} error:
+    !CHECK3-NOT: error:
+    !CHECK3-NOT: warning:
+    call foo((a))
+    !CHECK1: fatal-errors-warnings.f90:{{.*}} warning:
+    !CHECK2: fatal-errors-warnings.f90:{{.*}} error:
+    call foo(a([1]))
+    !CHECK1: fatal-errors-warnings.f90:{{.*}} error:
+    !CHECK2: fatal-errors-warnings.f90:{{.*}} error:
+    call foo(a(1))
+end
\ No newline at end of file

@llvmbot
Copy link
Member

llvmbot commented Jul 14, 2025

@llvm/pr-subscribers-flang-driver

Author: Andre Kuhlenschmidt (akuhlens)

Changes

This PR changes how -Werror promotes warnings to errors so that it interoperates with -Wfatal-error. In doing so it changes how warnings are presented when they are promoted to be in-line with gfortran and clang.

Flang:

$ build/bin/flang flang/test/Driver/color-diagnostics-scan.f &amp;&amp; build/bin/flang -Werror flang/test/Driver/color-diagnostics-scan.f
./flang/test/Driver/color-diagnostics-scan.f:29:3: warning: Character in fixed-form label field must be a digit [-Wscanning]
  1 continue
    ^
./flang/test/Driver/color-diagnostics-scan.f:30:1: warning: Character in fixed-form label field must be a digit [-Wscanning]
  end
  ^
error: Could not scan flang/test/Driver/color-diagnostics-scan.f
./flang/test/Driver/color-diagnostics-scan.f:29:3: error: Character in fixed-form label field must be a digit [-Wscanning]
  1 continue
    ^
./flang/test/Driver/color-diagnostics-scan.f:30:1: error: Character in fixed-form label field must be a digit [-Wscanning]
  end
  ^

clang

$ touch t.c &amp;&amp; clang -c -pedantic t.c &amp;&amp; clang -c -pedantic -Werror t.c
t.c:1:1: warning: ISO C requires a translation unit to contain at
      least one declaration [-Wempty-translation-unit]
1 warning generated.
t.c:1:1: error: ISO C requires a translation unit to contain at
      least one declaration [-Werror,-Wempty-translation-unit]
1 error generated.

gfortran

λ cat ~/tmp/alias.f90 &amp;&amp; gfortran -c -Wall ~/tmp/alias.f90 &amp;&amp; gfortran -c -Wall -Werro
r ~/tmp/alias.f90
program p
interface
  subroutine bar(a,b)
    integer, intent(in) :: a
    integer, intent(out) :: b
  end subroutine
end interface
integer :: a

call bar(a,a)
end program p
/home/akuhlenschmi/tmp/alias.f90:10:9:

   10 | call bar(a,a)
      |         1
Warning: Same actual argument associated with INTENT(IN) argument ‘a’ and INTENT(OUT) a
rgument ‘b’ at (1)
/home/akuhlenschmi/tmp/alias.f90:10:9:

   10 | call bar(a,a)
      |         1
Error: Same actual argument associated with INTENT(IN) argument ‘a’ and INTENT(OUT) arg
ument ‘b’ at (1) [-Werror]
f951: all warnings being treated as errors

Note, it should be a simple change to switch the text formatting back to the previous behavior if we prefer that, but my intuition was that it would behave this way and I was surprised when it didn't.


Full diff: https://github.com/llvm/llvm-project/pull/148748.diff

7 Files Affected:

  • (modified) flang/include/flang/Parser/message.h (+25-12)
  • (modified) flang/lib/Frontend/FrontendAction.cpp (+4-2)
  • (modified) flang/lib/Parser/message.cpp (+23-14)
  • (modified) flang/lib/Semantics/resolve-names.cpp (+2-1)
  • (modified) flang/lib/Semantics/semantics.cpp (+2-3)
  • (modified) flang/test/Driver/color-diagnostics-scan.f (+8-8)
  • (added) flang/test/Driver/fatal-errors-warnings.f90 (+32)
diff --git a/flang/include/flang/Parser/message.h b/flang/include/flang/Parser/message.h
index db1a0a65157e3..d1d313a70a016 100644
--- a/flang/include/flang/Parser/message.h
+++ b/flang/include/flang/Parser/message.h
@@ -56,13 +56,19 @@ class MessageFixedText {
 
   CharBlock text() const { return text_; }
   bool empty() const { return text_.empty(); }
-  Severity severity() const { return severity_; }
+  Severity severity(bool warningsAreErrors = false) const {
+    if (warningsAreErrors) {
+      return Severity::Error;
+    }
+    return severity_;
+  }
   MessageFixedText &set_severity(Severity severity) {
     severity_ = severity;
     return *this;
   }
-  bool IsFatal() const {
-    return severity_ == Severity::Error || severity_ == Severity::Todo;
+  bool IsFatal(bool warningsAreErrors = false) const {
+    Severity sev{severity(warningsAreErrors)};
+    return sev == Severity::Error || sev == Severity::Todo;
   }
 
 private:
@@ -105,7 +111,7 @@ class MessageFormattedText {
 public:
   template <typename... A>
   MessageFormattedText(const MessageFixedText &text, A &&...x)
-      : severity_{text.severity()} {
+      : severity_{text.severity(false)} {
     Format(&text, Convert(std::forward<A>(x))...);
   }
   MessageFormattedText(const MessageFormattedText &) = default;
@@ -113,14 +119,20 @@ class MessageFormattedText {
   MessageFormattedText &operator=(const MessageFormattedText &) = default;
   MessageFormattedText &operator=(MessageFormattedText &&) = default;
   const std::string &string() const { return string_; }
-  bool IsFatal() const {
-    return severity_ == Severity::Error || severity_ == Severity::Todo;
+  Severity severity(bool warningsAreErrors = false) const {
+    if (warningsAreErrors) {
+      return Severity::Error;
+    }
+    return severity_;
   }
-  Severity severity() const { return severity_; }
   MessageFormattedText &set_severity(Severity severity) {
     severity_ = severity;
     return *this;
   }
+  bool IsFatal(bool warningsAreErrors = false) const {
+    Severity sev{severity(warningsAreErrors)};
+    return sev == Severity::Error || sev == Severity::Todo;
+  }
   std::string MoveString() { return std::move(string_); }
   bool operator==(const MessageFormattedText &that) const {
     return severity_ == that.severity_ && string_ == that.string_;
@@ -281,8 +293,8 @@ class Message : public common::ReferenceCounted<Message> {
   }
 
   bool SortBefore(const Message &that) const;
-  bool IsFatal() const;
-  Severity severity() const;
+  bool IsFatal(bool warningsAreErrors = false) const;
+  Severity severity(bool warningsAreErrors = false) const;
   Message &set_severity(Severity);
   std::optional<common::LanguageFeature> languageFeature() const;
   Message &set_languageFeature(common::LanguageFeature);
@@ -293,7 +305,8 @@ class Message : public common::ReferenceCounted<Message> {
       const AllCookedSources &) const;
   void Emit(llvm::raw_ostream &, const AllCookedSources &,
       bool echoSourceLine = true,
-      const common::LanguageFeatureControl *hintFlags = nullptr) const;
+      const common::LanguageFeatureControl *hintFlags = nullptr,
+      bool warningsAreErrors = false) const;
 
   // If this Message or any of its attachments locates itself via a CharBlock,
   // replace its location with the corresponding ProvenanceRange.
@@ -355,9 +368,9 @@ class Messages {
   void Emit(llvm::raw_ostream &, const AllCookedSources &,
       bool echoSourceLines = true,
       const common::LanguageFeatureControl *hintFlags = nullptr,
-      std::size_t maxErrorsToEmit = 0) const;
+      std::size_t maxErrorsToEmit = 0, bool warningsAreErrors = false) const;
   void AttachTo(Message &, std::optional<Severity> = std::nullopt);
-  bool AnyFatalError() const;
+  bool AnyFatalError(bool warningsAreErrors = false) const;
 
 private:
   std::list<Message> messages_;
diff --git a/flang/lib/Frontend/FrontendAction.cpp b/flang/lib/Frontend/FrontendAction.cpp
index 2429e07e5b8c4..d32f477445f93 100644
--- a/flang/lib/Frontend/FrontendAction.cpp
+++ b/flang/lib/Frontend/FrontendAction.cpp
@@ -238,7 +238,8 @@ bool FrontendAction::reportFatalErrors(const char (&message)[N]) {
     instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
     instance->getParsing().messages().Emit(
         llvm::errs(), instance->getAllCookedSources(),
-        /*echoSourceLines=*/true, &features, maxErrors);
+        /*echoSourceLines=*/true, &features, maxErrors,
+        instance->getInvocation().getWarnAsErr());
     return true;
   }
   if (instance->getParsing().parseTree().has_value() &&
@@ -249,7 +250,8 @@ bool FrontendAction::reportFatalErrors(const char (&message)[N]) {
     instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
     instance->getParsing().messages().Emit(
         llvm::errs(), instance->getAllCookedSources(),
-        /*echoSourceLine=*/true, &features, maxErrors);
+        /*echoSourceLine=*/true, &features, maxErrors,
+        instance->getInvocation().getWarnAsErr());
     instance->getParsing().EmitMessage(
         llvm::errs(), instance->getParsing().finalRestingPlace(),
         "parser FAIL (final position)", "error: ", llvm::raw_ostream::RED);
diff --git a/flang/lib/Parser/message.cpp b/flang/lib/Parser/message.cpp
index 909fba948a45a..574e9f5d424f8 100644
--- a/flang/lib/Parser/message.cpp
+++ b/flang/lib/Parser/message.cpp
@@ -161,16 +161,21 @@ bool Message::SortBefore(const Message &that) const {
       location_, that.location_);
 }
 
-bool Message::IsFatal() const {
-  return severity() == Severity::Error || severity() == Severity::Todo;
+bool Message::IsFatal(bool warningsAreErrors) const {
+  Severity sev{severity(warningsAreErrors)};
+  return sev == Severity::Error || sev == Severity::Todo;
 }
 
-Severity Message::severity() const {
+Severity Message::severity(bool warningsAreErrors) const {
   return common::visit(
       common::visitors{
           [](const MessageExpectedText &) { return Severity::Error; },
-          [](const MessageFixedText &x) { return x.severity(); },
-          [](const MessageFormattedText &x) { return x.severity(); },
+          [=](const MessageFixedText &x) {
+            return x.severity(warningsAreErrors);
+          },
+          [=](const MessageFormattedText &x) {
+            return x.severity(warningsAreErrors);
+          },
       },
       text_);
 }
@@ -295,15 +300,16 @@ static constexpr int MAX_CONTEXTS_EMITTED{2};
 static constexpr bool OMIT_SHARED_CONTEXTS{true};
 
 void Message::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
-    bool echoSourceLine,
-    const common::LanguageFeatureControl *hintFlagPtr) const {
+    bool echoSourceLine, const common::LanguageFeatureControl *hintFlagPtr,
+    bool warningsAreErrors) const {
   std::optional<ProvenanceRange> provenanceRange{GetProvenanceRange(allCooked)};
   const AllSources &sources{allCooked.allSources()};
   const std::string text{ToString()};
+  Severity sev{severity(warningsAreErrors)};
   const std::string hint{
       HintLanguageControlFlag(hintFlagPtr, languageFeature_, usageWarning_)};
-  sources.EmitMessage(o, provenanceRange, text + hint, Prefix(severity()),
-      PrefixColor(severity()), echoSourceLine);
+  sources.EmitMessage(o, provenanceRange, text + hint, Prefix(sev),
+      PrefixColor(sev), echoSourceLine);
   // Refers to whether the attachment in the loop below is a context, but can't
   // be declared inside the loop because the previous iteration's
   // attachment->attachmentIsContext_ indicates this.
@@ -453,7 +459,7 @@ void Messages::ResolveProvenances(const AllCookedSources &allCooked) {
 
 void Messages::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
     bool echoSourceLines, const common::LanguageFeatureControl *hintFlagPtr,
-    std::size_t maxErrorsToEmit) const {
+    std::size_t maxErrorsToEmit, bool warningsAreErrors) const {
   std::vector<const Message *> sorted;
   for (const auto &msg : messages_) {
     sorted.push_back(&msg);
@@ -467,9 +473,9 @@ void Messages::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
       // Don't emit two identical messages for the same location
       continue;
     }
-    msg->Emit(o, allCooked, echoSourceLines, hintFlagPtr);
+    msg->Emit(o, allCooked, echoSourceLines, hintFlagPtr, warningsAreErrors);
     lastMsg = msg;
-    if (msg->IsFatal()) {
+    if (msg->IsFatal(warningsAreErrors)) {
       ++errorsEmitted;
     }
     // If maxErrorsToEmit is 0, emit all errors, otherwise break after
@@ -491,9 +497,12 @@ void Messages::AttachTo(Message &msg, std::optional<Severity> severity) {
   messages_.clear();
 }
 
-bool Messages::AnyFatalError() const {
+bool Messages::AnyFatalError(bool warningsAreErrors) const {
+  if (messages_.empty()) {
+    return false;
+  }
   for (const auto &msg : messages_) {
-    if (msg.IsFatal()) {
+    if (msg.IsFatal(warningsAreErrors)) {
       return true;
     }
   }
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 96faa5fd954cd..df898f6e5651e 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -7575,7 +7575,8 @@ bool DeclarationVisitor::OkToAddComponent(
       if (msg) {
         auto &said{Say2(name, std::move(*msg), *prev,
             "Previous declaration of '%s'"_en_US)};
-        if (msg->severity() == parser::Severity::Error) {
+        if (msg->severity(/*warningsAreErrors=*/false) ==
+            parser::Severity::Error) {
           Resolve(name, *prev);
           return false;
         }
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index ab78605d01f4c..b15ed057b52f2 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -376,8 +376,7 @@ const DeclTypeSpec &SemanticsContext::MakeLogicalType(int kind) {
 }
 
 bool SemanticsContext::AnyFatalError() const {
-  return !messages_.empty() &&
-      (warningsAreErrors_ || messages_.AnyFatalError());
+  return messages_.AnyFatalError(warningsAreErrors_);
 }
 bool SemanticsContext::HasError(const Symbol &symbol) {
   return errorSymbols_.count(symbol) > 0;
@@ -658,7 +657,7 @@ void Semantics::EmitMessages(llvm::raw_ostream &os) {
   context_.messages().ResolveProvenances(context_.allCookedSources());
   context_.messages().Emit(os, context_.allCookedSources(),
       /*echoSourceLine=*/true, &context_.languageFeatures(),
-      /*maxErrorsToEmit=*/context_.maxErrors());
+      context_.maxErrors(), context_.warningsAreErrors());
 }
 
 void SemanticsContext::DumpSymbols(llvm::raw_ostream &os) {
diff --git a/flang/test/Driver/color-diagnostics-scan.f b/flang/test/Driver/color-diagnostics-scan.f
index 29d4635b4fb03..2f647c923c2e6 100644
--- a/flang/test/Driver/color-diagnostics-scan.f
+++ b/flang/test/Driver/color-diagnostics-scan.f
@@ -3,24 +3,24 @@
 ! Windows command prompt doesn't support ANSI escape sequences.
 ! REQUIRES: shell
 
-! RUN: not %flang %s -E -Werror -fcolor-diagnostics 2>&1 \
+! RUN: %flang %s -E -fcolor-diagnostics 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
-! RUN: not %flang %s -E -Werror -fno-color-diagnostics 2>&1 \
+! RUN: %flang %s -E -fno-color-diagnostics 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_NCD
-! RUN: not %flang_fc1 -E -Werror %s -fcolor-diagnostics 2>&1 \
+! RUN: %flang_fc1 -E %s -fcolor-diagnostics 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
 
-! RUN: not %flang %s -E -Werror -fdiagnostics-color 2>&1 \
+! RUN: %flang %s -E -fdiagnostics-color 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
-! RUN: not %flang %s -E -Werror -fno-diagnostics-color 2>&1 \
+! RUN: %flang %s -E -fno-diagnostics-color 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_NCD
 
-! RUN: not %flang %s -E -Werror -fdiagnostics-color=always 2>&1 \
+! RUN: %flang %s -E -fdiagnostics-color=always 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
-! RUN: not %flang %s -E -Werror -fdiagnostics-color=never 2>&1 \
+! RUN: %flang %s -E -fdiagnostics-color=never 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_NCD
 
-! RUN: not %flang_fc1 -E -Werror %s 2>&1 | FileCheck %s --check-prefix=CHECK_NCD
+! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s --check-prefix=CHECK_NCD
 
 ! CHECK_CD: {{.*}}[0;1;35mwarning: {{.*}}[0mCharacter in fixed-form label field must be a digit
 
diff --git a/flang/test/Driver/fatal-errors-warnings.f90 b/flang/test/Driver/fatal-errors-warnings.f90
new file mode 100644
index 0000000000000..b4b23230587a4
--- /dev/null
+++ b/flang/test/Driver/fatal-errors-warnings.f90
@@ -0,0 +1,32 @@
+! RUN: not %flang_fc1 -Wfatal-errors -pedantic %s 2>&1 | FileCheck %s --check-prefix=CHECK1
+! RUN: not %flang_fc1 -pedantic -Werror %s 2>&1 | FileCheck %s --check-prefix=CHECK2
+! RUN: not %flang_fc1 -Wfatal-errors -pedantic -Werror %s 2>&1 | FileCheck %s --check-prefix=CHECK3
+
+module m
+    contains
+    subroutine foo(a)
+        real, intent(in), target :: a(:)
+    end subroutine
+end module
+
+program test
+    use m
+    real, target :: a(1)
+    real :: b(1)
+    call foo(a) ! ok
+    !CHECK1: fatal-errors-warnings.f90:{{.*}} warning:
+    !CHECK2: fatal-errors-warnings.f90:{{.*}} error:
+    !CHECK3: fatal-errors-warnings.f90:{{.*}} error:
+    call foo(b)
+    !CHECK1: fatal-errors-warnings.f90:{{.*}} warning:
+    !CHECK2: fatal-errors-warnings.f90:{{.*}} error:
+    !CHECK3-NOT: error:
+    !CHECK3-NOT: warning:
+    call foo((a))
+    !CHECK1: fatal-errors-warnings.f90:{{.*}} warning:
+    !CHECK2: fatal-errors-warnings.f90:{{.*}} error:
+    call foo(a([1]))
+    !CHECK1: fatal-errors-warnings.f90:{{.*}} error:
+    !CHECK2: fatal-errors-warnings.f90:{{.*}} error:
+    call foo(a(1))
+end
\ No newline at end of file

@llvmbot
Copy link
Member

llvmbot commented Jul 14, 2025

@llvm/pr-subscribers-flang-semantics

Author: Andre Kuhlenschmidt (akuhlens)

Changes

This PR changes how -Werror promotes warnings to errors so that it interoperates with -Wfatal-error. In doing so it changes how warnings are presented when they are promoted to be in-line with gfortran and clang.

Flang:

$ build/bin/flang flang/test/Driver/color-diagnostics-scan.f &amp;&amp; build/bin/flang -Werror flang/test/Driver/color-diagnostics-scan.f
./flang/test/Driver/color-diagnostics-scan.f:29:3: warning: Character in fixed-form label field must be a digit [-Wscanning]
  1 continue
    ^
./flang/test/Driver/color-diagnostics-scan.f:30:1: warning: Character in fixed-form label field must be a digit [-Wscanning]
  end
  ^
error: Could not scan flang/test/Driver/color-diagnostics-scan.f
./flang/test/Driver/color-diagnostics-scan.f:29:3: error: Character in fixed-form label field must be a digit [-Wscanning]
  1 continue
    ^
./flang/test/Driver/color-diagnostics-scan.f:30:1: error: Character in fixed-form label field must be a digit [-Wscanning]
  end
  ^

clang

$ touch t.c &amp;&amp; clang -c -pedantic t.c &amp;&amp; clang -c -pedantic -Werror t.c
t.c:1:1: warning: ISO C requires a translation unit to contain at
      least one declaration [-Wempty-translation-unit]
1 warning generated.
t.c:1:1: error: ISO C requires a translation unit to contain at
      least one declaration [-Werror,-Wempty-translation-unit]
1 error generated.

gfortran

λ cat ~/tmp/alias.f90 &amp;&amp; gfortran -c -Wall ~/tmp/alias.f90 &amp;&amp; gfortran -c -Wall -Werro
r ~/tmp/alias.f90
program p
interface
  subroutine bar(a,b)
    integer, intent(in) :: a
    integer, intent(out) :: b
  end subroutine
end interface
integer :: a

call bar(a,a)
end program p
/home/akuhlenschmi/tmp/alias.f90:10:9:

   10 | call bar(a,a)
      |         1
Warning: Same actual argument associated with INTENT(IN) argument ‘a’ and INTENT(OUT) a
rgument ‘b’ at (1)
/home/akuhlenschmi/tmp/alias.f90:10:9:

   10 | call bar(a,a)
      |         1
Error: Same actual argument associated with INTENT(IN) argument ‘a’ and INTENT(OUT) arg
ument ‘b’ at (1) [-Werror]
f951: all warnings being treated as errors

Note, it should be a simple change to switch the text formatting back to the previous behavior if we prefer that, but my intuition was that it would behave this way and I was surprised when it didn't.


Full diff: https://github.com/llvm/llvm-project/pull/148748.diff

7 Files Affected:

  • (modified) flang/include/flang/Parser/message.h (+25-12)
  • (modified) flang/lib/Frontend/FrontendAction.cpp (+4-2)
  • (modified) flang/lib/Parser/message.cpp (+23-14)
  • (modified) flang/lib/Semantics/resolve-names.cpp (+2-1)
  • (modified) flang/lib/Semantics/semantics.cpp (+2-3)
  • (modified) flang/test/Driver/color-diagnostics-scan.f (+8-8)
  • (added) flang/test/Driver/fatal-errors-warnings.f90 (+32)
diff --git a/flang/include/flang/Parser/message.h b/flang/include/flang/Parser/message.h
index db1a0a65157e3..d1d313a70a016 100644
--- a/flang/include/flang/Parser/message.h
+++ b/flang/include/flang/Parser/message.h
@@ -56,13 +56,19 @@ class MessageFixedText {
 
   CharBlock text() const { return text_; }
   bool empty() const { return text_.empty(); }
-  Severity severity() const { return severity_; }
+  Severity severity(bool warningsAreErrors = false) const {
+    if (warningsAreErrors) {
+      return Severity::Error;
+    }
+    return severity_;
+  }
   MessageFixedText &set_severity(Severity severity) {
     severity_ = severity;
     return *this;
   }
-  bool IsFatal() const {
-    return severity_ == Severity::Error || severity_ == Severity::Todo;
+  bool IsFatal(bool warningsAreErrors = false) const {
+    Severity sev{severity(warningsAreErrors)};
+    return sev == Severity::Error || sev == Severity::Todo;
   }
 
 private:
@@ -105,7 +111,7 @@ class MessageFormattedText {
 public:
   template <typename... A>
   MessageFormattedText(const MessageFixedText &text, A &&...x)
-      : severity_{text.severity()} {
+      : severity_{text.severity(false)} {
     Format(&text, Convert(std::forward<A>(x))...);
   }
   MessageFormattedText(const MessageFormattedText &) = default;
@@ -113,14 +119,20 @@ class MessageFormattedText {
   MessageFormattedText &operator=(const MessageFormattedText &) = default;
   MessageFormattedText &operator=(MessageFormattedText &&) = default;
   const std::string &string() const { return string_; }
-  bool IsFatal() const {
-    return severity_ == Severity::Error || severity_ == Severity::Todo;
+  Severity severity(bool warningsAreErrors = false) const {
+    if (warningsAreErrors) {
+      return Severity::Error;
+    }
+    return severity_;
   }
-  Severity severity() const { return severity_; }
   MessageFormattedText &set_severity(Severity severity) {
     severity_ = severity;
     return *this;
   }
+  bool IsFatal(bool warningsAreErrors = false) const {
+    Severity sev{severity(warningsAreErrors)};
+    return sev == Severity::Error || sev == Severity::Todo;
+  }
   std::string MoveString() { return std::move(string_); }
   bool operator==(const MessageFormattedText &that) const {
     return severity_ == that.severity_ && string_ == that.string_;
@@ -281,8 +293,8 @@ class Message : public common::ReferenceCounted<Message> {
   }
 
   bool SortBefore(const Message &that) const;
-  bool IsFatal() const;
-  Severity severity() const;
+  bool IsFatal(bool warningsAreErrors = false) const;
+  Severity severity(bool warningsAreErrors = false) const;
   Message &set_severity(Severity);
   std::optional<common::LanguageFeature> languageFeature() const;
   Message &set_languageFeature(common::LanguageFeature);
@@ -293,7 +305,8 @@ class Message : public common::ReferenceCounted<Message> {
       const AllCookedSources &) const;
   void Emit(llvm::raw_ostream &, const AllCookedSources &,
       bool echoSourceLine = true,
-      const common::LanguageFeatureControl *hintFlags = nullptr) const;
+      const common::LanguageFeatureControl *hintFlags = nullptr,
+      bool warningsAreErrors = false) const;
 
   // If this Message or any of its attachments locates itself via a CharBlock,
   // replace its location with the corresponding ProvenanceRange.
@@ -355,9 +368,9 @@ class Messages {
   void Emit(llvm::raw_ostream &, const AllCookedSources &,
       bool echoSourceLines = true,
       const common::LanguageFeatureControl *hintFlags = nullptr,
-      std::size_t maxErrorsToEmit = 0) const;
+      std::size_t maxErrorsToEmit = 0, bool warningsAreErrors = false) const;
   void AttachTo(Message &, std::optional<Severity> = std::nullopt);
-  bool AnyFatalError() const;
+  bool AnyFatalError(bool warningsAreErrors = false) const;
 
 private:
   std::list<Message> messages_;
diff --git a/flang/lib/Frontend/FrontendAction.cpp b/flang/lib/Frontend/FrontendAction.cpp
index 2429e07e5b8c4..d32f477445f93 100644
--- a/flang/lib/Frontend/FrontendAction.cpp
+++ b/flang/lib/Frontend/FrontendAction.cpp
@@ -238,7 +238,8 @@ bool FrontendAction::reportFatalErrors(const char (&message)[N]) {
     instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
     instance->getParsing().messages().Emit(
         llvm::errs(), instance->getAllCookedSources(),
-        /*echoSourceLines=*/true, &features, maxErrors);
+        /*echoSourceLines=*/true, &features, maxErrors,
+        instance->getInvocation().getWarnAsErr());
     return true;
   }
   if (instance->getParsing().parseTree().has_value() &&
@@ -249,7 +250,8 @@ bool FrontendAction::reportFatalErrors(const char (&message)[N]) {
     instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
     instance->getParsing().messages().Emit(
         llvm::errs(), instance->getAllCookedSources(),
-        /*echoSourceLine=*/true, &features, maxErrors);
+        /*echoSourceLine=*/true, &features, maxErrors,
+        instance->getInvocation().getWarnAsErr());
     instance->getParsing().EmitMessage(
         llvm::errs(), instance->getParsing().finalRestingPlace(),
         "parser FAIL (final position)", "error: ", llvm::raw_ostream::RED);
diff --git a/flang/lib/Parser/message.cpp b/flang/lib/Parser/message.cpp
index 909fba948a45a..574e9f5d424f8 100644
--- a/flang/lib/Parser/message.cpp
+++ b/flang/lib/Parser/message.cpp
@@ -161,16 +161,21 @@ bool Message::SortBefore(const Message &that) const {
       location_, that.location_);
 }
 
-bool Message::IsFatal() const {
-  return severity() == Severity::Error || severity() == Severity::Todo;
+bool Message::IsFatal(bool warningsAreErrors) const {
+  Severity sev{severity(warningsAreErrors)};
+  return sev == Severity::Error || sev == Severity::Todo;
 }
 
-Severity Message::severity() const {
+Severity Message::severity(bool warningsAreErrors) const {
   return common::visit(
       common::visitors{
           [](const MessageExpectedText &) { return Severity::Error; },
-          [](const MessageFixedText &x) { return x.severity(); },
-          [](const MessageFormattedText &x) { return x.severity(); },
+          [=](const MessageFixedText &x) {
+            return x.severity(warningsAreErrors);
+          },
+          [=](const MessageFormattedText &x) {
+            return x.severity(warningsAreErrors);
+          },
       },
       text_);
 }
@@ -295,15 +300,16 @@ static constexpr int MAX_CONTEXTS_EMITTED{2};
 static constexpr bool OMIT_SHARED_CONTEXTS{true};
 
 void Message::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
-    bool echoSourceLine,
-    const common::LanguageFeatureControl *hintFlagPtr) const {
+    bool echoSourceLine, const common::LanguageFeatureControl *hintFlagPtr,
+    bool warningsAreErrors) const {
   std::optional<ProvenanceRange> provenanceRange{GetProvenanceRange(allCooked)};
   const AllSources &sources{allCooked.allSources()};
   const std::string text{ToString()};
+  Severity sev{severity(warningsAreErrors)};
   const std::string hint{
       HintLanguageControlFlag(hintFlagPtr, languageFeature_, usageWarning_)};
-  sources.EmitMessage(o, provenanceRange, text + hint, Prefix(severity()),
-      PrefixColor(severity()), echoSourceLine);
+  sources.EmitMessage(o, provenanceRange, text + hint, Prefix(sev),
+      PrefixColor(sev), echoSourceLine);
   // Refers to whether the attachment in the loop below is a context, but can't
   // be declared inside the loop because the previous iteration's
   // attachment->attachmentIsContext_ indicates this.
@@ -453,7 +459,7 @@ void Messages::ResolveProvenances(const AllCookedSources &allCooked) {
 
 void Messages::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
     bool echoSourceLines, const common::LanguageFeatureControl *hintFlagPtr,
-    std::size_t maxErrorsToEmit) const {
+    std::size_t maxErrorsToEmit, bool warningsAreErrors) const {
   std::vector<const Message *> sorted;
   for (const auto &msg : messages_) {
     sorted.push_back(&msg);
@@ -467,9 +473,9 @@ void Messages::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
       // Don't emit two identical messages for the same location
       continue;
     }
-    msg->Emit(o, allCooked, echoSourceLines, hintFlagPtr);
+    msg->Emit(o, allCooked, echoSourceLines, hintFlagPtr, warningsAreErrors);
     lastMsg = msg;
-    if (msg->IsFatal()) {
+    if (msg->IsFatal(warningsAreErrors)) {
       ++errorsEmitted;
     }
     // If maxErrorsToEmit is 0, emit all errors, otherwise break after
@@ -491,9 +497,12 @@ void Messages::AttachTo(Message &msg, std::optional<Severity> severity) {
   messages_.clear();
 }
 
-bool Messages::AnyFatalError() const {
+bool Messages::AnyFatalError(bool warningsAreErrors) const {
+  if (messages_.empty()) {
+    return false;
+  }
   for (const auto &msg : messages_) {
-    if (msg.IsFatal()) {
+    if (msg.IsFatal(warningsAreErrors)) {
       return true;
     }
   }
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 96faa5fd954cd..df898f6e5651e 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -7575,7 +7575,8 @@ bool DeclarationVisitor::OkToAddComponent(
       if (msg) {
         auto &said{Say2(name, std::move(*msg), *prev,
             "Previous declaration of '%s'"_en_US)};
-        if (msg->severity() == parser::Severity::Error) {
+        if (msg->severity(/*warningsAreErrors=*/false) ==
+            parser::Severity::Error) {
           Resolve(name, *prev);
           return false;
         }
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index ab78605d01f4c..b15ed057b52f2 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -376,8 +376,7 @@ const DeclTypeSpec &SemanticsContext::MakeLogicalType(int kind) {
 }
 
 bool SemanticsContext::AnyFatalError() const {
-  return !messages_.empty() &&
-      (warningsAreErrors_ || messages_.AnyFatalError());
+  return messages_.AnyFatalError(warningsAreErrors_);
 }
 bool SemanticsContext::HasError(const Symbol &symbol) {
   return errorSymbols_.count(symbol) > 0;
@@ -658,7 +657,7 @@ void Semantics::EmitMessages(llvm::raw_ostream &os) {
   context_.messages().ResolveProvenances(context_.allCookedSources());
   context_.messages().Emit(os, context_.allCookedSources(),
       /*echoSourceLine=*/true, &context_.languageFeatures(),
-      /*maxErrorsToEmit=*/context_.maxErrors());
+      context_.maxErrors(), context_.warningsAreErrors());
 }
 
 void SemanticsContext::DumpSymbols(llvm::raw_ostream &os) {
diff --git a/flang/test/Driver/color-diagnostics-scan.f b/flang/test/Driver/color-diagnostics-scan.f
index 29d4635b4fb03..2f647c923c2e6 100644
--- a/flang/test/Driver/color-diagnostics-scan.f
+++ b/flang/test/Driver/color-diagnostics-scan.f
@@ -3,24 +3,24 @@
 ! Windows command prompt doesn't support ANSI escape sequences.
 ! REQUIRES: shell
 
-! RUN: not %flang %s -E -Werror -fcolor-diagnostics 2>&1 \
+! RUN: %flang %s -E -fcolor-diagnostics 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
-! RUN: not %flang %s -E -Werror -fno-color-diagnostics 2>&1 \
+! RUN: %flang %s -E -fno-color-diagnostics 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_NCD
-! RUN: not %flang_fc1 -E -Werror %s -fcolor-diagnostics 2>&1 \
+! RUN: %flang_fc1 -E %s -fcolor-diagnostics 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
 
-! RUN: not %flang %s -E -Werror -fdiagnostics-color 2>&1 \
+! RUN: %flang %s -E -fdiagnostics-color 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
-! RUN: not %flang %s -E -Werror -fno-diagnostics-color 2>&1 \
+! RUN: %flang %s -E -fno-diagnostics-color 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_NCD
 
-! RUN: not %flang %s -E -Werror -fdiagnostics-color=always 2>&1 \
+! RUN: %flang %s -E -fdiagnostics-color=always 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
-! RUN: not %flang %s -E -Werror -fdiagnostics-color=never 2>&1 \
+! RUN: %flang %s -E -fdiagnostics-color=never 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_NCD
 
-! RUN: not %flang_fc1 -E -Werror %s 2>&1 | FileCheck %s --check-prefix=CHECK_NCD
+! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s --check-prefix=CHECK_NCD
 
 ! CHECK_CD: {{.*}}[0;1;35mwarning: {{.*}}[0mCharacter in fixed-form label field must be a digit
 
diff --git a/flang/test/Driver/fatal-errors-warnings.f90 b/flang/test/Driver/fatal-errors-warnings.f90
new file mode 100644
index 0000000000000..b4b23230587a4
--- /dev/null
+++ b/flang/test/Driver/fatal-errors-warnings.f90
@@ -0,0 +1,32 @@
+! RUN: not %flang_fc1 -Wfatal-errors -pedantic %s 2>&1 | FileCheck %s --check-prefix=CHECK1
+! RUN: not %flang_fc1 -pedantic -Werror %s 2>&1 | FileCheck %s --check-prefix=CHECK2
+! RUN: not %flang_fc1 -Wfatal-errors -pedantic -Werror %s 2>&1 | FileCheck %s --check-prefix=CHECK3
+
+module m
+    contains
+    subroutine foo(a)
+        real, intent(in), target :: a(:)
+    end subroutine
+end module
+
+program test
+    use m
+    real, target :: a(1)
+    real :: b(1)
+    call foo(a) ! ok
+    !CHECK1: fatal-errors-warnings.f90:{{.*}} warning:
+    !CHECK2: fatal-errors-warnings.f90:{{.*}} error:
+    !CHECK3: fatal-errors-warnings.f90:{{.*}} error:
+    call foo(b)
+    !CHECK1: fatal-errors-warnings.f90:{{.*}} warning:
+    !CHECK2: fatal-errors-warnings.f90:{{.*}} error:
+    !CHECK3-NOT: error:
+    !CHECK3-NOT: warning:
+    call foo((a))
+    !CHECK1: fatal-errors-warnings.f90:{{.*}} warning:
+    !CHECK2: fatal-errors-warnings.f90:{{.*}} error:
+    call foo(a([1]))
+    !CHECK1: fatal-errors-warnings.f90:{{.*}} error:
+    !CHECK2: fatal-errors-warnings.f90:{{.*}} error:
+    call foo(a(1))
+end
\ No newline at end of file

Copy link
Contributor

@klausler klausler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't display warnings with error: on them. I understand that other compilers do that, but I think that it's confusing to users to have some diagnostic messages be errors in some contexts and not in others.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:driver flang:parser flang:semantics flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants