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