11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
18 namespace ast_matchers {
23 namespace llvm_check {
25 void PreferIsaOrDynCastInConditionalsCheck::registerMatchers(
26 MatchFinder *Finder) {
27 auto Condition = hasCondition(implicitCastExpr(has(
29 allOf(unless(
isMacroID()), unless(cxxMemberCallExpr()),
30 anyOf(callee(namedDecl(hasName(
"cast"))),
31 callee(namedDecl(hasName(
"dyn_cast")).bind(
"dyn_cast")))))
35 has(declStmt(containsDeclaration(
37 varDecl(hasInitializer(
38 callExpr(allOf(unless(
isMacroID()), unless(cxxMemberCallExpr()),
39 callee(namedDecl(hasName(
"cast")))))
46 unless(
isMacroID()), unless(cxxMemberCallExpr()),
47 allOf(callee(namedDecl(hasAnyName(
"isa",
"cast",
"cast_or_null",
48 "dyn_cast",
"dyn_cast_or_null"))
50 hasArgument(0, anyOf(declRefExpr().bind(
"arg"),
51 cxxMemberCallExpr().bind(
"arg"))))))
55 traverse(ast_type_traits::TK_AsIs,
57 ifStmt(Any), whileStmt(Any), doStmt(
Condition),
59 allOf(unless(isExpansionInFileMatching(
60 "llvm/include/llvm/Support/Casting.h")),
61 hasOperatorName(
"&&"),
62 hasLHS(implicitCastExpr().bind(
"lhs")),
63 hasRHS(anyOf(implicitCastExpr(has(CallExpression)),
69 void PreferIsaOrDynCastInConditionalsCheck::check(
70 const MatchFinder::MatchResult &Result) {
71 if (
const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>(
"assign")) {
72 SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
73 SourceLocation EndLoc =
74 StartLoc.getLocWithOffset(StringRef(
"cast").size() - 1);
76 diag(MatchedDecl->getBeginLoc(),
77 "cast<> in conditional will assert rather than return a null pointer")
78 << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc),
80 }
else if (
const auto *MatchedDecl =
81 Result.Nodes.getNodeAs<CallExpr>(
"call")) {
82 SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
83 SourceLocation EndLoc =
84 StartLoc.getLocWithOffset(StringRef(
"cast").size() - 1);
87 "cast<> in conditional will assert rather than return a null pointer";
88 if (Result.Nodes.getNodeAs<NamedDecl>(
"dyn_cast"))
89 Message =
"return value from dyn_cast<> not used";
91 diag(MatchedDecl->getBeginLoc(),
Message)
92 << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc),
"isa");
93 }
else if (
const auto *MatchedDecl =
94 Result.Nodes.getNodeAs<BinaryOperator>(
"and")) {
95 const auto *LHS = Result.Nodes.getNodeAs<ImplicitCastExpr>(
"lhs");
96 const auto *RHS = Result.Nodes.getNodeAs<CallExpr>(
"rhs");
97 const auto *Arg = Result.Nodes.getNodeAs<Expr>(
"arg");
98 const auto *Func = Result.Nodes.getNodeAs<NamedDecl>(
"func");
100 assert(LHS &&
"LHS is null");
101 assert(RHS &&
"RHS is null");
102 assert(Arg &&
"Arg is null");
103 assert(Func &&
"Func is null");
105 StringRef LHSString(Lexer::getSourceText(
106 CharSourceRange::getTokenRange(LHS->getSourceRange()),
107 *Result.SourceManager, getLangOpts()));
109 StringRef ArgString(Lexer::getSourceText(
110 CharSourceRange::getTokenRange(Arg->getSourceRange()),
111 *Result.SourceManager, getLangOpts()));
113 if (ArgString != LHSString)
116 StringRef RHSString(Lexer::getSourceText(
117 CharSourceRange::getTokenRange(RHS->getSourceRange()),
118 *Result.SourceManager, getLangOpts()));
120 std::string Replacement(
"isa_and_nonnull");
121 Replacement += RHSString.substr(Func->getName().size());
123 diag(MatchedDecl->getBeginLoc(),
124 "isa_and_nonnull<> is preferred over an explicit test for null "
125 "followed by calling isa<>")
126 << FixItHint::CreateReplacement(SourceRange(MatchedDecl->getBeginLoc(),
127 MatchedDecl->getEndLoc()),