clang-tools 10.0.0

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
10 #include "PreferIsaOrDynCastInConditionalsCheck.h"
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
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::readability::isMacroID
static bool isMacroID(SourceRange R)
Definition: IsolateDeclarationCheck.cpp:52
clang::ast_matchers::AST_MATCHER
AST_MATCHER(Expr, isMacroID)
Definition: PreferIsaOrDynCastInConditionalsCheck.cpp:18
clang::tidy::modernize::Message
constexpr llvm::StringLiteral Message
Definition: UseTrailingReturnTypeCheck.cpp:101
PreferIsaOrDynCastInConditionalsCheck.h
clang::clangd::getTokenRange
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:227
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
Condition
std::string Condition
Condition used after the preprocessor directive.
Definition: RedundantPreprocessorCheck.cpp:21