[clang] [flang] [flang/clang] Adding use of Clang's diagnostics in Flang (PR #130593)

Jean-Didier PAILLEUX via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 10 05:44:46 PDT 2025


https://github.com/JDPailleux created https://github.com/llvm/llvm-project/pull/130593

Hello,

Here's a proposal to support diagnostics in Flang using the `DiagnosticEngine` provided by Clang. The goal is to have a shared  diagnostic system between Clang and Flang. A small modification has been made to the tablegen to indicate whether or not a “Warning/Error” diagnostic is supported by Clang and/or Flang (by default for those already existing and unmodified, Clang is indicated as being the only compiler that supports the diagnostic). The addition of these 2 booleans was motivated by the fact that certain warning options (such as `-Wunused-dummy-argument`) are only supported by Flang and therefore cannot be used by Clang. In addition, this will make it possible to indicate warning options not yet supported by Flang. The Flang driver has been modified to support the correct `DiagnosticEngine`.


>From ad687856445ce2aea2c9ea57e052d70e34f139d9 Mon Sep 17 00:00:00 2001
From: Jean-Didier Pailleux <jean-didier.pailleux at sipearl.com>
Date: Mon, 10 Mar 2025 13:40:30 +0100
Subject: [PATCH] [flang/clang] Adding use of Clang's diagnostics in Flang

---
 clang/include/clang/Basic/Diagnostic.td       |   5 +-
 .../clang/Basic/DiagnosticCategories.h        |   3 +-
 clang/include/clang/Basic/DiagnosticIDs.h     |   5 +
 clang/lib/Basic/DiagnosticIDs.cpp             |  14 ++-
 clang/lib/Basic/Warnings.cpp                  |   9 ++
 clang/test/TableGen/DiagnosticBase.inc        |   4 +-
 clang/tools/diagtool/DiagnosticNames.cpp      |   3 +-
 .../TableGen/ClangDiagnosticsEmitter.cpp      |   4 +
 .../flang/Frontend/CompilerInvocation.h       |  10 +-
 flang/include/flang/Semantics/semantics.h     |   5 +-
 flang/include/flang/Semantics/symbol.h        |   3 +
 flang/lib/Frontend/CMakeLists.txt             |   1 +
 flang/lib/Frontend/CompilerInstance.cpp       |   3 +-
 flang/lib/Frontend/CompilerInvocation.cpp     |  50 +++++----
 flang/lib/Frontend/Warnings.cpp               | 102 ++++++++++++++++++
 flang/lib/Semantics/CMakeLists.txt            |   3 +
 flang/lib/Semantics/semantics.cpp             |   4 +-
 flang/test/Driver/w-arg-unknown.f90           |   7 ++
 flang/test/Driver/werror-wrong.f90            |   6 --
 flang/test/Driver/wextra-ok.f90               |   4 +-
 flang/tools/bbc/CMakeLists.txt                |   5 +
 flang/tools/bbc/bbc.cpp                       |  14 ++-
 flang/tools/flang-driver/driver.cpp           |  10 +-
 flang/tools/flang-driver/fc1_main.cpp         |  19 ++--
 24 files changed, 242 insertions(+), 51 deletions(-)
 create mode 100644 flang/lib/Frontend/Warnings.cpp
 create mode 100644 flang/test/Driver/w-arg-unknown.f90
 delete mode 100644 flang/test/Driver/werror-wrong.f90

diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td
index 0b8b3af939ba0..b3a2e76ddc453 100644
--- a/clang/include/clang/Basic/Diagnostic.td
+++ b/clang/include/clang/Basic/Diagnostic.td
@@ -55,11 +55,14 @@ class DiagCategory<string Name> {
 }
 
 // Diagnostic Groups.
-class DiagGroup<string Name, list<DiagGroup> subgroups = [], code docs = [{}]> {
+class DiagGroup<string Name, list<DiagGroup> subgroups = [], code docs = [{}],
+                bit clangDiag = 1, bit flangDiag = 0> {
   string GroupName = Name;
   list<DiagGroup> SubGroups = subgroups;
   string CategoryName = "";
   code Documentation = docs;
+  bit IsClangDiag = clangDiag;
+  bit IsFlangDiag = flangDiag;
 }
 class InGroup<DiagGroup G> { DiagGroup Group = G; }
 //class IsGroup<string Name> { DiagGroup Group = DiagGroup<Name>; }
diff --git a/clang/include/clang/Basic/DiagnosticCategories.h b/clang/include/clang/Basic/DiagnosticCategories.h
index 839f8dee3ca89..5e44889a2fcd8 100644
--- a/clang/include/clang/Basic/DiagnosticCategories.h
+++ b/clang/include/clang/Basic/DiagnosticCategories.h
@@ -21,7 +21,8 @@ namespace clang {
     };
 
     enum class Group {
-#define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs)    \
+#define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs,    \
+                       IsClang, IsFlang)                                       \
       GroupName,
 #include "clang/Basic/DiagnosticGroups.inc"
 #undef CATEGORY
diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h
index 017ef7065610f..e57500fb85d3c 100644
--- a/clang/include/clang/Basic/DiagnosticIDs.h
+++ b/clang/include/clang/Basic/DiagnosticIDs.h
@@ -365,6 +365,11 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
   /// Given a diagnostic group ID, return its documentation.
   static StringRef getWarningOptionDocumentation(diag::Group GroupID);
 
+  /// Given a diagnostic group ID, return true if its a Flang warning.
+  static bool isFlangWarningOption(diag::Group Group);
+  /// Given a diagnostic group ID, return true if its a Clang warning.
+  static bool isClangWarningOption(diag::Group Group);
+
   void setGroupSeverity(StringRef Group, diag::Severity);
   void setGroupNoWarningsAsError(StringRef Group, bool);
 
diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp
index ca5b8d2da769e..e275f7165b392 100644
--- a/clang/lib/Basic/DiagnosticIDs.cpp
+++ b/clang/lib/Basic/DiagnosticIDs.cpp
@@ -585,6 +585,8 @@ namespace {
     uint16_t Members;
     uint16_t SubGroups;
     StringRef Documentation;
+    bool IsClangDiag;
+    bool IsFlangDiag;
 
     StringRef getName() const { return DiagGroupNames[NameOffset]; }
   };
@@ -592,8 +594,9 @@ namespace {
 
 // Second the table of options, sorted by name for fast binary lookup.
 static const WarningOption OptionTable[] = {
-#define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs)        \
-  {FlagNameOffset, Members, SubGroups, Docs},
+#define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs,        \
+                   IsClang, IsFlang)                                           \
+  {FlagNameOffset, Members, SubGroups, Docs, IsClang, IsFlang},
 #include "clang/Basic/DiagnosticGroups.inc"
 #undef DIAG_ENTRY
 };
@@ -607,6 +610,13 @@ StringRef DiagnosticIDs::getWarningOptionForGroup(diag::Group Group) {
   return OptionTable[static_cast<int>(Group)].getName();
 }
 
+bool DiagnosticIDs::isFlangWarningOption(diag::Group Group) {
+  return OptionTable[static_cast<int>(Group)].IsFlangDiag;
+}
+
+bool DiagnosticIDs::isClangWarningOption(diag::Group Group) {
+  return OptionTable[static_cast<int>(Group)].IsClangDiag;
+}
 std::optional<diag::Group>
 DiagnosticIDs::getGroupForWarningOption(StringRef Name) {
   const auto *Found = llvm::partition_point(
diff --git a/clang/lib/Basic/Warnings.cpp b/clang/lib/Basic/Warnings.cpp
index 5f48e0ec81554..e6c1355650113 100644
--- a/clang/lib/Basic/Warnings.cpp
+++ b/clang/lib/Basic/Warnings.cpp
@@ -106,6 +106,15 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
       diag::Severity Mapping =
           isPositive ? diag::Severity::Warning : diag::Severity::Ignored;
 
+      // Check if the warning option is valid for Clang
+      std::optional<diag::Group> Group = DiagIDs->getGroupForWarningOption(Opt);
+      if (Group.has_value() && !DiagIDs->isClangWarningOption(Group.value())) {
+        const unsigned DiagID = Diags.getCustomDiagID(
+            DiagnosticsEngine::Error,
+            "Warning option \"%0\" is valid for Fortran but not for C++.");
+        Diags.Report(DiagID) << Opt;
+      }
+
       // -Wsystem-headers is a special case, not driven by the option table.  It
       // cannot be controlled with -Werror.
       if (Opt == "system-headers") {
diff --git a/clang/test/TableGen/DiagnosticBase.inc b/clang/test/TableGen/DiagnosticBase.inc
index 2fc7bb4266edb..359c6d1edd79a 100644
--- a/clang/test/TableGen/DiagnosticBase.inc
+++ b/clang/test/TableGen/DiagnosticBase.inc
@@ -55,11 +55,13 @@ class DiagCategory<string Name> {
 }
 
 // Diagnostic Groups.
-class DiagGroup<string Name, list<DiagGroup> subgroups = []> {
+class DiagGroup<string Name, list<DiagGroup> subgroups = [], bit clangDiag = 1, bit flangDiag = 0> {
   string GroupName = Name;
   list<DiagGroup> SubGroups = subgroups;
   string CategoryName = "";
   code Documentation = [{}];
+  bit IsClangDiag = clangDiag;
+  bit IsFlangDiag = flangDiag;
 }
 class InGroup<DiagGroup G> { DiagGroup Group = G; }
 //class IsGroup<string Name> { DiagGroup Group = DiagGroup<Name>; }
diff --git a/clang/tools/diagtool/DiagnosticNames.cpp b/clang/tools/diagtool/DiagnosticNames.cpp
index 4ac9825848ef3..95b9da461599b 100644
--- a/clang/tools/diagtool/DiagnosticNames.cpp
+++ b/clang/tools/diagtool/DiagnosticNames.cpp
@@ -62,7 +62,8 @@ const DiagnosticRecord &diagtool::getDiagnosticForID(short DiagID) {
 
 // Second the table of options, sorted by name for fast binary lookup.
 static const GroupRecord OptionTable[] = {
-#define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs)        \
+#define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs,        \
+                   IsClang, IsFlang)                                           \
   {FlagNameOffset, Members, SubGroups},
 #include "clang/Basic/DiagnosticGroups.inc"
 #undef DIAG_ENTRY
diff --git a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
index 8f846a4744bbf..0c0962f6fceaa 100644
--- a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
+++ b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
@@ -1890,6 +1890,10 @@ static void emitDiagTable(DiagsInGroupTy &DiagsInGroup,
 
     OS << "R\"(" << StringRef(Documentation).trim() << ")\"";
 
+    OS << ", /*IsClangDiag*/ "
+       << bool(GroupInfo.Defs.back()->getValueAsBit("IsClangDiag"));
+    OS << ", /*IsFlangDiag*/ "
+       << bool(GroupInfo.Defs.back()->getValueAsBit("IsFlangDiag"));
     OS << ")\n";
   }
   OS << "#endif // DIAG_ENTRY\n\n";
diff --git a/flang/include/flang/Frontend/CompilerInvocation.h b/flang/include/flang/Frontend/CompilerInvocation.h
index 9e6724be33033..9fdaeac3d999a 100644
--- a/flang/include/flang/Frontend/CompilerInvocation.h
+++ b/flang/include/flang/Frontend/CompilerInvocation.h
@@ -33,12 +33,18 @@ class TargetMachine;
 
 namespace Fortran::frontend {
 
+/// processWarningOptions - Initialize the diagnostic client and process the
+/// warning options specified on the command line.
+void processWarningOptions(clang::DiagnosticsEngine &Diags,
+                           const clang::DiagnosticOptions &Opts);
+
 /// Fill out Opts based on the options given in Args.
 ///
 /// When errors are encountered, return false and, if Diags is non-null,
 /// report the error(s).
 bool parseDiagnosticArgs(clang::DiagnosticOptions &opts,
-                         llvm::opt::ArgList &args);
+                         llvm::opt::ArgList &args,
+                         bool defaultDiagColor = true);
 
 class CompilerInvocationBase {
 public:
@@ -174,7 +180,7 @@ class CompilerInvocation : public CompilerInvocationBase {
   /// Creates and configures semantics context based on the compilation flags.
   std::unique_ptr<Fortran::semantics::SemanticsContext>
   getSemanticsCtx(Fortran::parser::AllCookedSources &allCookedSources,
-                  const llvm::TargetMachine &);
+                  const llvm::TargetMachine &, clang::DiagnosticsEngine &diag);
 
   std::string &getModuleDir() { return moduleDir; }
   const std::string &getModuleDir() const { return moduleDir; }
diff --git a/flang/include/flang/Semantics/semantics.h b/flang/include/flang/Semantics/semantics.h
index 730513dbe3232..72ecd046f4048 100644
--- a/flang/include/flang/Semantics/semantics.h
+++ b/flang/include/flang/Semantics/semantics.h
@@ -19,6 +19,7 @@
 #include "flang/Parser/message.h"
 #include "flang/Support/Fortran-features.h"
 #include "flang/Support/LangOptions.h"
+#include "clang/Basic/Diagnostic.h"
 #include <iosfwd>
 #include <set>
 #include <string>
@@ -68,7 +69,7 @@ class SemanticsContext {
 public:
   SemanticsContext(const common::IntrinsicTypeDefaultKinds &,
       const common::LanguageFeatureControl &, const common::LangOptions &,
-      parser::AllCookedSources &);
+      parser::AllCookedSources &, clang::DiagnosticsEngine &);
   ~SemanticsContext();
 
   const common::IntrinsicTypeDefaultKinds &defaultKinds() const {
@@ -82,6 +83,7 @@ class SemanticsContext {
   int doublePrecisionKind() const {
     return defaultKinds_.doublePrecisionKind();
   }
+  clang::DiagnosticsEngine &getDiagnostics() const { return diags_; }
   int quadPrecisionKind() const { return defaultKinds_.quadPrecisionKind(); }
   bool IsEnabled(common::LanguageFeature feature) const {
     return languageFeatures_.IsEnabled(feature);
@@ -305,6 +307,7 @@ class SemanticsContext {
   const common::IntrinsicTypeDefaultKinds &defaultKinds_;
   const common::LanguageFeatureControl &languageFeatures_;
   const common::LangOptions &langOpts_;
+  clang::DiagnosticsEngine &diags_;
   parser::AllCookedSources &allCookedSources_;
   std::optional<parser::CharBlock> location_;
   std::vector<std::string> searchDirectories_;
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 715811885c219..a14ca19241823 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -293,10 +293,13 @@ class EntityDetails : public WithBindName {
   void set_isDummy(bool value = true) { isDummy_ = value; }
   bool isFuncResult() const { return isFuncResult_; }
   void set_funcResult(bool x) { isFuncResult_ = x; }
+  bool isUsed() const { return isUsed_; }
+  void set_isUsed() { isUsed_ = true; }
 
 private:
   bool isDummy_{false};
   bool isFuncResult_{false};
+  bool isUsed_{false};
   const DeclTypeSpec *type_{nullptr};
   friend llvm::raw_ostream &operator<<(
       llvm::raw_ostream &, const EntityDetails &);
diff --git a/flang/lib/Frontend/CMakeLists.txt b/flang/lib/Frontend/CMakeLists.txt
index 80d63fca6fb76..1d49b91db092a 100644
--- a/flang/lib/Frontend/CMakeLists.txt
+++ b/flang/lib/Frontend/CMakeLists.txt
@@ -11,6 +11,7 @@ add_flang_library(flangFrontend
   TextDiagnosticPrinter.cpp
   TextDiagnosticBuffer.cpp
   TextDiagnostic.cpp
+  Warnings.cpp
 
   DEPENDS
   CUFDialect
diff --git a/flang/lib/Frontend/CompilerInstance.cpp b/flang/lib/Frontend/CompilerInstance.cpp
index b1fa32ecb4cfc..6374a8b0d45bd 100644
--- a/flang/lib/Frontend/CompilerInstance.cpp
+++ b/flang/lib/Frontend/CompilerInstance.cpp
@@ -163,7 +163,8 @@ bool CompilerInstance::executeAction(FrontendAction &act) {
   if (!setUpTargetMachine())
     return false;
   // Create the semantics context
-  semaContext = invoc.getSemanticsCtx(*allCookedSources, getTargetMachine());
+  semaContext = invoc.getSemanticsCtx(*allCookedSources, getTargetMachine(),
+                                      getDiagnostics());
   // Set options controlling lowering to FIR.
   invoc.setLoweringOptions();
 
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 8b07a50824899..cee4b7c053f9e 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -33,6 +33,7 @@
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
 #include "llvm/Support/CodeGen.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FileUtilities.h"
@@ -119,9 +120,26 @@ static unsigned getOptimizationLevel(llvm::opt::ArgList &args,
 }
 
 bool Fortran::frontend::parseDiagnosticArgs(clang::DiagnosticOptions &opts,
-                                            llvm::opt::ArgList &args) {
-  opts.ShowColors = parseShowColorsArgs(args);
-
+                                            llvm::opt::ArgList &args,
+                                            bool defaultDiagColor) {
+  opts.ShowColors = parseShowColorsArgs(args, defaultDiagColor);
+
+  for (llvm::opt::Arg *A : args.filtered(clang::driver::options::OPT_W_Group)) {
+    if (A->getOption().getKind() == llvm::opt::Option::FlagClass) {
+      // The argument is a pure flag (such as OPT_Wall).
+      opts.Warnings.push_back(
+          std::string(A->getOption().getName().drop_front(1)));
+    } else if (A->getOption().matches(
+                   clang::driver::options::OPT_W_value_Group)) {
+      // This is -Wfoo= where foo is the name of the diagnostic group.
+      // Add only the group name to the diagnostics.
+      opts.Warnings.push_back(
+          std::string(A->getOption().getName().drop_front(1).rtrim("=-")));
+    } else {
+      // Otherwise, add its value for OPT_W_Joined.
+      opts.Warnings.push_back(A->getValue());
+    }
+  }
   return true;
 }
 
@@ -927,22 +945,11 @@ static bool parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
                           clang::DiagnosticsEngine &diags) {
   unsigned numErrorsBefore = diags.getNumErrors();
 
-  // -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)) {
-    const auto &wArgs =
-        args.getAllArgValues(clang::driver::options::OPT_W_Joined);
-    for (const auto &wArg : wArgs) {
-      if (wArg == "error") {
-        res.setWarnAsErr(true);
-      } else {
-        const unsigned diagID =
-            diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
-                                  "Only `-Werror` is supported currently.");
-        diags.Report(diagID);
-      }
-    }
+  // Handle warning Diagnostic for the frontend.
+  clang::DiagnosticOptions &diagOpts = res.getDiagnosticOpts();
+  for (const auto &warning : diagOpts.Warnings) {
+    if (warning == "error")
+      res.setWarnAsErr(true);
   }
 
   // Default to off for `flang -fc1`.
@@ -1030,6 +1037,7 @@ static bool parseDialectArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
   if (args.hasArg(clang::driver::options::OPT_pedantic)) {
     res.setEnableConformanceChecks();
     res.setEnableUsageChecks();
+    res.getDiagnosticOpts().Pedantic = true;
   }
 
   // -w
@@ -1650,12 +1658,12 @@ void CompilerInvocation::setFortranOpts() {
 std::unique_ptr<Fortran::semantics::SemanticsContext>
 CompilerInvocation::getSemanticsCtx(
     Fortran::parser::AllCookedSources &allCookedSources,
-    const llvm::TargetMachine &targetMachine) {
+    const llvm::TargetMachine &targetMachine, clang::DiagnosticsEngine &diags) {
   auto &fortranOptions = getFortranOpts();
 
   auto semanticsContext = std::make_unique<semantics::SemanticsContext>(
       getDefaultKinds(), fortranOptions.features, getLangOpts(),
-      allCookedSources);
+      allCookedSources, diags);
 
   semanticsContext->set_moduleDirectory(getModuleDir())
       .set_searchDirectories(fortranOptions.searchDirectories)
diff --git a/flang/lib/Frontend/Warnings.cpp b/flang/lib/Frontend/Warnings.cpp
new file mode 100644
index 0000000000000..70e93efdd866d
--- /dev/null
+++ b/flang/lib/Frontend/Warnings.cpp
@@ -0,0 +1,102 @@
+//===--- Warnings.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Command line warning options handler.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is responsible for handling all warning options. This includes
+// a number of -Wfoo options and their variants, which are driven by TableGen-
+// generated data, and the special cases -pedantic, -pedantic-errors, -w,
+// -Werror, ...
+//
+// Each warning option controls any number of actual warnings.
+// Given a warning option 'foo', the following are valid:
+//    -Wfoo, -Wno-foo, -Werror=foo
+//
+#include "clang/Basic/AllDiagnostics.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticDriver.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "llvm/ADT/StringRef.h"
+#include <cstring>
+
+namespace Fortran::frontend {
+
+// EmitUnknownDiagWarning - Emit a warning and typo hint for unknown warning
+// opts
+
+static void EmitUnknownDiagWarning(clang::DiagnosticsEngine &diags,
+                                   clang::diag::Flavor flavor,
+                                   llvm::StringRef prefix,
+                                   llvm::StringRef opt) {
+  llvm::StringRef suggestion =
+      clang::DiagnosticIDs::getNearestOption(flavor, opt);
+  diags.Report(clang::diag::warn_unknown_diag_option)
+      << (flavor == clang::diag::Flavor::WarningOrError ? 0 : 1)
+      << (prefix.str() += std::string(opt)) << !suggestion.empty()
+      << (prefix.str() += std::string(suggestion));
+}
+
+void processWarningOptions(clang::DiagnosticsEngine &diags,
+                           const clang::DiagnosticOptions &opts) {
+  diags.setIgnoreAllWarnings(opts.IgnoreWarnings);
+  diags.setShowColors(opts.ShowColors);
+
+  // If -pedantic or -pedantic-errors was specified, then we want to map all
+  // extension diagnostics onto WARNING or ERROR.
+  if (opts.PedanticErrors)
+    diags.setExtensionHandlingBehavior(clang::diag::Severity::Error);
+  else if (opts.Pedantic)
+    diags.setExtensionHandlingBehavior(clang::diag::Severity::Warning);
+  else
+    diags.setExtensionHandlingBehavior(clang::diag::Severity::Ignored);
+
+  llvm::SmallVector<clang::diag::kind, 10> _diags;
+  const llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagIDs =
+      diags.getDiagnosticIDs();
+  for (unsigned i = 0, e = opts.Warnings.size(); i != e; ++i) {
+    const auto flavor = clang::diag::Flavor::WarningOrError;
+    llvm::StringRef opt = opts.Warnings[i];
+
+    // Check to see if this warning starts with "no-", if so, this is a
+    // negative form of the option.
+    bool isPositive = !opt.consume_front("no-");
+
+    // Figure out how this option affects the warning.  If -Wfoo, map the
+    // diagnostic to a warning, if -Wno-foo, map it to ignore.
+    clang::diag::Severity mapping = isPositive ? clang::diag::Severity::Warning
+                                               : clang::diag::Severity::Ignored;
+
+    // -Werror/-Wno-error is a special case, not controlled by the option table.
+    // TODO: Adding support of "specifier" form of -Werror=foo.
+    if (opt == "error") {
+      diags.setWarningsAsErrors(isPositive);
+      continue;
+    }
+
+    if (std::optional<clang::diag::Group> group =
+            diagIDs->getGroupForWarningOption(opt)) {
+      if (!diagIDs->isFlangWarningOption(group.value())) {
+        // Warning option not supported by Flang
+        // FIXME : Updating diagnostic error message when all warning options
+        // will be supported
+        const unsigned diagID =
+            diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
+                                  "Warning option \"%0\" not supported.");
+        diags.Report(diagID) << opt;
+      }
+    } else {
+      // Unkown warning option.
+      EmitUnknownDiagWarning(diags, flavor, isPositive ? "-W" : "-Wno-", opt);
+    }
+    diags.setSeverityForGroup(flavor, opt, mapping);
+  }
+}
+} // namespace Fortran::frontend
diff --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt
index 93bf0c7c5facd..90c34d9a8766c 100644
--- a/flang/lib/Semantics/CMakeLists.txt
+++ b/flang/lib/Semantics/CMakeLists.txt
@@ -63,4 +63,7 @@ add_flang_library(FortranSemantics
   FrontendOpenMP
   FrontendOpenACC
   TargetParser
+
+  CLANG_LIBS
+  clangBasic
 )
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index 10a01039ea0ae..4b4293e7eb006 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -346,9 +346,9 @@ SemanticsContext::SemanticsContext(
     const common::IntrinsicTypeDefaultKinds &defaultKinds,
     const common::LanguageFeatureControl &languageFeatures,
     const common::LangOptions &langOpts,
-    parser::AllCookedSources &allCookedSources)
+    parser::AllCookedSources &allCookedSources, clang::DiagnosticsEngine &diags)
     : defaultKinds_{defaultKinds}, languageFeatures_{languageFeatures},
-      langOpts_{langOpts}, allCookedSources_{allCookedSources},
+      langOpts_{langOpts}, allCookedSources_{allCookedSources}, diags_{diags},
       intrinsics_{evaluate::IntrinsicProcTable::Configure(defaultKinds_)},
       globalScope_{*this}, intrinsicModulesScope_{globalScope_.MakeScope(
                                Scope::Kind::IntrinsicModules, nullptr)},
diff --git a/flang/test/Driver/w-arg-unknown.f90 b/flang/test/Driver/w-arg-unknown.f90
new file mode 100644
index 0000000000000..19b39f6cb3b33
--- /dev/null
+++ b/flang/test/Driver/w-arg-unknown.f90
@@ -0,0 +1,7 @@
+! Ensure that unknown warning options generate a warning message. 
+
+! RUN: %flang_fc1 -fsyntax-only -WX %s  2>&1 | FileCheck %s --check-prefix=UNKNOWN1
+! RUN: %flang_fc1 -fsyntax-only -Werror2 %s  2>&1 | FileCheck %s --check-prefix=UNKNOWN2
+
+! UNKNOWN1: unknown warning option '-WX'
+! UNKNOWN2: unknown warning option '-Werror2'
diff --git a/flang/test/Driver/werror-wrong.f90 b/flang/test/Driver/werror-wrong.f90
deleted file mode 100644
index 58adf6f745d5e..0000000000000
--- a/flang/test/Driver/werror-wrong.f90
+++ /dev/null
@@ -1,6 +0,0 @@
-! 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
-
-! WRONG: Only `-Werror` is supported currently.
diff --git a/flang/test/Driver/wextra-ok.f90 b/flang/test/Driver/wextra-ok.f90
index 441029aa0af27..63c18c3ad2b12 100644
--- a/flang/test/Driver/wextra-ok.f90
+++ b/flang/test/Driver/wextra-ok.f90
@@ -2,10 +2,10 @@
 ! The first check should be changed if -Wextra is implemented
 
 ! RUN: %flang -std=f2018 -Wextra %s -c 2>&1 | FileCheck %s --check-prefix=CHECK-OK
-! RUN: not %flang -std=f2018 -Wblah -Wextra %s -c 2>&1 | FileCheck %s --check-prefix=WRONG
+! RUN: %flang -std=f2018 -Wblah -Wextra %s -c 2>&1 | FileCheck %s --check-prefix=WRONG
 
 ! CHECK-OK: the warning option '-Wextra' is not supported
-! WRONG: Only `-Werror` is supported currently.
+! WRONG: unknown warning option '-Wblah' 
 
 program wextra_ok
 end program wextra_ok
diff --git a/flang/tools/bbc/CMakeLists.txt b/flang/tools/bbc/CMakeLists.txt
index f950f03920d3f..7c6ac4220ca33 100644
--- a/flang/tools/bbc/CMakeLists.txt
+++ b/flang/tools/bbc/CMakeLists.txt
@@ -31,6 +31,11 @@ target_link_libraries(bbc PRIVATE
   FlangOpenMPTransforms
 )
 
+clang_target_link_libraries(bbc PRIVATE
+  PRIVATE
+  clangBasic
+)
+
 mlir_target_link_libraries(bbc PRIVATE
   ${dialect_libs}
   ${extension_libs}
diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp
index 3b19a1c2a78d9..0a07eee2da839 100644
--- a/flang/tools/bbc/bbc.cpp
+++ b/flang/tools/bbc/bbc.cpp
@@ -15,7 +15,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "flang/Frontend/CodeGenOptions.h"
+#include "flang/Frontend/CompilerInstance.h"
+#include "flang/Frontend/CompilerInvocation.h"
 #include "flang/Frontend/TargetOptions.h"
+#include "flang/Frontend/TextDiagnosticBuffer.h"
 #include "flang/Lower/Bridge.h"
 #include "flang/Lower/PFTBuilder.h"
 #include "flang/Lower/Support/Verifier.h"
@@ -492,6 +495,15 @@ static llvm::LogicalResult convertFortranSourceToMLIR(
 }
 
 int main(int argc, char **argv) {
+  // Creating a SemanticsContext require a DiagnosticsEngine
+  Fortran::frontend::TextDiagnosticBuffer *diagsBuffer =
+      new Fortran::frontend::TextDiagnosticBuffer;
+  llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
+      new clang::DiagnosticIDs());
+  llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagOpts =
+      new clang::DiagnosticOptions();
+  clang::DiagnosticsEngine diags(diagID, &*diagOpts, diagsBuffer);
+
   [[maybe_unused]] llvm::InitLLVM y(argc, argv);
   llvm::InitializeAllTargets();
   llvm::InitializeAllTargetMCs();
@@ -573,7 +585,7 @@ int main(int argc, char **argv) {
   Fortran::parser::AllSources allSources;
   Fortran::parser::AllCookedSources allCookedSources(allSources);
   Fortran::semantics::SemanticsContext semanticsContext{
-      defaultKinds, options.features, langOpts, allCookedSources};
+      defaultKinds, options.features, langOpts, allCookedSources, diags};
   semanticsContext.set_moduleDirectory(moduleDir)
       .set_moduleFileSuffix(moduleSuffix)
       .set_searchDirectories(includeDirs)
diff --git a/flang/tools/flang-driver/driver.cpp b/flang/tools/flang-driver/driver.cpp
index ed52988feaa59..a6c616289f28a 100644
--- a/flang/tools/flang-driver/driver.cpp
+++ b/flang/tools/flang-driver/driver.cpp
@@ -115,12 +115,20 @@ int main(int argc, const char **argv) {
 
   // Create DiagnosticsEngine for the compiler driver
   llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagOpts =
-      createAndPopulateDiagOpts(args);
+      new clang::DiagnosticOptions();
   llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
       new clang::DiagnosticIDs());
   Fortran::frontend::TextDiagnosticPrinter *diagClient =
       new Fortran::frontend::TextDiagnosticPrinter(llvm::errs(), &*diagOpts);
 
+  // Use the DiagnosticsEngine instance of the frontend driver
+  // for parsing the arguments
+  unsigned missingArgIndex, missingArgCount;
+  llvm::opt::InputArgList args2 = clang::driver::getDriverOptTable().ParseArgs(
+      args, missingArgIndex, missingArgCount,
+      llvm::opt::Visibility(clang::driver::options::FlangOption));
+  Fortran::frontend::parseDiagnosticArgs(*diagOpts, args2);
+
   diagClient->setPrefix(
       std::string(llvm::sys::path::stem(getExecutablePath(args[0]))));
 
diff --git a/flang/tools/flang-driver/fc1_main.cpp b/flang/tools/flang-driver/fc1_main.cpp
index 561a0dd5524e3..af98d8b0c6993 100644
--- a/flang/tools/flang-driver/fc1_main.cpp
+++ b/flang/tools/flang-driver/fc1_main.cpp
@@ -20,6 +20,7 @@
 #include "flang/Frontend/CompilerInvocation.h"
 #include "flang/Frontend/TextDiagnosticBuffer.h"
 #include "flang/FrontendTool/Utils.h"
+#include "clang/Driver/Driver.h"
 #include "clang/Driver/DriverDiagnostic.h"
 #include "llvm/MC/TargetRegistry.h"
 #include "llvm/Option/Arg.h"
@@ -63,15 +64,17 @@ int fc1_main(llvm::ArrayRef<const char *> argv, const char *argv0) {
   // them using a well formed diagnostic object.
   TextDiagnosticBuffer *diagsBuffer = new TextDiagnosticBuffer;
 
-  // Create CompilerInvocation - use a dedicated instance of DiagnosticsEngine
+  // Use the DiagnosticsEngine instance of the frontend driver
   // for parsing the arguments
-  llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
-      new clang::DiagnosticIDs());
-  llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagOpts =
-      new clang::DiagnosticOptions();
-  clang::DiagnosticsEngine diags(diagID, &*diagOpts, diagsBuffer);
-  bool success = CompilerInvocation::createFromArgs(flang->getInvocation(),
-                                                    argv, diags, argv0);
+  unsigned missingArgIndex, missingArgCount;
+  llvm::opt::InputArgList args = clang::driver::getDriverOptTable().ParseArgs(
+      argv.slice(0), missingArgIndex, missingArgCount,
+      llvm::opt::Visibility(clang::driver::options::FC1Option));
+  parseDiagnosticArgs(flang->getDiagnosticOpts(), args, false);
+
+  bool success = CompilerInvocation::createFromArgs(
+      flang->getInvocation(), argv, flang->getDiagnostics(), argv0);
+  processWarningOptions(flang->getDiagnostics(), flang->getDiagnosticOpts());
 
   // Initialize targets first, so that --version shows registered targets.
   llvm::InitializeAllTargets();



More information about the cfe-commits mailing list