clang-tools 17.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
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
25 MatchFinder *Finder) {
26 auto Condition = hasCondition(implicitCastExpr(has(
27 callExpr(
28 allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
29 anyOf(callee(namedDecl(hasName("cast"))),
30 callee(namedDecl(hasName("dyn_cast")).bind("dyn_cast")))))
31 .bind("call"))));
32
33 auto Any = anyOf(
34 has(declStmt(containsDeclaration(
35 0,
36 varDecl(hasInitializer(
37 callExpr(allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
38 callee(namedDecl(hasName("cast")))))
39 .bind("assign")))))),
40 Condition);
41
42 auto CallExpression =
43 callExpr(
44 allOf(
45 unless(isMacroID()), unless(cxxMemberCallExpr()),
46 allOf(callee(namedDecl(hasAnyName("isa", "cast", "cast_or_null",
47 "dyn_cast", "dyn_cast_or_null"))
48 .bind("func")),
49 hasArgument(
50 0,
51 mapAnyOf(declRefExpr, cxxMemberCallExpr).bind("arg")))))
52 .bind("rhs");
53
54 Finder->addMatcher(
55 traverse(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
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 tidy::llvm_check
133} // namespace clang
std::string Condition
Condition used after the preprocessor directive.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
AST_MATCHER(Decl, declHasNoReturnAttr)
matches a Decl if it has a "no return" attribute of any kind
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//