https://github.com/iidmsa created 
https://github.com/llvm/llvm-project/pull/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**`.

>From 9aa5b444598a729c29f3b57c243c54f260a378f1 Mon Sep 17 00:00:00 2001
From: Fady Farag <[email protected]>
Date: Tue, 22 Sep 2026 01:45:15 -0500
Subject: [PATCH] [alpha.webkit.UncountedLocalVarsChecker] Don't treat a call
 through a raw pointer/reference guardian argument as a mutation

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**`.
---
 .../WebKit/RawPtrRefLocalVarsChecker.cpp      | 42 +++++++---
 .../Checkers/WebKit/uncounted-local-vars.cpp  | 84 +++++++++++++++++++
 2 files changed, 116 insertions(+), 10 deletions(-)

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@-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@-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@-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@-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();

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to