https://github.com/earnol updated https://github.com/llvm/llvm-project/pull/197927
>From e5fdab236950aeb4d6593896ac7f9c0193759949 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 | 100 ++++++++++++++- .../clang-tidy/ClangTidyDiagnosticConsumer.h | 6 + .../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 | 118 +++++++++++++++++ .../clang-tidy/aliases/ClangTidyAliases.h | 31 +++++ .../clang-tidy/cert/CERTTidyModule.cpp | 5 - .../clang-tidy/tool/ClangTidyMain.cpp | 18 +++ clang-tools-extra/docs/ReleaseNotes.rst | 10 ++ clang-tools-extra/docs/clang-tidy/index.rst | 3 + .../clang-tidy/checkers/cert/oop11-cpp.cpp | 2 +- .../clang-tidy/checkers/check-aliasing.cpp | 41 ++++++ .../clang-tidy/checkers/hicpp-aliases.cpp | 120 ++++++++++++++++++ 17 files changed, 513 insertions(+), 13 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 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/hicpp-aliases.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..7db5265600bd2 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,20 @@ 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 the alias name for this check is suppressed. + const StringRef Alias = ClangTidyAliases::getAliasForCanonical(CheckName); + if (!Alias.empty() && + 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,11 +253,86 @@ 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(Checks); + + 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 &[Alias, Canonical] : ClangTidyAliases::getReference()) { + if (Re.match(Alias)) { + // Alias enabled or disabled: also enable/disable the canonical. + OS << ','; + if (IsNegative) + OS << '-'; + OS << Canonical; + } else if (IsNegative && Re.match(Canonical)) { + // Canonical disabled: also disable the alias. + OS << ",-"; + OS << 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(""))); + ExpandedChecks = expandCheckAliases(getOptions().Checks.value_or("")); + CheckFilter = std::make_unique<CachedGlobList>(StringRef(ExpandedChecks)); WarningAsErrorFilter = std::make_unique<CachedGlobList>( StringRef(getOptions().WarningsAsErrors.value_or(""))); static const std::vector<std::string> EmptyFileExtensions; diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h index c76f58bc4cc86..6fb5120db7cca 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,12 @@ class ClangTidyContext { std::string CurrentFile; ClangTidyOptions CurrentOptions; + std::string ExpandedChecks; 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..7425e4c1e4c8d --- /dev/null +++ b/clang-tools-extra/clang-tidy/aliases/ClangTidyAliases.cpp @@ -0,0 +1,118 @@ +//===----------------------------------------------------------------------===// +// +// 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/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include <cassert> +#include <utility> + +namespace clang::tidy { + +/// Alias table. Kept sorted by alias name for readability. +static constexpr std::pair<StringRef, StringRef> AliasTable[] = { + // Permanent aliases. + {"cert-dcl03-c", "misc-static-assert"}, + {"cert-oop11-cpp", "performance-move-constructor-init"}, + // Deprecated aliases: keeping for backward compatibility. + {"hicpp-avoid-c-arrays", "modernize-avoid-c-arrays"}, + {"hicpp-avoid-goto", "cppcoreguidelines-avoid-goto"}, + {"hicpp-braces-around-statements", "readability-braces-around-statements"}, + {"hicpp-deprecated-headers", "modernize-deprecated-headers"}, + {"hicpp-exception-baseclass", "bugprone-std-exception-baseclass"}, + {"hicpp-explicit-conversions", "misc-explicit-constructor"}, + {"hicpp-function-size", "readability-function-size"}, + {"hicpp-ignored-remove-result", "bugprone-unused-return-value"}, + {"hicpp-invalid-access-moved", "bugprone-use-after-move"}, + {"hicpp-member-init", "cppcoreguidelines-pro-type-member-init"}, + {"hicpp-move-const-arg", "performance-move-const-arg"}, + {"hicpp-multiway-paths-covered", "bugprone-unhandled-code-paths"}, + {"hicpp-named-parameter", "readability-named-parameter"}, + {"hicpp-new-delete-operators", "misc-new-delete-overloads"}, + {"hicpp-no-array-decay", + "cppcoreguidelines-pro-bounds-array-to-pointer-decay"}, + {"hicpp-no-assembler", "portability-no-assembler"}, + {"hicpp-no-malloc", "cppcoreguidelines-no-malloc"}, + {"hicpp-noexcept-move", "performance-noexcept-move-constructor"}, + {"hicpp-signed-bitwise", "bugprone-signed-bitwise"}, + {"hicpp-special-member-functions", + "cppcoreguidelines-special-member-functions"}, + {"hicpp-static-assert", "misc-static-assert"}, + {"hicpp-undelegated-constructor", "bugprone-undelegated-constructor"}, + {"hicpp-uppercase-literal-suffix", "readability-uppercase-literal-suffix"}, + {"hicpp-use-auto", "modernize-use-auto"}, + {"hicpp-use-emplace", "modernize-use-emplace"}, + {"hicpp-use-equals-default", "modernize-use-equals-default"}, + {"hicpp-use-equals-delete", "modernize-use-equals-delete"}, + {"hicpp-use-noexcept", "modernize-use-noexcept"}, + {"hicpp-use-nullptr", "modernize-use-nullptr"}, + {"hicpp-use-override", "modernize-use-override"}, + {"hicpp-vararg", "cppcoreguidelines-pro-type-vararg"}, +}; + +ArrayRef<std::pair<StringRef, StringRef>> ClangTidyAliases::getReference() { + return AliasTable; +} + +StringRef ClangTidyAliases::getCanonicalForAlias(StringRef Alias) { + static const auto *Map = [] { + auto *M = new llvm::StringMap<StringRef>(); + for (const auto &[A, Canonical] : AliasTable) { + [[maybe_unused]] auto Result = M->try_emplace(A, Canonical); + assert(Result.second && "Duplicate alias in ClangTidyAliases table"); + } + return M; + }(); + auto It = Map->find(Alias); + if (It != Map->end()) + return It->second; + return {}; +} + +StringRef ClangTidyAliases::getAliasForCanonical(StringRef Canonical) { + static const auto *ReverseMap = [] { + auto *M = new llvm::StringMap<StringRef>(); + for (const auto &[Alias, Canon] : AliasTable) { + [[maybe_unused]] auto Result = M->try_emplace(Canon, Alias); + assert(Result.second && + "Duplicate canonical name in ClangTidyAliases table"); + } + return M; + }(); + auto It = ReverseMap->find(Canonical); + if (It != ReverseMap->end()) + return It->second; + return {}; +} + +namespace aliases { +namespace { + +class AliasesModule : public ClangTidyModule { +public: + void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + for (const auto &[Alias, Canonical] : AliasTable) + CheckFactories.registerCheckAlias(Alias, 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..66b561541e444 --- /dev/null +++ b/clang-tools-extra/clang-tidy/aliases/ClangTidyAliases.h @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// 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 <utility> + +namespace clang::tidy { + +class ClangTidyAliases { +public: + /// Look up the canonical name for an alias. + static StringRef getCanonicalForAlias(StringRef Alias); + + /// Look up the alias for a canonical name. + static StringRef getAliasForCanonical(StringRef Canonical); + + /// Get a reference to the full alias table for iteration. + static ArrayRef<std::pair<StringRef, StringRef>> getReference(); +}; + +} // 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..0c2771385f5ba 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 &[Alias, Canonical] : ClangTidyAliases::getReference()) + if (OriginalFilter.contains(Alias)) + llvm::errs() << "note: '" << Alias + << "' is an alias for canonical name '" << 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..41ae4d2a78638 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -185,6 +185,16 @@ 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. + +- Preserved all 31 ``hicpp-*`` check aliases that were removed for backward + compatibility. + - 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..589708f838d1d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/check-aliasing.cpp @@ -0,0 +1,41 @@ +// 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 --allow-empty -check-prefix=NOLINT-ALIAS %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] + +// NOLINT-ALIAS-NOT: warning: + +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) +}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/hicpp-aliases.cpp b/clang-tools-extra/test/clang-tidy/checkers/hicpp-aliases.cpp new file mode 100644 index 0000000000000..fe556bfa8a5cd --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/hicpp-aliases.cpp @@ -0,0 +1,120 @@ +// Test that all 31 hicpp aliases are properly registered and that +// enable/disable semantics work correctly with bidirectional alias expansion. +// +// Test enable: hicpp-use-override triggers the check. +// RUN: clang-tidy %s --checks='-*,hicpp-use-override' -- -std=c++11 2>&1 | FileCheck -check-prefix=ENABLE %s +// +// Test disable: enabling alias then disabling canonical disables both. +// RUN: clang-tidy %s --allow-no-checks --checks='-*,hicpp-use-override,-modernize-use-override' -- -std=c++11 2>&1 | FileCheck --allow-empty -check-prefix=DISABLE %s +// +// Verify all 31 aliases are listed. +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*' 2>&1 | FileCheck -check-prefix=LIST %s +// +// Verify disable-by-canonical works for each alias (disabling canonical removes alias from list). +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-modernize-avoid-c-arrays' 2>&1 | FileCheck -check-prefix=NO-AVOID-C-ARRAYS %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-cppcoreguidelines-avoid-goto' 2>&1 | FileCheck -check-prefix=NO-AVOID-GOTO %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-readability-braces-around-statements' 2>&1 | FileCheck -check-prefix=NO-BRACES %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-modernize-deprecated-headers' 2>&1 | FileCheck -check-prefix=NO-DEPRECATED-HEADERS %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-bugprone-std-exception-baseclass' 2>&1 | FileCheck -check-prefix=NO-EXCEPTION %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-misc-explicit-constructor' 2>&1 | FileCheck -check-prefix=NO-EXPLICIT %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-readability-function-size' 2>&1 | FileCheck -check-prefix=NO-FUNCTION-SIZE %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-bugprone-unused-return-value' 2>&1 | FileCheck -check-prefix=NO-IGNORED-REMOVE %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-bugprone-signed-bitwise' 2>&1 | FileCheck -check-prefix=NO-SIGNED-BITWISE %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-readability-named-parameter' 2>&1 | FileCheck -check-prefix=NO-NAMED-PARAM %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-bugprone-use-after-move' 2>&1 | FileCheck -check-prefix=NO-INVALID-ACCESS %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-cppcoreguidelines-pro-type-member-init' 2>&1 | FileCheck -check-prefix=NO-MEMBER-INIT %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-performance-move-const-arg' 2>&1 | FileCheck -check-prefix=NO-MOVE-CONST %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-misc-new-delete-overloads' 2>&1 | FileCheck -check-prefix=NO-NEW-DELETE %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-performance-noexcept-move-constructor' 2>&1 | FileCheck -check-prefix=NO-NOEXCEPT-MOVE %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-cppcoreguidelines-pro-bounds-array-to-pointer-decay' 2>&1 | FileCheck -check-prefix=NO-ARRAY-DECAY %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-bugprone-unhandled-code-paths' 2>&1 | FileCheck -check-prefix=NO-MULTIWAY %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-portability-no-assembler' 2>&1 | FileCheck -check-prefix=NO-ASSEMBLER %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-cppcoreguidelines-no-malloc' 2>&1 | FileCheck -check-prefix=NO-MALLOC %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-cppcoreguidelines-special-member-functions' 2>&1 | FileCheck -check-prefix=NO-SPECIAL-MEMBER %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-misc-static-assert' 2>&1 | FileCheck -check-prefix=NO-STATIC-ASSERT %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-modernize-use-auto' 2>&1 | FileCheck -check-prefix=NO-USE-AUTO %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-bugprone-undelegated-constructor' 2>&1 | FileCheck -check-prefix=NO-UNDELEGATED %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-modernize-use-emplace' 2>&1 | FileCheck -check-prefix=NO-USE-EMPLACE %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-modernize-use-equals-default' 2>&1 | FileCheck -check-prefix=NO-USE-EQUALS-DEFAULT %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-modernize-use-equals-delete' 2>&1 | FileCheck -check-prefix=NO-USE-EQUALS-DELETE %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-modernize-use-noexcept' 2>&1 | FileCheck -check-prefix=NO-USE-NOEXCEPT %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-modernize-use-nullptr' 2>&1 | FileCheck -check-prefix=NO-USE-NULLPTR %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-modernize-use-override' 2>&1 | FileCheck -check-prefix=NO-USE-OVERRIDE %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-readability-uppercase-literal-suffix' 2>&1 | FileCheck -check-prefix=NO-UPPERCASE %s +// RUN: clang-tidy --list-checks --checks='-*,hicpp-*,-cppcoreguidelines-pro-type-vararg' 2>&1 | FileCheck -check-prefix=NO-VARARG %s + +// ENABLE: warning: annotate this function with 'override' +// DISABLE-NOT: warning: + +// LIST-DAG: hicpp-avoid-c-arrays +// LIST-DAG: hicpp-avoid-goto +// LIST-DAG: hicpp-braces-around-statements +// LIST-DAG: hicpp-deprecated-headers +// LIST-DAG: hicpp-exception-baseclass +// LIST-DAG: hicpp-explicit-conversions +// LIST-DAG: hicpp-function-size +// LIST-DAG: hicpp-ignored-remove-result +// LIST-DAG: hicpp-invalid-access-moved +// LIST-DAG: hicpp-member-init +// LIST-DAG: hicpp-move-const-arg +// LIST-DAG: hicpp-multiway-paths-covered +// LIST-DAG: hicpp-named-parameter +// LIST-DAG: hicpp-new-delete-operators +// LIST-DAG: hicpp-no-array-decay +// LIST-DAG: hicpp-no-assembler +// LIST-DAG: hicpp-no-malloc +// LIST-DAG: hicpp-noexcept-move +// LIST-DAG: hicpp-signed-bitwise +// LIST-DAG: hicpp-special-member-functions +// LIST-DAG: hicpp-static-assert +// LIST-DAG: hicpp-undelegated-constructor +// LIST-DAG: hicpp-uppercase-literal-suffix +// LIST-DAG: hicpp-use-auto +// LIST-DAG: hicpp-use-emplace +// LIST-DAG: hicpp-use-equals-default +// LIST-DAG: hicpp-use-equals-delete +// LIST-DAG: hicpp-use-noexcept +// LIST-DAG: hicpp-use-nullptr +// LIST-DAG: hicpp-use-override +// LIST-DAG: hicpp-vararg + +// NO-AVOID-C-ARRAYS-NOT: hicpp-avoid-c-arrays +// NO-AVOID-GOTO-NOT: hicpp-avoid-goto +// NO-BRACES-NOT: hicpp-braces-around-statements +// NO-DEPRECATED-HEADERS-NOT: hicpp-deprecated-headers +// NO-EXCEPTION-NOT: hicpp-exception-baseclass +// NO-EXPLICIT-NOT: hicpp-explicit-conversions +// NO-FUNCTION-SIZE-NOT: hicpp-function-size +// NO-IGNORED-REMOVE-NOT: hicpp-ignored-remove-result +// NO-SIGNED-BITWISE-NOT: hicpp-signed-bitwise +// NO-NAMED-PARAM-NOT: hicpp-named-parameter +// NO-INVALID-ACCESS-NOT: hicpp-invalid-access-moved +// NO-MEMBER-INIT-NOT: hicpp-member-init +// NO-MOVE-CONST-NOT: hicpp-move-const-arg +// NO-NEW-DELETE-NOT: hicpp-new-delete-operators +// NO-NOEXCEPT-MOVE-NOT: hicpp-noexcept-move +// NO-ARRAY-DECAY-NOT: hicpp-no-array-decay +// NO-MULTIWAY-NOT: hicpp-multiway-paths-covered +// NO-ASSEMBLER-NOT: hicpp-no-assembler +// NO-MALLOC-NOT: hicpp-no-malloc +// NO-SPECIAL-MEMBER-NOT: hicpp-special-member-functions +// NO-STATIC-ASSERT-NOT: hicpp-static-assert +// NO-USE-AUTO-NOT: hicpp-use-auto +// NO-UNDELEGATED-NOT: hicpp-undelegated-constructor +// NO-USE-EMPLACE-NOT: hicpp-use-emplace +// NO-USE-EQUALS-DEFAULT-NOT: hicpp-use-equals-default +// NO-USE-EQUALS-DELETE-NOT: hicpp-use-equals-delete +// NO-USE-NOEXCEPT-NOT: hicpp-use-noexcept +// NO-USE-NULLPTR-NOT: hicpp-use-nullptr +// NO-USE-OVERRIDE-NOT: hicpp-use-override +// NO-UPPERCASE-NOT: hicpp-uppercase-literal-suffix +// NO-VARARG-NOT: hicpp-vararg + +struct Base { + virtual void foo(); + virtual ~Base(); +}; + +struct Derived : Base { + void foo(); +}; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
