Skip to content

Commit 0073376

Browse files
authored
Merge branch 'main' into hliao/main/fix-cir-dependency
2 parents 2a86c00 + 646a6e7 commit 0073376

File tree

111 files changed

+4941
-8129
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+4941
-8129
lines changed

clang/docs/analyzer/checkers.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3476,6 +3476,24 @@ Limitations:
34763476
alpha.WebKit
34773477
^^^^^^^^^^^^
34783478
3479+
alpha.webkit.ForwardDeclChecker
3480+
"""""""""""""""""""""""""""""""
3481+
Check for local variables, member variables, and function arguments that are forward declared.
3482+
3483+
.. code-block:: cpp
3484+
3485+
struct Obj;
3486+
Obj* provide();
3487+
3488+
struct Foo {
3489+
Obj* ptr; // warn
3490+
};
3491+
3492+
void foo() {
3493+
Obj* obj = provide(); // warn
3494+
consume(obj); // warn
3495+
}
3496+
34793497
.. _alpha-webkit-NoUncheckedPtrMemberChecker:
34803498
34813499
alpha.webkit.MemoryUnsafeCastChecker

clang/include/clang/StaticAnalyzer/Checkers/Checkers.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,6 +1761,10 @@ def UncountedLambdaCapturesChecker : Checker<"UncountedLambdaCapturesChecker">,
17611761

17621762
let ParentPackage = WebKitAlpha in {
17631763

1764+
def ForwardDeclChecker : Checker<"ForwardDeclChecker">,
1765+
HelpText<"Check for forward declared local or member variables and function arguments">,
1766+
Documentation<HasDocumentation>;
1767+
17641768
def MemoryUnsafeCastChecker : Checker<"MemoryUnsafeCastChecker">,
17651769
HelpText<"Check for memory unsafe casts from base type to derived type.">,
17661770
Documentation<HasDocumentation>;

clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ add_clang_library(clangStaticAnalyzerCheckers
130130
ValistChecker.cpp
131131
VirtualCallChecker.cpp
132132
WebKit/ASTUtils.cpp
133+
WebKit/ForwardDeclChecker.cpp
133134
WebKit/MemoryUnsafeCastChecker.cpp
134135
WebKit/PtrTypesSemantics.cpp
135136
WebKit/RefCntblBaseVirtualDtorChecker.cpp
Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
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/CXXInheritance.h"
13+
#include "clang/AST/Decl.h"
14+
#include "clang/AST/DeclCXX.h"
15+
#include "clang/AST/RecursiveASTVisitor.h"
16+
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
17+
#include "clang/Basic/SourceLocation.h"
18+
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19+
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
20+
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21+
#include "clang/StaticAnalyzer/Core/Checker.h"
22+
#include "llvm/ADT/DenseSet.h"
23+
#include "llvm/Support/SaveAndRestore.h"
24+
#include <optional>
25+
26+
using namespace clang;
27+
using namespace ento;
28+
29+
namespace {
30+
31+
class ForwardDeclChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
32+
BugType Bug;
33+
mutable BugReporter *BR;
34+
mutable RetainTypeChecker RTC;
35+
mutable llvm::DenseSet<const Type *> SystemTypes;
36+
37+
public:
38+
ForwardDeclChecker()
39+
: Bug(this, "Forward declared member or local variable or parameter",
40+
"WebKit coding guidelines") {}
41+
42+
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
43+
BugReporter &BRArg) const {
44+
BR = &BRArg;
45+
46+
// The calls to checkAST* from AnalysisConsumer don't
47+
// visit template instantiations or lambda classes. We
48+
// want to visit those, so we make our own RecursiveASTVisitor.
49+
struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
50+
using Base = RecursiveASTVisitor<LocalVisitor>;
51+
52+
const ForwardDeclChecker *Checker;
53+
Decl *DeclWithIssue{nullptr};
54+
55+
explicit LocalVisitor(const ForwardDeclChecker *Checker)
56+
: Checker(Checker) {
57+
assert(Checker);
58+
}
59+
60+
bool shouldVisitTemplateInstantiations() const { return true; }
61+
bool shouldVisitImplicitCode() const { return false; }
62+
63+
bool VisitTypedefDecl(TypedefDecl *TD) {
64+
Checker->visitTypedef(TD);
65+
return true;
66+
}
67+
68+
bool VisitRecordDecl(const RecordDecl *RD) {
69+
Checker->visitRecordDecl(RD, DeclWithIssue);
70+
return true;
71+
}
72+
73+
bool TraverseDecl(Decl *D) {
74+
llvm::SaveAndRestore SavedDecl(DeclWithIssue);
75+
if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
76+
DeclWithIssue = D;
77+
return Base::TraverseDecl(D);
78+
}
79+
80+
bool VisitVarDecl(VarDecl *V) {
81+
if (V->isLocalVarDecl())
82+
Checker->visitVarDecl(V, DeclWithIssue);
83+
return true;
84+
}
85+
86+
bool VisitCallExpr(const CallExpr *CE) {
87+
Checker->visitCallExpr(CE, DeclWithIssue);
88+
return true;
89+
}
90+
91+
bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {
92+
Checker->visitConstructExpr(CE, DeclWithIssue);
93+
return true;
94+
}
95+
96+
bool VisitObjCMessageExpr(const ObjCMessageExpr *ObjCMsgExpr) {
97+
Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
98+
return true;
99+
}
100+
};
101+
102+
LocalVisitor visitor(this);
103+
RTC.visitTranslationUnitDecl(TUD);
104+
visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
105+
}
106+
107+
void visitTypedef(const TypedefDecl *TD) const {
108+
RTC.visitTypedef(TD);
109+
auto QT = TD->getUnderlyingType().getCanonicalType();
110+
if (BR->getSourceManager().isInSystemHeader(TD->getBeginLoc())) {
111+
if (auto *Type = QT.getTypePtrOrNull(); Type && QT->isPointerType())
112+
SystemTypes.insert(Type);
113+
}
114+
}
115+
116+
bool isUnknownType(QualType QT) const {
117+
auto *Type = QT.getTypePtrOrNull();
118+
if (!Type)
119+
return false;
120+
auto *CanonicalType = QT.getCanonicalType().getTypePtrOrNull();
121+
auto PointeeQT = Type->getPointeeType();
122+
auto *PointeeType = PointeeQT.getTypePtrOrNull();
123+
if (!PointeeType)
124+
return false;
125+
auto *R = PointeeType->getAsCXXRecordDecl();
126+
if (!R) // Forward declaration of a Objective-C interface is safe.
127+
return false;
128+
auto Name = R->getName();
129+
return !R->hasDefinition() && !RTC.isUnretained(QT) &&
130+
!SystemTypes.contains(CanonicalType) &&
131+
!Name.starts_with("Opaque") && Name != "_NSZone";
132+
}
133+
134+
void visitRecordDecl(const RecordDecl *RD, const Decl *DeclWithIssue) const {
135+
if (!RD->isThisDeclarationADefinition())
136+
return;
137+
138+
if (RD->isImplicit() || RD->isLambda())
139+
return;
140+
141+
const auto RDLocation = RD->getLocation();
142+
if (!RDLocation.isValid())
143+
return;
144+
145+
const auto Kind = RD->getTagKind();
146+
if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
147+
return;
148+
149+
if (BR->getSourceManager().isInSystemHeader(RDLocation))
150+
return;
151+
152+
// Ref-counted smartpointers actually have raw-pointer to uncounted type as
153+
// a member but we trust them to handle it correctly.
154+
auto R = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
155+
if (!R || isRefCounted(R) || isCheckedPtr(R) || isRetainPtr(R))
156+
return;
157+
158+
for (auto *Member : RD->fields()) {
159+
auto QT = Member->getType();
160+
if (isUnknownType(QT)) {
161+
SmallString<100> Buf;
162+
llvm::raw_svector_ostream Os(Buf);
163+
164+
const std::string TypeName = QT.getAsString();
165+
Os << "Member variable ";
166+
printQuotedName(Os, Member);
167+
Os << " uses a forward declared type '" << TypeName << "'";
168+
169+
const SourceLocation SrcLocToReport = Member->getBeginLoc();
170+
PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
171+
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
172+
Report->addRange(Member->getSourceRange());
173+
Report->setDeclWithIssue(DeclWithIssue);
174+
BR->emitReport(std::move(Report));
175+
}
176+
}
177+
}
178+
179+
void visitVarDecl(const VarDecl *V, const Decl *DeclWithIssue) const {
180+
if (BR->getSourceManager().isInSystemHeader(V->getBeginLoc()))
181+
return;
182+
183+
auto QT = V->getType();
184+
if (!isUnknownType(QT))
185+
return;
186+
187+
SmallString<100> Buf;
188+
llvm::raw_svector_ostream Os(Buf);
189+
Os << "Local variable ";
190+
printQuotedQualifiedName(Os, V);
191+
192+
reportBug(V->getBeginLoc(), V->getSourceRange(), DeclWithIssue, Os.str(),
193+
QT);
194+
}
195+
196+
void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const {
197+
if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
198+
return;
199+
200+
if (auto *F = CE->getDirectCallee()) {
201+
// Skip the first argument for overloaded member operators (e. g. lambda
202+
// or std::function call operator).
203+
unsigned ArgIdx =
204+
isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
205+
206+
for (auto P = F->param_begin();
207+
P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
208+
visitCallArg(CE->getArg(ArgIdx), *P, DeclWithIssue);
209+
}
210+
}
211+
212+
void visitConstructExpr(const CXXConstructExpr *CE,
213+
const Decl *DeclWithIssue) const {
214+
if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
215+
return;
216+
217+
if (auto *F = CE->getConstructor()) {
218+
// Skip the first argument for overloaded member operators (e. g. lambda
219+
// or std::function call operator).
220+
unsigned ArgIdx =
221+
isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
222+
223+
for (auto P = F->param_begin();
224+
P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
225+
visitCallArg(CE->getArg(ArgIdx), *P, DeclWithIssue);
226+
}
227+
}
228+
229+
void visitObjCMessageExpr(const ObjCMessageExpr *E,
230+
const Decl *DeclWithIssue) const {
231+
if (BR->getSourceManager().isInSystemHeader(E->getExprLoc()))
232+
return;
233+
234+
if (auto *Receiver = E->getInstanceReceiver()->IgnoreParenCasts()) {
235+
if (isUnknownType(E->getReceiverType()))
236+
reportUnknownRecieverType(Receiver, DeclWithIssue);
237+
}
238+
239+
auto *MethodDecl = E->getMethodDecl();
240+
if (!MethodDecl)
241+
return;
242+
243+
auto ArgCount = E->getNumArgs();
244+
for (unsigned i = 0; i < ArgCount && i < MethodDecl->param_size(); ++i)
245+
visitCallArg(E->getArg(i), MethodDecl->getParamDecl(i), DeclWithIssue);
246+
}
247+
248+
void visitCallArg(const Expr *Arg, const ParmVarDecl *Param,
249+
const Decl *DeclWithIssue) const {
250+
auto *ArgExpr = Arg->IgnoreParenCasts();
251+
if (auto *InnerCE = dyn_cast<CallExpr>(Arg)) {
252+
auto *InnerCallee = InnerCE->getDirectCallee();
253+
if (InnerCallee && InnerCallee->isInStdNamespace() &&
254+
safeGetName(InnerCallee) == "move" && InnerCE->getNumArgs() == 1) {
255+
ArgExpr = InnerCE->getArg(0);
256+
if (ArgExpr)
257+
ArgExpr = ArgExpr->IgnoreParenCasts();
258+
}
259+
}
260+
if (isa<CXXNullPtrLiteralExpr>(ArgExpr) || isa<IntegerLiteral>(ArgExpr) ||
261+
isa<CXXDefaultArgExpr>(ArgExpr))
262+
return;
263+
if (auto *DRE = dyn_cast<DeclRefExpr>(ArgExpr)) {
264+
if (auto *ValDecl = DRE->getDecl()) {
265+
if (isa<ParmVarDecl>(ValDecl))
266+
return;
267+
}
268+
}
269+
270+
QualType ArgType = Param->getType();
271+
if (!isUnknownType(ArgType))
272+
return;
273+
274+
reportUnknownArgType(Arg, Param, DeclWithIssue);
275+
}
276+
277+
void reportUnknownArgType(const Expr *CA, const ParmVarDecl *Param,
278+
const Decl *DeclWithIssue) const {
279+
assert(CA);
280+
281+
SmallString<100> Buf;
282+
llvm::raw_svector_ostream Os(Buf);
283+
284+
const std::string paramName = safeGetName(Param);
285+
Os << "Call argument";
286+
if (!paramName.empty()) {
287+
Os << " for parameter ";
288+
printQuotedQualifiedName(Os, Param);
289+
}
290+
291+
reportBug(CA->getExprLoc(), CA->getSourceRange(), DeclWithIssue, Os.str(),
292+
Param->getType());
293+
}
294+
295+
void reportUnknownRecieverType(const Expr *Receiver,
296+
const Decl *DeclWithIssue) const {
297+
assert(Receiver);
298+
reportBug(Receiver->getExprLoc(), Receiver->getSourceRange(), DeclWithIssue,
299+
"Receiver", Receiver->getType());
300+
}
301+
302+
void reportBug(const SourceLocation &SrcLoc, const SourceRange &SrcRange,
303+
const Decl *DeclWithIssue, const StringRef &Description,
304+
QualType Type) const {
305+
SmallString<100> Buf;
306+
llvm::raw_svector_ostream Os(Buf);
307+
308+
const std::string TypeName = Type.getAsString();
309+
Os << Description << " uses a forward declared type '" << TypeName << "'";
310+
311+
PathDiagnosticLocation BSLoc(SrcLoc, BR->getSourceManager());
312+
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
313+
Report->addRange(SrcRange);
314+
Report->setDeclWithIssue(DeclWithIssue);
315+
BR->emitReport(std::move(Report));
316+
}
317+
};
318+
319+
} // namespace
320+
321+
void ento::registerForwardDeclChecker(CheckerManager &Mgr) {
322+
Mgr.registerChecker<ForwardDeclChecker>();
323+
}
324+
325+
bool ento::shouldRegisterForwardDeclChecker(const CheckerManager &) {
326+
return true;
327+
}

0 commit comments

Comments
 (0)