[clang-tools-extra] [clang-tidy] identifier-naming: TrimPrefixSuffix option to avoid concatenating multiple pre(suf)fixes (PR #181502)
Brice Decaestecker via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 10 21:57:54 PDT 2026
https://github.com/NyuB updated https://github.com/llvm/llvm-project/pull/181502
>From dcd95f3fee630010e2ac94b795e63bffaaf814e1 Mon Sep 17 00:00:00 2001
From: Brice Decaestecker <brice.decaestecker at gmx.fr>
Date: Sat, 21 Feb 2026 14:29:27 +0100
Subject: [PATCH] Avoid identifier-naming concatenating multiple
(pre/su)ffixes, introducing options to strip out any other known (pre/su)ffix
before renaming
---
.../readability/IdentifierNamingCheck.cpp | 45 +++++++++++++---
.../readability/IdentifierNamingCheck.h | 23 +++++---
clang-tools-extra/docs/ReleaseNotes.rst | 3 ++
.../checks/readability/identifier-naming.rst | 54 +++++++++++++++++++
.../readability/identifier-naming-trim.cpp | 54 +++++++++++++++++++
5 files changed, 164 insertions(+), 15 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-trim.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp
index a356e1390b62d..2a5ce332c64ae 100644
--- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp
@@ -290,8 +290,10 @@ IdentifierNamingCheck::FileStyle IdentifierNamingCheck::getFileStyleFromOptions(
const bool IgnoreMainLike = Options.get("IgnoreMainLikeFunctions", false);
const bool CheckAnonFieldInParent =
Options.get("CheckAnonFieldInParent", false);
- return {std::move(Styles), std::move(HNOption), IgnoreMainLike,
- CheckAnonFieldInParent};
+ const bool TrimPrefixes = Options.get("TrimPrefixes", false);
+ const bool TrimSuffixes = Options.get("TrimSuffixes", false);
+ return {std::move(Styles), std::move(HNOption), IgnoreMainLike,
+ CheckAnonFieldInParent, TrimPrefixes, TrimSuffixes};
}
std::string IdentifierNamingCheck::HungarianNotation::getDeclTypeName(
@@ -858,6 +860,8 @@ void IdentifierNamingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
MainFileStyle->isIgnoringMainLikeFunction());
Options.store(Opts, "CheckAnonFieldInParent",
MainFileStyle->isCheckingAnonFieldInParentScope());
+ Options.store(Opts, "TrimPrefixes", MainFileStyle->isTrimmingPrefixes());
+ Options.store(Opts, "TrimSuffixes", MainFileStyle->isTrimmingSuffixes());
}
bool IdentifierNamingCheck::matchesStyle(
@@ -1083,11 +1087,33 @@ bool IdentifierNamingCheck::isParamInMainLikeFunction(
return Matcher.match(FDecl->getName());
}
+static void trimPrefixesAndSuffixes(
+ StringRef &Mid,
+ ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
+ bool TrimPrefixes, bool TrimSuffixes) {
+ bool Changed = true;
+ while (Changed) {
+ Changed = false;
+ for (const auto &OtherStyle : NamingStyles) {
+ if (OtherStyle) {
+ while (TrimPrefixes && !OtherStyle->Prefix.empty() &&
+ Mid.consume_front(OtherStyle->Prefix))
+ Changed = true;
+
+ while (TrimSuffixes && !OtherStyle->Suffix.empty() &&
+ Mid.consume_back(OtherStyle->Suffix))
+ Changed = true;
+ }
+ }
+ }
+}
+
std::string IdentifierNamingCheck::fixupWithStyle(
StringRef Type, StringRef Name,
const IdentifierNamingCheck::NamingStyle &Style,
+ ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
const IdentifierNamingCheck::HungarianNotationOption &HNOption,
- const Decl *D) const {
+ bool TrimPrefixes, bool TrimSuffixes, const Decl *D) const {
Name.consume_front(Style.Prefix);
Name.consume_back(Style.Suffix);
std::string Fixed = fixupWithCase(
@@ -1107,6 +1133,7 @@ std::string IdentifierNamingCheck::fixupWithStyle(
}
}
StringRef Mid = StringRef(Fixed).trim("_");
+ trimPrefixesAndSuffixes(Mid, NamingStyles, TrimPrefixes, TrimSuffixes);
if (Mid.empty())
Mid = "_";
@@ -1352,7 +1379,8 @@ IdentifierNamingCheck::getFailureInfo(
SourceLocation Location,
ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
const IdentifierNamingCheck::HungarianNotationOption &HNOption,
- StyleKind SK, const SourceManager &SM, bool IgnoreFailedSplit) const {
+ StyleKind SK, const SourceManager &SM, bool IgnoreFailedSplit,
+ bool TrimPrefixes, bool TrimSuffixes) const {
if (SK == SK_Invalid)
return std::nullopt;
@@ -1374,7 +1402,8 @@ IdentifierNamingCheck::getFailureInfo(
IdentifierNamingCheck::CT_LowerCase);
llvm::replace(KindName, '_', ' ');
- std::string Fixup = fixupWithStyle(Type, Name, Style, HNOption, ND);
+ std::string Fixup = fixupWithStyle(Type, Name, Style, NamingStyles, HNOption,
+ TrimPrefixes, TrimSuffixes, ND);
if (StringRef(Fixup) == Name) {
if (!IgnoreFailedSplit) {
LLVM_DEBUG(Location.print(llvm::dbgs(), SM);
@@ -1405,7 +1434,8 @@ IdentifierNamingCheck::getDeclFailureInfo(const NamedDecl *Decl,
findStyleKind(Decl, FileStyle.getStyles(),
FileStyle.isIgnoringMainLikeFunction(),
FileStyle.isCheckingAnonFieldInParentScope()),
- SM, IgnoreFailedSplit);
+ SM, IgnoreFailedSplit, FileStyle.isTrimmingPrefixes(),
+ FileStyle.isTrimmingSuffixes());
}
std::optional<RenamerClangTidyCheck::FailureInfo>
@@ -1423,7 +1453,8 @@ IdentifierNamingCheck::getMacroFailureInfo(const Token &MacroNameTok,
return getFailureInfo("", MacroNameTok.getIdentifierInfo()->getName(),
nullptr, Loc, Style.getStyles(), Style.getHNOption(),
- UsedKind, SM, IgnoreFailedSplit);
+ UsedKind, SM, IgnoreFailedSplit,
+ Style.isTrimmingPrefixes(), Style.isTrimmingSuffixes());
}
RenamerClangTidyCheck::DiagInfo
diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h
index 0afc7e2246816..ba1ccd6be99e3 100644
--- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h
@@ -128,10 +128,11 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
FileStyle() : IsActive(false), IgnoreMainLikeFunctions(false) {}
FileStyle(SmallVectorImpl<std::optional<NamingStyle>> &&Styles,
HungarianNotationOption HNOption, bool IgnoreMainLike,
- bool CheckAnonFieldInParent)
+ bool CheckAnonFieldInParent, bool TrimPrefixes, bool TrimSuffixes)
: Styles(std::move(Styles)), HNOption(std::move(HNOption)),
IsActive(true), IgnoreMainLikeFunctions(IgnoreMainLike),
- CheckAnonFieldInParentScope(CheckAnonFieldInParent) {}
+ CheckAnonFieldInParentScope(CheckAnonFieldInParent),
+ TrimPrefixes(TrimPrefixes), TrimSuffixes(TrimSuffixes) {}
ArrayRef<std::optional<NamingStyle>> getStyles() const {
assert(IsActive);
@@ -149,6 +150,8 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
bool isCheckingAnonFieldInParentScope() const {
return CheckAnonFieldInParentScope;
}
+ bool isTrimmingPrefixes() const { return TrimPrefixes; }
+ bool isTrimmingSuffixes() const { return TrimSuffixes; }
private:
SmallVector<std::optional<NamingStyle>, 0> Styles;
@@ -156,6 +159,8 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
bool IsActive;
bool IgnoreMainLikeFunctions;
bool CheckAnonFieldInParentScope;
+ bool TrimPrefixes;
+ bool TrimSuffixes;
};
IdentifierNamingCheck::FileStyle
@@ -173,11 +178,12 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
const IdentifierNamingCheck::HungarianNotationOption &HNOption,
IdentifierNamingCheck::CaseType Case) const;
- std::string
- fixupWithStyle(StringRef Type, StringRef Name,
- const IdentifierNamingCheck::NamingStyle &Style,
- const IdentifierNamingCheck::HungarianNotationOption &HNOption,
- const Decl *D) const;
+ std::string fixupWithStyle(
+ StringRef Type, StringRef Name,
+ const IdentifierNamingCheck::NamingStyle &Style,
+ ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
+ const IdentifierNamingCheck::HungarianNotationOption &HNOption,
+ bool TrimPrefixes, bool TrimSuffixes, const Decl *D) const;
StyleKind findStyleKind(
const NamedDecl *D,
@@ -189,7 +195,8 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
SourceLocation Location,
ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
const IdentifierNamingCheck::HungarianNotationOption &HNOption,
- StyleKind SK, const SourceManager &SM, bool IgnoreFailedSplit) const;
+ StyleKind SK, const SourceManager &SM, bool IgnoreFailedSplit,
+ bool TrimPrefixes, bool TrimSuffixes) const;
bool isParamInMainLikeFunction(const ParmVarDecl &ParmDecl,
bool IncludeMainLike) const;
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 17a5b006d4b8f..3663a1a17555d 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -480,6 +480,9 @@ Changes in existing checks
- Fixed a false positive where function templates could be diagnosed as generic
identifiers when `DefaultCase` was enabled.
+ - Added the `TrimPrefixes` and `TrimSuffixes` options to trim all known other
+ prefixes and suffixes when fixing an identifier.
+
- Improved :doc:`readability-implicit-bool-conversion
<clang-tidy/checks/readability/implicit-bool-conversion>` check:
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-naming.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-naming.rst
index c8f87dcba8c0a..fa6df081e7613 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-naming.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/identifier-naming.rst
@@ -85,6 +85,8 @@ The available options are summarized below:
- :option:`CheckAnonFieldInParent`
- :option:`GetConfigPerFile`
- :option:`IgnoreMainLikeFunctions`
+ - :option:`TrimPrefixes`
+ - :option:`TrimSuffixes`
**Specific options**
@@ -2652,6 +2654,58 @@ After:
template <template <typename> class pre_tpl_parameter_post, int COUNT_params,
typename... TYPE_parameters>
+.. option:: TrimPrefixes
+
+ When set to `true`, the suggested fix for identifiers that have the wrong
+ prefix will be trimmed of all known prefixes from other options. Default
+ is `false`.
+
+For example using values of:
+
+ - ParameterPrefix of ``p_``
+ - MemberPrefix of ``m_``
+ - TrimPrefixes of ``true``
+
+Identifies and/or transforms function parameter names as follows:
+
+Before:
+
+.. code-block:: c++
+
+ void f(int m_n) {}
+
+After:
+
+.. code-block:: c++
+
+ void f(int p_n) {}
+
+.. option:: TrimSuffixes
+
+ When set to `true`, the suggested fix for identifiers that have the wrong
+ suffix will be trimmed of all known suffixes from other options. Default
+ is `false`.
+
+For example using values of:
+
+ - ParameterSuffix of ``_p``
+ - MemberSuffix of ``_m``
+ - TrimSuffixes of ``true``
+
+Identifies and/or transforms function parameter names as follows:
+
+Before:
+
+.. code-block:: c++
+
+ void f(int n_m) {}
+
+After:
+
+.. code-block:: c++
+
+ void f(int n_p) {}
+
.. option:: TypeAliasCase
When defined, the check will ensure type alias names conform to the
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-trim.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-trim.cpp
new file mode 100644
index 0000000000000..7f0e5656653ee
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-trim.cpp
@@ -0,0 +1,54 @@
+// RUN: %check_clang_tidy %s readability-identifier-naming %t -- \
+// RUN: -config='{CheckOptions: { \
+// RUN: readability-identifier-naming.GlobalVariablePrefix: 'g_', \
+// RUN: readability-identifier-naming.MacroDefinitionPrefix: 'M_', \
+// RUN: readability-identifier-naming.MemberPrefix: 'm_', \
+// RUN: readability-identifier-naming.MemberSuffix: '_', \
+// RUN: readability-identifier-naming.ParameterPrefix: 'p_', \
+// RUN: readability-identifier-naming.TemplateParameterPrefix: t_, \
+// RUN: readability-identifier-naming.TrimPrefixes: 1, \
+// RUN: readability-identifier-naming.TrimSuffixes: 1, \
+// RUN: }}' \
+// RUN: -header-filter='' \
+// RUN: -- -fno-delayed-template-parsing -Dbad_macro \
+// RUN: -I%S/Inputs/identifier-naming \
+// RUN: -isystem %S/Inputs/identifier-naming/system
+
+#include <system-header.h>
+
+#define m_MACRO(arg) void f(int arg) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for macro definition 'm_MACRO'
+// CHECK-FIXES: #define M_MACRO(arg) void f(int arg) {}
+
+m_MACRO(m_wrong);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for parameter 'm_wrong'
+// CHECK-FIXES: M_MACRO(p_wrong);
+
+SYSTEM_MACRO(m_wrong);
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: invalid case style for global variable 'm_wrong'
+// CHECK-FIXES: SYSTEM_MACRO(g_wrong);
+
+struct Triple {
+ Triple(int m_wrong_, int missing, int p_ok): p_wrong_(m_wrong_), missing(missing), m_ok_(p_ok) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: invalid case style for parameter 'm_wrong_'
+ // CHECK-MESSAGES: :[[@LINE-2]]:30: warning: invalid case style for parameter 'missing'
+ // CHECK-FIXES: Triple(int p_wrong, int p_missing, int p_ok): m_wrong_(p_wrong), m_missing_(p_missing), m_ok_(p_ok) {}
+ int p_wrong_;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for member 'p_wrong_'
+ // CHECK-FIXES: int m_wrong_;
+ int missing;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for member 'missing'
+ // CHECK-FIXES: int m_missing_;
+ int m_ok_;
+};
+
+void multipleWrong(int m_m_wrong1, int wrong2__) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: invalid case style for parameter 'm_m_wrong1'
+// CHECK-MESSAGES: :[[@LINE-2]]:40: warning: invalid case style for parameter 'wrong2__'
+// CHECK-FIXES: void multipleWrong(int p_wrong1, int p_wrong2) {}
+
+template<typename p_t>
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: invalid case style for template parameter 'p_t'
+// CHECK-FIXES: template<typename t_t>
+void f(p_t p_arg) {}
+// CHECK-FIXES: void f(t_t p_arg) {}
More information about the cfe-commits
mailing list