22 MatchFinder *Finder) {
23 const auto AnyCalleeName = [](ArrayRef<StringRef> CalleeName) {
24 return allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
25 callee(expr(ignoringImpCasts(
26 declRefExpr(to(namedDecl(hasAnyName(CalleeName))),
27 hasAnyTemplateArgumentLoc(anything()))
31 auto CondExpr = hasCondition(implicitCastExpr(
32 has(callExpr(AnyCalleeName({
"cast",
"dyn_cast"})).bind(
"cond"))));
34 const auto CondExprOrCondVar =
35 anyOf(hasConditionVariableStatement(containsDeclaration(
36 0, varDecl(hasInitializer(callExpr(AnyCalleeName({
"cast"}))))
40 const auto CallWithBindedArg =
43 {
"isa",
"cast",
"cast_or_null",
"dyn_cast",
"dyn_cast_or_null"}),
44 hasArgument(0, mapAnyOf(declRefExpr, cxxMemberCallExpr).bind(
"arg")))
47 Finder->addMatcher(ifStmt(CondExprOrCondVar),
this);
48 Finder->addMatcher(forStmt(CondExprOrCondVar),
this);
49 Finder->addMatcher(whileStmt(CondExprOrCondVar),
this);
50 Finder->addMatcher(doStmt(CondExpr),
this);
51 Finder->addMatcher(binaryOperator(hasRHS(ignoringImpCasts(CallWithBindedArg)),
52 hasLHS(implicitCastExpr().bind(
"lhs")),
53 hasOperatorName(
"&&"),
54 unless(isExpansionInFileMatching(
55 "llvm/include/llvm/Support/Casting.h")))
61 const MatchFinder::MatchResult &Result) {
62 const auto *Callee = Result.Nodes.getNodeAs<DeclRefExpr>(
"callee");
64 assert(Callee &&
"Callee should be binded if anything is matched");
70 const SourceLocation StartLoc = Callee->getLocation();
71 const SourceLocation EndLoc = Callee->getNameInfo().getEndLoc();
73 if (Result.Nodes.getNodeAs<VarDecl>(
"var")) {
75 "cast<> in conditional will assert rather than return a null pointer")
76 << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc),
78 }
else if (Result.Nodes.getNodeAs<CallExpr>(
"cond")) {
80 "cast<> in conditional will assert rather than return a null pointer";
81 if (Callee->getDecl()->getName() ==
"dyn_cast")
82 Message =
"return value from dyn_cast<> not used";
84 diag(StartLoc, Message)
85 << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc),
"isa");
86 }
else if (Result.Nodes.getNodeAs<BinaryOperator>(
"and")) {
87 const auto *LHS = Result.Nodes.getNodeAs<ImplicitCastExpr>(
"lhs");
88 const auto *RHS = Result.Nodes.getNodeAs<CallExpr>(
"rhs");
89 const auto *Arg = Result.Nodes.getNodeAs<Expr>(
"arg");
91 assert(LHS &&
"LHS is null");
92 assert(RHS &&
"RHS is null");
93 assert(Arg &&
"Arg is null");
95 const auto GetText = [&](SourceRange R) {
96 return Lexer::getSourceText(CharSourceRange::getTokenRange(R),
97 *Result.SourceManager, getLangOpts());
100 const StringRef LHSString = GetText(LHS->getSourceRange());
101 const StringRef ArgString = GetText(Arg->getSourceRange());
103 if (ArgString != LHSString)
109 const std::string Replacement = llvm::formatv(
110 "{}isa_and_nonnull{}",
111 GetText(Callee->getQualifierLoc().getSourceRange()),
112 GetText(SourceRange(Callee->getLAngleLoc(), RHS->getEndLoc())));
114 diag(LHS->getBeginLoc(),
115 "isa_and_nonnull<> is preferred over an explicit test for null "
116 "followed by calling isa<>")
117 << FixItHint::CreateReplacement(
118 SourceRange(LHS->getBeginLoc(), RHS->getEndLoc()), Replacement);
121 R
"(One of "var", "cond" and "and" should be binded if anything is matched)");