Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -88,12 +90,24 @@ 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", ""))) {
IgnoredEnums.emplace_back("::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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
std::vector<StringRef> IgnoredEnums;
};

} // namespace clang::tidy::bugprone
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,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 specified 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ The check emits a warning only if an ``enum`` variable is default-initialized
value of 0. The type can be a scoped or non-scoped ``enum``. Unions are not
handled by the check (if it contains a member of enumeration type).

Note that the ``enum`` ``std::errc`` is always ignored because it is expected to
be default initialized, despite not defining an enumerator with the value 0.

.. code-block:: c++

enum class Enum1: int {
Expand Down Expand Up @@ -70,3 +73,12 @@ 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


Options
-------

.. option:: IgnoredEnums

Semicolon-separated list of regexes specifying enums for which this check won't be
enforced. Default is `::std::errc`.
Original file line number Diff line number Diff line change
@@ -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-or-later %s bugprone-invalid-enum-default-initialization %t
// 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'}}"

enum class Enum0: int {
A = 0,
Expand All @@ -24,66 +25,66 @@ 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};
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;

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}
{}
};
Expand All @@ -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;

Expand All @@ -139,7 +140,25 @@ 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

namespace std {
enum errc {
A = 1,
B
};
}

std::errc err{};