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"
19 using namespace clang::ast_matchers;
20 using namespace clang::tidy::matchers;
21 using llvm::SmallPtrSet;
22 using llvm::SmallPtrSetImpl;
26 namespace cppcoreguidelines {
34 template <
typename T,
typename Func>
35 void forEachField(
const RecordDecl *Record,
const T &Fields,
36 bool OneFieldPerUnion, 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(), OneFieldPerUnion, Fn);
45 if (OneFieldPerUnion && Record->isUnion())
50 void removeFieldsInitializedInBody(
51 const Stmt &Stmt, ASTContext &
Context,
52 SmallPtrSetImpl<const FieldDecl *> &FieldDecls) {
54 match(findAll(binaryOperator(
56 hasLHS(memberExpr(member(fieldDecl().bind(
"fieldDecl")))))),
58 for (
const auto &Match : Matches)
59 FieldDecls.erase(Match.getNodeAs<FieldDecl>(
"fieldDecl"));
62 StringRef getName(
const FieldDecl *Field) {
return Field->getName(); }
64 StringRef getName(
const RecordDecl *Record) {
66 if (
const TypedefNameDecl *Typedef = Record->getTypedefNameForAnonDecl())
67 return Typedef->getName();
68 return Record->getName();
73 template <
typename R,
typename T>
75 toCommaSeparatedString(
const R &OrderedDecls,
76 const SmallPtrSetImpl<const T *> &DeclsToInit) {
77 SmallVector<StringRef, 16> Names;
78 for (
const T *Decl : OrderedDecls) {
79 if (DeclsToInit.count(Decl))
80 Names.emplace_back(getName(Decl));
82 return llvm::join(Names.begin(), Names.end(),
", ");
85 SourceLocation getLocationForEndOfToken(
const ASTContext &Context,
87 return Lexer::getLocForEndOfToken(Location, 0, Context.getSourceManager(),
88 Context.getLangOpts());
92 enum class InitializerPlacement {
111 struct IntializerInsertion {
112 IntializerInsertion(InitializerPlacement
Placement,
113 const CXXCtorInitializer *
Where)
114 : Placement(Placement), Where(Where) {}
116 SourceLocation getLocation(
const ASTContext &Context,
117 const CXXConstructorDecl &Constructor)
const {
118 assert((
Where !=
nullptr ||
Placement == InitializerPlacement::New) &&
119 "Location should be relative to an existing initializer or this "
120 "insertion represents a new initializer list.");
121 SourceLocation Location;
123 case InitializerPlacement::New:
125 Context, Constructor.getBody()->getLocStart())
128 case InitializerPlacement::Before:
130 Context,
Where->getSourceRange().getBegin())
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 T *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 T *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(),
false,
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->getLocStart().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))
272 auto HasDefaultConstructor = hasInitializer(
273 cxxConstructExpr(unless(requiresZeroInitialization()),
274 hasDeclaration(cxxConstructorDecl(
275 isDefaultConstructor(), unless(isUserProvided())))));
277 varDecl(isDefinition(), HasDefaultConstructor,
278 hasAutomaticStorageDuration(),
279 hasType(recordDecl(has(fieldDecl()),
286 if (
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>(
"ctor")) {
288 if (!Ctor->getBody())
290 checkMissingMemberInitializer(*Result.Context, Ctor);
291 checkMissingBaseClassInitializer(*Result.Context, Ctor);
292 }
else if (
const auto *Var = Result.Nodes.getNodeAs<VarDecl>(
"var")) {
293 checkUninitializedTrivialType(*Result.Context, Var);
301 void ProTypeMemberInitCheck::checkMissingMemberInitializer(
302 ASTContext &Context,
const CXXConstructorDecl *Ctor) {
303 const CXXRecordDecl *ClassDecl = Ctor->getParent();
304 bool IsUnion = ClassDecl->isUnion();
306 if (IsUnion && ClassDecl->hasInClassInitializer())
310 SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
311 forEachField(ClassDecl, ClassDecl->fields(),
false, [&](
const FieldDecl *F) {
312 if (!F->hasInClassInitializer() &&
315 FieldsToInit.insert(F);
317 if (FieldsToInit.empty())
320 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
323 if (Init->isAnyMemberInitializer() && Init->isWritten()) {
326 FieldsToInit.erase(Init->getAnyMember());
329 removeFieldsInitializedInBody(*Ctor->getBody(), Context, FieldsToInit);
333 SmallVector<const FieldDecl *, 16> OrderedFields;
334 forEachField(ClassDecl, ClassDecl->fields(),
false,
335 [&](
const FieldDecl *F) { OrderedFields.push_back(F); });
338 SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
339 forEachField(ClassDecl, FieldsToInit,
false,
340 [&](
const FieldDecl *F) { AllFieldsToInit.insert(F); });
341 if (AllFieldsToInit.empty())
344 DiagnosticBuilder Diag =
345 diag(Ctor->getLocStart(),
347 ?
"union constructor should initialize one of these fields: %0"
348 :
"constructor does not initialize these fields: %0")
349 << toCommaSeparatedString(OrderedFields, AllFieldsToInit);
352 if (Ctor->getLocStart().isMacroID())
357 SmallPtrSet<const FieldDecl *, 16> FieldsToFix;
358 forEachField(ClassDecl, FieldsToInit,
true, [&](
const FieldDecl *F) {
360 if (!F->getType()->isEnumeralType())
361 FieldsToFix.insert(F);
363 if (FieldsToFix.empty())
367 if (Context.getLangOpts().CPlusPlus11) {
368 for (
const FieldDecl *Field : FieldsToFix) {
369 Diag << FixItHint::CreateInsertion(
370 getLocationForEndOfToken(Context, Field->getSourceRange().getEnd()),
375 fixInitializerList(Context, Diag, Ctor, FieldsToFix);
379 void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
380 const ASTContext &Context,
const CXXConstructorDecl *Ctor) {
381 const CXXRecordDecl *ClassDecl = Ctor->getParent();
384 SmallVector<const RecordDecl *, 4> AllBases;
385 SmallPtrSet<const RecordDecl *, 4> BasesToInit;
386 for (
const CXXBaseSpecifier &Base : ClassDecl->bases()) {
387 if (
const auto *BaseClassDecl = getCanonicalRecordDecl(Base.getType())) {
388 AllBases.emplace_back(BaseClassDecl);
389 if (!BaseClassDecl->field_empty() &&
392 BasesToInit.insert(BaseClassDecl);
396 if (BasesToInit.empty())
400 for (
const CXXCtorInitializer *Init : Ctor->inits()) {
401 if (Init->isBaseInitializer() && Init->isWritten())
402 BasesToInit.erase(Init->getBaseClass()->getAsCXXRecordDecl());
405 if (BasesToInit.empty())
408 DiagnosticBuilder Diag =
409 diag(Ctor->getLocStart(),
410 "constructor does not initialize these bases: %0")
411 << toCommaSeparatedString(AllBases, BasesToInit);
413 fixInitializerList(Context, Diag, Ctor, BasesToInit);
416 void ProTypeMemberInitCheck::checkUninitializedTrivialType(
417 const ASTContext &Context,
const VarDecl *Var) {
418 DiagnosticBuilder Diag =
419 diag(Var->getLocStart(),
"uninitialized record type: %0") << Var;
421 Diag << FixItHint::CreateInsertion(
422 getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()),
423 Context.getLangOpts().CPlusPlus11 ?
"{}" :
" = {}");
LangOptions getLangOpts() const
Returns the language options from the context.
std::unique_ptr< ast_matchers::MatchFinder > Finder
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
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
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.
std::map< std::string, std::string > OptionMap
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
InitializerPlacement Placement
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Token getPreviousNonCommentToken(const ASTContext &Context, SourceLocation Location)
Returns previous non-comment token skipping over any comment text or tok::unknown if not found...