clang-tools  11.0.0git
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 #include "clang/Lex/Lexer.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace ast_matchers {
19 AST_MATCHER(Expr, isMacroID) { return Node.getExprLoc().isMacroID(); }
20 } // namespace ast_matchers
21 
22 namespace tidy {
23 namespace llvm_check {
24 
25 void PreferIsaOrDynCastInConditionalsCheck::registerMatchers(
26  MatchFinder *Finder) {
27  auto Condition = hasCondition(implicitCastExpr(has(
28  callExpr(
29  allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
30  anyOf(callee(namedDecl(hasName("cast"))),
31  callee(namedDecl(hasName("dyn_cast")).bind("dyn_cast")))))
32  .bind("call"))));
33 
34  auto Any = anyOf(
35  has(declStmt(containsDeclaration(
36  0,
37  varDecl(hasInitializer(
38  callExpr(allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
39  callee(namedDecl(hasName("cast")))))
40  .bind("assign")))))),
41  Condition);
42 
43  auto CallExpression =
44  callExpr(
45  allOf(
46  unless(isMacroID()), unless(cxxMemberCallExpr()),
47  allOf(callee(namedDecl(hasAnyName("isa", "cast", "cast_or_null",
48  "dyn_cast", "dyn_cast_or_null"))
49  .bind("func")),
50  hasArgument(0, anyOf(declRefExpr().bind("arg"),
51  cxxMemberCallExpr().bind("arg"))))))
52  .bind("rhs");
53 
54  Finder->addMatcher(
55  traverse(ast_type_traits::TK_AsIs,
56  stmt(anyOf(
57  ifStmt(Any), whileStmt(Any), doStmt(Condition),
58  binaryOperator(
59  allOf(unless(isExpansionInFileMatching(
60  "llvm/include/llvm/Support/Casting.h")),
61  hasOperatorName("&&"),
62  hasLHS(implicitCastExpr().bind("lhs")),
63  hasRHS(anyOf(implicitCastExpr(has(CallExpression)),
64  CallExpression))))
65  .bind("and")))),
66  this);
67 }
68 
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);
75 
76  diag(MatchedDecl->getBeginLoc(),
77  "cast<> in conditional will assert rather than return a null pointer")
78  << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc),
79  "dyn_cast");
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);
85 
86  StringRef Message =
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";
90 
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");
99 
100  assert(LHS && "LHS is null");
101  assert(RHS && "RHS is null");
102  assert(Arg && "Arg is null");
103  assert(Func && "Func is null");
104 
105  StringRef LHSString(Lexer::getSourceText(
106  CharSourceRange::getTokenRange(LHS->getSourceRange()),
107  *Result.SourceManager, getLangOpts()));
108 
109  StringRef ArgString(Lexer::getSourceText(
110  CharSourceRange::getTokenRange(Arg->getSourceRange()),
111  *Result.SourceManager, getLangOpts()));
112 
113  if (ArgString != LHSString)
114  return;
115 
116  StringRef RHSString(Lexer::getSourceText(
117  CharSourceRange::getTokenRange(RHS->getSourceRange()),
118  *Result.SourceManager, getLangOpts()));
119 
120  std::string Replacement("isa_and_nonnull");
121  Replacement += RHSString.substr(Func->getName().size());
122 
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()),
128  Replacement);
129  }
130 }
131 
132 } // namespace llvm_check
133 } // namespace tidy
134 } // namespace clang
static bool isMacroID(SourceRange R)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::string Condition
Condition used after the preprocessor directive.