clang-tools 23.0.0git
ProTypeMemberInitCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
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"
17
18using namespace clang::ast_matchers;
19using namespace clang::tidy::matchers;
20using llvm::SmallPtrSet;
21using llvm::SmallPtrSetImpl;
22
24
25namespace {
26
27AST_MATCHER(CXXRecordDecl, hasDefaultConstructor) {
28 return Node.hasDefaultConstructor();
29}
30
31} // namespace
32
33// Iterate over all the fields in a record type, both direct and indirect (e.g.
34// if the record contains an anonymous struct).
35template <typename T, typename Func>
36static void forEachField(const RecordDecl &Record, const T &Fields,
37 const 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
48template <typename T, typename Func>
49static void forEachFieldWithFilter(const RecordDecl &Record, const T &Fields,
50 bool &AnyMemberHasInitPerUnion,
51 const Func &Fn) {
52 for (const FieldDecl *F : Fields) {
53 if (F->isAnonymousStructOrUnion()) {
54 if (const CXXRecordDecl *R = F->getType()->getAsCXXRecordDecl()) {
55 AnyMemberHasInitPerUnion = false;
56 forEachFieldWithFilter(*R, R->fields(), AnyMemberHasInitPerUnion, Fn);
57 }
58 } else {
59 Fn(F);
60 }
61 if (Record.isUnion() && AnyMemberHasInitPerUnion)
62 break;
63 }
64}
65
66static void
67removeFieldInitialized(const FieldDecl *M,
68 SmallPtrSetImpl<const FieldDecl *> &FieldDecls) {
69 const RecordDecl *R = M->getParent();
70 if (R && R->isUnion()) {
71 // Erase all members in a union if any member of it is initialized.
72 for (const auto *F : R->fields())
73 FieldDecls.erase(F);
74 } else
75 FieldDecls.erase(M);
76}
77
78static void
79removeFieldsInitializedInBody(const Stmt &Stmt, ASTContext &Context,
80 SmallPtrSetImpl<const FieldDecl *> &FieldDecls) {
81 auto Matches =
82 match(findAll(binaryOperator(
83 hasOperatorName("="),
84 hasLHS(memberExpr(member(fieldDecl().bind("fieldDecl")))))),
85 Stmt, Context);
86 for (const auto &Match : Matches)
87 removeFieldInitialized(Match.getNodeAs<FieldDecl>("fieldDecl"), FieldDecls);
88}
89
90static StringRef getName(const FieldDecl *Field) { return Field->getName(); }
91
92static StringRef getName(const RecordDecl *Record) {
93 // Get the typedef name if this is a C-style anonymous struct and typedef.
94 if (const TypedefNameDecl *Typedef = Record->getTypedefNameForAnonDecl())
95 return Typedef->getName();
96 return Record->getName();
97}
98
99// Creates comma separated list of decls requiring initialization in order of
100// declaration.
101template <typename R, typename T>
102static std::string
103toCommaSeparatedString(const R &OrderedDecls,
104 const SmallPtrSetImpl<const T *> &DeclsToInit) {
105 SmallVector<StringRef, 16> Names;
106 for (const T *Decl : OrderedDecls)
107 if (DeclsToInit.contains(Decl))
108 Names.emplace_back(getName(Decl));
109 return llvm::join(Names.begin(), Names.end(), ", ");
110}
111
112static SourceLocation getLocationForEndOfToken(const ASTContext &Context,
113 SourceLocation Location) {
114 return Lexer::getLocForEndOfToken(Location, 0, Context.getSourceManager(),
115 Context.getLangOpts());
116}
117
118namespace {
119
120// There are 3 kinds of insertion placements:
121enum class InitializerPlacement {
122 // 1. The fields are inserted after an existing CXXCtorInitializer stored in
123 // Where. This will be the case whenever there is a written initializer before
124 // the fields available.
125 After,
126
127 // 2. The fields are inserted before the first existing initializer stored in
128 // Where.
129 Before,
130
131 // 3. There are no written initializers and the fields will be inserted before
132 // the constructor's body creating a new initializer list including the ':'.
133 New
134};
135
136// An InitializerInsertion contains a list of fields and/or base classes to
137// insert into the initializer list of a constructor. We use this to ensure
138// proper absolute ordering according to the class declaration relative to the
139// (perhaps improper) ordering in the existing initializer list, if any.
140struct InitializerInsertion {
141 InitializerInsertion(InitializerPlacement Placement,
142 const CXXCtorInitializer *Where)
143 : Placement(Placement), Where(Where) {}
144
145 SourceLocation getLocation(const ASTContext &Context,
146 const CXXConstructorDecl &Constructor) const {
147 assert((Where != nullptr || Placement == InitializerPlacement::New) &&
148 "Location should be relative to an existing initializer or this "
149 "insertion represents a new initializer list.");
150 SourceLocation Location;
151 switch (Placement) {
152 case InitializerPlacement::New: {
153 const std::optional<Token> Tok = utils::lexer::getPreviousToken(
154 Constructor.getBody()->getBeginLoc(), Context.getSourceManager(),
155 Context.getLangOpts());
156 Location = Tok ? Tok->getLocation() : SourceLocation{};
157 break;
158 }
159 case InitializerPlacement::Before: {
160 const std::optional<Token> Tok = utils::lexer::getPreviousToken(
161 Where->getSourceRange().getBegin(), Context.getSourceManager(),
162 Context.getLangOpts());
163 Location = Tok ? Tok->getLocation() : SourceLocation{};
164 break;
165 }
166 case InitializerPlacement::After:
167 Location = Where->getRParenLoc();
168 break;
169 }
170 return getLocationForEndOfToken(Context, Location);
171 }
172
173 std::string codeToInsert() const {
174 assert(!Initializers.empty() && "No initializers to insert");
175 std::string Code;
176 llvm::raw_string_ostream Stream(Code);
177 const std::string Joined =
178 llvm::join(Initializers.begin(), Initializers.end(), "(), ");
179 switch (Placement) {
180 case InitializerPlacement::New:
181 Stream << " : " << Joined << "()";
182 break;
183 case InitializerPlacement::Before:
184 Stream << " " << Joined << "(),";
185 break;
186 case InitializerPlacement::After:
187 Stream << ", " << Joined << "()";
188 break;
189 }
190 return Stream.str();
191 }
192
193 InitializerPlacement Placement;
194 const CXXCtorInitializer *Where;
195 SmallVector<std::string, 4> Initializers;
196};
197
198} // namespace
199
200// Convenience utility to get a RecordDecl from a QualType.
201static const RecordDecl *getCanonicalRecordDecl(const QualType &Type) {
202 if (const auto *RT = Type->getAsCanonical<RecordType>())
203 return RT->getDecl();
204 return nullptr;
205}
206
207template <typename R, typename T>
208static SmallVector<InitializerInsertion, 16>
209computeInsertions(const CXXConstructorDecl::init_const_range &Inits,
210 const R &OrderedDecls,
211 const SmallPtrSetImpl<const T *> &DeclsToInit) {
212 SmallVector<InitializerInsertion, 16> Insertions;
213 Insertions.emplace_back(InitializerPlacement::New, nullptr);
214
215 typename R::const_iterator Decl = std::begin(OrderedDecls);
216 for (const CXXCtorInitializer *Init : Inits) {
217 if (Init->isWritten()) {
218 if (Insertions.size() == 1)
219 Insertions.emplace_back(InitializerPlacement::Before, Init);
220
221 // Gets either the field or base class being initialized by the provided
222 // initializer.
223 const auto *InitDecl =
224 Init->isAnyMemberInitializer()
225 ? static_cast<const NamedDecl *>(Init->getAnyMember())
226 : Init->getBaseClass()->getAsCXXRecordDecl();
227
228 // Add all fields between current field up until the next initializer.
229 for (; Decl != std::end(OrderedDecls) && *Decl != InitDecl; ++Decl) {
230 if (const auto *D = dyn_cast<T>(*Decl)) {
231 if (DeclsToInit.contains(D))
232 Insertions.back().Initializers.emplace_back(getName(D));
233 }
234 }
235
236 Insertions.emplace_back(InitializerPlacement::After, Init);
237 }
238 }
239
240 // Add remaining decls that require initialization.
241 for (; Decl != std::end(OrderedDecls); ++Decl) {
242 if (const auto *D = dyn_cast<T>(*Decl)) {
243 if (DeclsToInit.contains(D))
244 Insertions.back().Initializers.emplace_back(getName(D));
245 }
246 }
247 return Insertions;
248}
249
250// Gets the list of bases and members that could possibly be initialized, in
251// order as they appear in the class declaration.
252static void
253getInitializationsInOrder(const CXXRecordDecl &ClassDecl,
254 SmallVectorImpl<const NamedDecl *> &Decls) {
255 Decls.clear();
256 for (const auto &Base : ClassDecl.bases()) {
257 // Decl may be null if the base class is a template parameter.
258 if (const NamedDecl *Decl = getCanonicalRecordDecl(Base.getType()))
259 Decls.emplace_back(Decl);
260 }
261 forEachField(ClassDecl, ClassDecl.fields(),
262 [&](const FieldDecl *F) { Decls.push_back(F); });
263}
264
265template <typename T>
266static void fixInitializerList(const ASTContext &Context,
267 DiagnosticBuilder &Diag,
268 const CXXConstructorDecl *Ctor,
269 const SmallPtrSetImpl<const T *> &DeclsToInit) {
270 // Do not propose fixes in macros since we cannot place them correctly.
271 if (Ctor->getBeginLoc().isMacroID())
272 return;
273
274 SmallVector<const NamedDecl *, 16> OrderedDecls;
275 getInitializationsInOrder(*Ctor->getParent(), OrderedDecls);
276
277 for (const auto &Insertion :
278 computeInsertions(Ctor->inits(), OrderedDecls, DeclsToInit)) {
279 if (!Insertion.Initializers.empty())
280 Diag << FixItHint::CreateInsertion(Insertion.getLocation(Context, *Ctor),
281 Insertion.codeToInsert());
282 }
283}
284
286 ClangTidyContext *Context)
287 : ClangTidyCheck(Name, Context),
288 IgnoreArrays(Options.get("IgnoreArrays", false)),
289 UseAssignment(Options.get("UseAssignment", false)) {}
290
292 auto IsUserProvidedNonDelegatingConstructor =
293 allOf(isUserProvided(), unless(isInstantiated()),
294 unless(isDelegatingConstructor()),
295 ofClass(cxxRecordDecl().bind("parent")),
296 unless(hasAnyConstructorInitializer(cxxCtorInitializer(
297 isWritten(), unless(isMemberInitializer()),
298 hasTypeLoc(loc(
299 qualType(hasDeclaration(equalsBoundNode("parent")))))))));
300
301 auto IsNonTrivialDefaultConstructor = allOf(
302 isDefaultConstructor(), unless(isUserProvided()),
303 hasParent(cxxRecordDecl(unless(isTriviallyDefaultConstructible()))));
304 Finder->addMatcher(
305 cxxConstructorDecl(isDefinition(),
306 anyOf(IsUserProvidedNonDelegatingConstructor,
307 IsNonTrivialDefaultConstructor))
308 .bind("ctor"),
309 this);
310
311 // Match classes with a default constructor that is defaulted or is not in the
312 // AST.
313 Finder->addMatcher(
314 cxxRecordDecl(
315 isDefinition(), unless(isInstantiated()), hasDefaultConstructor(),
316 anyOf(has(cxxConstructorDecl(isDefaultConstructor(), isDefaulted(),
317 unless(isImplicit()))),
318 unless(has(cxxConstructorDecl()))),
319 unless(isTriviallyDefaultConstructible()))
320 .bind("record"),
321 this);
322
323 auto HasDefaultConstructor = hasInitializer(
324 cxxConstructExpr(unless(requiresZeroInitialization()),
325 hasDeclaration(cxxConstructorDecl(
326 isDefaultConstructor(), unless(isUserProvided())))));
327 Finder->addMatcher(
328 varDecl(isDefinition(), HasDefaultConstructor,
329 hasAutomaticStorageDuration(),
330 hasType(recordDecl(has(fieldDecl()),
331 isTriviallyDefaultConstructible())))
332 .bind("var"),
333 this);
334}
335
336void ProTypeMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
337 if (const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor")) {
338 // Skip declarations delayed by late template parsing without a body.
339 if (!Ctor->getBody())
340 return;
341 // Skip out-of-band explicitly defaulted special member functions
342 // (except the default constructor).
343 if (Ctor->isExplicitlyDefaulted() && !Ctor->isDefaultConstructor())
344 return;
345 checkMissingMemberInitializer(*Result.Context, *Ctor->getParent(), Ctor);
346 checkMissingBaseClassInitializer(*Result.Context, *Ctor->getParent(), Ctor);
347 } else if (const auto *Record =
348 Result.Nodes.getNodeAs<CXXRecordDecl>("record")) {
349 assert(Record->hasDefaultConstructor() &&
350 "Matched record should have a default constructor");
351 checkMissingMemberInitializer(*Result.Context, *Record, nullptr);
352 checkMissingBaseClassInitializer(*Result.Context, *Record, nullptr);
353 } else if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var")) {
354 checkUninitializedTrivialType(*Result.Context, Var);
355 }
356}
357
359 Options.store(Opts, "IgnoreArrays", IgnoreArrays);
360 Options.store(Opts, "UseAssignment", UseAssignment);
361}
362
363// FIXME: Copied from clang/lib/Sema/SemaDeclCXX.cpp.
364static bool isIncompleteOrZeroLengthArrayType(const ASTContext &Context,
365 QualType T) {
366 if (T->isIncompleteArrayType())
367 return true;
368
369 while (const ConstantArrayType *ArrayT = Context.getAsConstantArrayType(T)) {
370 if (!ArrayT->getSize())
371 return true;
372
373 T = ArrayT->getElementType();
374 }
375
376 return false;
377}
378
379static bool isEmpty(const ASTContext &Context, const QualType &Type) {
380 if (const CXXRecordDecl *ClassDecl = Type->getAsCXXRecordDecl())
381 return ClassDecl->isEmpty();
382 return isIncompleteOrZeroLengthArrayType(Context, Type);
383}
384
385static StringRef getInitializer(QualType QT, bool UseAssignment) {
386 static constexpr StringRef DefaultInitializer = "{}";
387 if (!UseAssignment)
388 return DefaultInitializer;
389
390 if (QT->isPointerType())
391 return " = nullptr";
392
393 const auto *BT = dyn_cast<BuiltinType>(QT.getCanonicalType().getTypePtr());
394 if (!BT)
395 return DefaultInitializer;
396
397 switch (BT->getKind()) {
398 case BuiltinType::Bool:
399 return " = false";
400 case BuiltinType::Float:
401 return " = 0.0F";
402 case BuiltinType::Double:
403 return " = 0.0";
404 case BuiltinType::LongDouble:
405 return " = 0.0L";
406 case BuiltinType::SChar:
407 case BuiltinType::Char_S:
408 case BuiltinType::WChar_S:
409 case BuiltinType::Char16:
410 case BuiltinType::Char32:
411 case BuiltinType::Short:
412 case BuiltinType::Int:
413 return " = 0";
414 case BuiltinType::UChar:
415 case BuiltinType::Char_U:
416 case BuiltinType::WChar_U:
417 case BuiltinType::UShort:
418 case BuiltinType::UInt:
419 return " = 0U";
420 case BuiltinType::Long:
421 return " = 0L";
422 case BuiltinType::ULong:
423 return " = 0UL";
424 case BuiltinType::LongLong:
425 return " = 0LL";
426 case BuiltinType::ULongLong:
427 return " = 0ULL";
428
429 default:
430 return DefaultInitializer;
431 }
432}
433
434static void
435computeFieldsToInit(const ASTContext &Context, const RecordDecl &Record,
436 bool IgnoreArrays,
437 SmallPtrSetImpl<const FieldDecl *> &FieldsToInit) {
438 bool AnyMemberHasInitPerUnion = false;
440 Record, Record.fields(), AnyMemberHasInitPerUnion,
441 [&](const FieldDecl *F) {
442 if (IgnoreArrays && F->getType()->isArrayType())
443 return;
444 if (F->hasInClassInitializer() && F->getParent()->isUnion()) {
445 AnyMemberHasInitPerUnion = true;
446 removeFieldInitialized(F, FieldsToInit);
447 }
448 if (!F->hasInClassInitializer() &&
450 Context) &&
451 !isEmpty(Context, F->getType()) && !F->isUnnamedBitField() &&
452 !AnyMemberHasInitPerUnion)
453 FieldsToInit.insert(F);
454 });
455}
456
457void ProTypeMemberInitCheck::checkMissingMemberInitializer(
458 ASTContext &Context, const CXXRecordDecl &ClassDecl,
459 const CXXConstructorDecl *Ctor) {
460 const bool IsUnion = ClassDecl.isUnion();
461
462 if (IsUnion && ClassDecl.hasInClassInitializer())
463 return;
464
465 // Gather all fields (direct and indirect) that need to be initialized.
466 SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
467 computeFieldsToInit(Context, ClassDecl, IgnoreArrays, FieldsToInit);
468 if (FieldsToInit.empty())
469 return;
470
471 if (Ctor) {
472 for (const CXXCtorInitializer *Init : Ctor->inits()) {
473 // Remove any fields that were explicitly written in the initializer list
474 // or in-class.
475 if (Init->isAnyMemberInitializer() && Init->isWritten()) {
476 if (IsUnion)
477 return; // We can only initialize one member of a union.
478 removeFieldInitialized(Init->getAnyMember(), FieldsToInit);
479 }
480 }
481 removeFieldsInitializedInBody(*Ctor->getBody(), Context, FieldsToInit);
482 }
483
484 // Collect all fields in order, both direct fields and indirect fields from
485 // anonymous record types.
486 SmallVector<const FieldDecl *, 16> OrderedFields;
487 forEachField(ClassDecl, ClassDecl.fields(),
488 [&](const FieldDecl *F) { OrderedFields.push_back(F); });
489
490 // Collect all the fields we need to initialize, including indirect fields.
491 // It only includes fields that have not been fixed
492 SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
493 forEachField(ClassDecl, FieldsToInit, [&](const FieldDecl *F) {
494 if (HasRecordClassMemberSet.insert(F).second)
495 AllFieldsToInit.insert(F);
496 });
497 if (FieldsToInit.empty())
498 return;
499
500 DiagnosticBuilder Diag =
501 diag(Ctor ? Ctor->getBeginLoc() : ClassDecl.getLocation(),
502 "%select{|union }0constructor %select{does not|should}0 initialize "
503 "%select{|one of }0these fields: %1")
504 << IsUnion << toCommaSeparatedString(OrderedFields, FieldsToInit);
505
506 if (AllFieldsToInit.empty())
507 return;
508
509 // Do not propose fixes for constructors in macros since we cannot place them
510 // correctly.
511 if (Ctor && Ctor->getBeginLoc().isMacroID())
512 return;
513
514 // Collect all fields but only suggest a fix for the first member of unions,
515 // as initializing more than one union member is an error.
516 SmallPtrSet<const FieldDecl *, 16> FieldsToFix;
517 bool AnyMemberHasInitPerUnion = false;
518 forEachFieldWithFilter(ClassDecl, ClassDecl.fields(),
519 AnyMemberHasInitPerUnion, [&](const FieldDecl *F) {
520 if (!FieldsToInit.contains(F))
521 return;
522 // Don't suggest fixes for enums because we don't
523 // know a good default. Don't suggest fixes for
524 // bitfields because in-class initialization is not
525 // possible until C++20.
526 if (F->getType()->isEnumeralType() ||
527 (!getLangOpts().CPlusPlus20 && F->isBitField()))
528 return;
529 FieldsToFix.insert(F);
530 AnyMemberHasInitPerUnion = true;
531 });
532 if (FieldsToFix.empty())
533 return;
534
535 // Use in-class initialization if possible.
536 if (Context.getLangOpts().CPlusPlus11) {
537 for (const FieldDecl *Field : FieldsToFix) {
538 Diag << FixItHint::CreateInsertion(
539 getLocationForEndOfToken(Context, Field->getSourceRange().getEnd()),
540 getInitializer(Field->getType(), UseAssignment));
541 }
542 } else if (Ctor) {
543 // Otherwise, rewrite the constructor's initializer list.
544 fixInitializerList(Context, Diag, Ctor, FieldsToFix);
545 }
546}
547
548void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
549 const ASTContext &Context, const CXXRecordDecl &ClassDecl,
550 const CXXConstructorDecl *Ctor) {
551 // Gather any base classes that need to be initialized.
552 SmallVector<const RecordDecl *, 4> AllBases;
553 SmallPtrSet<const RecordDecl *, 4> BasesToInit;
554 for (const CXXBaseSpecifier &Base : ClassDecl.bases()) {
555 if (const auto *BaseClassDecl = getCanonicalRecordDecl(Base.getType())) {
556 AllBases.emplace_back(BaseClassDecl);
557 if (!BaseClassDecl->field_empty() &&
559 Context))
560 BasesToInit.insert(BaseClassDecl);
561 }
562 }
563
564 if (BasesToInit.empty())
565 return;
566
567 // Remove any bases that were explicitly written in the initializer list.
568 if (Ctor) {
569 if (Ctor->isImplicit())
570 return;
571
572 for (const CXXCtorInitializer *Init : Ctor->inits())
573 if (Init->isBaseInitializer() && Init->isWritten())
574 BasesToInit.erase(Init->getBaseClass()->getAsCXXRecordDecl());
575 }
576
577 if (BasesToInit.empty())
578 return;
579
580 DiagnosticBuilder Diag =
581 diag(Ctor ? Ctor->getBeginLoc() : ClassDecl.getLocation(),
582 "constructor does not initialize these bases: %0")
583 << toCommaSeparatedString(AllBases, BasesToInit);
584
585 if (Ctor)
586 fixInitializerList(Context, Diag, Ctor, BasesToInit);
587}
588
589void ProTypeMemberInitCheck::checkUninitializedTrivialType(
590 const ASTContext &Context, const VarDecl *Var) {
591 // Verify that the record actually needs initialization
592 const CXXRecordDecl *Record = Var->getType()->getAsCXXRecordDecl();
593 if (!Record)
594 return;
595
596 SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
597 computeFieldsToInit(Context, *Record, IgnoreArrays, FieldsToInit);
598
599 if (FieldsToInit.empty())
600 return;
601
602 const DiagnosticBuilder Diag =
603 diag(Var->getBeginLoc(), "uninitialized record type: %0") << Var;
604
605 Diag << FixItHint::CreateInsertion(
606 getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()),
607 Context.getLangOpts().CPlusPlus11 ? "{}" : " = {}");
608}
609
610} // namespace clang::tidy::cppcoreguidelines
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
ProTypeMemberInitCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
llvm::SmallVector< uint64_t, 1024 > Record
static SourceLocation getLocationForEndOfToken(const ASTContext &Context, SourceLocation Location)
static void removeFieldsInitializedInBody(const Stmt &Stmt, ASTContext &Context, SmallPtrSetImpl< const FieldDecl * > &FieldDecls)
static std::string toCommaSeparatedString(const R &OrderedDecls, const SmallPtrSetImpl< const T * > &DeclsToInit)
static bool isEmpty(const ASTContext &Context, const QualType &Type)
static void removeFieldInitialized(const FieldDecl *M, SmallPtrSetImpl< const FieldDecl * > &FieldDecls)
static StringRef getName(const FieldDecl *Field)
static void fixInitializerList(const ASTContext &Context, DiagnosticBuilder &Diag, const CXXConstructorDecl *Ctor, const SmallPtrSetImpl< const T * > &DeclsToInit)
static StringRef getInitializer(QualType QT, bool UseAssignment)
static bool isIncompleteOrZeroLengthArrayType(const ASTContext &Context, QualType T)
static void forEachFieldWithFilter(const RecordDecl &Record, const T &Fields, bool &AnyMemberHasInitPerUnion, const Func &Fn)
static void getInitializationsInOrder(const CXXRecordDecl &ClassDecl, SmallVectorImpl< const NamedDecl * > &Decls)
static const RecordDecl * getCanonicalRecordDecl(const QualType &Type)
static void forEachField(const RecordDecl &Record, const T &Fields, const Func &Fn)
static SmallVector< InitializerInsertion, 16 > computeInsertions(const CXXConstructorDecl::init_const_range &Inits, const R &OrderedDecls, const SmallPtrSetImpl< const T * > &DeclsToInit)
static void computeFieldsToInit(const ASTContext &Context, const RecordDecl &Record, bool IgnoreArrays, SmallPtrSetImpl< const FieldDecl * > &FieldsToInit)
AST_MATCHER(BinaryOperator, isRelationalOperator)
std::optional< Token > getPreviousToken(SourceLocation Location, const SourceManager &SM, const LangOptions &LangOpts, bool SkipComments)
Returns previous token or std::nullopt if not found.
bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context)
Returns true if Type is trivially default constructible.
llvm::StringMap< ClangTidyValue > OptionMap