[clang-tools-extra] [clang-tidy] Add checker alias framework and restore hicpp aliases (PR #197927)
via cfe-commits
cfe-commits at lists.llvm.org
Fri May 15 07:03:10 PDT 2026
https://github.com/earnol updated https://github.com/llvm/llvm-project/pull/197927
>From f9d012b64a31a82a87af7c46f60ad8aff99ff3fb Mon Sep 17 00:00:00 2001
From: Vladislav Aranov <vladislav.aranov at ericsson.com>
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..9fff71bdefab7 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
@@ -19,6 +19,8 @@
#include "ClangTidyOptions.h"
#include "GlobList.h"
#include "NoLintDirectiveHandler.h"
+#include "aliases/ClangTidyAliases.h"
+#include "clang/Basic/LLVM.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/Attr.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();
+};
More information about the cfe-commits
mailing list