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