[clang-tools-extra] [clang-tidy] Add `TypedefInheritAnonTagConfig` to `readability-identifier-naming` (PR #213772)
David Meng via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 4 08:50:14 PDT 2026
https://github.com/davidmenggx updated https://github.com/llvm/llvm-project/pull/213772
>From 29bc34f02cb9aff77419b64daf25ab80eb602842 Mon Sep 17 00:00:00 2001
From: David Meng <davidmenggx at gmail.com>
Date: Mon, 3 Aug 2026 14:52:45 -0700
Subject: [PATCH 1/2] [clang-tidy] Add TypedefInheritAnonTagConfig to
readability-identifier-naming
`typedef enum {} MyEnum;` is currently checked against TypedefCase even
though the identifier names the enum itself. With the new option enabled
(default false), such a typedef or type alias is checked against the
style configured for that tag kind instead. If none is configured, the
typedef style still applies.
Closes https://github.com/llvm/llvm-project/issues/213665
---
.../readability/IdentifierNamingCheck.cpp | 87 +++++++---
.../readability/IdentifierNamingCheck.h | 23 ++-
clang-tools-extra/docs/ReleaseNotes.rst | 7 +
.../checks/readability/identifier-naming.rst | 35 ++++
...r-naming-typedef-inherit-anon-tag-config.c | 25 +++
...naming-typedef-inherit-anon-tag-config.cpp | 159 ++++++++++++++++++
6 files changed, 305 insertions(+), 31 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-typedef-inherit-anon-tag-config.c
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-typedef-inherit-anon-tag-config.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.cpp
index 42f3101592758..8efea89b4fc34 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);
+ const bool TypedefInheritAnonTagConfig =
+ Options.get("TypedefInheritAnonTagConfig", false);
return {std::move(Styles), std::move(HNOption), IgnoreMainLike,
- CheckAnonFieldInParent};
+ CheckAnonFieldInParent, TypedefInheritAnonTagConfig};
}
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, "TypedefInheritAnonTagConfig",
+ MainFileStyle->isTypedefInheritingAnonTagConfig());
}
bool IdentifierNamingCheck::matchesStyle(
@@ -1116,13 +1120,27 @@ std::string IdentifierNamingCheck::fixupWithStyle(
StyleKind IdentifierNamingCheck::findStyleKind(
const NamedDecl *D,
ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
- bool IgnoreMainLikeFunctions, bool CheckAnonFieldInParentScope) const {
+ bool IgnoreMainLikeFunctions, bool CheckAnonFieldInParentScope,
+ bool TypedefInheritAnonTagConfig) const {
assert(D && D->getIdentifier() && !D->getName().empty() && !D->isImplicit() &&
"Decl must be an explicit identifier with a name.");
if (isa<ObjCIvarDecl>(D) && NamingStyles[SK_ObjcIvar])
return SK_ObjcIvar;
+ // A typedef that provides the only name of an otherwise unnamed tag, as in
+ // `typedef enum {} E;`, names the tag itself, so it can be checked against
+ // the style configured for that tag kind.
+ if (TypedefInheritAnonTagConfig && isa<TypedefDecl, TypeAliasDecl>(D)) {
+ const TagDecl *Tag =
+ cast<TypedefNameDecl>(D)->getUnderlyingType()->getAsTagDecl();
+ if (Tag && Tag->getTypedefNameForAnonDecl() == D) {
+ const StyleKind SK = findStyleKindForTag(Tag, NamingStyles);
+ if (SK != SK_Invalid)
+ return SK;
+ }
+ }
+
if (isa<TypedefDecl>(D) && NamingStyles[SK_Typedef])
return SK_Typedef;
@@ -1164,30 +1182,9 @@ StyleKind IdentifierNamingCheck::findStyleKind(
if (Decl->isAnonymousStructOrUnion())
return SK_Invalid;
- if (const auto *Definition = Decl->getDefinition()) {
- if (const auto *CxxRecordDecl = dyn_cast<CXXRecordDecl>(Definition)) {
- if (CxxRecordDecl->isAbstract() && NamingStyles[SK_AbstractClass])
- return SK_AbstractClass;
- }
-
- if (Definition->isStruct() && NamingStyles[SK_Struct])
- return SK_Struct;
-
- if (Definition->isStruct() && NamingStyles[SK_Class])
- return SK_Class;
-
- if (Definition->isClass() && NamingStyles[SK_Class])
- return SK_Class;
-
- if (Definition->isClass() && NamingStyles[SK_Struct])
- return SK_Struct;
-
- if (Definition->isUnion() && NamingStyles[SK_Union])
- return SK_Union;
-
- if (Definition->isEnum() && NamingStyles[SK_Enum])
- return SK_Enum;
- }
+ const StyleKind SK = findStyleKindForTag(Decl, NamingStyles);
+ if (SK != SK_Invalid)
+ return SK;
return undefinedStyle(NamingStyles);
}
@@ -1405,7 +1402,8 @@ IdentifierNamingCheck::getDeclFailureInfo(const NamedDecl *Decl,
FileStyle.getStyles(), FileStyle.getHNOption(),
findStyleKind(Decl, FileStyle.getStyles(),
FileStyle.isIgnoringMainLikeFunction(),
- FileStyle.isCheckingAnonFieldInParentScope()),
+ FileStyle.isCheckingAnonFieldInParentScope(),
+ FileStyle.isTypedefInheritingAnonTagConfig()),
SM, IgnoreFailedSplit);
}
@@ -1515,6 +1513,41 @@ StyleKind IdentifierNamingCheck::findStyleKindForField(
return undefinedStyle(NamingStyles);
}
+StyleKind IdentifierNamingCheck::findStyleKindForTag(
+ const TagDecl *Tag,
+ ArrayRef<std::optional<NamingStyle>> NamingStyles) const {
+ if (isa<EnumDecl>(Tag) && NamingStyles[SK_Enum])
+ return SK_Enum;
+
+ const auto *Record = dyn_cast<RecordDecl>(Tag);
+ if (!Record)
+ return SK_Invalid;
+
+ if (const auto *Definition = Record->getDefinition()) {
+ if (const auto *CxxRecordDecl = dyn_cast<CXXRecordDecl>(Definition)) {
+ if (CxxRecordDecl->isAbstract() && NamingStyles[SK_AbstractClass])
+ return SK_AbstractClass;
+ }
+
+ if (Definition->isStruct() && NamingStyles[SK_Struct])
+ return SK_Struct;
+
+ if (Definition->isStruct() && NamingStyles[SK_Class])
+ return SK_Class;
+
+ if (Definition->isClass() && NamingStyles[SK_Class])
+ return SK_Class;
+
+ if (Definition->isClass() && NamingStyles[SK_Struct])
+ return SK_Struct;
+
+ if (Definition->isUnion() && NamingStyles[SK_Union])
+ return SK_Union;
+ }
+
+ return SK_Invalid;
+}
+
StyleKind IdentifierNamingCheck::findStyleKindForVar(
const VarDecl *Var, QualType Type,
ArrayRef<std::optional<NamingStyle>> NamingStyles) const {
diff --git a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h
index 0afc7e2246816..8f20acc6030cb 100644
--- a/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/IdentifierNamingCheck.h
@@ -125,13 +125,16 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
};
struct FileStyle {
- FileStyle() : IsActive(false), IgnoreMainLikeFunctions(false) {}
+ FileStyle()
+ : IsActive(false), IgnoreMainLikeFunctions(false),
+ TypedefInheritAnonTagConfig(false) {}
FileStyle(SmallVectorImpl<std::optional<NamingStyle>> &&Styles,
HungarianNotationOption HNOption, bool IgnoreMainLike,
- bool CheckAnonFieldInParent)
+ bool CheckAnonFieldInParent, bool TypedefInheritAnonTag)
: Styles(std::move(Styles)), HNOption(std::move(HNOption)),
IsActive(true), IgnoreMainLikeFunctions(IgnoreMainLike),
- CheckAnonFieldInParentScope(CheckAnonFieldInParent) {}
+ CheckAnonFieldInParentScope(CheckAnonFieldInParent),
+ TypedefInheritAnonTagConfig(TypedefInheritAnonTag) {}
ArrayRef<std::optional<NamingStyle>> getStyles() const {
assert(IsActive);
@@ -150,12 +153,17 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
return CheckAnonFieldInParentScope;
}
+ bool isTypedefInheritingAnonTagConfig() const {
+ return TypedefInheritAnonTagConfig;
+ }
+
private:
SmallVector<std::optional<NamingStyle>, 0> Styles;
HungarianNotationOption HNOption;
bool IsActive;
bool IgnoreMainLikeFunctions;
bool CheckAnonFieldInParentScope;
+ bool TypedefInheritAnonTagConfig;
};
IdentifierNamingCheck::FileStyle
@@ -182,7 +190,8 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
StyleKind findStyleKind(
const NamedDecl *D,
ArrayRef<std::optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
- bool IgnoreMainLikeFunctions, bool CheckAnonFieldInParentScope) const;
+ bool IgnoreMainLikeFunctions, bool CheckAnonFieldInParentScope,
+ bool TypedefInheritAnonTagConfig) const;
std::optional<RenamerClangTidyCheck::FailureInfo> getFailureInfo(
StringRef Type, StringRef Name, const NamedDecl *ND,
@@ -216,6 +225,12 @@ class IdentifierNamingCheck final : public RenamerClangTidyCheck {
const FieldDecl *Field, QualType Type,
ArrayRef<std::optional<NamingStyle>> NamingStyles) const;
+ /// Find the style kind configured for the kind of \p Tag, or \c SK_Invalid if
+ /// none is configured.
+ StyleKind
+ findStyleKindForTag(const TagDecl *Tag,
+ ArrayRef<std::optional<NamingStyle>> NamingStyles) const;
+
StyleKind
findStyleKindForVar(const VarDecl *Var, QualType Type,
ArrayRef<std::optional<NamingStyle>> NamingStyles) const;
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 05407c3319683..6a9946d1c9313 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -118,6 +118,13 @@ Changes in existing checks
nested expressions involving different macros or a mix of macro and
non-macro operands.
+- Improved :doc:`readability-identifier-naming
+ <clang-tidy/checks/readability/identifier-naming>` check by adding the
+ :option:`TypedefInheritAnonTagConfig` option, which checks a typedef or type
+ alias that provides the only name of an otherwise unnamed tag, such as
+ ``typedef enum {} MyEnum;``, against the style configured for that tag kind
+ instead of the typedef or type alias style.
+
- Improved :doc:`readability-named-parameter
<clang-tidy/checks/readability/named-parameter>` check by ignoring
standard tag types (e.g. ``std::in_place_t``, ``std::allocator_arg_t``,
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..e47939459dc77 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,7 @@ The available options are summarized below:
- :option:`CheckAnonFieldInParent`
- :option:`GetConfigPerFile`
- :option:`IgnoreMainLikeFunctions`
+ - :option:`TypedefInheritAnonTagConfig`
**Specific options**
@@ -2732,6 +2733,40 @@ After:
typedef int pre_myint_post;
+.. option:: TypedefInheritAnonTagConfig
+
+ When set to `true`, a typedef or type alias that provides the only name of
+ an otherwise unnamed tag, as in ``typedef enum {} MyEnum;``, is checked
+ against the naming style configured for the kind of that tag
+ (``AbstractClass``, ``Class``, ``Enum``, ``Struct`` or ``Union``, i.e.
+ :option:`EnumCase`, :option:`EnumPrefix`, :option:`EnumSuffix` and
+ :option:`EnumIgnoredRegexp` for an enum) rather than against the typedef
+ or type alias style. If no style is configured for that kind, the typedef
+ or type alias style still applies. Typedefs of named tags, of other
+ typedefs and of non-tag types are not affected. Default is `false`.
+
+For example using values of:
+
+ - TypedefInheritAnonTagConfig of `true`
+ - EnumCase of ``CamelCase``
+ - TypedefCase of ``lower_case``
+
+Identifies and/or transforms names as follows:
+
+Before:
+
+.. code-block:: c++
+
+ typedef enum { VAL } my_enum; // The typedef names the enum.
+ typedef enum Kind { VAL2 } my_kind; // Kind names the enum.
+
+After:
+
+.. code-block:: c++
+
+ typedef enum { VAL } MyEnum;
+ typedef enum Kind { VAL2 } my_kind;
+
.. option:: TypeTemplateParameterCase
When defined, the check will ensure type template parameter names conform to the
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-typedef-inherit-anon-tag-config.c b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-typedef-inherit-anon-tag-config.c
new file mode 100644
index 0000000000000..5312e5dfd5f47
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-typedef-inherit-anon-tag-config.c
@@ -0,0 +1,25 @@
+// RUN: %check_clang_tidy -std=c17 %s readability-identifier-naming %t -- \
+// RUN: -config='{CheckOptions: { \
+// RUN: readability-identifier-naming.TypedefInheritAnonTagConfig: true, \
+// RUN: readability-identifier-naming.EnumCase: CamelCase, \
+// RUN: readability-identifier-naming.StructCase: lower_case, \
+// RUN: readability-identifier-naming.UnionCase: UPPER_CASE, \
+// RUN: readability-identifier-naming.TypedefCase: camelBack, \
+// RUN: }}'
+
+typedef enum { EV_ANON } my_enum;
+// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: invalid case style for enum 'my_enum' [readability-identifier-naming]
+// CHECK-FIXES: typedef enum { EV_ANON } MyEnum;
+
+typedef struct { int Field; } My_Struct;
+// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: invalid case style for struct 'My_Struct' [readability-identifier-naming]
+// CHECK-FIXES: typedef struct { int Field; } my_struct;
+
+typedef union { int I; float F; } my_union;
+// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: invalid case style for union 'my_union' [readability-identifier-naming]
+// CHECK-FIXES: typedef union { int I; float F; } MY_UNION;
+
+// The tag has a name of its own, so the typedef style still applies.
+typedef struct data { int Field; } my_data;
+// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: invalid case style for typedef 'my_data' [readability-identifier-naming]
+// CHECK-FIXES: typedef struct data { int Field; } myData;
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-typedef-inherit-anon-tag-config.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-typedef-inherit-anon-tag-config.cpp
new file mode 100644
index 0000000000000..380b78d74c05b
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/identifier-naming-typedef-inherit-anon-tag-config.cpp
@@ -0,0 +1,159 @@
+// RUN: %check_clang_tidy -check-suffixes=TAGSTYLE,SHARED -std=c++17 %s \
+// RUN: readability-identifier-naming %t -- \
+// RUN: -config='{CheckOptions: { \
+// RUN: readability-identifier-naming.TypedefInheritAnonTagConfig: true, \
+// RUN: readability-identifier-naming.AbstractClassCase: CamelCase, \
+// RUN: readability-identifier-naming.ClassCase: CamelCase, \
+// RUN: readability-identifier-naming.EnumCase: CamelCase, \
+// RUN: readability-identifier-naming.EnumIgnoredRegexp: "ignored_.*", \
+// RUN: readability-identifier-naming.StructCase: lower_case, \
+// RUN: readability-identifier-naming.UnionCase: UPPER_CASE, \
+// RUN: readability-identifier-naming.TypeAliasCase: camelBack, \
+// RUN: readability-identifier-naming.TypedefCase: camelBack, \
+// RUN: }}'
+
+// Re-running the check on the fixed file must not produce any further
+// warning. Directly after the run it validates, because the runs below
+// overwrite %t.cpp.
+// RUN: clang-tidy %t.cpp -checks='-*,readability-identifier-naming' \
+// RUN: -warnings-as-errors='-*,readability-identifier-naming' \
+// RUN: -config='{CheckOptions: { \
+// RUN: readability-identifier-naming.TypedefInheritAnonTagConfig: true, \
+// RUN: readability-identifier-naming.AbstractClassCase: CamelCase, \
+// RUN: readability-identifier-naming.ClassCase: CamelCase, \
+// RUN: readability-identifier-naming.EnumCase: CamelCase, \
+// RUN: readability-identifier-naming.EnumIgnoredRegexp: "ignored_.*", \
+// RUN: readability-identifier-naming.StructCase: lower_case, \
+// RUN: readability-identifier-naming.UnionCase: UPPER_CASE, \
+// RUN: readability-identifier-naming.TypeAliasCase: camelBack, \
+// RUN: readability-identifier-naming.TypedefCase: camelBack, \
+// RUN: }}' -- -std=c++17
+
+// RUN: %check_clang_tidy -check-suffixes=TYPEDEFSTYLE,SHARED -std=c++17 %s \
+// RUN: readability-identifier-naming %t -- \
+// RUN: -config='{CheckOptions: { \
+// RUN: readability-identifier-naming.AbstractClassCase: CamelCase, \
+// RUN: readability-identifier-naming.ClassCase: CamelCase, \
+// RUN: readability-identifier-naming.EnumCase: CamelCase, \
+// RUN: readability-identifier-naming.EnumIgnoredRegexp: "ignored_.*", \
+// RUN: readability-identifier-naming.StructCase: lower_case, \
+// RUN: readability-identifier-naming.UnionCase: UPPER_CASE, \
+// RUN: readability-identifier-naming.TypeAliasCase: camelBack, \
+// RUN: readability-identifier-naming.TypedefCase: camelBack, \
+// RUN: }}'
+
+// RUN: %check_clang_tidy -check-suffixes=TYPEDEFSTYLE,SHARED -std=c++17 %s \
+// RUN: readability-identifier-naming %t -- \
+// RUN: -config='{CheckOptions: { \
+// RUN: readability-identifier-naming.TypedefInheritAnonTagConfig: true, \
+// RUN: readability-identifier-naming.TypeAliasCase: camelBack, \
+// RUN: readability-identifier-naming.TypedefCase: camelBack, \
+// RUN: }}'
+
+// The typedef is the only name of the tag it defines, so it can inherit the
+// style configured for that tag kind.
+
+typedef enum { EV_ANON } my_enum;
+// CHECK-MESSAGES-TAGSTYLE: :[[@LINE-1]]:26: warning: invalid case style for enum 'my_enum' [readability-identifier-naming]
+// CHECK-MESSAGES-TYPEDEFSTYLE: :[[@LINE-2]]:26: warning: invalid case style for typedef 'my_enum' [readability-identifier-naming]
+// CHECK-FIXES-TAGSTYLE: typedef enum { EV_ANON } MyEnum;
+// CHECK-FIXES-TYPEDEFSTYLE: typedef enum { EV_ANON } myEnum;
+
+typedef struct { int Field; } My_Struct;
+// CHECK-MESSAGES-TAGSTYLE: :[[@LINE-1]]:31: warning: invalid case style for struct 'My_Struct' [readability-identifier-naming]
+// CHECK-MESSAGES-TYPEDEFSTYLE: :[[@LINE-2]]:31: warning: invalid case style for typedef 'My_Struct' [readability-identifier-naming]
+// CHECK-FIXES-TAGSTYLE: typedef struct { int Field; } my_struct;
+// CHECK-FIXES-TYPEDEFSTYLE: typedef struct { int Field; } myStruct;
+
+typedef union { int I; float F; } my_union;
+// CHECK-MESSAGES-TAGSTYLE: :[[@LINE-1]]:35: warning: invalid case style for union 'my_union' [readability-identifier-naming]
+// CHECK-MESSAGES-TYPEDEFSTYLE: :[[@LINE-2]]:35: warning: invalid case style for typedef 'my_union' [readability-identifier-naming]
+// CHECK-FIXES-TAGSTYLE: typedef union { int I; float F; } MY_UNION;
+// CHECK-FIXES-TYPEDEFSTYLE: typedef union { int I; float F; } myUnion;
+
+typedef class { int Field; } my_class;
+// CHECK-MESSAGES-TAGSTYLE: :[[@LINE-1]]:30: warning: invalid case style for class 'my_class' [readability-identifier-naming]
+// CHECK-MESSAGES-TYPEDEFSTYLE: :[[@LINE-2]]:30: warning: invalid case style for typedef 'my_class' [readability-identifier-naming]
+// CHECK-FIXES-TAGSTYLE: typedef class { int Field; } MyClass;
+// CHECK-FIXES-TYPEDEFSTYLE: typedef class { int Field; } myClass;
+
+typedef class { public: virtual void f() = 0; } my_abstract;
+// CHECK-MESSAGES-TAGSTYLE: :[[@LINE-1]]:49: warning: invalid case style for abstract class 'my_abstract' [readability-identifier-naming]
+// CHECK-MESSAGES-TYPEDEFSTYLE: :[[@LINE-2]]:49: warning: invalid case style for typedef 'my_abstract' [readability-identifier-naming]
+// CHECK-FIXES-TAGSTYLE: typedef class { public: virtual void f() = 0; } MyAbstract;
+// CHECK-FIXES-TYPEDEFSTYLE: typedef class { public: virtual void f() = 0; } myAbstract;
+
+using my_alias_enum = enum { EV_ALIAS };
+// CHECK-MESSAGES-TAGSTYLE: :[[@LINE-1]]:7: warning: invalid case style for enum 'my_alias_enum' [readability-identifier-naming]
+// CHECK-MESSAGES-TYPEDEFSTYLE: :[[@LINE-2]]:7: warning: invalid case style for type alias 'my_alias_enum' [readability-identifier-naming]
+// CHECK-FIXES-TAGSTYLE: using MyAliasEnum = enum { EV_ALIAS };
+// CHECK-FIXES-TYPEDEFSTYLE: using myAliasEnum = enum { EV_ALIAS };
+
+// The whole style of the tag kind is inherited, not just its case, so the
+// ignored regexp of the enum applies here.
+
+typedef enum { EV_IGNORED } ignored_enum_t;
+// CHECK-MESSAGES-TYPEDEFSTYLE: :[[@LINE-1]]:29: warning: invalid case style for typedef 'ignored_enum_t' [readability-identifier-naming]
+// CHECK-FIXES-TAGSTYLE: typedef enum { EV_IGNORED } ignored_enum_t;
+// CHECK-FIXES-TYPEDEFSTYLE: typedef enum { EV_IGNORED } ignoredEnumT;
+
+// The tag has a name of its own, so the typedef is just an alias for it and
+// keeps the typedef style.
+
+typedef enum Kind { EV_NAMED } my_kind;
+// CHECK-MESSAGES-SHARED: :[[@LINE-1]]:32: warning: invalid case style for typedef 'my_kind' [readability-identifier-naming]
+// CHECK-FIXES-SHARED: typedef enum Kind { EV_NAMED } myKind;
+
+using my_kind_alias = Kind;
+// CHECK-MESSAGES-SHARED: :[[@LINE-1]]:7: warning: invalid case style for type alias 'my_kind_alias' [readability-identifier-naming]
+// CHECK-FIXES-SHARED: using myKindAlias = Kind;
+
+typedef struct data { int Field; } my_data;
+// CHECK-MESSAGES-SHARED: :[[@LINE-1]]:36: warning: invalid case style for typedef 'my_data' [readability-identifier-naming]
+// CHECK-FIXES-SHARED: typedef struct data { int Field; } myData;
+
+// Of several declarators, the first one that denotes the tag type itself names
+// the tag. The others are ordinary typedefs.
+
+typedef enum { EV_MULTI } FirstEnum, second_enum;
+// CHECK-MESSAGES-TYPEDEFSTYLE: :[[@LINE-1]]:27: warning: invalid case style for typedef 'FirstEnum' [readability-identifier-naming]
+// CHECK-MESSAGES-SHARED: :[[@LINE-2]]:38: warning: invalid case style for typedef 'second_enum' [readability-identifier-naming]
+// CHECK-FIXES-TAGSTYLE: typedef enum { EV_MULTI } FirstEnum, secondEnum;
+// CHECK-FIXES-TYPEDEFSTYLE: typedef enum { EV_MULTI } firstEnum, secondEnum;
+
+typedef struct { int Field; } *first_ptr, second_struct;
+// CHECK-MESSAGES-SHARED: :[[@LINE-1]]:32: warning: invalid case style for typedef 'first_ptr' [readability-identifier-naming]
+// CHECK-MESSAGES-TYPEDEFSTYLE: :[[@LINE-2]]:43: warning: invalid case style for typedef 'second_struct' [readability-identifier-naming]
+// CHECK-FIXES-TAGSTYLE: typedef struct { int Field; } *firstPtr, second_struct;
+// CHECK-FIXES-TYPEDEFSTYLE: typedef struct { int Field; } *firstPtr, secondStruct;
+
+// The typedef does not name a tag type at all.
+
+typedef struct { int Field; } *my_struct_ptr;
+// CHECK-MESSAGES-SHARED: :[[@LINE-1]]:32: warning: invalid case style for typedef 'my_struct_ptr' [readability-identifier-naming]
+// CHECK-FIXES-SHARED: typedef struct { int Field; } *myStructPtr;
+
+typedef int my_int;
+// CHECK-MESSAGES-SHARED: :[[@LINE-1]]:13: warning: invalid case style for typedef 'my_int' [readability-identifier-naming]
+// CHECK-FIXES-SHARED: typedef int myInt;
+
+// A typedef of a typedef does not name the tag either.
+
+typedef My_Struct my_struct_alias;
+// CHECK-MESSAGES-SHARED: :[[@LINE-1]]:19: warning: invalid case style for typedef 'my_struct_alias' [readability-identifier-naming]
+// CHECK-FIXES-TAGSTYLE: typedef my_struct myStructAlias;
+// CHECK-FIXES-TYPEDEFSTYLE: typedef myStruct myStructAlias;
+
+// The typedef names the tag in the template pattern as well as in its
+// instantiations, so it is reported only once.
+
+template <typename T>
+struct holder {
+ typedef enum { EV_TPL } inner_enum;
+ // CHECK-MESSAGES-TAGSTYLE: :[[@LINE-1]]:27: warning: invalid case style for enum 'inner_enum' [readability-identifier-naming]
+ // CHECK-MESSAGES-TYPEDEFSTYLE: :[[@LINE-2]]:27: warning: invalid case style for typedef 'inner_enum' [readability-identifier-naming]
+ // CHECK-FIXES-TAGSTYLE: typedef enum { EV_TPL } InnerEnum;
+ // CHECK-FIXES-TYPEDEFSTYLE: typedef enum { EV_TPL } innerEnum;
+};
+
+template struct holder<int>;
>From baf68647ca864f733b65e53843ab96232d542012 Mon Sep 17 00:00:00 2001
From: David Meng <davidmenggx at gmail.com>
Date: Tue, 4 Aug 2026 08:50:04 -0700
Subject: [PATCH 2/2] Update clang-tools-extra/docs/ReleaseNotes.rst
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
clang-tools-extra/docs/ReleaseNotes.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 6a9946d1c9313..422804542090f 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -120,7 +120,7 @@ Changes in existing checks
- Improved :doc:`readability-identifier-naming
<clang-tidy/checks/readability/identifier-naming>` check by adding the
- :option:`TypedefInheritAnonTagConfig` option, which checks a typedef or type
+ `TypedefInheritAnonTagConfig` option, which checks a typedef or type
alias that provides the only name of an otherwise unnamed tag, such as
``typedef enum {} MyEnum;``, against the style configured for that tag kind
instead of the typedef or type alias style.
More information about the cfe-commits
mailing list