[clang] [Analyzer] Support RefAllowingPartiallyDestroyed and RefPtrAllowingPartiallyDestroyed (PR #82209)

Ryosuke Niwa via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 19 01:37:58 PST 2024


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

>From d132b98b04d0cfb925f453f99a53f357d95c8e08 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Sun, 18 Feb 2024 21:47:48 -0800
Subject: [PATCH] [Analyzer] Support RefAllowingPartiallyDestroyed and
 RefPtrAllowingPartiallyDestroyed

This PR adds the support for WebKit's RefAllowingPartiallyDestroyed and
RefPtrAllowingPartiallyDestroyed, which are smart pointer types which may be used
after the destructor had started running.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp     | 27 +++++------
 .../Analysis/Checkers/WebKit/mock-types.h     |  3 +-
 .../ref-allowing-partially-destroyed.cpp      | 45 +++++++++++++++++++
 3 files changed, 61 insertions(+), 14 deletions(-)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-allowing-partially-destroyed.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 6f236db0474079..6f768983edb65b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -103,15 +103,18 @@ std::optional<bool> isRefCountable(const CXXRecordDecl* R)
   return hasRef && hasDeref;
 }
 
+bool isRefType(const std::string &name) {
+  return name == "Ref" || name == "RefAllowingPartiallyDestroyed" ||
+         name == "RefPtr" || name == "RefPtrAllowingPartiallyDestroyed";
+}
+
 bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
   assert(F);
   const auto &FunctionName = safeGetName(F);
 
-  return FunctionName == "Ref" || FunctionName == "makeRef"
-
-         || FunctionName == "RefPtr" || FunctionName == "makeRefPtr"
-
-         || FunctionName == "UniqueRef" || FunctionName == "makeUniqueRef" ||
+  return isRefType(FunctionName) || FunctionName == "makeRef" ||
+         FunctionName == "makeRefPtr" || FunctionName == "UniqueRef" ||
+         FunctionName == "makeUniqueRef" ||
          FunctionName == "makeUniqueRefWithoutFastMallocCheck"
 
          || FunctionName == "String" || FunctionName == "AtomString" ||
@@ -131,7 +134,7 @@ bool isReturnValueRefCounted(const clang::FunctionDecl *F) {
     if (auto *specialT = type->getAs<TemplateSpecializationType>()) {
       if (auto *decl = specialT->getTemplateName().getAsTemplateDecl()) {
         auto name = decl->getNameAsString();
-        return name == "Ref" || name == "RefPtr";
+        return isRefType(name);
       }
       return false;
     }
@@ -172,20 +175,18 @@ std::optional<bool> isGetterOfRefCounted(const CXXMethodDecl* M)
   if (isa<CXXMethodDecl>(M)) {
     const CXXRecordDecl *calleeMethodsClass = M->getParent();
     auto className = safeGetName(calleeMethodsClass);
-    auto methodName = safeGetName(M);
+    auto method = safeGetName(M);
 
-    if (((className == "Ref" || className == "RefPtr") &&
-         methodName == "get") ||
-        (className == "Ref" && methodName == "ptr") ||
+    if ((isRefType(className) && (method == "get" || method == "ptr")) ||
         ((className == "String" || className == "AtomString" ||
           className == "AtomStringImpl" || className == "UniqueString" ||
           className == "UniqueStringImpl" || className == "Identifier") &&
-         methodName == "impl"))
+         method == "impl"))
       return true;
 
     // Ref<T> -> T conversion
     // FIXME: Currently allowing any Ref<T> -> whatever cast.
-    if (className == "Ref" || className == "RefPtr") {
+    if (isRefType(className)) {
       if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) {
         if (auto *targetConversionType =
                 maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) {
@@ -202,7 +203,7 @@ bool isRefCounted(const CXXRecordDecl *R) {
   if (auto *TmplR = R->getTemplateInstantiationPattern()) {
     // FIXME: String/AtomString/UniqueString
     const auto &ClassName = safeGetName(TmplR);
-    return ClassName == "RefPtr" || ClassName == "Ref";
+    return isRefType(ClassName);
   }
   return false;
 }
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index d08a997aa8c043..e43641c0c61445 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -5,9 +5,10 @@ template <typename T> struct Ref {
   T *t;
 
   Ref() : t{} {};
-  Ref(T *) {}
+  Ref(T &) {}
   T *get() { return t; }
   T *ptr() { return t; }
+  T *operator->() { return t; }
   operator const T &() const { return *t; }
   operator T &() { return *t; }
 };
diff --git a/clang/test/Analysis/Checkers/WebKit/ref-allowing-partially-destroyed.cpp b/clang/test/Analysis/Checkers/WebKit/ref-allowing-partially-destroyed.cpp
new file mode 100644
index 00000000000000..6a5fbfb67c0c07
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/ref-allowing-partially-destroyed.cpp
@@ -0,0 +1,45 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+#include "mock-types.h"
+
+template <typename T> struct RefAllowingPartiallyDestroyed {
+  T *t;
+
+  RefAllowingPartiallyDestroyed() : t{} {};
+  RefAllowingPartiallyDestroyed(T &) {}
+  T *get() { return t; }
+  T *ptr() { return t; }
+  T *operator->() { return t; }
+  operator const T &() const { return *t; }
+  operator T &() { return *t; }
+};
+
+template <typename T> struct RefPtrAllowingPartiallyDestroyed {
+  T *t;
+
+  RefPtrAllowingPartiallyDestroyed() : t(new T) {}
+  RefPtrAllowingPartiallyDestroyed(T *t) : t(t) {}
+  T *get() { return t; }
+  T *operator->() { return t; }
+  const T *operator->() const { return t; }
+  T &operator*() { return *t; }
+  RefPtrAllowingPartiallyDestroyed &operator=(T *) { return *this; }
+  operator bool() { return t; }
+};
+
+class RefCounted {
+public:
+  void ref() const;
+  void deref() const;
+  void someFunction();
+};
+
+RefAllowingPartiallyDestroyed<RefCounted> object1();
+RefPtrAllowingPartiallyDestroyed<RefCounted> object2();
+RefCounted* object3();
+
+void testFunction() {
+  object1()->someFunction();
+  object2()->someFunction();
+}



More information about the cfe-commits mailing list