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());
55 auto ImplicitCastToNull = implicitCastExpr(
56 anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
57 anyOf(hasSourceExpression(gnuNullExpr()),
58 unless(hasImplicitDestinationType(
59 qualType(substTemplateTypeParmType())))),
60 unless(hasSourceExpression(hasType(sugaredNullptrType()))),
61 unless(hasImplicitDestinationType(
64 const auto IsOrHasDescendant = [](
const auto &InnerMatcher) {
65 return anyOf(InnerMatcher, hasDescendant(InnerMatcher));
69 castExpr(anyOf(ImplicitCastToNull,
70 explicitCastExpr(hasDescendant(ImplicitCastToNull))),
71 unless(hasAncestor(explicitCastExpr())),
72 unless(hasAncestor(cxxRewrittenBinaryOperator())))
77 cxxRewrittenBinaryOperator(
81 expr().bind(
"matchBinopOperands"),
82 hasEitherOperand(IsOrHasDescendant(
83 implicitCastExpr(ImplicitCastToNull,
84 hasAncestor(cxxRewrittenBinaryOperator().bind(
85 "checkBinopOperands")))
88 unless(hasAncestor(functionDecl(isDefaulted())))),
92 Finder->addMatcher(typeLoc(loc(decltypeType(decltypeTypeNullptrLiteral())))
93 .bind(
"matchDecltypeNullptr"),
98 const SourceManager &SM) {
99 return SM.isWrittenInSameFile(StartLoc, EndLoc);
106 SourceLocation StartLoc, SourceLocation EndLoc) {
107 const CharSourceRange Range(SourceRange(StartLoc, EndLoc),
true);
111 const SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
112 const bool NeedsSpace =
113 isAlphanumeric(*SM.getCharacterData(PreviousLocation));
114 Check.diag(Range.getBegin(),
"use nullptr") << FixItHint::CreateReplacement(
115 Range, NeedsSpace ?
" nullptr" :
"nullptr");
126 const SourceManager &SM,
127 const LangOptions &LO) {
128 assert(Loc.isMacroID());
129 SourceLocation OutermostMacroLoc;
131 while (Loc.isMacroID()) {
132 OutermostMacroLoc = Loc;
133 Loc = SM.getImmediateMacroCallerLoc(Loc);
136 return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
144class MacroArgUsageVisitor :
public RecursiveASTVisitor<MacroArgUsageVisitor> {
146 MacroArgUsageVisitor(SourceLocation CastLoc,
const SourceManager &SM)
147 : CastLoc(CastLoc), SM(SM) {
148 assert(CastLoc.isFileID());
151 bool TraverseStmt(Stmt *S) {
152 const bool VisitedPreviously = Visited;
154 if (!RecursiveASTVisitor<MacroArgUsageVisitor>::TraverseStmt(S))
161 if (!VisitedPreviously) {
162 if (Visited && !CastFound) {
176 bool VisitStmt(Stmt *S) {
177 if (SM.getFileLoc(S->getBeginLoc()) != CastLoc)
181 const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
182 if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
183 Cast->getCastKind() == CK_NullToMemberPointer))
189 bool TraverseInitListExpr(InitListExpr *S) {
194 return RecursiveASTVisitor<MacroArgUsageVisitor>::
195 TraverseSynOrSemInitListExpr(
196 S->isSemanticForm() ? S : S->getSemanticForm());
199 bool foundInvalid()
const {
return InvalidFound; }
202 SourceLocation CastLoc;
203 const SourceManager &SM;
205 bool Visited =
false;
206 bool CastFound =
false;
207 bool InvalidFound =
false;
221class CastSequenceVisitor :
public RecursiveASTVisitor<CastSequenceVisitor> {
223 CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
224 ClangTidyCheck &Check)
225 : SM(Context.getSourceManager()), Context(Context),
226 NullMacros(NullMacros), Check(Check) {}
228 bool TraverseStmt(Stmt *S) {
231 PruneSubtree =
false;
234 return RecursiveASTVisitor<CastSequenceVisitor>::TraverseStmt(S);
239 bool VisitStmt(Stmt *S) {
240 auto *
C = dyn_cast<CastExpr>(S);
242 if (
auto *E = dyn_cast<CXXDefaultArgExpr>(S)) {
243 C = dyn_cast<CastExpr>(E->getExpr());
244 FirstSubExpr =
nullptr;
247 FirstSubExpr =
nullptr;
251 auto *CastSubExpr =
C->getSubExpr()->IgnoreParens();
253 if (isa<CXXNullPtrLiteralExpr>(CastSubExpr))
257 FirstSubExpr = CastSubExpr;
259 if (
C->getCastKind() != CK_NullToPointer &&
260 C->getCastKind() != CK_NullToMemberPointer) {
264 SourceLocation StartLoc = FirstSubExpr->getBeginLoc();
265 SourceLocation EndLoc = FirstSubExpr->getEndLoc();
272 if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
273 const SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
274 FileLocEnd = SM.getFileLoc(EndLoc);
275 SourceLocation ImmediateMacroArgLoc, MacroLoc;
277 if (!getMacroAndArgLocations(StartLoc, ImmediateMacroArgLoc, MacroLoc) ||
278 ImmediateMacroArgLoc != FileLocStart)
279 return skipSubTree();
282 allArgUsesValid(C)) {
288 if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
289 const StringRef OutermostMacroName =
293 if (!llvm::is_contained(NullMacros, OutermostMacroName))
294 return skipSubTree();
296 StartLoc = SM.getFileLoc(StartLoc);
297 EndLoc = SM.getFileLoc(EndLoc);
301 return skipSubTree();
315 bool allArgUsesValid(
const CastExpr *CE) {
316 const SourceLocation CastLoc = CE->getBeginLoc();
320 SourceLocation ArgLoc, MacroLoc;
321 if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
325 DynTypedNode ContainingAncestor;
326 if (!findContainingAncestor(DynTypedNode::create<Stmt>(*CE), MacroLoc,
335 MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
336 if (
const auto *D = ContainingAncestor.get<Decl>())
337 ArgUsageVisitor.TraverseDecl(
const_cast<Decl *
>(D));
338 else if (
const auto *S = ContainingAncestor.get<Stmt>())
339 ArgUsageVisitor.TraverseStmt(
const_cast<Stmt *
>(S));
341 llvm_unreachable(
"Unhandled ContainingAncestor node type");
343 return !ArgUsageVisitor.foundInvalid();
354 bool getMacroAndArgLocations(SourceLocation Loc, SourceLocation &ArgLoc,
355 SourceLocation &MacroLoc) {
356 assert(Loc.isMacroID() &&
"Only reasonable to call this on macros");
362 const std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
363 const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
364 const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
366 const SourceLocation OldArgLoc = ArgLoc;
367 ArgLoc = Expansion.getExpansionLocStart();
368 if (!Expansion.isMacroArgExpansion()) {
369 if (!MacroLoc.isFileID())
372 const StringRef Name =
373 Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
374 return llvm::is_contained(NullMacros, Name);
377 MacroLoc = SM.getExpansionRange(ArgLoc).getBegin();
379 ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
380 if (ArgLoc.isFileID())
385 const FileID MacroFID = SM.getFileID(MacroLoc);
386 if (SM.isInFileID(ArgLoc, MacroFID)) {
393 llvm_unreachable(
"getMacroAndArgLocations");
407 bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
408 if (TestLoc.isFileID())
411 SourceLocation Loc = TestLoc, MacroLoc;
414 const std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
415 const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
416 const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
418 Loc = Expansion.getExpansionLocStart();
420 if (!Expansion.isMacroArgExpansion()) {
422 return Loc == TestMacroLoc;
430 MacroLoc = SM.getImmediateExpansionRange(Loc).getBegin();
431 if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
436 Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
437 if (Loc.isFileID()) {
444 llvm_unreachable(
"expandsFrom");
452 bool findContainingAncestor(DynTypedNode Start, SourceLocation MacroLoc,
453 DynTypedNode &Result) {
458 assert(MacroLoc.isFileID());
461 const auto &
Parents = Context.getParents(Start);
469 for (
const auto &Parent : Parents)
470 if (!Parent.get<InitListExpr>())
474 const DynTypedNode &Parent =
Parents[0];
477 if (
const auto *D = Parent.get<Decl>())
478 Loc =
D->getBeginLoc();
479 else if (
const auto *S = Parent.get<Stmt>())
480 Loc = S->getBeginLoc();
484 if (Loc.isValid() && !expandsFrom(Loc, MacroLoc)) {
492 llvm_unreachable(
"findContainingAncestor");
497 ArrayRef<StringRef> NullMacros;
498 ClangTidyCheck &Check;
499 Expr *FirstSubExpr =
nullptr;
500 bool PruneSubtree =
false;
507 NullMacrosStr(Options.get(
"NullMacros",
"NULL")),
508 IgnoredTypes(
utils::options::parseStringList(Options.get(
509 "IgnoredTypes",
"_CmpUnspecifiedParam;^std::__cmp_cat::__unspec"))),
510 UseNullptrT(Options.get(
"UseNullptrT", true)),
511 IncludeInserter(Options.getLocalOrGlobal(
"IncludeStyle",
512 utils::IncludeSorter::IS_LLVM),
513 areDiagsSelfContained()) {
514 NullMacrosStr.split(NullMacros,
",");
519 Preprocessor *ModuleExpanderPP) {
520 IncludeInserter.registerPreprocessor(PP);
524 Options.store(Opts,
"NullMacros", NullMacrosStr);
525 Options.store(Opts,
"IgnoredTypes",
527 Options.store(Opts,
"IncludeStyle", IncludeInserter.getStyle());
528 Options.store(Opts,
"UseNullptrT", UseNullptrT);
532 if (
const auto *MatchedTypeLoc =
533 Result.Nodes.getNodeAs<TypeLoc>(
"matchDecltypeNullptr")) {
534 diag(MatchedTypeLoc->getBeginLoc(),
"use std::nullptr_t instead")
535 << IncludeInserter.createIncludeInsertion(
536 Result.SourceManager->getFileID(MatchedTypeLoc->getBeginLoc()),
538 << FixItHint::CreateReplacement(MatchedTypeLoc->getSourceRange(),
543 const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(
CastSequence);
544 assert(NullCast &&
"Bad Callback. No node provided");
546 if (Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
547 "matchBinopOperands") !=
548 Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
"checkBinopOperands"))
554 CastSequenceVisitor(*Result.Context, NullMacros, *
this)
555 .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 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