[clang-tidy] Improve readability-enum-initial-value diagnostic message (#176485)

Enhance the readability-enum-initial-value checker to list which
enumerators
are not initialized in notes. This makes it easier for users to identify
which
specific enumerators need explicit initialization.
This commit is contained in:
Endre Fülöp 2026-01-19 15:42:17 +01:00 committed by GitHub
parent d12019d5e5
commit d1e02cd31e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 72 additions and 32 deletions

View File

@ -165,22 +165,36 @@ void EnumInitialValueCheck::registerMatchers(MatchFinder *Finder) {
void EnumInitialValueCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("inconsistent")) {
const DiagnosticBuilder Diag =
diag(
Enum->getBeginLoc(),
"initial values in enum '%0' are not consistent, consider explicit "
"initialization of all, none or only the first enumerator")
<< getName(Enum);
for (const EnumConstantDecl *ECD : Enum->enumerators())
if (ECD->getInitExpr() == nullptr) {
const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
ECD->getLocation(), 0, *Result.SourceManager, getLangOpts());
if (EndLoc.isMacroID())
continue;
llvm::SmallString<8> Str{" = "};
ECD->getInitVal().toString(Str);
Diag << FixItHint::CreateInsertion(EndLoc, Str);
// Emit warning first (DiagnosticBuilder emits on destruction), then notes.
// Notes must follow the primary diagnostic or they may be dropped.
{
const DiagnosticBuilder Diag =
diag(Enum->getBeginLoc(), "initial values in enum '%0' are not "
"consistent, consider explicit "
"initialization of all, none or only the "
"first enumerator")
<< getName(Enum);
for (const EnumConstantDecl *ECD : Enum->enumerators()) {
if (ECD->getInitExpr() == nullptr) {
const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
ECD->getLocation(), 0, *Result.SourceManager, getLangOpts());
if (EndLoc.isMacroID())
continue;
llvm::SmallString<8> Str{" = "};
ECD->getInitVal().toString(Str);
Diag << FixItHint::CreateInsertion(EndLoc, Str);
}
}
}
for (const EnumConstantDecl *ECD : Enum->enumerators()) {
if (ECD->getInitExpr() == nullptr) {
diag(ECD->getLocation(), "uninitialized enumerator '%0' defined here",
DiagnosticIDs::Note)
<< ECD->getName();
}
}
return;
}

View File

@ -113,6 +113,11 @@ Changes in existing checks
<clang-tidy/checks/performance/move-const-arg>` check by avoiding false
positives on trivially copyable types with a non-public copy constructor.
- Improved :doc:`readability-enum-initial-value
<clang-tidy/checks/readability/enum-initial-value>` check: the warning message
now uses separate note diagnostics for each uninitialized enumerator, making
it easier to see which specific enumerators need explicit initialization.
Removed checks
^^^^^^^^^^^^^^

View File

@ -36,19 +36,25 @@ The following three cases are accepted:
c2 = 2,
};
enum D { // Invalid, d1 is not explicitly initialized!
enum D { // warning: initial values in enum 'D' are not consistent,
// consider explicit initialization of all, none or only
// the first enumerator
d0 = 0,
d1,
d1, // note: uninitialized enumerator 'd1' defined here
d2 = 2,
};
enum E { // Invalid, e1, e3, and e5 are not explicitly initialized.
enum E { // warning: initial values in enum 'E' are not consistent,
// consider explicit initialization of all, none or only
// the first enumerator
e0 = 0,
e1,
e1, // note: uninitialized enumerator 'e1' defined here
e2 = 2,
e3, // Dangerous, as the numeric values of e3 and e5 are both 3, and this is not explicitly visible in the code!
e3, // note: uninitialized enumerator 'e3' defined here
// Dangerous, as the numeric values of e3 and e5 are both 3,
// and this is not explicitly visible in the code!
e4 = 2,
e5,
e5, // note: uninitialized enumerator 'e5' defined here
};
This check corresponds to the CERT C Coding Standard recommendation `INT09-C. Ensure enumeration constants map to unique values

View File

@ -6,10 +6,12 @@
// RUN: }}'
enum EError {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'EError' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum 'EError' are not consistent
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'EError' are not consistent, consider explicit initialization of all, none or only the first enumerator
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum 'EError' are not consistent, consider explicit initialization of all, none or only the first enumerator
EError_a = 1,
EError_b,
// CHECK-MESSAGES: :[[@LINE-1]]:3: note: uninitialized enumerator 'EError_b' defined here
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:3: note: uninitialized enumerator 'EError_b' defined here
// CHECK-FIXES: EError_b = 2,
EError_c = 3,
};
@ -34,10 +36,14 @@ enum EAll {
#define ENUMERATOR_1 EMacro1_b
enum EMacro1 {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'EMacro1' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum 'EMacro1' are not consistent
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'EMacro1' are not consistent, consider explicit initialization of all, none or only the first enumerator
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum 'EMacro1' are not consistent, consider explicit initialization of all, none or only the first enumerator
EMacro1_a = 1,
ENUMERATOR_1,
// CHECK-MESSAGES: :[[@LINE-1]]:3: note: uninitialized enumerator 'EMacro1_b' defined here
// CHECK-MESSAGES: note: expanded from macro 'ENUMERATOR_1'
// CHECK-MESSAGES-ENABLE: :[[@LINE-3]]:3: note: uninitialized enumerator 'EMacro1_b' defined here
// CHECK-MESSAGES-ENABLE: note: expanded from macro 'ENUMERATOR_1'
// CHECK-FIXES: ENUMERATOR_1 = 2,
EMacro1_c = 3,
};
@ -45,20 +51,24 @@ enum EMacro1 {
#define ENUMERATOR_2 EMacro2_b = 2
enum EMacro2 {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'EMacro2' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum 'EMacro2' are not consistent
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'EMacro2' are not consistent, consider explicit initialization of all, none or only the first enumerator
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum 'EMacro2' are not consistent, consider explicit initialization of all, none or only the first enumerator
EMacro2_a = 1,
ENUMERATOR_2,
EMacro2_c,
// CHECK-MESSAGES: :[[@LINE-1]]:3: note: uninitialized enumerator 'EMacro2_c' defined here
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:3: note: uninitialized enumerator 'EMacro2_c' defined here
// CHECK-FIXES: EMacro2_c = 3,
};
enum {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum '<unnamed>' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum '<unnamed>' are not consistent
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum '<unnamed>' are not consistent, consider explicit initialization of all, none or only the first enumerator
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum '<unnamed>' are not consistent, consider explicit initialization of all, none or only the first enumerator
EAnonymous_a = 1,
EAnonymous_b,
// CHECK-MESSAGES: :[[@LINE-1]]:3: note: uninitialized enumerator 'EAnonymous_b' defined here
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:3: note: uninitialized enumerator 'EAnonymous_b' defined here
// CHECK-FIXES: EAnonymous_b = 2,
EAnonymous_c = 3,
};
@ -94,12 +104,16 @@ enum EnumSequentialInitialValue {
enum WithFwdDeclInconsistent : int;
enum WithFwdDeclInconsistent : int {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'WithFwdDeclInconsistent' are not consistent
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum 'WithFwdDeclInconsistent' are not consistent
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'WithFwdDeclInconsistent' are not consistent, consider explicit initialization of all, none or only the first enumerator
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:1: warning: initial values in enum 'WithFwdDeclInconsistent' are not consistent, consider explicit initialization of all, none or only the first enumerator
EFI0,
// CHECK-MESSAGES: :[[@LINE-1]]:3: note: uninitialized enumerator 'EFI0' defined here
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:3: note: uninitialized enumerator 'EFI0' defined here
// CHECK-FIXES: EFI0 = 0,
EFI1 = 1,
EFI2,
// CHECK-MESSAGES: :[[@LINE-1]]:3: note: uninitialized enumerator 'EFI2' defined here
// CHECK-MESSAGES-ENABLE: :[[@LINE-2]]:3: note: uninitialized enumerator 'EFI2' defined here
// CHECK-FIXES: EFI2 = 2,
};

View File

@ -1,9 +1,10 @@
// RUN: %check_clang_tidy %s readability-enum-initial-value %t
enum class EError {
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'EError' are not consistent
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: initial values in enum 'EError' are not consistent, consider explicit initialization of all, none or only the first enumerator
EError_a = 1,
EError_b,
// CHECK-MESSAGES: :[[@LINE-1]]:3: note: uninitialized enumerator 'EError_b' defined here
// CHECK-FIXES: EError_b = 2,
EError_c = 3,
};