clang 24.0.0git
ForwardDeclChecker.cpp
Go to the documentation of this file.
1//=======- ForwardDeclChecker.cpp --------------------------------*- C++ -*-==//
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
9#include "ASTUtils.h"
10#include "DiagOutputUtils.h"
11#include "PtrTypesSemantics.h"
12#include "clang/AST/Decl.h"
13#include "clang/AST/DeclCXX.h"
22#include "llvm/ADT/DenseSet.h"
23#include "llvm/Support/SaveAndRestore.h"
24
25using namespace clang;
26using namespace ento;
27
28namespace {
29
30class ForwardDeclChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
31 BugType Bug;
32 mutable BugReporter *BR = nullptr;
33 mutable RetainTypeChecker RTC;
34 mutable llvm::DenseSet<const Type *> SystemTypes;
35
36public:
37 ForwardDeclChecker()
38 : Bug(this, "Forward declared member or local variable or parameter",
39 "WebKit coding guidelines") {}
40
41 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
42 BugReporter &BRArg) const {
43 BR = &BRArg;
44
45 // The calls to checkAST* from AnalysisConsumer don't
46 // visit template instantiations or lambda classes. We
47 // want to visit those, so we make our own RecursiveASTVisitor.
48 struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
49 using Base = RecursiveASTVisitor<LocalVisitor>;
50
51 const ForwardDeclChecker *Checker;
52 Decl *DeclWithIssue{nullptr};
53
54 explicit LocalVisitor(const ForwardDeclChecker *Checker)
55 : Checker(Checker) {
56 assert(Checker);
57 }
58
59 bool shouldVisitTemplateInstantiations() const { return true; }
60 bool shouldVisitImplicitCode() const { return false; }
61
62 bool VisitTypedefDecl(TypedefDecl *TD) {
63 Checker->visitTypedef(TD);
64 return true;
65 }
66
67 bool VisitRecordDecl(const RecordDecl *RD) {
68 Checker->visitRecordDecl(RD, DeclWithIssue);
69 return true;
70 }
71
72 bool TraverseDecl(Decl *D) {
73 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
74 if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
75 DeclWithIssue = D;
76 return Base::TraverseDecl(D);
77 }
78
79 bool VisitVarDecl(VarDecl *V) {
80 if (V->isLocalVarDecl())
81 Checker->visitVarDecl(V, DeclWithIssue);
82 return true;
83 }
84
85 bool VisitCallExpr(const CallExpr *CE) {
86 Checker->visitCallExpr(CE, DeclWithIssue);
87 return true;
88 }
89
90 bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {
91 Checker->visitConstructExpr(CE, DeclWithIssue);
92 return true;
93 }
94
95 bool VisitObjCMessageExpr(const ObjCMessageExpr *ObjCMsgExpr) {
96 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
97 return true;
98 }
99 };
100
101 LocalVisitor visitor(this);
102 RTC.visitTranslationUnitDecl(TUD);
103 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
104 }
105
106 void visitTypedef(const TypedefDecl *TD) const {
107 RTC.visitTypedef(TD);
108 auto QT = TD->getUnderlyingType().getCanonicalType();
109 assert(BR && "expected nonnull BugReporter");
110 if (BR->getSourceManager().isInSystemHeader(TD->getBeginLoc())) {
111 if (auto *Type = QT.getTypePtrOrNull())
112 SystemTypes.insert(Type);
113 }
114 }
115
116 bool isUnknownType(QualType QT) const {
117 auto *CanonicalType = QT.getCanonicalType().getTypePtrOrNull();
118 if (!CanonicalType)
119 return false;
120 auto PointeeQT = CanonicalType->getPointeeType();
121 auto *PointeeType = PointeeQT.getTypePtrOrNull();
122 if (!PointeeType)
123 return false;
124 auto *R = PointeeType->getAsCXXRecordDecl();
125 if (!R) // Forward declaration of a Objective-C interface is safe.
126 return false;
127 auto Name = R->getName();
128 if (R->hasDefinition())
129 return false;
130 // Find a definition amongst template declarations.
131 if (auto *Specialization = dyn_cast<ClassTemplateSpecializationDecl>(R)) {
132 if (auto *S = Specialization->getSpecializedTemplate()) {
133 for (S = S->getMostRecentDecl(); S; S = S->getPreviousDecl()) {
134 if (S->isThisDeclarationADefinition())
135 return false;
136 }
137 }
138 }
139 return !RTC.isUnretained(QT) && !SystemTypes.contains(CanonicalType) &&
140 !SystemTypes.contains(PointeeType) && !Name.starts_with("Opaque") &&
141 Name != "_NSZone";
142 }
143
144 void visitRecordDecl(const RecordDecl *RD, const Decl *DeclWithIssue) const {
146 return;
147
148 if (RD->isImplicit() || RD->isLambda())
149 return;
150
151 const auto RDLocation = RD->getLocation();
152 if (!RDLocation.isValid())
153 return;
154
155 const auto Kind = RD->getTagKind();
156 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
157 return;
158
159 assert(BR && "expected nonnull BugReporter");
160 if (BR->getSourceManager().isInSystemHeader(RDLocation))
161 return;
162
163 // Ref-counted smartpointers actually have raw-pointer to uncounted type as
164 // a member but we trust them to handle it correctly.
165 auto R = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
166 if (!R || isRefCounted(R) || isCheckedPtr(R) || isRetainPtrOrOSPtr(R))
167 return;
168
169 for (auto *Member : RD->fields()) {
170 auto QT = Member->getType();
171 if (isUnknownType(QT)) {
172 SmallString<100> Buf;
173 llvm::raw_svector_ostream Os(Buf);
174
175 const std::string TypeName = QT.getAsString();
176 Os << "Member variable ";
178 Os << " uses a forward declared type '" << TypeName << "'";
179
180 const SourceLocation SrcLocToReport = Member->getBeginLoc();
181 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
182 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
183 Report->addRange(Member->getSourceRange());
184 Report->setDeclWithIssue(DeclWithIssue);
185 BR->emitReport(std::move(Report));
186 }
187 }
188 }
189
190 void visitVarDecl(const VarDecl *V, const Decl *DeclWithIssue) const {
191 assert(BR && "expected nonnull BugReporter");
192 if (BR->getSourceManager().isInSystemHeader(V->getBeginLoc()))
193 return;
194
195 auto QT = V->getType();
196 if (!isUnknownType(QT))
197 return;
198
199 SmallString<100> Buf;
200 llvm::raw_svector_ostream Os(Buf);
201 Os << "Local variable ";
203
204 reportBug(V->getBeginLoc(), V->getSourceRange(), DeclWithIssue, Os.str(),
205 QT);
206 }
207
208 void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const {
209 assert(BR && "expected nonnull BugReporter");
210 if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
211 return;
212
213 if (auto *F = CE->getDirectCallee()) {
214 // Skip the first argument for overloaded member operators (e. g. lambda
215 // or std::function call operator).
216 unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa<CXXMethodDecl>(F);
217
218 for (auto P = F->param_begin();
219 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
220 visitCallArg(CE->getArg(ArgIdx), *P, DeclWithIssue);
221 }
222 }
223
224 void visitConstructExpr(const CXXConstructExpr *CE,
225 const Decl *DeclWithIssue) const {
226 assert(BR && "expected nonnull BugReporter");
227 if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
228 return;
229
230 if (const CXXMethodDecl *F = CE->getConstructor()) {
231 unsigned ArgIdx = 0;
232 for (auto P = F->param_begin();
233 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
234 visitCallArg(CE->getArg(ArgIdx), *P, DeclWithIssue);
235 }
236 }
237
238 void visitObjCMessageExpr(const ObjCMessageExpr *E,
239 const Decl *DeclWithIssue) const {
240 assert(BR && "expected nonnull BugReporter");
241 if (BR->getSourceManager().isInSystemHeader(E->getExprLoc()))
242 return;
243
244 if (auto *Receiver = E->getInstanceReceiver()) {
245 Receiver = Receiver->IgnoreParenCasts();
246 if (isUnknownType(E->getReceiverType()))
247 reportUnknownReceiverType(Receiver, DeclWithIssue);
248 }
249
250 auto *MethodDecl = E->getMethodDecl();
251 if (!MethodDecl)
252 return;
253
254 auto ArgCount = E->getNumArgs();
255 for (unsigned i = 0; i < ArgCount && i < MethodDecl->param_size(); ++i)
256 visitCallArg(E->getArg(i), MethodDecl->getParamDecl(i), DeclWithIssue);
257 }
258
259 void visitCallArg(const Expr *Arg, const ParmVarDecl *Param,
260 const Decl *DeclWithIssue) const {
261 auto *ArgExpr = Arg->IgnoreParenCasts();
262 while (ArgExpr) {
263 ArgExpr = ArgExpr->IgnoreParenCasts();
264 if (auto *InnerCE = dyn_cast<CallExpr>(ArgExpr)) {
265 if (auto *InnerCallee = InnerCE->getDirectCallee()) {
266 if (isStdOrWTFMove(InnerCallee) && InnerCE->getNumArgs() == 1) {
267 ArgExpr = InnerCE->getArg(0);
268 continue;
269 }
270 }
271 }
272 if (auto *UO = dyn_cast<UnaryOperator>(ArgExpr)) {
273 auto OpCode = UO->getOpcode();
274 if (OpCode == UO_Deref || OpCode == UO_AddrOf) {
275 ArgExpr = UO->getSubExpr();
276 continue;
277 }
278 }
279 break;
280 }
281
282 if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(ArgExpr)) {
283 if (isOwnerPtrType(MemberCallExpr->getObjectType()))
284 return;
285 }
286
287 if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(ArgExpr)) {
288 auto *Method = dyn_cast_or_null<CXXMethodDecl>(OpCE->getDirectCallee());
289 if (Method && isOwnerPtr(safeGetName(Method->getParent()))) {
290 if (OpCE->getOperator() == OO_Star && OpCE->getNumArgs() == 1)
291 return;
292 }
293 }
294
295 if (isNullPtr(ArgExpr) || isa<IntegerLiteral>(ArgExpr) ||
296 isa<CXXDefaultArgExpr>(ArgExpr))
297 return;
298
299 if (auto *DRE = dyn_cast<DeclRefExpr>(ArgExpr)) {
300 if (auto *ValDecl = DRE->getDecl()) {
301 if (isa<ParmVarDecl>(ValDecl))
302 return;
303 }
304 }
305
306 QualType ArgType = Param->getType();
307 if (!isUnknownType(ArgType))
308 return;
309
310 reportUnknownArgType(Arg, Param, DeclWithIssue);
311 }
312
313 void reportUnknownArgType(const Expr *CA, const ParmVarDecl *Param,
314 const Decl *DeclWithIssue) const {
315 assert(CA);
316
317 SmallString<100> Buf;
318 llvm::raw_svector_ostream Os(Buf);
319
320 const std::string paramName = safeGetName(Param);
321 Os << "Call argument";
322 if (!paramName.empty()) {
323 Os << " for parameter ";
324 printQuotedQualifiedName(Os, Param);
325 }
326
327 reportBug(CA->getExprLoc(), CA->getSourceRange(), DeclWithIssue, Os.str(),
328 Param->getType());
329 }
330
331 void reportUnknownReceiverType(const Expr *Receiver,
332 const Decl *DeclWithIssue) const {
333 assert(Receiver);
334 reportBug(Receiver->getExprLoc(), Receiver->getSourceRange(), DeclWithIssue,
335 "Receiver", Receiver->getType());
336 }
337
338 void reportBug(const SourceLocation &SrcLoc, const SourceRange &SrcRange,
339 const Decl *DeclWithIssue, const StringRef &Description,
340 QualType Type) const {
341 SmallString<100> Buf;
342 llvm::raw_svector_ostream Os(Buf);
343
344 const std::string TypeName = Type.getAsString();
345 Os << Description << " uses a forward declared type '" << TypeName << "'";
346
347 assert(BR && "expected nonnull BugReporter");
348 PathDiagnosticLocation BSLoc(SrcLoc, BR->getSourceManager());
349 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
350 Report->addRange(SrcRange);
351 Report->setDeclWithIssue(DeclWithIssue);
352 BR->emitReport(std::move(Report));
353 }
354};
355
356} // namespace
357
358void ento::registerForwardDeclChecker(CheckerManager &Mgr) {
359 Mgr.registerChecker<ForwardDeclChecker>();
360}
361
362bool ento::shouldRegisterForwardDeclChecker(const CheckerManager &) {
363 return true;
364}
#define V(N, I)
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Expr * getArg(unsigned Arg)
Return the specified argument.
Definition ExprCXX.h:1696
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Definition ExprCXX.h:1616
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3216
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition Expr.h:3195
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
Definition DeclBase.h:602
SourceLocation getLocation() const
Definition DeclBase.h:448
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition Expr.cpp:3134
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition Expr.cpp:283
QualType getType() const
Definition Expr.h:146
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition ExprObjC.h:1435
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
Definition ExprObjC.h:1300
const ObjCMethodDecl * getMethodDecl() const
Definition ExprObjC.h:1396
QualType getReceiverType() const
Retrieve the receiver type to which this message is being directed.
Definition ExprObjC.cpp:308
unsigned getNumArgs() const
Return the number of actual arguments in this message, not counting the receiver.
Definition ExprObjC.h:1422
QualType getCanonicalType() const
Definition TypeBase.h:8630
const Type * getTypePtrOrNull() const
Definition TypeBase.h:8582
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
Definition TypeBase.h:1348
bool isLambda() const
Determine whether this record is a class describing a lambda function object.
Definition Decl.cpp:5354
field_range fields() const
Definition Decl.h:4709
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:343
bool isThisDeclarationADefinition() const
Return true if this declaration is a completion definition of the type.
Definition Decl.h:3994
TagKind getTagKind() const
Definition Decl.h:4098
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Decl.h:3728
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:885
QualType getUnderlyingType() const
Definition Decl.h:3798
QualType getType() const
Definition Decl.h:725
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:565
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
@ Specialization
We are substituting template parameters for template arguments in order to form a template specializa...
Definition Template.h:50
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
void printQuotedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
bool isOwnerPtr(const std::string &Name)
bool isRefCounted(const CXXRecordDecl *R)
bool isOwnerPtrType(const clang::QualType T)
bool isRetainPtrOrOSPtr(const std::string &Name)
std::string safeGetName(const T *ASTNode)
Definition ASTUtils.h:109
bool isNullPtr(const clang::Expr *E)
Definition ASTUtils.cpp:535
bool isCheckedPtr(const std::string &Name)
bool isStdOrWTFMove(const clang::FunctionDecl *F)