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

Andre Kuhlenschmidt via flang-commits flang-commits at lists.llvm.org
Wed Jun 4 13:58:32 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 01/10] 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 02/10] 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 03/10] 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});
 }
 

>From 5f3feb64c1a97500e2808114d44bb07aa4ccb00c Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 29 May 2025 15:58:43 -0700
Subject: [PATCH 04/10] addressing feedback

---
 flang/include/flang/Common/enum-class.h       |  53 +++---
 flang/include/flang/Common/optional.h         |   7 +
 .../include/flang/Support/Fortran-features.h  |  16 --
 flang/lib/Support/Fortran-features.cpp        | 175 ++++++++----------
 flang/lib/Support/enum-class.cpp              |  15 +-
 flang/test/Driver/disable-diagnostic.f90      |   3 +-
 flang/test/Driver/werror-wrong.f90            |   2 +-
 flang/unittests/Common/CMakeLists.txt         |   2 +-
 .../unittests/Common/FortranFeaturesTest.cpp  | 159 +++-------------
 9 files changed, 153 insertions(+), 279 deletions(-)

diff --git a/flang/include/flang/Common/enum-class.h b/flang/include/flang/Common/enum-class.h
index baf9fe418141d..3dbd11bb4057c 100644
--- a/flang/include/flang/Common/enum-class.h
+++ b/flang/include/flang/Common/enum-class.h
@@ -17,9 +17,9 @@
 #ifndef FORTRAN_COMMON_ENUM_CLASS_H_
 #define FORTRAN_COMMON_ENUM_CLASS_H_
 
+#include "optional.h"
 #include <array>
 #include <functional>
-#include <optional>
 #include <string_view>
 namespace Fortran::common {
 
@@ -59,26 +59,6 @@ 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{ \
@@ -90,17 +70,34 @@ std::optional<NAME> inline FindEnum(
     return NAME##_names[static_cast<std::size_t>(e)]; \
   }
 
+namespace EnumClass {
+
+using Predicate = std::function<bool(const std::string_view)>;
+// Finds the first index for which the predicate returns true.
+optional<std::size_t> FindIndex(
+    Predicate pred, std::size_t size, const std::string_view *names);
+
+using FindIndexType = std::function<optional<std::size_t>(Predicate)>;
+
+template <typename NAME>
+optional<NAME> inline Find(Predicate pred, FindIndexType findIndex) {
+  return MapOption<int, NAME>(
+      findIndex(pred), [](int x) { return static_cast<NAME>(x); });
+}
+
+} // namespace EnumClass
+
 #define ENUM_CLASS_EXTRA(NAME) \
-  [[maybe_unused]] inline std::optional<int> Find##NAME##Index( \
-      ::Fortran::common::Predicate p) { \
-    return ::Fortran::common::FindEnumIndex( \
+  [[maybe_unused]] inline optional<std::size_t> Find##NAME##Index( \
+      ::Fortran::common::EnumClass::Predicate p) { \
+    return ::Fortran::common::EnumClass::FindIndex( \
         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 optional<NAME> Find##NAME( \
+      ::Fortran::common::EnumClass::Predicate p) { \
+    return ::Fortran::common::EnumClass::Find<NAME>(p, Find##NAME##Index); \
   } \
-  [[maybe_unused]] inline std::optional<NAME> StringTo##NAME( \
+  [[maybe_unused]] inline optional<NAME> StringTo##NAME( \
       const std::string_view name) { \
     return Find##NAME( \
         [name](const std::string_view s) -> bool { return name == s; }); \
diff --git a/flang/include/flang/Common/optional.h b/flang/include/flang/Common/optional.h
index c7c81f40cc8c8..5b623f01e828d 100644
--- a/flang/include/flang/Common/optional.h
+++ b/flang/include/flang/Common/optional.h
@@ -27,6 +27,7 @@
 #define FORTRAN_COMMON_OPTIONAL_H
 
 #include "api-attrs.h"
+#include <functional>
 #include <optional>
 #include <type_traits>
 
@@ -238,6 +239,12 @@ using std::nullopt_t;
 using std::optional;
 #endif // !STD_OPTIONAL_UNSUPPORTED
 
+template <typename T, typename U>
+std::optional<U> inline MapOption(
+    std::optional<T> x, std::function<U(const T)> f) {
+  return x ? std::optional<U>{f(*x)} : std::nullopt;
+}
+
 } // namespace Fortran::common
 
 #endif // FORTRAN_COMMON_OPTIONAL_H
diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index 4a8b0da4c0d4d..fd6a9139b7ea7 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -133,21 +133,5 @@ class LanguageFeatureControl {
   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/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index 0e394162ef577..72ea6639adf51 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -11,6 +11,10 @@
 #include "flang/Support/Fortran.h"
 #include "llvm/ADT/StringExtras.h"
 
+// Debugging
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+
 namespace Fortran::common {
 
 LanguageFeatureControl::LanguageFeatureControl() {
@@ -95,119 +99,99 @@ LanguageFeatureControl::LanguageFeatureControl() {
   warnLanguage_.set(LanguageFeature::NullActualForAllocatable);
 }
 
-// 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 {};
+// Namespace for helper functions for parsing CLI options
+// used instead of static so that there can be unit tests for these
+// functions.
+namespace FortranFeaturesHelpers {
+// Check if Lower Case Hyphenated words are equal to Camel Case words.
+// Because of out use case we know that 'r' the camel case string is
+// well formed in the sense that it is a sequence [a-zA-Z]+[a-zA-Z0-9]*.
+// This is checked in the enum-class.h file.
+bool LowerHyphEqualCamelCase(llvm::StringRef l, llvm::StringRef r) {
+  size_t ls{l.size()}, rs{r.size()};
+  if (ls < rs) {
+    return false;
   }
-  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;
+  bool atStartOfWord{true};
+  size_t wordCount{0}, j; // j is the number of word characters checked in r.
+  for (; j < rs; j++) {
+    if (wordCount + j >= ls) {
+      // `l` was shorter once the hiphens were removed.
+      // If r is null terminated, then we are good.
+      return r[j] == '\0';
     }
-  }
-  parts.push_back(llvm::StringRef(input.data() + startWord, i - startWord));
-  return parts;
-}
-
-// 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 (atStartOfWord) {
+      if (llvm::isUpper(r[j])) {
+        // Upper Case Run
+        if (l[wordCount + j] != llvm::toLower(r[j])) {
+          return false;
+        }
+      } else {
+        atStartOfWord = false;
+        if (l[wordCount + j] != r[j]) {
+          return false;
+        }
+      }
+    } else {
+      if (llvm::isUpper(r[j])) {
+        atStartOfWord = true;
+        if (l[wordCount + j] != '-') {
+          return false;
+        }
+        ++wordCount;
+        if (l[wordCount + j] != llvm::toLower(r[j])) {
+          return false;
+        }
+      } else if (l[wordCount + j] != r[j]) {
+        return false;
+      }
     }
-    if (l[j] != rc2l)
-      return false;
   }
-  // Process the lower case characters.
-  for (; j < ls; j++) {
-    if (l[j] != r[j]) {
-      return false;
-    }
+  // If there are more characters in l after processing all the characters in r.
+  // then fail unless the string is null terminated.
+  if (ls > wordCount + j) {
+    return l[wordCount + j] == '\0';
   }
   return true;
 }
 
 // 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))) {
+template <typename T>
+optional<std::pair<bool, T>> ParseCLIEnum(
+    llvm::StringRef input, EnumClass::FindIndexType findIndex) {
+  bool negated{false};
+  if (input.starts_with("no-")) {
     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();
+    input = input.drop_front(3);
   }
-  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 true;
-  };
-  auto cast = [negated](int x) { return std::pair{!negated, x}; };
-  return fmap<int, std::pair<bool, int>>(find(pred), cast);
+  EnumClass::Predicate predicate{
+      [input](llvm::StringRef r) { return LowerHyphEqualCamelCase(input, r); }};
+  optional<T> x = EnumClass::Find<T>(predicate, findIndex);
+  return MapOption<T, std::pair<bool, T>>(
+      x, [negated](T x) { return std::pair{!negated, x}; });
 }
 
-std::optional<std::pair<bool, LanguageFeature>> parseCLILanguageFeature(
+optional<std::pair<bool, UsageWarning>> parseCLIUsageWarning(
     llvm::StringRef input) {
-  return parseCLIEnum<LanguageFeature>(input, FindLanguageFeatureIndex);
+  return ParseCLIEnum<UsageWarning>(input, FindUsageWarningIndex);
 }
 
-std::optional<std::pair<bool, UsageWarning>> parseCLIUsageWarning(
+optional<std::pair<bool, LanguageFeature>> parseCLILanguageFeature(
     llvm::StringRef input) {
-  return parseCLIEnum<UsageWarning>(input, FindUsageWarningIndex);
+  return ParseCLIEnum<LanguageFeature>(input, FindLanguageFeatureIndex);
 }
 
+} // namespace FortranFeaturesHelpers
+
 // 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)) {
+  if (auto result = FortranFeaturesHelpers::parseCLILanguageFeature(input)) {
     EnableWarning(result->second, result->first);
     return true;
-  } else if (auto result = parseCLIUsageWarning(input)) {
+  } else if (auto result =
+                 FortranFeaturesHelpers::parseCLIUsageWarning(input)) {
     EnableWarning(result->second, result->first);
     return true;
   }
@@ -277,11 +261,10 @@ void ForEachEnum(std::function<void(ENUM)> f) {
 
 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); });
+  warnLanguage_.reset();
   if (yes) {
+    disableAllWarnings_ = false;
+    warnLanguage_.flip();
     // These three features do not need to be warned about,
     // but we do want their feature flags.
     warnLanguage_.set(LanguageFeature::OpenMP, false);
@@ -292,8 +275,10 @@ void LanguageFeatureControl::WarnOnAllNonstandard(bool yes) {
 
 void LanguageFeatureControl::WarnOnAllUsage(bool yes) {
   warnAllUsage_ = yes;
-  disableAllWarnings_ = yes ? false : disableAllWarnings_;
-  ForEachEnum<UsageWarning, UsageWarning_enumSize>(
-      [&](UsageWarning w) { warnUsage_.set(w, yes); });
+  warnUsage_.reset();
+  if (yes) {
+    disableAllWarnings_ = false;
+    warnUsage_.flip();
+  }
 }
 } // namespace Fortran::common
diff --git a/flang/lib/Support/enum-class.cpp b/flang/lib/Support/enum-class.cpp
index ac57f27ef1c9e..d6d0ee758175b 100644
--- a/flang/lib/Support/enum-class.cpp
+++ b/flang/lib/Support/enum-class.cpp
@@ -8,19 +8,20 @@
 //===----------------------------------------------------------------------===//
 
 #include "flang/Common/enum-class.h"
+#include "flang/Common/optional.h"
 #include <functional>
-#include <optional>
-namespace Fortran::common {
 
-std::optional<int> FindEnumIndex(
-    std::function<bool(const std::string_view)> pred, int size,
+namespace Fortran::common::EnumClass {
+
+optional<std::size_t> FindIndex(
+    std::function<bool(const std::string_view)> pred, size_t size,
     const std::string_view *names) {
-  for (int i = 0; i < size; ++i) {
+  for (size_t i = 0; i < size; ++i) {
     if (pred(names[i])) {
       return i;
     }
   }
-  return std::nullopt;
+  return nullopt;
 }
 
-} // namespace Fortran::common
\ No newline at end of file
+} // namespace Fortran::common::EnumClass
diff --git a/flang/test/Driver/disable-diagnostic.f90 b/flang/test/Driver/disable-diagnostic.f90
index 8a58e63cfa3ac..849489377da12 100644
--- a/flang/test/Driver/disable-diagnostic.f90
+++ b/flang/test/Driver/disable-diagnostic.f90
@@ -2,6 +2,7 @@
 ! 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
 
@@ -16,4 +17,4 @@ program disable_diagnostic
 end program disable_diagnostic
 
 subroutine sub()
-end subroutine sub
\ No newline at end of file
+end subroutine sub
diff --git a/flang/test/Driver/werror-wrong.f90 b/flang/test/Driver/werror-wrong.f90
index 33f0aff8a1739..6e3c7cca15bc7 100644
--- a/flang/test/Driver/werror-wrong.f90
+++ b/flang/test/Driver/werror-wrong.f90
@@ -4,4 +4,4 @@
 ! RUN: not %flang_fc1 -fsyntax-only -WX %s  2>&1 | FileCheck %s --check-prefix=WRONG2
 
 ! WRONG1: error: Unknown diagnostic option: -Wall
-! WRONG2: error: Unknown diagnostic option: -WX
\ No newline at end of file
+! WRONG2: error: Unknown diagnostic option: -WX
diff --git a/flang/unittests/Common/CMakeLists.txt b/flang/unittests/Common/CMakeLists.txt
index 19cc5a20fecf4..3149cb9f7bc47 100644
--- a/flang/unittests/Common/CMakeLists.txt
+++ b/flang/unittests/Common/CMakeLists.txt
@@ -3,4 +3,4 @@ add_flang_unittest(FlangCommonTests
   FastIntSetTest.cpp
   FortranFeaturesTest.cpp
 )
-target_link_libraries(FlangCommonTests PRIVATE FortranSupport)
\ No newline at end of file
+target_link_libraries(FlangCommonTests PRIVATE FortranSupport)
diff --git a/flang/unittests/Common/FortranFeaturesTest.cpp b/flang/unittests/Common/FortranFeaturesTest.cpp
index 597928e7fe56e..e12aff9f7b735 100644
--- a/flang/unittests/Common/FortranFeaturesTest.cpp
+++ b/flang/unittests/Common/FortranFeaturesTest.cpp
@@ -12,135 +12,34 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/ErrorHandling.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});
+#include <optional>
+
+namespace Fortran::common::FortranFeaturesHelpers {
+
+optional<std::pair<bool, UsageWarning>> parseCLIUsageWarning(
+    llvm::StringRef input);
+TEST(EnumClassTest, ParseCLIUsageWarning) {
+  EXPECT_EQ((parseCLIUsageWarning("no-twenty-one")), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("twenty-one")), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("no-seven-seven-seven")), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("seven-seven-seven")), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("no")), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("")), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("no-")), std::nullopt);
+  EXPECT_EQ(parseCLIUsageWarning("Portability"), std::nullopt);
+  auto expect{std::pair{false, UsageWarning::Portability}};
+  ASSERT_EQ(parseCLIUsageWarning("no-portability"), expect);
+  expect.first = true;
+  ASSERT_EQ((parseCLIUsageWarning("portability")), expect);
+  expect =
+      std::pair{false, Fortran::common::UsageWarning::PointerToUndefinable};
+  ASSERT_EQ((parseCLIUsageWarning("no-pointer-to-undefinable")), expect);
+  expect.first = true;
+  ASSERT_EQ((parseCLIUsageWarning("pointer-to-undefinable")), expect);
+  EXPECT_EQ(parseCLIUsageWarning("PointerToUndefinable"), std::nullopt);
+  EXPECT_EQ(parseCLIUsageWarning("NoPointerToUndefinable"), std::nullopt);
+  EXPECT_EQ(parseCLIUsageWarning("pointertoundefinable"), std::nullopt);
+  EXPECT_EQ(parseCLIUsageWarning("nopointertoundefinable"), std::nullopt);
 }
 
-} // namespace Fortran::common
+} // namespace Fortran::common::FortranFeaturesHelpers

>From 79303b42f7cfd3806c22bd34e5eced5f27d27f32 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 30 May 2025 15:42:27 -0700
Subject: [PATCH 05/10] removing debugging statement

---
 flang/lib/Support/Fortran-features.cpp | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index 72ea6639adf51..75baa0b096af0 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -11,10 +11,6 @@
 #include "flang/Support/Fortran.h"
 #include "llvm/ADT/StringExtras.h"
 
-// Debugging
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/raw_ostream.h"
-
 namespace Fortran::common {
 
 LanguageFeatureControl::LanguageFeatureControl() {

>From 8f0aa22125528a755ec61af2bd45b6c314cfe45c Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 30 May 2025 15:59:18 -0700
Subject: [PATCH 06/10] more feedback

---
 flang/include/flang/Support/Fortran-features.h |  4 +---
 flang/lib/Support/Fortran-features.cpp         | 16 +++++++++-------
 flang/unittests/Common/FortranFeaturesTest.cpp |  4 ----
 3 files changed, 10 insertions(+), 14 deletions(-)

diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index fd6a9139b7ea7..501b183cceeec 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -11,8 +11,6 @@
 
 #include "Fortran.h"
 #include "flang/Common/enum-set.h"
-#include "llvm/ADT/StringRef.h"
-#include <optional>
 #include <vector>
 
 namespace Fortran::common {
@@ -115,7 +113,7 @@ class LanguageFeatureControl {
     DisableAllNonstandardWarnings();
     DisableAllUsageWarnings();
   }
-  bool applyCLIOption(llvm::StringRef input);
+  bool applyCLIOption(std::string_view input);
   bool AreWarningsDisabled() const { return disableAllWarnings_; }
   bool IsEnabled(LanguageFeature f) const { return !disable_.test(f); }
   bool ShouldWarn(LanguageFeature f) const { return warnLanguage_.test(f); }
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index 75baa0b096af0..d140ecdff7f24 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -99,11 +99,11 @@ LanguageFeatureControl::LanguageFeatureControl() {
 // used instead of static so that there can be unit tests for these
 // functions.
 namespace FortranFeaturesHelpers {
-// Check if Lower Case Hyphenated words are equal to Camel Case words.
+// Check if lower case hyphenated words are equal to camel case words.
 // Because of out use case we know that 'r' the camel case string is
 // well formed in the sense that it is a sequence [a-zA-Z]+[a-zA-Z0-9]*.
 // This is checked in the enum-class.h file.
-bool LowerHyphEqualCamelCase(llvm::StringRef l, llvm::StringRef r) {
+static bool LowerHyphEqualCamelCase(llvm::StringRef l, llvm::StringRef r) {
   size_t ls{l.size()}, rs{r.size()};
   if (ls < rs) {
     return false;
@@ -161,8 +161,9 @@ optional<std::pair<bool, T>> ParseCLIEnum(
     negated = true;
     input = input.drop_front(3);
   }
-  EnumClass::Predicate predicate{
-      [input](llvm::StringRef r) { return LowerHyphEqualCamelCase(input, r); }};
+  EnumClass::Predicate predicate{[input](std::string_view r) {
+    return LowerHyphEqualCamelCase(input, r);
+  }};
   optional<T> x = EnumClass::Find<T>(predicate, findIndex);
   return MapOption<T, std::pair<bool, T>>(
       x, [negated](T x) { return std::pair{!negated, x}; });
@@ -182,12 +183,13 @@ optional<std::pair<bool, LanguageFeature>> parseCLILanguageFeature(
 
 // 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 = FortranFeaturesHelpers::parseCLILanguageFeature(input)) {
+bool LanguageFeatureControl::applyCLIOption(std::string_view input) {
+  llvm::StringRef inputRef{input};
+  if (auto result = FortranFeaturesHelpers::parseCLILanguageFeature(inputRef)) {
     EnableWarning(result->second, result->first);
     return true;
   } else if (auto result =
-                 FortranFeaturesHelpers::parseCLIUsageWarning(input)) {
+                 FortranFeaturesHelpers::parseCLIUsageWarning(inputRef)) {
     EnableWarning(result->second, result->first);
     return true;
   }
diff --git a/flang/unittests/Common/FortranFeaturesTest.cpp b/flang/unittests/Common/FortranFeaturesTest.cpp
index e12aff9f7b735..b3f0c31a57025 100644
--- a/flang/unittests/Common/FortranFeaturesTest.cpp
+++ b/flang/unittests/Common/FortranFeaturesTest.cpp
@@ -7,11 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #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 <optional>
 
 namespace Fortran::common::FortranFeaturesHelpers {

>From a0317745bca77a1134e116fd570b4ecca60e4d95 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 30 May 2025 17:02:17 -0700
Subject: [PATCH 07/10] adding insensitive match back

---
 .../include/flang/Support/Fortran-features.h  |  2 +-
 flang/lib/Support/Fortran-features.cpp        | 86 +++++++++++++++----
 .../unittests/Common/FortranFeaturesTest.cpp  | 75 +++++++++++-----
 3 files changed, 123 insertions(+), 40 deletions(-)

diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index 501b183cceeec..0b55a3175580a 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -113,7 +113,7 @@ class LanguageFeatureControl {
     DisableAllNonstandardWarnings();
     DisableAllUsageWarnings();
   }
-  bool applyCLIOption(std::string_view input);
+  bool applyCLIOption(std::string_view input, bool insensitive = false);
   bool AreWarningsDisabled() const { return disableAllWarnings_; }
   bool IsEnabled(LanguageFeature f) const { return !disable_.test(f); }
   bool ShouldWarn(LanguageFeature f) const { return warnLanguage_.test(f); }
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index d140ecdff7f24..80e87615697df 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -8,6 +8,7 @@
 
 #include "flang/Support/Fortran-features.h"
 #include "flang/Common/idioms.h"
+#include "flang/Common/optional.h"
 #include "flang/Support/Fortran.h"
 #include "llvm/ADT/StringExtras.h"
 
@@ -99,11 +100,48 @@ LanguageFeatureControl::LanguageFeatureControl() {
 // used instead of static so that there can be unit tests for these
 // functions.
 namespace FortranFeaturesHelpers {
+
+// 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;
+  }
+}
+
+// Check for case and punctuation insensitive string equality.
+// NB, b is probably not null terminated, so don't treat is like a C string.
+static bool InsensitiveWarningNameMatch(
+    std::string_view a, std::string_view b) {
+  size_t j{0}, aSize{a.size()}, k{0}, bSize{b.size()};
+  while (true) {
+    optional<char> ach{nullopt};
+    while (!ach && j < aSize) {
+      ach = GetWarningChar(a[j++]);
+    }
+    optional<char> bch{};
+    while (!bch && k < bSize) {
+      bch = GetWarningChar(b[k++]);
+    }
+    if (!ach && !bch) {
+      return true;
+    } else if (!ach || !bch || *ach != *bch) {
+      return false;
+    }
+    ach = bch = nullopt;
+  }
+}
+
 // Check if lower case hyphenated words are equal to camel case words.
 // Because of out use case we know that 'r' the camel case string is
 // well formed in the sense that it is a sequence [a-zA-Z]+[a-zA-Z0-9]*.
 // This is checked in the enum-class.h file.
-static bool LowerHyphEqualCamelCase(llvm::StringRef l, llvm::StringRef r) {
+static bool SensitiveWarningNameMatch(llvm::StringRef l, llvm::StringRef r) {
   size_t ls{l.size()}, rs{r.size()};
   if (ls < rs) {
     return false;
@@ -154,42 +192,56 @@ static bool LowerHyphEqualCamelCase(llvm::StringRef l, llvm::StringRef r) {
 // Parse a CLI enum option return the enum index and whether it should be
 // enabled (true) or disabled (false).
 template <typename T>
-optional<std::pair<bool, T>> ParseCLIEnum(
-    llvm::StringRef input, EnumClass::FindIndexType findIndex) {
+optional<std::pair<bool, T>> ParseCLIEnum(llvm::StringRef input,
+    EnumClass::FindIndexType findIndex, bool insensitive) {
   bool negated{false};
-  if (input.starts_with("no-")) {
-    negated = true;
-    input = input.drop_front(3);
+  EnumClass::Predicate predicate;
+  if (insensitive) {
+    if (input.starts_with_insensitive("no")) {
+      negated = true;
+      input = input.drop_front(2);
+    }
+    predicate = [input](std::string_view r) {
+      return InsensitiveWarningNameMatch(input, r);
+    };
+  } else {
+    if (input.starts_with("no-")) {
+      negated = true;
+      input = input.drop_front(3);
+    }
+    predicate = [input](std::string_view r) {
+      return SensitiveWarningNameMatch(input, r);
+    };
   }
-  EnumClass::Predicate predicate{[input](std::string_view r) {
-    return LowerHyphEqualCamelCase(input, r);
-  }};
   optional<T> x = EnumClass::Find<T>(predicate, findIndex);
   return MapOption<T, std::pair<bool, T>>(
       x, [negated](T x) { return std::pair{!negated, x}; });
 }
 
 optional<std::pair<bool, UsageWarning>> parseCLIUsageWarning(
-    llvm::StringRef input) {
-  return ParseCLIEnum<UsageWarning>(input, FindUsageWarningIndex);
+    llvm::StringRef input, bool insensitive) {
+  return ParseCLIEnum<UsageWarning>(input, FindUsageWarningIndex, insensitive);
 }
 
 optional<std::pair<bool, LanguageFeature>> parseCLILanguageFeature(
-    llvm::StringRef input) {
-  return ParseCLIEnum<LanguageFeature>(input, FindLanguageFeatureIndex);
+    llvm::StringRef input, bool insensitive) {
+  return ParseCLIEnum<LanguageFeature>(
+      input, FindLanguageFeatureIndex, insensitive);
 }
 
 } // namespace FortranFeaturesHelpers
 
 // Take a string from the CLI and apply it to the LanguageFeatureControl.
 // Return true if the option was applied recognized.
-bool LanguageFeatureControl::applyCLIOption(std::string_view input) {
+bool LanguageFeatureControl::applyCLIOption(
+    std::string_view input, bool insensitive) {
   llvm::StringRef inputRef{input};
-  if (auto result = FortranFeaturesHelpers::parseCLILanguageFeature(inputRef)) {
+  if (auto result = FortranFeaturesHelpers::parseCLILanguageFeature(
+          inputRef, insensitive)) {
     EnableWarning(result->second, result->first);
     return true;
-  } else if (auto result =
-                 FortranFeaturesHelpers::parseCLIUsageWarning(inputRef)) {
+  } else if (auto result = FortranFeaturesHelpers::parseCLIUsageWarning(
+                 inputRef, insensitive)) {
     EnableWarning(result->second, result->first);
     return true;
   }
diff --git a/flang/unittests/Common/FortranFeaturesTest.cpp b/flang/unittests/Common/FortranFeaturesTest.cpp
index b3f0c31a57025..4e9529d633ad9 100644
--- a/flang/unittests/Common/FortranFeaturesTest.cpp
+++ b/flang/unittests/Common/FortranFeaturesTest.cpp
@@ -13,29 +13,60 @@
 namespace Fortran::common::FortranFeaturesHelpers {
 
 optional<std::pair<bool, UsageWarning>> parseCLIUsageWarning(
-    llvm::StringRef input);
+    llvm::StringRef input, bool insensitive);
 TEST(EnumClassTest, ParseCLIUsageWarning) {
-  EXPECT_EQ((parseCLIUsageWarning("no-twenty-one")), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("twenty-one")), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("no-seven-seven-seven")), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("seven-seven-seven")), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("no")), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("")), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("no-")), std::nullopt);
-  EXPECT_EQ(parseCLIUsageWarning("Portability"), std::nullopt);
-  auto expect{std::pair{false, UsageWarning::Portability}};
-  ASSERT_EQ(parseCLIUsageWarning("no-portability"), expect);
-  expect.first = true;
-  ASSERT_EQ((parseCLIUsageWarning("portability")), expect);
-  expect =
-      std::pair{false, Fortran::common::UsageWarning::PointerToUndefinable};
-  ASSERT_EQ((parseCLIUsageWarning("no-pointer-to-undefinable")), expect);
-  expect.first = true;
-  ASSERT_EQ((parseCLIUsageWarning("pointer-to-undefinable")), expect);
-  EXPECT_EQ(parseCLIUsageWarning("PointerToUndefinable"), std::nullopt);
-  EXPECT_EQ(parseCLIUsageWarning("NoPointerToUndefinable"), std::nullopt);
-  EXPECT_EQ(parseCLIUsageWarning("pointertoundefinable"), std::nullopt);
-  EXPECT_EQ(parseCLIUsageWarning("nopointertoundefinable"), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("no-twenty-one", false)), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("twenty-one", false)), std::nullopt);
+  EXPECT_EQ(
+      (parseCLIUsageWarning("no-seven-seven-seven", false)), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("seven-seven-seven", false)), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("no", false)), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("", false)), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("no-", false)), std::nullopt);
+
+  EXPECT_EQ(parseCLIUsageWarning("Portability", false), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("no-portability", false)),
+      (std::optional{std::pair{false, UsageWarning::Portability}}));
+  EXPECT_EQ((parseCLIUsageWarning("portability", false)),
+      (std::optional{std::pair{true, UsageWarning::Portability}}));
+  EXPECT_EQ((parseCLIUsageWarning("no-pointer-to-undefinable", false)),
+      (std::optional{std::pair{false, UsageWarning::PointerToUndefinable}}));
+  EXPECT_EQ((parseCLIUsageWarning("pointer-to-undefinable", false)),
+      (std::optional{std::pair{true, UsageWarning::PointerToUndefinable}}));
+  EXPECT_EQ(parseCLIUsageWarning("PointerToUndefinable", false), std::nullopt);
+  EXPECT_EQ(
+      parseCLIUsageWarning("NoPointerToUndefinable", false), std::nullopt);
+  EXPECT_EQ(parseCLIUsageWarning("pointertoundefinable", false), std::nullopt);
+  EXPECT_EQ(
+      parseCLIUsageWarning("nopointertoundefinable", false), std::nullopt);
+
+  EXPECT_EQ((parseCLIUsageWarning("no-twenty-one", false)), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("twenty-one", true)), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("no-seven-seven-seven", true)), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("seven-seven-seven", true)), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("no", true)), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("", true)), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("no-", true)), std::nullopt);
+
+  EXPECT_EQ(parseCLIUsageWarning("Portability", true),
+      (std::optional{std::pair{true, UsageWarning::Portability}}));
+  EXPECT_EQ(parseCLIUsageWarning("no-portability", true),
+      (std::optional{std::pair{false, UsageWarning::Portability}}));
+
+  EXPECT_EQ((parseCLIUsageWarning("portability", true)),
+      (std::optional{std::pair{true, UsageWarning::Portability}}));
+  EXPECT_EQ((parseCLIUsageWarning("no-pointer-to-undefinable", true)),
+      (std::optional{std::pair{false, UsageWarning::PointerToUndefinable}}));
+  EXPECT_EQ((parseCLIUsageWarning("pointer-to-undefinable", true)),
+      (std::optional{std::pair{true, UsageWarning::PointerToUndefinable}}));
+  EXPECT_EQ(parseCLIUsageWarning("PointerToUndefinable", true),
+      (std::optional{std::pair{true, UsageWarning::PointerToUndefinable}}));
+  EXPECT_EQ(parseCLIUsageWarning("NoPointerToUndefinable", true),
+      (std::optional{std::pair{false, UsageWarning::PointerToUndefinable}}));
+  EXPECT_EQ(parseCLIUsageWarning("pointertoundefinable", true),
+      (std::optional{std::pair{true, UsageWarning::PointerToUndefinable}}));
+  EXPECT_EQ(parseCLIUsageWarning("nopointertoundefinable", true),
+      (std::optional{std::pair{false, UsageWarning::PointerToUndefinable}}));
 }
 
 } // namespace Fortran::common::FortranFeaturesHelpers

>From d37976f4167677294227c976b4aea03fdb130462 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 30 May 2025 18:01:35 -0700
Subject: [PATCH 08/10] fixing enum name

---
 flang/include/flang/Support/Fortran-features.h | 2 +-
 flang/lib/Semantics/tools.cpp                  | 2 +-
 flang/unittests/Common/FortranFeaturesTest.cpp | 5 ++++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index 0b55a3175580a..899959ad8a435 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -60,7 +60,7 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
     NonTargetPassedToTarget, PointerToPossibleNoncontiguous,
     ShortCharacterActual, ShortArrayActual, ImplicitInterfaceActual,
     PolymorphicTransferArg, PointerComponentTransferArg, TransferSizePresence,
-    F202XAllocatableBreakingChange, OptionalMustBePresent, CommonBlockPadding,
+    F202xAllocatableBreakingChange, OptionalMustBePresent, CommonBlockPadding,
     LogicalVsCBool, BindCCharLength, ProcDummyArgShapes, ExternalNameConflict,
     FoldingException, FoldingAvoidsRuntimeCrash, FoldingValueChecks,
     FoldingFailure, FoldingLimit, Interoperability, CharacterInteroperability,
diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp
index 1d1e3ac044166..e8da757416cc6 100644
--- a/flang/lib/Semantics/tools.cpp
+++ b/flang/lib/Semantics/tools.cpp
@@ -1672,7 +1672,7 @@ std::forward_list<std::string> GetAllNames(
 void WarnOnDeferredLengthCharacterScalar(SemanticsContext &context,
     const SomeExpr *expr, parser::CharBlock at, const char *what) {
   if (context.languageFeatures().ShouldWarn(
-          common::UsageWarning::F202XAllocatableBreakingChange)) {
+          common::UsageWarning::F202xAllocatableBreakingChange)) {
     if (const Symbol *
         symbol{evaluate::UnwrapWholeSymbolOrComponentDataRef(expr)}) {
       const Symbol &ultimate{ResolveAssociations(*symbol)};
diff --git a/flang/unittests/Common/FortranFeaturesTest.cpp b/flang/unittests/Common/FortranFeaturesTest.cpp
index 4e9529d633ad9..0e48697182ff5 100644
--- a/flang/unittests/Common/FortranFeaturesTest.cpp
+++ b/flang/unittests/Common/FortranFeaturesTest.cpp
@@ -52,7 +52,6 @@ TEST(EnumClassTest, ParseCLIUsageWarning) {
       (std::optional{std::pair{true, UsageWarning::Portability}}));
   EXPECT_EQ(parseCLIUsageWarning("no-portability", true),
       (std::optional{std::pair{false, UsageWarning::Portability}}));
-
   EXPECT_EQ((parseCLIUsageWarning("portability", true)),
       (std::optional{std::pair{true, UsageWarning::Portability}}));
   EXPECT_EQ((parseCLIUsageWarning("no-pointer-to-undefinable", true)),
@@ -67,6 +66,10 @@ TEST(EnumClassTest, ParseCLIUsageWarning) {
       (std::optional{std::pair{true, UsageWarning::PointerToUndefinable}}));
   EXPECT_EQ(parseCLIUsageWarning("nopointertoundefinable", true),
       (std::optional{std::pair{false, UsageWarning::PointerToUndefinable}}));
+
+  EXPECT_EQ(parseCLIUsageWarning("f202x-allocatable-breaking-change", false),
+      (std::optional{
+          std::pair{true, UsageWarning::F202xAllocatableBreakingChange}}));
 }
 
 } // namespace Fortran::common::FortranFeaturesHelpers

>From 9ae60472c9e005f0754f195ce1801b970e7244ea Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 30 May 2025 18:12:42 -0700
Subject: [PATCH 09/10] fixing uninitialized

---
 flang/lib/Support/Fortran-features.cpp         | 2 +-
 flang/unittests/Common/FortranFeaturesTest.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index 80e87615697df..dd1798e90051c 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -147,7 +147,7 @@ static bool SensitiveWarningNameMatch(llvm::StringRef l, llvm::StringRef r) {
     return false;
   }
   bool atStartOfWord{true};
-  size_t wordCount{0}, j; // j is the number of word characters checked in r.
+  size_t wordCount{0}, j{0}; // j is the number of word characters checked in r.
   for (; j < rs; j++) {
     if (wordCount + j >= ls) {
       // `l` was shorter once the hiphens were removed.
diff --git a/flang/unittests/Common/FortranFeaturesTest.cpp b/flang/unittests/Common/FortranFeaturesTest.cpp
index 0e48697182ff5..367500e46af20 100644
--- a/flang/unittests/Common/FortranFeaturesTest.cpp
+++ b/flang/unittests/Common/FortranFeaturesTest.cpp
@@ -40,7 +40,7 @@ TEST(EnumClassTest, ParseCLIUsageWarning) {
   EXPECT_EQ(
       parseCLIUsageWarning("nopointertoundefinable", false), std::nullopt);
 
-  EXPECT_EQ((parseCLIUsageWarning("no-twenty-one", false)), std::nullopt);
+  EXPECT_EQ((parseCLIUsageWarning("no-twenty-one", true)), std::nullopt);
   EXPECT_EQ((parseCLIUsageWarning("twenty-one", true)), std::nullopt);
   EXPECT_EQ((parseCLIUsageWarning("no-seven-seven-seven", true)), std::nullopt);
   EXPECT_EQ((parseCLIUsageWarning("seven-seven-seven", true)), std::nullopt);

>From 8b126eecb0b1c18e702dee2923d4d68bfb0307ae Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 4 Jun 2025 13:57:45 -0700
Subject: [PATCH 10/10] now uses table

---
 flang/include/flang/Common/enum-class.h       |  39 +-
 flang/include/flang/Common/optional.h         |   6 -
 .../include/flang/Support/Fortran-features.h  |  34 +-
 flang/lib/Frontend/CompilerInvocation.cpp     |   4 +-
 flang/lib/Semantics/tools.cpp                 |   2 +-
 flang/lib/Support/Fortran-features.cpp        | 238 +++----
 flang/unittests/Common/EnumClassTests.cpp     |  31 +-
 .../unittests/Common/FortranFeaturesTest.cpp  | 606 ++++++++++++++++--
 8 files changed, 689 insertions(+), 271 deletions(-)

diff --git a/flang/include/flang/Common/enum-class.h b/flang/include/flang/Common/enum-class.h
index 3dbd11bb4057c..c6cdebf00dec2 100644
--- a/flang/include/flang/Common/enum-class.h
+++ b/flang/include/flang/Common/enum-class.h
@@ -66,41 +66,16 @@ constexpr std::array<std::string_view, ITEMS> EnumNames(const char *p) {
   [[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::size_t EnumToInt(NAME e) { \
+    return static_cast<std::size_t>(e); \
+  } \
   [[maybe_unused]] static inline std::string_view EnumToString(NAME e) { \
     return NAME##_names[static_cast<std::size_t>(e)]; \
-  }
-
-namespace EnumClass {
-
-using Predicate = std::function<bool(const std::string_view)>;
-// Finds the first index for which the predicate returns true.
-optional<std::size_t> FindIndex(
-    Predicate pred, std::size_t size, const std::string_view *names);
-
-using FindIndexType = std::function<optional<std::size_t>(Predicate)>;
-
-template <typename NAME>
-optional<NAME> inline Find(Predicate pred, FindIndexType findIndex) {
-  return MapOption<int, NAME>(
-      findIndex(pred), [](int x) { return static_cast<NAME>(x); });
-}
-
-} // namespace EnumClass
-
-#define ENUM_CLASS_EXTRA(NAME) \
-  [[maybe_unused]] inline optional<std::size_t> Find##NAME##Index( \
-      ::Fortran::common::EnumClass::Predicate p) { \
-    return ::Fortran::common::EnumClass::FindIndex( \
-        p, NAME##_enumSize, NAME##_names.data()); \
-  } \
-  [[maybe_unused]] inline optional<NAME> Find##NAME( \
-      ::Fortran::common::EnumClass::Predicate p) { \
-    return ::Fortran::common::EnumClass::Find<NAME>(p, Find##NAME##Index); \
   } \
-  [[maybe_unused]] inline optional<NAME> StringTo##NAME( \
-      const std::string_view name) { \
-    return Find##NAME( \
-        [name](const std::string_view s) -> bool { return name == s; }); \
+  [[maybe_unused]] inline void ForEach##NAME(std::function<void(NAME)> f) { \
+    for (std::size_t i = 0; i < NAME##_enumSize; ++i) { \
+      f(static_cast<NAME>(i)); \
+    } \
   }
 } // namespace Fortran::common
 #endif // FORTRAN_COMMON_ENUM_CLASS_H_
diff --git a/flang/include/flang/Common/optional.h b/flang/include/flang/Common/optional.h
index 5b623f01e828d..fbf0f66b59a65 100644
--- a/flang/include/flang/Common/optional.h
+++ b/flang/include/flang/Common/optional.h
@@ -239,12 +239,6 @@ using std::nullopt_t;
 using std::optional;
 #endif // !STD_OPTIONAL_UNSUPPORTED
 
-template <typename T, typename U>
-std::optional<U> inline MapOption(
-    std::optional<T> x, std::function<U(const T)> f) {
-  return x ? std::optional<U>{f(*x)} : std::nullopt;
-}
-
 } // namespace Fortran::common
 
 #endif // FORTRAN_COMMON_OPTIONAL_H
diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index 899959ad8a435..61e7031b85988 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -11,6 +11,8 @@
 
 #include "Fortran.h"
 #include "flang/Common/enum-set.h"
+#include <llvm/Support/raw_ostream.h>
+#include <string_view>
 #include <vector>
 
 namespace Fortran::common {
@@ -60,7 +62,7 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
     NonTargetPassedToTarget, PointerToPossibleNoncontiguous,
     ShortCharacterActual, ShortArrayActual, ImplicitInterfaceActual,
     PolymorphicTransferArg, PointerComponentTransferArg, TransferSizePresence,
-    F202xAllocatableBreakingChange, OptionalMustBePresent, CommonBlockPadding,
+    F202XAllocatableBreakingChange, OptionalMustBePresent, CommonBlockPadding,
     LogicalVsCBool, BindCCharLength, ProcDummyArgShapes, ExternalNameConflict,
     FoldingException, FoldingAvoidsRuntimeCrash, FoldingValueChecks,
     FoldingFailure, FoldingLimit, Interoperability, CharacterInteroperability,
@@ -77,10 +79,6 @@ 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>;
 
@@ -113,16 +111,40 @@ class LanguageFeatureControl {
     DisableAllNonstandardWarnings();
     DisableAllUsageWarnings();
   }
-  bool applyCLIOption(std::string_view input, bool insensitive = false);
   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); }
+  // CLI options
+  bool applyCLIOption(std::string input);
+  void addAlternativeCliSpelling(LanguageFeature f, std::string input) {
+    cliOptions_.insert({input, {f}});
+  }
+  void addAlternativeCliSpelling(UsageWarning w, std::string input) {
+    cliOptions_.insert({input, {w}});
+  }
+  void replaceCliCanonicalSpelling(LanguageFeature f, std::string input);
+  void replaceCliCanonicalSpelling(UsageWarning w, std::string input);
+  std::string_view getDefaultCliSpelling(LanguageFeature f) const {
+    return languageFeatureCliCanonicalSpelling_[EnumToInt(f)];
+  };
+  std::string_view getDefaultCliSpelling(UsageWarning w) const {
+    return usageWarningCliCanonicalSpelling_[EnumToInt(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;
 
 private:
+  // Map from CLI syntax of language features and usage warnings to their enum
+  // values.
+  std::unordered_map<std::string, std::variant<LanguageFeature, UsageWarning>>
+      cliOptions_;
+  // These two arrays map the enum values to their cannonical CLI spellings.
+  std::array<std::string_view, LanguageFeature_enumSize>
+      languageFeatureCliCanonicalSpelling_;
+  std::array<std::string_view, UsageWarning_enumSize>
+      usageWarningCliCanonicalSpelling_;
   LanguageFeatures disable_;
   LanguageFeatures warnLanguage_;
   bool warnAllLanguage_{false};
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index d8bf601d0171d..6fa7b98ebb5dd 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -995,7 +995,7 @@ static bool parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
       if (wArg == "error") {
         res.setWarnAsErr(true);
         // -W(no-)<feature>
-      } else if (!features.applyCLIOption(wArg)) {
+      } else if (!res.getFrontendOpts().features.applyCLIOption(wArg)) {
         const unsigned diagID = diags.getCustomDiagID(
             clang::DiagnosticsEngine::Error, "Unknown diagnostic option: -W%0");
         diags.Report(diagID) << wArg;
@@ -1011,11 +1011,9 @@ static bool parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
 
   // Default to off for `flang -fc1`.
   bool showColors = parseShowColorsArgs(args, false);
-
   diags.getDiagnosticOptions().ShowColors = showColors;
   res.getDiagnosticOpts().ShowColors = showColors;
   res.getFrontendOpts().showColors = showColors;
-
   return diags.getNumErrors() == numErrorsBefore;
 }
 
diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp
index e8da757416cc6..1d1e3ac044166 100644
--- a/flang/lib/Semantics/tools.cpp
+++ b/flang/lib/Semantics/tools.cpp
@@ -1672,7 +1672,7 @@ std::forward_list<std::string> GetAllNames(
 void WarnOnDeferredLengthCharacterScalar(SemanticsContext &context,
     const SomeExpr *expr, parser::CharBlock at, const char *what) {
   if (context.languageFeatures().ShouldWarn(
-          common::UsageWarning::F202xAllocatableBreakingChange)) {
+          common::UsageWarning::F202XAllocatableBreakingChange)) {
     if (const Symbol *
         symbol{evaluate::UnwrapWholeSymbolOrComponentDataRef(expr)}) {
       const Symbol &ultimate{ResolveAssociations(*symbol)};
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index dd1798e90051c..cb3282f7b6a90 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -8,13 +8,80 @@
 
 #include "flang/Support/Fortran-features.h"
 #include "flang/Common/idioms.h"
-#include "flang/Common/optional.h"
+#include "flang/Parser/characters.h"
 #include "flang/Support/Fortran.h"
-#include "llvm/ADT/StringExtras.h"
+#include <string>
+#include <string_view>
 
 namespace Fortran::common {
 
+// Namespace for helper functions for parsing CLI options
+// used instead of static so that there can be unit tests for these
+// functions.
+namespace featuresHelpers {
+
+static std::vector<std::string_view> SplitCamelCase(std::string_view x) {
+  std::vector<std::string_view> result;
+  // NB, we start at 1 because the first character is never a word boundary.
+  size_t xSize{x.size()}, wordStart{0}, wordEnd{1};
+  for (; wordEnd < xSize; ++wordEnd) {
+    // Identify when wordEnd is at the start of a new word.
+    if ((!parser::IsUpperCaseLetter(x[wordEnd - 1]) &&
+            parser::IsUpperCaseLetter(x[wordEnd])) ||
+        // ACCUsage => ACC-Usage, CComment => C-Comment, etc.
+        (parser::IsUpperCaseLetter(x[wordEnd]) && wordEnd + 1 < xSize &&
+            parser::IsLowerCaseLetter(x[wordEnd + 1]))) {
+      result.push_back(
+          std::string_view(x.begin() + wordStart, wordEnd - wordStart));
+      wordStart = wordEnd;
+    }
+  }
+  // We went one past the end of the last word.
+  result.push_back(
+      std::string_view(x.begin() + wordStart, wordEnd - wordStart));
+  return result;
+}
+
+std::string CamelCaseToLowerCaseHyphenated(std::string_view x) {
+  std::vector<std::string_view> words{SplitCamelCase(x)};
+  std::string result{};
+  result.reserve(x.size() + words.size() + 1);
+  for (size_t i{0}; i < words.size(); ++i) {
+    std::string word{parser::ToLowerCaseLetters(words[i])};
+    result += i == 0 ? "" : "-";
+    result += word;
+  }
+  return result;
+}
+} // namespace featuresHelpers
+
 LanguageFeatureControl::LanguageFeatureControl() {
+
+  // Initialize the bidirectional maps with the default spellings.
+  cliOptions_.reserve(LanguageFeature_enumSize + UsageWarning_enumSize);
+  ForEachLanguageFeature([&](auto feature) {
+    std::string_view name{Fortran::common::EnumToString(feature)};
+    std::string cliOption{
+        featuresHelpers::CamelCaseToLowerCaseHyphenated(name)};
+    cliOptions_.insert({cliOption, {feature}});
+    languageFeatureCliCanonicalSpelling_[EnumToInt(feature)] =
+        std::string_view{cliOption};
+  });
+
+  ForEachUsageWarning([&](auto warning) {
+    std::string_view name{Fortran::common::EnumToString(warning)};
+    std::string cliOption{
+        featuresHelpers::CamelCaseToLowerCaseHyphenated(name)};
+    cliOptions_.insert({cliOption, {warning}});
+    usageWarningCliCanonicalSpelling_[EnumToInt(warning)] =
+        std::string_view{cliOption};
+  });
+
+  replaceCliCanonicalSpelling(UsageWarning::F202XAllocatableBreakingChange,
+      "f202x-allocatable-breaking-change");
+
+  // If we need to
+
   // These features must be explicitly enabled by command line options.
   disable_.set(LanguageFeature::OldDebugLines);
   disable_.set(LanguageFeature::OpenACC);
@@ -96,156 +163,41 @@ LanguageFeatureControl::LanguageFeatureControl() {
   warnLanguage_.set(LanguageFeature::NullActualForAllocatable);
 }
 
-// Namespace for helper functions for parsing CLI options
-// used instead of static so that there can be unit tests for these
-// functions.
-namespace FortranFeaturesHelpers {
-
-// 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;
+// Take a string from the CLI and apply it to the LanguageFeatureControl.
+// Return true if the option was applied recognized.
+bool LanguageFeatureControl::applyCLIOption(std::string input) {
+  bool negated{false};
+  if (input.size() > 3 && input.substr(0, 3) == "no-") {
+    negated = true;
+    input = input.substr(3);
   }
-}
-
-// Check for case and punctuation insensitive string equality.
-// NB, b is probably not null terminated, so don't treat is like a C string.
-static bool InsensitiveWarningNameMatch(
-    std::string_view a, std::string_view b) {
-  size_t j{0}, aSize{a.size()}, k{0}, bSize{b.size()};
-  while (true) {
-    optional<char> ach{nullopt};
-    while (!ach && j < aSize) {
-      ach = GetWarningChar(a[j++]);
-    }
-    optional<char> bch{};
-    while (!bch && k < bSize) {
-      bch = GetWarningChar(b[k++]);
-    }
-    if (!ach && !bch) {
+  if (auto it = cliOptions_.find(input); it != cliOptions_.end()) {
+    if (std::holds_alternative<LanguageFeature>(it->second)) {
+      EnableWarning(std::get<LanguageFeature>(it->second), !negated);
       return true;
-    } else if (!ach || !bch || *ach != *bch) {
-      return false;
-    }
-    ach = bch = nullopt;
-  }
-}
-
-// Check if lower case hyphenated words are equal to camel case words.
-// Because of out use case we know that 'r' the camel case string is
-// well formed in the sense that it is a sequence [a-zA-Z]+[a-zA-Z0-9]*.
-// This is checked in the enum-class.h file.
-static bool SensitiveWarningNameMatch(llvm::StringRef l, llvm::StringRef r) {
-  size_t ls{l.size()}, rs{r.size()};
-  if (ls < rs) {
-    return false;
-  }
-  bool atStartOfWord{true};
-  size_t wordCount{0}, j{0}; // j is the number of word characters checked in r.
-  for (; j < rs; j++) {
-    if (wordCount + j >= ls) {
-      // `l` was shorter once the hiphens were removed.
-      // If r is null terminated, then we are good.
-      return r[j] == '\0';
-    }
-    if (atStartOfWord) {
-      if (llvm::isUpper(r[j])) {
-        // Upper Case Run
-        if (l[wordCount + j] != llvm::toLower(r[j])) {
-          return false;
-        }
-      } else {
-        atStartOfWord = false;
-        if (l[wordCount + j] != r[j]) {
-          return false;
-        }
-      }
-    } else {
-      if (llvm::isUpper(r[j])) {
-        atStartOfWord = true;
-        if (l[wordCount + j] != '-') {
-          return false;
-        }
-        ++wordCount;
-        if (l[wordCount + j] != llvm::toLower(r[j])) {
-          return false;
-        }
-      } else if (l[wordCount + j] != r[j]) {
-        return false;
-      }
-    }
-  }
-  // If there are more characters in l after processing all the characters in r.
-  // then fail unless the string is null terminated.
-  if (ls > wordCount + j) {
-    return l[wordCount + j] == '\0';
-  }
-  return true;
-}
-
-// Parse a CLI enum option return the enum index and whether it should be
-// enabled (true) or disabled (false).
-template <typename T>
-optional<std::pair<bool, T>> ParseCLIEnum(llvm::StringRef input,
-    EnumClass::FindIndexType findIndex, bool insensitive) {
-  bool negated{false};
-  EnumClass::Predicate predicate;
-  if (insensitive) {
-    if (input.starts_with_insensitive("no")) {
-      negated = true;
-      input = input.drop_front(2);
     }
-    predicate = [input](std::string_view r) {
-      return InsensitiveWarningNameMatch(input, r);
-    };
-  } else {
-    if (input.starts_with("no-")) {
-      negated = true;
-      input = input.drop_front(3);
+    if (std::holds_alternative<UsageWarning>(it->second)) {
+      EnableWarning(std::get<UsageWarning>(it->second), !negated);
+      return true;
     }
-    predicate = [input](std::string_view r) {
-      return SensitiveWarningNameMatch(input, r);
-    };
   }
-  optional<T> x = EnumClass::Find<T>(predicate, findIndex);
-  return MapOption<T, std::pair<bool, T>>(
-      x, [negated](T x) { return std::pair{!negated, x}; });
-}
-
-optional<std::pair<bool, UsageWarning>> parseCLIUsageWarning(
-    llvm::StringRef input, bool insensitive) {
-  return ParseCLIEnum<UsageWarning>(input, FindUsageWarningIndex, insensitive);
+  return false;
 }
 
-optional<std::pair<bool, LanguageFeature>> parseCLILanguageFeature(
-    llvm::StringRef input, bool insensitive) {
-  return ParseCLIEnum<LanguageFeature>(
-      input, FindLanguageFeatureIndex, insensitive);
+void LanguageFeatureControl::replaceCliCanonicalSpelling(
+    LanguageFeature f, std::string input) {
+  std::string_view old{languageFeatureCliCanonicalSpelling_[EnumToInt(f)]};
+  cliOptions_.erase(std::string{old});
+  languageFeatureCliCanonicalSpelling_[EnumToInt(f)] = input;
+  cliOptions_.insert({input, {f}});
 }
 
-} // namespace FortranFeaturesHelpers
-
-// Take a string from the CLI and apply it to the LanguageFeatureControl.
-// Return true if the option was applied recognized.
-bool LanguageFeatureControl::applyCLIOption(
-    std::string_view input, bool insensitive) {
-  llvm::StringRef inputRef{input};
-  if (auto result = FortranFeaturesHelpers::parseCLILanguageFeature(
-          inputRef, insensitive)) {
-    EnableWarning(result->second, result->first);
-    return true;
-  } else if (auto result = FortranFeaturesHelpers::parseCLIUsageWarning(
-                 inputRef, insensitive)) {
-    EnableWarning(result->second, result->first);
-    return true;
-  }
-  return false;
+void LanguageFeatureControl::replaceCliCanonicalSpelling(
+    UsageWarning w, std::string input) {
+  std::string_view old{usageWarningCliCanonicalSpelling_[EnumToInt(w)]};
+  cliOptions_.erase(std::string{old});
+  usageWarningCliCanonicalSpelling_[EnumToInt(w)] = input;
+  cliOptions_.insert({input, {w}});
 }
 
 std::vector<const char *> LanguageFeatureControl::GetNames(
diff --git a/flang/unittests/Common/EnumClassTests.cpp b/flang/unittests/Common/EnumClassTests.cpp
index c9224a8ceba54..5034ff4f0179e 100644
--- a/flang/unittests/Common/EnumClassTests.cpp
+++ b/flang/unittests/Common/EnumClassTests.cpp
@@ -14,7 +14,6 @@ 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");
@@ -22,23 +21,15 @@ TEST(EnumClassTest, EnumToString) {
   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});
+TEST(EnumClassTest, EnumClassForEach) {
+  std::string result;
+  bool first{true};
+  ForEachTestEnum([&](auto e) {
+    if (!first) {
+      result += ", ";
+    }
+    result += EnumToString(e);
+    first = false;
+  });
+  ASSERT_EQ(result, "One, Two, Three");
 }
diff --git a/flang/unittests/Common/FortranFeaturesTest.cpp b/flang/unittests/Common/FortranFeaturesTest.cpp
index 367500e46af20..2093c42a449eb 100644
--- a/flang/unittests/Common/FortranFeaturesTest.cpp
+++ b/flang/unittests/Common/FortranFeaturesTest.cpp
@@ -8,68 +8,554 @@
 
 #include "gtest/gtest.h"
 #include "flang/Support/Fortran-features.h"
-#include <optional>
 
-namespace Fortran::common::FortranFeaturesHelpers {
+namespace Fortran::common::featuresHelpers {
 
-optional<std::pair<bool, UsageWarning>> parseCLIUsageWarning(
-    llvm::StringRef input, bool insensitive);
-TEST(EnumClassTest, ParseCLIUsageWarning) {
-  EXPECT_EQ((parseCLIUsageWarning("no-twenty-one", false)), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("twenty-one", false)), std::nullopt);
-  EXPECT_EQ(
-      (parseCLIUsageWarning("no-seven-seven-seven", false)), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("seven-seven-seven", false)), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("no", false)), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("", false)), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("no-", false)), std::nullopt);
+std::string CamelCaseToLowerCaseHyphenated(std::string_view x);
 
-  EXPECT_EQ(parseCLIUsageWarning("Portability", false), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("no-portability", false)),
-      (std::optional{std::pair{false, UsageWarning::Portability}}));
-  EXPECT_EQ((parseCLIUsageWarning("portability", false)),
-      (std::optional{std::pair{true, UsageWarning::Portability}}));
-  EXPECT_EQ((parseCLIUsageWarning("no-pointer-to-undefinable", false)),
-      (std::optional{std::pair{false, UsageWarning::PointerToUndefinable}}));
-  EXPECT_EQ((parseCLIUsageWarning("pointer-to-undefinable", false)),
-      (std::optional{std::pair{true, UsageWarning::PointerToUndefinable}}));
-  EXPECT_EQ(parseCLIUsageWarning("PointerToUndefinable", false), std::nullopt);
-  EXPECT_EQ(
-      parseCLIUsageWarning("NoPointerToUndefinable", false), std::nullopt);
-  EXPECT_EQ(parseCLIUsageWarning("pointertoundefinable", false), std::nullopt);
-  EXPECT_EQ(
-      parseCLIUsageWarning("nopointertoundefinable", false), std::nullopt);
-
-  EXPECT_EQ((parseCLIUsageWarning("no-twenty-one", true)), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("twenty-one", true)), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("no-seven-seven-seven", true)), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("seven-seven-seven", true)), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("no", true)), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("", true)), std::nullopt);
-  EXPECT_EQ((parseCLIUsageWarning("no-", true)), std::nullopt);
-
-  EXPECT_EQ(parseCLIUsageWarning("Portability", true),
-      (std::optional{std::pair{true, UsageWarning::Portability}}));
-  EXPECT_EQ(parseCLIUsageWarning("no-portability", true),
-      (std::optional{std::pair{false, UsageWarning::Portability}}));
-  EXPECT_EQ((parseCLIUsageWarning("portability", true)),
-      (std::optional{std::pair{true, UsageWarning::Portability}}));
-  EXPECT_EQ((parseCLIUsageWarning("no-pointer-to-undefinable", true)),
-      (std::optional{std::pair{false, UsageWarning::PointerToUndefinable}}));
-  EXPECT_EQ((parseCLIUsageWarning("pointer-to-undefinable", true)),
-      (std::optional{std::pair{true, UsageWarning::PointerToUndefinable}}));
-  EXPECT_EQ(parseCLIUsageWarning("PointerToUndefinable", true),
-      (std::optional{std::pair{true, UsageWarning::PointerToUndefinable}}));
-  EXPECT_EQ(parseCLIUsageWarning("NoPointerToUndefinable", true),
-      (std::optional{std::pair{false, UsageWarning::PointerToUndefinable}}));
-  EXPECT_EQ(parseCLIUsageWarning("pointertoundefinable", true),
-      (std::optional{std::pair{true, UsageWarning::PointerToUndefinable}}));
-  EXPECT_EQ(parseCLIUsageWarning("nopointertoundefinable", true),
-      (std::optional{std::pair{false, UsageWarning::PointerToUndefinable}}));
-
-  EXPECT_EQ(parseCLIUsageWarning("f202x-allocatable-breaking-change", false),
-      (std::optional{
-          std::pair{true, UsageWarning::F202xAllocatableBreakingChange}}));
+TEST(FortranFeaturesTest, CamelCaseToLowerCaseHyphenated) {
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::BackslashEscapes)),
+      "backslash-escapes");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::OldDebugLines)),
+      "old-debug-lines");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(
+                LanguageFeature::FixedFormContinuationWithColumn1Ampersand)),
+      "fixed-form-continuation-with-column1-ampersand");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::LogicalAbbreviations)),
+      "logical-abbreviations");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::XOROperator)),
+      "xor-operator");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::PunctuationInNames)),
+      "punctuation-in-names");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::OptionalFreeFormSpace)),
+      "optional-free-form-space");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::BOZExtensions)),
+      "boz-extensions");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::EmptyStatement)),
+      "empty-statement");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::AlternativeNE)),
+      "alternative-ne");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ExecutionPartNamelist)),
+      "execution-part-namelist");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::DECStructures)),
+      "dec-structures");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::DoubleComplex)),
+      "double-complex");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::Byte)),
+      "byte");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::StarKind)),
+      "star-kind");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ExponentMatchingKindParam)),
+      "exponent-matching-kind-param");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::QuadPrecision)),
+      "quad-precision");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::SlashInitialization)),
+      "slash-initialization");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::TripletInArrayConstructor)),
+      "triplet-in-array-constructor");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::MissingColons)),
+      "missing-colons");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::SignedComplexLiteral)),
+      "signed-complex-literal");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::OldStyleParameter)),
+      "old-style-parameter");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ComplexConstructor)),
+      "complex-constructor");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::PercentLOC)),
+      "percent-loc");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::SignedMultOperand)),
+      "signed-mult-operand");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::FileName)),
+      "file-name");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::Carriagecontrol)),
+      "carriagecontrol");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::Convert)),
+      "convert");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::Dispose)),
+      "dispose");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::IOListLeadingComma)),
+      "io-list-leading-comma");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::AbbreviatedEditDescriptor)),
+      "abbreviated-edit-descriptor");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ProgramParentheses)),
+      "program-parentheses");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::PercentRefAndVal)),
+      "percent-ref-and-val");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::OmitFunctionDummies)),
+      "omit-function-dummies");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::CrayPointer)),
+      "cray-pointer");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::Hollerith)),
+      "hollerith");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ArithmeticIF)),
+      "arithmetic-if");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::Assign)),
+      "assign");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::AssignedGOTO)),
+      "assigned-goto");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::Pause)),
+      "pause");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::OpenACC)),
+      "open-acc");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::OpenMP)),
+      "open-mp");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::CUDA)),
+      "cuda");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::CruftAfterAmpersand)),
+      "cruft-after-ampersand");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ClassicCComments)),
+      "classic-c-comments");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::AdditionalFormats)),
+      "additional-formats");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::BigIntLiterals)),
+      "big-int-literals");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::RealDoControls)),
+      "real-do-controls");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::EquivalenceNumericWithCharacter)),
+      "equivalence-numeric-with-character");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::EquivalenceNonDefaultNumeric)),
+      "equivalence-non-default-numeric");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::EquivalenceSameNonSequence)),
+      "equivalence-same-non-sequence");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::AdditionalIntrinsics)),
+      "additional-intrinsics");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::AnonymousParents)),
+      "anonymous-parents");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::OldLabelDoEndStatements)),
+      "old-label-do-end-statements");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::LogicalIntegerAssignment)),
+      "logical-integer-assignment");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::EmptySourceFile)),
+      "empty-source-file");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ProgramReturn)),
+      "program-return");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ImplicitNoneTypeNever)),
+      "implicit-none-type-never");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ImplicitNoneTypeAlways)),
+      "implicit-none-type-always");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ImplicitNoneExternal)),
+      "implicit-none-external");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ForwardRefImplicitNone)),
+      "forward-ref-implicit-none");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::OpenAccessAppend)),
+      "open-access-append");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::BOZAsDefaultInteger)),
+      "boz-as-default-integer");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::DistinguishableSpecifics)),
+      "distinguishable-specifics");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::DefaultSave)),
+      "default-save");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::PointerInSeqType)),
+      "pointer-in-seq-type");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::NonCharacterFormat)),
+      "non-character-format");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::SaveMainProgram)),
+      "save-main-program");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::SaveBigMainProgramVariables)),
+      "save-big-main-program-variables");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::DistinctArrayConstructorLengths)),
+      "distinct-array-constructor-lengths");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::PPCVector)),
+      "ppc-vector");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::RelaxedIntentInChecking)),
+      "relaxed-intent-in-checking");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ForwardRefImplicitNoneData)),
+      "forward-ref-implicit-none-data");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::NullActualForAllocatable)),
+      "null-actual-for-allocatable");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(
+                LanguageFeature::ActualIntegerConvertedToSmallerKind)),
+      "actual-integer-converted-to-smaller-kind");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::HollerithOrCharacterAsBOZ)),
+      "hollerith-or-character-as-boz");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::BindingAsProcedure)),
+      "binding-as-procedure");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::StatementFunctionExtensions)),
+      "statement-function-extensions");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(
+                LanguageFeature::UseGenericIntrinsicWhenSpecificDoesntMatch)),
+      "use-generic-intrinsic-when-specific-doesnt-match");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::DataStmtExtensions)),
+      "data-stmt-extensions");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::RedundantContiguous)),
+      "redundant-contiguous");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::RedundantAttribute)),
+      "redundant-attribute");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::InitBlankCommon)),
+      "init-blank-common");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::EmptyBindCDerivedType)),
+      "empty-bind-c-derived-type");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::MiscSourceExtensions)),
+      "misc-source-extensions");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::AllocateToOtherLength)),
+      "allocate-to-other-length");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::LongNames)),
+      "long-names");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::IntrinsicAsSpecific)),
+      "intrinsic-as-specific");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::BenignNameClash)),
+      "benign-name-clash");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::BenignRedundancy)),
+      "benign-redundancy");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(
+                LanguageFeature::NullMoldAllocatableComponentValue)),
+      "null-mold-allocatable-component-value");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::NopassScalarBase)),
+      "nopass-scalar-base");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::MiscUseExtensions)),
+      "misc-use-extensions");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ImpliedDoIndexScope)),
+      "implied-do-index-scope");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::DistinctCommonSizes)),
+      "distinct-common-sizes");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::OddIndexVariableRestrictions)),
+      "odd-index-variable-restrictions");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::IndistinguishableSpecifics)),
+      "indistinguishable-specifics");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::SubroutineAndFunctionSpecifics)),
+      "subroutine-and-function-specifics");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::EmptySequenceType)),
+      "empty-sequence-type");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::NonSequenceCrayPointee)),
+      "non-sequence-cray-pointee");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::BranchIntoConstruct)),
+      "branch-into-construct");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::BadBranchTarget)),
+      "bad-branch-target");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::HollerithPolymorphic)),
+      "hollerith-polymorphic");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ListDirectedSize)),
+      "list-directed-size");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::NonBindCInteroperability)),
+      "non-bind-c-interoperability");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::CudaManaged)),
+      "cuda-managed");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::CudaUnified)),
+      "cuda-unified");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::
+                    PolymorphicActualAllocatableOrPointerToMonomorphicDummy)),
+      "polymorphic-actual-allocatable-or-pointer-to-monomorphic-dummy");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::RelaxedPureDummy)),
+      "relaxed-pure-dummy");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(
+                LanguageFeature::UndefinableAsynchronousOrVolatileActual)),
+      "undefinable-asynchronous-or-volatile-actual");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::AutomaticInMainProgram)),
+      "automatic-in-main-program");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::PrintCptr)),
+      "print-cptr");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::SavedLocalInSpecExpr)),
+      "saved-local-in-spec-expr");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::PrintNamelist)),
+      "print-namelist");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(
+                LanguageFeature::AssumedRankPassedToNonAssumedRank)),
+      "assumed-rank-passed-to-non-assumed-rank");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::IgnoreIrrelevantAttributes)),
+      "ignore-irrelevant-attributes");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(LanguageFeature::Unsigned)),
+      "unsigned");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::AmbiguousStructureConstructor)),
+      "ambiguous-structure-constructor");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ContiguousOkForSeqAssociation)),
+      "contiguous-ok-for-seq-association");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(LanguageFeature::ForwardRefExplicitTypeDummy)),
+      "forward-ref-explicit-type-dummy");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::Portability)),
+      "portability");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::PointerToUndefinable)),
+      "pointer-to-undefinable");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::NonTargetPassedToTarget)),
+      "non-target-passed-to-target");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::PointerToPossibleNoncontiguous)),
+      "pointer-to-possible-noncontiguous");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::ShortCharacterActual)),
+      "short-character-actual");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::ShortArrayActual)),
+      "short-array-actual");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::ImplicitInterfaceActual)),
+      "implicit-interface-actual");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::PolymorphicTransferArg)),
+      "polymorphic-transfer-arg");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::PointerComponentTransferArg)),
+      "pointer-component-transfer-arg");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::TransferSizePresence)),
+      "transfer-size-presence");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::F202XAllocatableBreakingChange)),
+      "f202-x-allocatable-breaking-change");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::OptionalMustBePresent)),
+      "optional-must-be-present");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::CommonBlockPadding)),
+      "common-block-padding");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::LogicalVsCBool)),
+      "logical-vs-c-bool");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::BindCCharLength)),
+      "bind-c-char-length");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::ProcDummyArgShapes)),
+      "proc-dummy-arg-shapes");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::ExternalNameConflict)),
+      "external-name-conflict");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::FoldingException)),
+      "folding-exception");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::FoldingAvoidsRuntimeCrash)),
+      "folding-avoids-runtime-crash");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::FoldingValueChecks)),
+      "folding-value-checks");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::FoldingFailure)),
+      "folding-failure");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::FoldingLimit)),
+      "folding-limit");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::Interoperability)),
+      "interoperability");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::CharacterInteroperability)),
+      "character-interoperability");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::Bounds)),
+      "bounds");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::Preprocessing)),
+      "preprocessing");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::Scanning)),
+      "scanning");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::OpenAccUsage)),
+      "open-acc-usage");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::ProcPointerCompatibility)),
+      "proc-pointer-compatibility");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::VoidMold)),
+      "void-mold");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::KnownBadImplicitInterface)),
+      "known-bad-implicit-interface");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::EmptyCase)),
+      "empty-case");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::CaseOverflow)),
+      "case-overflow");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::CUDAUsage)),
+      "cuda-usage");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::IgnoreTKRUsage)),
+      "ignore-tkr-usage");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::ExternalInterfaceMismatch)),
+      "external-interface-mismatch");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::DefinedOperatorArgs)),
+      "defined-operator-args");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::Final)),
+      "final");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::ZeroDoStep)),
+      "zero-do-step");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::UnusedForallIndex)),
+      "unused-forall-index");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::OpenMPUsage)),
+      "open-mp-usage");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::DataLength)),
+      "data-length");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::IgnoredDirective)),
+      "ignored-directive");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::HomonymousSpecific)),
+      "homonymous-specific");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::HomonymousResult)),
+      "homonymous-result");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::IgnoredIntrinsicFunctionType)),
+      "ignored-intrinsic-function-type");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::PreviousScalarUse)),
+      "previous-scalar-use");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::RedeclaredInaccessibleComponent)),
+      "redeclared-inaccessible-component");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::ImplicitShared)),
+      "implicit-shared");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::IndexVarRedefinition)),
+      "index-var-redefinition");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::IncompatibleImplicitInterfaces)),
+      "incompatible-implicit-interfaces");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::VectorSubscriptFinalization)),
+      "vector-subscript-finalization");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::UndefinedFunctionResult)),
+      "undefined-function-result");
+  EXPECT_EQ(
+      CamelCaseToLowerCaseHyphenated(EnumToString(UsageWarning::UselessIomsg)),
+      "useless-iomsg");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::MismatchingDummyProcedure)),
+      "mismatching-dummy-procedure");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::SubscriptedEmptyArray)),
+      "subscripted-empty-array");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::UnsignedLiteralTruncation)),
+      "unsigned-literal-truncation");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(
+                UsageWarning::CompatibleDeclarationsFromDistinctModules)),
+      "compatible-declarations-from-distinct-modules");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(
+                UsageWarning::NullActualForDefaultIntentAllocatable)),
+      "null-actual-for-default-intent-allocatable");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(EnumToString(
+                UsageWarning::UseAssociationIntoSameNameSubprogram)),
+      "use-association-into-same-name-subprogram");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::HostAssociatedIntentOutInSpecExpr)),
+      "host-associated-intent-out-in-spec-expr");
+  EXPECT_EQ(CamelCaseToLowerCaseHyphenated(
+                EnumToString(UsageWarning::NonVolatilePointerToVolatile)),
+      "non-volatile-pointer-to-volatile");
 }
 
-} // namespace Fortran::common::FortranFeaturesHelpers
+} // namespace Fortran::common::featuresHelpers



More information about the flang-commits mailing list