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 = isa<CXXOperatorCallExpr>(Val: CE) && isa<CXXMethodDecl>(Val: F);
216
217 for (auto P = F->param_begin();
218 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
219 visitCallArg(Arg: CE->getArg(Arg: ArgIdx), Param: *P, DeclWithIssue);
220 }
221 }
222
223 void visitConstructExpr(const CXXConstructExpr *CE,
224 const Decl *DeclWithIssue) const {
225 assert(BR && "expected nonnull BugReporter");
226 if (BR->getSourceManager().isInSystemHeader(Loc: CE->getExprLoc()))
227 return;
228
229 if (const CXXMethodDecl *F = CE->getConstructor()) {
230 unsigned ArgIdx = 0;
231 for (auto P = F->param_begin();
232 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
233 visitCallArg(Arg: CE->getArg(Arg: ArgIdx), Param: *P, DeclWithIssue);
234 }
235 }
236
237 void visitObjCMessageExpr(const ObjCMessageExpr *E,
238 const Decl *DeclWithIssue) const {
239 assert(BR && "expected nonnull BugReporter");
240 if (BR->getSourceManager().isInSystemHeader(Loc: E->getExprLoc()))
241 return;
242
243 if (auto *Receiver = E->getInstanceReceiver()) {
244 Receiver = Receiver->IgnoreParenCasts();
245 if (isUnknownType(QT: E->getReceiverType()))
246 reportUnknownReceiverType(Receiver, DeclWithIssue);
247 }
248
249 auto *MethodDecl = E->getMethodDecl();
250 if (!MethodDecl)
251 return;
252
253 auto ArgCount = E->getNumArgs();
254 for (unsigned i = 0; i < ArgCount && i < MethodDecl->param_size(); ++i)
255 visitCallArg(Arg: E->getArg(Arg: i), Param: MethodDecl->getParamDecl(Idx: i), DeclWithIssue);
256 }
257
258 void visitCallArg(const Expr *Arg, const ParmVarDecl *Param,
259 const Decl *DeclWithIssue) const {
260 auto *ArgExpr = Arg->IgnoreParenCasts();
261 while (ArgExpr) {
262 ArgExpr = ArgExpr->IgnoreParenCasts();
263 if (auto *InnerCE = dyn_cast<CallExpr>(Val: ArgExpr)) {
264 if (auto *InnerCallee = InnerCE->getDirectCallee()) {
265 if (isStdOrWTFMove(F: InnerCallee) && InnerCE->getNumArgs() == 1) {
266 ArgExpr = InnerCE->getArg(Arg: 0);
267 continue;
268 }
269 }
270 }
271 if (auto *UO = dyn_cast<UnaryOperator>(Val: ArgExpr)) {
272 auto OpCode = UO->getOpcode();
273 if (OpCode == UO_Deref || OpCode == UO_AddrOf) {
274 ArgExpr = UO->getSubExpr();
275 continue;
276 }
277 }
278 break;
279 }
280
281 if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(Val: ArgExpr)) {
282 if (isOwnerPtrType(T: MemberCallExpr->getObjectType()))
283 return;
284 }
285
286 if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(Val: ArgExpr)) {
287 auto *Method = dyn_cast_or_null<CXXMethodDecl>(Val: OpCE->getDirectCallee());
288 if (Method && isOwnerPtr(Name: safeGetName(ASTNode: Method->getParent()))) {
289 if (OpCE->getOperator() == OO_Star && OpCE->getNumArgs() == 1)
290 return;
291 }
292 }
293
294 if (isNullPtr(E: ArgExpr) || isa<IntegerLiteral>(Val: ArgExpr) ||
295 isa<CXXDefaultArgExpr>(Val: ArgExpr))
296 return;
297
298 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: ArgExpr)) {
299 if (auto *ValDecl = DRE->getDecl()) {
300 if (isa<ParmVarDecl>(Val: ValDecl))
301 return;
302 }
303 }
304
305 QualType ArgType = Param->getType();
306 if (!isUnknownType(QT: ArgType))
307 return;
308
309 reportUnknownArgType(CA: Arg, Param, DeclWithIssue);
310 }
311
312 void reportUnknownArgType(const Expr *CA, const ParmVarDecl *Param,
313 const Decl *DeclWithIssue) const {
314 assert(CA);
315
316 SmallString<100> Buf;
317 llvm::raw_svector_ostream Os(Buf);
318
319 const std::string paramName = safeGetName(ASTNode: Param);
320 Os << "Call argument";
321 if (!paramName.empty()) {
322 Os << " for parameter ";
323 printQuotedQualifiedName(Os, D: Param);
324 }
325
326 reportBug(SrcLoc: CA->getExprLoc(), SrcRange: CA->getSourceRange(), DeclWithIssue, Description: Os.str(),
327 Type: Param->getType());
328 }
329
330 void reportUnknownReceiverType(const Expr *Receiver,
331 const Decl *DeclWithIssue) const {
332 assert(Receiver);
333 reportBug(SrcLoc: Receiver->getExprLoc(), SrcRange: Receiver->getSourceRange(), DeclWithIssue,
334 Description: "Receiver", Type: Receiver->getType());
335 }
336
337 void reportBug(const SourceLocation &SrcLoc, const SourceRange &SrcRange,
338 const Decl *DeclWithIssue, const StringRef &Description,
339 QualType Type) const {
340 SmallString<100> Buf;
341 llvm::raw_svector_ostream Os(Buf);
342
343 const std::string TypeName = Type.getAsString();
344 Os << Description << " uses a forward declared type '" << TypeName << "'";
345
346 assert(BR && "expected nonnull BugReporter");
347 PathDiagnosticLocation BSLoc(SrcLoc, BR->getSourceManager());
348 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
349 Report->addRange(R: SrcRange);
350 Report->setDeclWithIssue(DeclWithIssue);
351 BR->emitReport(R: std::move(Report));
352 }
353};
354
355} // namespace
356
357void ento::registerForwardDeclChecker(CheckerManager &Mgr) {
358 Mgr.registerChecker<ForwardDeclChecker>();
359}
360
361bool ento::shouldRegisterForwardDeclChecker(const CheckerManager &) {
362 return true;
363}
364