1 //===--- PreferIsaOrDynCastInConditionalsCheck.cpp - clang-tidy
2 //---------------------===//
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8 //===----------------------------------------------------------------------===//
10 #include "PreferIsaOrDynCastInConditionalsCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
15 using namespace clang::ast_matchers
;
18 namespace ast_matchers
{
19 AST_MATCHER(Expr
, isMacroID
) { return Node
.getExprLoc().isMacroID(); }
20 } // namespace ast_matchers
22 namespace tidy::llvm_check
{
24 void PreferIsaOrDynCastInConditionalsCheck::registerMatchers(
25 MatchFinder
*Finder
) {
26 auto Condition
= hasCondition(implicitCastExpr(has(
27 callExpr(unless(isMacroID()), unless(cxxMemberCallExpr()),
28 anyOf(callee(namedDecl(hasName("cast"))),
29 callee(namedDecl(hasName("dyn_cast")).bind("dyn_cast"))))
33 has(declStmt(containsDeclaration(
34 0, varDecl(hasInitializer(callExpr(unless(isMacroID()),
35 unless(cxxMemberCallExpr()),
36 callee(namedDecl(hasName("cast"))))
43 unless(isMacroID()), unless(cxxMemberCallExpr()),
44 callee(namedDecl(hasAnyName("isa", "cast", "cast_or_null", "dyn_cast",
47 hasArgument(0, mapAnyOf(declRefExpr
, cxxMemberCallExpr
).bind("arg")))
54 ifStmt(Any
), whileStmt(Any
), doStmt(Condition
),
55 binaryOperator(unless(isExpansionInFileMatching(
56 "llvm/include/llvm/Support/Casting.h")),
57 hasOperatorName("&&"),
58 hasLHS(implicitCastExpr().bind("lhs")),
59 hasRHS(anyOf(implicitCastExpr(has(CallExpression
)),
65 void PreferIsaOrDynCastInConditionalsCheck::check(
66 const MatchFinder::MatchResult
&Result
) {
67 if (const auto *MatchedDecl
= Result
.Nodes
.getNodeAs
<CallExpr
>("assign")) {
68 SourceLocation StartLoc
= MatchedDecl
->getCallee()->getExprLoc();
69 SourceLocation EndLoc
=
70 StartLoc
.getLocWithOffset(StringRef("cast").size() - 1);
72 diag(MatchedDecl
->getBeginLoc(),
73 "cast<> in conditional will assert rather than return a null pointer")
74 << FixItHint::CreateReplacement(SourceRange(StartLoc
, EndLoc
),
76 } else if (const auto *MatchedDecl
=
77 Result
.Nodes
.getNodeAs
<CallExpr
>("call")) {
78 SourceLocation StartLoc
= MatchedDecl
->getCallee()->getExprLoc();
79 SourceLocation EndLoc
=
80 StartLoc
.getLocWithOffset(StringRef("cast").size() - 1);
83 "cast<> in conditional will assert rather than return a null pointer";
84 if (Result
.Nodes
.getNodeAs
<NamedDecl
>("dyn_cast"))
85 Message
= "return value from dyn_cast<> not used";
87 diag(MatchedDecl
->getBeginLoc(), Message
)
88 << FixItHint::CreateReplacement(SourceRange(StartLoc
, EndLoc
), "isa");
89 } else if (const auto *MatchedDecl
=
90 Result
.Nodes
.getNodeAs
<BinaryOperator
>("and")) {
91 const auto *LHS
= Result
.Nodes
.getNodeAs
<ImplicitCastExpr
>("lhs");
92 const auto *RHS
= Result
.Nodes
.getNodeAs
<CallExpr
>("rhs");
93 const auto *Arg
= Result
.Nodes
.getNodeAs
<Expr
>("arg");
94 const auto *Func
= Result
.Nodes
.getNodeAs
<NamedDecl
>("func");
96 assert(LHS
&& "LHS is null");
97 assert(RHS
&& "RHS is null");
98 assert(Arg
&& "Arg is null");
99 assert(Func
&& "Func is null");
101 StringRef
LHSString(Lexer::getSourceText(
102 CharSourceRange::getTokenRange(LHS
->getSourceRange()),
103 *Result
.SourceManager
, getLangOpts()));
105 StringRef
ArgString(Lexer::getSourceText(
106 CharSourceRange::getTokenRange(Arg
->getSourceRange()),
107 *Result
.SourceManager
, getLangOpts()));
109 if (ArgString
!= LHSString
)
112 StringRef
RHSString(Lexer::getSourceText(
113 CharSourceRange::getTokenRange(RHS
->getSourceRange()),
114 *Result
.SourceManager
, getLangOpts()));
116 std::string
Replacement("isa_and_nonnull");
117 Replacement
+= RHSString
.substr(Func
->getName().size());
119 diag(MatchedDecl
->getBeginLoc(),
120 "isa_and_nonnull<> is preferred over an explicit test for null "
121 "followed by calling isa<>")
122 << FixItHint::CreateReplacement(SourceRange(MatchedDecl
->getBeginLoc(),
123 MatchedDecl
->getEndLoc()),
128 } // namespace tidy::llvm_check