12#include "clang/AST/ASTContext.h"
13#include "clang/AST/RecursiveASTVisitor.h"
14#include "clang/ASTMatchers/ASTMatchFinder.h"
15#include "clang/Lex/Lexer.h"
25 const Type *DesugaredType = Node.getUnqualifiedDesugaredType();
26 if (
const auto *BT = dyn_cast<BuiltinType>(DesugaredType))
27 return BT->getKind() == BuiltinType::NullPtr;
31AST_MATCHER(DecltypeType, decltypeTypeNullptrLiteral) {
32 if (
const Expr *E = Node.getUnderlyingExpr())
33 return isa<CXXNullPtrLiteralExpr>(E->IgnoreParens());
42 "^std::__cmp_cat::__unspec;"
43 "^std::__cmp_cat::__literal_zero";
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(
68 const auto IsOrHasDescendant = [](
const auto &InnerMatcher) {
69 return anyOf(InnerMatcher, hasDescendant(InnerMatcher));
73 castExpr(anyOf(ImplicitCastToNull,
74 explicitCastExpr(hasDescendant(ImplicitCastToNull))),
75 unless(hasAncestor(explicitCastExpr())),
76 unless(hasAncestor(cxxRewrittenBinaryOperator())))
81 cxxRewrittenBinaryOperator(
85 expr().bind(
"matchBinopOperands"),
86 hasEitherOperand(IsOrHasDescendant(
87 implicitCastExpr(ImplicitCastToNull,
88 hasAncestor(cxxRewrittenBinaryOperator().bind(
89 "checkBinopOperands")))
92 unless(hasAncestor(functionDecl(isDefaulted())))),
96 Finder->addMatcher(typeLoc(loc(decltypeType(decltypeTypeNullptrLiteral())),
97 unless(hasParent(typedefNameDecl(hasAnyName(
98 "::nullptr_t",
"::std::nullptr_t")))))
99 .bind(
"matchDecltypeNullptr"),
104 const SourceManager &SM) {
105 return SM.isWrittenInSameFile(StartLoc, EndLoc);
112 SourceLocation StartLoc, SourceLocation EndLoc) {
113 const CharSourceRange Range(SourceRange(StartLoc, EndLoc),
true);
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");
132 const SourceManager &SM,
133 const LangOptions &LO) {
134 assert(Loc.isMacroID());
135 SourceLocation OutermostMacroLoc;
137 while (Loc.isMacroID()) {
138 OutermostMacroLoc = Loc;
139 Loc = SM.getImmediateMacroCallerLoc(Loc);
142 return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
150class MacroArgUsageVisitor :
public RecursiveASTVisitor<MacroArgUsageVisitor> {
152 MacroArgUsageVisitor(SourceLocation CastLoc,
const SourceManager &SM)
153 : CastLoc(CastLoc), SM(SM) {
154 assert(CastLoc.isFileID());
157 bool TraverseStmt(Stmt *S) {
158 const bool VisitedPreviously = Visited;
160 if (!RecursiveASTVisitor<MacroArgUsageVisitor>::TraverseStmt(S))
167 if (!VisitedPreviously) {
168 if (Visited && !CastFound) {
182 bool VisitStmt(Stmt *S) {
183 if (SM.getFileLoc(S->getBeginLoc()) != CastLoc)
187 const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
188 if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
189 Cast->getCastKind() == CK_NullToMemberPointer))
195 bool TraverseInitListExpr(InitListExpr *S) {
200 return RecursiveASTVisitor<MacroArgUsageVisitor>::
201 TraverseSynOrSemInitListExpr(
202 S->isSemanticForm() ? S : S->getSemanticForm());
205 bool foundInvalid()
const {
return InvalidFound; }
208 SourceLocation CastLoc;
209 const SourceManager &SM;
211 bool Visited =
false;
212 bool CastFound =
false;
213 bool InvalidFound =
false;
227class CastSequenceVisitor :
public RecursiveASTVisitor<CastSequenceVisitor> {
229 CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
230 ClangTidyCheck &Check)
231 : SM(Context.getSourceManager()), Context(Context),
232 NullMacros(NullMacros), Check(Check) {}
234 bool TraverseStmt(Stmt *S) {
237 PruneSubtree =
false;
240 return RecursiveASTVisitor<CastSequenceVisitor>::TraverseStmt(S);
245 bool VisitStmt(Stmt *S) {
246 auto *
C = dyn_cast<CastExpr>(S);
248 if (
auto *E = dyn_cast<CXXDefaultArgExpr>(S)) {
249 C = dyn_cast<CastExpr>(E->getExpr());
250 FirstSubExpr =
nullptr;
253 FirstSubExpr =
nullptr;
257 auto *CastSubExpr =
C->getSubExpr()->IgnoreParens();
259 if (isa<CXXNullPtrLiteralExpr>(CastSubExpr))
263 FirstSubExpr = CastSubExpr;
265 if (
C->getCastKind() != CK_NullToPointer &&
266 C->getCastKind() != CK_NullToMemberPointer) {
270 SourceLocation StartLoc = FirstSubExpr->getBeginLoc();
271 SourceLocation EndLoc = FirstSubExpr->getEndLoc();
278 if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
279 const SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
280 FileLocEnd = SM.getFileLoc(EndLoc);
281 SourceLocation ImmediateMacroArgLoc, MacroLoc;
283 if (!getMacroAndArgLocations(StartLoc, ImmediateMacroArgLoc, MacroLoc) ||
284 ImmediateMacroArgLoc != FileLocStart)
285 return skipSubTree();
288 allArgUsesValid(C)) {
294 if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
295 const StringRef OutermostMacroName =
299 if (!llvm::is_contained(NullMacros, OutermostMacroName))
300 return skipSubTree();
302 StartLoc = SM.getFileLoc(StartLoc);
303 EndLoc = SM.getFileLoc(EndLoc);
307 return skipSubTree();
321 bool allArgUsesValid(
const CastExpr *CE) {
322 const SourceLocation CastLoc = CE->getBeginLoc();
326 SourceLocation ArgLoc, MacroLoc;
327 if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
331 DynTypedNode ContainingAncestor;
332 if (!findContainingAncestor(DynTypedNode::create<Stmt>(*CE), MacroLoc,
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));
347 llvm_unreachable(
"Unhandled ContainingAncestor node type");
349 return !ArgUsageVisitor.foundInvalid();
360 bool getMacroAndArgLocations(SourceLocation Loc, SourceLocation &ArgLoc,
361 SourceLocation &MacroLoc) {
362 assert(Loc.isMacroID() &&
"Only reasonable to call this on macros");
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();
372 const SourceLocation OldArgLoc = ArgLoc;
373 ArgLoc = Expansion.getExpansionLocStart();
374 if (!Expansion.isMacroArgExpansion()) {
375 if (!MacroLoc.isFileID())
378 const StringRef Name =
379 Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
380 return llvm::is_contained(NullMacros, Name);
383 MacroLoc = SM.getExpansionRange(ArgLoc).getBegin();
385 ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
386 if (ArgLoc.isFileID())
391 const FileID MacroFID = SM.getFileID(MacroLoc);
392 if (SM.isInFileID(ArgLoc, MacroFID)) {
399 llvm_unreachable(
"getMacroAndArgLocations");
413 bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
414 if (TestLoc.isFileID())
417 SourceLocation Loc = TestLoc, MacroLoc;
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();
424 Loc = Expansion.getExpansionLocStart();
426 if (!Expansion.isMacroArgExpansion()) {
428 return Loc == TestMacroLoc;
436 MacroLoc = SM.getImmediateExpansionRange(Loc).getBegin();
437 if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
442 Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
443 if (Loc.isFileID()) {
450 llvm_unreachable(
"expandsFrom");
458 bool findContainingAncestor(DynTypedNode Start, SourceLocation MacroLoc,
459 DynTypedNode &Result) {
464 assert(MacroLoc.isFileID());
467 const auto &
Parents = Context.getParents(Start);
475 for (
const auto &Parent : Parents)
476 if (!Parent.get<InitListExpr>())
480 const DynTypedNode &Parent =
Parents[0];
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();
490 if (Loc.isValid() && !expandsFrom(Loc, MacroLoc)) {
498 llvm_unreachable(
"findContainingAncestor");
503 ArrayRef<StringRef> NullMacros;
504 ClangTidyCheck &Check;
505 Expr *FirstSubExpr =
nullptr;
506 bool PruneSubtree =
false;
513 NullMacrosStr(Options.get(
"NullMacros",
"NULL")),
514 IgnoredTypes(
utils::options::parseStringList(
516 UseNullptrT(Options.get(
"UseNullptrT", true)),
517 IncludeInserter(Options.getLocalOrGlobal(
"IncludeStyle",
518 utils::IncludeSorter::IS_LLVM),
519 areDiagsSelfContained()) {
520 NullMacrosStr.split(NullMacros,
",");
525 Preprocessor *ModuleExpanderPP) {
526 IncludeInserter.registerPreprocessor(PP);
530 Options.store(Opts,
"NullMacros", NullMacrosStr);
531 Options.store(Opts,
"IgnoredTypes",
533 Options.store(Opts,
"IncludeStyle", IncludeInserter.getStyle());
534 Options.store(Opts,
"UseNullptrT", UseNullptrT);
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()),
544 << FixItHint::CreateReplacement(MatchedTypeLoc->getSourceRange(),
549 const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(
CastSequence);
550 assert(NullCast &&
"Bad Callback. No node provided");
552 if (Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
553 "matchBinopOperands") !=
554 Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
"checkBinopOperands"))
560 CastSequenceVisitor(*Result.Context, NullMacros, *
this)
561 .TraverseStmt(
const_cast<CastExpr *
>(NullCast));
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.
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.
llvm::StringMap< ClangTidyValue > OptionMap