[flang-commits] [flang] [flang][cli] Add diagnostic flags to the CLI (PR #142022)

Andre Kuhlenschmidt via flang-commits flang-commits at lists.llvm.org
Thu May 29 12:57:35 PDT 2025


https://github.com/akuhlens updated https://github.com/llvm/llvm-project/pull/142022

>From 8f3fd2daab46f477e87043c66b3049dff4a5b20e Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 29 May 2025 12:11:04 -0700
Subject: [PATCH 1/3] initial commit

---
 flang/include/flang/Common/enum-class.h       |  47 ++++-
 .../include/flang/Support/Fortran-features.h  |  51 ++++--
 flang/lib/Frontend/CompilerInvocation.cpp     |  62 ++++---
 flang/lib/Support/CMakeLists.txt              |   1 +
 flang/lib/Support/Fortran-features.cpp        | 168 ++++++++++++++----
 flang/lib/Support/enum-class.cpp              |  24 +++
 flang/test/Driver/disable-diagnostic.f90      |  19 ++
 flang/test/Driver/werror-wrong.f90            |   7 +-
 flang/test/Driver/wextra-ok.f90               |   2 +-
 flang/unittests/Common/CMakeLists.txt         |   3 +
 flang/unittests/Common/EnumClassTests.cpp     |  45 +++++
 .../unittests/Common/FortranFeaturesTest.cpp  | 142 +++++++++++++++
 12 files changed, 483 insertions(+), 88 deletions(-)
 create mode 100644 flang/lib/Support/enum-class.cpp
 create mode 100644 flang/test/Driver/disable-diagnostic.f90
 create mode 100644 flang/unittests/Common/EnumClassTests.cpp
 create mode 100644 flang/unittests/Common/FortranFeaturesTest.cpp

diff --git a/flang/include/flang/Common/enum-class.h b/flang/include/flang/Common/enum-class.h
index 41575d45091a8..baf9fe418141d 100644
--- a/flang/include/flang/Common/enum-class.h
+++ b/flang/include/flang/Common/enum-class.h
@@ -18,8 +18,9 @@
 #define FORTRAN_COMMON_ENUM_CLASS_H_
 
 #include <array>
-#include <string>
-
+#include <functional>
+#include <optional>
+#include <string_view>
 namespace Fortran::common {
 
 constexpr std::size_t CountEnumNames(const char *p) {
@@ -58,15 +59,51 @@ constexpr std::array<std::string_view, ITEMS> EnumNames(const char *p) {
   return result;
 }
 
+template <typename F, typename T>
+std::optional<T> inline fmap(std::optional<F> x, std::function<T(const F)> f) {
+  return x ? std::optional<T>{f(*x)} : std::nullopt;
+}
+
+using Predicate = std::function<bool(const std::string_view)>;
+// Finds the first index for which the predicate returns true.
+std::optional<int> FindEnumIndex(
+    Predicate pred, int size, const std::string_view *names);
+
+using FindEnumIndexType = std::optional<int>(
+    Predicate, int, const std::string_view *);
+
+template <typename NAME>
+std::optional<NAME> inline FindEnum(
+    Predicate pred, std::function<std::optional<int>(Predicate)> find) {
+  std::function<NAME(int)> f = [](int x) { return static_cast<NAME>(x); };
+  return fmap(find(pred), f);
+}
+
 #define ENUM_CLASS(NAME, ...) \
   enum class NAME { __VA_ARGS__ }; \
   [[maybe_unused]] static constexpr std::size_t NAME##_enumSize{ \
       ::Fortran::common::CountEnumNames(#__VA_ARGS__)}; \
+  [[maybe_unused]] static constexpr std::array<std::string_view, \
+      NAME##_enumSize> NAME##_names{ \
+      ::Fortran::common::EnumNames<NAME##_enumSize>(#__VA_ARGS__)}; \
   [[maybe_unused]] static inline std::string_view EnumToString(NAME e) { \
-    static const constexpr auto names{ \
-        ::Fortran::common::EnumNames<NAME##_enumSize>(#__VA_ARGS__)}; \
-    return names[static_cast<std::size_t>(e)]; \
+    return NAME##_names[static_cast<std::size_t>(e)]; \
   }
 
+#define ENUM_CLASS_EXTRA(NAME) \
+  [[maybe_unused]] inline std::optional<int> Find##NAME##Index( \
+      ::Fortran::common::Predicate p) { \
+    return ::Fortran::common::FindEnumIndex( \
+        p, NAME##_enumSize, NAME##_names.data()); \
+  } \
+  [[maybe_unused]] inline std::optional<NAME> Find##NAME( \
+      ::Fortran::common::Predicate p) { \
+    return ::Fortran::common::FindEnum<NAME>(p, Find##NAME##Index); \
+  } \
+  [[maybe_unused]] inline std::optional<NAME> StringTo##NAME( \
+      const std::string_view name) { \
+    return Find##NAME( \
+        [name](const std::string_view s) -> bool { return name == s; }); \
+  }
 } // namespace Fortran::common
 #endif // FORTRAN_COMMON_ENUM_CLASS_H_
diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index e696da9042480..d5aa7357ffea0 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -12,6 +12,8 @@
 #include "Fortran.h"
 #include "flang/Common/enum-set.h"
 #include "flang/Common/idioms.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
 #include <optional>
 #include <vector>
 
@@ -79,12 +81,13 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
     NullActualForDefaultIntentAllocatable, UseAssociationIntoSameNameSubprogram,
     HostAssociatedIntentOutInSpecExpr, NonVolatilePointerToVolatile)
 
+// Generate default String -> Enum mapping.
+ENUM_CLASS_EXTRA(LanguageFeature)
+ENUM_CLASS_EXTRA(UsageWarning)
+
 using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
 using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
 
-std::optional<LanguageFeature> FindLanguageFeature(const char *);
-std::optional<UsageWarning> FindUsageWarning(const char *);
-
 class LanguageFeatureControl {
 public:
   LanguageFeatureControl();
@@ -97,8 +100,10 @@ class LanguageFeatureControl {
   void EnableWarning(UsageWarning w, bool yes = true) {
     warnUsage_.set(w, yes);
   }
-  void WarnOnAllNonstandard(bool yes = true) { warnAllLanguage_ = yes; }
-  void WarnOnAllUsage(bool yes = true) { warnAllUsage_ = yes; }
+  void WarnOnAllNonstandard(bool yes = true);
+  bool IsWarnOnAllNonstandard() const { return warnAllLanguage_; }
+  void WarnOnAllUsage(bool yes = true);
+  bool IsWarnOnAllUsage() const { return warnAllUsage_; }
   void DisableAllNonstandardWarnings() {
     warnAllLanguage_ = false;
     warnLanguage_.clear();
@@ -107,16 +112,16 @@ class LanguageFeatureControl {
     warnAllUsage_ = false;
     warnUsage_.clear();
   }
-
-  bool IsEnabled(LanguageFeature f) const { return !disable_.test(f); }
-  bool ShouldWarn(LanguageFeature f) const {
-    return (warnAllLanguage_ && f != LanguageFeature::OpenMP &&
-               f != LanguageFeature::OpenACC && f != LanguageFeature::CUDA) ||
-        warnLanguage_.test(f);
-  }
-  bool ShouldWarn(UsageWarning w) const {
-    return warnAllUsage_ || warnUsage_.test(w);
+  void DisableAllWarnings() {
+    disableAllWarnings_ = true;
+    DisableAllNonstandardWarnings();
+    DisableAllUsageWarnings();
   }
+  bool applyCLIOption(llvm::StringRef input);
+  bool AreWarningsDisabled() const { return disableAllWarnings_; }
+  bool IsEnabled(LanguageFeature f) const { return !disable_.test(f); }
+  bool ShouldWarn(LanguageFeature f) const { return warnLanguage_.test(f); }
+  bool ShouldWarn(UsageWarning w) const { return warnUsage_.test(w); }
   // Return all spellings of operators names, depending on features enabled
   std::vector<const char *> GetNames(LogicalOperator) const;
   std::vector<const char *> GetNames(RelationalOperator) const;
@@ -127,6 +132,24 @@ class LanguageFeatureControl {
   bool warnAllLanguage_{false};
   UsageWarnings warnUsage_;
   bool warnAllUsage_{false};
+  bool disableAllWarnings_{false};
 };
+
+// Parse a CLI enum option return the enum index and whether it should be
+// enabled (true) or disabled (false). Just exposed for the template below.
+std::optional<std::pair<bool, int>> parseCLIEnumIndex(
+    llvm::StringRef input, std::function<std::optional<int>(Predicate)> find);
+
+template <typename ENUM>
+std::optional<std::pair<bool, ENUM>> parseCLIEnum(
+    llvm::StringRef input, std::function<std::optional<int>(Predicate)> find) {
+  using To = std::pair<bool, ENUM>;
+  using From = std::pair<bool, int>;
+  static std::function<To(From)> cast = [](From x) {
+    return std::pair{x.first, static_cast<ENUM>(x.second)};
+  };
+  return fmap(parseCLIEnumIndex(input, find), cast);
+}
+
 } // namespace Fortran::common
 #endif // FORTRAN_SUPPORT_FORTRAN_FEATURES_H_
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index ba2531819ee5e..9ea568549bd6c 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -34,6 +34,7 @@
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
 #include "llvm/Support/CodeGen.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/Path.h"
@@ -45,6 +46,7 @@
 #include <cstdlib>
 #include <memory>
 #include <optional>
+#include <string>
 
 using namespace Fortran::frontend;
 
@@ -971,10 +973,23 @@ static bool parseSemaArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
 
 /// Parses all diagnostics related arguments and populates the variables
 /// options accordingly. Returns false if new errors are generated.
+/// FC1 driver entry point for parsing diagnostic arguments.
 static bool parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
                           clang::DiagnosticsEngine &diags) {
   unsigned numErrorsBefore = diags.getNumErrors();
 
+  auto &features = res.getFrontendOpts().features;
+  // The order of these flags (-pedantic -W<feature> -w) is important and is
+  // chosen to match clang's behavior.
+
+  // -pedantic
+  if (args.hasArg(clang::driver::options::OPT_pedantic)) {
+    features.WarnOnAllNonstandard();
+    features.WarnOnAllUsage();
+    res.setEnableConformanceChecks();
+    res.setEnableUsageChecks();
+  }
+
   // -Werror option
   // TODO: Currently throws a Diagnostic for anything other than -W<error>,
   // this has to change when other -W<opt>'s are supported.
@@ -984,21 +999,27 @@ static bool parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
     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);
+        // -W(no-)<feature>
+      } else if (!features.applyCLIOption(wArg)) {
+        const unsigned diagID = diags.getCustomDiagID(
+            clang::DiagnosticsEngine::Error, "Unknown diagnostic option: -W%0");
+        diags.Report(diagID) << wArg;
       }
     }
   }
 
+  // -w
+  if (args.hasArg(clang::driver::options::OPT_w)) {
+    features.DisableAllWarnings();
+    res.setDisableWarnings();
+  }
+
   // Default to off for `flang -fc1`.
-  res.getFrontendOpts().showColors =
-      parseShowColorsArgs(args, /*defaultDiagColor=*/false);
+  bool showColors = parseShowColorsArgs(args, false);
 
-  // Honor color diagnostics.
-  res.getDiagnosticOpts().ShowColors = res.getFrontendOpts().showColors;
+  diags.getDiagnosticOptions().ShowColors = showColors;
+  res.getDiagnosticOpts().ShowColors = showColors;
+  res.getFrontendOpts().showColors = showColors;
 
   return diags.getNumErrors() == numErrorsBefore;
 }
@@ -1074,16 +1095,6 @@ static bool parseDialectArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
         Fortran::common::LanguageFeature::OpenACC);
   }
 
-  // -pedantic
-  if (args.hasArg(clang::driver::options::OPT_pedantic)) {
-    res.setEnableConformanceChecks();
-    res.setEnableUsageChecks();
-  }
-
-  // -w
-  if (args.hasArg(clang::driver::options::OPT_w))
-    res.setDisableWarnings();
-
   // -std=f2018
   // TODO: Set proper options when more fortran standards
   // are supported.
@@ -1092,6 +1103,7 @@ static bool parseDialectArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
     // We only allow f2018 as the given standard
     if (standard == "f2018") {
       res.setEnableConformanceChecks();
+      res.getFrontendOpts().features.WarnOnAllNonstandard();
     } else {
       const unsigned diagID =
           diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
@@ -1099,6 +1111,7 @@ static bool parseDialectArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
       diags.Report(diagID);
     }
   }
+
   return diags.getNumErrors() == numErrorsBefore;
 }
 
@@ -1694,16 +1707,7 @@ void CompilerInvocation::setFortranOpts() {
   if (frontendOptions.needProvenanceRangeToCharBlockMappings)
     fortranOptions.needProvenanceRangeToCharBlockMappings = true;
 
-  if (getEnableConformanceChecks())
-    fortranOptions.features.WarnOnAllNonstandard();
-
-  if (getEnableUsageChecks())
-    fortranOptions.features.WarnOnAllUsage();
-
-  if (getDisableWarnings()) {
-    fortranOptions.features.DisableAllNonstandardWarnings();
-    fortranOptions.features.DisableAllUsageWarnings();
-  }
+  fortranOptions.features = frontendOptions.features;
 }
 
 std::unique_ptr<Fortran::semantics::SemanticsContext>
diff --git a/flang/lib/Support/CMakeLists.txt b/flang/lib/Support/CMakeLists.txt
index 363f57ce97dae..9ef31a2a6dcc7 100644
--- a/flang/lib/Support/CMakeLists.txt
+++ b/flang/lib/Support/CMakeLists.txt
@@ -44,6 +44,7 @@ endif()
 
 add_flang_library(FortranSupport
   default-kinds.cpp
+  enum-class.cpp
   Flags.cpp
   Fortran.cpp
   Fortran-features.cpp
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index bee8984102b82..55abf0385d185 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -9,6 +9,8 @@
 #include "flang/Support/Fortran-features.h"
 #include "flang/Common/idioms.h"
 #include "flang/Support/Fortran.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/raw_ostream.h"
 
 namespace Fortran::common {
 
@@ -94,57 +96,123 @@ LanguageFeatureControl::LanguageFeatureControl() {
   warnLanguage_.set(LanguageFeature::NullActualForAllocatable);
 }
 
-// Ignore case and any inserted punctuation (like '-'/'_')
-static std::optional<char> GetWarningChar(char ch) {
-  if (ch >= 'a' && ch <= 'z') {
-    return ch;
-  } else if (ch >= 'A' && ch <= 'Z') {
-    return ch - 'A' + 'a';
-  } else if (ch >= '0' && ch <= '9') {
-    return ch;
-  } else {
-    return std::nullopt;
+// Split a string with camel case into the individual words.
+// Note, the small vector is just an array of a few pointers and lengths
+// into the original input string. So all this allocation should be pretty
+// cheap.
+llvm::SmallVector<llvm::StringRef> splitCamelCase(llvm::StringRef input) {
+  using namespace llvm;
+  if (input.empty()) {
+    return {};
   }
+  SmallVector<StringRef> parts{};
+  parts.reserve(input.size());
+  auto check = [&input](size_t j, function_ref<bool(char)> predicate) {
+    return j < input.size() && predicate(input[j]);
+  };
+  size_t i{0};
+  size_t startWord = i;
+  for (; i < input.size(); i++) {
+    if ((check(i, isUpper) && check(i + 1, isUpper) && check(i + 2, isLower)) ||
+        ((check(i, isLower) || check(i, isDigit)) && check(i + 1, isUpper))) {
+      parts.push_back(StringRef(input.data() + startWord, i - startWord + 1));
+      startWord = i + 1;
+    }
+  }
+  parts.push_back(llvm::StringRef(input.data() + startWord, i - startWord));
+  return parts;
 }
 
-static bool WarningNameMatch(const char *a, const char *b) {
-  while (true) {
-    auto ach{GetWarningChar(*a)};
-    while (!ach && *a) {
-      ach = GetWarningChar(*++a);
-    }
-    auto bch{GetWarningChar(*b)};
-    while (!bch && *b) {
-      bch = GetWarningChar(*++b);
+// Split a string whith hyphens into the individual words.
+llvm::SmallVector<llvm::StringRef> splitHyphenated(llvm::StringRef input) {
+  auto parts = llvm::SmallVector<llvm::StringRef>{};
+  llvm::SplitString(input, parts, "-");
+  return parts;
+}
+
+// Check if two strings are equal while normalizing case for the
+// right word which is assumed to be a single word in camel case.
+bool equalLowerCaseWithCamelCaseWord(llvm::StringRef l, llvm::StringRef r) {
+  size_t ls = l.size();
+  if (ls != r.size())
+    return false;
+  size_t j{0};
+  // Process the upper case characters.
+  for (; j < ls; j++) {
+    char rc = r[j];
+    char rc2l = llvm::toLower(rc);
+    if (rc == rc2l) {
+      // Past run of Uppers Case;
+      break;
     }
-    if (!ach && !bch) {
-      return true;
-    } else if (!ach || !bch || *ach != *bch) {
+    if (l[j] != rc2l)
+      return false;
+  }
+  // Process the lower case characters.
+  for (; j < ls; j++) {
+    if (l[j] != r[j]) {
       return false;
     }
-    ++a, ++b;
   }
+  return true;
 }
 
-template <typename ENUM, std::size_t N>
-std::optional<ENUM> ScanEnum(const char *name) {
-  if (name) {
-    for (std::size_t j{0}; j < N; ++j) {
-      auto feature{static_cast<ENUM>(j)};
-      if (WarningNameMatch(name, EnumToString(feature).data())) {
-        return feature;
+// Parse a CLI enum option return the enum index and whether it should be
+// enabled (true) or disabled (false).
+std::optional<std::pair<bool, int>> parseCLIEnumIndex(
+    llvm::StringRef input, std::function<std::optional<int>(Predicate)> find) {
+  auto parts = splitHyphenated(input);
+  bool negated = false;
+  if (parts.size() >= 1 && !parts[0].compare(llvm::StringRef("no", 2))) {
+    negated = true;
+    // Remove the "no" part
+    parts = llvm::SmallVector<llvm::StringRef>(parts.begin() + 1, parts.end());
+  }
+  size_t chars = 0;
+  for (auto p : parts) {
+    chars += p.size();
+  }
+  auto pred = [&](auto s) {
+    if (chars != s.size()) {
+      return false;
+    }
+    auto ccParts = splitCamelCase(s);
+    auto num_ccParts = ccParts.size();
+    if (parts.size() != num_ccParts) {
+      return false;
+    }
+    for (size_t i{0}; i < num_ccParts; i++) {
+      if (!equalLowerCaseWithCamelCaseWord(parts[i], ccParts[i])) {
+        return false;
       }
     }
-  }
-  return std::nullopt;
+    return true;
+  };
+  auto cast = [negated](int x) { return std::pair{!negated, x}; };
+  return fmap<int, std::pair<bool, int>>(find(pred), cast);
 }
 
-std::optional<LanguageFeature> FindLanguageFeature(const char *name) {
-  return ScanEnum<LanguageFeature, LanguageFeature_enumSize>(name);
+std::optional<std::pair<bool, LanguageFeature>> parseCLILanguageFeature(
+    llvm::StringRef input) {
+  return parseCLIEnum<LanguageFeature>(input, FindLanguageFeatureIndex);
 }
 
-std::optional<UsageWarning> FindUsageWarning(const char *name) {
-  return ScanEnum<UsageWarning, UsageWarning_enumSize>(name);
+std::optional<std::pair<bool, UsageWarning>> parseCLIUsageWarning(
+    llvm::StringRef input) {
+  return parseCLIEnum<UsageWarning>(input, FindUsageWarningIndex);
+}
+
+// Take a string from the CLI and apply it to the LanguageFeatureControl.
+// Return true if the option was applied recognized.
+bool LanguageFeatureControl::applyCLIOption(llvm::StringRef input) {
+  if (auto result = parseCLILanguageFeature(input)) {
+    EnableWarning(result->second, result->first);
+    return true;
+  } else if (auto result = parseCLIUsageWarning(input)) {
+    EnableWarning(result->second, result->first);
+    return true;
+  }
+  return false;
 }
 
 std::vector<const char *> LanguageFeatureControl::GetNames(
@@ -201,4 +269,32 @@ std::vector<const char *> LanguageFeatureControl::GetNames(
   }
 }
 
+template <typename ENUM, std::size_t N>
+void ForEachEnum(std::function<void(ENUM)> f) {
+  for (size_t j{0}; j < N; ++j) {
+    f(static_cast<ENUM>(j));
+  }
+}
+
+void LanguageFeatureControl::WarnOnAllNonstandard(bool yes) {
+  warnAllLanguage_ = yes;
+  disableAllWarnings_ = yes ? false : disableAllWarnings_;
+  // should be equivalent to: reset().flip() set ...
+  ForEachEnum<LanguageFeature, LanguageFeature_enumSize>(
+      [&](LanguageFeature f) { warnLanguage_.set(f, yes); });
+  if (yes) {
+    // These three features do not need to be warned about,
+    // but we do want their feature flags.
+    warnLanguage_.set(LanguageFeature::OpenMP, false);
+    warnLanguage_.set(LanguageFeature::OpenACC, false);
+    warnLanguage_.set(LanguageFeature::CUDA, false);
+  }
+}
+
+void LanguageFeatureControl::WarnOnAllUsage(bool yes) {
+  warnAllUsage_ = yes;
+  disableAllWarnings_ = yes ? false : disableAllWarnings_;
+  ForEachEnum<UsageWarning, UsageWarning_enumSize>(
+      [&](UsageWarning w) { warnUsage_.set(w, yes); });
+}
 } // namespace Fortran::common
diff --git a/flang/lib/Support/enum-class.cpp b/flang/lib/Support/enum-class.cpp
new file mode 100644
index 0000000000000..ed11318382b35
--- /dev/null
+++ b/flang/lib/Support/enum-class.cpp
@@ -0,0 +1,24 @@
+//===-- lib/Support/enum-class.cpp -------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Common/enum-class.h"
+#include <optional>
+#include <functional>
+namespace Fortran::common {
+
+std::optional<int> FindEnumIndex(std::function<bool(const std::string_view)> pred, int size, const std::string_view *names) {
+    for (int i = 0; i < size; ++i) {
+        if (pred(names[i])) {
+            return i;
+        }
+    }
+    return std::nullopt;
+}
+
+
+} // namespace Fortran::common
\ No newline at end of file
diff --git a/flang/test/Driver/disable-diagnostic.f90 b/flang/test/Driver/disable-diagnostic.f90
new file mode 100644
index 0000000000000..8a58e63cfa3ac
--- /dev/null
+++ b/flang/test/Driver/disable-diagnostic.f90
@@ -0,0 +1,19 @@
+! RUN: %flang -Wknown-bad-implicit-interface %s -c 2>&1 | FileCheck %s --check-prefix=WARN
+! RUN: %flang -pedantic -Wno-known-bad-implicit-interface %s -c 2>&1 | FileCheck %s --allow-empty
+! RUN: not %flang -WKnownBadImplicitInterface %s -c 2>&1 | FileCheck %s --check-prefix=ERROR1
+! RUN: not %flang -WKnown-Bad-Implicit-Interface %s -c 2>&1 | FileCheck %s --check-prefix=ERROR2
+! ERROR1: error: Unknown diagnostic option: -WKnownBadImplicitInterface
+! ERROR2: error: Unknown diagnostic option: -WKnown-Bad-Implicit-Interface
+
+program disable_diagnostic
+  REAL :: x
+  INTEGER :: y
+   ! CHECK-NOT: warning
+  ! WARN: warning: If the procedure's interface were explicit, this reference would be in error
+  call sub(x)
+  ! WARN: warning: If the procedure's interface were explicit, this reference would be in error
+  call sub(y)
+end program disable_diagnostic
+
+subroutine sub()
+end subroutine sub
\ No newline at end of file
diff --git a/flang/test/Driver/werror-wrong.f90 b/flang/test/Driver/werror-wrong.f90
index 58adf6f745d5e..33f0aff8a1739 100644
--- a/flang/test/Driver/werror-wrong.f90
+++ b/flang/test/Driver/werror-wrong.f90
@@ -1,6 +1,7 @@
 ! 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
+! RUN: not %flang_fc1 -fsyntax-only -Wall %s  2>&1 | FileCheck %s --check-prefix=WRONG1
+! RUN: not %flang_fc1 -fsyntax-only -WX %s  2>&1 | FileCheck %s --check-prefix=WRONG2
 
-! WRONG: Only `-Werror` is supported currently.
+! WRONG1: error: Unknown diagnostic option: -Wall
+! WRONG2: error: Unknown diagnostic option: -WX
\ No newline at end of file
diff --git a/flang/test/Driver/wextra-ok.f90 b/flang/test/Driver/wextra-ok.f90
index 441029aa0af27..db15c7f14aa35 100644
--- a/flang/test/Driver/wextra-ok.f90
+++ b/flang/test/Driver/wextra-ok.f90
@@ -5,7 +5,7 @@
 ! RUN: not %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 diagnostic option: -Wblah
 
 program wextra_ok
 end program wextra_ok
diff --git a/flang/unittests/Common/CMakeLists.txt b/flang/unittests/Common/CMakeLists.txt
index bda02ed29a5ef..19cc5a20fecf4 100644
--- a/flang/unittests/Common/CMakeLists.txt
+++ b/flang/unittests/Common/CMakeLists.txt
@@ -1,3 +1,6 @@
 add_flang_unittest(FlangCommonTests
+  EnumClassTests.cpp
   FastIntSetTest.cpp
+  FortranFeaturesTest.cpp
 )
+target_link_libraries(FlangCommonTests PRIVATE FortranSupport)
\ No newline at end of file
diff --git a/flang/unittests/Common/EnumClassTests.cpp b/flang/unittests/Common/EnumClassTests.cpp
new file mode 100644
index 0000000000000..f67c453cfad15
--- /dev/null
+++ b/flang/unittests/Common/EnumClassTests.cpp
@@ -0,0 +1,45 @@
+//===-- flang/unittests/Common/FastIntSetTest.cpp ---------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Common/enum-class.h"
+#include "flang/Common/template.h"
+#include "gtest/gtest.h"
+
+using namespace Fortran::common;
+using namespace std;
+
+ENUM_CLASS(TestEnum, One, Two, 
+            Three)
+ENUM_CLASS_EXTRA(TestEnum)
+
+TEST(EnumClassTest, EnumToString) {
+  ASSERT_EQ(EnumToString(TestEnum::One), "One");
+  ASSERT_EQ(EnumToString(TestEnum::Two), "Two");
+  ASSERT_EQ(EnumToString(TestEnum::Three), "Three");
+}
+
+TEST(EnumClassTest, EnumToStringData) {
+  ASSERT_STREQ(EnumToString(TestEnum::One).data(), "One, Two, Three");
+}
+
+TEST(EnumClassTest, StringToEnum) {
+  ASSERT_EQ(StringToTestEnum("One"), std::optional{TestEnum::One});
+  ASSERT_EQ(StringToTestEnum("Two"), std::optional{TestEnum::Two});
+  ASSERT_EQ(StringToTestEnum("Three"), std::optional{TestEnum::Three});
+  ASSERT_EQ(StringToTestEnum("Four"), std::nullopt);
+  ASSERT_EQ(StringToTestEnum(""), std::nullopt);
+  ASSERT_EQ(StringToTestEnum("One, Two, Three"), std::nullopt);
+}
+
+ENUM_CLASS(TestEnumExtra, TwentyOne, FortyTwo, SevenSevenSeven)
+ENUM_CLASS_EXTRA(TestEnumExtra)
+
+TEST(EnumClassTest, FindNameNormal) {
+  auto p1 = [](auto s) { return s == "TwentyOne"; };
+  ASSERT_EQ(FindTestEnumExtra(p1), std::optional{TestEnumExtra::TwentyOne});
+}
diff --git a/flang/unittests/Common/FortranFeaturesTest.cpp b/flang/unittests/Common/FortranFeaturesTest.cpp
new file mode 100644
index 0000000000000..7ec7054f14f6e
--- /dev/null
+++ b/flang/unittests/Common/FortranFeaturesTest.cpp
@@ -0,0 +1,142 @@
+//===-- flang/unittests/Common/FastIntSetTest.cpp ---------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Common/enum-class.h"
+#include "flang/Support/Fortran-features.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "gtest/gtest.h"
+
+namespace Fortran::common {
+
+// Not currently exported from Fortran-features.h
+llvm::SmallVector<llvm::StringRef> splitCamelCase(llvm::StringRef input);
+llvm::SmallVector<llvm::StringRef> splitHyphenated(llvm::StringRef input);
+bool equalLowerCaseWithCamelCaseWord(llvm::StringRef l, llvm::StringRef r);
+
+ENUM_CLASS(TestEnumExtra, TwentyOne, FortyTwo, SevenSevenSeven)
+ENUM_CLASS_EXTRA(TestEnumExtra)
+
+TEST(EnumClassTest, SplitCamelCase) {
+
+  auto parts = splitCamelCase("oP");
+  ASSERT_EQ(parts.size(), (size_t)2);
+
+  if (parts[0].compare(llvm::StringRef("o", 1))) {
+    ADD_FAILURE() << "First part is not OP";
+  }
+  if (parts[1].compare(llvm::StringRef("P", 1))) {
+    ADD_FAILURE() << "Second part is not Name";
+  }
+ 
+  parts = splitCamelCase("OPName");
+  ASSERT_EQ(parts.size(), (size_t)2);
+
+  if (parts[0].compare(llvm::StringRef("OP", 2))) {
+    ADD_FAILURE() << "First part is not OP";
+  }
+  if (parts[1].compare(llvm::StringRef("Name", 4))) {
+    ADD_FAILURE() << "Second part is not Name";
+  }
+
+  parts = splitCamelCase("OpName");
+  ASSERT_EQ(parts.size(), (size_t)2);
+  if (parts[0].compare(llvm::StringRef("Op", 2))) {
+    ADD_FAILURE() << "First part is not Op";
+  }
+  if (parts[1].compare(llvm::StringRef("Name", 4))) {
+    ADD_FAILURE() << "Second part is not Name";
+  }
+
+  parts = splitCamelCase("opName");
+  ASSERT_EQ(parts.size(), (size_t)2);
+  if (parts[0].compare(llvm::StringRef("op", 2))) {
+    ADD_FAILURE() << "First part is not op";
+  }
+  if (parts[1].compare(llvm::StringRef("Name", 4))) {
+    ADD_FAILURE() << "Second part is not Name";
+  }
+
+  parts = splitCamelCase("FlangTestProgram123");
+  ASSERT_EQ(parts.size(), (size_t)3);
+  if (parts[0].compare(llvm::StringRef("Flang", 5))) {
+    ADD_FAILURE() << "First part is not Flang";
+  }
+  if (parts[1].compare(llvm::StringRef("Test", 4))) {
+    ADD_FAILURE() << "Second part is not Test";
+  }
+  if (parts[2].compare(llvm::StringRef("Program123", 10))) {
+    ADD_FAILURE() << "Third part is not Program123";
+  }
+  for (auto p : parts) {
+    llvm::errs() << p << " " << p.size() << "\n";
+  }
+}
+
+TEST(EnumClassTest, SplitHyphenated) {
+  auto parts = splitHyphenated("no-twenty-one");
+  ASSERT_EQ(parts.size(), (size_t)3);
+  if (parts[0].compare(llvm::StringRef("no", 2))) {
+    ADD_FAILURE() << "First part is not twenty";
+  }
+  if (parts[1].compare(llvm::StringRef("twenty", 6))) {
+    ADD_FAILURE() << "Second part is not one";
+  }
+  if (parts[2].compare(llvm::StringRef("one", 3))) {
+    ADD_FAILURE() << "Third part is not one";
+  }
+  for (auto p : parts) {
+    llvm::errs() << p << " " << p.size() << "\n";
+  }
+}
+
+TEST(EnumClassTest, equalLowerCaseWithCamelCaseWord) {
+  EXPECT_FALSE(equalLowerCaseWithCamelCaseWord("O", "O"));
+  EXPECT_FALSE(equalLowerCaseWithCamelCaseWord("o", "p"));
+  EXPECT_FALSE(equalLowerCaseWithCamelCaseWord("o", "P"));
+  EXPECT_FALSE(equalLowerCaseWithCamelCaseWord("1", "2"));
+  EXPECT_FALSE(equalLowerCaseWithCamelCaseWord("Op", "op"));
+  EXPECT_FALSE(equalLowerCaseWithCamelCaseWord("op", "Oplss"));
+  EXPECT_FALSE(equalLowerCaseWithCamelCaseWord("oplss", "OplSS"));
+  EXPECT_FALSE(equalLowerCaseWithCamelCaseWord("OPLSS", "oplss"));
+  EXPECT_FALSE(equalLowerCaseWithCamelCaseWord("OPLSS", "OPLSS"));
+
+  EXPECT_TRUE(equalLowerCaseWithCamelCaseWord("o", "O"));
+  EXPECT_TRUE(equalLowerCaseWithCamelCaseWord("oplss", "OPLSS"));
+  EXPECT_TRUE(equalLowerCaseWithCamelCaseWord("oplss", "oplss"));
+  EXPECT_TRUE(equalLowerCaseWithCamelCaseWord("op555", "OP555"));
+  EXPECT_TRUE(equalLowerCaseWithCamelCaseWord("op555", "op555"));
+}
+
+std::optional<std::pair<bool, TestEnumExtra>> parseCLITestEnumExtraOption(llvm::StringRef input) {
+  return parseCLIEnum<TestEnumExtra>(input, FindTestEnumExtraIndex); 
+}
+
+TEST(EnumClassTest, parseCLIEnumOption) {
+  auto result = parseCLITestEnumExtraOption("no-twenty-one");
+  auto expected = std::pair<bool, TestEnumExtra>(false, TestEnumExtra::TwentyOne);
+  ASSERT_EQ(result, std::optional{expected});
+  result = parseCLITestEnumExtraOption("twenty-one");
+  expected = std::pair<bool, TestEnumExtra>(true, TestEnumExtra::TwentyOne);
+  ASSERT_EQ(result, std::optional{expected});
+  result = parseCLITestEnumExtraOption("no-forty-two");
+  expected = std::pair<bool, TestEnumExtra>(false, TestEnumExtra::FortyTwo);
+  ASSERT_EQ(result, std::optional{expected});
+  result = parseCLITestEnumExtraOption("forty-two");
+  expected = std::pair<bool, TestEnumExtra>(true, TestEnumExtra::FortyTwo);
+  ASSERT_EQ(result, std::optional{expected});
+  result = parseCLITestEnumExtraOption("no-seven-seven-seven");
+  expected = std::pair<bool, TestEnumExtra>(false, TestEnumExtra::SevenSevenSeven);
+  ASSERT_EQ(result, std::optional{expected});
+  result = parseCLITestEnumExtraOption("seven-seven-seven");
+  expected = std::pair<bool, TestEnumExtra>(true, TestEnumExtra::SevenSevenSeven);
+  ASSERT_EQ(result, std::optional{expected});
+}
+
+} // namespace Fortran::common

>From 49a0579f9477936b72f0580823b4dd6824697512 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 29 May 2025 12:56:14 -0700
Subject: [PATCH 2/3] adjust headers

---
 flang/include/flang/Support/Fortran-features.h | 4 +---
 flang/lib/Frontend/CompilerInvocation.cpp      | 5 -----
 flang/lib/Support/Fortran-features.cpp         | 1 -
 3 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index d5aa7357ffea0..4a8b0da4c0d4d 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -11,9 +11,7 @@
 
 #include "Fortran.h"
 #include "flang/Common/enum-set.h"
-#include "flang/Common/idioms.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/raw_ostream.h"
+#include "llvm/ADT/StringRef.h"
 #include <optional>
 #include <vector>
 
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 9ea568549bd6c..d8bf601d0171d 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -20,11 +20,9 @@
 #include "flang/Support/Version.h"
 #include "flang/Tools/TargetSetup.h"
 #include "flang/Version.inc"
-#include "clang/Basic/AllDiagnostics.h"
 #include "clang/Basic/DiagnosticDriver.h"
 #include "clang/Basic/DiagnosticOptions.h"
 #include "clang/Driver/Driver.h"
-#include "clang/Driver/DriverDiagnostic.h"
 #include "clang/Driver/OptionUtils.h"
 #include "clang/Driver/Options.h"
 #include "llvm/ADT/StringRef.h"
@@ -34,9 +32,7 @@
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
 #include "llvm/Support/CodeGen.h"
-#include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
-#include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/raw_ostream.h"
@@ -46,7 +42,6 @@
 #include <cstdlib>
 #include <memory>
 #include <optional>
-#include <string>
 
 using namespace Fortran::frontend;
 
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index 55abf0385d185..0e394162ef577 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -10,7 +10,6 @@
 #include "flang/Common/idioms.h"
 #include "flang/Support/Fortran.h"
 #include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/raw_ostream.h"
 
 namespace Fortran::common {
 

>From fa2db7090c6d374ce1a835ad26d19a1d7bd42262 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 29 May 2025 12:57:22 -0700
Subject: [PATCH 3/3] reformat

---
 flang/lib/Support/enum-class.cpp              | 20 ++++++++++---------
 flang/unittests/Common/EnumClassTests.cpp     |  5 ++---
 .../unittests/Common/FortranFeaturesTest.cpp  | 18 ++++++++++-------
 3 files changed, 24 insertions(+), 19 deletions(-)

diff --git a/flang/lib/Support/enum-class.cpp b/flang/lib/Support/enum-class.cpp
index ed11318382b35..ac57f27ef1c9e 100644
--- a/flang/lib/Support/enum-class.cpp
+++ b/flang/lib/Support/enum-class.cpp
@@ -1,4 +1,5 @@
-//===-- lib/Support/enum-class.cpp -------------------------------*- C++ -*-===//
+//===-- lib/Support/enum-class.cpp -------------------------------*- C++
+//-*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -7,18 +8,19 @@
 //===----------------------------------------------------------------------===//
 
 #include "flang/Common/enum-class.h"
-#include <optional>
 #include <functional>
+#include <optional>
 namespace Fortran::common {
 
-std::optional<int> FindEnumIndex(std::function<bool(const std::string_view)> pred, int size, const std::string_view *names) {
-    for (int i = 0; i < size; ++i) {
-        if (pred(names[i])) {
-            return i;
-        }
+std::optional<int> FindEnumIndex(
+    std::function<bool(const std::string_view)> pred, int size,
+    const std::string_view *names) {
+  for (int i = 0; i < size; ++i) {
+    if (pred(names[i])) {
+      return i;
     }
-    return std::nullopt;
+  }
+  return std::nullopt;
 }
 
-
 } // namespace Fortran::common
\ No newline at end of file
diff --git a/flang/unittests/Common/EnumClassTests.cpp b/flang/unittests/Common/EnumClassTests.cpp
index f67c453cfad15..c9224a8ceba54 100644
--- a/flang/unittests/Common/EnumClassTests.cpp
+++ b/flang/unittests/Common/EnumClassTests.cpp
@@ -6,15 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "gtest/gtest.h"
 #include "flang/Common/enum-class.h"
 #include "flang/Common/template.h"
-#include "gtest/gtest.h"
 
 using namespace Fortran::common;
 using namespace std;
 
-ENUM_CLASS(TestEnum, One, Two, 
-            Three)
+ENUM_CLASS(TestEnum, One, Two, Three)
 ENUM_CLASS_EXTRA(TestEnum)
 
 TEST(EnumClassTest, EnumToString) {
diff --git a/flang/unittests/Common/FortranFeaturesTest.cpp b/flang/unittests/Common/FortranFeaturesTest.cpp
index 7ec7054f14f6e..597928e7fe56e 100644
--- a/flang/unittests/Common/FortranFeaturesTest.cpp
+++ b/flang/unittests/Common/FortranFeaturesTest.cpp
@@ -6,12 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "gtest/gtest.h"
 #include "flang/Common/enum-class.h"
 #include "flang/Support/Fortran-features.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/ErrorHandling.h"
-#include "gtest/gtest.h"
 
 namespace Fortran::common {
 
@@ -34,7 +34,7 @@ TEST(EnumClassTest, SplitCamelCase) {
   if (parts[1].compare(llvm::StringRef("P", 1))) {
     ADD_FAILURE() << "Second part is not Name";
   }
- 
+
   parts = splitCamelCase("OPName");
   ASSERT_EQ(parts.size(), (size_t)2);
 
@@ -114,13 +114,15 @@ TEST(EnumClassTest, equalLowerCaseWithCamelCaseWord) {
   EXPECT_TRUE(equalLowerCaseWithCamelCaseWord("op555", "op555"));
 }
 
-std::optional<std::pair<bool, TestEnumExtra>> parseCLITestEnumExtraOption(llvm::StringRef input) {
-  return parseCLIEnum<TestEnumExtra>(input, FindTestEnumExtraIndex); 
+std::optional<std::pair<bool, TestEnumExtra>> parseCLITestEnumExtraOption(
+    llvm::StringRef input) {
+  return parseCLIEnum<TestEnumExtra>(input, FindTestEnumExtraIndex);
 }
 
 TEST(EnumClassTest, parseCLIEnumOption) {
   auto result = parseCLITestEnumExtraOption("no-twenty-one");
-  auto expected = std::pair<bool, TestEnumExtra>(false, TestEnumExtra::TwentyOne);
+  auto expected =
+      std::pair<bool, TestEnumExtra>(false, TestEnumExtra::TwentyOne);
   ASSERT_EQ(result, std::optional{expected});
   result = parseCLITestEnumExtraOption("twenty-one");
   expected = std::pair<bool, TestEnumExtra>(true, TestEnumExtra::TwentyOne);
@@ -132,10 +134,12 @@ TEST(EnumClassTest, parseCLIEnumOption) {
   expected = std::pair<bool, TestEnumExtra>(true, TestEnumExtra::FortyTwo);
   ASSERT_EQ(result, std::optional{expected});
   result = parseCLITestEnumExtraOption("no-seven-seven-seven");
-  expected = std::pair<bool, TestEnumExtra>(false, TestEnumExtra::SevenSevenSeven);
+  expected =
+      std::pair<bool, TestEnumExtra>(false, TestEnumExtra::SevenSevenSeven);
   ASSERT_EQ(result, std::optional{expected});
   result = parseCLITestEnumExtraOption("seven-seven-seven");
-  expected = std::pair<bool, TestEnumExtra>(true, TestEnumExtra::SevenSevenSeven);
+  expected =
+      std::pair<bool, TestEnumExtra>(true, TestEnumExtra::SevenSevenSeven);
   ASSERT_EQ(result, std::optional{expected});
 }
 



More information about the flang-commits mailing list