https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/157993
>From e4334be7318658f8530747d3c275499157a41149 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa <rn...@webkit.org> Date: Wed, 10 Sep 2025 22:15:10 -0700 Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] A return value can be erroneously treated as unsafe if it's a template parameter When a template class takes Ref<T> as a template parameter and this template parameter is used as the return value of a member function, the return value can be treated as unsafe (i.e. emits a false positive). The issue was caused by getCanonicalType sometimes converting Ref<T> to T. Workaround this problem by avoid emitting a warning when the original, non-canonical type is a safe pointer type. --- .../WebKit/RawPtrRefCallArgsChecker.cpp | 17 +++++++++++++-- .../WebKit/template-wrapper-call-arg.cpp | 21 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/template-wrapper-call-arg.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp index e80f1749d595b..47bd13bea4abb 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp @@ -122,9 +122,22 @@ class RawPtrRefCallArgsChecker return; } auto *E = MemberCallExpr->getImplicitObjectArgument(); - QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType(); + auto MemberCallQT = MemberCallExpr->getObjectType(); + QualType ArgType = MemberCallQT.getCanonicalType(); std::optional<bool> IsUnsafe = isUnsafeType(ArgType); - if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(E)) + + // Sometimes, canonical type erroneously turns Ref<T> into T. + // Workaround this problem by checking again if the original type was + // a SubstTemplateTypeParmType of a safe smart pointer type (e.g. Ref). + bool IsSafePtr = false; + if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(MemberCallQT)) { + if (auto *Decl = Subst->getAssociatedDecl()) { + if (auto *CXXRD = dyn_cast<CXXRecordDecl>(Decl)) + IsSafePtr = isSafePtr(CXXRD); + } + } + + if (IsUnsafe && *IsUnsafe && !IsSafePtr && !isPtrOriginSafe(E)) reportBugOnThis(E, D); } diff --git a/clang/test/Analysis/Checkers/WebKit/template-wrapper-call-arg.cpp b/clang/test/Analysis/Checkers/WebKit/template-wrapper-call-arg.cpp new file mode 100644 index 0000000000000..b0ff210f9415e --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/template-wrapper-call-arg.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s +// expected-no-diagnostics + +#include "mock-types.h" + +struct Obj { + void ref() const; + void deref() const; + + void someFunction(); +}; + +template<typename T> class Wrapper { +public: + T obj(); +}; + +static void foo(Wrapper<Ref<Obj>>&& wrapper) +{ + wrapper.obj()->someFunction(); +} >From a5593c64c71a588ef30d56eb9e6b89bde7eb8ea2 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa <rn...@webkit.org> Date: Thu, 11 Sep 2025 12:46:18 -0700 Subject: [PATCH 2/2] Fix the failing test case. We need to be checking the return type of a callee, not any member call type. --- .../Checkers/WebKit/ASTUtils.cpp | 18 ++++++++++++++++++ .../WebKit/RawPtrRefCallArgsChecker.cpp | 17 ++--------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 9a7f5b71cae71..f49a6f3b16491 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -154,6 +154,24 @@ bool tryToFindPtrOrigin( Name == "NSClassFromString") return callback(E, true); } + + // Sometimes, canonical type erroneously turns Ref<T> into T. + // Workaround this problem by checking again if the original type was + // a SubstTemplateTypeParmType of a safe smart pointer type (e.g. Ref). + if (auto *CalleeDecl = call->getCalleeDecl()) { + if (auto *FD = dyn_cast<FunctionDecl>(CalleeDecl)) { + auto RetType = FD->getReturnType(); + if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(RetType)) { + if (auto *SubstType = Subst->desugar().getTypePtr()) { + if (auto *RD = dyn_cast<RecordType>(SubstType)) { + if (auto *CXX = dyn_cast<CXXRecordDecl>(RD->getOriginalDecl())) + if (isSafePtr(CXX)) + return callback(E, true); + } + } + } + } + } } if (auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E)) { if (auto *Method = ObjCMsgExpr->getMethodDecl()) { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp index 47bd13bea4abb..e80f1749d595b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp @@ -122,22 +122,9 @@ class RawPtrRefCallArgsChecker return; } auto *E = MemberCallExpr->getImplicitObjectArgument(); - auto MemberCallQT = MemberCallExpr->getObjectType(); - QualType ArgType = MemberCallQT.getCanonicalType(); + QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType(); std::optional<bool> IsUnsafe = isUnsafeType(ArgType); - - // Sometimes, canonical type erroneously turns Ref<T> into T. - // Workaround this problem by checking again if the original type was - // a SubstTemplateTypeParmType of a safe smart pointer type (e.g. Ref). - bool IsSafePtr = false; - if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(MemberCallQT)) { - if (auto *Decl = Subst->getAssociatedDecl()) { - if (auto *CXXRD = dyn_cast<CXXRecordDecl>(Decl)) - IsSafePtr = isSafePtr(CXXRD); - } - } - - if (IsUnsafe && *IsUnsafe && !IsSafePtr && !isPtrOriginSafe(E)) + if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(E)) reportBugOnThis(E, D); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits