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" 21 using llvm::SmallPtrSet;
22 using llvm::SmallPtrSetImpl;
26 namespace cppcoreguidelines {
31 return Node.hasDefaultConstructor();
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);
48 void removeFieldsInitializedInBody(
49 const Stmt &Stmt, ASTContext &Context,
50 SmallPtrSetImpl<const FieldDecl *> &FieldDecls) {
52 match(findAll(binaryOperator(
54 hasLHS(memberExpr(member(fieldDecl().bind(
"fieldDecl")))))),
56 for (
const auto &Match : Matches)
57 FieldDecls.erase(Match.getNodeAs<FieldDecl>(
"fieldDecl"));
60 StringRef getName(
const FieldDecl *Field) {
return Field->getName(); }
62 StringRef getName(
const RecordDecl *Record) {
64 if (
const TypedefNameDecl *Typedef = Record->getTypedefNameForAnonDecl())
65 return Typedef->getName();
66 return Record->getName();
71 template <
typename R,
typename T>
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));
80 return llvm::join(Names.begin(), Names.end(),
", ");
83 SourceLocation getLocationForEndOfToken(
const ASTContext &Context,
85 return Lexer::getLocForEndOfToken(Location, 0, Context.getSourceManager(),
86 Context.getLangOpts());
109 struct IntializerInsertion {
111 const CXXCtorInitializer *
Where)
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;
121 case InitializerPlacement::New:
123 Context, Constructor.getBody()->getLocStart())
126 case InitializerPlacement::Before:
128 Context, Where->getSourceRange().getBegin())
131 case InitializerPlacement::After:
132 Location = Where->getRParenLoc();
135 return getLocationForEndOfToken(Context, Location);
138 std::string codeToInsert()
const {
139 assert(!
Initializers.empty() &&
"No initializers to insert");
141 llvm::raw_string_ostream Stream(Code);
145 case InitializerPlacement::New:
146 Stream <<
" : " << joined <<
"()";
148 case InitializerPlacement::Before:
149 Stream <<
" " << joined <<
"(),";
151 case InitializerPlacement::After:
152 Stream <<
", " << joined <<
"()";
164 const RecordDecl *getCanonicalRecordDecl(
const QualType &Type) {
165 if (
const auto *RT = Type.getCanonicalType()->getAs<RecordType>())
166 return RT->getDecl();
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);
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);
186 const auto *InitDecl =
187 Init->isAnyMemberInitializer()
188 ?
static_cast<const NamedDecl *
>(Init->getAnyMember())
189 : Init->getBaseClass()->getAsCXXRecordDecl();
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));
199 Insertions.emplace_back(InitializerPlacement::After, Init);
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));
215 void getInitializationsInOrder(
const CXXRecordDecl &ClassDecl,
216 SmallVectorImpl<const NamedDecl *> &Decls) {
218 for (
const auto &Base : ClassDecl.bases()) {
220 if (
const NamedDecl *Decl = getCanonicalRecordDecl(Base.getType())) {
221 Decls.emplace_back(Decl);
224 forEachField(ClassDecl, ClassDecl.fields(),
225 [&](
const FieldDecl *F) { Decls.push_back(F); });
228 template <
typename T>
229 void fixInitializerList(
const ASTContext &Context, DiagnosticBuilder &Diag,
230 const CXXConstructorDecl *Ctor,
231 const SmallPtrSetImpl<const T *> &DeclsToInit) {
233 if (Ctor->getLocStart().isMacroID())
236 SmallVector<const NamedDecl *, 16> OrderedDecls;
237 getInitializationsInOrder(*Ctor->getParent(), OrderedDecls);
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());
249 ProTypeMemberInitCheck::ProTypeMemberInitCheck(StringRef
Name,
252 IgnoreArrays(Options.get(
"IgnoreArrays", false)) {}
258 auto IsUserProvidedNonDelegatingConstructor =
259 allOf(isUserProvided(),
260 unless(anyOf(isInstantiated(), isDelegatingConstructor())));
261 auto IsNonTrivialDefaultConstructor = allOf(
262 isDefaultConstructor(), unless(isUserProvided()),
265 cxxConstructorDecl(isDefinition(),
266 anyOf(IsUserProvidedNonDelegatingConstructor,
267 IsNonTrivialDefaultConstructor))
275 isDefinition(), unless(isInstantiated()), hasDefaultConstructor(),
276 anyOf(has(cxxConstructorDecl(isDefaultConstructor(), isDefaulted(),
277 unless(isImplicit()))),
278 unless(has(cxxConstructorDecl()))),
283 auto HasDefaultConstructor = hasInitializer(
284 cxxConstructExpr(unless(requiresZeroInitialization()),
285 hasDeclaration(cxxConstructorDecl(
286 isDefaultConstructor(), unless(isUserProvided())))));
288 varDecl(isDefinition(), HasDefaultConstructor,
289 hasAutomaticStorageDuration(),
290 hasType(recordDecl(has(fieldDecl()),
297 if (
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor")) {
299 if (!Ctor->getBody())
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);
320 if (T->isIncompleteArrayType())
323 while (
const ConstantArrayType *ArrayT = Context.getAsConstantArrayType(T)) {
324 if (!ArrayT->getSize())
327 T = ArrayT->getElementType();
333 static bool isEmpty(ASTContext &Context,
const QualType &Type) {
334 if (
const CXXRecordDecl *ClassDecl = Type->getAsCXXRecordDecl()) {
335 return ClassDecl->isEmpty();
340 void ProTypeMemberInitCheck::checkMissingMemberInitializer(
341 ASTContext &Context,
const CXXRecordDecl &ClassDecl,
342 const CXXConstructorDecl *Ctor) {
343 bool IsUnion = ClassDecl.isUnion();
345 if (IsUnion && ClassDecl.hasInClassInitializer())
349 SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
350 forEachField(ClassDecl, ClassDecl.fields(), [&](
const FieldDecl *F) {
351 if (!F->hasInClassInitializer() &&
354 !
isEmpty(Context, F->getType()) && !F->isUnnamedBitfield())
355 FieldsToInit.insert(F);
357 if (FieldsToInit.empty())
361 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
364 if (Init->isAnyMemberInitializer() && Init->isWritten()) {
367 FieldsToInit.erase(Init->getAnyMember());
370 removeFieldsInitializedInBody(*Ctor->getBody(), Context, FieldsToInit);
375 SmallVector<const FieldDecl *, 16> OrderedFields;
376 forEachField(ClassDecl, ClassDecl.fields(),
377 [&](
const FieldDecl *F) { OrderedFields.push_back(F); });
380 SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
381 forEachField(ClassDecl, FieldsToInit,
382 [&](
const FieldDecl *F) { AllFieldsToInit.insert(F); });
383 if (AllFieldsToInit.empty())
386 DiagnosticBuilder Diag =
387 diag(Ctor ? Ctor->getLocStart() : ClassDecl.getLocation(),
389 ?
"union constructor should initialize one of these fields: %0" 390 :
"constructor does not initialize these fields: %0")
391 << toCommaSeparatedString(OrderedFields, AllFieldsToInit);
395 if (Ctor && Ctor->getLocStart().isMacroID())
400 SmallPtrSet<const FieldDecl *, 16> FieldsToFix;
401 SmallPtrSet<const RecordDecl *, 4> UnionsSeen;
402 forEachField(ClassDecl, OrderedFields, [&](
const FieldDecl *F) {
403 if (!FieldsToInit.count(F))
408 if (F->getType()->isEnumeralType() ||
411 if (!F->getParent()->isUnion() || UnionsSeen.insert(F->getParent()).second)
412 FieldsToFix.insert(F);
414 if (FieldsToFix.empty())
418 if (Context.getLangOpts().CPlusPlus11) {
419 for (
const FieldDecl *Field : FieldsToFix) {
420 Diag << FixItHint::CreateInsertion(
421 getLocationForEndOfToken(Context, Field->getSourceRange().getEnd()),
426 fixInitializerList(Context, Diag, Ctor, FieldsToFix);
430 void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
431 const ASTContext &Context,
const CXXRecordDecl &ClassDecl,
432 const CXXConstructorDecl *Ctor) {
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() &&
443 BasesToInit.insert(BaseClassDecl);
447 if (BasesToInit.empty())
452 if (Ctor->isImplicit())
455 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
456 if (Init->isBaseInitializer() && Init->isWritten())
457 BasesToInit.erase(Init->getBaseClass()->getAsCXXRecordDecl());
461 if (BasesToInit.empty())
464 DiagnosticBuilder Diag =
465 diag(Ctor ? Ctor->getLocStart() : ClassDecl.getLocation(),
466 "constructor does not initialize these bases: %0")
467 << toCommaSeparatedString(AllBases, BasesToInit);
470 fixInitializerList(Context, Diag, Ctor, BasesToInit);
473 void ProTypeMemberInitCheck::checkUninitializedTrivialType(
474 const ASTContext &Context,
const VarDecl *Var) {
475 DiagnosticBuilder Diag =
476 diag(Var->getLocStart(),
"uninitialized record type: %0") << Var;
478 Diag << FixItHint::CreateInsertion(
479 getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()),
480 Context.getLangOpts().CPlusPlus11 ?
"{}" :
" = {}");
AST_MATCHER(BinaryOperator, isAssignmentOperator)
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.
LangOptions getLangOpts() const
Returns the language options from the context.
static bool isIncompleteOrZeroLengthArrayType(ASTContext &Context, QualType T)
Token getPreviousToken(const ASTContext &Context, SourceLocation Location, 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.
static bool isEmpty(ASTContext &Context, const QualType &Type)
Base class for all clang-tidy checks.
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...
bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context)
Returns true if Type is trivially default constructible.
const CXXCtorInitializer * Where
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)
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.