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