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) || isRetainPtr(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 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(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 if (auto *InnerCE = dyn_cast<CallExpr>(Val: Arg)) {
267 auto *InnerCallee = InnerCE->getDirectCallee();
268 if (InnerCallee && InnerCallee->isInStdNamespace() &&
269 safeGetName(ASTNode: InnerCallee) == "move" && InnerCE->getNumArgs() == 1) {
270 ArgExpr = InnerCE->getArg(Arg: 0);
271 if (ArgExpr)
272 ArgExpr = ArgExpr->IgnoreParenCasts();
273 }
274 }
275 if (isa<CXXNullPtrLiteralExpr>(Val: ArgExpr) || isa<IntegerLiteral>(Val: ArgExpr) ||
276 isa<CXXDefaultArgExpr>(Val: ArgExpr))
277 return;
278 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: ArgExpr)) {
279 if (auto *ValDecl = DRE->getDecl()) {
280 if (isa<ParmVarDecl>(Val: ValDecl))
281 return;
282 }
283 }
284
285 QualType ArgType = Param->getType();
286 if (!isUnknownType(QT: ArgType))
287 return;
288
289 reportUnknownArgType(CA: Arg, Param, DeclWithIssue);
290 }
291
292 void reportUnknownArgType(const Expr *CA, const ParmVarDecl *Param,
293 const Decl *DeclWithIssue) const {
294 assert(CA);
295
296 SmallString<100> Buf;
297 llvm::raw_svector_ostream Os(Buf);
298
299 const std::string paramName = safeGetName(ASTNode: Param);
300 Os << "Call argument";
301 if (!paramName.empty()) {
302 Os << " for parameter ";
303 printQuotedQualifiedName(Os, D: Param);
304 }
305
306 reportBug(SrcLoc: CA->getExprLoc(), SrcRange: CA->getSourceRange(), DeclWithIssue, Description: Os.str(),
307 Type: Param->getType());
308 }
309
310 void reportUnknownRecieverType(const Expr *Receiver,
311 const Decl *DeclWithIssue) const {
312 assert(Receiver);
313 reportBug(SrcLoc: Receiver->getExprLoc(), SrcRange: Receiver->getSourceRange(), DeclWithIssue,
314 Description: "Receiver", Type: Receiver->getType());
315 }
316
317 void reportBug(const SourceLocation &SrcLoc, const SourceRange &SrcRange,
318 const Decl *DeclWithIssue, const StringRef &Description,
319 QualType Type) const {
320 SmallString<100> Buf;
321 llvm::raw_svector_ostream Os(Buf);
322
323 const std::string TypeName = Type.getAsString();
324 Os << Description << " uses a forward declared type '" << TypeName << "'";
325
326 assert(BR && "expected nonnull BugReporter");
327 PathDiagnosticLocation BSLoc(SrcLoc, BR->getSourceManager());
328 auto Report = std::make_unique<BasicBugReport>(args: Bug, args: Os.str(), args&: BSLoc);
329 Report->addRange(R: SrcRange);
330 Report->setDeclWithIssue(DeclWithIssue);
331 BR->emitReport(R: std::move(Report));
332 }
333};
334
335} // namespace
336
337void ento::registerForwardDeclChecker(CheckerManager &Mgr) {
338 Mgr.registerChecker<ForwardDeclChecker>();
339}
340
341bool ento::shouldRegisterForwardDeclChecker(const CheckerManager &) {
342 return true;
343}
344