1 //===--- ProTypeMemberInitCheck.cpp - clang-tidy---------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "ProTypeMemberInitCheck.h"
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"
17 
18 using namespace clang::ast_matchers;
19 using namespace clang::tidy::matchers;
20 using llvm::SmallPtrSet;
21 using llvm::SmallPtrSetImpl;
22 
23 namespace clang {
24 namespace tidy {
25 namespace cppcoreguidelines {
26 
27 namespace {
28 
AST_MATCHER(CXXRecordDecl,hasDefaultConstructor)29 AST_MATCHER(CXXRecordDecl, hasDefaultConstructor) {
30   return Node.hasDefaultConstructor();
31 }
32 
33 // Iterate over all the fields in a record type, both direct and indirect (e.g.
34 // if the record contains an anonymous struct).
35 template <typename T, typename Func>
forEachField(const RecordDecl & Record,const T & Fields,Func && Fn)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);
41     } else {
42       Fn(F);
43     }
44   }
45 }
46 
47 template <typename T, typename Func>
forEachFieldWithFilter(const RecordDecl & Record,const T & Fields,bool & AnyMemberHasInitPerUnion,Func && Fn)48 void forEachFieldWithFilter(const RecordDecl &Record, const T &Fields,
49                             bool &AnyMemberHasInitPerUnion, Func &&Fn) {
50   for (const FieldDecl *F : Fields) {
51     if (F->isAnonymousStructOrUnion()) {
52       if (const CXXRecordDecl *R = F->getType()->getAsCXXRecordDecl()) {
53         AnyMemberHasInitPerUnion = false;
54         forEachFieldWithFilter(*R, R->fields(), AnyMemberHasInitPerUnion, Fn);
55       }
56     } else {
57       Fn(F);
58     }
59     if (Record.isUnion() && AnyMemberHasInitPerUnion)
60       break;
61   }
62 }
63 
removeFieldsInitializedInBody(const Stmt & Stmt,ASTContext & Context,SmallPtrSetImpl<const FieldDecl * > & FieldDecls)64 void removeFieldsInitializedInBody(
65     const Stmt &Stmt, ASTContext &Context,
66     SmallPtrSetImpl<const FieldDecl *> &FieldDecls) {
67   auto Matches =
68       match(findAll(binaryOperator(
69                 hasOperatorName("="),
70                 hasLHS(memberExpr(member(fieldDecl().bind("fieldDecl")))))),
71             Stmt, Context);
72   for (const auto &Match : Matches)
73     FieldDecls.erase(Match.getNodeAs<FieldDecl>("fieldDecl"));
74 }
75 
getName(const FieldDecl * Field)76 StringRef getName(const FieldDecl *Field) { return Field->getName(); }
77 
getName(const RecordDecl * Record)78 StringRef getName(const RecordDecl *Record) {
79   // Get the typedef name if this is a C-style anonymous struct and typedef.
80   if (const TypedefNameDecl *Typedef = Record->getTypedefNameForAnonDecl())
81     return Typedef->getName();
82   return Record->getName();
83 }
84 
85 // Creates comma separated list of decls requiring initialization in order of
86 // declaration.
87 template <typename R, typename T>
88 std::string
toCommaSeparatedString(const R & OrderedDecls,const SmallPtrSetImpl<const T * > & DeclsToInit)89 toCommaSeparatedString(const R &OrderedDecls,
90                        const SmallPtrSetImpl<const T *> &DeclsToInit) {
91   SmallVector<StringRef, 16> Names;
92   for (const T *Decl : OrderedDecls) {
93     if (DeclsToInit.count(Decl))
94       Names.emplace_back(getName(Decl));
95   }
96   return llvm::join(Names.begin(), Names.end(), ", ");
97 }
98 
getLocationForEndOfToken(const ASTContext & Context,SourceLocation Location)99 SourceLocation getLocationForEndOfToken(const ASTContext &Context,
100                                         SourceLocation Location) {
101   return Lexer::getLocForEndOfToken(Location, 0, Context.getSourceManager(),
102                                     Context.getLangOpts());
103 }
104 
105 // There are 3 kinds of insertion placements:
106 enum class InitializerPlacement {
107   // 1. The fields are inserted after an existing CXXCtorInitializer stored in
108   // Where. This will be the case whenever there is a written initializer before
109   // the fields available.
110   After,
111 
112   // 2. The fields are inserted before the first existing initializer stored in
113   // Where.
114   Before,
115 
116   // 3. There are no written initializers and the fields will be inserted before
117   // the constructor's body creating a new initializer list including the ':'.
118   New
119 };
120 
121 // An InitializerInsertion contains a list of fields and/or base classes to
122 // insert into the initializer list of a constructor. We use this to ensure
123 // proper absolute ordering according to the class declaration relative to the
124 // (perhaps improper) ordering in the existing initializer list, if any.
125 struct InitializerInsertion {
InitializerInsertionclang::tidy::cppcoreguidelines::__anon699381af0111::InitializerInsertion126   InitializerInsertion(InitializerPlacement Placement,
127                        const CXXCtorInitializer *Where)
128       : Placement(Placement), Where(Where) {}
129 
getLocationclang::tidy::cppcoreguidelines::__anon699381af0111::InitializerInsertion130   SourceLocation getLocation(const ASTContext &Context,
131                              const CXXConstructorDecl &Constructor) const {
132     assert((Where != nullptr || Placement == InitializerPlacement::New) &&
133            "Location should be relative to an existing initializer or this "
134            "insertion represents a new initializer list.");
135     SourceLocation Location;
136     switch (Placement) {
137     case InitializerPlacement::New:
138       Location = utils::lexer::getPreviousToken(
139                      Constructor.getBody()->getBeginLoc(),
140                      Context.getSourceManager(), Context.getLangOpts())
141                      .getLocation();
142       break;
143     case InitializerPlacement::Before:
144       Location = utils::lexer::getPreviousToken(
145                      Where->getSourceRange().getBegin(),
146                      Context.getSourceManager(), Context.getLangOpts())
147                      .getLocation();
148       break;
149     case InitializerPlacement::After:
150       Location = Where->getRParenLoc();
151       break;
152     }
153     return getLocationForEndOfToken(Context, Location);
154   }
155 
codeToInsertclang::tidy::cppcoreguidelines::__anon699381af0111::InitializerInsertion156   std::string codeToInsert() const {
157     assert(!Initializers.empty() && "No initializers to insert");
158     std::string Code;
159     llvm::raw_string_ostream Stream(Code);
160     std::string Joined =
161         llvm::join(Initializers.begin(), Initializers.end(), "(), ");
162     switch (Placement) {
163     case InitializerPlacement::New:
164       Stream << " : " << Joined << "()";
165       break;
166     case InitializerPlacement::Before:
167       Stream << " " << Joined << "(),";
168       break;
169     case InitializerPlacement::After:
170       Stream << ", " << Joined << "()";
171       break;
172     }
173     return Stream.str();
174   }
175 
176   InitializerPlacement Placement;
177   const CXXCtorInitializer *Where;
178   SmallVector<std::string, 4> Initializers;
179 };
180 
181 // Convenience utility to get a RecordDecl from a QualType.
getCanonicalRecordDecl(const QualType & Type)182 const RecordDecl *getCanonicalRecordDecl(const QualType &Type) {
183   if (const auto *RT = Type.getCanonicalType()->getAs<RecordType>())
184     return RT->getDecl();
185   return nullptr;
186 }
187 
188 template <typename R, typename T>
189 SmallVector<InitializerInsertion, 16>
computeInsertions(const CXXConstructorDecl::init_const_range & Inits,const R & OrderedDecls,const SmallPtrSetImpl<const T * > & DeclsToInit)190 computeInsertions(const CXXConstructorDecl::init_const_range &Inits,
191                   const R &OrderedDecls,
192                   const SmallPtrSetImpl<const T *> &DeclsToInit) {
193   SmallVector<InitializerInsertion, 16> Insertions;
194   Insertions.emplace_back(InitializerPlacement::New, nullptr);
195 
196   typename R::const_iterator Decl = std::begin(OrderedDecls);
197   for (const CXXCtorInitializer *Init : Inits) {
198     if (Init->isWritten()) {
199       if (Insertions.size() == 1)
200         Insertions.emplace_back(InitializerPlacement::Before, Init);
201 
202       // Gets either the field or base class being initialized by the provided
203       // initializer.
204       const auto *InitDecl =
205           Init->isAnyMemberInitializer()
206               ? static_cast<const NamedDecl *>(Init->getAnyMember())
207               : Init->getBaseClass()->getAsCXXRecordDecl();
208 
209       // Add all fields between current field up until the next initializer.
210       for (; Decl != std::end(OrderedDecls) && *Decl != InitDecl; ++Decl) {
211         if (const auto *D = dyn_cast<T>(*Decl)) {
212           if (DeclsToInit.contains(D))
213             Insertions.back().Initializers.emplace_back(getName(D));
214         }
215       }
216 
217       Insertions.emplace_back(InitializerPlacement::After, Init);
218     }
219   }
220 
221   // Add remaining decls that require initialization.
222   for (; Decl != std::end(OrderedDecls); ++Decl) {
223     if (const auto *D = dyn_cast<T>(*Decl)) {
224       if (DeclsToInit.contains(D))
225         Insertions.back().Initializers.emplace_back(getName(D));
226     }
227   }
228   return Insertions;
229 }
230 
231 // Gets the list of bases and members that could possibly be initialized, in
232 // order as they appear in the class declaration.
getInitializationsInOrder(const CXXRecordDecl & ClassDecl,SmallVectorImpl<const NamedDecl * > & Decls)233 void getInitializationsInOrder(const CXXRecordDecl &ClassDecl,
234                                SmallVectorImpl<const NamedDecl *> &Decls) {
235   Decls.clear();
236   for (const auto &Base : ClassDecl.bases()) {
237     // Decl may be null if the base class is a template parameter.
238     if (const NamedDecl *Decl = getCanonicalRecordDecl(Base.getType())) {
239       Decls.emplace_back(Decl);
240     }
241   }
242   forEachField(ClassDecl, ClassDecl.fields(),
243                [&](const FieldDecl *F) { Decls.push_back(F); });
244 }
245 
246 template <typename T>
fixInitializerList(const ASTContext & Context,DiagnosticBuilder & Diag,const CXXConstructorDecl * Ctor,const SmallPtrSetImpl<const T * > & DeclsToInit)247 void fixInitializerList(const ASTContext &Context, DiagnosticBuilder &Diag,
248                         const CXXConstructorDecl *Ctor,
249                         const SmallPtrSetImpl<const T *> &DeclsToInit) {
250   // Do not propose fixes in macros since we cannot place them correctly.
251   if (Ctor->getBeginLoc().isMacroID())
252     return;
253 
254   SmallVector<const NamedDecl *, 16> OrderedDecls;
255   getInitializationsInOrder(*Ctor->getParent(), OrderedDecls);
256 
257   for (const auto &Insertion :
258        computeInsertions(Ctor->inits(), OrderedDecls, DeclsToInit)) {
259     if (!Insertion.Initializers.empty())
260       Diag << FixItHint::CreateInsertion(Insertion.getLocation(Context, *Ctor),
261                                          Insertion.codeToInsert());
262   }
263 }
264 
265 } // anonymous namespace
266 
ProTypeMemberInitCheck(StringRef Name,ClangTidyContext * Context)267 ProTypeMemberInitCheck::ProTypeMemberInitCheck(StringRef Name,
268                                                ClangTidyContext *Context)
269     : ClangTidyCheck(Name, Context),
270       IgnoreArrays(Options.get("IgnoreArrays", false)),
271       UseAssignment(Options.getLocalOrGlobal("UseAssignment", false)) {}
272 
registerMatchers(MatchFinder * Finder)273 void ProTypeMemberInitCheck::registerMatchers(MatchFinder *Finder) {
274   auto IsUserProvidedNonDelegatingConstructor =
275       allOf(isUserProvided(),
276             unless(anyOf(isInstantiated(), isDelegatingConstructor())));
277   auto IsNonTrivialDefaultConstructor = allOf(
278       isDefaultConstructor(), unless(isUserProvided()),
279       hasParent(cxxRecordDecl(unless(isTriviallyDefaultConstructible()))));
280   Finder->addMatcher(
281       cxxConstructorDecl(isDefinition(),
282                          anyOf(IsUserProvidedNonDelegatingConstructor,
283                                IsNonTrivialDefaultConstructor))
284           .bind("ctor"),
285       this);
286 
287   // Match classes with a default constructor that is defaulted or is not in the
288   // AST.
289   Finder->addMatcher(
290       cxxRecordDecl(
291           isDefinition(), unless(isInstantiated()), hasDefaultConstructor(),
292           anyOf(has(cxxConstructorDecl(isDefaultConstructor(), isDefaulted(),
293                                        unless(isImplicit()))),
294                 unless(has(cxxConstructorDecl()))),
295           unless(isTriviallyDefaultConstructible()))
296           .bind("record"),
297       this);
298 
299   auto HasDefaultConstructor = hasInitializer(
300       cxxConstructExpr(unless(requiresZeroInitialization()),
301                        hasDeclaration(cxxConstructorDecl(
302                            isDefaultConstructor(), unless(isUserProvided())))));
303   Finder->addMatcher(
304       varDecl(isDefinition(), HasDefaultConstructor,
305               hasAutomaticStorageDuration(),
306               hasType(recordDecl(has(fieldDecl()),
307                                  isTriviallyDefaultConstructible())))
308           .bind("var"),
309       this);
310 }
311 
check(const MatchFinder::MatchResult & Result)312 void ProTypeMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
313   if (const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor")) {
314     // Skip declarations delayed by late template parsing without a body.
315     if (!Ctor->getBody())
316       return;
317     // Skip out-of-band explicitly defaulted special member functions
318     // (except the default constructor).
319     if (Ctor->isExplicitlyDefaulted() && !Ctor->isDefaultConstructor())
320       return;
321     checkMissingMemberInitializer(*Result.Context, *Ctor->getParent(), Ctor);
322     checkMissingBaseClassInitializer(*Result.Context, *Ctor->getParent(), Ctor);
323   } else if (const auto *Record =
324                  Result.Nodes.getNodeAs<CXXRecordDecl>("record")) {
325     assert(Record->hasDefaultConstructor() &&
326            "Matched record should have a default constructor");
327     checkMissingMemberInitializer(*Result.Context, *Record, nullptr);
328     checkMissingBaseClassInitializer(*Result.Context, *Record, nullptr);
329   } else if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var")) {
330     checkUninitializedTrivialType(*Result.Context, Var);
331   }
332 }
333 
storeOptions(ClangTidyOptions::OptionMap & Opts)334 void ProTypeMemberInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
335   Options.store(Opts, "IgnoreArrays", IgnoreArrays);
336   Options.store(Opts, "UseAssignment", UseAssignment);
337 }
338 
339 // FIXME: Copied from clang/lib/Sema/SemaDeclCXX.cpp.
isIncompleteOrZeroLengthArrayType(ASTContext & Context,QualType T)340 static bool isIncompleteOrZeroLengthArrayType(ASTContext &Context, QualType T) {
341   if (T->isIncompleteArrayType())
342     return true;
343 
344   while (const ConstantArrayType *ArrayT = Context.getAsConstantArrayType(T)) {
345     if (!ArrayT->getSize())
346       return true;
347 
348     T = ArrayT->getElementType();
349   }
350 
351   return false;
352 }
353 
isEmpty(ASTContext & Context,const QualType & Type)354 static bool isEmpty(ASTContext &Context, const QualType &Type) {
355   if (const CXXRecordDecl *ClassDecl = Type->getAsCXXRecordDecl()) {
356     return ClassDecl->isEmpty();
357   }
358   return isIncompleteOrZeroLengthArrayType(Context, Type);
359 }
360 
getInitializer(QualType QT,bool UseAssignment)361 static const char *getInitializer(QualType QT, bool UseAssignment) {
362   const char *DefaultInitializer = "{}";
363   if (!UseAssignment)
364     return DefaultInitializer;
365 
366   if (QT->isPointerType())
367     return " = nullptr";
368 
369   const BuiltinType *BT =
370       dyn_cast<BuiltinType>(QT.getCanonicalType().getTypePtr());
371   if (!BT)
372     return DefaultInitializer;
373 
374   switch (BT->getKind()) {
375   case BuiltinType::Bool:
376     return " = false";
377   case BuiltinType::Float:
378     return " = 0.0F";
379   case BuiltinType::Double:
380     return " = 0.0";
381   case BuiltinType::LongDouble:
382     return " = 0.0L";
383   case BuiltinType::SChar:
384   case BuiltinType::Char_S:
385   case BuiltinType::WChar_S:
386   case BuiltinType::Char16:
387   case BuiltinType::Char32:
388   case BuiltinType::Short:
389   case BuiltinType::Int:
390     return " = 0";
391   case BuiltinType::UChar:
392   case BuiltinType::Char_U:
393   case BuiltinType::WChar_U:
394   case BuiltinType::UShort:
395   case BuiltinType::UInt:
396     return " = 0U";
397   case BuiltinType::Long:
398     return " = 0L";
399   case BuiltinType::ULong:
400     return " = 0UL";
401   case BuiltinType::LongLong:
402     return " = 0LL";
403   case BuiltinType::ULongLong:
404     return " = 0ULL";
405 
406   default:
407     return DefaultInitializer;
408   }
409 }
410 
checkMissingMemberInitializer(ASTContext & Context,const CXXRecordDecl & ClassDecl,const CXXConstructorDecl * Ctor)411 void ProTypeMemberInitCheck::checkMissingMemberInitializer(
412     ASTContext &Context, const CXXRecordDecl &ClassDecl,
413     const CXXConstructorDecl *Ctor) {
414   bool IsUnion = ClassDecl.isUnion();
415 
416   if (IsUnion && ClassDecl.hasInClassInitializer())
417     return;
418 
419   // Gather all fields (direct and indirect) that need to be initialized.
420   SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
421   forEachField(ClassDecl, ClassDecl.fields(), [&](const FieldDecl *F) {
422     if (IgnoreArrays && F->getType()->isArrayType())
423       return;
424     if (!F->hasInClassInitializer() &&
425         utils::type_traits::isTriviallyDefaultConstructible(F->getType(),
426                                                             Context) &&
427         !isEmpty(Context, F->getType()) && !F->isUnnamedBitfield())
428       FieldsToInit.insert(F);
429   });
430   if (FieldsToInit.empty())
431     return;
432 
433   if (Ctor) {
434     for (const CXXCtorInitializer *Init : Ctor->inits()) {
435       // Remove any fields that were explicitly written in the initializer list
436       // or in-class.
437       if (Init->isAnyMemberInitializer() && Init->isWritten()) {
438         if (IsUnion)
439           return; // We can only initialize one member of a union.
440         FieldsToInit.erase(Init->getAnyMember());
441       }
442     }
443     removeFieldsInitializedInBody(*Ctor->getBody(), Context, FieldsToInit);
444   }
445 
446   // Collect all fields in order, both direct fields and indirect fields from
447   // anonymous record types.
448   SmallVector<const FieldDecl *, 16> OrderedFields;
449   forEachField(ClassDecl, ClassDecl.fields(),
450                [&](const FieldDecl *F) { OrderedFields.push_back(F); });
451 
452   // Collect all the fields we need to initialize, including indirect fields.
453   // It only includes fields that have not been fixed
454   SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
455   forEachField(ClassDecl, FieldsToInit, [&](const FieldDecl *F) {
456     if (!HasRecordClassMemberSet.contains(F)) {
457       AllFieldsToInit.insert(F);
458       HasRecordClassMemberSet.insert(F);
459     }
460   });
461   if (FieldsToInit.empty())
462     return;
463 
464   DiagnosticBuilder Diag =
465       diag(Ctor ? Ctor->getBeginLoc() : ClassDecl.getLocation(),
466            "%select{|union }0constructor %select{does not|should}0 initialize "
467            "%select{|one of }0these fields: %1")
468       << IsUnion << toCommaSeparatedString(OrderedFields, FieldsToInit);
469 
470   if (AllFieldsToInit.empty())
471     return;
472 
473   // Do not propose fixes for constructors in macros since we cannot place them
474   // correctly.
475   if (Ctor && Ctor->getBeginLoc().isMacroID())
476     return;
477 
478   // Collect all fields but only suggest a fix for the first member of unions,
479   // as initializing more than one union member is an error.
480   SmallPtrSet<const FieldDecl *, 16> FieldsToFix;
481   bool AnyMemberHasInitPerUnion = false;
482   forEachFieldWithFilter(ClassDecl, ClassDecl.fields(),
483                          AnyMemberHasInitPerUnion, [&](const FieldDecl *F) {
484     if (!FieldsToInit.count(F))
485       return;
486     // Don't suggest fixes for enums because we don't know a good default.
487     // Don't suggest fixes for bitfields because in-class initialization is not
488     // possible until C++20.
489     if (F->getType()->isEnumeralType() ||
490         (!getLangOpts().CPlusPlus20 && F->isBitField()))
491       return;
492     FieldsToFix.insert(F);
493     AnyMemberHasInitPerUnion = true;
494   });
495   if (FieldsToFix.empty())
496     return;
497 
498   // Use in-class initialization if possible.
499   if (Context.getLangOpts().CPlusPlus11) {
500     for (const FieldDecl *Field : FieldsToFix) {
501       Diag << FixItHint::CreateInsertion(
502           getLocationForEndOfToken(Context, Field->getSourceRange().getEnd()),
503           getInitializer(Field->getType(), UseAssignment));
504     }
505   } else if (Ctor) {
506     // Otherwise, rewrite the constructor's initializer list.
507     fixInitializerList(Context, Diag, Ctor, FieldsToFix);
508   }
509 }
510 
checkMissingBaseClassInitializer(const ASTContext & Context,const CXXRecordDecl & ClassDecl,const CXXConstructorDecl * Ctor)511 void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
512     const ASTContext &Context, const CXXRecordDecl &ClassDecl,
513     const CXXConstructorDecl *Ctor) {
514 
515   // Gather any base classes that need to be initialized.
516   SmallVector<const RecordDecl *, 4> AllBases;
517   SmallPtrSet<const RecordDecl *, 4> BasesToInit;
518   for (const CXXBaseSpecifier &Base : ClassDecl.bases()) {
519     if (const auto *BaseClassDecl = getCanonicalRecordDecl(Base.getType())) {
520       AllBases.emplace_back(BaseClassDecl);
521       if (!BaseClassDecl->field_empty() &&
522           utils::type_traits::isTriviallyDefaultConstructible(Base.getType(),
523                                                               Context))
524         BasesToInit.insert(BaseClassDecl);
525     }
526   }
527 
528   if (BasesToInit.empty())
529     return;
530 
531   // Remove any bases that were explicitly written in the initializer list.
532   if (Ctor) {
533     if (Ctor->isImplicit())
534       return;
535 
536     for (const CXXCtorInitializer *Init : Ctor->inits()) {
537       if (Init->isBaseInitializer() && Init->isWritten())
538         BasesToInit.erase(Init->getBaseClass()->getAsCXXRecordDecl());
539     }
540   }
541 
542   if (BasesToInit.empty())
543     return;
544 
545   DiagnosticBuilder Diag =
546       diag(Ctor ? Ctor->getBeginLoc() : ClassDecl.getLocation(),
547            "constructor does not initialize these bases: %0")
548       << toCommaSeparatedString(AllBases, BasesToInit);
549 
550   if (Ctor)
551     fixInitializerList(Context, Diag, Ctor, BasesToInit);
552 }
553 
checkUninitializedTrivialType(const ASTContext & Context,const VarDecl * Var)554 void ProTypeMemberInitCheck::checkUninitializedTrivialType(
555     const ASTContext &Context, const VarDecl *Var) {
556   DiagnosticBuilder Diag =
557       diag(Var->getBeginLoc(), "uninitialized record type: %0") << Var;
558 
559   Diag << FixItHint::CreateInsertion(
560       getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()),
561       Context.getLangOpts().CPlusPlus11 ? "{}" : " = {}");
562 }
563 
564 } // namespace cppcoreguidelines
565 } // namespace tidy
566 } // namespace clang
567