[clang] [Webkit Checkers] Treat const member variables as a safe origin (PR #115594)

Ryosuke Niwa via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 14 17:01:33 PST 2024


https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/115594

>From 28ee8321eb6e405fd1ebae9043c3ffafe20a4b35 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Sat, 9 Nov 2024 00:14:36 -0800
Subject: [PATCH 1/5] [Webkit Checkers] Treat const member variables as a safe
 origin

Treat const Ref, RefPtr, CheckedRef, CheckedPtr member variables as safe pointer origin
in WebKit's local variable and call arguments checkers.
---
 .../Checkers/WebKit/ASTUtils.cpp              |  6 +++
 .../WebKit/RawPtrRefLocalVarsChecker.cpp      |  8 +++
 .../WebKit/call-args-checked-const-member.cpp | 43 ++++++++++++++++
 .../WebKit/call-args-counted-const-member.cpp | 43 ++++++++++++++++
 .../local-vars-checked-const-member.cpp       | 50 +++++++++++++++++++
 .../local-vars-counted-const-member.cpp       | 50 +++++++++++++++++++
 .../Analysis/Checkers/WebKit/mock-types.h     | 25 ++++------
 7 files changed, 211 insertions(+), 14 deletions(-)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/call-args-checked-const-member.cpp
 create mode 100644 clang/test/Analysis/Checkers/WebKit/call-args-counted-const-member.cpp
 create mode 100644 clang/test/Analysis/Checkers/WebKit/local-vars-checked-const-member.cpp
 create mode 100644 clang/test/Analysis/Checkers/WebKit/local-vars-counted-const-member.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 9d34dfd3cea636..ad2be6f6793cea 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -142,6 +142,12 @@ bool isASafeCallArg(const Expr *E) {
         return true;
     }
   }
+  if (auto *ME = dyn_cast<MemberExpr>(E)) {
+    if (auto *D = ME->getMemberDecl()) {
+      auto T = D->getType();
+      return isSafePtrType(T) && T.isConstQualified();
+    }
+  }
 
   // TODO: checker for method calls on non-refcounted objects
   return isa<CXXThisExpr>(E);
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
index 06f8f43cee8151..be954ad3026c22 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
@@ -281,6 +281,14 @@ class RawPtrRefLocalVarsChecker
                 if (isa<IntegerLiteral>(InitArgOrigin))
                   return true;
 
+                if (auto *ME = dyn_cast<MemberExpr>(InitArgOrigin)) {
+                  if (auto *D = ME->getMemberDecl()) {
+                    auto T = D->getType();
+                    if (isSafePtrType(T) && T.isConstQualified())
+                      return true;
+                  }
+                }
+
                 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
                   if (auto *MaybeGuardian =
                           dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-checked-const-member.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-checked-const-member.cpp
new file mode 100644
index 00000000000000..6a54ac7b2ca791
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-checked-const-member.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncheckedCallArgsChecker -verify %s
+
+#include "mock-types.h"
+
+namespace call_args_const_checkedptr_member {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const CheckedPtr<CheckedObj> m_obj1;
+  CheckedPtr<CheckedObj> m_obj2;
+};
+
+void Foo::bar() {
+  m_obj1->method();
+  m_obj2->method();
+  // expected-warning at -1{{Call argument for 'this' parameter is unchecked and unsafe}}
+}
+
+} // namespace call_args_const_checkedptr_member
+
+namespace call_args_const_checkedref_member {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const CheckedRef<CheckedObj> m_obj1;
+  CheckedRef<CheckedObj> m_obj2;
+};
+
+void Foo::bar() {
+  m_obj1->method();
+  m_obj2->method();
+  // expected-warning at -1{{Call argument for 'this' parameter is unchecked and unsafe}}
+}
+
+} // namespace call_args_const_checkedref_member
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-counted-const-member.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-counted-const-member.cpp
new file mode 100644
index 00000000000000..33af5ad9014de4
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-counted-const-member.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+
+#include "mock-types.h"
+
+namespace call_args_const_refptr_member {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const RefPtr<RefCountable> m_obj1;
+  RefPtr<RefCountable> m_obj2;
+};
+
+void Foo::bar() {
+  m_obj1->method();
+  m_obj2->method();
+  // expected-warning at -1{{Call argument for 'this' parameter is uncounted and unsafe}}
+}
+
+} // namespace call_args_const_refptr_member
+
+namespace call_args_const_ref_member {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const Ref<RefCountable> m_obj1;
+  Ref<RefCountable> m_obj2;
+};
+
+void Foo::bar() {
+  m_obj1->method();
+  m_obj2->method();
+  // expected-warning at -1{{Call argument for 'this' parameter is uncounted and unsafe}}
+}
+
+} // namespace call_args_const_ref_member
diff --git a/clang/test/Analysis/Checkers/WebKit/local-vars-checked-const-member.cpp b/clang/test/Analysis/Checkers/WebKit/local-vars-checked-const-member.cpp
new file mode 100644
index 00000000000000..3deef66c271096
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/local-vars-checked-const-member.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncheckedLocalVarsChecker -verify %s
+
+#include "mock-types.h"
+#include "mock-system-header.h"
+
+void someFunction();
+
+namespace local_vars_const_checkedptr_member {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const CheckedPtr<CheckedObj> m_obj1;
+  CheckedPtr<CheckedObj> m_obj2;
+};
+
+void Foo::bar() {
+  auto* obj1 = m_obj1.get();
+  obj1->method();
+  auto* obj2 = m_obj2.get();
+  // expected-warning at -1{{Local variable 'obj2' is unchecked and unsafe [alpha.webkit.UncheckedLocalVarsChecker]}}
+  obj2->method();
+}
+
+} // namespace local_vars_const_checkedptr_member
+
+namespace local_vars_const_checkedref_member {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const CheckedRef<CheckedObj> m_obj1;
+  CheckedRef<CheckedObj> m_obj2;
+};
+
+void Foo::bar() {
+  auto& obj1 = m_obj1.get();
+  obj1.method();
+  auto& obj2 = m_obj2.get();
+  // expected-warning at -1{{Local variable 'obj2' is unchecked and unsafe [alpha.webkit.UncheckedLocalVarsChecker]}}
+  obj2.method();
+}
+
+} // namespace local_vars_const_ref_member
diff --git a/clang/test/Analysis/Checkers/WebKit/local-vars-counted-const-member.cpp b/clang/test/Analysis/Checkers/WebKit/local-vars-counted-const-member.cpp
new file mode 100644
index 00000000000000..6d6a7718244002
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/local-vars-counted-const-member.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedLocalVarsChecker -verify %s
+
+#include "mock-types.h"
+#include "mock-system-header.h"
+
+void someFunction();
+
+namespace local_vars_const_refptr_member {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const RefPtr<RefCountable> m_obj1;
+  RefPtr<RefCountable> m_obj2;
+};
+
+void Foo::bar() {
+  auto* obj1 = m_obj1.get();
+  obj1->method();
+  auto* obj2 = m_obj2.get();
+  // expected-warning at -1{{Local variable 'obj2' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
+  obj2->method();
+}
+
+} // namespace local_vars_const_refptr_member
+
+namespace local_vars_const_ref_member {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const Ref<RefCountable> m_obj1;
+  Ref<RefCountable> m_obj2;
+};
+
+void Foo::bar() {
+  auto& obj1 = m_obj1.get();
+  obj1.method();
+  auto& obj2 = m_obj2.get();
+  // expected-warning at -1{{Local variable 'obj2' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
+  obj2.method();
+}
+
+} // namespace local_vars_const_ref_member
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index 9c9326f0f11cfb..8f84589477bff1 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -66,11 +66,10 @@ template <typename T, typename PtrTraits = RawPtrTraits<T>, typename RefDerefTra
     t = o.t;
     o.t = tmp;
   }
-  T &get() { return *PtrTraits::unwrap(t); }
-  T *ptr() { return PtrTraits::unwrap(t); }
-  T *operator->() { return PtrTraits::unwrap(t); }
-  operator const T &() const { return *PtrTraits::unwrap(t); }
-  operator T &() { return *PtrTraits::unwrap(t); }
+  T &get() const { return *PtrTraits::unwrap(t); }
+  T *ptr() const { return PtrTraits::unwrap(t); }
+  T *operator->() const { return PtrTraits::unwrap(t); }
+  operator T &() const { return *PtrTraits::unwrap(t); }
   T* leakRef() { return PtrTraits::exchange(t, nullptr); }
 };
 
@@ -102,9 +101,8 @@ template <typename T> struct RefPtr {
     t = o.t;
     o.t = tmp;
   }
-  T *get() { return t; }
-  T *operator->() { return t; }
-  const T *operator->() const { return t; }
+  T *get() const { return t; }
+  T *operator->() const { return t; }
   T &operator*() { return *t; }
   RefPtr &operator=(T *t) {
     RefPtr o(t);
@@ -149,9 +147,9 @@ template <typename T> struct CheckedRef {
   CheckedRef(T &t) : t(&t) { t.incrementCheckedPtrCount(); }
   CheckedRef(const CheckedRef &o) : t(o.t) { if (t) t->incrementCheckedPtrCount(); }
   ~CheckedRef() { if (t) t->decrementCheckedPtrCount(); }
-  T &get() { return *t; }
-  T *ptr() { return t; }
-  T *operator->() { return t; }
+  T &get() const { return *t; }
+  T *ptr() const { return t; }
+  T *operator->() const { return t; }
   operator const T &() const { return *t; }
   operator T &() { return *t; }
 };
@@ -174,9 +172,8 @@ template <typename T> struct CheckedPtr {
     if (t)
       t->decrementCheckedPtrCount();
   }
-  T *get() { return t; }
-  T *operator->() { return t; }
-  const T *operator->() const { return t; }
+  T *get() const { return t; }
+  T *operator->() const { return t; }
   T &operator*() { return *t; }
   CheckedPtr &operator=(T *) { return *this; }
   operator bool() const { return t; }

>From 8bda96559ff4fc50a76b25a1879cc7fc2c828616 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Tue, 12 Nov 2024 23:14:57 -0800
Subject: [PATCH 2/5] Add the support for treating const unique_ptr as a safe
 pointer origin.

Also allow .get() and .ptr() on const member variable.
---
 .../Checkers/WebKit/ASTUtils.cpp              | 28 +++++++++++++++----
 .../StaticAnalyzer/Checkers/WebKit/ASTUtils.h |  3 ++
 .../Checkers/WebKit/PtrTypesSemantics.cpp     | 22 +++++++++++----
 .../Checkers/WebKit/PtrTypesSemantics.h       |  4 +++
 .../WebKit/RawPtrRefLocalVarsChecker.cpp      |  9 ++----
 .../WebKit/call-args-checked-const-member.cpp | 20 +++++++++++++
 .../WebKit/call-args-counted-const-member.cpp | 23 +++++++++++++++
 .../local-vars-checked-const-member.cpp       | 22 +++++++++++++++
 .../local-vars-counted-const-member.cpp       | 22 +++++++++++++++
 .../Analysis/Checkers/WebKit/mock-types.h     | 27 ++++++++++++++++++
 10 files changed, 162 insertions(+), 18 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index ad2be6f6793cea..b3cd594a0f3529 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -142,15 +142,31 @@ bool isASafeCallArg(const Expr *E) {
         return true;
     }
   }
-  if (auto *ME = dyn_cast<MemberExpr>(E)) {
-    if (auto *D = ME->getMemberDecl()) {
-      auto T = D->getType();
-      return isSafePtrType(T) && T.isConstQualified();
-    }
-  }
+  if (isConstOwnerPtrMemberExpr(E))
+    return true;
 
   // TODO: checker for method calls on non-refcounted objects
   return isa<CXXThisExpr>(E);
 }
 
+bool isConstOwnerPtrMemberExpr(const clang::Expr *E) {
+  if (auto *MCE = dyn_cast<CXXMemberCallExpr>(E)) {
+    if (auto *Callee = MCE->getDirectCallee()) {
+      auto Name = safeGetName(Callee);
+      if (Name == "get" || Name == "ptr") {
+        auto *ThisArg = MCE->getImplicitObjectArgument();
+        E = ThisArg;
+      }
+    }
+  }
+  auto *ME = dyn_cast<MemberExpr>(E);
+  if (!ME)
+    return false;
+  auto *D = ME->getMemberDecl();
+  if (!D)
+    return false;
+  auto T = D->getType();
+  return isOwnerPtrType(T) && T.isConstQualified();
+}
+
 } // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
index e972924e0c523d..ddbef0fba04489 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
@@ -64,6 +64,9 @@ bool tryToFindPtrOrigin(
 /// \returns Whether \p E is a safe call arugment.
 bool isASafeCallArg(const clang::Expr *E);
 
+/// \returns true if E is a MemberExpr accessing a const smart pointer type.
+bool isConstOwnerPtrMemberExpr(const clang::Expr *E);
+
 /// \returns name of AST node or empty string.
 template <typename T> std::string safeGetName(const T *ASTNode) {
   const auto *const ND = llvm::dyn_cast_or_null<clang::NamedDecl>(ASTNode);
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 31bebdb07dbdc2..b168fc4dc75e7f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -145,7 +145,8 @@ bool isCtorOfSafePtr(const clang::FunctionDecl *F) {
   return isCtorOfRefCounted(F) || isCtorOfCheckedPtr(F);
 }
 
-bool isSafePtrType(const clang::QualType T) {
+template <typename Predicate>
+static bool isPtrOfType(const clang::QualType T, Predicate Pred) {
   QualType type = T;
   while (!type.isNull()) {
     if (auto *elaboratedT = type->getAs<ElaboratedType>()) {
@@ -153,10 +154,8 @@ bool isSafePtrType(const clang::QualType T) {
       continue;
     }
     if (auto *specialT = type->getAs<TemplateSpecializationType>()) {
-      if (auto *decl = specialT->getTemplateName().getAsTemplateDecl()) {
-        auto name = decl->getNameAsString();
-        return isRefType(name) || isCheckedPtr(name);
-      }
+      if (auto *decl = specialT->getTemplateName().getAsTemplateDecl())
+        return Pred(decl->getNameAsString());
       return false;
     }
     return false;
@@ -164,6 +163,19 @@ bool isSafePtrType(const clang::QualType T) {
   return false;
 }
 
+bool isSafePtrType(const clang::QualType T) {
+  return isPtrOfType(T, [](auto Name) {
+    return isRefType(Name) || isCheckedPtr(Name);
+  });
+}
+
+bool isOwnerPtrType(const clang::QualType T) {
+  return isPtrOfType(T, [](auto Name) {
+    return isRefType(Name) || isCheckedPtr(Name) || Name == "unique_ptr" ||
+           Name == "UniqueRef" || Name == "LazyUniqueRef";
+  });
+}
+
 std::optional<bool> isUncounted(const QualType T) {
   if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(T)) {
     if (auto *Decl = Subst->getAssociatedDecl()) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 7c6c0a63f22aba..e9af4d8a0878e3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -79,6 +79,10 @@ std::optional<bool> isUncheckedPtr(const clang::QualType T);
 /// variant, false if not.
 bool isSafePtrType(const clang::QualType T);
 
+/// \returns true if \p T is a RefPtr, Ref, CheckedPtr, CheckedRef, or
+/// unique_ptr, false if not.
+bool isOwnerPtrType(const clang::QualType T);
+
 /// \returns true if \p F creates ref-countable object from uncounted parameter,
 /// false if not.
 bool isCtorOfRefCounted(const clang::FunctionDecl *F);
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
index be954ad3026c22..48c3dc4c94477a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
@@ -281,13 +281,8 @@ class RawPtrRefLocalVarsChecker
                 if (isa<IntegerLiteral>(InitArgOrigin))
                   return true;
 
-                if (auto *ME = dyn_cast<MemberExpr>(InitArgOrigin)) {
-                  if (auto *D = ME->getMemberDecl()) {
-                    auto T = D->getType();
-                    if (isSafePtrType(T) && T.isConstQualified())
-                      return true;
-                  }
-                }
+                if (isConstOwnerPtrMemberExpr(InitArgOrigin))
+                  return true;
 
                 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
                   if (auto *MaybeGuardian =
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-checked-const-member.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-checked-const-member.cpp
index 6a54ac7b2ca791..76f80a12c1703c 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-checked-const-member.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-checked-const-member.cpp
@@ -41,3 +41,23 @@ void Foo::bar() {
 }
 
 } // namespace call_args_const_checkedref_member
+
+namespace call_args_const_unique_ptr {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const std::unique_ptr<CheckedObj> m_obj1;
+  std::unique_ptr<CheckedObj> m_obj2;
+};
+
+void Foo::bar() {
+  m_obj1->method();
+  m_obj2->method();
+  // expected-warning at -1{{Call argument for 'this' parameter is unchecked and unsafe}}
+}
+
+} // namespace call_args_const_unique_ptr
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-counted-const-member.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-counted-const-member.cpp
index 33af5ad9014de4..cf47f744a44617 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-counted-const-member.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-counted-const-member.cpp
@@ -2,6 +2,9 @@
 
 #include "mock-types.h"
 
+namespace std {
+}
+
 namespace call_args_const_refptr_member {
 
 class Foo {
@@ -41,3 +44,23 @@ void Foo::bar() {
 }
 
 } // namespace call_args_const_ref_member
+
+namespace call_args_const_unique_ptr {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const std::unique_ptr<RefCountable> m_obj1;
+  std::unique_ptr<RefCountable> m_obj2;
+};
+
+void Foo::bar() {
+  m_obj1->method();
+  m_obj2->method();
+  // expected-warning at -1{{Call argument for 'this' parameter is uncounted and unsafe}}
+}
+
+} // namespace call_args_const_unique_ptr
diff --git a/clang/test/Analysis/Checkers/WebKit/local-vars-checked-const-member.cpp b/clang/test/Analysis/Checkers/WebKit/local-vars-checked-const-member.cpp
index 3deef66c271096..e52d1e735f6379 100644
--- a/clang/test/Analysis/Checkers/WebKit/local-vars-checked-const-member.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/local-vars-checked-const-member.cpp
@@ -48,3 +48,25 @@ void Foo::bar() {
 }
 
 } // namespace local_vars_const_ref_member
+
+namespace call_args_const_unique_ptr {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const std::unique_ptr<CheckedObj> m_obj1;
+  std::unique_ptr<CheckedObj> m_obj2;
+};
+
+void Foo::bar() {
+  auto* obj1 = m_obj1.get();
+  obj1->method();
+  auto* obj2 = m_obj2.get();
+  // expected-warning at -1{{Local variable 'obj2' is unchecked and unsafe [alpha.webkit.UncheckedLocalVarsChecker]}}
+  obj2->method();
+}
+
+} // namespace call_args_const_unique_ptr
diff --git a/clang/test/Analysis/Checkers/WebKit/local-vars-counted-const-member.cpp b/clang/test/Analysis/Checkers/WebKit/local-vars-counted-const-member.cpp
index 6d6a7718244002..03d16285f88b53 100644
--- a/clang/test/Analysis/Checkers/WebKit/local-vars-counted-const-member.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/local-vars-counted-const-member.cpp
@@ -48,3 +48,25 @@ void Foo::bar() {
 }
 
 } // namespace local_vars_const_ref_member
+
+namespace call_args_const_unique_ptr {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const std::unique_ptr<RefCountable> m_obj1;
+  std::unique_ptr<RefCountable> m_obj2;
+};
+
+void Foo::bar() {
+  auto* obj1 = m_obj1.get();
+  obj1->method();
+  auto* obj2 = m_obj2.get();
+  // expected-warning at -1{{Local variable 'obj2' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
+  obj2->method();
+}
+
+} // namespace call_args_const_unique_ptr
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index 8f84589477bff1..5dcf23a388c492 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -197,4 +197,31 @@ class RefCountableAndCheckable {
   int trivial() { return 0; }
 };
 
+namespace std {
+
+template <typename T>
+class unique_ptr {
+private:
+  T *t;
+
+public:
+  unique_ptr() : t(nullptr) { }
+  unique_ptr(T *t) : t(t) { }
+  ~unique_ptr() {
+    if (t)
+      delete t;
+  }
+  template <typename U> unique_ptr(unique_ptr<U>&& u)
+    : t(u.t)
+  {
+    u.t = nullptr;
+  }
+  T *get() const { return t; }
+  T *operator->() const { return t; }
+  T &operator*() { return *t; }
+  unique_ptr &operator=(T *) { return *this; }
+};
+
+};
+
 #endif

>From 4b7233afd73ac80c3f5a4f0b373a689115a8ba81 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Tue, 12 Nov 2024 23:21:57 -0800
Subject: [PATCH 3/5] Fix formatting.

---
 .../lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index b168fc4dc75e7f..e92c261c54e431 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -164,9 +164,8 @@ static bool isPtrOfType(const clang::QualType T, Predicate Pred) {
 }
 
 bool isSafePtrType(const clang::QualType T) {
-  return isPtrOfType(T, [](auto Name) {
-    return isRefType(Name) || isCheckedPtr(Name);
-  });
+  return isPtrOfType(
+      T, [](auto Name) {return isRefType(Name) || isCheckedPtr(Name); });
 }
 
 bool isOwnerPtrType(const clang::QualType T) {

>From 4efb59c4f8bfb2883bca86e4bd6b1a8ffa1fb619 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Tue, 12 Nov 2024 23:27:43 -0800
Subject: [PATCH 4/5] Fix formatting 2.

---
 clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index e92c261c54e431..76dec6f2753251 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -165,7 +165,7 @@ static bool isPtrOfType(const clang::QualType T, Predicate Pred) {
 
 bool isSafePtrType(const clang::QualType T) {
   return isPtrOfType(
-      T, [](auto Name) {return isRefType(Name) || isCheckedPtr(Name); });
+      T, [](auto Name) { return isRefType(Name) || isCheckedPtr(Name); });
 }
 
 bool isOwnerPtrType(const clang::QualType T) {

>From b74c55ae5e401701c7e4c9e5e29904a06cb5855e Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Thu, 14 Nov 2024 16:59:39 -0800
Subject: [PATCH 5/5] Use more early return in isPtrOfType and add a test case
 for UniqueRef

---
 .../Checkers/WebKit/PtrTypesSemantics.cpp     | 11 +++++-----
 .../WebKit/call-args-counted-const-member.cpp | 20 ++++++++++++++++++
 .../Analysis/Checkers/WebKit/mock-types.h     | 21 +++++++++++++++++++
 3 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 76dec6f2753251..76d3975536e260 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -153,12 +153,13 @@ static bool isPtrOfType(const clang::QualType T, Predicate Pred) {
       type = elaboratedT->desugar();
       continue;
     }
-    if (auto *specialT = type->getAs<TemplateSpecializationType>()) {
-      if (auto *decl = specialT->getTemplateName().getAsTemplateDecl())
-        return Pred(decl->getNameAsString());
+    auto *SpecialT = type->getAs<TemplateSpecializationType>();
+    if (!SpecialT)
       return false;
-    }
-    return false;
+    auto *Decl = SpecialT->getTemplateName().getAsTemplateDecl();
+    if (!Decl)
+      return false;
+    return Pred(Decl->getNameAsString());
   }
   return false;
 }
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-counted-const-member.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-counted-const-member.cpp
index cf47f744a44617..b3296507a5c92d 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-counted-const-member.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-counted-const-member.cpp
@@ -64,3 +64,23 @@ void Foo::bar() {
 }
 
 } // namespace call_args_const_unique_ptr
+
+namespace call_args_const_unique_ref {
+
+class Foo {
+public:
+  Foo();
+  void bar();
+
+private:
+  const UniqueRef<RefCountable> m_obj1;
+  UniqueRef<RefCountable> m_obj2;
+};
+
+void Foo::bar() {
+  m_obj1->method();
+  m_obj2->method();
+  // expected-warning at -1{{Call argument for 'this' parameter is uncounted and unsafe}}
+}
+
+} // namespace call_args_const_unique_ref
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index 5dcf23a388c492..82dc4a6a5b3c8c 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -197,6 +197,27 @@ class RefCountableAndCheckable {
   int trivial() { return 0; }
 };
 
+template <typename T>
+class UniqueRef {
+private:
+  T *t;
+
+public:
+  UniqueRef(T &t) : t(&t) { }
+  ~UniqueRef() {
+    if (t)
+      delete t;
+  }
+  template <typename U> UniqueRef(UniqueRef<U>&& u)
+    : t(u.t)
+  {
+    u.t = nullptr;
+  }
+  T &get() const { return *t; }
+  T *operator->() const { return t; }
+  UniqueRef &operator=(T &) { return *this; }
+};
+
 namespace std {
 
 template <typename T>



More information about the cfe-commits mailing list