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