clang-tools 23.0.0git
llvm/RedundantCastingCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "clang/AST/ASTTypeTraits.h"
11#include "clang/AST/Decl.h"
12#include "clang/AST/DeclCXX.h"
13#include "clang/AST/Expr.h"
14#include "clang/AST/ExprCXX.h"
15#include "clang/AST/NestedNameSpecifierBase.h"
16#include "clang/AST/ParentMapContext.h"
17#include "clang/AST/TemplateBase.h"
18#include "clang/AST/TypeBase.h"
19#include "clang/ASTMatchers/ASTMatchFinder.h"
20#include "clang/ASTMatchers/ASTMatchers.h"
21#include "clang/Lex/Lexer.h"
22#include "clang/Tooling/FixIt.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/SmallVector.h"
25#include "llvm/Support/ErrorHandling.h"
26
27using namespace clang::ast_matchers;
28
30
31namespace {
32AST_MATCHER(Expr, isMacroID) { return Node.getExprLoc().isMacroID(); }
33AST_MATCHER_P(OverloadExpr, hasAnyUnresolvedName, ArrayRef<StringRef>, Names) {
34 const DeclarationName DeclName = Node.getName();
35 if (!DeclName.isIdentifier())
36 return false;
37 const IdentifierInfo *II = DeclName.getAsIdentifierInfo();
38 return llvm::any_of(Names, [II](StringRef Name) { return II->isStr(Name); });
39}
40} // namespace
41
42static constexpr StringRef CastFunctionNames[] = {
43 "cast", "cast_or_null", "cast_if_present",
44 "dyn_cast", "dyn_cast_or_null", "dyn_cast_if_present"};
45
46static constexpr StringRef IsaFunctionNames[] = {"isa", "isa_and_nonnull",
47 "isa_and_present"};
48
49void RedundantCastingCheck::registerMatchers(MatchFinder *Finder) {
50 auto IsInLLVMNamespace = hasDeclContext(
51 namespaceDecl(hasName("llvm"), hasDeclContext(translationUnitDecl())));
52 auto AnyCastCalleeName =
53 allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
54 callee(expr(declRefExpr(to(namedDecl(hasAnyName(CastFunctionNames),
55 IsInLLVMNamespace)),
56 templateArgumentLocCountIs(1))
57 .bind("callee"))));
58 auto AnyCastCalleeNameInUninstantiatedTemplate = allOf(
59 unless(isMacroID()), unless(cxxMemberCallExpr()),
60 callee(expr(unresolvedLookupExpr(hasAnyUnresolvedName(CastFunctionNames),
61 templateArgumentLocCountIs(1))
62 .bind("callee"))));
63 Finder->addMatcher(
64 callExpr(AnyCastCalleeName, argumentCountIs(1),
65 optionally(
66 hasParent(callExpr(AnyCastCalleeName).bind("parent_cast"))))
67 .bind("call"),
68 this);
69
70 auto AnyIsaCalleeName =
71 allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
72 callee(expr(declRefExpr(to(namedDecl(hasAnyName(IsaFunctionNames),
73 IsInLLVMNamespace)),
74 hasAnyTemplateArgumentLoc(anything()))
75 .bind("callee"))));
76 Finder->addMatcher(
77 callExpr(AnyIsaCalleeName, argumentCountIs(1)).bind("call"), this);
78
79 auto AnyIsaCalleeNameInUninstantiatedTemplate = allOf(
80 unless(isMacroID()), unless(cxxMemberCallExpr()),
81 callee(expr(unresolvedLookupExpr(hasAnyUnresolvedName(IsaFunctionNames),
82 hasAnyTemplateArgumentLoc(anything()))
83 .bind("callee"))));
84 Finder->addMatcher(
85 callExpr(
86 anyOf(AnyCastCalleeNameInUninstantiatedTemplate,
87 AnyIsaCalleeNameInUninstantiatedTemplate),
88 argumentCountIs(1),
89 optionally(hasAncestor(
90 namespaceDecl(hasName("llvm"), hasParent(translationUnitDecl()))
91 .bind("llvm_ns"))))
92 .bind("call"),
93 this);
94}
95
96static QualType stripPointerOrReference(QualType Ty) {
97 QualType Pointee = Ty->getPointeeType();
98 if (Pointee.isNull())
99 return Ty;
100 return Pointee;
101}
102
103static bool isLLVMNamespace(NestedNameSpecifier NNS) {
104 if (NNS.getKind() != NestedNameSpecifier::Kind::Namespace)
105 return false;
106 auto Pair = NNS.getAsNamespaceAndPrefix();
107 if (Pair.Namespace->getNamespace()->getName() != "llvm")
108 return false;
109 const NestedNameSpecifier::Kind Kind = Pair.Prefix.getKind();
110 return Kind == NestedNameSpecifier::Kind::Null ||
111 Kind == NestedNameSpecifier::Kind::Global;
112}
113
114void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) {
115 const BoundNodes &Nodes = Result.Nodes;
116 const auto *Call = Nodes.getNodeAs<CallExpr>("call");
117
118 ArrayRef<TemplateArgumentLoc> TArgLocs;
119 std::string FuncName;
120 if (const auto *ResolvedCallee = Nodes.getNodeAs<DeclRefExpr>("callee")) {
121 TArgLocs = ResolvedCallee->template_arguments();
122 const auto *F = cast<FunctionDecl>(ResolvedCallee->getDecl());
123 FuncName = F->getName();
124 } else if (const auto *UnresolvedCallee =
125 Nodes.getNodeAs<UnresolvedLookupExpr>("callee")) {
126 const bool IsExplicitlyLLVM =
127 isLLVMNamespace(UnresolvedCallee->getQualifier());
128 const auto *CallerNS = Nodes.getNodeAs<NamedDecl>("llvm_ns");
129 if (!IsExplicitlyLLVM && !CallerNS)
130 return;
131
132 TArgLocs = UnresolvedCallee->template_arguments();
133 FuncName = UnresolvedCallee->getName().getAsString();
134 } else {
135 llvm_unreachable("unexpected callee kind");
136 }
137
138 llvm::SmallVector<CanQualType, 4> TargetTypes;
139 for (const TemplateArgumentLoc &TArgLoc : TArgLocs) {
140 const TemplateArgument TArg = TArgLoc.getArgument();
141 if (TArg.getKind() == TemplateArgument::Type) {
142 const CanQualType TargetTy =
143 TArg.getAsType()->getCanonicalTypeUnqualified();
144 TargetTypes.emplace_back(TargetTy);
145 } else if (TArg.getKind() == TemplateArgument::Pack) {
146 for (const TemplateArgument &E : TArg.pack_elements()) {
147 if (E.getKind() != TemplateArgument::Type)
148 return;
149 TargetTypes.emplace_back(E.getAsType()->getCanonicalTypeUnqualified());
150 }
151 } else {
152 llvm_unreachable("unexpected template argument");
153 }
154 }
155
156 const Expr *Arg = Call->getArg(0);
157 const QualType ArgTy = Arg->getType();
158 const QualType ArgPointeeTy = stripPointerOrReference(ArgTy);
159 const CanQualType FromTy = ArgPointeeTy->getCanonicalTypeUnqualified();
160 const auto *FromDecl = FromTy->getAsCXXRecordDecl();
161 const bool IsIsa = StringRef(FuncName).starts_with("isa");
162 if (!IsIsa && TargetTypes.size() != 1)
163 return;
164
165 for (const CanQualType TargetTy : TargetTypes) {
166 const auto *RetDecl = TargetTy->getAsCXXRecordDecl();
167 const bool IsDerived =
168 FromDecl && RetDecl && FromDecl->isDerivedFrom(RetDecl);
169 if (FromTy != TargetTy && !IsDerived)
170 continue;
171
172 if (IsIsa) {
173 diag(Call->getExprLoc(), "call to '%0' always succeeds") << FuncName;
174 } else {
175 QualType ParentTy;
176 if (const auto *ParentCast = Nodes.getNodeAs<Expr>("parent_cast")) {
177 ParentTy = ParentCast->getType();
178 } else {
179 // IgnoreUnlessSpelledInSource prevents matching implicit casts
180 const TraversalKindScope TmpTraversalKind(*Result.Context, TK_AsIs);
181 for (const DynTypedNode Parent : Result.Context->getParents(*Call)) {
182 if (const auto *ParentCastExpr = Parent.get<CastExpr>()) {
183 ParentTy = ParentCastExpr->getType();
184 break;
185 }
186 }
187 }
188 if (!ParentTy.isNull()) {
189 const CXXRecordDecl *ParentDecl = ParentTy->getAsCXXRecordDecl();
190 if (FromDecl && ParentDecl) {
191 CXXBasePaths Paths(/*FindAmbiguities=*/true,
192 /*RecordPaths=*/false,
193 /*DetectVirtual=*/false);
194 const bool IsDerivedFromParent =
195 FromDecl && ParentDecl &&
196 FromDecl->isDerivedFrom(ParentDecl, Paths);
197 // For the following case a direct `cast<A>(d)` would be ambiguous:
198 // struct A {};
199 // struct B : A {};
200 // struct C : A {};
201 // struct D : B, C {};
202 // So we should not warn for `A *a = cast<C>(d)`.
203 if (IsDerivedFromParent &&
204 Paths.isAmbiguous(ParentTy->getCanonicalTypeUnqualified()))
205 return;
206 }
207 }
208
209 diag(Call->getExprLoc(), "redundant use of '%0'")
210 << FuncName
211 << tooling::fixit::createReplacement(*Call, *Arg, *Result.Context);
212 }
213 // printing the canonical type for a template parameter prints as e.g.
214 // 'type-parameter-0-0'
215 const QualType DiagFromTy(ArgPointeeTy->getUnqualifiedDesugaredType(), 0);
216 diag(
217 Arg->getExprLoc(),
218 "source expression has%select{| pointee}0 type %1%select{|, which is a "
219 "subtype of %3}2",
220 DiagnosticIDs::Note)
221 << Arg->getSourceRange() << ArgTy->isPointerType() << DiagFromTy
222 << (FromTy != TargetTy) << TargetTy;
223 return;
224 }
225}
226
227} // namespace clang::tidy::llvm_check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
static constexpr StringRef CastFunctionNames[]
static bool isLLVMNamespace(NestedNameSpecifier NNS)
static constexpr StringRef IsaFunctionNames[]
static QualType stripPointerOrReference(QualType Ty)
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
AST_MATCHER(BinaryOperator, isRelationalOperator)