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 <[email protected]> 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 <[email protected]> Date: Tue, 4 Aug 2026 08:50:04 -0700 Subject: [PATCH 2/2] Update clang-tools-extra/docs/ReleaseNotes.rst Co-authored-by: EugeneZelenko <[email protected]> --- 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. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
