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;
49 auto ImplicitCastToNull = implicitCastExpr(
50 anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
51 anyOf(hasSourceExpression(gnuNullExpr()),
52 unless(hasImplicitDestinationType(
53 qualType(substTemplateTypeParmType())))),
54 unless(hasSourceExpression(hasType(sugaredNullptrType()))),
55 unless(hasImplicitDestinationType(
58 auto IsOrHasDescendant = [](
const auto &InnerMatcher) {
59 return anyOf(InnerMatcher, hasDescendant(InnerMatcher));
63 castExpr(anyOf(ImplicitCastToNull,
64 explicitCastExpr(hasDescendant(ImplicitCastToNull))),
65 unless(hasAncestor(explicitCastExpr())),
66 unless(hasAncestor(cxxRewrittenBinaryOperator())))
71 cxxRewrittenBinaryOperator(
75 expr().bind(
"matchBinopOperands"),
76 hasEitherOperand(IsOrHasDescendant(
77 implicitCastExpr(ImplicitCastToNull,
78 hasAncestor(cxxRewrittenBinaryOperator().bind(
79 "checkBinopOperands")))
82 unless(hasAncestor(functionDecl(isDefaulted())))),
87 const SourceManager &SM) {
88 return SM.isWrittenInSameFile(StartLoc, EndLoc);
95 SourceLocation StartLoc, SourceLocation EndLoc) {
96 const CharSourceRange Range(SourceRange(StartLoc, EndLoc),
true);
100 const SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
101 const bool NeedsSpace =
102 isAlphanumeric(*SM.getCharacterData(PreviousLocation));
103 Check.diag(Range.getBegin(),
"use nullptr") << FixItHint::CreateReplacement(
104 Range, NeedsSpace ?
" nullptr" :
"nullptr");
115 const SourceManager &SM,
116 const LangOptions &LO) {
117 assert(Loc.isMacroID());
118 SourceLocation OutermostMacroLoc;
120 while (Loc.isMacroID()) {
121 OutermostMacroLoc = Loc;
122 Loc = SM.getImmediateMacroCallerLoc(Loc);
125 return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
133class MacroArgUsageVisitor :
public RecursiveASTVisitor<MacroArgUsageVisitor> {
135 MacroArgUsageVisitor(SourceLocation CastLoc,
const SourceManager &SM)
136 : CastLoc(CastLoc), SM(SM) {
137 assert(CastLoc.isFileID());
140 bool TraverseStmt(Stmt *S) {
141 const bool VisitedPreviously = Visited;
143 if (!RecursiveASTVisitor<MacroArgUsageVisitor>::TraverseStmt(S))
150 if (!VisitedPreviously) {
151 if (Visited && !CastFound) {
165 bool VisitStmt(Stmt *S) {
166 if (SM.getFileLoc(S->getBeginLoc()) != CastLoc)
170 const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
171 if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
172 Cast->getCastKind() == CK_NullToMemberPointer))
178 bool TraverseInitListExpr(InitListExpr *S) {
183 return RecursiveASTVisitor<MacroArgUsageVisitor>::
184 TraverseSynOrSemInitListExpr(
185 S->isSemanticForm() ? S : S->getSemanticForm());
188 bool foundInvalid()
const {
return InvalidFound; }
191 SourceLocation CastLoc;
192 const SourceManager &SM;
194 bool Visited =
false;
195 bool CastFound =
false;
196 bool InvalidFound =
false;
210class CastSequenceVisitor :
public RecursiveASTVisitor<CastSequenceVisitor> {
212 CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
213 ClangTidyCheck &Check)
214 : SM(Context.getSourceManager()), Context(Context),
215 NullMacros(NullMacros), Check(Check) {}
217 bool TraverseStmt(Stmt *S) {
220 PruneSubtree =
false;
223 return RecursiveASTVisitor<CastSequenceVisitor>::TraverseStmt(S);
228 bool VisitStmt(Stmt *S) {
229 auto *
C = dyn_cast<CastExpr>(S);
231 if (
auto *E = dyn_cast<CXXDefaultArgExpr>(S)) {
232 C = dyn_cast<CastExpr>(E->getExpr());
233 FirstSubExpr =
nullptr;
236 FirstSubExpr =
nullptr;
240 auto *CastSubExpr =
C->getSubExpr()->IgnoreParens();
242 if (isa<CXXNullPtrLiteralExpr>(CastSubExpr))
246 FirstSubExpr = CastSubExpr;
248 if (
C->getCastKind() != CK_NullToPointer &&
249 C->getCastKind() != CK_NullToMemberPointer) {
253 SourceLocation StartLoc = FirstSubExpr->getBeginLoc();
254 SourceLocation EndLoc = FirstSubExpr->getEndLoc();
261 if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
262 const SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
263 FileLocEnd = SM.getFileLoc(EndLoc);
264 SourceLocation ImmediateMacroArgLoc, MacroLoc;
266 if (!getMacroAndArgLocations(StartLoc, ImmediateMacroArgLoc, MacroLoc) ||
267 ImmediateMacroArgLoc != FileLocStart)
268 return skipSubTree();
271 allArgUsesValid(C)) {
277 if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
278 const StringRef OutermostMacroName =
282 if (!llvm::is_contained(NullMacros, OutermostMacroName))
283 return skipSubTree();
285 StartLoc = SM.getFileLoc(StartLoc);
286 EndLoc = SM.getFileLoc(EndLoc);
290 return skipSubTree();
304 bool allArgUsesValid(
const CastExpr *CE) {
305 const SourceLocation CastLoc = CE->getBeginLoc();
309 SourceLocation ArgLoc, MacroLoc;
310 if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
314 DynTypedNode ContainingAncestor;
315 if (!findContainingAncestor(DynTypedNode::create<Stmt>(*CE), MacroLoc,
324 MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
325 if (
const auto *D = ContainingAncestor.get<Decl>())
326 ArgUsageVisitor.TraverseDecl(
const_cast<Decl *
>(D));
327 else if (
const auto *S = ContainingAncestor.get<Stmt>())
328 ArgUsageVisitor.TraverseStmt(
const_cast<Stmt *
>(S));
330 llvm_unreachable(
"Unhandled ContainingAncestor node type");
332 return !ArgUsageVisitor.foundInvalid();
343 bool getMacroAndArgLocations(SourceLocation Loc, SourceLocation &ArgLoc,
344 SourceLocation &MacroLoc) {
345 assert(Loc.isMacroID() &&
"Only reasonable to call this on macros");
351 const std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
352 const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
353 const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
355 const SourceLocation OldArgLoc = ArgLoc;
356 ArgLoc = Expansion.getExpansionLocStart();
357 if (!Expansion.isMacroArgExpansion()) {
358 if (!MacroLoc.isFileID())
361 const StringRef Name =
362 Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
363 return llvm::is_contained(NullMacros, Name);
366 MacroLoc = SM.getExpansionRange(ArgLoc).getBegin();
368 ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
369 if (ArgLoc.isFileID())
374 const FileID MacroFID = SM.getFileID(MacroLoc);
375 if (SM.isInFileID(ArgLoc, MacroFID)) {
382 llvm_unreachable(
"getMacroAndArgLocations");
396 bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
397 if (TestLoc.isFileID())
400 SourceLocation Loc = TestLoc, MacroLoc;
403 const std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
404 const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
405 const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
407 Loc = Expansion.getExpansionLocStart();
409 if (!Expansion.isMacroArgExpansion()) {
411 return Loc == TestMacroLoc;
419 MacroLoc = SM.getImmediateExpansionRange(Loc).getBegin();
420 if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
425 Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
426 if (Loc.isFileID()) {
433 llvm_unreachable(
"expandsFrom");
441 bool findContainingAncestor(DynTypedNode Start, SourceLocation MacroLoc,
442 DynTypedNode &Result) {
447 assert(MacroLoc.isFileID());
450 const auto &
Parents = Context.getParents(Start);
458 for (
const auto &Parent : Parents)
459 if (!Parent.get<InitListExpr>())
463 const DynTypedNode &Parent =
Parents[0];
466 if (
const auto *D = Parent.get<Decl>())
467 Loc =
D->getBeginLoc();
468 else if (
const auto *S = Parent.get<Stmt>())
469 Loc = S->getBeginLoc();
474 if (!expandsFrom(Loc, MacroLoc)) {
482 llvm_unreachable(
"findContainingAncestor");
487 ArrayRef<StringRef> NullMacros;
488 ClangTidyCheck &Check;
489 Expr *FirstSubExpr =
nullptr;
490 bool PruneSubtree =
false;
497 NullMacrosStr(Options.get(
"NullMacros",
"NULL")),
498 IgnoredTypes(
utils::options::parseStringList(Options.get(
499 "IgnoredTypes",
"_CmpUnspecifiedParam;^std::__cmp_cat::__unspec"))) {
500 NullMacrosStr.split(NullMacros,
",");
504 Options.store(Opts,
"NullMacros", NullMacrosStr);
505 Options.store(Opts,
"IgnoredTypes",
510 const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(
CastSequence);
511 assert(NullCast &&
"Bad Callback. No node provided");
513 if (Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
514 "matchBinopOperands") !=
515 Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
"checkBinopOperands"))
521 CastSequenceVisitor(*Result.Context, NullMacros, *
this)
522 .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 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