clang-tools 24.0.0git
UseNullptrCheck.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
9#include "UseNullptrCheck.h"
10#include "../utils/Matchers.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/AST/RecursiveASTVisitor.h"
14#include "clang/ASTMatchers/ASTMatchFinder.h"
15#include "clang/Lex/Lexer.h"
16
17using namespace clang;
18using namespace clang::ast_matchers;
19using namespace llvm;
20
21namespace clang::tidy::modernize {
22namespace {
23
24AST_MATCHER(Type, sugaredNullptrType) {
25 const Type *DesugaredType = Node.getUnqualifiedDesugaredType();
26 if (const auto *BT = dyn_cast<BuiltinType>(DesugaredType))
27 return BT->getKind() == BuiltinType::NullPtr;
28 return false;
29}
30
31AST_MATCHER(DecltypeType, decltypeTypeNullptrLiteral) {
32 if (const Expr *E = Node.getUnderlyingExpr())
33 return isa<CXXNullPtrLiteralExpr>(E->IgnoreParens());
34 return false;
35}
36
37} // namespace
38
39static constexpr char CastSequence[] = "sequence";
40
41static constexpr char DefaultIgnoredTypes[] = "std::_CmpUnspecifiedParam;"
42 "^std::__cmp_cat::__unspec;"
43 "^std::__cmp_cat::__literal_zero";
44
45/// Create a matcher that finds implicit casts as well as the head of a
46/// sequence of zero or more nested explicit casts that have an implicit cast
47/// to null within.
48/// Finding sequences of explicit casts is necessary so that an entire sequence
49/// can be replaced instead of just the inner-most implicit cast.
50///
51/// TODO/NOTE: The second "anyOf" below discards matches on a substituted type,
52/// since we don't know if that would _always_ be a pointer type for all other
53/// specializations, unless the expression was "__null", in which case we assume
54/// that all specializations are expected to be for pointer types. Ideally this
55/// would check for the "NULL" macro instead, but that'd be harder to express.
56/// In practice, "NULL" is often defined as "__null", and this is a useful
57/// condition.
58void UseNullptrCheck::registerMatchers(MatchFinder *Finder) {
59 auto ImplicitCastToNull = implicitCastExpr(
60 anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
61 anyOf(hasSourceExpression(gnuNullExpr()),
62 unless(hasImplicitDestinationType(
63 qualType(substTemplateTypeParmType())))),
64 unless(hasSourceExpression(hasType(sugaredNullptrType()))),
65 unless(hasImplicitDestinationType(
66 qualType(matchers::matchesAnyListedTypeName(IgnoredTypes)))));
67
68 const auto IsOrHasDescendant = [](const auto &InnerMatcher) {
69 return anyOf(InnerMatcher, hasDescendant(InnerMatcher));
70 };
71
72 Finder->addMatcher(
73 castExpr(anyOf(ImplicitCastToNull,
74 explicitCastExpr(hasDescendant(ImplicitCastToNull))),
75 unless(hasAncestor(explicitCastExpr())),
76 unless(hasAncestor(cxxRewrittenBinaryOperator())))
77 .bind(CastSequence),
78 this);
79
80 Finder->addMatcher(
81 cxxRewrittenBinaryOperator(
82 // Match rewritten operators, but verify (in the check method)
83 // that if an implicit cast is found, it is not from another
84 // nested rewritten operator.
85 expr().bind("matchBinopOperands"),
86 hasEitherOperand(IsOrHasDescendant(
87 implicitCastExpr(ImplicitCastToNull,
88 hasAncestor(cxxRewrittenBinaryOperator().bind(
89 "checkBinopOperands")))
90 .bind(CastSequence))),
91 // Skip defaulted comparison operators.
92 unless(hasAncestor(functionDecl(isDefaulted())))),
93 this);
94
95 if (UseNullptrT)
96 Finder->addMatcher(typeLoc(loc(decltypeType(decltypeTypeNullptrLiteral())),
97 unless(hasParent(typedefNameDecl(hasAnyName(
98 "::nullptr_t", "::std::nullptr_t")))))
99 .bind("matchDecltypeNullptr"),
100 this);
101}
102
103static bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc,
104 const SourceManager &SM) {
105 return SM.isWrittenInSameFile(StartLoc, EndLoc);
106}
107
108/// Replaces the provided range with the text "nullptr", but only if
109/// the start and end location are both in main file.
110/// Returns true if and only if a replacement was made.
111static void replaceWithNullptr(ClangTidyCheck &Check, const SourceManager &SM,
112 SourceLocation StartLoc, SourceLocation EndLoc) {
113 const CharSourceRange Range(SourceRange(StartLoc, EndLoc), true);
114 // Add a space if nullptr follows an alphanumeric character. This happens
115 // whenever there is an c-style explicit cast to nullptr not surrounded by
116 // parentheses and right beside a return statement.
117 const SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
118 const bool NeedsSpace =
119 isAlphanumeric(*SM.getCharacterData(PreviousLocation));
120 Check.diag(Range.getBegin(), "use nullptr") << FixItHint::CreateReplacement(
121 Range, NeedsSpace ? " nullptr" : "nullptr");
122}
123
124/// Returns the name of the outermost macro.
125///
126/// Given
127/// \code
128/// #define MY_NULL NULL
129/// \endcode
130/// If \p Loc points to NULL, this function will return the name MY_NULL.
131static StringRef getOutermostMacroName(SourceLocation Loc,
132 const SourceManager &SM,
133 const LangOptions &LO) {
134 assert(Loc.isMacroID());
135 SourceLocation OutermostMacroLoc;
136
137 while (Loc.isMacroID()) {
138 OutermostMacroLoc = Loc;
139 Loc = SM.getImmediateMacroCallerLoc(Loc);
140 }
141
142 return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
143}
144
145namespace {
146
147/// RecursiveASTVisitor for ensuring all nodes rooted at a given AST
148/// subtree that have file-level source locations corresponding to a macro
149/// argument have implicit NullTo(Member)Pointer nodes as ancestors.
150class MacroArgUsageVisitor : public RecursiveASTVisitor<MacroArgUsageVisitor> {
151public:
152 MacroArgUsageVisitor(SourceLocation CastLoc, const SourceManager &SM)
153 : CastLoc(CastLoc), SM(SM) {
154 assert(CastLoc.isFileID());
155 }
156
157 bool TraverseStmt(Stmt *S) {
158 const bool VisitedPreviously = Visited;
159
160 if (!RecursiveASTVisitor<MacroArgUsageVisitor>::TraverseStmt(S))
161 return false;
162
163 // The point at which VisitedPreviously is false and Visited is true is the
164 // root of a subtree containing nodes whose locations match CastLoc. It's
165 // at this point we test that the Implicit NullTo(Member)Pointer cast was
166 // found or not.
167 if (!VisitedPreviously) {
168 if (Visited && !CastFound) {
169 // Found nodes with matching SourceLocations but didn't come across a
170 // cast. This is an invalid macro arg use. Can stop traversal
171 // completely now.
172 InvalidFound = true;
173 return false;
174 }
175 // Reset state as we unwind back up the tree.
176 CastFound = false;
177 Visited = false;
178 }
179 return true;
180 }
181
182 bool VisitStmt(Stmt *S) {
183 if (SM.getFileLoc(S->getBeginLoc()) != CastLoc)
184 return true;
185 Visited = true;
186
187 const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
188 if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
189 Cast->getCastKind() == CK_NullToMemberPointer))
190 CastFound = true;
191
192 return true;
193 }
194
195 bool TraverseInitListExpr(InitListExpr *S) {
196 // Only go through the semantic form of the InitListExpr, because
197 // ImplicitCast might not appear in the syntactic form, and this results in
198 // finding usages of the macro argument that don't have a ImplicitCast as an
199 // ancestor (thus invalidating the replacement) when they actually have.
200 return RecursiveASTVisitor<MacroArgUsageVisitor>::
201 TraverseSynOrSemInitListExpr(
202 S->isSemanticForm() ? S : S->getSemanticForm());
203 }
204
205 bool foundInvalid() const { return InvalidFound; }
206
207private:
208 SourceLocation CastLoc;
209 const SourceManager &SM;
210
211 bool Visited = false;
212 bool CastFound = false;
213 bool InvalidFound = false;
214};
215
216/// Looks for implicit casts as well as sequences of 0 or more explicit
217/// casts with an implicit null-to-pointer cast within.
218///
219/// The matcher this visitor is used with will find a single implicit cast or a
220/// top-most explicit cast (i.e. it has no explicit casts as an ancestor) where
221/// an implicit cast is nested within. However, there is no guarantee that only
222/// explicit casts exist between the found top-most explicit cast and the
223/// possibly more than one nested implicit cast. This visitor finds all cast
224/// sequences with an implicit cast to null within and creates a replacement
225/// leaving the outermost explicit cast unchanged to avoid introducing
226/// ambiguities.
227class CastSequenceVisitor : public RecursiveASTVisitor<CastSequenceVisitor> {
228public:
229 CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
230 ClangTidyCheck &Check)
231 : SM(Context.getSourceManager()), Context(Context),
232 NullMacros(NullMacros), Check(Check) {}
233
234 bool TraverseStmt(Stmt *S) {
235 // Stop traversing down the tree if requested.
236 if (PruneSubtree) {
237 PruneSubtree = false;
238 return true;
239 }
240 return RecursiveASTVisitor<CastSequenceVisitor>::TraverseStmt(S);
241 }
242
243 // Only VisitStmt is overridden as we shouldn't find other base AST types
244 // within a cast expression.
245 bool VisitStmt(Stmt *S) {
246 auto *C = dyn_cast<CastExpr>(S);
247 // Catch the castExpr inside cxxDefaultArgExpr.
248 if (auto *E = dyn_cast<CXXDefaultArgExpr>(S)) {
249 C = dyn_cast<CastExpr>(E->getExpr());
250 FirstSubExpr = nullptr;
251 }
252 if (!C) {
253 FirstSubExpr = nullptr;
254 return true;
255 }
256
257 auto *CastSubExpr = C->getSubExpr()->IgnoreParens();
258 // Ignore cast expressions which cast nullptr literal.
259 if (isa<CXXNullPtrLiteralExpr>(CastSubExpr))
260 return true;
261
262 if (!FirstSubExpr)
263 FirstSubExpr = CastSubExpr;
264
265 if (C->getCastKind() != CK_NullToPointer &&
266 C->getCastKind() != CK_NullToMemberPointer) {
267 return true;
268 }
269
270 SourceLocation StartLoc = FirstSubExpr->getBeginLoc();
271 SourceLocation EndLoc = FirstSubExpr->getEndLoc();
272
273 // If the location comes from a macro arg expansion, *all* uses of that
274 // arg must be checked to result in NullTo(Member)Pointer casts.
275 //
276 // If the location comes from a macro body expansion, check to see if its
277 // coming from one of the allowed 'NULL' macros.
278 if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
279 const SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
280 FileLocEnd = SM.getFileLoc(EndLoc);
281 SourceLocation ImmediateMacroArgLoc, MacroLoc;
282 // Skip NULL macros used in macro.
283 if (!getMacroAndArgLocations(StartLoc, ImmediateMacroArgLoc, MacroLoc) ||
284 ImmediateMacroArgLoc != FileLocStart)
285 return skipSubTree();
286
287 if (isReplaceableRange(FileLocStart, FileLocEnd, SM) &&
288 allArgUsesValid(C)) {
289 replaceWithNullptr(Check, SM, FileLocStart, FileLocEnd);
290 }
291 return true;
292 }
293
294 if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
295 const StringRef OutermostMacroName =
296 getOutermostMacroName(StartLoc, SM, Context.getLangOpts());
297
298 // Check to see if the user wants to replace the macro being expanded.
299 if (!llvm::is_contained(NullMacros, OutermostMacroName))
300 return skipSubTree();
301
302 StartLoc = SM.getFileLoc(StartLoc);
303 EndLoc = SM.getFileLoc(EndLoc);
304 }
305
306 if (!isReplaceableRange(StartLoc, EndLoc, SM))
307 return skipSubTree();
308 replaceWithNullptr(Check, SM, StartLoc, EndLoc);
309
310 return true;
311 }
312
313private:
314 bool skipSubTree() {
315 PruneSubtree = true;
316 return true;
317 }
318
319 /// Tests that all expansions of a macro arg, one of which expands to
320 /// result in \p CE, yield NullTo(Member)Pointer casts.
321 bool allArgUsesValid(const CastExpr *CE) {
322 const SourceLocation CastLoc = CE->getBeginLoc();
323
324 // Step 1: Get location of macro arg and location of the macro the arg was
325 // provided to.
326 SourceLocation ArgLoc, MacroLoc;
327 if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
328 return false;
329
330 // Step 2: Find the first ancestor that doesn't expand from this macro.
331 DynTypedNode ContainingAncestor;
332 if (!findContainingAncestor(DynTypedNode::create<Stmt>(*CE), MacroLoc,
333 ContainingAncestor))
334 return false;
335
336 // Step 3:
337 // Visit children of this containing parent looking for the least-descended
338 // nodes of the containing parent which are macro arg expansions that expand
339 // from the given arg location.
340 // Visitor needs: arg loc.
341 MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
342 if (const auto *D = ContainingAncestor.get<Decl>())
343 ArgUsageVisitor.TraverseDecl(const_cast<Decl *>(D));
344 else if (const auto *S = ContainingAncestor.get<Stmt>())
345 ArgUsageVisitor.TraverseStmt(const_cast<Stmt *>(S));
346 else
347 llvm_unreachable("Unhandled ContainingAncestor node type");
348
349 return !ArgUsageVisitor.foundInvalid();
350 }
351
352 /// Given the SourceLocation for a macro arg expansion, finds the
353 /// non-macro SourceLocation of the macro the arg was passed to and the
354 /// non-macro SourceLocation of the argument in the arg list to that macro.
355 /// These results are returned via \c MacroLoc and \c ArgLoc respectively.
356 /// These values are undefined if the return value is false.
357 ///
358 /// \returns false if one of the returned SourceLocations would be a
359 /// SourceLocation pointing within the definition of another macro.
360 bool getMacroAndArgLocations(SourceLocation Loc, SourceLocation &ArgLoc,
361 SourceLocation &MacroLoc) {
362 assert(Loc.isMacroID() && "Only reasonable to call this on macros");
363
364 ArgLoc = Loc;
365
366 // Find the location of the immediate macro expansion.
367 while (true) {
368 const std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
369 const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
370 const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
371
372 const SourceLocation OldArgLoc = ArgLoc;
373 ArgLoc = Expansion.getExpansionLocStart();
374 if (!Expansion.isMacroArgExpansion()) {
375 if (!MacroLoc.isFileID())
376 return false;
377
378 const StringRef Name =
379 Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
380 return llvm::is_contained(NullMacros, Name);
381 }
382
383 MacroLoc = SM.getExpansionRange(ArgLoc).getBegin();
384
385 ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
386 if (ArgLoc.isFileID())
387 return true;
388
389 // If spelling location resides in the same FileID as macro expansion
390 // location, it means there is no inner macro.
391 const FileID MacroFID = SM.getFileID(MacroLoc);
392 if (SM.isInFileID(ArgLoc, MacroFID)) {
393 // Don't transform this case. If the characters that caused the
394 // null-conversion come from within a macro, they can't be changed.
395 return false;
396 }
397 }
398
399 llvm_unreachable("getMacroAndArgLocations");
400 }
401
402 /// Tests if TestMacroLoc is found while recursively unravelling
403 /// expansions starting at TestLoc. TestMacroLoc.isFileID() must be true.
404 /// Implementation is very similar to getMacroAndArgLocations() except in this
405 /// case, it's not assumed that TestLoc is expanded from a macro argument.
406 /// While unravelling expansions macro arguments are handled as with
407 /// getMacroAndArgLocations() but in this function macro body expansions are
408 /// also handled.
409 ///
410 /// False means either:
411 /// - TestLoc is not from a macro expansion.
412 /// - TestLoc is from a different macro expansion.
413 bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
414 if (TestLoc.isFileID())
415 return false;
416
417 SourceLocation Loc = TestLoc, MacroLoc;
418
419 while (true) {
420 const std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
421 const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
422 const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
423
424 Loc = Expansion.getExpansionLocStart();
425
426 if (!Expansion.isMacroArgExpansion()) {
427 if (Loc.isFileID())
428 return Loc == TestMacroLoc;
429 // Since Loc is still a macro ID and it's not an argument expansion, we
430 // don't need to do the work of handling an argument expansion. Simply
431 // keep recursively expanding until we hit a FileID or a macro arg
432 // expansion or a macro arg expansion.
433 continue;
434 }
435
436 MacroLoc = SM.getImmediateExpansionRange(Loc).getBegin();
437 if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
438 // Match made.
439 return true;
440 }
441
442 Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
443 if (Loc.isFileID()) {
444 // If we made it this far without finding a match, there is no match to
445 // be made.
446 return false;
447 }
448 }
449
450 llvm_unreachable("expandsFrom");
451 }
452
453 /// Given a starting point \c Start in the AST, find an ancestor that
454 /// doesn't expand from the macro called at file location \c MacroLoc.
455 ///
456 /// \pre MacroLoc.isFileID()
457 /// \returns true if such an ancestor was found, false otherwise.
458 bool findContainingAncestor(DynTypedNode Start, SourceLocation MacroLoc,
459 DynTypedNode &Result) {
460 // Below we're only following the first parent back up the AST. This should
461 // be fine since for the statements we care about there should only be one
462 // parent, except for the case specified below.
463
464 assert(MacroLoc.isFileID());
465
466 while (true) {
467 const auto &Parents = Context.getParents(Start);
468 if (Parents.empty())
469 return false;
470 if (Parents.size() > 1) {
471 // If there are more than one parents, don't do the replacement unless
472 // they are InitListsExpr (semantic and syntactic form). In this case we
473 // can choose any one here, and the ASTVisitor will take care of
474 // traversing the right one.
475 for (const auto &Parent : Parents)
476 if (!Parent.get<InitListExpr>())
477 return false;
478 }
479
480 const DynTypedNode &Parent = Parents[0];
481
482 SourceLocation Loc;
483 if (const auto *D = Parent.get<Decl>())
484 Loc = D->getBeginLoc();
485 else if (const auto *S = Parent.get<Stmt>())
486 Loc = S->getBeginLoc();
487
488 // TypeLoc and NestedNameSpecifierLoc are members of the parent map. Skip
489 // them and keep going up.
490 if (Loc.isValid() && !expandsFrom(Loc, MacroLoc)) {
491 Result = Parent;
492 return true;
493 }
494
495 Start = Parent;
496 }
497
498 llvm_unreachable("findContainingAncestor");
499 }
500
501 SourceManager &SM;
502 ASTContext &Context;
503 ArrayRef<StringRef> NullMacros;
504 ClangTidyCheck &Check;
505 Expr *FirstSubExpr = nullptr;
506 bool PruneSubtree = false;
507};
508
509} // namespace
510
512 : ClangTidyCheck(Name, Context),
513 NullMacrosStr(Options.get("NullMacros", "NULL")),
514 IgnoredTypes(utils::options::parseStringList(
515 Options.get("IgnoredTypes", DefaultIgnoredTypes))),
516 UseNullptrT(Options.get("UseNullptrT", true)),
517 IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
518 utils::IncludeSorter::IS_LLVM),
519 areDiagsSelfContained()) {
520 NullMacrosStr.split(NullMacros, ",");
521}
522
523void UseNullptrCheck::registerPPCallbacks(const SourceManager &SM,
524 Preprocessor *PP,
525 Preprocessor *ModuleExpanderPP) {
526 IncludeInserter.registerPreprocessor(PP);
527}
528
530 Options.store(Opts, "NullMacros", NullMacrosStr);
531 Options.store(Opts, "IgnoredTypes",
533 Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
534 Options.store(Opts, "UseNullptrT", UseNullptrT);
535}
536
537void UseNullptrCheck::check(const MatchFinder::MatchResult &Result) {
538 if (const auto *MatchedTypeLoc =
539 Result.Nodes.getNodeAs<TypeLoc>("matchDecltypeNullptr")) {
540 diag(MatchedTypeLoc->getBeginLoc(), "use std::nullptr_t instead")
541 << IncludeInserter.createIncludeInsertion(
542 Result.SourceManager->getFileID(MatchedTypeLoc->getBeginLoc()),
543 "<cstddef>")
544 << FixItHint::CreateReplacement(MatchedTypeLoc->getSourceRange(),
545 "std::nullptr_t");
546 return;
547 }
548
549 const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(CastSequence);
550 assert(NullCast && "Bad Callback. No node provided");
551
552 if (Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
553 "matchBinopOperands") !=
554 Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>("checkBinopOperands"))
555 return;
556
557 // Given an implicit null-ptr cast or an explicit cast with an implicit
558 // null-to-pointer cast within use CastSequenceVisitor to identify sequences
559 // of explicit casts that can be converted into 'nullptr'.
560 CastSequenceVisitor(*Result.Context, NullMacros, *this)
561 .TraverseStmt(const_cast<CastExpr *>(NullCast));
562}
563
564} // namespace clang::tidy::modernize
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
UseNullptrCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Create a matcher that finds implicit casts as well as the head of a sequence of zero or more nested e...
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
@ Type
An inlay hint that for a type annotation.
Definition Protocol.h:1745
inline ::clang::ast_matchers::internal::Matcher< QualType > matchesAnyListedTypeName(llvm::ArrayRef< StringRef > NameList, bool CanonicalTypes)
AST_MATCHER(BinaryOperator, isRelationalOperator)
static StringRef getOutermostMacroName(SourceLocation Loc, const SourceManager &SM, const LangOptions &LO)
Returns the name of the outermost macro.
static constexpr char DefaultIgnoredTypes[]
static constexpr char CastSequence[]
static void replaceWithNullptr(ClangTidyCheck &Check, const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLoc)
Replaces the provided range with the text "nullptr", but only if the start and end location are both ...
static bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc, const SourceManager &SM)
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
Definition Generators.h:150
llvm::StringMap< ClangTidyValue > OptionMap