1//===--- PreferIsaOrDynCastInConditionalsCheck.cpp - clang-tidy
2//---------------------===//
3//
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
7//
8//===----------------------------------------------------------------------===//
9
10#include "PreferIsaOrDynCastInConditionalsCheck.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
14
15using namespace clang::ast_matchers;
16
17namespace clang {
18namespace ast_matchers {
19AST_MATCHER(Expr, isMacroID) { return Node.getExprLoc().isMacroID(); }
20} // namespace ast_matchers
21
22namespace tidy::llvm_check {
23
24void PreferIsaOrDynCastInConditionalsCheck::registerMatchers(
25 MatchFinder *Finder) {
26 auto Condition = hasCondition(InnerMatcher: implicitCastExpr(has(
27 callExpr(unless(isMacroID()), unless(cxxMemberCallExpr()),
28 anyOf(callee(InnerMatcher: namedDecl(hasName(Name: "cast"))),
29 callee(InnerMatcher: namedDecl(hasName(Name: "dyn_cast")).bind(ID: "dyn_cast"))))
30 .bind(ID: "call"))));
31
32 auto Any = anyOf(
33 has(declStmt(containsDeclaration(
34 N: 0, InnerMatcher: varDecl(hasInitializer(InnerMatcher: callExpr(unless(isMacroID()),
35 unless(cxxMemberCallExpr()),
36 callee(InnerMatcher: namedDecl(hasName(Name: "cast"))))
37 .bind(ID: "assign")))))),
38 Condition);
39
40 auto CallExpression =
41 callExpr(
42
43 unless(isMacroID()), unless(cxxMemberCallExpr()),
44 callee(InnerMatcher: namedDecl(hasAnyName("isa", "cast", "cast_or_null", "dyn_cast",
45 "dyn_cast_or_null"))
46 .bind(ID: "func")),
47 hasArgument(N: 0, InnerMatcher: mapAnyOf(declRefExpr, cxxMemberCallExpr).bind(ID: "arg")))
48 .bind(ID: "rhs");
49
50 Finder->addMatcher(
51 NodeMatch: traverse(
52 TK: TK_AsIs,
53 InnerMatcher: stmt(anyOf(
54 ifStmt(Any), whileStmt(Any), doStmt(Condition),
55 binaryOperator(unless(isExpansionInFileMatching(
56 RegExp: "llvm/include/llvm/Support/Casting.h")),
57 hasOperatorName(Name: "&&"),
58 hasLHS(InnerMatcher: implicitCastExpr().bind(ID: "lhs")),
59 hasRHS(InnerMatcher: anyOf(implicitCastExpr(has(CallExpression)),
60 CallExpression)))
61 .bind(ID: "and")))),
62 Action: this);
63}
64
65void PreferIsaOrDynCastInConditionalsCheck::check(
66 const MatchFinder::MatchResult &Result) {
67 if (const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>(ID: "assign")) {
68 SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
69 SourceLocation EndLoc =
70 StartLoc.getLocWithOffset(Offset: StringRef("cast").size() - 1);
71
72 diag(Loc: MatchedDecl->getBeginLoc(),
73 Description: "cast<> in conditional will assert rather than return a null pointer")
74 << FixItHint::CreateReplacement(RemoveRange: SourceRange(StartLoc, EndLoc),
75 Code: "dyn_cast");
76 } else if (const auto *MatchedDecl =
77 Result.Nodes.getNodeAs<CallExpr>(ID: "call")) {
78 SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
79 SourceLocation EndLoc =
80 StartLoc.getLocWithOffset(Offset: StringRef("cast").size() - 1);
81
82 StringRef Message =
83 "cast<> in conditional will assert rather than return a null pointer";
84 if (Result.Nodes.getNodeAs<NamedDecl>(ID: "dyn_cast"))
85 Message = "return value from dyn_cast<> not used";
86
87 diag(Loc: MatchedDecl->getBeginLoc(), Description: Message)
88 << FixItHint::CreateReplacement(RemoveRange: SourceRange(StartLoc, EndLoc), Code: "isa");
89 } else if (const auto *MatchedDecl =
90 Result.Nodes.getNodeAs<BinaryOperator>(ID: "and")) {
91 const auto *LHS = Result.Nodes.getNodeAs<ImplicitCastExpr>(ID: "lhs");
92 const auto *RHS = Result.Nodes.getNodeAs<CallExpr>(ID: "rhs");
93 const auto *Arg = Result.Nodes.getNodeAs<Expr>(ID: "arg");
94 const auto *Func = Result.Nodes.getNodeAs<NamedDecl>(ID: "func");
95
96 assert(LHS && "LHS is null");
97 assert(RHS && "RHS is null");
98 assert(Arg && "Arg is null");
99 assert(Func && "Func is null");
100
101 StringRef LHSString(Lexer::getSourceText(
102 Range: CharSourceRange::getTokenRange(LHS->getSourceRange()),
103 SM: *Result.SourceManager, LangOpts: getLangOpts()));
104
105 StringRef ArgString(Lexer::getSourceText(
106 Range: CharSourceRange::getTokenRange(Arg->getSourceRange()),
107 SM: *Result.SourceManager, LangOpts: getLangOpts()));
108
109 if (ArgString != LHSString)
110 return;
111
112 StringRef RHSString(Lexer::getSourceText(
113 Range: CharSourceRange::getTokenRange(RHS->getSourceRange()),
114 SM: *Result.SourceManager, LangOpts: getLangOpts()));
115
116 std::string Replacement("isa_and_nonnull");
117 Replacement += RHSString.substr(Start: Func->getName().size());
118
119 diag(Loc: MatchedDecl->getBeginLoc(),
120 Description: "isa_and_nonnull<> is preferred over an explicit test for null "
121 "followed by calling isa<>")
122 << FixItHint::CreateReplacement(RemoveRange: SourceRange(MatchedDecl->getBeginLoc(),
123 MatchedDecl->getEndLoc()),
124 Code: Replacement);
125 }
126}
127
128} // namespace tidy::llvm_check
129} // namespace clang
130

source code of clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp