#include "ProTypeMemberInitCheck.h"
#include "../utils/LexerUtils.h"
+#include "../utils/Matchers.h"
+#include "../utils/TypeTraits.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/SmallPtrSet.h"
using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
using llvm::SmallPtrSet;
using llvm::SmallPtrSetImpl;
namespace {
-AST_MATCHER(CXXConstructorDecl, isUserProvided) {
- return Node.isUserProvided();
-}
-
-static void
-fieldsRequiringInit(const RecordDecl::field_range &Fields,
- SmallPtrSetImpl<const FieldDecl *> &FieldsToInit) {
+void fieldsRequiringInit(const RecordDecl::field_range &Fields,
+ ASTContext &Context,
+ SmallPtrSetImpl<const FieldDecl *> &FieldsToInit) {
for (const FieldDecl *F : Fields) {
QualType Type = F->getType();
- if (Type->isPointerType() || Type->isBuiltinType())
+ if (!F->hasInClassInitializer() &&
+ type_traits::isTriviallyDefaultConstructible(Type, Context))
FieldsToInit.insert(F);
}
}
FieldDecls.erase(Match.getNodeAs<FieldDecl>("fieldDecl"));
}
-// Creates comma separated list of fields requiring initialization in order of
+StringRef getName(const FieldDecl *Field) { return Field->getName(); }
+
+StringRef getName(const RecordDecl *Record) {
+ // Get the typedef name if this is a C-style anonymous struct and typedef.
+ if (const TypedefNameDecl *Typedef = Record->getTypedefNameForAnonDecl())
+ return Typedef->getName();
+ return Record->getName();
+}
+
+// Creates comma separated list of decls requiring initialization in order of
// declaration.
-std::string toCommaSeparatedString(
- const RecordDecl::field_range &FieldRange,
- const SmallPtrSetImpl<const FieldDecl *> &FieldsRequiringInit) {
- std::string List;
- llvm::raw_string_ostream Stream(List);
- size_t AddedFields = 0;
- for (const FieldDecl *Field : FieldRange) {
- if (FieldsRequiringInit.count(Field) > 0) {
- Stream << Field->getName();
- if (++AddedFields < FieldsRequiringInit.size())
- Stream << ", ";
- }
+template <typename R, typename T>
+std::string
+toCommaSeparatedString(const R &OrderedDecls,
+ const SmallPtrSetImpl<const T *> &DeclsToInit) {
+ SmallVector<StringRef, 16> Names;
+ for (const T *Decl : OrderedDecls) {
+ if (DeclsToInit.count(Decl))
+ Names.emplace_back(getName(Decl));
}
- return Stream.str();
+ return llvm::join(Names.begin(), Names.end(), ", ");
}
-// Contains all fields in correct order that need to be inserted at the same
-// location for pre C++11.
-// There are 3 kinds of insertions:
-// 1. The fields are inserted after an existing CXXCtorInitializer stored in
-// InitializerBefore. This will be the case whenever there is a written
-// initializer before the fields available.
-// 2. The fields are inserted before the first existing initializer stored in
-// InitializerAfter.
-// 3. There are no written initializers and the fields will be inserted before
-// the constructor's body creating a new initializer list including the ':'.
-struct FieldsInsertion {
- const CXXCtorInitializer *InitializerBefore;
- const CXXCtorInitializer *InitializerAfter;
- SmallVector<const FieldDecl *, 4> Fields;
+SourceLocation getLocationForEndOfToken(const ASTContext &Context,
+ SourceLocation Location) {
+ return Lexer::getLocForEndOfToken(Location, 0, Context.getSourceManager(),
+ Context.getLangOpts());
+};
+
+// There are 3 kinds of insertion placements:
+enum class InitializerPlacement {
+ // 1. The fields are inserted after an existing CXXCtorInitializer stored in
+ // Where. This will be the case whenever there is a written initializer before
+ // the fields available.
+ After,
+
+ // 2. The fields are inserted before the first existing initializer stored in
+ // Where.
+ Before,
+
+ // 3. There are no written initializers and the fields will be inserted before
+ // the constructor's body creating a new initializer list including the ':'.
+ New
+};
+
+// An InitializerInsertion contains a list of fields and/or base classes to
+// insert into the initializer list of a constructor. We use this to ensure
+// proper absolute ordering according to the class declaration relative to the
+// (perhaps improper) ordering in the existing initializer list, if any.
+struct IntializerInsertion {
+ IntializerInsertion(InitializerPlacement Placement,
+ const CXXCtorInitializer *Where)
+ : Placement(Placement), Where(Where) {}
SourceLocation getLocation(const ASTContext &Context,
const CXXConstructorDecl &Constructor) const {
- if (InitializerBefore != nullptr) {
- return Lexer::getLocForEndOfToken(InitializerBefore->getRParenLoc(), 0,
- Context.getSourceManager(),
- Context.getLangOpts());
+ assert((Where != nullptr || Placement == InitializerPlacement::New) &&
+ "Location should be relative to an existing initializer or this "
+ "insertion represents a new initializer list.");
+ SourceLocation Location;
+ switch (Placement) {
+ case InitializerPlacement::New:
+ Location = lexer_utils::getPreviousNonCommentToken(
+ Context, Constructor.getBody()->getLocStart())
+ .getLocation();
+ break;
+ case InitializerPlacement::Before:
+ Location = lexer_utils::getPreviousNonCommentToken(
+ Context, Where->getSourceRange().getBegin())
+ .getLocation();
+ break;
+ case InitializerPlacement::After:
+ Location = Where->getRParenLoc();
+ break;
}
- auto StartLocation = InitializerAfter != nullptr
- ? InitializerAfter->getSourceRange().getBegin()
- : Constructor.getBody()->getLocStart();
- auto Token =
- lexer_utils::getPreviousNonCommentToken(Context, StartLocation);
- return Lexer::getLocForEndOfToken(Token.getLocation(), 0,
- Context.getSourceManager(),
- Context.getLangOpts());
+ return getLocationForEndOfToken(Context, Location);
}
std::string codeToInsert() const {
- assert(!Fields.empty() && "No fields to insert");
+ assert(!Initializers.empty() && "No initializers to insert");
std::string Code;
llvm::raw_string_ostream Stream(Code);
- // Code will be inserted before the first written initializer after ':',
- // append commas.
- if (InitializerAfter != nullptr) {
- for (const auto *Field : Fields)
- Stream << " " << Field->getName() << "(),";
- } else {
- // The full initializer list is created, add extra space after
- // constructor's rparens.
- if (InitializerBefore == nullptr)
- Stream << " ";
- for (const auto *Field : Fields)
- Stream << ", " << Field->getName() << "()";
+ std::string joined =
+ llvm::join(Initializers.begin(), Initializers.end(), "(), ");
+ switch (Placement) {
+ case InitializerPlacement::New:
+ Stream << " : " << joined << "()";
+ break;
+ case InitializerPlacement::Before:
+ Stream << " " << joined << "(),";
+ break;
+ case InitializerPlacement::After:
+ Stream << ", " << joined << "()";
+ break;
}
- Stream.flush();
- // The initializer list is created, replace leading comma with colon.
- if (InitializerBefore == nullptr && InitializerAfter == nullptr)
- Code[1] = ':';
return Code;
}
+
+ InitializerPlacement Placement;
+ const CXXCtorInitializer *Where;
+ SmallVector<std::string, 4> Initializers;
};
-SmallVector<FieldsInsertion, 16> computeInsertions(
- const CXXConstructorDecl::init_const_range &Inits,
- const RecordDecl::field_range &Fields,
- const SmallPtrSetImpl<const FieldDecl *> &FieldsRequiringInit) {
- // Find last written non-member initializer or null.
- const CXXCtorInitializer *LastWrittenNonMemberInit = nullptr;
- for (const CXXCtorInitializer *Init : Inits) {
- if (Init->isWritten() && !Init->isMemberInitializer())
- LastWrittenNonMemberInit = Init;
- }
- SmallVector<FieldsInsertion, 16> OrderedFields;
- OrderedFields.push_back({LastWrittenNonMemberInit, nullptr, {}});
+// Convenience utility to get a RecordDecl from a QualType.
+const RecordDecl *getCanonicalRecordDecl(const QualType &Type) {
+ if (const auto *RT = Type.getCanonicalType()->getAs<RecordType>())
+ return RT->getDecl();
+ return nullptr;
+}
+
+template <typename R, typename T>
+SmallVector<IntializerInsertion, 16>
+computeInsertions(const CXXConstructorDecl::init_const_range &Inits,
+ const R &OrderedDecls,
+ const SmallPtrSetImpl<const T *> &DeclsToInit) {
+ SmallVector<IntializerInsertion, 16> Insertions;
+ Insertions.emplace_back(InitializerPlacement::New, nullptr);
- auto CurrentField = Fields.begin();
+ typename R::const_iterator Decl = std::begin(OrderedDecls);
for (const CXXCtorInitializer *Init : Inits) {
- if (Init->isWritten() && Init->isMemberInitializer()) {
- const FieldDecl *MemberField = Init->getMember();
- // Add all fields between current field and this member field the previous
- // FieldsInsertion if the field requires initialization.
- for (; CurrentField != Fields.end() && *CurrentField != MemberField;
- ++CurrentField) {
- if (FieldsRequiringInit.count(*CurrentField) > 0)
- OrderedFields.back().Fields.push_back(*CurrentField);
+ if (Init->isWritten()) {
+ if (Insertions.size() == 1)
+ Insertions.emplace_back(InitializerPlacement::Before, Init);
+
+ // Gets either the field or base class being initialized by the provided
+ // initializer.
+ const auto *InitDecl =
+ Init->isMemberInitializer()
+ ? static_cast<const NamedDecl *>(Init->getMember())
+ : Init->getBaseClass()->getAs<RecordType>()->getDecl();
+
+ // Add all fields between current field up until the next intializer.
+ for (; Decl != std::end(OrderedDecls) && *Decl != InitDecl; ++Decl) {
+ if (const T *D = dyn_cast<T>(*Decl)) {
+ if (DeclsToInit.count(D) > 0)
+ Insertions.back().Initializers.emplace_back(getName(D));
+ }
}
- // If this is the first written member initializer and there was no
- // written non-member initializer set this initializer as
- // InitializerAfter.
- if (OrderedFields.size() == 1 &&
- OrderedFields.back().InitializerBefore == nullptr)
- OrderedFields.back().InitializerAfter = Init;
- OrderedFields.push_back({Init, nullptr, {}});
+
+ Insertions.emplace_back(InitializerPlacement::After, Init);
}
}
- // Add remaining fields that require initialization to last FieldsInsertion.
- for (; CurrentField != Fields.end(); ++CurrentField) {
- if (FieldsRequiringInit.count(*CurrentField) > 0)
- OrderedFields.back().Fields.push_back(*CurrentField);
+
+ // Add remaining decls that require initialization.
+ for (; Decl != std::end(OrderedDecls); ++Decl) {
+ if (const T *D = dyn_cast<T>(*Decl)) {
+ if (DeclsToInit.count(D) > 0)
+ Insertions.back().Initializers.emplace_back(getName(D));
+ }
+ }
+ return Insertions;
+}
+
+// Gets the list of bases and members that could possibly be initialized, in
+// order as they appear in the class declaration.
+void getInitializationsInOrder(const CXXRecordDecl *ClassDecl,
+ SmallVectorImpl<const NamedDecl *> &Decls) {
+ Decls.clear();
+ for (const auto &Base : ClassDecl->bases())
+ Decls.emplace_back(getCanonicalRecordDecl(Base.getType()));
+ Decls.append(ClassDecl->fields().begin(), ClassDecl->fields().end());
+}
+
+template <typename T>
+void fixInitializerList(const ASTContext &Context, DiagnosticBuilder &Diag,
+ const CXXConstructorDecl *Ctor,
+ const SmallPtrSetImpl<const T *> &DeclsToInit) {
+ // Do not propose fixes in macros since we cannot place them correctly.
+ if (Ctor->getLocStart().isMacroID())
+ return;
+
+ SmallVector<const NamedDecl *, 16> OrderedDecls;
+ getInitializationsInOrder(Ctor->getParent(), OrderedDecls);
+
+ for (const auto &Insertion :
+ computeInsertions(Ctor->inits(), OrderedDecls, DeclsToInit)) {
+ if (!Insertion.Initializers.empty())
+ Diag << FixItHint::CreateInsertion(Insertion.getLocation(Context, *Ctor),
+ Insertion.codeToInsert());
+ }
+}
+
+template <typename T, typename Func>
+void forEachField(const RecordDecl *Record, const T &Fields,
+ bool OneFieldPerUnion, Func &&Fn) {
+ for (const FieldDecl *F : Fields) {
+ if (F->isAnonymousStructOrUnion()) {
+ if (const RecordDecl *R = getCanonicalRecordDecl(F->getType()))
+ forEachField(R, R->fields(), OneFieldPerUnion, Fn);
+ } else {
+ Fn(F);
+ }
+
+ if (OneFieldPerUnion && Record->isUnion())
+ break;
}
- return OrderedFields;
}
} // namespace
+ProTypeMemberInitCheck::ProTypeMemberInitCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoreArrays(Options.get("IgnoreArrays", false)) {}
+
void ProTypeMemberInitCheck::registerMatchers(MatchFinder *Finder) {
- Finder->addMatcher(cxxConstructorDecl(isDefinition(), isUserProvided(),
- unless(isInstantiated()))
- .bind("ctor"),
- this);
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ auto IsUserProvidedNonDelegatingConstructor =
+ allOf(isUserProvided(),
+ unless(anyOf(isInstantiated(), isDelegatingConstructor())));
+ auto IsNonTrivialDefaultConstructor = allOf(
+ isDefaultConstructor(), unless(isUserProvided()),
+ hasParent(cxxRecordDecl(unless(isTriviallyDefaultConstructible()))));
+ Finder->addMatcher(
+ cxxConstructorDecl(isDefinition(),
+ anyOf(IsUserProvidedNonDelegatingConstructor,
+ IsNonTrivialDefaultConstructor))
+ .bind("ctor"),
+ this);
+ auto HasDefaultConstructor = hasInitializer(
+ cxxConstructExpr(unless(requiresZeroInitialization()),
+ hasDeclaration(cxxConstructorDecl(
+ isDefaultConstructor(), unless(isUserProvided())))));
+ Finder->addMatcher(
+ varDecl(isDefinition(), HasDefaultConstructor,
+ hasType(recordDecl(has(fieldDecl()),
+ isTriviallyDefaultConstructible())))
+ .bind("var"),
+ this);
}
void ProTypeMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
- const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
- const auto &MemberFields = Ctor->getParent()->fields();
+ if (const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor")) {
+ checkMissingMemberInitializer(*Result.Context, Ctor);
+ checkMissingBaseClassInitializer(*Result.Context, Ctor);
+ } else if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var")) {
+ checkUninitializedTrivialType(*Result.Context, Var);
+ }
+}
+
+void ProTypeMemberInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IgnoreArrays", IgnoreArrays);
+}
+
+void ProTypeMemberInitCheck::checkMissingMemberInitializer(
+ ASTContext &Context, const CXXConstructorDecl *Ctor) {
+ const CXXRecordDecl *ClassDecl = Ctor->getParent();
+ bool IsUnion = ClassDecl->isUnion();
+
+ if (IsUnion && ClassDecl->hasInClassInitializer())
+ return;
// Skip declarations delayed by late template parsing without a body.
const Stmt *Body = Ctor->getBody();
return;
SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
- fieldsRequiringInit(MemberFields, FieldsToInit);
+ fieldsRequiringInit(ClassDecl->fields(), Context, FieldsToInit);
if (FieldsToInit.empty())
return;
- for (CXXCtorInitializer *Init : Ctor->inits()) {
- // Return early if this constructor simply delegates to another constructor
- // in the same class.
- if (Init->isDelegatingInitializer())
- return;
- if (!Init->isMemberInitializer())
- continue;
- FieldsToInit.erase(Init->getMember());
+ for (const CXXCtorInitializer *Init : Ctor->inits()) {
+ // Remove any fields that were explicitly written in the initializer list
+ // or in-class.
+ if (Init->isAnyMemberInitializer() && Init->isWritten()) {
+ if (IsUnion)
+ return; // We can only initialize one member of a union.
+ FieldsToInit.erase(Init->getMember());
+ }
}
- removeFieldsInitializedInBody(*Body, *Result.Context, FieldsToInit);
+ removeFieldsInitializedInBody(*Body, Context, FieldsToInit);
- if (FieldsToInit.empty())
+ // Collect all fields in order, both direct fields and indirect fields from
+ // anonmyous record types.
+ SmallVector<const FieldDecl *, 16> OrderedFields;
+ forEachField(ClassDecl, ClassDecl->fields(), false,
+ [&](const FieldDecl *F) { OrderedFields.push_back(F); });
+
+ // Collect all the fields we need to initialize, including indirect fields.
+ SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
+ forEachField(ClassDecl, FieldsToInit, false,
+ [&](const FieldDecl *F) { AllFieldsToInit.insert(F); });
+ if (AllFieldsToInit.empty())
return;
DiagnosticBuilder Diag =
diag(Ctor->getLocStart(),
- "constructor does not initialize these built-in/pointer fields: %0")
- << toCommaSeparatedString(MemberFields, FieldsToInit);
+ IsUnion
+ ? "union constructor should initialize one of these fields: %0"
+ : "constructor does not initialize these fields: %0")
+ << toCommaSeparatedString(OrderedFields, AllFieldsToInit);
+
// Do not propose fixes in macros since we cannot place them correctly.
if (Ctor->getLocStart().isMacroID())
return;
- // For C+11 use in-class initialization which covers all future constructors
- // as well.
- if (Result.Context->getLangOpts().CPlusPlus11) {
- for (const auto *Field : FieldsToInit) {
+
+ // Collect all fields but only suggest a fix for the first member of unions,
+ // as initializing more than one union member is an error.
+ SmallPtrSet<const FieldDecl *, 16> FieldsToFix;
+ forEachField(ClassDecl, FieldsToInit, true, [&](const FieldDecl *F) {
+ // Don't suggest fixes for enums because we don't know a good default.
+ if (!F->getType()->isEnumeralType())
+ FieldsToFix.insert(F);
+ });
+ if (FieldsToFix.empty())
+ return;
+
+ // Use in-class initialization if possible.
+ if (Context.getLangOpts().CPlusPlus11) {
+ for (const FieldDecl *Field : FieldsToFix) {
Diag << FixItHint::CreateInsertion(
- Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
- Result.Context->getSourceManager(),
- Result.Context->getLangOpts()),
+ getLocationForEndOfToken(Context, Field->getSourceRange().getEnd()),
"{}");
}
- return;
+ } else {
+ // Otherwise, rewrite the constructor's initializer list.
+ fixInitializerList(Context, Diag, Ctor, FieldsToFix);
}
- for (const auto &FieldsInsertion :
- computeInsertions(Ctor->inits(), MemberFields, FieldsToInit)) {
- if (!FieldsInsertion.Fields.empty())
- Diag << FixItHint::CreateInsertion(
- FieldsInsertion.getLocation(*Result.Context, *Ctor),
- FieldsInsertion.codeToInsert());
+}
+
+void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
+ const ASTContext &Context, const CXXConstructorDecl *Ctor) {
+ const CXXRecordDecl *ClassDecl = Ctor->getParent();
+
+ // Gather any base classes that need to be initialized.
+ SmallVector<const RecordDecl *, 4> AllBases;
+ SmallPtrSet<const RecordDecl *, 4> BasesToInit;
+ for (const CXXBaseSpecifier &Base : ClassDecl->bases()) {
+ if (const auto *BaseClassDecl = getCanonicalRecordDecl(Base.getType())) {
+ AllBases.emplace_back(BaseClassDecl);
+ if (!BaseClassDecl->field_empty() &&
+ type_traits::isTriviallyDefaultConstructible(Base.getType(), Context))
+ BasesToInit.insert(BaseClassDecl);
+ }
}
+
+ if (BasesToInit.empty())
+ return;
+
+ // Remove any bases that were explicitly written in the initializer list.
+ for (const CXXCtorInitializer *Init : Ctor->inits()) {
+ if (Init->isBaseInitializer() && Init->isWritten())
+ BasesToInit.erase(Init->getBaseClass()->getAs<RecordType>()->getDecl());
+ }
+
+ if (BasesToInit.empty())
+ return;
+
+ DiagnosticBuilder Diag =
+ diag(Ctor->getLocStart(),
+ "constructor does not initialize these bases: %0")
+ << toCommaSeparatedString(AllBases, BasesToInit);
+
+ fixInitializerList(Context, Diag, Ctor, BasesToInit);
+}
+
+void ProTypeMemberInitCheck::checkUninitializedTrivialType(
+ const ASTContext &Context, const VarDecl *Var) {
+ DiagnosticBuilder Diag =
+ diag(Var->getLocStart(), "uninitialized record type: %0") << Var;
+
+ Diag << FixItHint::CreateInsertion(
+ getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()),
+ Context.getLangOpts().CPlusPlus11 ? "{}" : " = {}");
}
} // namespace cppcoreguidelines
int F;
// CHECK-FIXES: int F{};
PositiveFieldBeforeConstructor() {}
- // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: F
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
// CHECK-FIXES: PositiveFieldBeforeConstructor() {}
};
struct PositiveFieldAfterConstructor {
PositiveFieldAfterConstructor() {}
- // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: F, G
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G
// CHECK-FIXES: PositiveFieldAfterConstructor() {}
int F;
// CHECK-FIXES: int F{};
};
PositiveSeparateDefinition::PositiveSeparateDefinition() {}
-// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these built-in/pointer fields: F
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
// CHECK-FIXES: PositiveSeparateDefinition::PositiveSeparateDefinition() {}
struct PositiveMixedFieldOrder {
PositiveMixedFieldOrder() : J(0) {}
- // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: I, K
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: I, K
// CHECK-FIXES: PositiveMixedFieldOrder() : J(0) {}
int I;
// CHECK-FIXES: int I{};
template <typename T>
struct Template {
Template() {}
- // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: F
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
int F;
// CHECK-FIXES: int F{};
T T1;
};
NegativeFieldInitializedInDefinition::NegativeFieldInitializedInDefinition() : F() {}
-
struct NegativeInClassInitialized {
int F = 0;
};
#define UNINITIALIZED_FIELD_IN_MACRO_BODY(FIELD) \
- struct UninitializedField##FIELD { \
- UninitializedField##FIELD() {} \
- int FIELD; \
- }; \
+ struct UninitializedField##FIELD { \
+ UninitializedField##FIELD() {} \
+ int FIELD; \
+ }; \
// Ensure FIELD is not initialized since fixes inside of macros are disabled.
// CHECK-FIXES: int FIELD;
UNINITIALIZED_FIELD_IN_MACRO_BODY(F);
-// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these built-in/pointer fields: F
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
UNINITIALIZED_FIELD_IN_MACRO_BODY(G);
-// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these built-in/pointer fields: G
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: G
#define UNINITIALIZED_FIELD_IN_MACRO_ARGUMENT(ARGUMENT) \
- ARGUMENT \
+ ARGUMENT
UNINITIALIZED_FIELD_IN_MACRO_ARGUMENT(struct UninitializedFieldInMacroArg {
UninitializedFieldInMacroArg() {}
int Field;
});
-// CHECK-MESSAGES: :[[@LINE-3]]:3: warning: constructor does not initialize these built-in/pointer fields: Field
+// CHECK-MESSAGES: :[[@LINE-3]]:3: warning: constructor does not initialize these fields: Field
// Ensure FIELD is not initialized since fixes inside of macros are disabled.
// CHECK-FIXES: int Field;
+
+struct NegativeAggregateType {
+ int X;
+ int Y;
+ int Z;
+};
+
+struct PositiveTrivialType {
+ PositiveTrivialType() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+
+ NegativeAggregateType F;
+ // CHECK-FIXES: NegativeAggregateType F{};
+};
+
+struct NegativeNonTrivialType {
+ PositiveTrivialType F;
+};
+
+static void PositiveUninitializedTrivialType() {
+ NegativeAggregateType X;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: uninitialized record type: 'X'
+ // CHECK-FIXES: NegativeAggregateType X{};
+
+ NegativeAggregateType A[10]; // Don't warn because this isn't an object type.
+}
+
+static void NegativeInitializedTrivialType() {
+ NegativeAggregateType X{};
+ NegativeAggregateType Y = {};
+ NegativeAggregateType Z = NegativeAggregateType();
+ NegativeAggregateType A[10]{};
+ NegativeAggregateType B[10] = {};
+ int C; // No need to initialize this because we don't have a constructor.
+ int D[8];
+ NegativeAggregateType E = {0, 1, 2};
+ NegativeAggregateType F({});
+}
+
+struct NonTrivialType {
+ NonTrivialType() = default;
+ NonTrivialType(const NonTrivialType &RHS) : X(RHS.X), Y(RHS.Y) {}
+
+ int X;
+ int Y;
+};
+
+static void PositiveNonTrivialTypeWithCopyConstructor() {
+ NonTrivialType T;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: uninitialized record type: 'T'
+ // CHECK-FIXES: NonTrivialType T{};
+
+ NonTrivialType A[8];
+ // Don't warn because this isn't an object type
+}
+
+struct ComplexNonTrivialType {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constructor does not initialize these fields: Y
+ NegativeFieldInitialized X;
+ int Y;
+ // CHECK-FIXES: int Y{};
+};
+
+static void PositiveComplexNonTrivialType() {
+ ComplexNonTrivialType T;
+}
+
+struct PositiveStaticMember {
+ static NonTrivialType X;
+ static NonTrivialType Y;
+ static constexpr NonTrivialType Z{};
+};
+
+NonTrivialType PositiveStaticMember::X;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: uninitialized record type: 'X'
+// CHECK-FIXES: NonTrivialType PositiveStaticMember::X{};
+
+NonTrivialType PositiveStaticMember::Y{};
+
+struct PositiveMultipleConstructors {
+ PositiveMultipleConstructors() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B
+
+ PositiveMultipleConstructors(int) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B
+
+ PositiveMultipleConstructors(const PositiveMultipleConstructors &) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B
+
+ // FIXME: The fix-its here collide providing an erroneous fix
+ int A, B;
+ // CHECK-FIXES: int A{}{}{}, B{}{}{};
+};
+
+typedef struct {
+ int Member;
+} CStyleStruct;
+
+struct PositiveUninitializedBase : public NegativeAggregateType, CStyleStruct {
+ PositiveUninitializedBase() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType, CStyleStruct
+ // CHECK-FIXES: PositiveUninitializedBase() : NegativeAggregateType(), CStyleStruct() {}
+};
+
+struct PositiveUninitializedBaseOrdering : public NegativeAggregateType,
+ public NonTrivialType {
+ PositiveUninitializedBaseOrdering() : B() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType, NonTrivialType
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: constructor does not initialize these fields: A
+ // CHECK-FIXES: PositiveUninitializedBaseOrdering() : NegativeAggregateType(), NonTrivialType(), B() {}
+
+ // This is somewhat pathological with the base class initializer at the end...
+ PositiveUninitializedBaseOrdering(int) : B(), NonTrivialType(), A() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType
+ // CHECK-FIXES: PositiveUninitializedBaseOrdering(int) : B(), NegativeAggregateType(), NonTrivialType(), A() {}
+
+ PositiveUninitializedBaseOrdering(float) : B(), NegativeAggregateType(), A() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NonTrivialType
+ // CHECK-FIXES: PositiveUninitializedBaseOrdering(float) : B(), NegativeAggregateType(), NonTrivialType(), A() {}
+
+ int A, B;
+ // CHECK-FIXES: int A{}, B;
+};
+
+// We shouldn't need to initialize anything because PositiveUninitializedBase
+// has a user-defined constructor.
+struct NegativeUninitializedBase : public PositiveUninitializedBase {
+ NegativeUninitializedBase() {}
+};
+
+struct InheritedAggregate : public NegativeAggregateType {
+ int F;
+};
+
+static InheritedAggregate PositiveGlobal;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: uninitialized record type: 'PositiveGlobal'
+// CHECK-FIXES: InheritedAggregate PositiveGlobal{};
+
+enum TestEnum {
+ A,
+ B,
+ C
+};
+
+enum class TestScopedEnum {
+ A,
+ B,
+ C
+};
+
+struct PositiveEnumType {
+ PositiveEnumType() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: X, Y
+ // No proposed fixes, as we don't know whether value initialization for these
+ // enums really makes sense.
+
+ TestEnum X;
+ TestScopedEnum Y;
+};
+
+extern "C" {
+struct NegativeCStruct {
+ int X, Y, Z;
+};
+
+static void PositiveCStructVariable() {
+ NegativeCStruct X;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: uninitialized record type: 'X'
+ // CHECK-FIXES: NegativeCStruct X{};
+}
+}
+
+union NegativeUnionInClass {
+ NegativeUnionInClass() {} // No message as a union can only initialize one member.
+ int X = 0;
+ float Y;
+};
+
+union PositiveUnion {
+ PositiveUnion() : X() {} // No message as a union can only initialize one member.
+ PositiveUnion(int) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: union constructor should initialize one of these fields: X, Y
+
+ int X;
+ // CHECK-FIXES: int X{};
+
+ // Make sure we don't give Y an initializer.
+ float Y;
+ // CHECK-FIXES-NOT: float Y{};
+};
+
+struct PositiveAnonymousUnionAndStruct {
+ PositiveAnonymousUnionAndStruct() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B, Y, Z, C, D, E, F, X
+
+ union {
+ int A;
+ // CHECK-FIXES: int A{};
+ short B;
+ };
+
+ struct {
+ int Y;
+ // CHECK-FIXES: int Y{};
+ char *Z;
+ // CHECK-FIXES: char *Z{};
+
+ struct {
+ short C;
+ // CHECK-FIXES: short C{};
+ double D;
+ // CHECK-FIXES: double D{};
+ };
+
+ union {
+ long E;
+ // CHECK-FIXES: long E{};
+ float F;
+ };
+ };
+ int X;
+ // CHECK-FIXES: int X{};
+};