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