10#include "clang/AST/ASTContext.h"
11#include "clang/AST/RecursiveASTVisitor.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
22const char CastSequence[] =
"sequence";
25 const Type *DesugaredType = Node.getUnqualifiedDesugaredType();
26 if (
const auto *BT = dyn_cast<BuiltinType>(DesugaredType))
27 return BT->getKind() == BuiltinType::NullPtr;
36StatementMatcher makeCastSequenceMatcher() {
37 StatementMatcher ImplicitCastToNull = implicitCastExpr(
38 anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
39 unless(hasImplicitDestinationType(qualType(substTemplateTypeParmType()))),
40 unless(hasSourceExpression(hasType(sugaredNullptrType()))));
42 auto IsOrHasDescendant = [](
auto InnerMatcher) {
43 return anyOf(InnerMatcher, hasDescendant(InnerMatcher));
48 anyOf(castExpr(anyOf(ImplicitCastToNull,
49 explicitCastExpr(hasDescendant(ImplicitCastToNull))),
50 unless(hasAncestor(explicitCastExpr())),
51 unless(hasAncestor(cxxRewrittenBinaryOperator())))
53 cxxRewrittenBinaryOperator(
57 expr().bind(
"matchBinopOperands"),
58 hasEitherOperand(IsOrHasDescendant(
61 hasAncestor(cxxRewrittenBinaryOperator().bind(
62 "checkBinopOperands")))
63 .bind(CastSequence))),
65 unless(hasAncestor(functionDecl(isDefaulted()))))));
68bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc,
69 const SourceManager &SM) {
70 return SM.isWrittenInSameFile(StartLoc, EndLoc);
76void replaceWithNullptr(ClangTidyCheck &Check, SourceManager &SM,
77 SourceLocation StartLoc, SourceLocation EndLoc) {
78 CharSourceRange
Range(SourceRange(StartLoc, EndLoc),
true);
82 SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
83 bool NeedsSpace = isAlphanumeric(*SM.getCharacterData(PreviousLocation));
84 Check.diag(
Range.getBegin(),
"use nullptr") << FixItHint::CreateReplacement(
85 Range, NeedsSpace ?
" nullptr" :
"nullptr");
95StringRef getOutermostMacroName(SourceLocation
Loc,
const SourceManager &SM,
96 const LangOptions &LO) {
97 assert(
Loc.isMacroID());
98 SourceLocation OutermostMacroLoc;
100 while (
Loc.isMacroID()) {
101 OutermostMacroLoc =
Loc;
102 Loc = SM.getImmediateMacroCallerLoc(
Loc);
105 return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
113 MacroArgUsageVisitor(SourceLocation CastLoc,
const SourceManager &SM)
114 : CastLoc(CastLoc), SM(SM), Visited(false), CastFound(false),
115 InvalidFound(false) {
116 assert(CastLoc.isFileID());
119 bool TraverseStmt(Stmt *S) {
120 bool VisitedPreviously = Visited;
129 if (!VisitedPreviously) {
130 if (Visited && !CastFound) {
144 bool VisitStmt(Stmt *S) {
145 if (SM.getFileLoc(S->getBeginLoc()) != CastLoc)
149 const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
150 if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
151 Cast->getCastKind() == CK_NullToMemberPointer))
157 bool TraverseInitListExpr(InitListExpr *S) {
164 S->isSemanticForm() ? S : S->getSemanticForm());
167 bool foundInvalid()
const {
return InvalidFound; }
170 SourceLocation CastLoc;
171 const SourceManager &SM;
191 CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
192 ClangTidyCheck &Check)
193 : SM(Context.getSourceManager()), Context(Context),
194 NullMacros(NullMacros), Check(Check), FirstSubExpr(nullptr),
195 PruneSubtree(false) {}
197 bool TraverseStmt(Stmt *S) {
200 PruneSubtree =
false;
208 bool VisitStmt(Stmt *S) {
209 auto *
C = dyn_cast<CastExpr>(S);
211 if (
auto *
E = dyn_cast<CXXDefaultArgExpr>(S)) {
212 C = dyn_cast<CastExpr>(
E->getExpr());
213 FirstSubExpr =
nullptr;
216 FirstSubExpr =
nullptr;
220 auto* CastSubExpr =
C->getSubExpr()->IgnoreParens();
222 if (isa<CXXNullPtrLiteralExpr>(CastSubExpr)) {
227 FirstSubExpr = CastSubExpr;
229 if (
C->getCastKind() != CK_NullToPointer &&
230 C->getCastKind() != CK_NullToMemberPointer) {
234 SourceLocation StartLoc = FirstSubExpr->getBeginLoc();
235 SourceLocation EndLoc = FirstSubExpr->getEndLoc();
242 if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
243 SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
244 FileLocEnd = SM.getFileLoc(EndLoc);
245 SourceLocation ImmediateMacroArgLoc, MacroLoc;
247 if (!getMacroAndArgLocations(StartLoc, ImmediateMacroArgLoc, MacroLoc) ||
248 ImmediateMacroArgLoc != FileLocStart)
249 return skipSubTree();
251 if (isReplaceableRange(FileLocStart, FileLocEnd, SM) &&
252 allArgUsesValid(
C)) {
253 replaceWithNullptr(Check, SM, FileLocStart, FileLocEnd);
258 if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
259 StringRef OutermostMacroName =
260 getOutermostMacroName(StartLoc, SM, Context.getLangOpts());
263 if (!llvm::is_contained(NullMacros, OutermostMacroName))
264 return skipSubTree();
266 StartLoc = SM.getFileLoc(StartLoc);
267 EndLoc = SM.getFileLoc(EndLoc);
270 if (!isReplaceableRange(StartLoc, EndLoc, SM)) {
271 return skipSubTree();
273 replaceWithNullptr(Check, SM, StartLoc, EndLoc);
286 bool allArgUsesValid(
const CastExpr *
CE) {
287 SourceLocation CastLoc =
CE->getBeginLoc();
291 SourceLocation ArgLoc, MacroLoc;
292 if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
296 DynTypedNode ContainingAncestor;
297 if (!findContainingAncestor(DynTypedNode::create<Stmt>(*
CE), MacroLoc,
306 MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
307 if (
const auto *D = ContainingAncestor.get<
Decl>())
308 ArgUsageVisitor.TraverseDecl(
const_cast<Decl *
>(D));
309 else if (
const auto *S = ContainingAncestor.get<Stmt>())
310 ArgUsageVisitor.TraverseStmt(
const_cast<Stmt *
>(S));
312 llvm_unreachable(
"Unhandled ContainingAncestor node type");
314 return !ArgUsageVisitor.foundInvalid();
325 bool getMacroAndArgLocations(SourceLocation
Loc, SourceLocation &ArgLoc,
326 SourceLocation &MacroLoc) {
327 assert(
Loc.isMacroID() &&
"Only reasonable to call this on macros");
333 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
334 const SrcMgr::SLocEntry *
E = &SM.getSLocEntry(LocInfo.first);
335 const SrcMgr::ExpansionInfo &Expansion =
E->getExpansion();
337 SourceLocation OldArgLoc = ArgLoc;
338 ArgLoc = Expansion.getExpansionLocStart();
339 if (!Expansion.isMacroArgExpansion()) {
340 if (!MacroLoc.isFileID())
344 Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
345 return llvm::is_contained(NullMacros,
Name);
348 MacroLoc = SM.getExpansionRange(ArgLoc).getBegin();
350 ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
351 if (ArgLoc.isFileID())
356 FileID MacroFID = SM.getFileID(MacroLoc);
357 if (SM.isInFileID(ArgLoc, MacroFID)) {
364 llvm_unreachable(
"getMacroAndArgLocations");
378 bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
379 if (TestLoc.isFileID()) {
383 SourceLocation
Loc = TestLoc, MacroLoc;
386 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(
Loc);
387 const SrcMgr::SLocEntry *
E = &SM.getSLocEntry(LocInfo.first);
388 const SrcMgr::ExpansionInfo &Expansion =
E->getExpansion();
390 Loc = Expansion.getExpansionLocStart();
392 if (!Expansion.isMacroArgExpansion()) {
393 if (
Loc.isFileID()) {
394 return Loc == TestMacroLoc;
403 MacroLoc = SM.getImmediateExpansionRange(
Loc).getBegin();
404 if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
409 Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
410 if (
Loc.isFileID()) {
417 llvm_unreachable(
"expandsFrom");
425 bool findContainingAncestor(DynTypedNode Start, SourceLocation MacroLoc,
426 DynTypedNode &Result) {
431 assert(MacroLoc.isFileID());
434 const auto &
Parents = Context.getParents(Start);
442 for (
const auto &
Parent : Parents) {
443 if (!
Parent.get<InitListExpr>())
452 Loc =
D->getBeginLoc();
453 else if (
const auto *S =
Parent.get<Stmt>())
454 Loc = S->getBeginLoc();
459 if (!expandsFrom(
Loc, MacroLoc)) {
467 llvm_unreachable(
"findContainingAncestor");
473 ArrayRef<StringRef> NullMacros;
474 ClangTidyCheck &Check;
483 NullMacrosStr(Options.get(
"NullMacros",
"NULL")) {
484 StringRef(NullMacrosStr).split(NullMacros,
",");
492 Finder->addMatcher(makeCastSequenceMatcher(),
this);
496 const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(CastSequence);
497 assert(NullCast &&
"Bad Callback. No node provided");
499 if (Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
500 "matchBinopOperands") !=
501 Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
"checkBinopOperands"))
507 CastSequenceVisitor(*Result.Context, NullMacros, *
this)
508 .TraverseStmt(
const_cast<CastExpr *
>(NullCast));
const FunctionDecl * Decl
CharSourceRange Range
SourceRange for the file name.
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.
UseNullptrCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
AST_MATCHER(Decl, declHasNoReturnAttr)
matches a Decl if it has a "no return" attribute of any kind
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
llvm::StringMap< ClangTidyValue > OptionMap