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"
14#include "clang/AST/RecursiveASTVisitor.h"
15#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
16#include "clang/Basic/SourceLocation.h"
17#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
19#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20#include "clang/StaticAnalyzer/Core/Checker.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>(Val: D) || isa<ObjCMethodDecl>(Val: 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(E: ObjCMsgExpr, DeclWithIssue);
96 return true;
97 }
98 };
99
100 LocalVisitor visitor(this);
101 RTC.visitTranslationUnitDecl(TUD);
102 visitor.TraverseDecl(D: 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(Loc: TD->getBeginLoc())) {
110 if (auto *Type = QT.getTypePtrOrNull())
111 SystemTypes.insert(V: 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>(Val: 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(V: CanonicalType) &&
139 !SystemTypes.contains(V: PointeeType) && !Name.starts_with(Prefix: "Opaque") &&
140 Name != "_NSZone";
141 }
142
143 void visitRecordDecl(const RecordDecl *RD, const Decl *DeclWithIssue) const {
144 if (!RD->isThisDeclarationADefinition())
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(Loc: 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>(Val: RD);
165 if (!R || isRefCounted(Class: R) || isCheckedPtr(Class: R) || isRetainPtrOrOSPtr(Class: 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 ";
176 printQuotedName(Os, D: Member);
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>(args: Bug, args: Os.str(), args&: BSLoc);
182 Report->addRange(R: Member->getSourceRange());
183 Report->setDeclWithIssue(DeclWithIssue);
184 BR->emitReport(R: 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(Loc: 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 ";
201 printQuotedQualifiedName(Os, D: V);
202
203 reportBug(SrcLoc: V->getBeginLoc(), SrcRange: V->getSourceRange(), DeclWithIssue, Description: Os.str(),
204 Type: QT);
205 }
206
207 void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const {
208 assert(BR && "expected nonnull BugReporter");
209 if (BR->getSourceManager().isInSystemHeader(Loc: 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>(Val: CE) && isa_and_nonnull<CXXMethodDecl>(Val: F);
217
218 for (auto P = F->param_begin();
219 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
220 visitCallArg(Arg: CE->getArg(Arg: ArgIdx), Param: *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(Loc: 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>(Val: CE) && isa_and_nonnull<CXXMethodDecl>(Val: F);
235
236 for (auto P = F->param_begin();
237 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
238 visitCallArg(Arg: CE->getArg(Arg: ArgIdx), Param: *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(Loc: E->getExprLoc()))
246 return;
247
248 if (auto *Receiver = E->getInstanceReceiver()) {
249 Receiver = Receiver->IgnoreParenCasts();
250 if (isUnknownType(QT: E->getReceiverType()))
251 reportUnknownReceiverType(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(Arg: E->getArg(Arg: i), Param: MethodDecl->getParamDecl(Idx: 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>(Val: ArgExpr)) {
269 if (auto *InnerCallee = InnerCE->getDirectCallee()) {
270 if (isStdOrWTFMove(F: InnerCallee) && InnerCE->getNumArgs() == 1) {
271 ArgExpr = InnerCE->getArg(Arg: 0);
272 continue;
273 }
274 }
275 }
276 if (auto *UO = dyn_cast<UnaryOperator>(Val: 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>(Val: ArgExpr)) {
287 if (isOwnerPtrType(T: MemberCallExpr->getObjectType()))
288 return;
289 }
290
291 if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Val: ArgExpr)) {
292 auto *Method = dyn_cast_or_null<CXXMethodDecl>(Val: OpCE->getDirectCallee());
293 if (Method && isOwnerPtr(Name: safeGetName(ASTNode: Method->getParent()))) {
294 if (OpCE->getOperator() == OO_Star && OpCE->getNumArgs() == 1)
295 return;
296 }
297 }
298
299 if (isNullPtr(E: ArgExpr) || isa<IntegerLiteral>(Val: ArgExpr) ||
300 isa<CXXDefaultArgExpr>(Val: ArgExpr))
301 return;
302
303 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: ArgExpr)) {
304 if (auto *ValDecl = DRE->getDecl()) {
305 if (isa<ParmVarDecl>(Val: ValDecl))
306 return;
307 }
308 }
309
310 QualType ArgType = Param->getType();
311 if (!isUnknownType(QT: ArgType))
312 return;
313
314 reportUnknownArgType(CA: 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(ASTNode: Param);
325 Os << "Call argument";
326 if (!paramName.empty()) {
327 Os << " for parameter ";
328 printQuotedQualifiedName(Os, D: Param);
329 }
330
331 reportBug(SrcLoc: CA->getExprLoc(), SrcRange: CA->getSourceRange(), DeclWithIssue, Description: Os.str(),
332 Type: Param->getType());
333 }
334
335 void reportUnknownReceiverType(const Expr *Receiver,
336 const Decl *DeclWithIssue) const {
337 assert(Receiver);
338 reportBug(SrcLoc: Receiver->getExprLoc(), SrcRange: Receiver->getSourceRange(), DeclWithIssue,
339 Description: "Receiver", Type: 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>(args: Bug, args: Os.str(), args&: BSLoc);
354 Report->addRange(R: SrcRange);
355 Report->setDeclWithIssue(DeclWithIssue);
356 BR->emitReport(R: 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}
369