[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
Mon Mar 2 06:21:56 PST 2026
https://github.com/NyuB updated https://github.com/llvm/llvm-project/pull/181502
>From 92fc943c93a0aacfe610bde0c464351b179e761a 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 | 46 +++++++++++++---
.../readability/IdentifierNamingCheck.h | 23 +++++---
clang-tools-extra/docs/ReleaseNotes.rst | 5 ++
.../checks/readability/identifier-naming.rst | 54 +++++++++++++++++++
.../readability/identifier-naming-trim.cpp | 33 ++++++++++++
5 files changed, 146 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 83fc3727cb5c8..f425148aac40a 100644
--- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp
@@ -291,8 +291,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(
@@ -859,6 +861,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(
@@ -1084,11 +1088,34 @@ bool IdentifierNamingCheck::isParamInMainLikeFunction(
return Matcher.match(FDecl->getName());
}
+static void trimPrefixesAndSuffixes(
+ StringRef &Mid,
+ ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
+ bool TrimPrefixes, bool TrimSuffixes) {
+ bool LoopWhileToRemove = true;
+ while (LoopWhileToRemove) {
+ LoopWhileToRemove = false;
+ for (unsigned I = 0; I < SK_Count; ++I) {
+ if (const std::optional<IdentifierNamingCheck::NamingStyle> &OtherStyle =
+ NamingStyles[I]) {
+ while (TrimPrefixes && !OtherStyle->Prefix.empty() &&
+ Mid.consume_front(OtherStyle->Prefix))
+ LoopWhileToRemove = true;
+
+ while (TrimSuffixes && !OtherStyle->Suffix.empty() &&
+ Mid.consume_back(OtherStyle->Suffix))
+ LoopWhileToRemove = 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(
@@ -1108,6 +1135,7 @@ std::string IdentifierNamingCheck::fixupWithStyle(
}
}
StringRef Mid = StringRef(Fixed).trim("_");
+ trimPrefixesAndSuffixes(Mid, NamingStyles, TrimPrefixes, TrimSuffixes);
if (Mid.empty())
Mid = "_";
@@ -1337,7 +1365,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;
@@ -1359,7 +1388,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);
@@ -1390,7 +1420,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>
@@ -1408,7 +1439,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 b016556cc2ab8..aa746228b62c7 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 9aef286eedc30..dd27526c1ee88 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -280,6 +280,11 @@ Changes in existing checks
now uses separate note diagnostics for each uninitialized enumerator, making
it easier to see which specific enumerators need explicit initialization.
+- Improved :doc:`readability-identifier-naming
+ <clang-tidy/checks/readability/identifier-naming>` check by adding the
+ `TrimPrefixes` and `TrimSuffixes` options, to trim all known other prefixes
+ and suffixes when fixing an identifier
+
- Improved :doc:`readability-non-const-parameter
<clang-tidy/checks/readability/non-const-parameter>` check by avoiding false
positives on parameters used in dependent expressions (e.g. inside generic
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..1f88f446a9453
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-trim.cpp
@@ -0,0 +1,33 @@
+// RUN: %check_clang_tidy -std=c++20 %s readability-identifier-naming %t -- \
+// RUN: -config='{CheckOptions: { \
+// RUN: readability-identifier-naming.MemberPrefix: 'm_', \
+// RUN: readability-identifier-naming.MemberSuffix: '_', \
+// RUN: readability-identifier-naming.ParameterPrefix: 'p_', \
+// 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
+
+// clang-format off
+
+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) {}
More information about the cfe-commits
mailing list