10#include "../utils/Matchers.h"
11#include "../utils/OptionsUtils.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"
24const char CastSequence[] =
"sequence";
27 const Type *DesugaredType =
Node.getUnqualifiedDesugaredType();
28 if (
const auto *BT = dyn_cast<BuiltinType>(DesugaredType))
29 return BT->getKind() == BuiltinType::NullPtr;
38StatementMatcher makeCastSequenceMatcher(llvm::ArrayRef<StringRef> NameList) {
39 auto ImplicitCastToNull = implicitCastExpr(
40 anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
41 unless(hasImplicitDestinationType(qualType(substTemplateTypeParmType()))),
42 unless(hasSourceExpression(hasType(sugaredNullptrType()))),
43 unless(hasImplicitDestinationType(
46 auto IsOrHasDescendant = [](
auto InnerMatcher) {
47 return anyOf(InnerMatcher, hasDescendant(InnerMatcher));
52 anyOf(castExpr(anyOf(ImplicitCastToNull,
53 explicitCastExpr(hasDescendant(ImplicitCastToNull))),
54 unless(hasAncestor(explicitCastExpr())),
55 unless(hasAncestor(cxxRewrittenBinaryOperator())))
57 cxxRewrittenBinaryOperator(
61 expr().bind(
"matchBinopOperands"),
62 hasEitherOperand(IsOrHasDescendant(
65 hasAncestor(cxxRewrittenBinaryOperator().bind(
66 "checkBinopOperands")))
67 .bind(CastSequence))),
69 unless(hasAncestor(functionDecl(isDefaulted()))))));
72bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc,
73 const SourceManager &SM) {
74 return SM.isWrittenInSameFile(StartLoc, EndLoc);
80void replaceWithNullptr(ClangTidyCheck &Check, SourceManager &SM,
81 SourceLocation StartLoc, SourceLocation EndLoc) {
82 CharSourceRange
Range(SourceRange(StartLoc, EndLoc),
true);
86 SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
87 bool NeedsSpace = isAlphanumeric(*SM.getCharacterData(PreviousLocation));
88 Check.diag(
Range.getBegin(),
"use nullptr") << FixItHint::CreateReplacement(
89 Range, NeedsSpace ?
" nullptr" :
"nullptr");
99StringRef getOutermostMacroName(SourceLocation
Loc,
const SourceManager &SM,
100 const LangOptions &LO) {
101 assert(
Loc.isMacroID());
102 SourceLocation OutermostMacroLoc;
104 while (
Loc.isMacroID()) {
105 OutermostMacroLoc =
Loc;
106 Loc = SM.getImmediateMacroCallerLoc(
Loc);
109 return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
117 MacroArgUsageVisitor(SourceLocation CastLoc,
const SourceManager &SM)
118 : CastLoc(CastLoc), SM(SM) {
119 assert(CastLoc.isFileID());
122 bool TraverseStmt(Stmt *S) {
123 bool VisitedPreviously = Visited;
132 if (!VisitedPreviously) {
133 if (Visited && !CastFound) {
147 bool VisitStmt(Stmt *S) {
148 if (SM.getFileLoc(S->getBeginLoc()) != CastLoc)
152 const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
153 if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
154 Cast->getCastKind() == CK_NullToMemberPointer))
160 bool TraverseInitListExpr(InitListExpr *S) {
167 S->isSemanticForm() ? S : S->getSemanticForm());
170 bool foundInvalid()
const {
return InvalidFound; }
173 SourceLocation CastLoc;
174 const SourceManager &SM;
176 bool Visited =
false;
177 bool CastFound =
false;
178 bool InvalidFound =
false;
194 CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
195 ClangTidyCheck &Check)
196 : SM(Context.getSourceManager()), Context(Context),
197 NullMacros(NullMacros), Check(Check) {}
199 bool TraverseStmt(Stmt *S) {
202 PruneSubtree =
false;
210 bool VisitStmt(Stmt *S) {
211 auto *
C = dyn_cast<CastExpr>(S);
213 if (
auto *
E = dyn_cast<CXXDefaultArgExpr>(S)) {
214 C = dyn_cast<CastExpr>(
E->getExpr());
215 FirstSubExpr =
nullptr;
218 FirstSubExpr =
nullptr;
222 auto* CastSubExpr =
C->getSubExpr()->IgnoreParens();
224 if (isa<CXXNullPtrLiteralExpr>(CastSubExpr)) {
229 FirstSubExpr = CastSubExpr;
231 if (
C->getCastKind() != CK_NullToPointer &&
232 C->getCastKind() != CK_NullToMemberPointer) {
236 SourceLocation StartLoc = FirstSubExpr->getBeginLoc();
237 SourceLocation EndLoc = FirstSubExpr->getEndLoc();
244 if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
245 SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
246 FileLocEnd = SM.getFileLoc(EndLoc);
247 SourceLocation ImmediateMacroArgLoc, MacroLoc;
249 if (!getMacroAndArgLocations(StartLoc, ImmediateMacroArgLoc, MacroLoc) ||
250 ImmediateMacroArgLoc != FileLocStart)
251 return skipSubTree();
253 if (isReplaceableRange(FileLocStart, FileLocEnd, SM) &&
254 allArgUsesValid(
C)) {
255 replaceWithNullptr(Check, SM, FileLocStart, FileLocEnd);
260 if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
261 StringRef OutermostMacroName =
262 getOutermostMacroName(StartLoc, SM, Context.getLangOpts());
265 if (!llvm::is_contained(NullMacros, OutermostMacroName))
266 return skipSubTree();
268 StartLoc = SM.getFileLoc(StartLoc);
269 EndLoc = SM.getFileLoc(EndLoc);
272 if (!isReplaceableRange(StartLoc, EndLoc, SM)) {
273 return skipSubTree();
275 replaceWithNullptr(Check, SM, StartLoc, EndLoc);
288 bool allArgUsesValid(
const CastExpr *
CE) {
289 SourceLocation CastLoc =
CE->getBeginLoc();
293 SourceLocation ArgLoc, MacroLoc;
294 if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
298 DynTypedNode ContainingAncestor;
299 if (!findContainingAncestor(DynTypedNode::create<Stmt>(*
CE), MacroLoc,
308 MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
309 if (
const auto *D = ContainingAncestor.get<
Decl>())
310 ArgUsageVisitor.TraverseDecl(
const_cast<Decl *
>(D));
311 else if (
const auto *S = ContainingAncestor.get<Stmt>())
312 ArgUsageVisitor.TraverseStmt(
const_cast<Stmt *
>(S));
314 llvm_unreachable(
"Unhandled ContainingAncestor node type");
316 return !ArgUsageVisitor.foundInvalid();
327 bool getMacroAndArgLocations(SourceLocation
Loc, SourceLocation &ArgLoc,
328 SourceLocation &MacroLoc) {
329 assert(
Loc.isMacroID() &&
"Only reasonable to call this on macros");
335 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
336 const SrcMgr::SLocEntry *
E = &SM.getSLocEntry(LocInfo.first);
337 const SrcMgr::ExpansionInfo &Expansion =
E->getExpansion();
339 SourceLocation OldArgLoc = ArgLoc;
340 ArgLoc = Expansion.getExpansionLocStart();
341 if (!Expansion.isMacroArgExpansion()) {
342 if (!MacroLoc.isFileID())
346 Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
347 return llvm::is_contained(NullMacros,
Name);
350 MacroLoc = SM.getExpansionRange(ArgLoc).getBegin();
352 ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
353 if (ArgLoc.isFileID())
358 FileID MacroFID = SM.getFileID(MacroLoc);
359 if (SM.isInFileID(ArgLoc, MacroFID)) {
366 llvm_unreachable(
"getMacroAndArgLocations");
380 bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
381 if (TestLoc.isFileID()) {
385 SourceLocation
Loc = TestLoc, MacroLoc;
388 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(
Loc);
389 const SrcMgr::SLocEntry *
E = &SM.getSLocEntry(LocInfo.first);
390 const SrcMgr::ExpansionInfo &Expansion =
E->getExpansion();
392 Loc = Expansion.getExpansionLocStart();
394 if (!Expansion.isMacroArgExpansion()) {
395 if (
Loc.isFileID()) {
396 return Loc == TestMacroLoc;
405 MacroLoc = SM.getImmediateExpansionRange(
Loc).getBegin();
406 if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
411 Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
412 if (
Loc.isFileID()) {
419 llvm_unreachable(
"expandsFrom");
427 bool findContainingAncestor(DynTypedNode Start, SourceLocation MacroLoc,
428 DynTypedNode &Result) {
433 assert(MacroLoc.isFileID());
436 const auto &
Parents = Context.getParents(Start);
444 for (
const auto &
Parent : Parents) {
445 if (!
Parent.get<InitListExpr>())
454 Loc = D->getBeginLoc();
455 else if (
const auto *S =
Parent.get<Stmt>())
456 Loc = S->getBeginLoc();
461 if (!expandsFrom(
Loc, MacroLoc)) {
469 llvm_unreachable(
"findContainingAncestor");
474 ArrayRef<StringRef> NullMacros;
475 ClangTidyCheck &Check;
476 Expr *FirstSubExpr =
nullptr;
477 bool PruneSubtree =
false;
484 NullMacrosStr(Options.get(
"NullMacros",
"NULL")),
485 IgnoredTypes(utils::options::parseStringList(Options.get(
487 "std::_CmpUnspecifiedParam::;^std::__cmp_cat::__unspec"))) {
488 StringRef(NullMacrosStr).split(NullMacros,
",");
498 Finder->addMatcher(makeCastSequenceMatcher(IgnoredTypes),
this);
502 const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(CastSequence);
503 assert(NullCast &&
"Bad Callback. No node provided");
505 if (Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
506 "matchBinopOperands") !=
507 Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
"checkBinopOperands"))
513 CastSequenceVisitor(*Result.Context, NullMacros, *
this)
514 .TraverseStmt(
const_cast<CastExpr *
>(NullCast));
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
CharSourceRange Range
SourceRange for the file name.
::clang::DynTypedNode Node
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
inline ::clang::ast_matchers::internal::Matcher< QualType > matchesAnyListedTypeName(llvm::ArrayRef< StringRef > NameList)
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