Skip to content

Conversation

felix642
Copy link
Contributor

Added a new Option IgnoredEnums to bugprone invalid enum default initialization to limit the scope of the analysis. This is needed to remove warnings on enums like std::errc where the enum doesn't define a value of 0, but is still used to check if some function calls like std::from_chars are executed correctly.

The C++ Standard section 22.13.2 mentions the following : "[...] If the member ec of the return value is such that the value is equal to the value of a value-initialized errc, the conversion was successful [...]"

This means that a call to std::errc{} is clearly defined by the standard and should not raise any warning under this check.

@llvmbot
Copy link
Member

llvmbot commented Sep 17, 2025

@llvm/pr-subscribers-clang-tidy

Author: Félix-Antoine Constantin (felix642)

Changes

Added a new Option IgnoredEnums to bugprone invalid enum default initialization to limit the scope of the analysis. This is needed to remove warnings on enums like std::errc where the enum doesn't define a value of 0, but is still used to check if some function calls like std::from_chars are executed correctly.

The C++ Standard section 22.13.2 mentions the following : "[...] If the member ec of the return value is such that the value is equal to the value of a value-initialized errc, the conversion was successful [...]"

This means that a call to std::errc{} is clearly defined by the standard and should not raise any warning under this check.


Full diff: https://github.com/llvm/llvm-project/pull/159220.diff

5 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp (+15-3)
  • (modified) clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.h (+4)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+4)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/bugprone/invalid-enum-default-initialization.rst (+5)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/invalid-enum-default-initialization.cpp (+33-23)
diff --git a/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp
index 1e657888b0fc0..55bcec27b1863 100644
--- a/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp
@@ -7,6 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "InvalidEnumDefaultInitializationCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/TypeVisitor.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -88,12 +90,22 @@ class FindEnumMember : public TypeVisitor<FindEnumMember, bool> {
 
 InvalidEnumDefaultInitializationCheck::InvalidEnumDefaultInitializationCheck(
     StringRef Name, ClangTidyContext *Context)
-    : ClangTidyCheck(Name, Context) {}
+    : ClangTidyCheck(Name, Context),
+      IgnoredEnums(utils::options::parseStringList(
+          Options.get("IgnoredEnums", "::std::errc"))) {}
+
+void InvalidEnumDefaultInitializationCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IgnoredEnums",
+                utils::options::serializeStringList(IgnoredEnums));
+}
 
 void InvalidEnumDefaultInitializationCheck::registerMatchers(
     MatchFinder *Finder) {
-  auto EnumWithoutZeroValue = enumType(
-      hasDeclaration(enumDecl(isCompleteAndHasNoZeroValue()).bind("enum")));
+  auto EnumWithoutZeroValue = enumType(hasDeclaration(
+      enumDecl(isCompleteAndHasNoZeroValue(),
+               unless(matchers::matchesAnyListedName(IgnoredEnums)))
+          .bind("enum")));
   auto EnumOrArrayOfEnum = qualType(hasUnqualifiedDesugaredType(
       anyOf(EnumWithoutZeroValue,
             arrayType(hasElementType(qualType(
diff --git a/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.h b/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.h
index b9b4f20d111fc..af4a97ae12cec 100644
--- a/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.h
@@ -24,6 +24,10 @@ class InvalidEnumDefaultInitializationCheck : public ClangTidyCheck {
                                         ClangTidyContext *Context);
   void registerMatchers(ast_matchers::MatchFinder *Finder) override;
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  const std::vector<StringRef> IgnoredEnums;
 };
 
 } // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3f403c42a168a..a678be2dbb4e9 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -219,6 +219,10 @@ Changes in existing checks
   <clang-tidy/checks/bugprone/infinite-loop>` check by adding detection for
   variables introduced by structured bindings.
 
+- Improved :doc:`bugprone-invalid-enum-default-initialization
+  <clang-tidy/checks/bugprone/invalid-enum-default-initialization>` with new
+  ``IgnoredEnums`` option to ignore some enums during analysis.
+
 - Improved :doc:`bugprone-narrowing-conversions
   <clang-tidy/checks/bugprone/narrowing-conversions>` check by fixing
   false positive from analysis of a conditional expression in C.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/invalid-enum-default-initialization.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/invalid-enum-default-initialization.rst
index a3bd2b6d85c37..92a51d5ec0a31 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/invalid-enum-default-initialization.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/invalid-enum-default-initialization.rst
@@ -70,3 +70,8 @@ enum type) are set to 0.
   enum Enum1 Array3[2][2] = {{Enum1_A, Enum1_A}}; // warn: elements of second array are initialized to 0
 
   struct Struct1 S1 = {1}; // warn: element 'b' is initialized to 0
+
+.. option:: IgnoredContainers
+
+Semicolon-separated list of enums regexp for which this check won't be
+enforced. Default is `::std::errc`.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/invalid-enum-default-initialization.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/invalid-enum-default-initialization.cpp
index eb3d5632eaef7..54e37fa32e187 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/invalid-enum-default-initialization.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/invalid-enum-default-initialization.cpp
@@ -1,4 +1,5 @@
-// RUN: %check_clang_tidy -std=c++17 %s bugprone-invalid-enum-default-initialization %t
+// RUN: %check_clang_tidy -check-suffixes=,DEFAULT -std=c++17 %s bugprone-invalid-enum-default-initialization %t
+// RUN: %check_clang_tidy -std=c++17 %s bugprone-invalid-enum-default-initialization %t -- -config="{CheckOptions: {bugprone-invalid-enum-default-initialization.IgnoredEnums: '::MyEnum'}}"
 
 enum class Enum0: int {
   A = 0,
@@ -24,10 +25,10 @@ Enum0 E0_6{Enum0::B};
 
 Enum1 E1_1{};
 // CHECK-NOTES: :[[@LINE-1]]:11: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-// CHECK-NOTES: :8:12: note: enum is defined here
+// CHECK-NOTES: :9:12: note: enum is defined here
 Enum1 E1_2 = Enum1();
 // CHECK-NOTES: :[[@LINE-1]]:14: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-// CHECK-NOTES: :8:12: note: enum is defined here
+// CHECK-NOTES: :9:12: note: enum is defined here
 Enum1 E1_3;
 Enum1 E1_4{0};
 Enum1 E1_5{Enum1::A};
@@ -35,44 +36,44 @@ Enum1 E1_6{Enum1::B};
 
 Enum2 E2_1{};
 // CHECK-NOTES: :[[@LINE-1]]:11: warning: enum value of type 'Enum2' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-// CHECK-NOTES: :13:6: note: enum is defined here
+// CHECK-NOTES: :14:6: note: enum is defined here
 Enum2 E2_2 = Enum2();
 // CHECK-NOTES: :[[@LINE-1]]:14: warning: enum value of type 'Enum2' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-// CHECK-NOTES: :13:6: note: enum is defined here
+// CHECK-NOTES: :14:6: note: enum is defined here
 
 void f1() {
   static Enum1 S; // FIMXE: warn for this?
   Enum1 A;
   Enum1 B = Enum1();
   // CHECK-NOTES: :[[@LINE-1]]:13: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   int C = int();
 }
 
 void f2() {
   Enum1 A{};
   // CHECK-NOTES: :[[@LINE-1]]:10: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   Enum1 B = Enum1();
   // CHECK-NOTES: :[[@LINE-1]]:13: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   Enum1 C[5] = {{}};
   // CHECK-NOTES: :[[@LINE-1]]:16: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   // CHECK-NOTES: :[[@LINE-3]]:17: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   Enum1 D[5] = {}; // FIMXE: warn for this?
   // CHECK-NOTES: :[[@LINE-1]]:16: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
 }
 
 struct S1 {
   Enum1 E_1{};
   // CHECK-NOTES: :[[@LINE-1]]:12: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   Enum1 E_2 = Enum1();
   // CHECK-NOTES: :[[@LINE-1]]:15: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   Enum1 E_3;
   Enum1 E_4;
   Enum1 E_5;
@@ -80,10 +81,10 @@ struct S1 {
   S1() :
     E_3{},
     // CHECK-NOTES: :[[@LINE-1]]:8: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-    // CHECK-NOTES: :8:12: note: enum is defined here
+    // CHECK-NOTES: :9:12: note: enum is defined here
     E_4(),
     // CHECK-NOTES: :[[@LINE-1]]:8: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-    // CHECK-NOTES: :8:12: note: enum is defined here
+    // CHECK-NOTES: :9:12: note: enum is defined here
     E_5{Enum1::B}
   {}
 };
@@ -110,22 +111,22 @@ struct S5 {
 
 S2 VarS2{};
 // CHECK-NOTES: :[[@LINE-1]]:9: warning: enum value of type 'Enum1' initialized with invalid value of 0
-// CHECK-NOTES: :8:12: note: enum is defined here
+// CHECK-NOTES: :9:12: note: enum is defined here
 // CHECK-NOTES: :[[@LINE-3]]:9: warning: enum value of type 'Enum2' initialized with invalid value of 0
-// CHECK-NOTES: :13:6: note: enum is defined here
+// CHECK-NOTES: :14:6: note: enum is defined here
 S3 VarS3{};
 // CHECK-NOTES: :[[@LINE-1]]:10: warning: enum value of type 'Enum1' initialized with invalid value of 0
-// CHECK-NOTES: :8:12: note: enum is defined here
+// CHECK-NOTES: :9:12: note: enum is defined here
 // CHECK-NOTES: :[[@LINE-3]]:10: warning: enum value of type 'Enum2' initialized with invalid value of 0
-// CHECK-NOTES: :13:6: note: enum is defined here
+// CHECK-NOTES: :14:6: note: enum is defined here
 S4 VarS4{};
 // CHECK-NOTES: :[[@LINE-1]]:10: warning: enum value of type 'Enum1' initialized with invalid value of 0
-// CHECK-NOTES: :8:12: note: enum is defined here
+// CHECK-NOTES: :9:12: note: enum is defined here
 // CHECK-NOTES: :[[@LINE-3]]:10: warning: enum value of type 'Enum2' initialized with invalid value of 0
-// CHECK-NOTES: :13:6: note: enum is defined here
+// CHECK-NOTES: :14:6: note: enum is defined here
 S5 VarS5{};
 // CHECK-NOTES: :[[@LINE-1]]:10: warning: enum value of type 'Enum1' initialized with invalid value of 0
-// CHECK-NOTES: :8:12: note: enum is defined here
+// CHECK-NOTES: :9:12: note: enum is defined here
 
 enum class EnumFwd;
 
@@ -139,7 +140,16 @@ template<typename T>
 struct Templ {
   T Mem1{};
   // CHECK-NOTES: :[[@LINE-1]]:9: warning: enum value of type 'Enum1' initialized with invalid value of 0
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
 };
 
 Templ<Enum1> TemplVar;
+
+enum MyEnum {
+  A = 1,
+  B
+};
+
+MyEnum MyEnumVar{};
+// CHECK-NOTES-DEFAULT: :[[@LINE-1]]:17: warning: enum value of type 'MyEnum' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
+// CHECK-NOTES-DEFAULT: :148:6: note: enum is defined here

@llvmbot
Copy link
Member

llvmbot commented Sep 17, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: Félix-Antoine Constantin (felix642)

Changes

Added a new Option IgnoredEnums to bugprone invalid enum default initialization to limit the scope of the analysis. This is needed to remove warnings on enums like std::errc where the enum doesn't define a value of 0, but is still used to check if some function calls like std::from_chars are executed correctly.

The C++ Standard section 22.13.2 mentions the following : "[...] If the member ec of the return value is such that the value is equal to the value of a value-initialized errc, the conversion was successful [...]"

This means that a call to std::errc{} is clearly defined by the standard and should not raise any warning under this check.


Full diff: https://github.com/llvm/llvm-project/pull/159220.diff

5 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp (+15-3)
  • (modified) clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.h (+4)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+4)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/bugprone/invalid-enum-default-initialization.rst (+5)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/invalid-enum-default-initialization.cpp (+33-23)
diff --git a/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp
index 1e657888b0fc0..55bcec27b1863 100644
--- a/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp
@@ -7,6 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "InvalidEnumDefaultInitializationCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/TypeVisitor.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -88,12 +90,22 @@ class FindEnumMember : public TypeVisitor<FindEnumMember, bool> {
 
 InvalidEnumDefaultInitializationCheck::InvalidEnumDefaultInitializationCheck(
     StringRef Name, ClangTidyContext *Context)
-    : ClangTidyCheck(Name, Context) {}
+    : ClangTidyCheck(Name, Context),
+      IgnoredEnums(utils::options::parseStringList(
+          Options.get("IgnoredEnums", "::std::errc"))) {}
+
+void InvalidEnumDefaultInitializationCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IgnoredEnums",
+                utils::options::serializeStringList(IgnoredEnums));
+}
 
 void InvalidEnumDefaultInitializationCheck::registerMatchers(
     MatchFinder *Finder) {
-  auto EnumWithoutZeroValue = enumType(
-      hasDeclaration(enumDecl(isCompleteAndHasNoZeroValue()).bind("enum")));
+  auto EnumWithoutZeroValue = enumType(hasDeclaration(
+      enumDecl(isCompleteAndHasNoZeroValue(),
+               unless(matchers::matchesAnyListedName(IgnoredEnums)))
+          .bind("enum")));
   auto EnumOrArrayOfEnum = qualType(hasUnqualifiedDesugaredType(
       anyOf(EnumWithoutZeroValue,
             arrayType(hasElementType(qualType(
diff --git a/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.h b/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.h
index b9b4f20d111fc..af4a97ae12cec 100644
--- a/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.h
@@ -24,6 +24,10 @@ class InvalidEnumDefaultInitializationCheck : public ClangTidyCheck {
                                         ClangTidyContext *Context);
   void registerMatchers(ast_matchers::MatchFinder *Finder) override;
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  const std::vector<StringRef> IgnoredEnums;
 };
 
 } // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3f403c42a168a..a678be2dbb4e9 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -219,6 +219,10 @@ Changes in existing checks
   <clang-tidy/checks/bugprone/infinite-loop>` check by adding detection for
   variables introduced by structured bindings.
 
+- Improved :doc:`bugprone-invalid-enum-default-initialization
+  <clang-tidy/checks/bugprone/invalid-enum-default-initialization>` with new
+  ``IgnoredEnums`` option to ignore some enums during analysis.
+
 - Improved :doc:`bugprone-narrowing-conversions
   <clang-tidy/checks/bugprone/narrowing-conversions>` check by fixing
   false positive from analysis of a conditional expression in C.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/invalid-enum-default-initialization.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/invalid-enum-default-initialization.rst
index a3bd2b6d85c37..92a51d5ec0a31 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/invalid-enum-default-initialization.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/invalid-enum-default-initialization.rst
@@ -70,3 +70,8 @@ enum type) are set to 0.
   enum Enum1 Array3[2][2] = {{Enum1_A, Enum1_A}}; // warn: elements of second array are initialized to 0
 
   struct Struct1 S1 = {1}; // warn: element 'b' is initialized to 0
+
+.. option:: IgnoredContainers
+
+Semicolon-separated list of enums regexp for which this check won't be
+enforced. Default is `::std::errc`.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/invalid-enum-default-initialization.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/invalid-enum-default-initialization.cpp
index eb3d5632eaef7..54e37fa32e187 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/invalid-enum-default-initialization.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/invalid-enum-default-initialization.cpp
@@ -1,4 +1,5 @@
-// RUN: %check_clang_tidy -std=c++17 %s bugprone-invalid-enum-default-initialization %t
+// RUN: %check_clang_tidy -check-suffixes=,DEFAULT -std=c++17 %s bugprone-invalid-enum-default-initialization %t
+// RUN: %check_clang_tidy -std=c++17 %s bugprone-invalid-enum-default-initialization %t -- -config="{CheckOptions: {bugprone-invalid-enum-default-initialization.IgnoredEnums: '::MyEnum'}}"
 
 enum class Enum0: int {
   A = 0,
@@ -24,10 +25,10 @@ Enum0 E0_6{Enum0::B};
 
 Enum1 E1_1{};
 // CHECK-NOTES: :[[@LINE-1]]:11: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-// CHECK-NOTES: :8:12: note: enum is defined here
+// CHECK-NOTES: :9:12: note: enum is defined here
 Enum1 E1_2 = Enum1();
 // CHECK-NOTES: :[[@LINE-1]]:14: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-// CHECK-NOTES: :8:12: note: enum is defined here
+// CHECK-NOTES: :9:12: note: enum is defined here
 Enum1 E1_3;
 Enum1 E1_4{0};
 Enum1 E1_5{Enum1::A};
@@ -35,44 +36,44 @@ Enum1 E1_6{Enum1::B};
 
 Enum2 E2_1{};
 // CHECK-NOTES: :[[@LINE-1]]:11: warning: enum value of type 'Enum2' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-// CHECK-NOTES: :13:6: note: enum is defined here
+// CHECK-NOTES: :14:6: note: enum is defined here
 Enum2 E2_2 = Enum2();
 // CHECK-NOTES: :[[@LINE-1]]:14: warning: enum value of type 'Enum2' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-// CHECK-NOTES: :13:6: note: enum is defined here
+// CHECK-NOTES: :14:6: note: enum is defined here
 
 void f1() {
   static Enum1 S; // FIMXE: warn for this?
   Enum1 A;
   Enum1 B = Enum1();
   // CHECK-NOTES: :[[@LINE-1]]:13: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   int C = int();
 }
 
 void f2() {
   Enum1 A{};
   // CHECK-NOTES: :[[@LINE-1]]:10: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   Enum1 B = Enum1();
   // CHECK-NOTES: :[[@LINE-1]]:13: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   Enum1 C[5] = {{}};
   // CHECK-NOTES: :[[@LINE-1]]:16: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   // CHECK-NOTES: :[[@LINE-3]]:17: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   Enum1 D[5] = {}; // FIMXE: warn for this?
   // CHECK-NOTES: :[[@LINE-1]]:16: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
 }
 
 struct S1 {
   Enum1 E_1{};
   // CHECK-NOTES: :[[@LINE-1]]:12: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   Enum1 E_2 = Enum1();
   // CHECK-NOTES: :[[@LINE-1]]:15: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
   Enum1 E_3;
   Enum1 E_4;
   Enum1 E_5;
@@ -80,10 +81,10 @@ struct S1 {
   S1() :
     E_3{},
     // CHECK-NOTES: :[[@LINE-1]]:8: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-    // CHECK-NOTES: :8:12: note: enum is defined here
+    // CHECK-NOTES: :9:12: note: enum is defined here
     E_4(),
     // CHECK-NOTES: :[[@LINE-1]]:8: warning: enum value of type 'Enum1' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
-    // CHECK-NOTES: :8:12: note: enum is defined here
+    // CHECK-NOTES: :9:12: note: enum is defined here
     E_5{Enum1::B}
   {}
 };
@@ -110,22 +111,22 @@ struct S5 {
 
 S2 VarS2{};
 // CHECK-NOTES: :[[@LINE-1]]:9: warning: enum value of type 'Enum1' initialized with invalid value of 0
-// CHECK-NOTES: :8:12: note: enum is defined here
+// CHECK-NOTES: :9:12: note: enum is defined here
 // CHECK-NOTES: :[[@LINE-3]]:9: warning: enum value of type 'Enum2' initialized with invalid value of 0
-// CHECK-NOTES: :13:6: note: enum is defined here
+// CHECK-NOTES: :14:6: note: enum is defined here
 S3 VarS3{};
 // CHECK-NOTES: :[[@LINE-1]]:10: warning: enum value of type 'Enum1' initialized with invalid value of 0
-// CHECK-NOTES: :8:12: note: enum is defined here
+// CHECK-NOTES: :9:12: note: enum is defined here
 // CHECK-NOTES: :[[@LINE-3]]:10: warning: enum value of type 'Enum2' initialized with invalid value of 0
-// CHECK-NOTES: :13:6: note: enum is defined here
+// CHECK-NOTES: :14:6: note: enum is defined here
 S4 VarS4{};
 // CHECK-NOTES: :[[@LINE-1]]:10: warning: enum value of type 'Enum1' initialized with invalid value of 0
-// CHECK-NOTES: :8:12: note: enum is defined here
+// CHECK-NOTES: :9:12: note: enum is defined here
 // CHECK-NOTES: :[[@LINE-3]]:10: warning: enum value of type 'Enum2' initialized with invalid value of 0
-// CHECK-NOTES: :13:6: note: enum is defined here
+// CHECK-NOTES: :14:6: note: enum is defined here
 S5 VarS5{};
 // CHECK-NOTES: :[[@LINE-1]]:10: warning: enum value of type 'Enum1' initialized with invalid value of 0
-// CHECK-NOTES: :8:12: note: enum is defined here
+// CHECK-NOTES: :9:12: note: enum is defined here
 
 enum class EnumFwd;
 
@@ -139,7 +140,16 @@ template<typename T>
 struct Templ {
   T Mem1{};
   // CHECK-NOTES: :[[@LINE-1]]:9: warning: enum value of type 'Enum1' initialized with invalid value of 0
-  // CHECK-NOTES: :8:12: note: enum is defined here
+  // CHECK-NOTES: :9:12: note: enum is defined here
 };
 
 Templ<Enum1> TemplVar;
+
+enum MyEnum {
+  A = 1,
+  B
+};
+
+MyEnum MyEnumVar{};
+// CHECK-NOTES-DEFAULT: :[[@LINE-1]]:17: warning: enum value of type 'MyEnum' initialized with invalid value of 0, enum doesn't have a zero-value enumerator
+// CHECK-NOTES-DEFAULT: :148:6: note: enum is defined here

@felix642
Copy link
Contributor Author

@balazske, this check was recently added by you. If you have time to review this PR I would greatly appreciate it.

@balazske balazske self-requested a review September 17, 2025 07:40
@balazske
Copy link
Collaborator

Would it be better to add the std::errc always to the ignore list? If the user specifies the ignore list and it does not contain std::errc it is missing from the list. I suspect that the warning for std::errc is never needed from this checker.

@felix642
Copy link
Contributor Author

Would it be better to add the std::errc always to the ignore list? If the user specifies the ignore list and it does not contain std::errc it is missing from the list. I suspect that the warning for std::errc is never needed from this checker.

I think you're right. I wanted to give the user as much control as possible hence why I added an option with std::errc as the default value, but I don't see any reason why the user would want to flag valid code. It might also play against them, if they decide to use this option, they might forget to keep the enum and the check might start flagging code that is shouldn't.

I think, I'll keep the option, but move the enum to be implicitely added to make sure we don't raise false positive.

Copy link

github-actions bot commented Sep 21, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@balazske
Copy link
Collaborator

Code is good, just documentation can be updated (std::errc is always ignored) and formatting should be fixed.

@felix642
Copy link
Contributor Author

Code is good, just documentation can be updated (std::errc is always ignored) and formatting should be fixed.

Should we explicitly mention std::errc or should we change the check description for something like:

Detects default initialization (to 0) of variables with enum type where the enum has no enumerator with value 0, and where such initialization is not defined as valid by the C++ standard.

This way we won't have to change the check description if we add other enumerators in the future.

@balazske
Copy link
Collaborator

balazske commented Sep 23, 2025

This way we won't have to change the check description if we add other enumerators in the future.

I think it is more accurate to have the exact list of ignored enums. Probably (but I am not familiar with the standard) this kind of initialization is always valid (no undefined behavior), just it may be confusing and probably indication of a coding error.

@felix642 felix642 requested a review from vbvictor September 25, 2025 16:14
@felix642 felix642 requested a review from vbvictor September 25, 2025 16:33
Copy link
Contributor

@vbvictor vbvictor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Added a new Option IgnoredEnums to bugprone invalid enum
default initialization to limit the scope of the analysis.
This is needed to remove warnings on enums like std::errc
where the enum doesn't define a value of 0, but is still
used to check if some function calls like std::from_chars
are executed correctly.
@felix642 felix642 force-pushed the new-check-option-Ignored-enum branch from 10f5dd2 to 1c05947 Compare September 30, 2025 13:53
@felix642
Copy link
Contributor Author

@vbvictor or @balazske, I just rebased my branch on top of main. I think we are ready to merge this PR, but I do not have write access to the llvm project. If you could merge this PR for me I would greatly appreciate it.

@vbvictor
Copy link
Contributor

Please make your email public - https://llvm.org/docs/DeveloperPolicy.html#email-addresses

@felix642
Copy link
Contributor Author

Thank you for the info. I don't know why this setting was changed. Could you please confirm that everything looks good now or if my email still appears as private ?

@vbvictor
Copy link
Contributor

Thank you for the info. I don't know why this setting was changed. Could you please confirm that everything looks good now or if my email still appears as private ?

Everything is fine, thanks.

@vbvictor vbvictor merged commit 042540a into llvm:main Sep 30, 2025
11 checks passed
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Oct 3, 2025
)

Added a new Option IgnoredEnums to bugprone invalid enum default
initialization to limit the scope of the analysis. This is needed to
remove warnings on enums like std::errc where the enum doesn't define a
value of 0, but is still used to check if some function calls like
std::from_chars are executed correctly.

The C++ Standard section 22.13.2 mentions the following : "[...] If the
member ec of the return value is such that the value is equal to the
value of a value-initialized errc, the conversion was successful [...]"

This means that a call to `std::errc{}` is clearly defined by the
standard and should not raise any warning under this check.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants