[flang][driver] Add options for -Werror

With the option given, warnings are treated as error.

Reviewed By: awarzynski

Differential Revision: https://reviews.llvm.org/D98657

GitOrigin-RevId: 7416e8a8431a0f2711be9d16e111d1781b74df96
diff --git a/include/flang/Frontend/CompilerInvocation.h b/include/flang/Frontend/CompilerInvocation.h
index 99050dc..529f15e 100644
--- a/include/flang/Frontend/CompilerInvocation.h
+++ b/include/flang/Frontend/CompilerInvocation.h
@@ -71,6 +71,8 @@
 
   bool debugModuleDir_ = false;
 
+  bool warnAsErr_ = false;
+
   // Fortran Dialect options
   Fortran::common::IntrinsicTypeDefaultKinds defaultKinds_;
 
@@ -98,6 +100,9 @@
   bool &debugModuleDir() { return debugModuleDir_; }
   const bool &debugModuleDir() const { return debugModuleDir_; }
 
+  bool &warnAsErr() { return warnAsErr_; }
+  const bool &warnAsErr() const { return warnAsErr_; }
+
   bool &enableConformanceChecks() { return EnableConformanceChecks_; }
   const bool &enableConformanceChecks() const {
     return EnableConformanceChecks_;
@@ -126,6 +131,8 @@
 
   void SetDebugModuleDir(bool flag) { debugModuleDir_ = flag; }
 
+  void SetWarnAsErr(bool flag) { warnAsErr_ = flag; }
+
   /// Set the Fortran options to predifined defaults. These defaults are
   /// consistend with f18/f18.cpp.
   // TODO: We should map frontendOpts_ to parserOpts_ instead. For that, we
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 3bd541e..ce7392c 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -350,6 +350,26 @@
   }
 }
 
+/// Parses all diagnostics related arguments and populates the variables
+/// options accordingly.
+static void parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
+    clang::DiagnosticsEngine &diags) {
+  // -Werror option
+  // TODO: Currently throws a Diagnostic for anything other than -W<error>,
+  // this has to change when other -W<opt>'s are supported.
+  if (args.hasArg(clang::driver::options::OPT_W_Joined)) {
+    if (args.getLastArgValue(clang::driver::options::OPT_W_Joined)
+            .equals("error")) {
+      res.SetWarnAsErr(true);
+    } else {
+      const unsigned diagID =
+          diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
+              "Only `-Werror` is supported currently.");
+      diags.Report(diagID);
+    }
+  }
+}
+
 /// Parses all Dialect related arguments and populates the variables
 /// options accordingly.
 static void parseDialectArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
@@ -445,6 +465,8 @@
   parseSemaArgs(res, args, diags);
   // Parse dialect arguments
   parseDialectArgs(res, args, diags);
+  // Parse diagnostic arguments
+  parseDiagArgs(res, args, diags);
 
   return success;
 }
@@ -574,5 +596,6 @@
 
   semanticsContext_->set_moduleDirectory(moduleDir())
       .set_searchDirectories(fortranOptions.searchDirectories)
-      .set_warnOnNonstandardUsage(enableConformanceChecks());
+      .set_warnOnNonstandardUsage(enableConformanceChecks())
+      .set_warningsAreErrors(warnAsErr());
 }
diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp
index 1871a35..f5420c2 100644
--- a/lib/Frontend/FrontendActions.cpp
+++ b/lib/Frontend/FrontendActions.cpp
@@ -64,7 +64,9 @@
   // Prescan. In case of failure, report and return.
   ci.parsing().Prescan(currentInputPath, parserOptions);
 
-  if (ci.parsing().messages().AnyFatalError()) {
+  if (!ci.parsing().messages().empty() &&
+      (ci.invocation().warnAsErr() ||
+          ci.parsing().messages().AnyFatalError())) {
     const unsigned diagID = ci.diagnostics().getCustomDiagID(
         clang::DiagnosticsEngine::Error, "Could not scan %0");
     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
@@ -97,7 +99,9 @@
   // Prescan. In case of failure, report and return.
   ci.parsing().Prescan(currentInputPath, parserOptions);
 
-  if (ci.parsing().messages().AnyFatalError()) {
+  if (!ci.parsing().messages().empty() &&
+      (ci.invocation().warnAsErr() ||
+          ci.parsing().messages().AnyFatalError())) {
     const unsigned diagID = ci.diagnostics().getCustomDiagID(
         clang::DiagnosticsEngine::Error, "Could not scan %0");
     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
@@ -109,7 +113,9 @@
   // Parse. In case of failure, report and return.
   ci.parsing().Parse(llvm::outs());
 
-  if (ci.parsing().messages().AnyFatalError()) {
+  if (!ci.parsing().messages().empty() &&
+      (ci.invocation().warnAsErr() ||
+          ci.parsing().messages().AnyFatalError())) {
     unsigned diagID = ci.diagnostics().getCustomDiagID(
         clang::DiagnosticsEngine::Error, "Could not parse %0");
     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
@@ -264,7 +270,9 @@
   // Parse. In case of failure, report and return.
   ci.parsing().Parse(llvm::outs());
 
-  if (ci.parsing().messages().AnyFatalError()) {
+  if (!ci.parsing().messages().empty() &&
+      (ci.invocation().warnAsErr() ||
+          ci.parsing().messages().AnyFatalError())) {
     unsigned diagID = ci.diagnostics().getCustomDiagID(
         clang::DiagnosticsEngine::Error, "Could not parse %0");
     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
diff --git a/test/Driver/driver-help-hidden.f90 b/test/Driver/driver-help-hidden.f90
index e8b1141..263dfb1 100644
--- a/test/Driver/driver-help-hidden.f90
+++ b/test/Driver/driver-help-hidden.f90
@@ -51,6 +51,7 @@
 ! CHECK-NEXT: -std=<value>           Language standard to compile for
 ! CHECK-NEXT: -U <macro>             Undefine macro <macro>
 ! CHECK-NEXT: --version Print version information
+! CHECK-NEXT: -W<warning>            Enable the specified warning
 ! CHECK-NEXT: -Xflang <arg>          Pass <arg> to the flang compiler
 
 !-------------------------------------------------------------
diff --git a/test/Driver/driver-help.f90 b/test/Driver/driver-help.f90
index 855d6f2..ba755ef 100644
--- a/test/Driver/driver-help.f90
+++ b/test/Driver/driver-help.f90
@@ -51,6 +51,7 @@
 ! HELP-NEXT: -std=<value>           Language standard to compile for
 ! HELP-NEXT: -U <macro>             Undefine macro <macro>
 ! HELP-NEXT: --version              Print version information
+! HELP-NEXT: -W<warning>            Enable the specified warning
 ! HELP-NEXT: -Xflang <arg>          Pass <arg> to the flang compiler
 
 !-------------------------------------------------------------
@@ -103,6 +104,7 @@
 ! HELP-FC1-NEXT: -test-io               Run the InputOuputTest action. Use for development and testing only.
 ! HELP-FC1-NEXT: -U <macro>             Undefine macro <macro>
 ! HELP-FC1-NEXT: --version              Print version information
+! HELP-FC1-NEXT: -W<warning>            Enable the specified warning
 
 !---------------
 ! EXPECTED ERROR
diff --git a/test/Driver/werror_parse.f b/test/Driver/werror_parse.f
new file mode 100644
index 0000000..f5425f5
--- /dev/null
+++ b/test/Driver/werror_parse.f
@@ -0,0 +1,33 @@
+! Ensure argument -Werror work as expected, this file checks for the functional correctness for
+! actions that extend the PrescanAndSemaAction, particularly for Semantic warnings/errors.
+! Multiple RUN lines are added to make sure that the behavior is consistent across multiple actions.
+
+! RUN: not %flang_fc1 -fsyntax-only -std=f2018 -Werror %s  2>&1 | FileCheck %s --check-prefix=WITH
+! RUN: not %flang_fc1 -fsyntax-only -std=f2018 -Werror -fdebug-dump-parse-tree %s  2>&1 | FileCheck %s --check-prefix=WITH
+! RUN: not %flang_fc1 -fsyntax-only -std=f2018 -Werror -fdebug-unparse-with-symbols %s  2>&1 | FileCheck %s --check-prefix=WITH
+! RUN: not %flang_fc1 -fsyntax-only -std=f2018 -Werror -fdebug-unparse %s  2>&1 | FileCheck %s --check-prefix=WITH
+! RUN: not %flang_fc1 -fsyntax-only -std=f2018 -Werror -fdebug-dump-symbols %s  2>&1 | FileCheck %s --check-prefix=WITH
+
+
+! RUN: %flang_fc1 -fsyntax-only -std=f2018 %s  2>&1 | FileCheck %s --allow-empty --check-prefix=WITHOUT
+! RUN: %flang_fc1 -fsyntax-only -std=f2018 -fdebug-dump-parse-tree %s  2>&1 | FileCheck %s --allow-empty --check-prefix=WITHOUT
+! RUN: %flang_fc1 -fsyntax-only -std=f2018 -fdebug-unparse-with-symbols %s  2>&1 | FileCheck %s --allow-empty --check-prefix=WITHOUT
+! RUN: %flang_fc1 -fsyntax-only -std=f2018 -fdebug-unparse %s  2>&1 | FileCheck %s --allow-empty --check-prefix=WITHOUT
+! RUN: %flang_fc1 -fsyntax-only -std=f2018 -fdebug-dump-symbols %s  2>&1 | FileCheck %s --allow-empty --check-prefix=WITHOUT
+
+!-----------------------------------------
+! EXPECTED OUTPUT WITH -Werror
+!-----------------------------------------
+! WITH: Could not parse
+
+!-----------------------------------------
+! EXPECTED OUTPUT WITHOUT -Werror
+!-----------------------------------------
+! WITHOUT-NOT: Could not parse
+
+#ifndef _OM_NO_IOSTREAM
+#ifdef WIN32
+#ifndef USE_IOSTREAM
+#define USE_IOSTREAM
+#endif USE_IOSTREAM
+#endif WIN32
diff --git a/test/Driver/werror_scan.f b/test/Driver/werror_scan.f
new file mode 100644
index 0000000..8643635
--- /dev/null
+++ b/test/Driver/werror_scan.f
@@ -0,0 +1,25 @@
+! Ensure argument -Werror work as expected, this file checks for the functional correctness for
+! actions that extend the PrescanAction
+! Multiple RUN lines are added to make sure that the behavior is consistent across multiple actions.
+
+! RUN: not %flang_fc1 -fsyntax-only -E -Werror %s  2>&1 | FileCheck %s --check-prefix=WITH
+! RUN: not %flang_fc1 -fsyntax-only -fdebug-dump-parsing-log -Werror %s  2>&1 | FileCheck %s --check-prefix=WITH
+! RUN: not %flang_fc1 -fsyntax-only -fdebug-dump-provenance -Werror %s  2>&1 | FileCheck %s --check-prefix=WITH
+! RUN: not %flang_fc1 -fsyntax-only -fdebug-measure-parse-tree -Werror %s  2>&1 | FileCheck %s --check-prefix=WITH
+! RUN: %flang_fc1 -fsyntax-only -E %s  2>&1 | FileCheck %s --allow-empty --check-prefix=WITHOUT
+! RUN: %flang_fc1 -fsyntax-only -fdebug-dump-parsing-log %s  2>&1 | FileCheck %s --check-prefix=WITHOUT
+! RUN: %flang_fc1 -fsyntax-only -fdebug-dump-provenance %s  2>&1 | FileCheck %s --check-prefix=WITHOUT
+! RUN: %flang_fc1 -fsyntax-only -fdebug-measure-parse-tree %s  2>&1 | FileCheck %s --check-prefix=WITHOUT
+
+!-----------------------------------------
+! EXPECTED OUTPUT WITH -Werror
+!-----------------------------------------
+! WITH: Could not scan
+
+!-----------------------------------------
+! EXPECTED OUTPUT WITHOUT -Werror
+!-----------------------------------------
+! WITHOUT-NOT: Could not scan
+
+1 continue
+end
diff --git a/test/Driver/werror_sema.f90 b/test/Driver/werror_sema.f90
new file mode 100644
index 0000000..1a7b6cc
--- /dev/null
+++ b/test/Driver/werror_sema.f90
@@ -0,0 +1,31 @@
+! Ensure argument -Werror work as expected, this file checks for the functional correctness for
+! actions that extend the PrescanAndSemaAction, particularly for Semantic warnings/errors.
+! Multiple RUN lines are added to make sure that the behavior is consistent across multiple actions.
+
+! RUN: not %flang_fc1 -fsyntax-only -Werror %s  2>&1 | FileCheck %s --check-prefix=WITH
+! RUN: not %flang_fc1 -fsyntax-only -Werror -fdebug-dump-parse-tree %s  2>&1 | FileCheck %s --check-prefix=WITH
+! RUN: not %flang_fc1 -fsyntax-only -Werror -fdebug-unparse-with-symbols %s  2>&1 | FileCheck %s --check-prefix=WITH
+! RUN: not %flang_fc1 -fsyntax-only -Werror -fdebug-unparse %s  2>&1 | FileCheck %s --check-prefix=WITH
+! RUN: not %flang_fc1 -fsyntax-only -Werror -fdebug-dump-symbols %s  2>&1 | FileCheck %s --check-prefix=WITH
+
+
+! RUN: %flang_fc1 -fsyntax-only %s  2>&1 | FileCheck %s --allow-empty --check-prefix=WITHOUT
+! RUN: %flang_fc1 -fsyntax-only -fdebug-dump-parse-tree %s  2>&1 | FileCheck %s --allow-empty --check-prefix=WITHOUT
+! RUN: %flang_fc1 -fsyntax-only -fdebug-unparse-with-symbols %s  2>&1 | FileCheck %s --allow-empty --check-prefix=WITHOUT
+! RUN: %flang_fc1 -fsyntax-only -fdebug-unparse %s  2>&1 | FileCheck %s --allow-empty --check-prefix=WITHOUT
+! RUN: %flang_fc1 -fsyntax-only -fdebug-dump-symbols %s  2>&1 | FileCheck %s --allow-empty --check-prefix=WITHOUT
+
+!-----------------------------------------
+! EXPECTED OUTPUT WITH -Werror
+!-----------------------------------------
+! WITH: Semantic errors in
+
+!-----------------------------------------
+! EXPECTED OUTPUT WITHOUT -Werror
+!-----------------------------------------
+! WITHOUT-NOT: Semantic errors in
+
+PROGRAM werror
+REAL, DIMENSION(20, 10) :: A
+FORALL (J=1:N)  A(I, I) = 1
+END PROGRAM werror
diff --git a/test/Driver/werror_wrong.f90 b/test/Driver/werror_wrong.f90
new file mode 100644
index 0000000..3151bb1
--- /dev/null
+++ b/test/Driver/werror_wrong.f90
@@ -0,0 +1,9 @@
+! Ensure that only argument -Werror is supported.
+
+! RUN: not %flang_fc1 -fsyntax-only -Wall %s  2>&1 | FileCheck %s --check-prefix=WRONG
+! RUN: not %flang_fc1 -fsyntax-only -WX %s  2>&1 | FileCheck %s --check-prefix=WRONG
+
+!-----------------------------------------
+! EXPECTED OUTPUT WITH -W<opt>
+!-----------------------------------------
+! WRONG: Only `-Werror` is supported currently.
diff --git a/tools/f18/f18.cpp b/tools/f18/f18.cpp
index 9c5e6c9..92d04dd 100644
--- a/tools/f18/f18.cpp
+++ b/tools/f18/f18.cpp
@@ -511,8 +511,14 @@
     } else if (arg == "-fopenmp") {
       options.features.Enable(Fortran::common::LanguageFeature::OpenMP);
       predefinitions.emplace_back("_OPENMP", "201511");
-    } else if (arg == "-Werror") {
-      driver.warningsAreErrors = true;
+    } else if (arg.find("-W") != std::string::npos) {
+      if (arg == "-Werror")
+        driver.warningsAreErrors = true;
+      else {
+        // Only -Werror is supported currently
+        llvm::errs() << "Only `-Werror` is supported currently.\n";
+        return EXIT_FAILURE;
+      }
     } else if (arg == "-ed") {
       options.features.Enable(Fortran::common::LanguageFeature::OldDebugLines);
     } else if (arg == "-E") {