Skip to content

Commit b80d982

Browse files
authored
[alpha.webkit.UncountedLocalVarsChecker] Detect assignments to uncounted local variable and parameters. (#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 04c94ba commit b80d982

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
@@ -135,7 +135,19 @@ class UncountedLocalVarsChecker
135135
bool shouldVisitImplicitCode() const { return false; }
136136

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

@@ -174,7 +186,7 @@ class UncountedLocalVarsChecker
174186
visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
175187
}
176188

177-
void visitVarDecl(const VarDecl *V) const {
189+
void visitVarDecl(const VarDecl *V, const Expr *Value) const {
178190
if (shouldSkipVarDecl(V))
179191
return;
180192

@@ -184,12 +196,8 @@ class UncountedLocalVarsChecker
184196

185197
std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
186198
if (IsUncountedPtr && *IsUncountedPtr) {
187-
const Expr *const InitExpr = V->getInit();
188-
if (!InitExpr)
189-
return; // FIXME: later on we might warn on uninitialized vars too
190-
191199
if (tryToFindPtrOrigin(
192-
InitExpr, /*StopAtFirstRefCountedObj=*/false,
200+
Value, /*StopAtFirstRefCountedObj=*/false,
193201
[&](const clang::Expr *InitArgOrigin, bool IsSafe) {
194202
if (!InitArgOrigin)
195203
return true;
@@ -232,34 +240,46 @@ class UncountedLocalVarsChecker
232240
}))
233241
return;
234242

235-
reportBug(V);
243+
reportBug(V, Value);
236244
}
237245
}
238246

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

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

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