Skip to content

Commit 40e972f

Browse files
committed
[alpha.webkit.UncountedLocalVarsChecker] Detect assignments to uncounted local variable and parameters. (llvm#92639)
This PR updates alpha.webkit.UncountedLocalVarsChecker to emit warnings for assignments to uncounted local variable and parameters instead of just the initialization during the declaration.
1 parent 00e31ab commit 40e972f

File tree

2 files changed

+117
-24
lines changed

2 files changed

+117
-24
lines changed

clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,19 @@ class UncountedLocalVarsChecker
136136
bool shouldVisitImplicitCode() const { return false; }
137137

138138
bool VisitVarDecl(VarDecl *V) {
139-
Checker->visitVarDecl(V);
139+
auto *Init = V->getInit();
140+
if (Init && V->isLocalVarDecl())
141+
Checker->visitVarDecl(V, Init);
142+
return true;
143+
}
144+
145+
bool VisitBinaryOperator(const BinaryOperator *BO) {
146+
if (BO->isAssignmentOp()) {
147+
if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
148+
if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl()))
149+
Checker->visitVarDecl(V, BO->getRHS());
150+
}
151+
}
140152
return true;
141153
}
142154

@@ -175,7 +187,7 @@ class UncountedLocalVarsChecker
175187
visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
176188
}
177189

178-
void visitVarDecl(const VarDecl *V) const {
190+
void visitVarDecl(const VarDecl *V, const Expr *Value) const {
179191
if (shouldSkipVarDecl(V))
180192
return;
181193

@@ -185,12 +197,8 @@ class UncountedLocalVarsChecker
185197

186198
std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
187199
if (IsUncountedPtr && *IsUncountedPtr) {
188-
const Expr *const InitExpr = V->getInit();
189-
if (!InitExpr)
190-
return; // FIXME: later on we might warn on uninitialized vars too
191-
192200
if (tryToFindPtrOrigin(
193-
InitExpr, /*StopAtFirstRefCountedObj=*/false,
201+
Value, /*StopAtFirstRefCountedObj=*/false,
194202
[&](const clang::Expr *InitArgOrigin, bool IsSafe) {
195203
if (!InitArgOrigin)
196204
return true;
@@ -233,34 +241,46 @@ class UncountedLocalVarsChecker
233241
}))
234242
return;
235243

236-
reportBug(V);
244+
reportBug(V, Value);
237245
}
238246
}
239247

240248
bool shouldSkipVarDecl(const VarDecl *V) const {
241249
assert(V);
242-
if (!V->isLocalVarDecl())
243-
return true;
244-
245-
if (BR->getSourceManager().isInSystemHeader(V->getLocation()))
246-
return true;
247-
248-
return false;
250+
return BR->getSourceManager().isInSystemHeader(V->getLocation());
249251
}
250252

251-
void reportBug(const VarDecl *V) const {
253+
void reportBug(const VarDecl *V, const Expr *Value) const {
252254
assert(V);
253255
SmallString<100> Buf;
254256
llvm::raw_svector_ostream Os(Buf);
255257

256-
Os << "Local variable ";
257-
printQuotedQualifiedName(Os, V);
258-
Os << " is uncounted and unsafe.";
259-
260-
PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
261-
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
262-
Report->addRange(V->getSourceRange());
263-
BR->emitReport(std::move(Report));
258+
if (dyn_cast<ParmVarDecl>(V)) {
259+
Os << "Assignment to an uncounted parameter ";
260+
printQuotedQualifiedName(Os, V);
261+
Os << " is unsafe.";
262+
263+
PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager());
264+
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
265+
Report->addRange(Value->getSourceRange());
266+
BR->emitReport(std::move(Report));
267+
} else {
268+
if (V->hasLocalStorage())
269+
Os << "Local variable ";
270+
else if (V->isStaticLocal())
271+
Os << "Static local variable ";
272+
else if (V->hasGlobalStorage())
273+
Os << "Global variable ";
274+
else
275+
Os << "Variable ";
276+
printQuotedQualifiedName(Os, V);
277+
Os << " is uncounted and unsafe.";
278+
279+
PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
280+
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
281+
Report->addRange(V->getSourceRange());
282+
BR->emitReport(std::move(Report));
283+
}
264284
}
265285
};
266286
} // namespace

clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,76 @@ void foo() {
216216
}
217217

218218
} // namespace conditional_op
219+
220+
namespace local_assignment_basic {
221+
222+
RefCountable *provide_ref_cntbl();
223+
224+
void foo(RefCountable* a) {
225+
RefCountable* b = a;
226+
// expected-warning@-1{{Local variable 'b' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
227+
if (b->trivial())
228+
b = provide_ref_cntbl();
229+
}
230+
231+
void bar(RefCountable* a) {
232+
RefCountable* b;
233+
// expected-warning@-1{{Local variable 'b' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
234+
b = provide_ref_cntbl();
235+
}
236+
237+
void baz() {
238+
RefPtr a = provide_ref_cntbl();
239+
{
240+
RefCountable* b = a.get();
241+
// expected-warning@-1{{Local variable 'b' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
242+
b = provide_ref_cntbl();
243+
}
244+
}
245+
246+
} // namespace local_assignment_basic
247+
248+
namespace local_assignment_to_parameter {
249+
250+
RefCountable *provide_ref_cntbl();
251+
void someFunction();
252+
253+
void foo(RefCountable* a) {
254+
a = provide_ref_cntbl();
255+
// expected-warning@-1{{Assignment to an uncounted parameter 'a' is unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
256+
someFunction();
257+
a->method();
258+
}
259+
260+
} // namespace local_assignment_to_parameter
261+
262+
namespace local_assignment_to_static_local {
263+
264+
RefCountable *provide_ref_cntbl();
265+
void someFunction();
266+
267+
void foo() {
268+
static RefCountable* a = nullptr;
269+
// expected-warning@-1{{Static local variable 'a' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
270+
a = provide_ref_cntbl();
271+
someFunction();
272+
a->method();
273+
}
274+
275+
} // namespace local_assignment_to_static_local
276+
277+
namespace local_assignment_to_global {
278+
279+
RefCountable *provide_ref_cntbl();
280+
void someFunction();
281+
282+
RefCountable* g_a = nullptr;
283+
// expected-warning@-1{{Global variable 'local_assignment_to_global::g_a' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
284+
285+
void foo() {
286+
g_a = provide_ref_cntbl();
287+
someFunction();
288+
g_a->method();
289+
}
290+
291+
} // namespace local_assignment_to_global

0 commit comments

Comments
 (0)