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