llvm-project/flang/lib/Support/Fortran-features.cpp
Andre Kuhlenschmidt 83b462af17
[flang][CLI] Have the CLI hint the flag to disable a warning (#144767)
Adds a hint to the warning message to disable a warning and updates the
tests to expect this.

Also fixes a bug in the storage of canonical spelling of error flags so
that they are not used after free.
2025-06-30 10:17:05 -07:00

266 lines
10 KiB
C++

//===-- lib/Support/Fortran-features.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/Support/Fortran-features.h"
#include "flang/Common/idioms.h"
#include "flang/Parser/characters.h"
#include "flang/Support/Fortran.h"
#include <string>
#include <string_view>
namespace Fortran::common {
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(x.substr(wordStart, wordEnd - wordStart));
wordStart = wordEnd;
}
}
// We went one past the end of the last word.
result.push_back(x.substr(wordStart, wordEnd - wordStart));
return result;
}
// Namespace for helper functions for parsing Cli options used instead of static
// so that there can be unit tests for this function.
namespace details {
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 details
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{details::CamelCaseToLowerCaseHyphenated(name)};
cliOptions_.insert({cliOption, {feature}});
languageFeatureCliCanonicalSpelling_[EnumToInt(feature)] =
std::move(cliOption);
});
ForEachUsageWarning([&](auto warning) {
std::string_view name{Fortran::common::EnumToString(warning)};
std::string cliOption{details::CamelCaseToLowerCaseHyphenated(name)};
cliOptions_.insert({cliOption, {warning}});
usageWarningCliCanonicalSpelling_[EnumToInt(warning)] =
std::move(cliOption);
});
// These features must be explicitly enabled by command line options.
disable_.set(LanguageFeature::OldDebugLines);
disable_.set(LanguageFeature::OpenACC);
disable_.set(LanguageFeature::OpenMP);
disable_.set(LanguageFeature::CUDA); // !@cuf
disable_.set(LanguageFeature::CudaManaged);
disable_.set(LanguageFeature::CudaUnified);
disable_.set(LanguageFeature::ImplicitNoneTypeNever);
disable_.set(LanguageFeature::ImplicitNoneTypeAlways);
disable_.set(LanguageFeature::ImplicitNoneExternal);
disable_.set(LanguageFeature::DefaultSave);
disable_.set(LanguageFeature::SaveMainProgram);
// These features, if enabled, conflict with valid standard usage,
// so there are disabled here by default.
disable_.set(LanguageFeature::BackslashEscapes);
disable_.set(LanguageFeature::LogicalAbbreviations);
disable_.set(LanguageFeature::XOROperator);
disable_.set(LanguageFeature::OldStyleParameter);
// Possibly an accidental "feature" of nvfortran.
disable_.set(LanguageFeature::AssumedRankPassedToNonAssumedRank);
// These warnings are enabled by default, but only because they used
// to be unconditional. TODO: prune this list
warnLanguage_.set(LanguageFeature::ExponentMatchingKindParam);
warnLanguage_.set(LanguageFeature::RedundantAttribute);
warnLanguage_.set(LanguageFeature::SubroutineAndFunctionSpecifics);
warnLanguage_.set(LanguageFeature::EmptySequenceType);
warnLanguage_.set(LanguageFeature::NonSequenceCrayPointee);
warnLanguage_.set(LanguageFeature::BranchIntoConstruct);
warnLanguage_.set(LanguageFeature::BadBranchTarget);
warnLanguage_.set(LanguageFeature::HollerithPolymorphic);
warnLanguage_.set(LanguageFeature::ListDirectedSize);
warnLanguage_.set(LanguageFeature::IgnoreIrrelevantAttributes);
warnLanguage_.set(LanguageFeature::AmbiguousStructureConstructor);
warnUsage_.set(UsageWarning::ShortArrayActual);
warnUsage_.set(UsageWarning::FoldingException);
warnUsage_.set(UsageWarning::FoldingAvoidsRuntimeCrash);
warnUsage_.set(UsageWarning::FoldingValueChecks);
warnUsage_.set(UsageWarning::FoldingFailure);
warnUsage_.set(UsageWarning::FoldingLimit);
warnUsage_.set(UsageWarning::Interoperability);
// CharacterInteroperability warnings about length are off by default
warnUsage_.set(UsageWarning::Bounds);
warnUsage_.set(UsageWarning::Preprocessing);
warnUsage_.set(UsageWarning::Scanning);
warnUsage_.set(UsageWarning::OpenAccUsage);
warnUsage_.set(UsageWarning::ProcPointerCompatibility);
warnUsage_.set(UsageWarning::VoidMold);
warnUsage_.set(UsageWarning::KnownBadImplicitInterface);
warnUsage_.set(UsageWarning::EmptyCase);
warnUsage_.set(UsageWarning::CaseOverflow);
warnUsage_.set(UsageWarning::CUDAUsage);
warnUsage_.set(UsageWarning::IgnoreTKRUsage);
warnUsage_.set(UsageWarning::ExternalInterfaceMismatch);
warnUsage_.set(UsageWarning::DefinedOperatorArgs);
warnUsage_.set(UsageWarning::Final);
warnUsage_.set(UsageWarning::ZeroDoStep);
warnUsage_.set(UsageWarning::UnusedForallIndex);
warnUsage_.set(UsageWarning::OpenMPUsage);
warnUsage_.set(UsageWarning::DataLength);
warnUsage_.set(UsageWarning::IgnoredDirective);
warnUsage_.set(UsageWarning::HomonymousSpecific);
warnUsage_.set(UsageWarning::HomonymousResult);
warnUsage_.set(UsageWarning::IgnoredIntrinsicFunctionType);
warnUsage_.set(UsageWarning::PreviousScalarUse);
warnUsage_.set(UsageWarning::RedeclaredInaccessibleComponent);
warnUsage_.set(UsageWarning::ImplicitShared);
warnUsage_.set(UsageWarning::IndexVarRedefinition);
warnUsage_.set(UsageWarning::IncompatibleImplicitInterfaces);
warnUsage_.set(UsageWarning::VectorSubscriptFinalization);
warnUsage_.set(UsageWarning::UndefinedFunctionResult);
warnUsage_.set(UsageWarning::UselessIomsg);
warnUsage_.set(UsageWarning::UnsignedLiteralTruncation);
warnUsage_.set(UsageWarning::NullActualForDefaultIntentAllocatable);
warnUsage_.set(UsageWarning::UseAssociationIntoSameNameSubprogram);
warnUsage_.set(UsageWarning::HostAssociatedIntentOutInSpecExpr);
warnUsage_.set(UsageWarning::NonVolatilePointerToVolatile);
// New warnings, on by default
warnLanguage_.set(LanguageFeature::SavedLocalInSpecExpr);
warnLanguage_.set(LanguageFeature::NullActualForAllocatable);
}
std::optional<LanguageControlFlag> LanguageFeatureControl::FindWarning(
std::string_view input) {
bool negated{false};
if (input.size() > 3 && input.substr(0, 3) == "no-") {
negated = true;
input = input.substr(3);
}
if (auto it{cliOptions_.find(std::string{input})}; it != cliOptions_.end()) {
return std::make_pair(it->second, !negated);
}
return std::nullopt;
}
bool LanguageFeatureControl::EnableWarning(std::string_view input) {
if (auto warningAndEnabled{FindWarning(input)}) {
EnableWarning(warningAndEnabled->first, warningAndEnabled->second);
return true;
}
return false;
}
void LanguageFeatureControl::ReplaceCliCanonicalSpelling(
LanguageFeature f, std::string input) {
cliOptions_.erase(languageFeatureCliCanonicalSpelling_[EnumToInt(f)]);
cliOptions_.insert({input, {f}});
languageFeatureCliCanonicalSpelling_[EnumToInt(f)] = std::move(input);
}
void LanguageFeatureControl::ReplaceCliCanonicalSpelling(
UsageWarning w, std::string input) {
cliOptions_.erase(usageWarningCliCanonicalSpelling_[EnumToInt(w)]);
cliOptions_.insert({input, {w}});
usageWarningCliCanonicalSpelling_[EnumToInt(w)] = std::move(input);
}
std::vector<const char *> LanguageFeatureControl::GetNames(
LogicalOperator opr) const {
std::vector<const char *> result;
result.push_back(AsFortran(opr));
if (opr == LogicalOperator::Neqv && IsEnabled(LanguageFeature::XOROperator)) {
result.push_back(".xor.");
}
if (IsEnabled(LanguageFeature::LogicalAbbreviations)) {
switch (opr) {
SWITCH_COVERS_ALL_CASES
case LogicalOperator::And:
result.push_back(".a.");
break;
case LogicalOperator::Or:
result.push_back(".o.");
break;
case LogicalOperator::Not:
result.push_back(".n.");
break;
case LogicalOperator::Neqv:
if (IsEnabled(LanguageFeature::XOROperator)) {
result.push_back(".x.");
}
break;
case LogicalOperator::Eqv:
break;
}
}
return result;
}
std::vector<const char *> LanguageFeatureControl::GetNames(
RelationalOperator opr) const {
switch (opr) {
SWITCH_COVERS_ALL_CASES
case RelationalOperator::LT:
return {".lt.", "<"};
case RelationalOperator::LE:
return {".le.", "<="};
case RelationalOperator::EQ:
return {".eq.", "=="};
case RelationalOperator::GE:
return {".ge.", ">="};
case RelationalOperator::GT:
return {".gt.", ">"};
case RelationalOperator::NE:
if (IsEnabled(LanguageFeature::AlternativeNE)) {
return {".ne.", "/=", "<>"};
} else {
return {".ne.", "/="};
}
}
}
void LanguageFeatureControl::WarnOnAllNonstandard(bool yes) {
warnAllLanguage_ = 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);
warnLanguage_.set(LanguageFeature::OpenACC, false);
warnLanguage_.set(LanguageFeature::CUDA, false);
}
}
void LanguageFeatureControl::WarnOnAllUsage(bool yes) {
warnAllUsage_ = yes;
warnUsage_.reset();
if (yes) {
disableAllWarnings_ = false;
warnUsage_.flip();
}
}
} // namespace Fortran::common