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
