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