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 Constructor.getBody()->getBeginLoc(),
124 Context.getSourceManager(), Context.getLangOpts())
127 case InitializerPlacement::Before:
129 Where->getSourceRange().getBegin(),
130 Context.getSourceManager(), Context.getLangOpts())
133 case InitializerPlacement::After:
134 Location = Where->getRParenLoc();
137 return getLocationForEndOfToken(Context, Location);
140 std::string codeToInsert()
const {
141 assert(!
Initializers.empty() &&
"No initializers to insert");
143 llvm::raw_string_ostream Stream(Code);
147 case InitializerPlacement::New:
148 Stream <<
" : " << joined <<
"()";
150 case InitializerPlacement::Before:
151 Stream <<
" " << joined <<
"(),";
153 case InitializerPlacement::After:
154 Stream <<
", " << joined <<
"()";
166 const RecordDecl *getCanonicalRecordDecl(
const QualType &Type) {
167 if (
const auto *RT = Type.getCanonicalType()->getAs<RecordType>())
168 return RT->getDecl();
172 template <
typename R,
typename T>
173 SmallVector<IntializerInsertion, 16>
174 computeInsertions(
const CXXConstructorDecl::init_const_range &Inits,
175 const R &OrderedDecls,
176 const SmallPtrSetImpl<const T *> &DeclsToInit) {
177 SmallVector<IntializerInsertion, 16> Insertions;
178 Insertions.emplace_back(InitializerPlacement::New,
nullptr);
180 typename R::const_iterator Decl = std::begin(OrderedDecls);
181 for (
const CXXCtorInitializer *Init : Inits) {
182 if (Init->isWritten()) {
183 if (Insertions.size() == 1)
184 Insertions.emplace_back(InitializerPlacement::Before, Init);
188 const auto *InitDecl =
189 Init->isAnyMemberInitializer()
190 ?
static_cast<const NamedDecl *
>(Init->getAnyMember())
191 : Init->getBaseClass()->getAsCXXRecordDecl();
194 for (; Decl != std::end(OrderedDecls) && *Decl != InitDecl; ++Decl) {
195 if (
const auto *
D = dyn_cast<T>(*Decl)) {
196 if (DeclsToInit.count(
D) > 0)
197 Insertions.back().Initializers.emplace_back(getName(
D));
201 Insertions.emplace_back(InitializerPlacement::After, Init);
206 for (; Decl != std::end(OrderedDecls); ++Decl) {
207 if (
const auto *
D = dyn_cast<T>(*Decl)) {
208 if (DeclsToInit.count(
D) > 0)
209 Insertions.back().Initializers.emplace_back(getName(
D));
217 void getInitializationsInOrder(
const CXXRecordDecl &ClassDecl,
218 SmallVectorImpl<const NamedDecl *> &Decls) {
220 for (
const auto &Base : ClassDecl.bases()) {
222 if (
const NamedDecl *Decl = getCanonicalRecordDecl(Base.getType())) {
223 Decls.emplace_back(Decl);
226 forEachField(ClassDecl, ClassDecl.fields(),
227 [&](
const FieldDecl *F) { Decls.push_back(F); });
230 template <
typename T>
231 void fixInitializerList(
const ASTContext &Context, DiagnosticBuilder &Diag,
232 const CXXConstructorDecl *Ctor,
233 const SmallPtrSetImpl<const T *> &DeclsToInit) {
235 if (Ctor->getBeginLoc().isMacroID())
238 SmallVector<const NamedDecl *, 16> OrderedDecls;
239 getInitializationsInOrder(*Ctor->getParent(), OrderedDecls);
241 for (
const auto &Insertion :
242 computeInsertions(Ctor->inits(), OrderedDecls, DeclsToInit)) {
243 if (!Insertion.Initializers.empty())
244 Diag << FixItHint::CreateInsertion(Insertion.getLocation(Context, *Ctor),
245 Insertion.codeToInsert());
251 ProTypeMemberInitCheck::ProTypeMemberInitCheck(StringRef
Name,
254 IgnoreArrays(Options.get(
"IgnoreArrays", 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);
322 if (T->isIncompleteArrayType())
325 while (
const ConstantArrayType *ArrayT = Context.getAsConstantArrayType(T)) {
326 if (!ArrayT->getSize())
329 T = ArrayT->getElementType();
335 static bool isEmpty(ASTContext &Context,
const QualType &Type) {
336 if (
const CXXRecordDecl *ClassDecl = Type->getAsCXXRecordDecl()) {
337 return ClassDecl->isEmpty();
342 void ProTypeMemberInitCheck::checkMissingMemberInitializer(
343 ASTContext &Context,
const CXXRecordDecl &ClassDecl,
344 const CXXConstructorDecl *Ctor) {
345 bool IsUnion = ClassDecl.isUnion();
347 if (IsUnion && ClassDecl.hasInClassInitializer())
351 SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
352 forEachField(ClassDecl, ClassDecl.fields(), [&](
const FieldDecl *F) {
353 if (!F->hasInClassInitializer() &&
356 !
isEmpty(Context, F->getType()) && !F->isUnnamedBitfield())
357 FieldsToInit.insert(F);
359 if (FieldsToInit.empty())
363 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
366 if (Init->isAnyMemberInitializer() && Init->isWritten()) {
369 FieldsToInit.erase(Init->getAnyMember());
372 removeFieldsInitializedInBody(*Ctor->getBody(), Context, FieldsToInit);
377 SmallVector<const FieldDecl *, 16> OrderedFields;
378 forEachField(ClassDecl, ClassDecl.fields(),
379 [&](
const FieldDecl *F) { OrderedFields.push_back(F); });
382 SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
383 forEachField(ClassDecl, FieldsToInit,
384 [&](
const FieldDecl *F) { AllFieldsToInit.insert(F); });
385 if (AllFieldsToInit.empty())
388 DiagnosticBuilder Diag =
389 diag(Ctor ? Ctor->getBeginLoc() : ClassDecl.getLocation(),
391 ?
"union constructor should initialize one of these fields: %0" 392 :
"constructor does not initialize these fields: %0")
393 << toCommaSeparatedString(OrderedFields, AllFieldsToInit);
397 if (Ctor && Ctor->getBeginLoc().isMacroID())
402 SmallPtrSet<const FieldDecl *, 16> FieldsToFix;
403 SmallPtrSet<const RecordDecl *, 4> UnionsSeen;
404 forEachField(ClassDecl, OrderedFields, [&](
const FieldDecl *F) {
405 if (!FieldsToInit.count(F))
410 if (F->getType()->isEnumeralType() ||
413 if (!F->getParent()->isUnion() || UnionsSeen.insert(F->getParent()).second)
414 FieldsToFix.insert(F);
416 if (FieldsToFix.empty())
420 if (Context.getLangOpts().CPlusPlus11) {
421 for (
const FieldDecl *Field : FieldsToFix) {
422 Diag << FixItHint::CreateInsertion(
423 getLocationForEndOfToken(Context, Field->getSourceRange().getEnd()),
428 fixInitializerList(Context, Diag, Ctor, FieldsToFix);
432 void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
433 const ASTContext &Context,
const CXXRecordDecl &ClassDecl,
434 const CXXConstructorDecl *Ctor) {
437 SmallVector<const RecordDecl *, 4> AllBases;
438 SmallPtrSet<const RecordDecl *, 4> BasesToInit;
439 for (
const CXXBaseSpecifier &Base : ClassDecl.bases()) {
440 if (
const auto *BaseClassDecl = getCanonicalRecordDecl(Base.getType())) {
441 AllBases.emplace_back(BaseClassDecl);
442 if (!BaseClassDecl->field_empty() &&
445 BasesToInit.insert(BaseClassDecl);
449 if (BasesToInit.empty())
454 if (Ctor->isImplicit())
457 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
458 if (Init->isBaseInitializer() && Init->isWritten())
459 BasesToInit.erase(Init->getBaseClass()->getAsCXXRecordDecl());
463 if (BasesToInit.empty())
466 DiagnosticBuilder Diag =
467 diag(Ctor ? Ctor->getBeginLoc() : ClassDecl.getLocation(),
468 "constructor does not initialize these bases: %0")
469 << toCommaSeparatedString(AllBases, BasesToInit);
472 fixInitializerList(Context, Diag, Ctor, BasesToInit);
475 void ProTypeMemberInitCheck::checkUninitializedTrivialType(
476 const ASTContext &Context,
const VarDecl *Var) {
477 DiagnosticBuilder Diag =
478 diag(Var->getBeginLoc(),
"uninitialized record type: %0") << Var;
480 Diag << FixItHint::CreateInsertion(
481 getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()),
482 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(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.
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
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
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.