[clang-tools-extra] [clang-tidy] Add checker alias framework and restore hicpp aliases (PR #197927)
via cfe-commits
cfe-commits at lists.llvm.org
Thu May 21 08:21:52 PDT 2026
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 <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 | 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) {}
+};
More information about the cfe-commits
mailing list