10 #include "../utils/LexerUtils.h"
11 #include "../utils/Matchers.h"
12 #include "../utils/TypeTraits.h"
13 #include "clang/AST/ASTContext.h"
14 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 #include "clang/Lex/Lexer.h"
16 #include "llvm/ADT/SmallPtrSet.h"
20 using llvm::SmallPtrSet;
21 using llvm::SmallPtrSetImpl;
25 namespace cppcoreguidelines {
30 return Node.hasDefaultConstructor();
35 template <
typename T,
typename Func>
36 void forEachField(
const RecordDecl &
Record,
const T &Fields, Func &&Fn) {
37 for (
const FieldDecl *F : Fields) {
38 if (F->isAnonymousStructOrUnion()) {
39 if (
const CXXRecordDecl *R = F->getType()->getAsCXXRecordDecl())
40 forEachField(*R, R->fields(), Fn);
47 void removeFieldsInitializedInBody(
48 const Stmt &Stmt, ASTContext &Context,
49 SmallPtrSetImpl<const FieldDecl *> &FieldDecls) {
51 match(findAll(binaryOperator(
53 hasLHS(memberExpr(member(fieldDecl().bind(
"fieldDecl")))))),
55 for (
const auto &Match : Matches)
56 FieldDecls.erase(Match.getNodeAs<FieldDecl>(
"fieldDecl"));
59 StringRef getName(
const FieldDecl *Field) {
return Field->getName(); }
61 StringRef getName(
const RecordDecl *
Record) {
63 if (
const TypedefNameDecl *Typedef =
Record->getTypedefNameForAnonDecl())
70 template <
typename R,
typename T>
72 toCommaSeparatedString(
const R &OrderedDecls,
73 const SmallPtrSetImpl<const T *> &DeclsToInit) {
74 SmallVector<StringRef, 16> Names;
75 for (
const T *
Decl : OrderedDecls) {
76 if (DeclsToInit.count(
Decl))
77 Names.emplace_back(getName(
Decl));
79 return llvm::join(Names.begin(), Names.end(),
", ");
82 SourceLocation getLocationForEndOfToken(
const ASTContext &Context,
84 return Lexer::getLocForEndOfToken(
Location, 0, Context.getSourceManager(),
85 Context.getLangOpts());
108 struct IntializerInsertion {
109 IntializerInsertion(InitializerPlacement
Placement,
110 const CXXCtorInitializer *
Where)
113 SourceLocation getLocation(
const ASTContext &Context,
114 const CXXConstructorDecl &Constructor)
const {
115 assert((
Where !=
nullptr ||
Placement == InitializerPlacement::New) &&
116 "Location should be relative to an existing initializer or this "
117 "insertion represents a new initializer list.");
120 case InitializerPlacement::New:
122 Constructor.getBody()->getBeginLoc(),
123 Context.getSourceManager(), Context.getLangOpts())
126 case InitializerPlacement::Before:
128 Where->getSourceRange().getBegin(),
129 Context.getSourceManager(), Context.getLangOpts())
132 case InitializerPlacement::After:
136 return getLocationForEndOfToken(Context,
Location);
139 std::string codeToInsert()
const {
140 assert(!
Initializers.empty() &&
"No initializers to insert");
142 llvm::raw_string_ostream Stream(
Code);
146 case InitializerPlacement::New:
147 Stream <<
" : " << joined <<
"()";
149 case InitializerPlacement::Before:
150 Stream <<
" " << joined <<
"(),";
152 case InitializerPlacement::After:
153 Stream <<
", " << joined <<
"()";
165 const RecordDecl *getCanonicalRecordDecl(
const QualType &
Type) {
166 if (
const auto *RT =
Type.getCanonicalType()->getAs<RecordType>())
167 return RT->getDecl();
171 template <
typename R,
typename T>
172 SmallVector<IntializerInsertion, 16>
173 computeInsertions(
const CXXConstructorDecl::init_const_range &Inits,
174 const R &OrderedDecls,
175 const SmallPtrSetImpl<const T *> &DeclsToInit) {
176 SmallVector<IntializerInsertion, 16> Insertions;
177 Insertions.emplace_back(InitializerPlacement::New,
nullptr);
179 typename R::const_iterator
Decl = std::begin(OrderedDecls);
180 for (
const CXXCtorInitializer *Init : Inits) {
181 if (Init->isWritten()) {
182 if (Insertions.size() == 1)
183 Insertions.emplace_back(InitializerPlacement::Before, Init);
187 const auto *InitDecl =
188 Init->isAnyMemberInitializer()
189 ? static_cast<const NamedDecl *>(Init->getAnyMember())
190 : Init->getBaseClass()->getAsCXXRecordDecl();
193 for (;
Decl != std::end(OrderedDecls) && *
Decl != InitDecl; ++
Decl) {
194 if (
const auto *D = dyn_cast<T>(*
Decl)) {
195 if (DeclsToInit.count(D) > 0)
196 Insertions.back().Initializers.emplace_back(getName(D));
200 Insertions.emplace_back(InitializerPlacement::After, Init);
205 for (;
Decl != std::end(OrderedDecls); ++
Decl) {
206 if (
const auto *D = dyn_cast<T>(*
Decl)) {
207 if (DeclsToInit.count(D) > 0)
208 Insertions.back().Initializers.emplace_back(getName(D));
216 void getInitializationsInOrder(
const CXXRecordDecl &ClassDecl,
217 SmallVectorImpl<const NamedDecl *> &Decls) {
219 for (
const auto &
Base : ClassDecl.bases()) {
221 if (
const NamedDecl *
Decl = getCanonicalRecordDecl(
Base.getType())) {
222 Decls.emplace_back(
Decl);
225 forEachField(ClassDecl, ClassDecl.fields(),
226 [&](
const FieldDecl *F) { Decls.push_back(F); });
229 template <
typename T>
230 void fixInitializerList(
const ASTContext &Context, DiagnosticBuilder &Diag,
231 const CXXConstructorDecl *Ctor,
232 const SmallPtrSetImpl<const T *> &DeclsToInit) {
234 if (Ctor->getBeginLoc().isMacroID())
237 SmallVector<const NamedDecl *, 16> OrderedDecls;
238 getInitializationsInOrder(*Ctor->getParent(), OrderedDecls);
240 for (
const auto &Insertion :
241 computeInsertions(Ctor->inits(), OrderedDecls, DeclsToInit)) {
242 if (!Insertion.Initializers.empty())
243 Diag << FixItHint::CreateInsertion(Insertion.getLocation(Context, *Ctor),
244 Insertion.codeToInsert());
250 ProTypeMemberInitCheck::ProTypeMemberInitCheck(StringRef
Name,
253 IgnoreArrays(Options.get(
"IgnoreArrays", false)),
254 UseAssignment(Options.getLocalOrGlobal(
"UseAssignment", false)) {}
257 auto IsUserProvidedNonDelegatingConstructor =
258 allOf(isUserProvided(),
259 unless(anyOf(isInstantiated(), isDelegatingConstructor())));
260 auto IsNonTrivialDefaultConstructor = allOf(
261 isDefaultConstructor(), unless(isUserProvided()),
264 cxxConstructorDecl(isDefinition(),
265 anyOf(IsUserProvidedNonDelegatingConstructor,
266 IsNonTrivialDefaultConstructor))
274 isDefinition(), unless(isInstantiated()), hasDefaultConstructor(),
275 anyOf(has(cxxConstructorDecl(isDefaultConstructor(), isDefaulted(),
276 unless(isImplicit()))),
277 unless(has(cxxConstructorDecl()))),
282 auto HasDefaultConstructor = hasInitializer(
283 cxxConstructExpr(unless(requiresZeroInitialization()),
284 hasDeclaration(cxxConstructorDecl(
285 isDefaultConstructor(), unless(isUserProvided())))));
287 varDecl(isDefinition(), HasDefaultConstructor,
288 hasAutomaticStorageDuration(),
289 hasType(recordDecl(has(fieldDecl()),
296 if (
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor")) {
298 if (!Ctor->getBody())
300 checkMissingMemberInitializer(*Result.Context, *Ctor->getParent(), Ctor);
301 checkMissingBaseClassInitializer(*Result.Context, *Ctor->getParent(), Ctor);
302 }
else if (
const auto *
Record =
303 Result.Nodes.getNodeAs<CXXRecordDecl>(
"record")) {
304 assert(
Record->hasDefaultConstructor() &&
305 "Matched record should have a default constructor");
306 checkMissingMemberInitializer(*Result.Context, *
Record,
nullptr);
307 checkMissingBaseClassInitializer(*Result.Context, *
Record,
nullptr);
308 }
else if (
const auto *Var = Result.Nodes.getNodeAs<VarDecl>(
"var")) {
309 checkUninitializedTrivialType(*Result.Context, Var);
320 if (T->isIncompleteArrayType())
323 while (
const ConstantArrayType *ArrayT = Context.getAsConstantArrayType(T)) {
324 if (!ArrayT->getSize())
327 T = ArrayT->getElementType();
334 if (
const CXXRecordDecl *ClassDecl =
Type->getAsCXXRecordDecl()) {
335 return ClassDecl->isEmpty();
341 const char *DefaultInitializer =
"{}";
343 return DefaultInitializer;
345 if (QT->isPointerType())
348 const BuiltinType *BT =
349 dyn_cast<BuiltinType>(QT.getCanonicalType().getTypePtr());
351 return DefaultInitializer;
353 switch (BT->getKind()) {
354 case BuiltinType::Bool:
356 case BuiltinType::Float:
358 case BuiltinType::Double:
360 case BuiltinType::LongDouble:
362 case BuiltinType::SChar:
363 case BuiltinType::Char_S:
364 case BuiltinType::WChar_S:
365 case BuiltinType::Char16:
366 case BuiltinType::Char32:
367 case BuiltinType::Short:
368 case BuiltinType::Int:
370 case BuiltinType::UChar:
371 case BuiltinType::Char_U:
372 case BuiltinType::WChar_U:
373 case BuiltinType::UShort:
374 case BuiltinType::UInt:
376 case BuiltinType::Long:
378 case BuiltinType::ULong:
380 case BuiltinType::LongLong:
382 case BuiltinType::ULongLong:
386 return DefaultInitializer;
390 void ProTypeMemberInitCheck::checkMissingMemberInitializer(
391 ASTContext &Context,
const CXXRecordDecl &ClassDecl,
392 const CXXConstructorDecl *Ctor) {
393 bool IsUnion = ClassDecl.isUnion();
395 if (IsUnion && ClassDecl.hasInClassInitializer())
399 SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
400 forEachField(ClassDecl, ClassDecl.fields(), [&](
const FieldDecl *F) {
401 if (!F->hasInClassInitializer() &&
404 !
isEmpty(Context, F->getType()) && !F->isUnnamedBitfield())
405 FieldsToInit.insert(F);
407 if (FieldsToInit.empty())
411 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
414 if (Init->isAnyMemberInitializer() && Init->isWritten()) {
417 FieldsToInit.erase(Init->getAnyMember());
420 removeFieldsInitializedInBody(*Ctor->getBody(), Context, FieldsToInit);
425 SmallVector<const FieldDecl *, 16> OrderedFields;
426 forEachField(ClassDecl, ClassDecl.fields(),
427 [&](
const FieldDecl *F) { OrderedFields.push_back(F); });
430 SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
431 forEachField(ClassDecl, FieldsToInit,
432 [&](
const FieldDecl *F) { AllFieldsToInit.insert(F); });
433 if (AllFieldsToInit.empty())
436 DiagnosticBuilder Diag =
437 diag(Ctor ? Ctor->getBeginLoc() : ClassDecl.getLocation(),
439 ?
"union constructor should initialize one of these fields: %0"
440 :
"constructor does not initialize these fields: %0")
441 << toCommaSeparatedString(OrderedFields, AllFieldsToInit);
445 if (Ctor && Ctor->getBeginLoc().isMacroID())
450 SmallPtrSet<const FieldDecl *, 16> FieldsToFix;
451 SmallPtrSet<const RecordDecl *, 4> UnionsSeen;
452 forEachField(ClassDecl, OrderedFields, [&](
const FieldDecl *F) {
453 if (!FieldsToInit.count(F))
458 if (F->getType()->isEnumeralType() ||
461 if (!F->getParent()->isUnion() || UnionsSeen.insert(F->getParent()).second)
462 FieldsToFix.insert(F);
464 if (FieldsToFix.empty())
468 if (Context.getLangOpts().CPlusPlus11) {
469 for (
const FieldDecl *Field : FieldsToFix) {
470 Diag << FixItHint::CreateInsertion(
471 getLocationForEndOfToken(Context,
Field->getSourceRange().getEnd()),
476 fixInitializerList(Context, Diag, Ctor, FieldsToFix);
480 void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
481 const ASTContext &Context,
const CXXRecordDecl &ClassDecl,
482 const CXXConstructorDecl *Ctor) {
485 SmallVector<const RecordDecl *, 4> AllBases;
486 SmallPtrSet<const RecordDecl *, 4> BasesToInit;
487 for (
const CXXBaseSpecifier &
Base : ClassDecl.bases()) {
488 if (
const auto *BaseClassDecl = getCanonicalRecordDecl(
Base.getType())) {
489 AllBases.emplace_back(BaseClassDecl);
490 if (!BaseClassDecl->field_empty() &&
493 BasesToInit.insert(BaseClassDecl);
497 if (BasesToInit.empty())
502 if (Ctor->isImplicit())
505 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
506 if (Init->isBaseInitializer() && Init->isWritten())
507 BasesToInit.erase(Init->getBaseClass()->getAsCXXRecordDecl());
511 if (BasesToInit.empty())
514 DiagnosticBuilder Diag =
515 diag(Ctor ? Ctor->getBeginLoc() : ClassDecl.getLocation(),
516 "constructor does not initialize these bases: %0")
517 << toCommaSeparatedString(AllBases, BasesToInit);
520 fixInitializerList(Context, Diag, Ctor, BasesToInit);
523 void ProTypeMemberInitCheck::checkUninitializedTrivialType(
524 const ASTContext &Context,
const VarDecl *Var) {
525 DiagnosticBuilder Diag =
526 diag(Var->getBeginLoc(),
"uninitialized record type: %0") << Var;
528 Diag << FixItHint::CreateInsertion(
529 getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()),
530 Context.getLangOpts().CPlusPlus11 ?
"{}" :
" = {}");