Skip to content

Commit 042540a

Browse files
authored
[clang-tidy] New Option Invalid Enum Default Initialization (#159220)
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.
1 parent ccd06e4 commit 042540a

File tree

5 files changed

+79
-26
lines changed

5 files changed

+79
-26
lines changed

clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "InvalidEnumDefaultInitializationCheck.h"
10+
#include "../utils/Matchers.h"
11+
#include "../utils/OptionsUtils.h"
1012
#include "clang/AST/ASTContext.h"
1113
#include "clang/AST/TypeVisitor.h"
1214
#include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -88,12 +90,24 @@ class FindEnumMember : public TypeVisitor<FindEnumMember, bool> {
8890

8991
InvalidEnumDefaultInitializationCheck::InvalidEnumDefaultInitializationCheck(
9092
StringRef Name, ClangTidyContext *Context)
91-
: ClangTidyCheck(Name, Context) {}
93+
: ClangTidyCheck(Name, Context),
94+
IgnoredEnums(
95+
utils::options::parseStringList(Options.get("IgnoredEnums", ""))) {
96+
IgnoredEnums.emplace_back("::std::errc");
97+
}
98+
99+
void InvalidEnumDefaultInitializationCheck::storeOptions(
100+
ClangTidyOptions::OptionMap &Opts) {
101+
Options.store(Opts, "IgnoredEnums",
102+
utils::options::serializeStringList(IgnoredEnums));
103+
}
92104

93105
void InvalidEnumDefaultInitializationCheck::registerMatchers(
94106
MatchFinder *Finder) {
95-
auto EnumWithoutZeroValue = enumType(
96-
hasDeclaration(enumDecl(isCompleteAndHasNoZeroValue()).bind("enum")));
107+
auto EnumWithoutZeroValue = enumType(hasDeclaration(
108+
enumDecl(isCompleteAndHasNoZeroValue(),
109+
unless(matchers::matchesAnyListedName(IgnoredEnums)))
110+
.bind("enum")));
97111
auto EnumOrArrayOfEnum = qualType(hasUnqualifiedDesugaredType(
98112
anyOf(EnumWithoutZeroValue,
99113
arrayType(hasElementType(qualType(

clang-tools-extra/clang-tidy/bugprone/InvalidEnumDefaultInitializationCheck.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ class InvalidEnumDefaultInitializationCheck : public ClangTidyCheck {
2424
ClangTidyContext *Context);
2525
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
2626
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
27+
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
28+
29+
private:
30+
std::vector<StringRef> IgnoredEnums;
2731
};
2832

2933
} // namespace clang::tidy::bugprone

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ Changes in existing checks
253253
<clang-tidy/checks/bugprone/infinite-loop>` check by adding detection for
254254
variables introduced by structured bindings.
255255

256+
- Improved :doc:`bugprone-invalid-enum-default-initialization
257+
<clang-tidy/checks/bugprone/invalid-enum-default-initialization>` with new
258+
`IgnoredEnums` option to ignore specified enums during analysis.
259+
256260
- Improved :doc:`bugprone-narrowing-conversions
257261
<clang-tidy/checks/bugprone/narrowing-conversions>` check by fixing
258262
false positive from analysis of a conditional expression in C.

clang-tools-extra/docs/clang-tidy/checks/bugprone/invalid-enum-default-initialization.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ The check emits a warning only if an ``enum`` variable is default-initialized
1919
value of 0. The type can be a scoped or non-scoped ``enum``. Unions are not
2020
handled by the check (if it contains a member of enumeration type).
2121

22+
Note that the ``enum`` ``std::errc`` is always ignored because it is expected to
23+
be default initialized, despite not defining an enumerator with the value 0.
24+
2225
.. code-block:: c++
2326

2427
enum class Enum1: int {
@@ -70,3 +73,12 @@ enum type) are set to 0.
7073
enum Enum1 Array3[2][2] = {{Enum1_A, Enum1_A}}; // warn: elements of second array are initialized to 0
7174
7275
struct Struct1 S1 = {1}; // warn: element 'b' is initialized to 0
76+
77+
78+
Options
79+
-------
80+
81+
.. option:: IgnoredEnums
82+
83+
Semicolon-separated list of regexes specifying enums for which this check won't be
84+
enforced. Default is `::std::errc`.

clang-tools-extra/test/clang-tidy/checkers/bugprone/invalid-enum-default-initialization.cpp

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %check_clang_tidy -std=c++17 %s bugprone-invalid-enum-default-initialization %t
1+
// RUN: %check_clang_tidy -check-suffixes=,DEFAULT -std=c++17-or-later %s bugprone-invalid-enum-default-initialization %t
2+
// RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-invalid-enum-default-initialization %t -- -config="{CheckOptions: {bugprone-invalid-enum-default-initialization.IgnoredEnums: '::MyEnum'}}"
23

34
enum class Enum0: int {
45
A = 0,
@@ -24,66 +25,66 @@ Enum0 E0_6{Enum0::B};
2425

2526
Enum1 E1_1{};
2627
// 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
27-
// CHECK-NOTES: :8:12: note: enum is defined here
28+
// CHECK-NOTES: :9:12: note: enum is defined here
2829
Enum1 E1_2 = Enum1();
2930
// 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
30-
// CHECK-NOTES: :8:12: note: enum is defined here
31+
// CHECK-NOTES: :9:12: note: enum is defined here
3132
Enum1 E1_3;
3233
Enum1 E1_4{0};
3334
Enum1 E1_5{Enum1::A};
3435
Enum1 E1_6{Enum1::B};
3536

3637
Enum2 E2_1{};
3738
// 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
38-
// CHECK-NOTES: :13:6: note: enum is defined here
39+
// CHECK-NOTES: :14:6: note: enum is defined here
3940
Enum2 E2_2 = Enum2();
4041
// 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
41-
// CHECK-NOTES: :13:6: note: enum is defined here
42+
// CHECK-NOTES: :14:6: note: enum is defined here
4243

4344
void f1() {
4445
static Enum1 S; // FIMXE: warn for this?
4546
Enum1 A;
4647
Enum1 B = Enum1();
4748
// 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
48-
// CHECK-NOTES: :8:12: note: enum is defined here
49+
// CHECK-NOTES: :9:12: note: enum is defined here
4950
int C = int();
5051
}
5152

5253
void f2() {
5354
Enum1 A{};
5455
// 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
55-
// CHECK-NOTES: :8:12: note: enum is defined here
56+
// CHECK-NOTES: :9:12: note: enum is defined here
5657
Enum1 B = Enum1();
5758
// 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
58-
// CHECK-NOTES: :8:12: note: enum is defined here
59+
// CHECK-NOTES: :9:12: note: enum is defined here
5960
Enum1 C[5] = {{}};
6061
// 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
61-
// CHECK-NOTES: :8:12: note: enum is defined here
62+
// CHECK-NOTES: :9:12: note: enum is defined here
6263
// 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
63-
// CHECK-NOTES: :8:12: note: enum is defined here
64+
// CHECK-NOTES: :9:12: note: enum is defined here
6465
Enum1 D[5] = {}; // FIMXE: warn for this?
6566
// 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
66-
// CHECK-NOTES: :8:12: note: enum is defined here
67+
// CHECK-NOTES: :9:12: note: enum is defined here
6768
}
6869

6970
struct S1 {
7071
Enum1 E_1{};
7172
// 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
72-
// CHECK-NOTES: :8:12: note: enum is defined here
73+
// CHECK-NOTES: :9:12: note: enum is defined here
7374
Enum1 E_2 = Enum1();
7475
// 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
75-
// CHECK-NOTES: :8:12: note: enum is defined here
76+
// CHECK-NOTES: :9:12: note: enum is defined here
7677
Enum1 E_3;
7778
Enum1 E_4;
7879
Enum1 E_5;
7980

8081
S1() :
8182
E_3{},
8283
// 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
83-
// CHECK-NOTES: :8:12: note: enum is defined here
84+
// CHECK-NOTES: :9:12: note: enum is defined here
8485
E_4(),
8586
// 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
86-
// CHECK-NOTES: :8:12: note: enum is defined here
87+
// CHECK-NOTES: :9:12: note: enum is defined here
8788
E_5{Enum1::B}
8889
{}
8990
};
@@ -110,22 +111,22 @@ struct S5 {
110111

111112
S2 VarS2{};
112113
// CHECK-NOTES: :[[@LINE-1]]:9: warning: enum value of type 'Enum1' initialized with invalid value of 0
113-
// CHECK-NOTES: :8:12: note: enum is defined here
114+
// CHECK-NOTES: :9:12: note: enum is defined here
114115
// CHECK-NOTES: :[[@LINE-3]]:9: warning: enum value of type 'Enum2' initialized with invalid value of 0
115-
// CHECK-NOTES: :13:6: note: enum is defined here
116+
// CHECK-NOTES: :14:6: note: enum is defined here
116117
S3 VarS3{};
117118
// CHECK-NOTES: :[[@LINE-1]]:10: warning: enum value of type 'Enum1' initialized with invalid value of 0
118-
// CHECK-NOTES: :8:12: note: enum is defined here
119+
// CHECK-NOTES: :9:12: note: enum is defined here
119120
// CHECK-NOTES: :[[@LINE-3]]:10: warning: enum value of type 'Enum2' initialized with invalid value of 0
120-
// CHECK-NOTES: :13:6: note: enum is defined here
121+
// CHECK-NOTES: :14:6: note: enum is defined here
121122
S4 VarS4{};
122123
// CHECK-NOTES: :[[@LINE-1]]:10: warning: enum value of type 'Enum1' initialized with invalid value of 0
123-
// CHECK-NOTES: :8:12: note: enum is defined here
124+
// CHECK-NOTES: :9:12: note: enum is defined here
124125
// CHECK-NOTES: :[[@LINE-3]]:10: warning: enum value of type 'Enum2' initialized with invalid value of 0
125-
// CHECK-NOTES: :13:6: note: enum is defined here
126+
// CHECK-NOTES: :14:6: note: enum is defined here
126127
S5 VarS5{};
127128
// CHECK-NOTES: :[[@LINE-1]]:10: warning: enum value of type 'Enum1' initialized with invalid value of 0
128-
// CHECK-NOTES: :8:12: note: enum is defined here
129+
// CHECK-NOTES: :9:12: note: enum is defined here
129130

130131
enum class EnumFwd;
131132

@@ -139,7 +140,25 @@ template<typename T>
139140
struct Templ {
140141
T Mem1{};
141142
// CHECK-NOTES: :[[@LINE-1]]:9: warning: enum value of type 'Enum1' initialized with invalid value of 0
142-
// CHECK-NOTES: :8:12: note: enum is defined here
143+
// CHECK-NOTES: :9:12: note: enum is defined here
143144
};
144145

145146
Templ<Enum1> TemplVar;
147+
148+
enum MyEnum {
149+
A = 1,
150+
B
151+
};
152+
153+
MyEnum MyEnumVar{};
154+
// 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
155+
// CHECK-NOTES-DEFAULT: :148:6: note: enum is defined here
156+
157+
namespace std {
158+
enum errc {
159+
A = 1,
160+
B
161+
};
162+
}
163+
164+
std::errc err{};

0 commit comments

Comments
 (0)