https://github.com/earnol updated https://github.com/llvm/llvm-project/pull/197927
>From eb2369d69bb6fdce14948fe6ef36aa770d4479ef Mon Sep 17 00:00:00 2001 From: Vladislav Aranov <[email protected]> Date: Fri, 15 May 2026 00:38:26 +0200 Subject: [PATCH] [clang-tidy] Add checker alias framework and restore hicpp aliases The change https://github.com/llvm/llvm-project/issues/183462 and subsequent changes removed all hicpp checkers with giving little time to adapt. This change adds a generic check alias mechanism to clang-tidy that supports: registering aliases, central alias management table, bidirectional disable semantics, NOLINT suppression using alias names, canonical name preference, opt-in alias usage notification feature. --- clang-tools-extra/clang-tidy/CMakeLists.txt | 9 +- clang-tools-extra/clang-tidy/ClangTidy.cpp | 2 + .../ClangTidyDiagnosticConsumer.cpp | 109 +++++++++++++++++- .../clang-tidy/ClangTidyDiagnosticConsumer.h | 7 ++ .../clang-tidy/ClangTidyForceLinker.h | 5 + .../clang-tidy/ClangTidyModule.cpp | 14 ++- .../clang-tidy/ClangTidyModule.h | 21 ++++ .../clang-tidy/aliases/CMakeLists.txt | 21 ++++ .../clang-tidy/aliases/ClangTidyAliases.cpp | 95 +++++++++++++++ .../clang-tidy/aliases/ClangTidyAliases.h | 57 +++++++++ .../clang-tidy/cert/CERTTidyModule.cpp | 5 - .../clang-tidy/tool/ClangTidyMain.cpp | 18 +++ clang-tools-extra/docs/ReleaseNotes.rst | 7 ++ clang-tools-extra/docs/clang-tidy/index.rst | 3 + .../clang-tidy/checkers/cert/oop11-cpp.cpp | 2 +- .../clang-tidy/checkers/check-aliasing.cpp | 49 ++++++++ 16 files changed, 409 insertions(+), 15 deletions(-) create mode 100644 clang-tools-extra/clang-tidy/aliases/CMakeLists.txt create mode 100644 clang-tools-extra/clang-tidy/aliases/ClangTidyAliases.cpp create mode 100644 clang-tools-extra/clang-tidy/aliases/ClangTidyAliases.h create mode 100644 clang-tools-extra/test/clang-tidy/checkers/check-aliasing.cpp diff --git a/clang-tools-extra/clang-tidy/CMakeLists.txt b/clang-tools-extra/clang-tidy/CMakeLists.txt index 9ee9255fbe17b..5d71355fc0c6e 100644 --- a/clang-tools-extra/clang-tidy/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/CMakeLists.txt @@ -19,6 +19,9 @@ add_clang_library(clangTidy STATIC GlobList.cpp NoLintDirectiveHandler.cpp + LINK_LIBS + clangTidyAliasesModule + DEPENDS ClangSACheckers omp_gen @@ -50,8 +53,9 @@ endif() # Checks. # If you add a check, also add it to ClangTidyForceLinker.h in this directory. -add_subdirectory(android) add_subdirectory(abseil) +add_subdirectory(aliases) +add_subdirectory(android) add_subdirectory(altera) add_subdirectory(boost) add_subdirectory(bugprone) @@ -77,8 +81,9 @@ add_subdirectory(portability) add_subdirectory(readability) add_subdirectory(zircon) set(ALL_CLANG_TIDY_CHECKS - clangTidyAndroidModule clangTidyAbseilModule + clangTidyAliasesModule + clangTidyAndroidModule clangTidyAlteraModule clangTidyBoostModule clangTidyBugproneModule diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 05c8fd02fe86a..a8468a6e333d9 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -360,6 +360,7 @@ ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory( std::unique_ptr<ClangTidyModule> Module = E.instantiate(); Module->addCheckFactories(*CheckFactories); } + CheckFactories->resolveAliases(); } #if CLANG_TIDY_ENABLE_STATIC_ANALYZER @@ -720,6 +721,7 @@ ChecksAndOptions getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers, ClangTidyModuleRegistry::entries()) { Module.instantiate()->addCheckFactories(Factories); } + Factories.resolveAliases(); for (const auto &Factory : Factories) Result.Checks.insert(Factory.getKey()); diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp index 88d0a433bc7fb..c39c044be286a 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -19,6 +19,7 @@ #include "ClangTidyOptions.h" #include "GlobList.h" #include "NoLintDirectiveHandler.h" +#include "aliases/ClangTidyAliases.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/Attr.h" @@ -27,6 +28,7 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/LLVM.h" #include "clang/Basic/SourceManager.h" #include "clang/Frontend/DiagnosticRenderer.h" #include "clang/Lex/Lexer.h" @@ -34,9 +36,12 @@ #include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" // IWYU pragma: keep #include "llvm/ADT/StringMap.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Regex.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> #include <optional> #include <tuple> #include <utility> @@ -216,8 +221,23 @@ bool ClangTidyContext::shouldSuppressDiagnostic( SmallVectorImpl<tooling::Diagnostic> &NoLintErrors, bool AllowIO, bool EnableNoLintBlocks) { const std::string CheckName = getCheckName(Info.getID()); - return NoLintHandler.shouldSuppress(DiagLevel, Info, CheckName, NoLintErrors, - AllowIO, EnableNoLintBlocks); + if (NoLintHandler.shouldSuppress(DiagLevel, Info, CheckName, NoLintErrors, + AllowIO, EnableNoLintBlocks)) + return true; + // Also check if any alias name for this check is suppressed. + for (const StringRef Alias : + ClangTidyAliases::getAliasesForCanonical(CheckName)) { + if (NoLintHandler.shouldSuppress(DiagLevel, Info, std::string(Alias), + NoLintErrors, AllowIO, + EnableNoLintBlocks)) { + if (NotifyAliases) + llvm::errs() << "note: '" << Alias + << "' is an alias for canonical name '" << CheckName + << "' [checker-alias]\n"; + return true; + } + } + return false; } void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) { @@ -236,13 +256,90 @@ static bool parseFileExtensions(llvm::ArrayRef<std::string> AllFileExtensions, return true; } +/// Expand check alias patterns in the checks string. +/// For each glob pattern, if it matches an alias name, also add the canonical +/// name (and vice versa) with the same +/- prefix. This ensures that disabling +/// either name disables both. +static std::string expandCheckAliases(StringRef Checks) { + if (Checks.empty()) + return {}; + + std::string Result; + llvm::raw_string_ostream OS(Result); + bool First = true; + + StringRef Remaining = Checks; + while (!Remaining.empty()) { + // Trim leading whitespace/commas. + Remaining = Remaining.ltrim(" \t\n"); + if (Remaining.empty()) + break; + if (Remaining.front() == ',') { + Remaining = Remaining.drop_front(); + continue; + } + + // Extract the prefix (- or nothing). + const bool IsNegative = Remaining.consume_front("-"); + // Extract the glob text up to the next comma or newline. + const StringRef Glob = + Remaining.substr(0, Remaining.find_first_of(",\n")).trim(); + Remaining = Remaining.substr(Glob.size()); + + if (Glob.empty()) + continue; + + // Write the original pattern. + if (!First) + OS << ','; + if (IsNegative) + OS << '-'; + OS << Glob; + First = false; + + // Check if this glob matches any alias or canonical name. + // Create a regex from the glob for matching. + SmallString<128> RegexText("^"); + for (const char C : Glob) { + if (C == '*') { + RegexText.append(".*"); + } else if (StringRef("()^$|+?.[]\\{}").contains(C)) { + RegexText.push_back('\\'); + RegexText.push_back(C); + } else { + RegexText.push_back(C); + } + } + RegexText.push_back('$'); + const llvm::Regex Re(RegexText); + + for (const auto &Entry : ClangTidyAliases::activeEntries()) { + if (Re.match(Entry.Alias)) { + // Alias enabled or disabled: also enable/disable the canonical. + OS << ','; + if (IsNegative) + OS << '-'; + OS << Entry.Canonical; + } else if (IsNegative && Re.match(Entry.Canonical)) { + // Canonical disabled: also disable the alias. + OS << ",-"; + OS << Entry.Alias; + } + } + } + + return Result; +} + void ClangTidyContext::setCurrentFile(StringRef File) { CurrentFile = std::string(File); CurrentOptions = getOptionsForFile(CurrentFile); - CheckFilter = std::make_unique<CachedGlobList>( - StringRef(getOptions().Checks.value_or(""))); - WarningAsErrorFilter = std::make_unique<CachedGlobList>( - StringRef(getOptions().WarningsAsErrors.value_or(""))); + ExpandedChecks = expandCheckAliases(getOptions().Checks.value_or("")); + CheckFilter = std::make_unique<CachedGlobList>(StringRef(ExpandedChecks)); + ExpandedWarningsAsErrors = + expandCheckAliases(getOptions().WarningsAsErrors.value_or("")); + WarningAsErrorFilter = + std::make_unique<CachedGlobList>(StringRef(ExpandedWarningsAsErrors)); static const std::vector<std::string> EmptyFileExtensions; if (!parseFileExtensions(getOptions().HeaderFileExtensions ? *getOptions().HeaderFileExtensions diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h index c76f58bc4cc86..168544eabfc0e 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h @@ -150,6 +150,9 @@ class ClangTidyContext { /// exposed as a 'clang-diagnostic-*' check. bool isCompilerDiagnostic(unsigned DiagnosticID) const; + /// Enable alias usage notifications. + void setNotifyAliases(bool Notify) { NotifyAliases = Notify; } + /// Returns \c true if the check is enabled for the \c CurrentFile. /// /// The \c CurrentFile can be changed using \c setCurrentFile. @@ -247,9 +250,13 @@ class ClangTidyContext { std::string CurrentFile; ClangTidyOptions CurrentOptions; + std::string ExpandedChecks; + std::string ExpandedWarningsAsErrors; std::unique_ptr<CachedGlobList> CheckFilter; std::unique_ptr<CachedGlobList> WarningAsErrorFilter; + bool NotifyAliases = false; + FileExtensionsSet HeaderFileExtensions; FileExtensionsSet ImplementationFileExtensions; diff --git a/clang-tools-extra/clang-tidy/ClangTidyForceLinker.h b/clang-tools-extra/clang-tidy/ClangTidyForceLinker.h index 2450384016e25..590f24fb61a65 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyForceLinker.h +++ b/clang-tools-extra/clang-tidy/ClangTidyForceLinker.h @@ -18,6 +18,11 @@ extern volatile int AbseilModuleAnchorSource; [[maybe_unused]] static int AbseilModuleAnchorDestination = AbseilModuleAnchorSource; +// This anchor is used to force the linker to link the AliasesModule. +extern volatile int AliasesModuleAnchorSource; +[[maybe_unused]] static int AliasesModuleAnchorDestination = + AliasesModuleAnchorSource; + // This anchor is used to force the linker to link the AlteraModule. extern volatile int AlteraModuleAnchorSource; [[maybe_unused]] static int AlteraModuleAnchorDestination = diff --git a/clang-tools-extra/clang-tidy/ClangTidyModule.cpp b/clang-tools-extra/clang-tidy/ClangTidyModule.cpp index 976e87dffb0bf..7217dfd1e0652 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyModule.cpp @@ -12,9 +12,18 @@ #include "ClangTidyModule.h" #include "ClangTidyCheck.h" +#include "aliases/ClangTidyAliases.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" namespace clang::tidy { +/// Returns true if CheckName is an alias whose canonical check is also enabled. +static bool isRedundantAlias(StringRef CheckName, ClangTidyContext *Context) { + const StringRef Canonical = ClangTidyAliases::getCanonicalForAlias(CheckName); + return !Canonical.empty() && Context->isCheckEnabled(Canonical); +} + void ClangTidyCheckFactories::registerCheckFactory(StringRef Name, CheckFactory Factory) { Factories.insert_or_assign(Name, std::move(Factory)); @@ -24,7 +33,8 @@ std::vector<std::unique_ptr<ClangTidyCheck>> ClangTidyCheckFactories::createChecks(ClangTidyContext *Context) const { std::vector<std::unique_ptr<ClangTidyCheck>> Checks; for (const auto &[CheckName, Factory] : Factories) - if (Context->isCheckEnabled(CheckName)) + if (Context->isCheckEnabled(CheckName) && + !isRedundantAlias(CheckName, Context)) Checks.emplace_back(Factory(CheckName, Context)); return Checks; } @@ -37,6 +47,8 @@ ClangTidyCheckFactories::createChecksForLanguage( for (const auto &[CheckName, Factory] : Factories) { if (!Context->isCheckEnabled(CheckName)) continue; + if (isRedundantAlias(CheckName, Context)) + continue; std::unique_ptr<ClangTidyCheck> Check = Factory(CheckName, Context); if (Check->isLanguageVersionSupported(LO)) Checks.push_back(std::move(Check)); diff --git a/clang-tools-extra/clang-tidy/ClangTidyModule.h b/clang-tools-extra/clang-tidy/ClangTidyModule.h index 3db92c2dab981..3d84d8f5fd6b1 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyModule.h +++ b/clang-tools-extra/clang-tidy/ClangTidyModule.h @@ -10,11 +10,14 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULE_H #include "ClangTidyOptions.h" +#include "llvm/ADT/SmallVector.h" // IWYU pragma: keep #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Registry.h" #include <functional> #include <memory> +#include <string> +#include <utility> namespace clang::tidy { @@ -65,6 +68,23 @@ class ClangTidyCheckFactories { void eraseCheck(StringRef CheckName) { Factories.erase(CheckName); } + /// Registers \p AliasName as an alias for check \p CanonicalName. + /// The alias is resolved after all modules have registered their checks. + void registerCheckAlias(StringRef AliasName, StringRef CanonicalName) { + PendingAliases.emplace_back(AliasName, CanonicalName); + } + + /// Resolves all pending aliases. Must be called after all modules have + /// registered their check factories. + void resolveAliases() { + for (const auto &[Alias, Canonical] : PendingAliases) { + auto It = Factories.find(Canonical); + if (It != Factories.end()) + Factories[Alias] = It->second; + } + PendingAliases.clear(); + } + /// Create instances of checks that are enabled. std::vector<std::unique_ptr<ClangTidyCheck>> createChecks(ClangTidyContext *Context) const; @@ -80,6 +100,7 @@ class ClangTidyCheckFactories { private: FactoryMap Factories; + SmallVector<std::pair<std::string, std::string>> PendingAliases; }; /// A clang-tidy module groups a number of \c ClangTidyChecks and gives diff --git a/clang-tools-extra/clang-tidy/aliases/CMakeLists.txt b/clang-tools-extra/clang-tidy/aliases/CMakeLists.txt new file mode 100644 index 0000000000000..4e39b71827121 --- /dev/null +++ b/clang-tools-extra/clang-tidy/aliases/CMakeLists.txt @@ -0,0 +1,21 @@ +set(LLVM_LINK_COMPONENTS + support + FrontendOpenMP + ) + +add_clang_library(clangTidyAliasesModule STATIC + ClangTidyAliases.cpp + + LINK_LIBS + clangTidy + clangTidyUtils + + DEPENDS + omp_gen + ClangDriverOptions + ) + +clang_target_link_libraries(clangTidyAliasesModule + PRIVATE + clangBasic + ) diff --git a/clang-tools-extra/clang-tidy/aliases/ClangTidyAliases.cpp b/clang-tools-extra/clang-tidy/aliases/ClangTidyAliases.cpp new file mode 100644 index 0000000000000..e22e3a16e4809 --- /dev/null +++ b/clang-tools-extra/clang-tidy/aliases/ClangTidyAliases.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// 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 "ClangTidyAliases.h" +#include "../ClangTidyModule.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" // IWYU pragma: keep +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Config/llvm-config.h" +#include <cassert> + +namespace clang::tidy { + +/// Alias table. Kept sorted by alias name for readability. +static constexpr ClangTidyAliases::Entry AliasTable[] = { + // Permanent aliases. + {"cert-dcl03-c", "misc-static-assert", ClangTidyAliases::NoExpiration}, + {"cert-oop11-cpp", "performance-move-constructor-init", + ClangTidyAliases::NoExpiration}, +}; + +ArrayRef<ClangTidyAliases::Entry> ClangTidyAliases::getReference() { + return AliasTable; +} + +bool ClangTidyAliases::isActive(const Entry &E) { + return E.ExpirationVersion <= NoExpiration || + LLVM_VERSION_MAJOR < E.ExpirationVersion; +} + +ClangTidyAliases::ActiveRange ClangTidyAliases::activeEntries() { + return llvm::make_filter_range(getReference(), isActive); +} + +StringRef ClangTidyAliases::getCanonicalForAlias(StringRef Alias) { + static const auto *Map = [] { + auto *M = new llvm::StringMap<StringRef>(); + for (const auto &Entry : activeEntries()) { + [[maybe_unused]] auto Result = + M->try_emplace(Entry.Alias, Entry.Canonical); + assert(Result.second && "Duplicate alias in ClangTidyAliases table"); + } + return M; + }(); + auto It = Map->find(Alias); + if (It != Map->end()) + return It->second; + return {}; +} + +const SmallVector<StringRef> & +ClangTidyAliases::getAliasesForCanonical(StringRef Canonical) { + static const SmallVector<StringRef> Empty; + static const auto *ReverseMap = [] { + auto *M = new llvm::StringMap<SmallVector<StringRef>>(); + for (const auto &Entry : activeEntries()) + (*M)[Entry.Canonical].push_back(Entry.Alias); + return M; + }(); + auto It = ReverseMap->find(Canonical); + if (It != ReverseMap->end()) + return It->second; + return Empty; +} + +namespace aliases { +namespace { + +class AliasesModule : public ClangTidyModule { +public: + void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + for (const auto &Entry : ClangTidyAliases::activeEntries()) + CheckFactories.registerCheckAlias(Entry.Alias, Entry.Canonical); + } +}; + +} // namespace + +static ClangTidyModuleRegistry::Add<AliasesModule> + X("aliases-module", "Adds check aliases for backward compatibility."); + +} // namespace aliases + +// This anchor is used to force the linker to link in the generated object file +// and thus register the AliasesModule. +volatile int AliasesModuleAnchorSource = 0; // NOLINT(misc-use-internal-linkage) + +} // namespace clang::tidy diff --git a/clang-tools-extra/clang-tidy/aliases/ClangTidyAliases.h b/clang-tools-extra/clang-tidy/aliases/ClangTidyAliases.h new file mode 100644 index 0000000000000..69b9cbabced51 --- /dev/null +++ b/clang-tools-extra/clang-tidy/aliases/ClangTidyAliases.h @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALIASES_CLANGTIDYALIASES_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALIASES_CLANGTIDYALIASES_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" // IWYU pragma: keep +#include "llvm/ADT/StringRef.h" // IWYU pragma: keep + +namespace clang::tidy { + +class ClangTidyAliases { +public: + /// Alias never expires. + static constexpr int NoExpiration = -1; + + /// An entry in the alias table. + /// ExpirationVersion is the LLVM major release at which the alias expires. + /// Use NoExpiration for permanent aliases. + struct Entry { + StringRef Alias; + StringRef Canonical; + int ExpirationVersion; + }; + + /// Look up the canonical name for an alias. + static StringRef getCanonicalForAlias(StringRef Alias); + + /// Look up aliases for a canonical name (may return multiple). + static const SmallVector<StringRef> & + getAliasesForCanonical(StringRef Canonical); + + /// Get a reference to the full alias table for iteration. + static ArrayRef<Entry> getReference(); + + /// Returns true if the given entry is active (not expired) in the current + /// LLVM version. + static bool isActive(const Entry &E); + + /// Filtered range type for iterating only active (non-expired) entries. + using ActiveRange = llvm::iterator_range< + llvm::filter_iterator<const Entry *, bool (*)(const Entry &)>>; + + /// Get only active (non-expired) alias entries. + static ActiveRange activeEntries(); +}; + +} // namespace clang::tidy + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALIASES_CLANGTIDYALIASES_H diff --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp index ee1ce59d80b0d..3a56f5b1f9393 100644 --- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp @@ -35,11 +35,9 @@ #include "../misc/NewDeleteOverloadsCheck.h" #include "../misc/NonCopyableObjectsCheck.h" #include "../misc/PredictableRandCheck.h" -#include "../misc/StaticAssertCheck.h" #include "../misc/ThrowByValueCatchByReferenceCheck.h" #include "../modernize/AvoidSetjmpLongjmpCheck.h" #include "../modernize/AvoidVariadicFunctionsCheck.h" -#include "../performance/MoveConstructorInitCheck.h" #include "../readability/EnumInitialValueCheck.h" #include "../readability/UppercaseLiteralSuffixCheck.h" @@ -275,8 +273,6 @@ class CERTModule : public ClangTidyModule { CheckFactories.registerCheck<bugprone::SignalHandlerCheck>( "cert-msc54-cpp"); // OOP - CheckFactories.registerCheck<performance::MoveConstructorInitCheck>( - "cert-oop11-cpp"); CheckFactories.registerCheck<bugprone::UnhandledSelfAssignmentCheck>( "cert-oop54-cpp"); CheckFactories.registerCheck<bugprone::RawMemoryCallOnNonTrivialTypeCheck>( @@ -292,7 +288,6 @@ class CERTModule : public ClangTidyModule { CheckFactories.registerCheck<bugprone::SpuriouslyWakeUpFunctionsCheck>( "cert-con36-c"); // DCL - CheckFactories.registerCheck<misc::StaticAssertCheck>("cert-dcl03-c"); CheckFactories.registerCheck<readability::UppercaseLiteralSuffixCheck>( "cert-dcl16-c"); CheckFactories.registerCheck<bugprone::ReservedIdentifierCheck>( diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp index 949a88f0fd50d..85b9fea869757 100644 --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -18,6 +18,7 @@ #include "../ClangTidy.h" #include "../ClangTidyForceLinker.h" // IWYU pragma: keep #include "../GlobList.h" +#include "../aliases/ClangTidyAliases.h" #include "clang/Tooling/CommonOptionsParser.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/CommandLine.h" @@ -237,6 +238,12 @@ line or a specific configuration file. )"), cl::init(false), cl::cat(ClangTidyCategory)); +static cl::opt<bool> NotifyAliases("notify-aliases", desc(R"( +Emit a note for each check alias used in --checks +or NOLINT comments, showing the canonical check name. +)"), + cl::init(false), cl::cat(ClangTidyCategory)); + static cl::opt<std::string> Config("config", desc(R"( Specifies a configuration in YAML/JSON format: -config="{Checks: '*', @@ -658,6 +665,16 @@ int clangTidyMain(int argc, const char **argv) { getCheckNames(EffectiveOptions, AllowEnablingAnalyzerAlphaCheckers, ExperimentalCustomChecks); + if (NotifyAliases) { + const GlobList OriginalFilter( + StringRef(EffectiveOptions.Checks.value_or("")), false); + for (const auto &Entry : ClangTidyAliases::activeEntries()) + if (OriginalFilter.contains(Entry.Alias)) + llvm::errs() << "note: '" << Entry.Alias + << "' is an alias for canonical name '" << Entry.Canonical + << "' [checker-alias]\n"; + } + if (ExplainConfig) { // FIXME: Show other ClangTidyOptions' fields, like ExtraArg. std::vector<ClangTidyOptionsProvider::OptionsSource> RawOptions = @@ -737,6 +754,7 @@ int clangTidyMain(int argc, const char **argv) { ClangTidyContext Context( std::move(OwningOptionsProvider), AllowEnablingAnalyzerAlphaCheckers, EnableModuleHeadersParsing, ExperimentalCustomChecks); + Context.setNotifyAliases(NotifyAliases); std::vector<ClangTidyError> Errors = runClangTidy(Context, OptionsParser->getCompilations(), PathList, BaseFS, FixNotes, EnableCheckProfile, ProfilePrefix, Quiet); diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index b1b786cfc4593..baaa76f664401 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -185,6 +185,13 @@ Improvements to clang-query Improvements to clang-tidy -------------------------- +- Added a check alias framework that allows registering alternative names for + existing checks. Aliases support bidirectional disable semantics (disabling + either the alias or canonical name disables both) and NOLINT suppression + using alias names. Added ``--notify-aliases`` option that emits a note for + each alias used in ``--checks`` or ``NOLINT`` comments, showing the canonical + check name. + - Improved :program:`check_clang_tidy.py` script by adding the `-check-header` argument to simplify testing of header files. This argument automatically manages the creation of temporary header files and ensures that diagnostics diff --git a/clang-tools-extra/docs/clang-tidy/index.rst b/clang-tools-extra/docs/clang-tidy/index.rst index 908dee6c18a7f..0d0ead0aad773 100644 --- a/clang-tools-extra/docs/clang-tidy/index.rst +++ b/clang-tools-extra/docs/clang-tidy/index.rst @@ -190,6 +190,9 @@ An overview of all the command-line options: --explain-config - For each enabled check explains, where it is enabled, i.e. in clang-tidy binary, command line or a specific configuration file. + --notify-aliases - Emit a note for each check alias used in + --checks or NOLINT comments, showing the + canonical check name. --export-fixes=<filename> - YAML file to store suggested fixes in. The stored fixes can be applied to the input source code with clang-apply-replacements. diff --git a/clang-tools-extra/test/clang-tidy/checkers/cert/oop11-cpp.cpp b/clang-tools-extra/test/clang-tidy/checkers/cert/oop11-cpp.cpp index f7f3a64867598..e7a8c5706e7b1 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cert/oop11-cpp.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cert/oop11-cpp.cpp @@ -11,7 +11,7 @@ struct B { struct D { B b; - // CHECK-MESSAGES: :[[@LINE+1]]:14: warning: move constructor initializes class member by calling a copy constructor [cert-oop11-cpp] + // CHECK-MESSAGES: :[[@LINE+1]]:14: warning: move constructor initializes class member by calling a copy constructor [performance-move-constructor-init] D(D &&d) : b(d.b) {} // This should not produce a diagnostic because it is not covered under diff --git a/clang-tools-extra/test/clang-tidy/checkers/check-aliasing.cpp b/clang-tools-extra/test/clang-tidy/checkers/check-aliasing.cpp new file mode 100644 index 0000000000000..e644d35358e73 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/check-aliasing.cpp @@ -0,0 +1,49 @@ +// Test the clang-tidy check alias framework using permanent cert aliases. +// +// Test: enabling alias enables canonical check. +// RUN: clang-tidy --list-checks --checks='-*,cert-oop11-cpp' 2>&1 | FileCheck -check-prefix=ENABLE %s +// +// Test: disabling canonical disables alias. +// RUN: clang-tidy --list-checks --allow-no-checks --checks='-*,cert-oop11-cpp,-performance-move-constructor-init' 2>&1 | FileCheck -check-prefix=DISABLE %s +// +// Test: disabling alias disables canonical. +// RUN: clang-tidy --list-checks --allow-no-checks --checks='-*,performance-move-constructor-init,-cert-oop11-cpp' 2>&1 | FileCheck -check-prefix=DISABLE-REVERSE %s +// +// Test: --notify-aliases emits note for alias in --checks. +// RUN: clang-tidy %s --notify-aliases --checks='-*,cert-dcl03-c' -- -std=c++11 2>&1 | FileCheck -check-prefix=NOTIFY %s +// +// Test: NOLINT with alias name suppresses canonical diagnostic. +// RUN: clang-tidy %s --checks='-*,cert-oop11-cpp' -- -std=c++11 2>&1 | FileCheck -check-prefix=NOLINT-ALIAS %s +// +// Test: --warnings-as-errors with alias name promotes canonical diagnostic. +// RUN: not clang-tidy %s --checks='-*,performance-move-constructor-init' --warnings-as-errors='cert-oop11-cpp' -- -std=c++11 2>&1 | FileCheck -check-prefix=WARN-AS-ERR %s + +// ENABLE-DAG: cert-oop11-cpp +// ENABLE-DAG: performance-move-constructor-init + +// DISABLE-NOT: cert-oop11-cpp +// DISABLE-NOT: performance-move-constructor-init + +// DISABLE-REVERSE-NOT: cert-oop11-cpp +// DISABLE-REVERSE-NOT: performance-move-constructor-init + +// NOTIFY: note: 'cert-dcl03-c' is an alias for canonical name 'misc-static-assert' [checker-alias] + +struct B { + B(B &&) noexcept = default; + B(const B &) = default; + B &operator=(const B &) = default; + ~B() {} +}; + +struct D { + B b; + D(D &&d) : b(d.b) {} // NOLINT(cert-oop11-cpp) + // NOLINT-ALIAS: Suppressed 1 warnings (1 NOLINT) +}; + +struct E { + B b; + // WARN-AS-ERR: :[[@LINE+1]]:{{.*}} error: {{.*}} [performance-move-constructor-init,-warnings-as-errors] + E(E &&e) : b(e.b) {} +}; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
