[clang] 88dcdc2 - [alpha.webkit.UncountedLocalVarsChecker] Don't treat a call through a raw pointer/reference guardian argument as a mutation (#225309)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 22 14:56:52 PDT 2026
Author: Fady Farag
Date: 2026-09-22T14:56:44-07:00
New Revision: 88dcdc2d619dd09665beb67479a330c36f7251b7
URL: https://github.com/llvm/llvm-project/commit/88dcdc2d619dd09665beb67479a330c36f7251b7
DIFF: https://github.com/llvm/llvm-project/commit/88dcdc2d619dd09665beb67479a330c36f7251b7.diff
LOG: [alpha.webkit.UncountedLocalVarsChecker] Don't treat a call through a raw pointer/reference guardian argument as a mutation (#225309)
Previously, when a function parameter was used as the guardian of a raw
pointer/reference local, `GuardianVisitor` treated any non-const member
function call on the parameter, or passing it to a non-const parameter,
as a mutation. That is correct for a `RefPtr<T>&` parameter but not for
a raw pointer/reference parameter, where such a call operates on the
pointee and cannot change the parameter, which caused an unnecessary
warning whenever the parameter was used at all. This only treats a raw
pointer/reference guardian argument as mutated when it is assigned to,
or when the callee can reseat it by receiving it as `Bar*&` or `Bar**`.
Added:
Modified:
clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
index 2d34ed9e4fae3..d648c7f4f45d6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
@@ -52,8 +52,11 @@ bool isRefcountedStringsHack(const VarDecl *V) {
struct GuardianVisitor : DynamicRecursiveASTVisitor {
const VarDecl *Guardian{nullptr};
+ bool GuardianIsRawPtrOrRef{false};
- explicit GuardianVisitor(const VarDecl *Guardian) : Guardian(Guardian) {
+ explicit GuardianVisitor(const VarDecl *Guardian,
+ bool GuardianIsRawPtrOrRef = false)
+ : Guardian(Guardian), GuardianIsRawPtrOrRef(GuardianIsRawPtrOrRef) {
assert(Guardian);
}
@@ -110,6 +113,8 @@ struct GuardianVisitor : DynamicRecursiveASTVisitor {
}
bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) override {
+ if (GuardianIsRawPtrOrRef)
+ return true;
auto *Method = MCE->getMethodDecl();
auto ObjType = MCE->getObjectType();
if (ObjType.isConstQualified())
@@ -125,14 +130,29 @@ struct GuardianVisitor : DynamicRecursiveASTVisitor {
private:
bool mutatesGuardian(const Expr *Arg, const ParmVarDecl *ParmDecl) {
Arg = Arg->IgnoreParenCasts();
- if (auto *VarRef = dyn_cast<DeclRefExpr>(Arg)) {
- if (VarRef->getDecl() == Guardian) {
- auto ArgType = ParmDecl ? ParmDecl->getType() : Arg->getType();
- if (!ArgType.isConstQualified())
- return true;
- }
+ auto ArgType = ParmDecl ? ParmDecl->getType() : Arg->getType();
+ bool IsAddressOf = false;
+ if (auto *UO = dyn_cast<UnaryOperator>(Arg);
+ UO && UO->getOpcode() == UO_AddrOf) {
+ Arg = UO->getSubExpr()->IgnoreParenCasts();
+ IsAddressOf = true;
}
- return false;
+ auto *VarRef = dyn_cast<DeclRefExpr>(Arg);
+ if (!VarRef || VarRef->getDecl() != Guardian)
+ return false;
+ if (GuardianIsRawPtrOrRef && !Guardian->getType()->isPointerType())
+ return false;
+ if (IsAddressOf) {
+ if (!ArgType->isPointerType())
+ return false;
+ return !ArgType->getPointeeType().isConstQualified();
+ }
+ if (GuardianIsRawPtrOrRef) {
+ if (!ArgType->isReferenceType())
+ return false;
+ return !ArgType.getNonReferenceType().isConstQualified();
+ }
+ return !ArgType.isConstQualified();
}
};
@@ -406,10 +426,12 @@ class RawPtrRefLocalVarsChecker
}
if (isa<ParmVarDecl>(MaybeGuardian)) {
+ bool IsRawPtrOrRef = isUnsafePtr(GuardianType).value_or(false);
+ GuardianVisitor Visitor{MaybeGuardian, IsRawPtrOrRef};
if (auto *FD = dyn_cast<FunctionDecl>(DeclWithIssue))
- return GuardianVisitor{MaybeGuardian}.TraverseStmt(FD->getBody());
+ return Visitor.TraverseStmt(FD->getBody());
if (auto *MD = dyn_cast<ObjCMethodDecl>(DeclWithIssue))
- return GuardianVisitor{MaybeGuardian}.TraverseStmt(MD->getBody());
+ return Visitor.TraverseStmt(MD->getBody());
}
return false;
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp
index 2a3d9f2fefab8..656a755022d1e 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp
@@ -420,6 +420,90 @@ void foo(RefPtr<RefCountable>& arg) {
} // namespace local_assignment_to_guardian
+namespace raw_pointer_or_reference_guardian_parameter {
+
+void consume(RefCountable&);
+void consumeConst(const RefCountable&);
+void consumePtr(RefCountable*);
+void reseat(RefCountable*&);
+
+void memberCallThroughReference(RefCountable& obj) {
+ RefCountable& ref = obj;
+ obj.method();
+ ref.method();
+}
+
+void memberCallThroughPointer(RefCountable* obj) {
+ RefCountable* ptr = obj;
+ obj->method();
+ ptr->method();
+}
+
+void passReferenceToNonConstReference(RefCountable& obj) {
+ RefCountable& ref = obj;
+ consume(obj);
+ ref.method();
+}
+
+void passConstReferenceToConstReference(const RefCountable& obj) {
+ const RefCountable& ref = obj;
+ consumeConst(obj);
+ ref.constMethod();
+}
+
+void passPointerByValue(RefCountable* obj) {
+ RefCountable* ptr = obj;
+ consumePtr(obj);
+ ptr->method();
+}
+
+void passPointerByNonConstReference(RefCountable* obj) {
+ RefCountable* ptr = obj;
+ // expected-warning at -1{{Local variable 'ptr' is a raw pointer to RefPtr-capable type 'RefCountable' [alpha.webkit.UncountedLocalVarsChecker]}}
+ reseat(obj);
+ ptr->method();
+}
+
+void assignToPointer(RefCountable* obj, RefCountable* other) {
+ RefCountable* ptr = obj;
+ // expected-warning at -1{{Local variable 'ptr' is a raw pointer to RefPtr-capable type 'RefCountable' [alpha.webkit.UncountedLocalVarsChecker]}}
+ obj = other;
+ ptr->method();
+}
+
+void reseatViaPointerToPointer(RefCountable**);
+void readViaPointerToConstPointer(RefCountable* const*);
+
+void passAddressOfPointer(RefCountable* obj) {
+ RefCountable* ptr = obj;
+ // expected-warning at -1{{Local variable 'ptr' is a raw pointer to RefPtr-capable type 'RefCountable' [alpha.webkit.UncountedLocalVarsChecker]}}
+ reseatViaPointerToPointer(&obj);
+ ptr->method();
+}
+
+void passAddressOfPointerAsConst(RefCountable* obj) {
+ RefCountable* ptr = obj;
+ readViaPointerToConstPointer(&obj);
+ ptr->method();
+}
+
+void passAddressOfReference(RefCountable& obj) {
+ RefCountable& ref = obj;
+ consumePtr(&obj);
+ ref.method();
+}
+
+void reseatSmartPointer(RefPtr<RefCountable>*);
+
+void passAddressOfSmartPointerParameter(RefPtr<RefCountable>& guardian) {
+ RefCountable* ptr = guardian.get();
+ // expected-warning at -1{{Local variable 'ptr' is a raw pointer to RefPtr-capable type 'RefCountable' [alpha.webkit.UncountedLocalVarsChecker]}}
+ reseatSmartPointer(&guardian);
+ ptr->method();
+}
+
+} // namespace raw_pointer_or_reference_guardian_parameter
+
namespace local_assignment_to_parameter {
RefCountable *provide_ref_cntbl();
More information about the cfe-commits
mailing list