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;
46StatementMatcher makeCastSequenceMatcher(llvm::ArrayRef<StringRef> NameList) {
47 auto ImplicitCastToNull = implicitCastExpr(
48 anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
49 anyOf(hasSourceExpression(gnuNullExpr()),
50 unless(hasImplicitDestinationType(
51 qualType(substTemplateTypeParmType())))),
52 unless(hasSourceExpression(hasType(sugaredNullptrType()))),
53 unless(hasImplicitDestinationType(
56 auto IsOrHasDescendant = [](
auto InnerMatcher) {
57 return anyOf(InnerMatcher, hasDescendant(InnerMatcher));
62 anyOf(castExpr(anyOf(ImplicitCastToNull,
63 explicitCastExpr(hasDescendant(ImplicitCastToNull))),
64 unless(hasAncestor(explicitCastExpr())),
65 unless(hasAncestor(cxxRewrittenBinaryOperator())))
67 cxxRewrittenBinaryOperator(
71 expr().bind(
"matchBinopOperands"),
72 hasEitherOperand(IsOrHasDescendant(
75 hasAncestor(cxxRewrittenBinaryOperator().bind(
76 "checkBinopOperands")))
77 .bind(CastSequence))),
79 unless(hasAncestor(functionDecl(isDefaulted()))))));
82bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc,
83 const SourceManager &SM) {
84 return SM.isWrittenInSameFile(StartLoc, EndLoc);
90void replaceWithNullptr(ClangTidyCheck &Check, SourceManager &SM,
91 SourceLocation StartLoc, SourceLocation EndLoc) {
92 CharSourceRange
Range(SourceRange(StartLoc, EndLoc),
true);
96 SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
97 bool NeedsSpace = isAlphanumeric(*SM.getCharacterData(PreviousLocation));
98 Check.diag(
Range.getBegin(),
"use nullptr") << FixItHint::CreateReplacement(
99 Range, NeedsSpace ?
" nullptr" :
"nullptr");
109StringRef getOutermostMacroName(SourceLocation
Loc,
const SourceManager &SM,
110 const LangOptions &LO) {
111 assert(
Loc.isMacroID());
112 SourceLocation OutermostMacroLoc;
114 while (
Loc.isMacroID()) {
115 OutermostMacroLoc =
Loc;
116 Loc = SM.getImmediateMacroCallerLoc(
Loc);
119 return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
127 MacroArgUsageVisitor(SourceLocation CastLoc,
const SourceManager &SM)
128 : CastLoc(CastLoc), SM(SM) {
129 assert(CastLoc.isFileID());
132 bool TraverseStmt(Stmt *S) {
133 bool VisitedPreviously = Visited;
142 if (!VisitedPreviously) {
143 if (Visited && !CastFound) {
157 bool VisitStmt(Stmt *S) {
158 if (SM.getFileLoc(S->getBeginLoc()) != CastLoc)
162 const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
163 if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
164 Cast->getCastKind() == CK_NullToMemberPointer))
170 bool TraverseInitListExpr(InitListExpr *S) {
177 S->isSemanticForm() ? S : S->getSemanticForm());
180 bool foundInvalid()
const {
return InvalidFound; }
183 SourceLocation CastLoc;
184 const SourceManager &SM;
186 bool Visited =
false;
187 bool CastFound =
false;
188 bool InvalidFound =
false;
204 CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
205 ClangTidyCheck &Check)
206 : SM(Context.getSourceManager()), Context(Context),
207 NullMacros(NullMacros), Check(Check) {}
209 bool TraverseStmt(Stmt *S) {
212 PruneSubtree =
false;
220 bool VisitStmt(Stmt *S) {
221 auto *
C = dyn_cast<CastExpr>(S);
223 if (
auto *
E = dyn_cast<CXXDefaultArgExpr>(S)) {
224 C = dyn_cast<CastExpr>(
E->getExpr());
225 FirstSubExpr =
nullptr;
228 FirstSubExpr =
nullptr;
232 auto* CastSubExpr =
C->getSubExpr()->IgnoreParens();
234 if (isa<CXXNullPtrLiteralExpr>(CastSubExpr)) {
239 FirstSubExpr = CastSubExpr;
241 if (
C->getCastKind() != CK_NullToPointer &&
242 C->getCastKind() != CK_NullToMemberPointer) {
246 SourceLocation StartLoc = FirstSubExpr->getBeginLoc();
247 SourceLocation EndLoc = FirstSubExpr->getEndLoc();
254 if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
255 SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
256 FileLocEnd = SM.getFileLoc(EndLoc);
257 SourceLocation ImmediateMacroArgLoc, MacroLoc;
259 if (!getMacroAndArgLocations(StartLoc, ImmediateMacroArgLoc, MacroLoc) ||
260 ImmediateMacroArgLoc != FileLocStart)
261 return skipSubTree();
263 if (isReplaceableRange(FileLocStart, FileLocEnd, SM) &&
264 allArgUsesValid(
C)) {
265 replaceWithNullptr(Check, SM, FileLocStart, FileLocEnd);
270 if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
271 StringRef OutermostMacroName =
272 getOutermostMacroName(StartLoc, SM, Context.getLangOpts());
275 if (!llvm::is_contained(NullMacros, OutermostMacroName))
276 return skipSubTree();
278 StartLoc = SM.getFileLoc(StartLoc);
279 EndLoc = SM.getFileLoc(EndLoc);
282 if (!isReplaceableRange(StartLoc, EndLoc, SM)) {
283 return skipSubTree();
285 replaceWithNullptr(Check, SM, StartLoc, EndLoc);
298 bool allArgUsesValid(
const CastExpr *
CE) {
299 SourceLocation CastLoc =
CE->getBeginLoc();
303 SourceLocation ArgLoc, MacroLoc;
304 if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
308 DynTypedNode ContainingAncestor;
309 if (!findContainingAncestor(DynTypedNode::create<Stmt>(*
CE), MacroLoc,
318 MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
319 if (
const auto *D = ContainingAncestor.get<
Decl>())
320 ArgUsageVisitor.TraverseDecl(
const_cast<Decl *
>(D));
321 else if (
const auto *S = ContainingAncestor.get<Stmt>())
322 ArgUsageVisitor.TraverseStmt(
const_cast<Stmt *
>(S));
324 llvm_unreachable(
"Unhandled ContainingAncestor node type");
326 return !ArgUsageVisitor.foundInvalid();
337 bool getMacroAndArgLocations(SourceLocation
Loc, SourceLocation &ArgLoc,
338 SourceLocation &MacroLoc) {
339 assert(
Loc.isMacroID() &&
"Only reasonable to call this on macros");
345 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
346 const SrcMgr::SLocEntry *
E = &SM.getSLocEntry(LocInfo.first);
347 const SrcMgr::ExpansionInfo &Expansion =
E->getExpansion();
349 SourceLocation OldArgLoc = ArgLoc;
350 ArgLoc = Expansion.getExpansionLocStart();
351 if (!Expansion.isMacroArgExpansion()) {
352 if (!MacroLoc.isFileID())
356 Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
357 return llvm::is_contained(NullMacros,
Name);
360 MacroLoc = SM.getExpansionRange(ArgLoc).getBegin();
362 ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
363 if (ArgLoc.isFileID())
368 FileID MacroFID = SM.getFileID(MacroLoc);
369 if (SM.isInFileID(ArgLoc, MacroFID)) {
376 llvm_unreachable(
"getMacroAndArgLocations");
390 bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
391 if (TestLoc.isFileID()) {
395 SourceLocation
Loc = TestLoc, MacroLoc;
398 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(
Loc);
399 const SrcMgr::SLocEntry *
E = &SM.getSLocEntry(LocInfo.first);
400 const SrcMgr::ExpansionInfo &Expansion =
E->getExpansion();
402 Loc = Expansion.getExpansionLocStart();
404 if (!Expansion.isMacroArgExpansion()) {
405 if (
Loc.isFileID()) {
406 return Loc == TestMacroLoc;
415 MacroLoc = SM.getImmediateExpansionRange(
Loc).getBegin();
416 if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
421 Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
422 if (
Loc.isFileID()) {
429 llvm_unreachable(
"expandsFrom");
437 bool findContainingAncestor(DynTypedNode Start, SourceLocation MacroLoc,
438 DynTypedNode &Result) {
443 assert(MacroLoc.isFileID());
446 const auto &
Parents = Context.getParents(Start);
454 for (
const auto &
Parent : Parents) {
455 if (!
Parent.get<InitListExpr>())
464 Loc = D->getBeginLoc();
465 else if (
const auto *S =
Parent.get<Stmt>())
466 Loc = S->getBeginLoc();
471 if (!expandsFrom(
Loc, MacroLoc)) {
479 llvm_unreachable(
"findContainingAncestor");
484 ArrayRef<StringRef> NullMacros;
485 ClangTidyCheck &Check;
486 Expr *FirstSubExpr =
nullptr;
487 bool PruneSubtree =
false;
494 NullMacrosStr(Options.get(
"NullMacros",
"NULL")),
495 IgnoredTypes(utils::options::parseStringList(Options.get(
497 "std::_CmpUnspecifiedParam::;^std::__cmp_cat::__unspec"))) {
498 StringRef(NullMacrosStr).split(NullMacros,
",");
508 Finder->addMatcher(makeCastSequenceMatcher(IgnoredTypes),
this);
512 const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(CastSequence);
513 assert(NullCast &&
"Bad Callback. No node provided");
515 if (Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
516 "matchBinopOperands") !=
517 Result.Nodes.getNodeAs<CXXRewrittenBinaryOperator>(
"checkBinopOperands"))
523 CastSequenceVisitor(*Result.Context, NullMacros, *
this)
524 .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(Expr, isMacroID)
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