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())
65 return Record->getName();
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 {
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.");
118 SourceLocation Location;
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:
133 Location = Where->getRParenLoc();
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)) {}
260 auto IsUserProvidedNonDelegatingConstructor =
261 allOf(isUserProvided(),
262 unless(anyOf(isInstantiated(), isDelegatingConstructor())));
263 auto IsNonTrivialDefaultConstructor = allOf(
264 isDefaultConstructor(), unless(isUserProvided()),
267 cxxConstructorDecl(isDefinition(),
268 anyOf(IsUserProvidedNonDelegatingConstructor,
269 IsNonTrivialDefaultConstructor))
277 isDefinition(), unless(isInstantiated()), hasDefaultConstructor(),
278 anyOf(has(cxxConstructorDecl(isDefaultConstructor(), isDefaulted(),
279 unless(isImplicit()))),
280 unless(has(cxxConstructorDecl()))),
285 auto HasDefaultConstructor = hasInitializer(
286 cxxConstructExpr(unless(requiresZeroInitialization()),
287 hasDeclaration(cxxConstructorDecl(
288 isDefaultConstructor(), unless(isUserProvided())))));
290 varDecl(isDefinition(), HasDefaultConstructor,
291 hasAutomaticStorageDuration(),
292 hasType(recordDecl(has(fieldDecl()),
299 if (
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor")) {
301 if (!Ctor->getBody())
303 checkMissingMemberInitializer(*Result.Context, *Ctor->getParent(), Ctor);
304 checkMissingBaseClassInitializer(*Result.Context, *Ctor->getParent(), Ctor);
305 }
else if (
const auto *Record =
306 Result.Nodes.getNodeAs<CXXRecordDecl>(
"record")) {
307 assert(Record->hasDefaultConstructor() &&
308 "Matched record should have a default constructor");
309 checkMissingMemberInitializer(*Result.Context, *Record,
nullptr);
310 checkMissingBaseClassInitializer(*Result.Context, *Record,
nullptr);
311 }
else if (
const auto *Var = Result.Nodes.getNodeAs<VarDecl>(
"var")) {
312 checkUninitializedTrivialType(*Result.Context, Var);
323 if (T->isIncompleteArrayType())
326 while (
const ConstantArrayType *ArrayT = Context.getAsConstantArrayType(T)) {
327 if (!ArrayT->getSize())
330 T = ArrayT->getElementType();
337 if (
const CXXRecordDecl *ClassDecl = Type->getAsCXXRecordDecl()) {
338 return ClassDecl->isEmpty();
344 const char *DefaultInitializer =
"{}";
346 return DefaultInitializer;
348 if (QT->isPointerType())
351 const BuiltinType *BT =
352 dyn_cast<BuiltinType>(QT.getCanonicalType().getTypePtr());
354 return DefaultInitializer;
356 switch (BT->getKind()) {
357 case BuiltinType::Bool:
359 case BuiltinType::Float:
361 case BuiltinType::Double:
363 case BuiltinType::LongDouble:
365 case BuiltinType::SChar:
366 case BuiltinType::Char_S:
367 case BuiltinType::WChar_S:
368 case BuiltinType::Char16:
369 case BuiltinType::Char32:
370 case BuiltinType::Short:
371 case BuiltinType::Int:
373 case BuiltinType::UChar:
374 case BuiltinType::Char_U:
375 case BuiltinType::WChar_U:
376 case BuiltinType::UShort:
377 case BuiltinType::UInt:
379 case BuiltinType::Long:
381 case BuiltinType::ULong:
383 case BuiltinType::LongLong:
385 case BuiltinType::ULongLong:
389 return DefaultInitializer;
393 void ProTypeMemberInitCheck::checkMissingMemberInitializer(
394 ASTContext &Context,
const CXXRecordDecl &ClassDecl,
395 const CXXConstructorDecl *Ctor) {
396 bool IsUnion = ClassDecl.isUnion();
398 if (IsUnion && ClassDecl.hasInClassInitializer())
402 SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
403 forEachField(ClassDecl, ClassDecl.fields(), [&](
const FieldDecl *F) {
404 if (!F->hasInClassInitializer() &&
407 !
isEmpty(Context, F->getType()) && !F->isUnnamedBitfield())
408 FieldsToInit.insert(F);
410 if (FieldsToInit.empty())
414 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
417 if (Init->isAnyMemberInitializer() && Init->isWritten()) {
420 FieldsToInit.erase(Init->getAnyMember());
423 removeFieldsInitializedInBody(*Ctor->getBody(), Context, FieldsToInit);
428 SmallVector<const FieldDecl *, 16> OrderedFields;
429 forEachField(ClassDecl, ClassDecl.fields(),
430 [&](
const FieldDecl *F) { OrderedFields.push_back(F); });
433 SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
434 forEachField(ClassDecl, FieldsToInit,
435 [&](
const FieldDecl *F) { AllFieldsToInit.insert(F); });
436 if (AllFieldsToInit.empty())
439 DiagnosticBuilder Diag =
440 diag(Ctor ? Ctor->getBeginLoc() : ClassDecl.getLocation(),
442 ?
"union constructor should initialize one of these fields: %0" 443 :
"constructor does not initialize these fields: %0")
444 << toCommaSeparatedString(OrderedFields, AllFieldsToInit);
448 if (Ctor && Ctor->getBeginLoc().isMacroID())
453 SmallPtrSet<const FieldDecl *, 16> FieldsToFix;
454 SmallPtrSet<const RecordDecl *, 4> UnionsSeen;
455 forEachField(ClassDecl, OrderedFields, [&](
const FieldDecl *F) {
456 if (!FieldsToInit.count(F))
461 if (F->getType()->isEnumeralType() ||
464 if (!F->getParent()->isUnion() || UnionsSeen.insert(F->getParent()).second)
465 FieldsToFix.insert(F);
467 if (FieldsToFix.empty())
471 if (Context.getLangOpts().CPlusPlus11) {
472 for (
const FieldDecl *Field : FieldsToFix) {
473 Diag << FixItHint::CreateInsertion(
474 getLocationForEndOfToken(Context, Field->getSourceRange().getEnd()),
479 fixInitializerList(Context, Diag, Ctor, FieldsToFix);
483 void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
484 const ASTContext &Context,
const CXXRecordDecl &ClassDecl,
485 const CXXConstructorDecl *Ctor) {
488 SmallVector<const RecordDecl *, 4> AllBases;
489 SmallPtrSet<const RecordDecl *, 4> BasesToInit;
490 for (
const CXXBaseSpecifier &
Base : ClassDecl.bases()) {
491 if (
const auto *BaseClassDecl = getCanonicalRecordDecl(
Base.getType())) {
492 AllBases.emplace_back(BaseClassDecl);
493 if (!BaseClassDecl->field_empty() &&
496 BasesToInit.insert(BaseClassDecl);
500 if (BasesToInit.empty())
505 if (Ctor->isImplicit())
508 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
509 if (Init->isBaseInitializer() && Init->isWritten())
510 BasesToInit.erase(Init->getBaseClass()->getAsCXXRecordDecl());
514 if (BasesToInit.empty())
517 DiagnosticBuilder Diag =
518 diag(Ctor ? Ctor->getBeginLoc() : ClassDecl.getLocation(),
519 "constructor does not initialize these bases: %0")
520 << toCommaSeparatedString(AllBases, BasesToInit);
523 fixInitializerList(Context, Diag, Ctor, BasesToInit);
526 void ProTypeMemberInitCheck::checkUninitializedTrivialType(
527 const ASTContext &Context,
const VarDecl *Var) {
528 DiagnosticBuilder Diag =
529 diag(Var->getBeginLoc(),
"uninitialized record type: %0") << Var;
531 Diag << FixItHint::CreateInsertion(
532 getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()),
533 Context.getLangOpts().CPlusPlus11 ?
"{}" :
" = {}");
const FunctionDecl * Decl
static bool isIncompleteOrZeroLengthArrayType(ASTContext &Context, QualType T)
Token getPreviousToken(SourceLocation Location, const SourceManager &SM, const LangOptions &LangOpts, bool SkipComments)
Returns previous token or tok::unknown if not found.
llvm::SmallVector< uint64_t, 1024 > Record
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
AST_MATCHER(Expr, isMacroID)
static bool isEmpty(ASTContext &Context, const QualType &Type)
Base class for all clang-tidy checks.
const LangOptions & getLangOpts() const
Returns the language options from the context.
SmallVector< std::string, 4 > Initializers
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
static const char * getInitializer(QualType QT, bool UseAssignment)
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context)
Returns true if Type is trivially default constructible.
const CXXCtorInitializer * Where
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
InitializerPlacement Placement
static std::string join(ArrayRef< SpecialMemberFunctionsCheck::SpecialMemberFunctionKind > SMFS, llvm::StringRef AndOr)
std::unique_ptr< GlobalCompilationDatabase > Base
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.