[clang] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. (PR #69985)

2023-11-09 Thread Ryosuke Niwa via cfe-commits

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

>From bf169f3d79f8a5b0658d9e060ddd6c3e773b979a Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 23 Oct 2023 16:38:10 -0700
Subject: [PATCH] Fix WebKit static analyzers to support ref and deref methods
 to be defined on different classes.

---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 62 ++-
 .../Checkers/WebKit/PtrTypesSemantics.h   |  8 +--
 .../WebKit/RefCntblBaseVirtualDtorChecker.cpp | 47 --
 ...virtual-dtor-ref-deref-on-diff-classes.cpp | 22 +++
 ...nted-members-ref-deref-on-diff-classes.cpp | 23 +++
 5 files changed, 126 insertions(+), 36 deletions(-)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-ref-deref-on-diff-classes.cpp
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/uncounted-members-ref-deref-on-diff-classes.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index c1f180f31338cb3..d2b663410580008 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -18,24 +18,15 @@ using namespace clang;
 
 namespace {
 
-bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
+bool hasPublicMethodInBaseClass(const CXXRecordDecl *R,
+const char *NameToMatch) {
   assert(R);
   assert(R->hasDefinition());
 
-  bool hasRef = false;
-  bool hasDeref = false;
   for (const CXXMethodDecl *MD : R->methods()) {
 const auto MethodName = safeGetName(MD);
-
-if (MethodName == "ref" && MD->getAccess() == AS_public) {
-  if (hasDeref)
-return true;
-  hasRef = true;
-} else if (MethodName == "deref" && MD->getAccess() == AS_public) {
-  if (hasRef)
-return true;
-  hasDeref = true;
-}
+if (MethodName == NameToMatch && MD->getAccess() == AS_public)
+  return true;
   }
   return false;
 }
@@ -44,9 +35,8 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
 
 namespace clang {
 
-std::optional
-isRefCountable(const CXXBaseSpecifier* Base)
-{
+std::optional
+hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) {
   assert(Base);
 
   const Type *T = Base->getType().getTypePtrOrNull();
@@ -59,7 +49,7 @@ isRefCountable(const CXXBaseSpecifier* Base)
   if (!R->hasDefinition())
 return std::nullopt;
 
-  return hasPublicRefAndDeref(R) ? R : nullptr;
+  return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr;
 }
 
 std::optional isRefCountable(const CXXRecordDecl* R)
@@ -70,29 +60,45 @@ std::optional isRefCountable(const CXXRecordDecl* R)
   if (!R)
 return std::nullopt;
 
-  if (hasPublicRefAndDeref(R))
+  bool hasRef = hasPublicMethodInBaseClass(R, "ref");
+  bool hasDeref = hasPublicMethodInBaseClass(R, "deref");
+  if (hasRef && hasDeref)
 return true;
 
   CXXBasePaths Paths;
   Paths.setOrigin(const_cast(R));
 
   bool AnyInconclusiveBase = false;
-  const auto isRefCountableBase =
-  [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) {
-  std::optional IsRefCountable = 
clang::isRefCountable(Base);
-  if (!IsRefCountable) {
-  AnyInconclusiveBase = true;
-  return false;
-  }
-  return (*IsRefCountable) != nullptr;
+  const auto hasPublicRefInBase =
+  [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
+auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
+if (!hasRefInBase) {
+  AnyInconclusiveBase = true;
+  return false;
+}
+return (*hasRefInBase) != nullptr;
   };
 
-  bool BasesResult = R->lookupInBases(isRefCountableBase, Paths,
+  hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths,
   /*LookupInDependent =*/true);
   if (AnyInconclusiveBase)
 return std::nullopt;
 
-  return BasesResult;
+  const auto hasPublicDerefInBase =
+  [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
+auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
+if (!hasDerefInBase) {
+  AnyInconclusiveBase = true;
+  return false;
+}
+return (*hasDerefInBase) != nullptr;
+  };
+  hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths,
+  /*LookupInDependent =*/true);
+  if (AnyInconclusiveBase)
+return std::nullopt;
+
+  return hasRef && hasDeref;
 }
 
 bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 91e3ccf2ec3047e..45b21cc0918443b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebK

[clang] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. (PR #69985)

2023-11-09 Thread Ryosuke Niwa via cfe-commits


@@ -77,14 +77,53 @@ class RefCntblBaseVirtualDtorChecker
   (AccSpec == AS_none && RD->isClass()))
 return false;
 
-  std::optional RefCntblBaseRD = 
isRefCountable(Base);
-  if (!RefCntblBaseRD || !(*RefCntblBaseRD))
+  auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
+  auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
+
+  bool hasRef = hasRefInBase && *hasRefInBase != nullptr;
+  bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr;
+
+  const Type *T = Base->getType().getTypePtrOrNull();
+  if (!T)
+return false;
+
+  const CXXRecordDecl *C = T->getAsCXXRecordDecl();

rniwa wrote:

Fixed.

https://github.com/llvm/llvm-project/pull/69985
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. (PR #69985)

2023-11-09 Thread Ryosuke Niwa via cfe-commits

rniwa wrote:

Yes, it would be great if you can merge the change now that I've addressed your 
nits.

https://github.com/llvm/llvm-project/pull/69985
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Ignore assignment to Ref / RefPtr in alpha.webkit.UncountedCallArgsChecker. (PR #80810)

2024-02-06 Thread Ryosuke Niwa via cfe-commits

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

>From e179bbef69084caac3948977a7091332c69130f5 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 6 Feb 2024 01:13:34 -0800
Subject: [PATCH] Ignore assignment to Ref / RefPtr in
 alpha.webkit.UncountedCallArgsChecker.

---
 .../WebKit/UncountedCallArgsChecker.cpp   | 10 +++
 .../Checkers/WebKit/assignment-to-refptr.cpp  | 71 +++
 2 files changed, 81 insertions(+)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 407b6ba7a76428..419140bf12b66c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -126,6 +126,16 @@ class UncountedCallArgsChecker
 // of object on LHS.
 if (auto *MemberOp = dyn_cast(CE)) {
   // Note: assignemnt to built-in type isn't derived from CallExpr.
+  if (MemberOp->getOperator() ==
+  OO_Equal) { // Ignore assignment to Ref/RefPtr.
+auto *callee = MemberOp->getDirectCallee();
+if (auto *calleeDecl = dyn_cast(callee)) {
+  if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
+if (isRefCounted(classDecl))
+  return true;
+  }
+}
+  }
   if (MemberOp->isAssignmentOp())
 return false;
 }
diff --git a/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp 
b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp
new file mode 100644
index 00..c8ad634a5493f5
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp
@@ -0,0 +1,71 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+template
+class RefPtr {
+public:
+inline constexpr RefPtr() : m_ptr(nullptr) { }
+inline RefPtr(T* ptr)
+  : m_ptr(ptr)
+{
+if (m_ptr)
+m_ptr->ref();
+}
+
+inline RefPtr(const RefPtr& o)
+: m_ptr(o.m_ptr)
+{
+if (m_ptr)
+m_ptr->ref();
+}
+
+inline ~RefPtr()
+{
+if (m_ptr)
+m_ptr->deref();
+}
+
+inline T* operator->() const { return m_ptr; }
+explicit operator bool() const { return m_ptr; }
+  
+RefPtr& operator=(const RefPtr&);
+RefPtr& operator=(T*);
+
+private:
+T* m_ptr;
+};
+
+template
+inline RefPtr& RefPtr::operator=(const RefPtr& o)
+{
+if (m_ptr)
+m_ptr->deref();
+m_ptr = o.m_ptr;
+if (m_ptr)
+m_ptr->ref();
+return *this;
+}
+
+template
+inline RefPtr& RefPtr::operator=(T* optr)
+{
+if (m_ptr)
+m_ptr->deref();
+m_ptr = optr;
+if (m_ptr)
+m_ptr->ref();
+return *this;
+}
+
+class Node {
+public:
+Node* nextSibling() const;
+
+void ref() const;
+void deref() const;
+};
+
+static void removeDetachedChildren(Node* firstChild)
+{
+for (RefPtr child = firstChild; child; child = child->nextSibling());
+}

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Add the support for calling Ref::ptr accessor. (PR #80919)

2024-02-06 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/80919

This accessor returns a pointer from Ref type and is therefore safe.

>From 63a64cf22e5e470db3426688a2517c2fef64fd46 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 6 Feb 2024 17:56:01 -0800
Subject: [PATCH] Add the support for calling Ref::ptr accessor.

This accessor returns a pointer from Ref type and is therefore safe.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp |  1 +
 .../Checkers/WebKit/ref-ptr-accessor.cpp  | 86 +++
 2 files changed, 87 insertions(+)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index c1f180f31338cb..4bc3f7a8c9c3ce 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -148,6 +148,7 @@ std::optional isGetterOfRefCounted(const 
CXXMethodDecl* M)
 
 if (((className == "Ref" || className == "RefPtr") &&
  methodName == "get") ||
+(className == "Ref" && methodName == "ptr") ||
 ((className == "String" || className == "AtomString" ||
   className == "AtomStringImpl" || className == "UniqueString" ||
   className == "UniqueStringImpl" || className == "Identifier") &&
diff --git a/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp 
b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp
new file mode 100644
index 00..d3185a9f2d58a4
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp
@@ -0,0 +1,86 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+template
+class Ref {
+public:
+~Ref()
+{
+if (auto* ptr = m_ptr)
+ptr->deref();
+m_ptr = nullptr;
+}
+
+Ref(T& object)
+: m_ptr(&object)
+{
+object.ref();
+}
+
+Ref(const Ref& other)
+: m_ptr(other.ptr())
+{
+m_ptr->ref();
+}
+
+template Ref(const Ref& other)
+: m_ptr(other.ptr())
+{
+m_ptr->ref();
+}
+
+Ref(Ref&& other)
+: m_ptr(&other.leakRef())
+{
+}
+
+template
+Ref(Ref&& other)
+: m_ptr(&other.leakRef())
+{
+}
+
+T* operator->() const { return m_ptr; }
+T* ptr() const { return m_ptr; }
+T& get() const { return *m_ptr; }
+operator T&() const { return *m_ptr; }
+bool operator!() const { return !*m_ptr; }
+
+T& leakRef()
+{
+T& result = *m_ptr;
+m_ptr = nullptr;
+return result;
+}
+
+private:
+template friend inline Ref adoptRef(U&);
+
+enum AdoptTag { Adopt };
+Ref(T& object, AdoptTag)
+: m_ptr(&object)
+{
+}
+
+T* m_ptr;
+};
+
+class Node {
+public:
+static Ref create();
+virtual ~Node();
+
+void ref() const;
+void deref() const;
+
+protected:
+explicit Node();
+};
+
+void someFunction(Node*);
+
+void testFunction()
+{
+Ref node = Node::create();
+someFunction(node.ptr());
+}

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Add a new attribute value for suppressing WebKit's unsafe member variable warning (PR #70124)

2024-02-06 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa closed https://github.com/llvm/llvm-project/pull/70124
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Add the support for calling Ref::ptr accessor. (PR #80919)

2024-02-06 Thread Ryosuke Niwa via cfe-commits

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

>From b117f4c8247a14a02ddb2cc89493a54a6dd3815e Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 6 Feb 2024 17:56:01 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add the support for
 calling Ref::ptr accessor.

This accessor returns a pointer from Ref type and is therefore safe.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp |  1 +
 .../Checkers/WebKit/ref-ptr-accessor.cpp  | 86 +++
 2 files changed, 87 insertions(+)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index c1f180f31338c..4bc3f7a8c9c3c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -148,6 +148,7 @@ std::optional isGetterOfRefCounted(const 
CXXMethodDecl* M)
 
 if (((className == "Ref" || className == "RefPtr") &&
  methodName == "get") ||
+(className == "Ref" && methodName == "ptr") ||
 ((className == "String" || className == "AtomString" ||
   className == "AtomStringImpl" || className == "UniqueString" ||
   className == "UniqueStringImpl" || className == "Identifier") &&
diff --git a/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp 
b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp
new file mode 100644
index 0..d3185a9f2d58a
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp
@@ -0,0 +1,86 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+template
+class Ref {
+public:
+~Ref()
+{
+if (auto* ptr = m_ptr)
+ptr->deref();
+m_ptr = nullptr;
+}
+
+Ref(T& object)
+: m_ptr(&object)
+{
+object.ref();
+}
+
+Ref(const Ref& other)
+: m_ptr(other.ptr())
+{
+m_ptr->ref();
+}
+
+template Ref(const Ref& other)
+: m_ptr(other.ptr())
+{
+m_ptr->ref();
+}
+
+Ref(Ref&& other)
+: m_ptr(&other.leakRef())
+{
+}
+
+template
+Ref(Ref&& other)
+: m_ptr(&other.leakRef())
+{
+}
+
+T* operator->() const { return m_ptr; }
+T* ptr() const { return m_ptr; }
+T& get() const { return *m_ptr; }
+operator T&() const { return *m_ptr; }
+bool operator!() const { return !*m_ptr; }
+
+T& leakRef()
+{
+T& result = *m_ptr;
+m_ptr = nullptr;
+return result;
+}
+
+private:
+template friend inline Ref adoptRef(U&);
+
+enum AdoptTag { Adopt };
+Ref(T& object, AdoptTag)
+: m_ptr(&object)
+{
+}
+
+T* m_ptr;
+};
+
+class Node {
+public:
+static Ref create();
+virtual ~Node();
+
+void ref() const;
+void deref() const;
+
+protected:
+explicit Node();
+};
+
+void someFunction(Node*);
+
+void testFunction()
+{
+Ref node = Node::create();
+someFunction(node.ptr());
+}

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Fix an implicit cast to a base ref counted class generates a false positive. (PR #80934)

2024-02-06 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/80934

The bug was caused by isRefCountable erroneously returning false for a class 
with both ref() and deref() functions defined because we were not resetting the 
base paths results between looking for "ref()" and "deref()"

>From 377f096eda881b9a7c9b4d413081f11a3c4af62e Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 6 Feb 2024 20:10:33 -0800
Subject: [PATCH] Fix an implicit cast to a base ref counted class generates a
 false positive.

The bug was caused by isRefCountable erroneously returning false for a class
with both ref() and deref() functions defined because we were not resetting
the base paths results between looking for "ref()" and "deref()"
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp |  1 +
 ...to-base-class-with-deref-in-superclass.cpp | 51 +++
 2 files changed, 52 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..0fd8afedc0b0f5 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -84,6 +84,7 @@ std::optional isRefCountable(const CXXRecordDecl* R)
   if (AnyInconclusiveBase)
 return std::nullopt;
 
+  Paths.clear();
   const auto hasPublicDerefInBase =
   [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
 auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
diff --git 
a/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
 
b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
new file mode 100644
index 00..49826c98a4610d
--- /dev/null
+++ 
b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+template
+class Ref {
+public:
+~Ref()
+{
+if (auto* ptr = m_ptr)
+ptr->deref();
+m_ptr = nullptr;
+}
+
+Ref(T& object)
+: m_ptr(&object)
+{
+object.ref();
+}
+
+operator T&() const { return *m_ptr; }
+bool operator!() const { return !*m_ptr; }
+
+private:
+T* m_ptr;
+};
+
+class Base {
+public:
+virtual ~Base();
+void ref() const;
+void deref() const;
+};
+
+class Event : public Base {
+protected:
+explicit Event();
+};
+
+class SubEvent : public Event {
+public:
+static Ref create();
+private:
+SubEvent() = default;
+};
+
+void someFunction(Base&);
+
+static void test()
+{
+someFunction(SubEvent::create());
+}

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Fix an implicit cast to a base ref counted class generates a false positive. (PR #80934)

2024-02-06 Thread Ryosuke Niwa via cfe-commits

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

>From 9154815c48578df9ee384a9707dd79ee64259cea Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 6 Feb 2024 20:10:33 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Fix an implicit cast
 to a base ref counted class generates a false positive.

The bug was caused by isRefCountable erroneously returning false for a class
with both ref() and deref() functions defined because we were not resetting
the base paths results between looking for "ref()" and "deref()"
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp |  1 +
 ...to-base-class-with-deref-in-superclass.cpp | 51 +++
 2 files changed, 52 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..0fd8afedc0b0f5 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -84,6 +84,7 @@ std::optional isRefCountable(const CXXRecordDecl* R)
   if (AnyInconclusiveBase)
 return std::nullopt;
 
+  Paths.clear();
   const auto hasPublicDerefInBase =
   [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
 auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
diff --git 
a/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
 
b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
new file mode 100644
index 00..49826c98a4610d
--- /dev/null
+++ 
b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+template
+class Ref {
+public:
+~Ref()
+{
+if (auto* ptr = m_ptr)
+ptr->deref();
+m_ptr = nullptr;
+}
+
+Ref(T& object)
+: m_ptr(&object)
+{
+object.ref();
+}
+
+operator T&() const { return *m_ptr; }
+bool operator!() const { return !*m_ptr; }
+
+private:
+T* m_ptr;
+};
+
+class Base {
+public:
+virtual ~Base();
+void ref() const;
+void deref() const;
+};
+
+class Event : public Base {
+protected:
+explicit Event();
+};
+
+class SubEvent : public Event {
+public:
+static Ref create();
+private:
+SubEvent() = default;
+};
+
+void someFunction(Base&);
+
+static void test()
+{
+someFunction(SubEvent::create());
+}

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Allow default arguments to be evaluated like other arguments. (PR #80956)

2024-02-07 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/80956

This PR aligns the evaluation of default arguments with other kinds of 
arguments by extracting the expressions within them as argument values to be 
evaluated.

>From 9d3f7377901539abeef949c1b33a99b1278900d8 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 7 Feb 2024 00:57:00 -0800
Subject: [PATCH] Allow default arguments to be evaluated like other arguments.

This PR aligns the evaluation of default arguments with other kinds of arguments
by extracting the expressions within them as argument values to be evaluated.
---
 .../WebKit/UncountedCallArgsChecker.cpp   |  3 ++
 .../ref-countable-default-arg-nullptr.cpp | 45 +++
 2 files changed, 48 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..7cb0e4680d9e9e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -91,6 +91,9 @@ class UncountedCallArgsChecker
 
 const auto *Arg = CE->getArg(ArgIdx);
 
+if (auto *defaultArg = dyn_cast(Arg))
+  Arg = defaultArg->getExpr();
+
 std::pair ArgOrigin =
 tryToFindPtrOrigin(Arg, true);
 
diff --git 
a/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp 
b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp
new file mode 100644
index 00..cd38b335dcf85e
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp
@@ -0,0 +1,45 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+
+template 
+class RefPtr {
+public:
+  RefPtr(T* ptr)
+: m_ptr(ptr)
+  {
+if (m_ptr)
+  m_ptr->ref();
+  }
+
+  ~RefPtr()
+  {
+if (m_ptr)
+  m_ptr->deref();
+  }
+
+  T* get() { return m_ptr; }
+
+private:
+  T* m_ptr;
+};
+
+class Obj {
+public:
+  static Obj* get();
+  static RefPtr create();
+  void ref() const;
+  void deref() const;
+};
+
+void someFunction(Obj*, Obj* = nullptr);
+void otherFunction(Obj*, Obj* = Obj::get());
+// expected-warning@-1{{Call argument is uncounted and unsafe 
[alpha.webkit.UncountedCallArgsChecker]}}
+void anotherFunction(Obj*, Obj* = Obj::create().get());
+
+void otherFunction() {
+  someFunction(nullptr);
+  someFunction(Obj::get());
+  // expected-warning@-1{{Call argument is uncounted and unsafe 
[alpha.webkit.UncountedCallArgsChecker]}}
+  someFunction(Obj::create().get());
+  otherFunction(nullptr);
+  anotherFunction(nullptr);
+}

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Allow default arguments to be evaluated like other arguments. (PR #80956)

2024-02-07 Thread Ryosuke Niwa via cfe-commits

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

>From bc390afd3a2e9de6e2a883205ce3862e45d26e06 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 7 Feb 2024 00:57:00 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Allow default
 arguments to be evaluated like other arguments.

This PR aligns the evaluation of default arguments with other kinds of arguments
by extracting the expressions within them as argument values to be evaluated.
---
 .../WebKit/UncountedCallArgsChecker.cpp   |  3 ++
 .../ref-countable-default-arg-nullptr.cpp | 45 +++
 2 files changed, 48 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..7cb0e4680d9e9e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -91,6 +91,9 @@ class UncountedCallArgsChecker
 
 const auto *Arg = CE->getArg(ArgIdx);
 
+if (auto *defaultArg = dyn_cast(Arg))
+  Arg = defaultArg->getExpr();
+
 std::pair ArgOrigin =
 tryToFindPtrOrigin(Arg, true);
 
diff --git 
a/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp 
b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp
new file mode 100644
index 00..cd38b335dcf85e
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp
@@ -0,0 +1,45 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+
+template 
+class RefPtr {
+public:
+  RefPtr(T* ptr)
+: m_ptr(ptr)
+  {
+if (m_ptr)
+  m_ptr->ref();
+  }
+
+  ~RefPtr()
+  {
+if (m_ptr)
+  m_ptr->deref();
+  }
+
+  T* get() { return m_ptr; }
+
+private:
+  T* m_ptr;
+};
+
+class Obj {
+public:
+  static Obj* get();
+  static RefPtr create();
+  void ref() const;
+  void deref() const;
+};
+
+void someFunction(Obj*, Obj* = nullptr);
+void otherFunction(Obj*, Obj* = Obj::get());
+// expected-warning@-1{{Call argument is uncounted and unsafe 
[alpha.webkit.UncountedCallArgsChecker]}}
+void anotherFunction(Obj*, Obj* = Obj::create().get());
+
+void otherFunction() {
+  someFunction(nullptr);
+  someFunction(Obj::get());
+  // expected-warning@-1{{Call argument is uncounted and unsafe 
[alpha.webkit.UncountedCallArgsChecker]}}
+  someFunction(Obj::create().get());
+  otherFunction(nullptr);
+  anotherFunction(nullptr);
+}

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Add the support for calling Ref::ptr accessor. (PR #80919)

2024-02-09 Thread Ryosuke Niwa via cfe-commits


@@ -0,0 +1,86 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+template
+class Ref {

rniwa wrote:

Sure.

https://github.com/llvm/llvm-project/pull/80919
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Add the support for calling Ref::ptr accessor. (PR #80919)

2024-02-09 Thread Ryosuke Niwa via cfe-commits

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

>From 26f23953843b7c79664c7e721c3cbf130be405c0 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 6 Feb 2024 17:56:01 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add the support for
 calling Ref::ptr accessor.

This accessor returns a pointer from Ref type and is therefore safe.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp |  1 +
 .../Checkers/WebKit/ref-ptr-accessor.cpp  | 24 +++
 clang/test/Analysis/Checkers/WebKit/ref.h | 63 +++
 3 files changed, 88 insertions(+)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp
 create mode 100644 clang/test/Analysis/Checkers/WebKit/ref.h

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index c1f180f31338cb..4bc3f7a8c9c3ce 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -148,6 +148,7 @@ std::optional isGetterOfRefCounted(const 
CXXMethodDecl* M)
 
 if (((className == "Ref" || className == "RefPtr") &&
  methodName == "get") ||
+(className == "Ref" && methodName == "ptr") ||
 ((className == "String" || className == "AtomString" ||
   className == "AtomStringImpl" || className == "UniqueString" ||
   className == "UniqueStringImpl" || className == "Identifier") &&
diff --git a/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp 
b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp
new file mode 100644
index 00..1cd9bfb38c3b97
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp
@@ -0,0 +1,24 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+#include "ref.h"
+
+class Node {
+public:
+static Ref create();
+virtual ~Node();
+
+void ref() const;
+void deref() const;
+
+protected:
+explicit Node();
+};
+
+void someFunction(Node*);
+
+void testFunction()
+{
+Ref node = Node::create();
+someFunction(node.ptr());
+}
diff --git a/clang/test/Analysis/Checkers/WebKit/ref.h 
b/clang/test/Analysis/Checkers/WebKit/ref.h
new file mode 100644
index 00..051a0447391bef
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/ref.h
@@ -0,0 +1,63 @@
+template
+class Ref {
+public:
+~Ref()
+{
+if (auto* ptr = m_ptr)
+ptr->deref();
+m_ptr = nullptr;
+}
+
+Ref(T& object)
+: m_ptr(&object)
+{
+object.ref();
+}
+
+Ref(const Ref& other)
+: m_ptr(other.ptr())
+{
+m_ptr->ref();
+}
+
+template Ref(const Ref& other)
+: m_ptr(other.ptr())
+{
+m_ptr->ref();
+}
+
+Ref(Ref&& other)
+: m_ptr(&other.leakRef())
+{
+}
+
+template
+Ref(Ref&& other)
+: m_ptr(&other.leakRef())
+{
+}
+
+T* operator->() const { return m_ptr; }
+T* ptr() const { return m_ptr; }
+T& get() const { return *m_ptr; }
+operator T&() const { return *m_ptr; }
+bool operator!() const { return !*m_ptr; }
+
+T& leakRef()
+{
+T& result = *m_ptr;
+m_ptr = nullptr;
+return result;
+}
+
+private:
+template friend inline Ref adoptRef(U&);
+
+enum AdoptTag { Adopt };
+Ref(T& object, AdoptTag)
+: m_ptr(&object)
+{
+}
+
+T* m_ptr;
+};

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Add the support for calling Ref::ptr accessor. (PR #80919)

2024-02-09 Thread Ryosuke Niwa via cfe-commits

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

>From 6476d4cbca68815a96cea56519e3f5c75b4f9738 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 6 Feb 2024 17:56:01 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add the support for
 calling Ref::ptr accessor.

This accessor returns a pointer from Ref type and is therefore safe.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp|  1 +
 clang/test/Analysis/Checkers/WebKit/mock-types.h | 10 ++
 .../Analysis/Checkers/WebKit/ref-ptr-accessor.cpp| 12 
 3 files changed, 19 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index c1f180f31338cb..4bc3f7a8c9c3ce 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -148,6 +148,7 @@ std::optional isGetterOfRefCounted(const 
CXXMethodDecl* M)
 
 if (((className == "Ref" || className == "RefPtr") &&
  methodName == "get") ||
+(className == "Ref" && methodName == "ptr") ||
 ((className == "String" || className == "AtomString" ||
   className == "AtomStringImpl" || className == "UniqueString" ||
   className == "UniqueStringImpl" || className == "Identifier") &&
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index 5f570b8bee8cb8..f10d530e1109ef 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -2,13 +2,14 @@
 #define mock_types_1103988513531
 
 template  struct Ref {
-  T t;
+  T *t;
 
   Ref() : t{} {};
   Ref(T *) {}
-  T *get() { return nullptr; }
-  operator const T &() const { return t; }
-  operator T &() { return t; }
+  T *get() { return t; }
+  T *ptr() { return t; }
+  operator const T &() const { return *t; }
+  operator T &() { return *t; }
 };
 
 template  struct RefPtr {
@@ -39,6 +40,7 @@ template  bool operator!=(const RefPtr &, T *) 
{ return false; }
 template  bool operator!=(const RefPtr &, T &) { return false; }
 
 struct RefCountable {
+  static Ref create();
   void ref() {}
   void deref() {}
 };
diff --git a/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp 
b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp
new file mode 100644
index 00..560dcfd4bdb094
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/ref-ptr-accessor.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+#include "mock-types.h"
+
+void someFunction(RefCountable*);
+
+void testFunction()
+{
+Ref item = RefCountable::create();
+someFunction(item.ptr());
+}

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Ignore assignment to Ref / RefPtr in alpha.webkit.UncountedCallArgsChecker. (PR #80810)

2024-02-09 Thread Ryosuke Niwa via cfe-commits

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

>From e179bbef69084caac3948977a7091332c69130f5 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 6 Feb 2024 01:13:34 -0800
Subject: [PATCH 1/2] Ignore assignment to Ref / RefPtr in
 alpha.webkit.UncountedCallArgsChecker.

---
 .../WebKit/UncountedCallArgsChecker.cpp   | 10 +++
 .../Checkers/WebKit/assignment-to-refptr.cpp  | 71 +++
 2 files changed, 81 insertions(+)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 407b6ba7a76428..419140bf12b66c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -126,6 +126,16 @@ class UncountedCallArgsChecker
 // of object on LHS.
 if (auto *MemberOp = dyn_cast(CE)) {
   // Note: assignemnt to built-in type isn't derived from CallExpr.
+  if (MemberOp->getOperator() ==
+  OO_Equal) { // Ignore assignment to Ref/RefPtr.
+auto *callee = MemberOp->getDirectCallee();
+if (auto *calleeDecl = dyn_cast(callee)) {
+  if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
+if (isRefCounted(classDecl))
+  return true;
+  }
+}
+  }
   if (MemberOp->isAssignmentOp())
 return false;
 }
diff --git a/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp 
b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp
new file mode 100644
index 00..c8ad634a5493f5
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp
@@ -0,0 +1,71 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+template
+class RefPtr {
+public:
+inline constexpr RefPtr() : m_ptr(nullptr) { }
+inline RefPtr(T* ptr)
+  : m_ptr(ptr)
+{
+if (m_ptr)
+m_ptr->ref();
+}
+
+inline RefPtr(const RefPtr& o)
+: m_ptr(o.m_ptr)
+{
+if (m_ptr)
+m_ptr->ref();
+}
+
+inline ~RefPtr()
+{
+if (m_ptr)
+m_ptr->deref();
+}
+
+inline T* operator->() const { return m_ptr; }
+explicit operator bool() const { return m_ptr; }
+  
+RefPtr& operator=(const RefPtr&);
+RefPtr& operator=(T*);
+
+private:
+T* m_ptr;
+};
+
+template
+inline RefPtr& RefPtr::operator=(const RefPtr& o)
+{
+if (m_ptr)
+m_ptr->deref();
+m_ptr = o.m_ptr;
+if (m_ptr)
+m_ptr->ref();
+return *this;
+}
+
+template
+inline RefPtr& RefPtr::operator=(T* optr)
+{
+if (m_ptr)
+m_ptr->deref();
+m_ptr = optr;
+if (m_ptr)
+m_ptr->ref();
+return *this;
+}
+
+class Node {
+public:
+Node* nextSibling() const;
+
+void ref() const;
+void deref() const;
+};
+
+static void removeDetachedChildren(Node* firstChild)
+{
+for (RefPtr child = firstChild; child; child = child->nextSibling());
+}

>From 6eded7d9da598f15647cb25fc6ac023b27d16132 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Fri, 9 Feb 2024 15:17:23 -0800
Subject: [PATCH 2/2] Use mock-types.h instead of embedding RefPtr definiton in
 the test.

---
 .../Checkers/WebKit/assignment-to-refptr.cpp  | 56 +--
 .../Analysis/Checkers/WebKit/mock-types.h |  1 +
 2 files changed, 2 insertions(+), 55 deletions(-)

diff --git a/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp 
b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp
index c8ad634a5493f5..8b2b4671ed96b1 100644
--- a/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp
@@ -1,61 +1,7 @@
 // RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
 // expected-no-diagnostics
 
-template
-class RefPtr {
-public:
-inline constexpr RefPtr() : m_ptr(nullptr) { }
-inline RefPtr(T* ptr)
-  : m_ptr(ptr)
-{
-if (m_ptr)
-m_ptr->ref();
-}
-
-inline RefPtr(const RefPtr& o)
-: m_ptr(o.m_ptr)
-{
-if (m_ptr)
-m_ptr->ref();
-}
-
-inline ~RefPtr()
-{
-if (m_ptr)
-m_ptr->deref();
-}
-
-inline T* operator->() const { return m_ptr; }
-explicit operator bool() const { return m_ptr; }
-  
-RefPtr& operator=(const RefPtr&);
-RefPtr& operator=(T*);
-
-private:
-T* m_ptr;
-};
-
-template
-inline RefPtr& RefPtr::operator=(const RefPtr& o)
-{
-if (m_ptr)
-m_ptr->deref();
-m_ptr = o.m_ptr;
-if (m_ptr)
-m_ptr->ref();
-return *this;
-}
-
-template
-inline RefPtr& RefPtr::operator=(T* optr)
-{
-if (m_ptr)
-m_ptr->deref();
-m_ptr = optr;
-  

[clang] Fix an implicit cast to a base ref counted class generates a false positive. (PR #80934)

2024-02-09 Thread Ryosuke Niwa via cfe-commits

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

>From 9154815c48578df9ee384a9707dd79ee64259cea Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 6 Feb 2024 20:10:33 -0800
Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Fix an implicit
 cast to a base ref counted class generates a false positive.

The bug was caused by isRefCountable erroneously returning false for a class
with both ref() and deref() functions defined because we were not resetting
the base paths results between looking for "ref()" and "deref()"
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp |  1 +
 ...to-base-class-with-deref-in-superclass.cpp | 51 +++
 2 files changed, 52 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..0fd8afedc0b0f5 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -84,6 +84,7 @@ std::optional isRefCountable(const CXXRecordDecl* R)
   if (AnyInconclusiveBase)
 return std::nullopt;
 
+  Paths.clear();
   const auto hasPublicDerefInBase =
   [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
 auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
diff --git 
a/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
 
b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
new file mode 100644
index 00..49826c98a4610d
--- /dev/null
+++ 
b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+template
+class Ref {
+public:
+~Ref()
+{
+if (auto* ptr = m_ptr)
+ptr->deref();
+m_ptr = nullptr;
+}
+
+Ref(T& object)
+: m_ptr(&object)
+{
+object.ref();
+}
+
+operator T&() const { return *m_ptr; }
+bool operator!() const { return !*m_ptr; }
+
+private:
+T* m_ptr;
+};
+
+class Base {
+public:
+virtual ~Base();
+void ref() const;
+void deref() const;
+};
+
+class Event : public Base {
+protected:
+explicit Event();
+};
+
+class SubEvent : public Event {
+public:
+static Ref create();
+private:
+SubEvent() = default;
+};
+
+void someFunction(Base&);
+
+static void test()
+{
+someFunction(SubEvent::create());
+}

>From c4e5d9c747b021201c5cdb7d3073f6ec358418e5 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Fri, 9 Feb 2024 15:27:18 -0800
Subject: [PATCH 2/2] Use mock-types.h instead of embedding the defintion of
 Ref in the test.

---
 ...to-base-class-with-deref-in-superclass.cpp | 23 +--
 1 file changed, 1 insertion(+), 22 deletions(-)

diff --git 
a/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
 
b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
index 49826c98a4610d..176238f31bd2e4 100644
--- 
a/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
+++ 
b/clang/test/Analysis/Checkers/WebKit/implicit-cast-to-base-class-with-deref-in-superclass.cpp
@@ -1,28 +1,7 @@
 // RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
 // expected-no-diagnostics
 
-template
-class Ref {
-public:
-~Ref()
-{
-if (auto* ptr = m_ptr)
-ptr->deref();
-m_ptr = nullptr;
-}
-
-Ref(T& object)
-: m_ptr(&object)
-{
-object.ref();
-}
-
-operator T&() const { return *m_ptr; }
-bool operator!() const { return !*m_ptr; }
-
-private:
-T* m_ptr;
-};
+#include "mock-types.h"
 
 class Base {
 public:

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Allow default arguments to be evaluated like other arguments. (PR #80956)

2024-02-09 Thread Ryosuke Niwa via cfe-commits

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

>From bc390afd3a2e9de6e2a883205ce3862e45d26e06 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 7 Feb 2024 00:57:00 -0800
Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Allow default
 arguments to be evaluated like other arguments.

This PR aligns the evaluation of default arguments with other kinds of arguments
by extracting the expressions within them as argument values to be evaluated.
---
 .../WebKit/UncountedCallArgsChecker.cpp   |  3 ++
 .../ref-countable-default-arg-nullptr.cpp | 45 +++
 2 files changed, 48 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..7cb0e4680d9e9e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -91,6 +91,9 @@ class UncountedCallArgsChecker
 
 const auto *Arg = CE->getArg(ArgIdx);
 
+if (auto *defaultArg = dyn_cast(Arg))
+  Arg = defaultArg->getExpr();
+
 std::pair ArgOrigin =
 tryToFindPtrOrigin(Arg, true);
 
diff --git 
a/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp 
b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp
new file mode 100644
index 00..cd38b335dcf85e
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp
@@ -0,0 +1,45 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+
+template 
+class RefPtr {
+public:
+  RefPtr(T* ptr)
+: m_ptr(ptr)
+  {
+if (m_ptr)
+  m_ptr->ref();
+  }
+
+  ~RefPtr()
+  {
+if (m_ptr)
+  m_ptr->deref();
+  }
+
+  T* get() { return m_ptr; }
+
+private:
+  T* m_ptr;
+};
+
+class Obj {
+public:
+  static Obj* get();
+  static RefPtr create();
+  void ref() const;
+  void deref() const;
+};
+
+void someFunction(Obj*, Obj* = nullptr);
+void otherFunction(Obj*, Obj* = Obj::get());
+// expected-warning@-1{{Call argument is uncounted and unsafe 
[alpha.webkit.UncountedCallArgsChecker]}}
+void anotherFunction(Obj*, Obj* = Obj::create().get());
+
+void otherFunction() {
+  someFunction(nullptr);
+  someFunction(Obj::get());
+  // expected-warning@-1{{Call argument is uncounted and unsafe 
[alpha.webkit.UncountedCallArgsChecker]}}
+  someFunction(Obj::create().get());
+  otherFunction(nullptr);
+  anotherFunction(nullptr);
+}

>From 4ca232820a5789155cdef2aa9768d715c3a874b8 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Fri, 9 Feb 2024 15:55:50 -0800
Subject: [PATCH 2/2] Use "mock-types.h" instead of embedding RefPtr in the
 test.

---
 .../ref-countable-default-arg-nullptr.cpp | 22 +--
 1 file changed, 1 insertion(+), 21 deletions(-)

diff --git 
a/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp 
b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp
index cd38b335dcf85e..a1860a5434c864 100644
--- a/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/ref-countable-default-arg-nullptr.cpp
@@ -1,26 +1,6 @@
 // RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
 
-template 
-class RefPtr {
-public:
-  RefPtr(T* ptr)
-: m_ptr(ptr)
-  {
-if (m_ptr)
-  m_ptr->ref();
-  }
-
-  ~RefPtr()
-  {
-if (m_ptr)
-  m_ptr->deref();
-  }
-
-  T* get() { return m_ptr; }
-
-private:
-  T* m_ptr;
-};
+#include "mock-types.h"
 
 class Obj {
 public:

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)

2024-02-11 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81400

This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety 
of the object argument in a member function call. It also removes the exemption 
of local variables from this checker so that each local variable's safety is 
checked if it's used in a function call instead of relying on the local 
variable checker to find those since local variable checker currently has 
exemption for "for" and "if" statements.

>From b7121ce4f2ef69b4a410f2399fbd9d9525156b93 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Sun, 11 Feb 2024 00:07:30 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Check the safety of
 the object argument in a member function call.

This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety 
of the object argument in
a member function call. It also removes the exemption of local variables from 
this checker so that each
local variable's safety is checked if it's used in a function call instead of 
relying on the local variable
checker to find those since local variable checker currently has exemption for 
"for" and "if" statements.
---
 .../Checkers/WebKit/ASTUtils.cpp  |  2 +-
 .../WebKit/UncountedCallArgsChecker.cpp   | 68 +--
 .../WebKit/call-args-inside-if-statement.cpp  | 20 ++
 3 files changed, 69 insertions(+), 21 deletions(-)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 4526fac64735bf..da0d52e361c946 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -85,7 +85,7 @@ bool isASafeCallArg(const Expr *E) {
   assert(E);
   if (auto *Ref = dyn_cast(E)) {
 if (auto *D = dyn_cast_or_null(Ref->getFoundDecl())) {
-  if (isa(D) || D->isLocalVarDecl())
+  if (isa(D))
 return true;
 }
   }
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..fa6aeb4741d0b7 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -70,6 +70,14 @@ class UncountedCallArgsChecker
   // or std::function call operator).
   unsigned ArgIdx = isa(CE) && 
isa_and_nonnull(F);
 
+  if (auto* MemberCallExpr = dyn_cast(CE)) {
+auto *E = MemberCallExpr->getImplicitObjectArgument();
+auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull();
+std::optional IsUncounted = 
isUncounted(ArgType->getAsCXXRecordDecl());
+if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E))
+  reportBugOnThis(E);
+  }
+
   for (auto P = F->param_begin();
// FIXME: Also check variadic function parameters.
// FIXME: Also check default function arguments. Probably a 
different
@@ -90,32 +98,36 @@ class UncountedCallArgsChecker
   continue;
 
 const auto *Arg = CE->getArg(ArgIdx);
-
-std::pair ArgOrigin =
-tryToFindPtrOrigin(Arg, true);
-
-// Temporary ref-counted object created as part of the call argument
-// would outlive the call.
-if (ArgOrigin.second)
-  continue;
-
-if (isa(ArgOrigin.first)) {
-  // foo(nullptr)
-  continue;
-}
-if (isa(ArgOrigin.first)) {
-  // FIXME: Check the value.
-  // foo(NULL)
-  continue;
-}
-
-if (isASafeCallArg(ArgOrigin.first))
+
+if (isPtrOriginSafe(Arg))
   continue;
 
 reportBug(Arg, *P);
   }
 }
   }
+  
+  bool isPtrOriginSafe(const Expr *Arg) const {
+std::pair ArgOrigin =
+tryToFindPtrOrigin(Arg, true);
+
+// Temporary ref-counted object created as part of the call argument
+// would outlive the call.
+if (ArgOrigin.second)
+  return true;
+
+if (isa(ArgOrigin.first)) {
+  // foo(nullptr)
+  return true;
+}
+if (isa(ArgOrigin.first)) {
+  // FIXME: Check the value.
+  // foo(NULL)
+  return true;
+}
+
+return isASafeCallArg(ArgOrigin.first);
+  }
 
   bool shouldSkipCall(const CallExpr *CE) const {
 if (CE->getNumArgs() == 0)
@@ -183,6 +195,22 @@ class UncountedCallArgsChecker
 Report->addRange(CallArg->getSourceRange());
 BR->emitReport(std::move(Report));
   }
+
+  void reportBugOnThis(const Expr *CallArg) const {
+assert(CallArg);
+
+SmallString<100> Buf;
+llvm::raw_svector_ostream Os(Buf);
+
+Os << "Call argument for 'this' parameter is uncounted and unsafe.";
+
+const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
+
+PathDiagnosticLocation 

[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)

2024-02-11 Thread Ryosuke Niwa via cfe-commits

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

>From 16d5c240639cdf25b824f5a1a60c256d33fe3565 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Sun, 11 Feb 2024 00:07:30 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Check the safety of
 the object argument in a member function call.

This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety 
of the object argument in
a member function call. It also removes the exemption of local variables from 
this checker so that each
local variable's safety is checked if it's used in a function call instead of 
relying on the local variable
checker to find those since local variable checker currently has exemption for 
"for" and "if" statements.
---
 .../Checkers/WebKit/ASTUtils.cpp  |  2 +-
 .../WebKit/UncountedCallArgsChecker.cpp   | 66 +--
 .../WebKit/call-args-inside-if-statement.cpp  | 20 ++
 3 files changed, 68 insertions(+), 20 deletions(-)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 4526fac64735bf..da0d52e361c946 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -85,7 +85,7 @@ bool isASafeCallArg(const Expr *E) {
   assert(E);
   if (auto *Ref = dyn_cast(E)) {
 if (auto *D = dyn_cast_or_null(Ref->getFoundDecl())) {
-  if (isa(D) || D->isLocalVarDecl())
+  if (isa(D))
 return true;
 }
   }
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..170ff7f531f9e9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -70,6 +70,14 @@ class UncountedCallArgsChecker
   // or std::function call operator).
   unsigned ArgIdx = isa(CE) && 
isa_and_nonnull(F);
 
+  if (auto *MemberCallExpr = dyn_cast(CE)) {
+auto *E = MemberCallExpr->getImplicitObjectArgument();
+auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull();
+std::optional IsUncounted = 
isUncounted(ArgType->getAsCXXRecordDecl());
+if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E))
+  reportBugOnThis(E);
+  }
+
   for (auto P = F->param_begin();
// FIXME: Also check variadic function parameters.
// FIXME: Also check default function arguments. Probably a 
different
@@ -91,25 +99,7 @@ class UncountedCallArgsChecker
 
 const auto *Arg = CE->getArg(ArgIdx);
 
-std::pair ArgOrigin =
-tryToFindPtrOrigin(Arg, true);
-
-// Temporary ref-counted object created as part of the call argument
-// would outlive the call.
-if (ArgOrigin.second)
-  continue;
-
-if (isa(ArgOrigin.first)) {
-  // foo(nullptr)
-  continue;
-}
-if (isa(ArgOrigin.first)) {
-  // FIXME: Check the value.
-  // foo(NULL)
-  continue;
-}
-
-if (isASafeCallArg(ArgOrigin.first))
+if (isPtrOriginSafe(Arg))
   continue;
 
 reportBug(Arg, *P);
@@ -117,6 +107,28 @@ class UncountedCallArgsChecker
 }
   }
 
+  bool isPtrOriginSafe(const Expr *Arg) const {
+std::pair ArgOrigin =
+tryToFindPtrOrigin(Arg, true);
+
+// Temporary ref-counted object created as part of the call argument
+// would outlive the call.
+if (ArgOrigin.second)
+  return true;
+
+if (isa(ArgOrigin.first)) {
+  // foo(nullptr)
+  return true;
+}
+if (isa(ArgOrigin.first)) {
+  // FIXME: Check the value.
+  // foo(NULL)
+  return true;
+}
+
+return isASafeCallArg(ArgOrigin.first);
+  }
+
   bool shouldSkipCall(const CallExpr *CE) const {
 if (CE->getNumArgs() == 0)
   return false;
@@ -183,6 +195,22 @@ class UncountedCallArgsChecker
 Report->addRange(CallArg->getSourceRange());
 BR->emitReport(std::move(Report));
   }
+
+  void reportBugOnThis(const Expr *CallArg) const {
+assert(CallArg);
+
+SmallString<100> Buf;
+llvm::raw_svector_ostream Os(Buf);
+
+Os << "Call argument for 'this' parameter is uncounted and unsafe.";
+
+const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
+
+PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+auto Report = std::make_unique(Bug, Os.str(), BSLoc);
+Report->addRange(CallArg->getSourceRange());
+BR->emitReport(std::move(Report));
+  }
 };
 } // namespace
 
diff --git 
a/clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp
new file mode 10064

[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)

2024-02-11 Thread Ryosuke Niwa via cfe-commits

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

>From c8ac9ad6ecd95a3dbd023458a572b08a4664de03 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Sun, 11 Feb 2024 00:07:30 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Check the safety of
 the object argument in a member function call.

This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety 
of the object argument in
a member function call. It also removes the exemption of local variables from 
this checker so that each
local variable's safety is checked if it's used in a function call instead of 
relying on the local variable
checker to find those since local variable checker currently has exemption for 
"for" and "if" statements.
---
 .../Checkers/WebKit/ASTUtils.cpp  |  2 +-
 .../WebKit/UncountedCallArgsChecker.cpp   | 67 +--
 .../WebKit/call-args-inside-if-statement.cpp  | 20 ++
 3 files changed, 69 insertions(+), 20 deletions(-)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 4526fac64735bf..da0d52e361c946 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -85,7 +85,7 @@ bool isASafeCallArg(const Expr *E) {
   assert(E);
   if (auto *Ref = dyn_cast(E)) {
 if (auto *D = dyn_cast_or_null(Ref->getFoundDecl())) {
-  if (isa(D) || D->isLocalVarDecl())
+  if (isa(D))
 return true;
 }
   }
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..cda96b70ea8735 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -70,6 +70,15 @@ class UncountedCallArgsChecker
   // or std::function call operator).
   unsigned ArgIdx = isa(CE) && 
isa_and_nonnull(F);
 
+  if (auto *MemberCallExpr = dyn_cast(CE)) {
+auto *E = MemberCallExpr->getImplicitObjectArgument();
+auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull();
+std::optional IsUncounted =
+isUncounted(ArgType->getAsCXXRecordDecl());
+if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E))
+  reportBugOnThis(E);
+  }
+
   for (auto P = F->param_begin();
// FIXME: Also check variadic function parameters.
// FIXME: Also check default function arguments. Probably a 
different
@@ -91,25 +100,7 @@ class UncountedCallArgsChecker
 
 const auto *Arg = CE->getArg(ArgIdx);
 
-std::pair ArgOrigin =
-tryToFindPtrOrigin(Arg, true);
-
-// Temporary ref-counted object created as part of the call argument
-// would outlive the call.
-if (ArgOrigin.second)
-  continue;
-
-if (isa(ArgOrigin.first)) {
-  // foo(nullptr)
-  continue;
-}
-if (isa(ArgOrigin.first)) {
-  // FIXME: Check the value.
-  // foo(NULL)
-  continue;
-}
-
-if (isASafeCallArg(ArgOrigin.first))
+if (isPtrOriginSafe(Arg))
   continue;
 
 reportBug(Arg, *P);
@@ -117,6 +108,28 @@ class UncountedCallArgsChecker
 }
   }
 
+  bool isPtrOriginSafe(const Expr *Arg) const {
+std::pair ArgOrigin =
+tryToFindPtrOrigin(Arg, true);
+
+// Temporary ref-counted object created as part of the call argument
+// would outlive the call.
+if (ArgOrigin.second)
+  return true;
+
+if (isa(ArgOrigin.first)) {
+  // foo(nullptr)
+  return true;
+}
+if (isa(ArgOrigin.first)) {
+  // FIXME: Check the value.
+  // foo(NULL)
+  return true;
+}
+
+return isASafeCallArg(ArgOrigin.first);
+  }
+
   bool shouldSkipCall(const CallExpr *CE) const {
 if (CE->getNumArgs() == 0)
   return false;
@@ -183,6 +196,22 @@ class UncountedCallArgsChecker
 Report->addRange(CallArg->getSourceRange());
 BR->emitReport(std::move(Report));
   }
+
+  void reportBugOnThis(const Expr *CallArg) const {
+assert(CallArg);
+
+SmallString<100> Buf;
+llvm::raw_svector_ostream Os(Buf);
+
+Os << "Call argument for 'this' parameter is uncounted and unsafe.";
+
+const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
+
+PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+auto Report = std::make_unique(Bug, Os.str(), BSLoc);
+Report->addRange(CallArg->getSourceRange());
+BR->emitReport(std::move(Report));
+  }
 };
 } // namespace
 
diff --git 
a/clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-inside-if-statement.cpp
new fi

[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)

2024-02-12 Thread Ryosuke Niwa via cfe-commits

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

>From 04e18254efc4f671e0bbd9625c7e994fe47c1636 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Sun, 11 Feb 2024 00:07:30 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Check the safety of
 the object argument in a member function call.

This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety 
of the object argument in
a member function call.
---
 .../WebKit/UncountedCallArgsChecker.cpp   | 67 +--
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 18 +
 2 files changed, 66 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..cda96b70ea8735 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -70,6 +70,15 @@ class UncountedCallArgsChecker
   // or std::function call operator).
   unsigned ArgIdx = isa(CE) && 
isa_and_nonnull(F);
 
+  if (auto *MemberCallExpr = dyn_cast(CE)) {
+auto *E = MemberCallExpr->getImplicitObjectArgument();
+auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull();
+std::optional IsUncounted =
+isUncounted(ArgType->getAsCXXRecordDecl());
+if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E))
+  reportBugOnThis(E);
+  }
+
   for (auto P = F->param_begin();
// FIXME: Also check variadic function parameters.
// FIXME: Also check default function arguments. Probably a 
different
@@ -91,25 +100,7 @@ class UncountedCallArgsChecker
 
 const auto *Arg = CE->getArg(ArgIdx);
 
-std::pair ArgOrigin =
-tryToFindPtrOrigin(Arg, true);
-
-// Temporary ref-counted object created as part of the call argument
-// would outlive the call.
-if (ArgOrigin.second)
-  continue;
-
-if (isa(ArgOrigin.first)) {
-  // foo(nullptr)
-  continue;
-}
-if (isa(ArgOrigin.first)) {
-  // FIXME: Check the value.
-  // foo(NULL)
-  continue;
-}
-
-if (isASafeCallArg(ArgOrigin.first))
+if (isPtrOriginSafe(Arg))
   continue;
 
 reportBug(Arg, *P);
@@ -117,6 +108,28 @@ class UncountedCallArgsChecker
 }
   }
 
+  bool isPtrOriginSafe(const Expr *Arg) const {
+std::pair ArgOrigin =
+tryToFindPtrOrigin(Arg, true);
+
+// Temporary ref-counted object created as part of the call argument
+// would outlive the call.
+if (ArgOrigin.second)
+  return true;
+
+if (isa(ArgOrigin.first)) {
+  // foo(nullptr)
+  return true;
+}
+if (isa(ArgOrigin.first)) {
+  // FIXME: Check the value.
+  // foo(NULL)
+  return true;
+}
+
+return isASafeCallArg(ArgOrigin.first);
+  }
+
   bool shouldSkipCall(const CallExpr *CE) const {
 if (CE->getNumArgs() == 0)
   return false;
@@ -183,6 +196,22 @@ class UncountedCallArgsChecker
 Report->addRange(CallArg->getSourceRange());
 BR->emitReport(std::move(Report));
   }
+
+  void reportBugOnThis(const Expr *CallArg) const {
+assert(CallArg);
+
+SmallString<100> Buf;
+llvm::raw_svector_ostream Os(Buf);
+
+Os << "Call argument for 'this' parameter is uncounted and unsafe.";
+
+const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
+
+PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+auto Report = std::make_unique(Bug, Os.str(), BSLoc);
+Report->addRange(CallArg->getSourceRange());
+BR->emitReport(std::move(Report));
+  }
 };
 } // namespace
 
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
new file mode 100644
index 00..e5e39e3faac714
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+
+#include "mock-types.h"
+
+class RefCounted {
+public:
+  void ref() const;
+  void deref() const;
+  void someFunction();
+};
+
+RefCounted* refCountedObj();
+
+void test()
+{
+  refCountedObj()->someFunction();
+  // expected-warning@-1{{Call argument for 'this' parameter is uncounted and 
unsafe}}
+}

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. (PR #81527)

2024-02-12 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81527

Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call.

>From c67d64d7ca5885b03b6e0738d5e237ccbd3ed38a Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 12 Feb 2024 12:31:37 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe
 functions to call.

Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp |  5 +++--
 .../WebKit/UncountedCallArgsChecker.cpp   | 12 ++-
 ...ncast.cpp => call-args-safe-functions.cpp} | 21 +++
 3 files changed, 31 insertions(+), 7 deletions(-)
 rename clang/test/Analysis/Checkers/WebKit/{call-args-dynamic-downcast.cpp => 
call-args-safe-functions.cpp} (55%)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..e2c19ef43be132 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -192,8 +192,9 @@ bool isPtrConversion(const FunctionDecl *F) {
   // FIXME: check # of params == 1
   const auto FunctionName = safeGetName(F);
   if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
-  FunctionName == "dynamicDowncast"
-  || FunctionName == "downcast" || FunctionName == "bitwise_cast")
+  FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
+  FunctionName == "checkedDowncast" || FunctionName == "uncheckedDowncast" 
|| 
+  FunctionName == "bitwise_cast")
 return true;
 
   return false;
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..5d5fce2867f99b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -148,13 +148,15 @@ class UncountedCallArgsChecker
 
 auto name = safeGetName(Callee);
 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
-name == "dynamicDowncast" || name == "downcast" || name == 
"bitwise_cast" ||
-name == "is" || name == "equal" || name == "hash" ||
-name == "isType"
+name == "dynamicDowncast" || name == "downcast" ||
+name == "checkedDowncast" || name == "uncheckedDowncast" ||
+name == "bitwise_cast" || name == "is" || name == "equal" ||
+name == "hash" || name == "isType" ||
 // FIXME: Most/all of these should be implemented via attributes.
-|| name == "equalIgnoringASCIICase" ||
+name == "equalIgnoringASCIICase" ||
 name == "equalIgnoringASCIICaseCommon" ||
-name == "equalIgnoringNullity")
+name == "equalIgnoringNullity" ||
+name == "toString")
   return true;
 
 return false;
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
similarity index 55%
rename from clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp
rename to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
index 28156623d9a0fd..a87446564870cd 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
@@ -23,13 +23,34 @@ class OtherObject {
 Derived* obj();
 };
 
+class String {
+};
+
 template
 inline Target* dynamicDowncast(Source* source)
 {
 return static_cast(source);
 }
 
+template
+inline Target* checkedDowncast(Source* source)
+{
+return static_cast(source);
+}
+
+template
+inline Target* uncheckedDowncast(Source* source)
+{
+return static_cast(source);
+}
+
+template
+String toString(const Types&... values);
+
 void foo(OtherObject* other)
 {
 dynamicDowncast(other->obj());
+checkedDowncast(other->obj());
+uncheckedDowncast(other->obj());
+toString(other->obj());
 }

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. (PR #81527)

2024-02-12 Thread Ryosuke Niwa via cfe-commits

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

>From 7596e70e7ed63d881d4754f3f18b9e01f38f176f Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 12 Feb 2024 12:31:37 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe
 functions to call.

Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp |  5 +++--
 .../WebKit/UncountedCallArgsChecker.cpp   | 11 +-
 ...ncast.cpp => call-args-safe-functions.cpp} | 21 +++
 3 files changed, 30 insertions(+), 7 deletions(-)
 rename clang/test/Analysis/Checkers/WebKit/{call-args-dynamic-downcast.cpp => 
call-args-safe-functions.cpp} (55%)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..16fc6a68a0800a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -192,8 +192,9 @@ bool isPtrConversion(const FunctionDecl *F) {
   // FIXME: check # of params == 1
   const auto FunctionName = safeGetName(F);
   if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
-  FunctionName == "dynamicDowncast"
-  || FunctionName == "downcast" || FunctionName == "bitwise_cast")
+  FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
+  FunctionName == "checkedDowncast" ||
+  FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast"s
 return true;
 
   return false;
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..cc4585a0b0eeff 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -148,13 +148,14 @@ class UncountedCallArgsChecker
 
 auto name = safeGetName(Callee);
 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
-name == "dynamicDowncast" || name == "downcast" || name == 
"bitwise_cast" ||
-name == "is" || name == "equal" || name == "hash" ||
-name == "isType"
+name == "dynamicDowncast" || name == "downcast" ||
+name == "checkedDowncast" || name == "uncheckedDowncast" ||
+name == "bitwise_cast" || name == "is" || name == "equal" ||
+name == "hash" || name == "isType" ||
 // FIXME: Most/all of these should be implemented via attributes.
-|| name == "equalIgnoringASCIICase" ||
+name == "equalIgnoringASCIICase" ||
 name == "equalIgnoringASCIICaseCommon" ||
-name == "equalIgnoringNullity")
+name == "equalIgnoringNullity" || name == "toString")
   return true;
 
 return false;
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
similarity index 55%
rename from clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp
rename to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
index 28156623d9a0fd..a87446564870cd 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
@@ -23,13 +23,34 @@ class OtherObject {
 Derived* obj();
 };
 
+class String {
+};
+
 template
 inline Target* dynamicDowncast(Source* source)
 {
 return static_cast(source);
 }
 
+template
+inline Target* checkedDowncast(Source* source)
+{
+return static_cast(source);
+}
+
+template
+inline Target* uncheckedDowncast(Source* source)
+{
+return static_cast(source);
+}
+
+template
+String toString(const Types&... values);
+
 void foo(OtherObject* other)
 {
 dynamicDowncast(other->obj());
+checkedDowncast(other->obj());
+uncheckedDowncast(other->obj());
+toString(other->obj());
 }

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. (PR #81527)

2024-02-12 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa closed https://github.com/llvm/llvm-project/pull/81527
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. (PR #81532)

2024-02-12 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81532

Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call.

>From 7596e70e7ed63d881d4754f3f18b9e01f38f176f Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 12 Feb 2024 12:31:37 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe
 functions to call.

Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp |  5 +++--
 .../WebKit/UncountedCallArgsChecker.cpp   | 11 +-
 ...ncast.cpp => call-args-safe-functions.cpp} | 21 +++
 3 files changed, 30 insertions(+), 7 deletions(-)
 rename clang/test/Analysis/Checkers/WebKit/{call-args-dynamic-downcast.cpp => 
call-args-safe-functions.cpp} (55%)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..16fc6a68a0800a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -192,8 +192,9 @@ bool isPtrConversion(const FunctionDecl *F) {
   // FIXME: check # of params == 1
   const auto FunctionName = safeGetName(F);
   if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
-  FunctionName == "dynamicDowncast"
-  || FunctionName == "downcast" || FunctionName == "bitwise_cast")
+  FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
+  FunctionName == "checkedDowncast" ||
+  FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast"s
 return true;
 
   return false;
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..cc4585a0b0eeff 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -148,13 +148,14 @@ class UncountedCallArgsChecker
 
 auto name = safeGetName(Callee);
 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
-name == "dynamicDowncast" || name == "downcast" || name == 
"bitwise_cast" ||
-name == "is" || name == "equal" || name == "hash" ||
-name == "isType"
+name == "dynamicDowncast" || name == "downcast" ||
+name == "checkedDowncast" || name == "uncheckedDowncast" ||
+name == "bitwise_cast" || name == "is" || name == "equal" ||
+name == "hash" || name == "isType" ||
 // FIXME: Most/all of these should be implemented via attributes.
-|| name == "equalIgnoringASCIICase" ||
+name == "equalIgnoringASCIICase" ||
 name == "equalIgnoringASCIICaseCommon" ||
-name == "equalIgnoringNullity")
+name == "equalIgnoringNullity" || name == "toString")
   return true;
 
 return false;
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
similarity index 55%
rename from clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp
rename to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
index 28156623d9a0fd..a87446564870cd 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
@@ -23,13 +23,34 @@ class OtherObject {
 Derived* obj();
 };
 
+class String {
+};
+
 template
 inline Target* dynamicDowncast(Source* source)
 {
 return static_cast(source);
 }
 
+template
+inline Target* checkedDowncast(Source* source)
+{
+return static_cast(source);
+}
+
+template
+inline Target* uncheckedDowncast(Source* source)
+{
+return static_cast(source);
+}
+
+template
+String toString(const Types&... values);
+
 void foo(OtherObject* other)
 {
 dynamicDowncast(other->obj());
+checkedDowncast(other->obj());
+uncheckedDowncast(other->obj());
+toString(other->obj());
 }

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. (PR #81532)

2024-02-12 Thread Ryosuke Niwa via cfe-commits

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

>From 9a25673cf0c585b1d13e54b1b836edaae1a1ca2c Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 12 Feb 2024 12:31:37 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe
 functions to call.

Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp |  5 +++--
 .../WebKit/UncountedCallArgsChecker.cpp   | 11 +-
 ...ncast.cpp => call-args-safe-functions.cpp} | 21 +++
 3 files changed, 30 insertions(+), 7 deletions(-)
 rename clang/test/Analysis/Checkers/WebKit/{call-args-dynamic-downcast.cpp => 
call-args-safe-functions.cpp} (55%)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..d9f4e1a598e788 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -192,8 +192,9 @@ bool isPtrConversion(const FunctionDecl *F) {
   // FIXME: check # of params == 1
   const auto FunctionName = safeGetName(F);
   if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
-  FunctionName == "dynamicDowncast"
-  || FunctionName == "downcast" || FunctionName == "bitwise_cast")
+  FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
+  FunctionName == "checkedDowncast" ||
+  FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast"
 return true;
 
   return false;
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..cc4585a0b0eeff 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -148,13 +148,14 @@ class UncountedCallArgsChecker
 
 auto name = safeGetName(Callee);
 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
-name == "dynamicDowncast" || name == "downcast" || name == 
"bitwise_cast" ||
-name == "is" || name == "equal" || name == "hash" ||
-name == "isType"
+name == "dynamicDowncast" || name == "downcast" ||
+name == "checkedDowncast" || name == "uncheckedDowncast" ||
+name == "bitwise_cast" || name == "is" || name == "equal" ||
+name == "hash" || name == "isType" ||
 // FIXME: Most/all of these should be implemented via attributes.
-|| name == "equalIgnoringASCIICase" ||
+name == "equalIgnoringASCIICase" ||
 name == "equalIgnoringASCIICaseCommon" ||
-name == "equalIgnoringNullity")
+name == "equalIgnoringNullity" || name == "toString")
   return true;
 
 return false;
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
similarity index 55%
rename from clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp
rename to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
index 28156623d9a0fd..a87446564870cd 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
@@ -23,13 +23,34 @@ class OtherObject {
 Derived* obj();
 };
 
+class String {
+};
+
 template
 inline Target* dynamicDowncast(Source* source)
 {
 return static_cast(source);
 }
 
+template
+inline Target* checkedDowncast(Source* source)
+{
+return static_cast(source);
+}
+
+template
+inline Target* uncheckedDowncast(Source* source)
+{
+return static_cast(source);
+}
+
+template
+String toString(const Types&... values);
+
 void foo(OtherObject* other)
 {
 dynamicDowncast(other->obj());
+checkedDowncast(other->obj());
+uncheckedDowncast(other->obj());
+toString(other->obj());
 }

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe functions to call. (PR #81532)

2024-02-12 Thread Ryosuke Niwa via cfe-commits

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

>From 52b6e959d69a96704ec2a403131627049a782352 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 12 Feb 2024 12:31:37 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Add a few more safe
 functions to call.

Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp |  5 +++--
 .../WebKit/UncountedCallArgsChecker.cpp   | 11 +-
 ...ncast.cpp => call-args-safe-functions.cpp} | 21 +++
 3 files changed, 30 insertions(+), 7 deletions(-)
 rename clang/test/Analysis/Checkers/WebKit/{call-args-dynamic-downcast.cpp => 
call-args-safe-functions.cpp} (55%)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..c4a78da25438be 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -192,8 +192,9 @@ bool isPtrConversion(const FunctionDecl *F) {
   // FIXME: check # of params == 1
   const auto FunctionName = safeGetName(F);
   if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
-  FunctionName == "dynamicDowncast"
-  || FunctionName == "downcast" || FunctionName == "bitwise_cast")
+  FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
+  FunctionName == "checkedDowncast" ||
+  FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast")
 return true;
 
   return false;
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..cc4585a0b0eeff 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -148,13 +148,14 @@ class UncountedCallArgsChecker
 
 auto name = safeGetName(Callee);
 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
-name == "dynamicDowncast" || name == "downcast" || name == 
"bitwise_cast" ||
-name == "is" || name == "equal" || name == "hash" ||
-name == "isType"
+name == "dynamicDowncast" || name == "downcast" ||
+name == "checkedDowncast" || name == "uncheckedDowncast" ||
+name == "bitwise_cast" || name == "is" || name == "equal" ||
+name == "hash" || name == "isType" ||
 // FIXME: Most/all of these should be implemented via attributes.
-|| name == "equalIgnoringASCIICase" ||
+name == "equalIgnoringASCIICase" ||
 name == "equalIgnoringASCIICaseCommon" ||
-name == "equalIgnoringNullity")
+name == "equalIgnoringNullity" || name == "toString")
   return true;
 
 return false;
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
similarity index 55%
rename from clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp
rename to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
index 28156623d9a0fd..a87446564870cd 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
@@ -23,13 +23,34 @@ class OtherObject {
 Derived* obj();
 };
 
+class String {
+};
+
 template
 inline Target* dynamicDowncast(Source* source)
 {
 return static_cast(source);
 }
 
+template
+inline Target* checkedDowncast(Source* source)
+{
+return static_cast(source);
+}
+
+template
+inline Target* uncheckedDowncast(Source* source)
+{
+return static_cast(source);
+}
+
+template
+String toString(const Types&... values);
+
 void foo(OtherObject* other)
 {
 dynamicDowncast(other->obj());
+checkedDowncast(other->obj());
+uncheckedDowncast(other->obj());
+toString(other->obj());
 }

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Detect a return value of Ref & RefPtr (PR #81580)

2024-02-13 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81580

This PR makes the checker not emit warning when a function is called with a 
return value of another function when the return value is of type Ref or 
RefPtr.

>From 52b6e959d69a96704ec2a403131627049a782352 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 12 Feb 2024 12:31:37 -0800
Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Add a few more
 safe functions to call.

Added checkedDowncast, uncheckedDowncast, & toString as safe functions to call.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp |  5 +++--
 .../WebKit/UncountedCallArgsChecker.cpp   | 11 +-
 ...ncast.cpp => call-args-safe-functions.cpp} | 21 +++
 3 files changed, 30 insertions(+), 7 deletions(-)
 rename clang/test/Analysis/Checkers/WebKit/{call-args-dynamic-downcast.cpp => 
call-args-safe-functions.cpp} (55%)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..c4a78da25438be 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -192,8 +192,9 @@ bool isPtrConversion(const FunctionDecl *F) {
   // FIXME: check # of params == 1
   const auto FunctionName = safeGetName(F);
   if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
-  FunctionName == "dynamicDowncast"
-  || FunctionName == "downcast" || FunctionName == "bitwise_cast")
+  FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
+  FunctionName == "checkedDowncast" ||
+  FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast")
 return true;
 
   return false;
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..cc4585a0b0eeff 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -148,13 +148,14 @@ class UncountedCallArgsChecker
 
 auto name = safeGetName(Callee);
 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
-name == "dynamicDowncast" || name == "downcast" || name == 
"bitwise_cast" ||
-name == "is" || name == "equal" || name == "hash" ||
-name == "isType"
+name == "dynamicDowncast" || name == "downcast" ||
+name == "checkedDowncast" || name == "uncheckedDowncast" ||
+name == "bitwise_cast" || name == "is" || name == "equal" ||
+name == "hash" || name == "isType" ||
 // FIXME: Most/all of these should be implemented via attributes.
-|| name == "equalIgnoringASCIICase" ||
+name == "equalIgnoringASCIICase" ||
 name == "equalIgnoringASCIICaseCommon" ||
-name == "equalIgnoringNullity")
+name == "equalIgnoringNullity" || name == "toString")
   return true;
 
 return false;
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
similarity index 55%
rename from clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp
rename to clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
index 28156623d9a0fd..a87446564870cd 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-dynamic-downcast.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-safe-functions.cpp
@@ -23,13 +23,34 @@ class OtherObject {
 Derived* obj();
 };
 
+class String {
+};
+
 template
 inline Target* dynamicDowncast(Source* source)
 {
 return static_cast(source);
 }
 
+template
+inline Target* checkedDowncast(Source* source)
+{
+return static_cast(source);
+}
+
+template
+inline Target* uncheckedDowncast(Source* source)
+{
+return static_cast(source);
+}
+
+template
+String toString(const Types&... values);
+
 void foo(OtherObject* other)
 {
 dynamicDowncast(other->obj());
+checkedDowncast(other->obj());
+uncheckedDowncast(other->obj());
+toString(other->obj());
 }

>From a781c46e5db132e31816efd7bd475d6af157f256 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 13 Feb 2024 00:35:36 -0800
Subject: [PATCH 2/2] [alpha.webkit.UncountedCallArgsChecker] Detect a return
 value of Ref & RefPtr

This PR makes the checker not emit warning when a function is called with a 
return value
of another function when the return value is of type Ref or RefPtr.
---
 .../Checkers/WebKit/ASTUtils.cpp  |  6 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 
 .../Checkers/WebKit/PtrTypesSemantics.h   |  3 +++
 .../call-args-protected-return-value.cpp  | 23 +++
 .../Analysis/Checkers/WebKit/mock-types.h |  1 +
 5 files changed, 53 insertions(+)
 create mode 100644 
clang/te

[clang] Detect a return value of Ref & RefPtr (PR #81580)

2024-02-13 Thread Ryosuke Niwa via cfe-commits

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

>From 51fb3aa575c4509d4b4199d16d10e05281f911ac Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 13 Feb 2024 00:35:36 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect a return value
 of Ref & RefPtr

This PR makes the checker not emit warning when a function is called with a 
return value
of another function when the return value is of type Ref or RefPtr.
---
 .../Checkers/WebKit/ASTUtils.cpp  |  6 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 
 .../Checkers/WebKit/PtrTypesSemantics.h   |  3 +++
 .../call-args-protected-return-value.cpp  | 23 +++
 .../Analysis/Checkers/WebKit/mock-types.h |  1 +
 5 files changed, 53 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 4526fac64735bf..b76c0551c77bb0 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -19,6 +19,10 @@ namespace clang {
 std::pair
 tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
   while (E) {
+if (auto *tempExpr = dyn_cast(E)) {
+  E = tempExpr->getSubExpr();
+  continue;
+}
 if (auto *cast = dyn_cast(E)) {
   if (StopAtFirstRefCountedObj) {
 if (auto *ConversionFunc =
@@ -62,6 +66,8 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = call->getArg(0);
   continue;
 }
+if (isReturnValueRefCounted(callee))
+  return {E, true};
 
 if (isPtrConversion(callee)) {
   E = call->getArg(0);
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..377c2b9376c1b9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -118,6 +118,26 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
  || FunctionName == "Identifier";
 }
 
+bool isReturnValueRefCounted(const clang::FunctionDecl *F) {
+  assert(F);
+  auto *type = F->getReturnType().getTypePtrOrNull();
+  while (type) {
+if (auto *elaboratedT = dyn_cast(type)) {
+  type = elaboratedT->desugar().getTypePtrOrNull();
+  continue;  
+}
+if (auto *specialT = dyn_cast(type)) {
+  if (auto* decl = specialT->getTemplateName().getAsTemplateDecl()) {
+auto name = decl->getNameAsString();
+return name == "Ref" || name == "RefPtr";
+  }
+  return false;
+}
+return false;
+  }
+  return false;
+}
+
 std::optional isUncounted(const CXXRecordDecl* Class)
 {
   // Keep isRefCounted first as it's cheaper.
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 45b21cc0918443..c2c5b74442ba43 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -50,6 +50,9 @@ std::optional isUncountedPtr(const clang::Type* T);
 /// false if not.
 bool isCtorOfRefCounted(const clang::FunctionDecl *F);
 
+/// \returns true if \p F returns a ref-counted object, false if not.
+bool isReturnValueRefCounted(const clang::FunctionDecl *F);
+
 /// \returns true if \p M is getter of a ref-counted class, false if not.
 std::optional isGetterOfRefCounted(const clang::CXXMethodDecl* Method);
 
diff --git 
a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
new file mode 100644
index 00..1c4b3df211b1e3
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+#include "mock-types.h"
+
+class RefCounted {
+public:
+  void ref();
+  void deref();
+};
+
+class Object {
+public:
+  void someFunction(RefCounted&);
+};
+
+RefPtr object();
+RefPtr protectedTargetObject();
+
+void testFunction() {
+  if (RefPtr obj = object())
+obj->someFunction(*protectedTargetObject());
+}
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index 5f570b8bee8cb8..814e0944145992 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -20,6 +20,7 @@ template  struct RefPtr {
   T *operator->() { return t; }
   T &operator*() { return *t; }
   RefPtr &operator=(T *) { return *this; }
+  operator bool() { return t; }
 };
 
 template  bool operator==(const RefPt

[clang] Detect a return value of Ref & RefPtr (PR #81580)

2024-02-13 Thread Ryosuke Niwa via cfe-commits

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

>From 478c7d2c0bb902b829d522fc483e68b8e36489ea Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 13 Feb 2024 00:35:36 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect a return value
 of `Ref` & `RefPtr`

This PR makes the checker not emit warning when a function is called with a 
return value
of another function when the return value is of type `Ref` or `RefPtr`.
---
 .../Checkers/WebKit/ASTUtils.cpp  |  6 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 
 .../Checkers/WebKit/PtrTypesSemantics.h   |  3 +++
 .../call-args-protected-return-value.cpp  | 23 +++
 .../Analysis/Checkers/WebKit/mock-types.h |  1 +
 5 files changed, 53 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 4526fac64735bf..b76c0551c77bb0 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -19,6 +19,10 @@ namespace clang {
 std::pair
 tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
   while (E) {
+if (auto *tempExpr = dyn_cast(E)) {
+  E = tempExpr->getSubExpr();
+  continue;
+}
 if (auto *cast = dyn_cast(E)) {
   if (StopAtFirstRefCountedObj) {
 if (auto *ConversionFunc =
@@ -62,6 +66,8 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = call->getArg(0);
   continue;
 }
+if (isReturnValueRefCounted(callee))
+  return {E, true};
 
 if (isPtrConversion(callee)) {
   E = call->getArg(0);
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..377c2b9376c1b9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -118,6 +118,26 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
  || FunctionName == "Identifier";
 }
 
+bool isReturnValueRefCounted(const clang::FunctionDecl *F) {
+  assert(F);
+  auto *type = F->getReturnType().getTypePtrOrNull();
+  while (type) {
+if (auto *elaboratedT = dyn_cast(type)) {
+  type = elaboratedT->desugar().getTypePtrOrNull();
+  continue;  
+}
+if (auto *specialT = dyn_cast(type)) {
+  if (auto* decl = specialT->getTemplateName().getAsTemplateDecl()) {
+auto name = decl->getNameAsString();
+return name == "Ref" || name == "RefPtr";
+  }
+  return false;
+}
+return false;
+  }
+  return false;
+}
+
 std::optional isUncounted(const CXXRecordDecl* Class)
 {
   // Keep isRefCounted first as it's cheaper.
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 45b21cc0918443..c2c5b74442ba43 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -50,6 +50,9 @@ std::optional isUncountedPtr(const clang::Type* T);
 /// false if not.
 bool isCtorOfRefCounted(const clang::FunctionDecl *F);
 
+/// \returns true if \p F returns a ref-counted object, false if not.
+bool isReturnValueRefCounted(const clang::FunctionDecl *F);
+
 /// \returns true if \p M is getter of a ref-counted class, false if not.
 std::optional isGetterOfRefCounted(const clang::CXXMethodDecl* Method);
 
diff --git 
a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
new file mode 100644
index 00..1c4b3df211b1e3
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+#include "mock-types.h"
+
+class RefCounted {
+public:
+  void ref();
+  void deref();
+};
+
+class Object {
+public:
+  void someFunction(RefCounted&);
+};
+
+RefPtr object();
+RefPtr protectedTargetObject();
+
+void testFunction() {
+  if (RefPtr obj = object())
+obj->someFunction(*protectedTargetObject());
+}
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index 5f570b8bee8cb8..814e0944145992 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -20,6 +20,7 @@ template  struct RefPtr {
   T *operator->() { return t; }
   T &operator*() { return *t; }
   RefPtr &operator=(T *) { return *this; }
+  operator bool() { return t; }
 };
 
 template  bool operator==(con

[clang] Detect a return value of Ref & RefPtr (PR #81580)

2024-02-13 Thread Ryosuke Niwa via cfe-commits

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

>From 24fc75756094d36e72c69805c9d476d8144ed869 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 13 Feb 2024 00:35:36 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect a return value
 of `Ref` & `RefPtr`

This PR makes the checker not emit warning when a function is called with a 
return value
of another function when the return value is of type `Ref` or `RefPtr`.
---
 .../Checkers/WebKit/ASTUtils.cpp  |  6 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 
 .../Checkers/WebKit/PtrTypesSemantics.h   |  3 +++
 .../call-args-protected-return-value.cpp  | 23 +++
 .../Analysis/Checkers/WebKit/mock-types.h |  1 +
 5 files changed, 53 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 4526fac64735bf..b76c0551c77bb0 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -19,6 +19,10 @@ namespace clang {
 std::pair
 tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
   while (E) {
+if (auto *tempExpr = dyn_cast(E)) {
+  E = tempExpr->getSubExpr();
+  continue;
+}
 if (auto *cast = dyn_cast(E)) {
   if (StopAtFirstRefCountedObj) {
 if (auto *ConversionFunc =
@@ -62,6 +66,8 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = call->getArg(0);
   continue;
 }
+if (isReturnValueRefCounted(callee))
+  return {E, true};
 
 if (isPtrConversion(callee)) {
   E = call->getArg(0);
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..70c94dd0e960ab 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -118,6 +118,26 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
  || FunctionName == "Identifier";
 }
 
+bool isReturnValueRefCounted(const clang::FunctionDecl *F) {
+  assert(F);
+  auto *type = F->getReturnType().getTypePtrOrNull();
+  while (type) {
+if (auto *elaboratedT = dyn_cast(type)) {
+  type = elaboratedT->desugar().getTypePtrOrNull();
+  continue;
+}
+if (auto *specialT = dyn_cast(type)) {
+  if (auto *decl = specialT->getTemplateName().getAsTemplateDecl()) {
+auto name = decl->getNameAsString();
+return name == "Ref" || name == "RefPtr";
+  }
+  return false;
+}
+return false;
+  }
+  return false;
+}
+
 std::optional isUncounted(const CXXRecordDecl* Class)
 {
   // Keep isRefCounted first as it's cheaper.
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 45b21cc0918443..c2c5b74442ba43 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -50,6 +50,9 @@ std::optional isUncountedPtr(const clang::Type* T);
 /// false if not.
 bool isCtorOfRefCounted(const clang::FunctionDecl *F);
 
+/// \returns true if \p F returns a ref-counted object, false if not.
+bool isReturnValueRefCounted(const clang::FunctionDecl *F);
+
 /// \returns true if \p M is getter of a ref-counted class, false if not.
 std::optional isGetterOfRefCounted(const clang::CXXMethodDecl* Method);
 
diff --git 
a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
new file mode 100644
index 00..1c4b3df211b1e3
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+#include "mock-types.h"
+
+class RefCounted {
+public:
+  void ref();
+  void deref();
+};
+
+class Object {
+public:
+  void someFunction(RefCounted&);
+};
+
+RefPtr object();
+RefPtr protectedTargetObject();
+
+void testFunction() {
+  if (RefPtr obj = object())
+obj->someFunction(*protectedTargetObject());
+}
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index 5f570b8bee8cb8..814e0944145992 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -20,6 +20,7 @@ template  struct RefPtr {
   T *operator->() { return t; }
   T &operator*() { return *t; }
   RefPtr &operator=(T *) { return *this; }
+  operator bool() { return t; }
 };
 
 template  bool operator==(const Ref

[clang] Detect a return value of Ref & RefPtr (PR #81580)

2024-02-13 Thread Ryosuke Niwa via cfe-commits

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

>From 24fc75756094d36e72c69805c9d476d8144ed869 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 13 Feb 2024 00:35:36 -0800
Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Detect a return
 value of `Ref` & `RefPtr`

This PR makes the checker not emit warning when a function is called with a 
return value
of another function when the return value is of type `Ref` or `RefPtr`.
---
 .../Checkers/WebKit/ASTUtils.cpp  |  6 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 
 .../Checkers/WebKit/PtrTypesSemantics.h   |  3 +++
 .../call-args-protected-return-value.cpp  | 23 +++
 .../Analysis/Checkers/WebKit/mock-types.h |  1 +
 5 files changed, 53 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 4526fac64735bf..b76c0551c77bb0 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -19,6 +19,10 @@ namespace clang {
 std::pair
 tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
   while (E) {
+if (auto *tempExpr = dyn_cast(E)) {
+  E = tempExpr->getSubExpr();
+  continue;
+}
 if (auto *cast = dyn_cast(E)) {
   if (StopAtFirstRefCountedObj) {
 if (auto *ConversionFunc =
@@ -62,6 +66,8 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = call->getArg(0);
   continue;
 }
+if (isReturnValueRefCounted(callee))
+  return {E, true};
 
 if (isPtrConversion(callee)) {
   E = call->getArg(0);
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d2b66341058000..70c94dd0e960ab 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -118,6 +118,26 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
  || FunctionName == "Identifier";
 }
 
+bool isReturnValueRefCounted(const clang::FunctionDecl *F) {
+  assert(F);
+  auto *type = F->getReturnType().getTypePtrOrNull();
+  while (type) {
+if (auto *elaboratedT = dyn_cast(type)) {
+  type = elaboratedT->desugar().getTypePtrOrNull();
+  continue;
+}
+if (auto *specialT = dyn_cast(type)) {
+  if (auto *decl = specialT->getTemplateName().getAsTemplateDecl()) {
+auto name = decl->getNameAsString();
+return name == "Ref" || name == "RefPtr";
+  }
+  return false;
+}
+return false;
+  }
+  return false;
+}
+
 std::optional isUncounted(const CXXRecordDecl* Class)
 {
   // Keep isRefCounted first as it's cheaper.
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 45b21cc0918443..c2c5b74442ba43 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -50,6 +50,9 @@ std::optional isUncountedPtr(const clang::Type* T);
 /// false if not.
 bool isCtorOfRefCounted(const clang::FunctionDecl *F);
 
+/// \returns true if \p F returns a ref-counted object, false if not.
+bool isReturnValueRefCounted(const clang::FunctionDecl *F);
+
 /// \returns true if \p M is getter of a ref-counted class, false if not.
 std::optional isGetterOfRefCounted(const clang::CXXMethodDecl* Method);
 
diff --git 
a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
new file mode 100644
index 00..1c4b3df211b1e3
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+#include "mock-types.h"
+
+class RefCounted {
+public:
+  void ref();
+  void deref();
+};
+
+class Object {
+public:
+  void someFunction(RefCounted&);
+};
+
+RefPtr object();
+RefPtr protectedTargetObject();
+
+void testFunction() {
+  if (RefPtr obj = object())
+obj->someFunction(*protectedTargetObject());
+}
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index 5f570b8bee8cb8..814e0944145992 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -20,6 +20,7 @@ template  struct RefPtr {
   T *operator->() { return t; }
   T &operator*() { return *t; }
   RefPtr &operator=(T *) { return *this; }
+  operator bool() { return t; }
 };
 
 template  bool operator==(const

[clang] Detect a return value of Ref & RefPtr (PR #81580)

2024-02-13 Thread Ryosuke Niwa via cfe-commits


@@ -118,6 +118,26 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
  || FunctionName == "Identifier";
 }
 
+bool isReturnValueRefCounted(const clang::FunctionDecl *F) {
+  assert(F);
+  auto *type = F->getReturnType().getTypePtrOrNull();
+  while (type) {
+if (auto *elaboratedT = dyn_cast(type)) {
+  type = elaboratedT->desugar().getTypePtrOrNull();
+  continue;
+}
+if (auto *specialT = dyn_cast(type)) {

rniwa wrote:

Sounds good. Fixed.

https://github.com/llvm/llvm-project/pull/81580
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)

2024-02-13 Thread Ryosuke Niwa via cfe-commits


@@ -70,6 +70,15 @@ class UncountedCallArgsChecker
   // or std::function call operator).
   unsigned ArgIdx = isa(CE) && 
isa_and_nonnull(F);
 
+  if (auto *MemberCallExpr = dyn_cast(CE)) {
+auto *E = MemberCallExpr->getImplicitObjectArgument();
+auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull();

rniwa wrote:

Ah, okay. Will do that.

https://github.com/llvm/llvm-project/pull/81400
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)

2024-02-13 Thread Ryosuke Niwa via cfe-commits

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

>From 04e18254efc4f671e0bbd9625c7e994fe47c1636 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Sun, 11 Feb 2024 00:07:30 -0800
Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Check the safety
 of the object argument in a member function call.

This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety 
of the object argument in
a member function call.
---
 .../WebKit/UncountedCallArgsChecker.cpp   | 67 +--
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 18 +
 2 files changed, 66 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 31ccae8b097b89..cda96b70ea8735 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -70,6 +70,15 @@ class UncountedCallArgsChecker
   // or std::function call operator).
   unsigned ArgIdx = isa(CE) && 
isa_and_nonnull(F);
 
+  if (auto *MemberCallExpr = dyn_cast(CE)) {
+auto *E = MemberCallExpr->getImplicitObjectArgument();
+auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull();
+std::optional IsUncounted =
+isUncounted(ArgType->getAsCXXRecordDecl());
+if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E))
+  reportBugOnThis(E);
+  }
+
   for (auto P = F->param_begin();
// FIXME: Also check variadic function parameters.
// FIXME: Also check default function arguments. Probably a 
different
@@ -91,25 +100,7 @@ class UncountedCallArgsChecker
 
 const auto *Arg = CE->getArg(ArgIdx);
 
-std::pair ArgOrigin =
-tryToFindPtrOrigin(Arg, true);
-
-// Temporary ref-counted object created as part of the call argument
-// would outlive the call.
-if (ArgOrigin.second)
-  continue;
-
-if (isa(ArgOrigin.first)) {
-  // foo(nullptr)
-  continue;
-}
-if (isa(ArgOrigin.first)) {
-  // FIXME: Check the value.
-  // foo(NULL)
-  continue;
-}
-
-if (isASafeCallArg(ArgOrigin.first))
+if (isPtrOriginSafe(Arg))
   continue;
 
 reportBug(Arg, *P);
@@ -117,6 +108,28 @@ class UncountedCallArgsChecker
 }
   }
 
+  bool isPtrOriginSafe(const Expr *Arg) const {
+std::pair ArgOrigin =
+tryToFindPtrOrigin(Arg, true);
+
+// Temporary ref-counted object created as part of the call argument
+// would outlive the call.
+if (ArgOrigin.second)
+  return true;
+
+if (isa(ArgOrigin.first)) {
+  // foo(nullptr)
+  return true;
+}
+if (isa(ArgOrigin.first)) {
+  // FIXME: Check the value.
+  // foo(NULL)
+  return true;
+}
+
+return isASafeCallArg(ArgOrigin.first);
+  }
+
   bool shouldSkipCall(const CallExpr *CE) const {
 if (CE->getNumArgs() == 0)
   return false;
@@ -183,6 +196,22 @@ class UncountedCallArgsChecker
 Report->addRange(CallArg->getSourceRange());
 BR->emitReport(std::move(Report));
   }
+
+  void reportBugOnThis(const Expr *CallArg) const {
+assert(CallArg);
+
+SmallString<100> Buf;
+llvm::raw_svector_ostream Os(Buf);
+
+Os << "Call argument for 'this' parameter is uncounted and unsafe.";
+
+const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
+
+PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+auto Report = std::make_unique(Bug, Os.str(), BSLoc);
+Report->addRange(CallArg->getSourceRange());
+BR->emitReport(std::move(Report));
+  }
 };
 } // namespace
 
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
new file mode 100644
index 00..e5e39e3faac714
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+
+#include "mock-types.h"
+
+class RefCounted {
+public:
+  void ref() const;
+  void deref() const;
+  void someFunction();
+};
+
+RefCounted* refCountedObj();
+
+void test()
+{
+  refCountedObj()->someFunction();
+  // expected-warning@-1{{Call argument for 'this' parameter is uncounted and 
unsafe}}
+}

>From 2d780389a46e5c053053c1432d2ec3af2a7653e9 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 13 Feb 2024 19:36:09 -0800
Subject: [PATCH 2/2] Address the review comment. Namely avoid calling
 getTypePtrOrNull and don't use raw_svector_ostream.

---
 .../Checkers/WebKit/UncountedCallArgsChecker.cpp | 9 ++---
 1 file changed, 2 insertions(+), 7 deletions(-)

d

[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)

2024-02-13 Thread Ryosuke Niwa via cfe-commits


@@ -183,6 +196,22 @@ class UncountedCallArgsChecker
 Report->addRange(CallArg->getSourceRange());
 BR->emitReport(std::move(Report));
   }
+
+  void reportBugOnThis(const Expr *CallArg) const {
+assert(CallArg);
+
+SmallString<100> Buf;
+llvm::raw_svector_ostream Os(Buf);
+
+Os << "Call argument for 'this' parameter is uncounted and unsafe.";

rniwa wrote:

Good point. Fixed.

https://github.com/llvm/llvm-project/pull/81400
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)

2024-02-13 Thread Ryosuke Niwa via cfe-commits

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

>From 6f94c583f5201fbd73156969fa410669d6e1be93 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 13 Feb 2024 20:05:18 -0800
Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Check the safety
 of the object argument in a member function call.

This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety 
of the object argument in
a member function call.
---
 .../WebKit/UncountedCallArgsChecker.cpp   | 67 +--
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 18 +
 2 files changed, 66 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index f4e6191cf05a3c..d4bf3e2c2e75db 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -70,6 +70,15 @@ class UncountedCallArgsChecker
   // or std::function call operator).
   unsigned ArgIdx = isa(CE) && 
isa_and_nonnull(F);
 
+  if (auto *MemberCallExpr = dyn_cast(CE)) {
+auto *E = MemberCallExpr->getImplicitObjectArgument();
+auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull();
+std::optional IsUncounted =
+isUncounted(ArgType->getAsCXXRecordDecl());
+if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E))
+  reportBugOnThis(E);
+  }
+
   for (auto P = F->param_begin();
// FIXME: Also check variadic function parameters.
// FIXME: Also check default function arguments. Probably a 
different
@@ -94,25 +103,7 @@ class UncountedCallArgsChecker
 if (auto *defaultArg = dyn_cast(Arg))
   Arg = defaultArg->getExpr();
 
-std::pair ArgOrigin =
-tryToFindPtrOrigin(Arg, true);
-
-// Temporary ref-counted object created as part of the call argument
-// would outlive the call.
-if (ArgOrigin.second)
-  continue;
-
-if (isa(ArgOrigin.first)) {
-  // foo(nullptr)
-  continue;
-}
-if (isa(ArgOrigin.first)) {
-  // FIXME: Check the value.
-  // foo(NULL)
-  continue;
-}
-
-if (isASafeCallArg(ArgOrigin.first))
+if (isPtrOriginSafe(Arg))
   continue;
 
 reportBug(Arg, *P);
@@ -120,6 +111,28 @@ class UncountedCallArgsChecker
 }
   }
 
+  bool isPtrOriginSafe(const Expr *Arg) const {
+std::pair ArgOrigin =
+tryToFindPtrOrigin(Arg, true);
+
+// Temporary ref-counted object created as part of the call argument
+// would outlive the call.
+if (ArgOrigin.second)
+  return true;
+
+if (isa(ArgOrigin.first)) {
+  // foo(nullptr)
+  return true;
+}
+if (isa(ArgOrigin.first)) {
+  // FIXME: Check the value.
+  // foo(NULL)
+  return true;
+}
+
+return isASafeCallArg(ArgOrigin.first);
+  }
+
   bool shouldSkipCall(const CallExpr *CE) const {
 if (CE->getNumArgs() == 0)
   return false;
@@ -196,6 +209,22 @@ class UncountedCallArgsChecker
 Report->addRange(CallArg->getSourceRange());
 BR->emitReport(std::move(Report));
   }
+
+  void reportBugOnThis(const Expr *CallArg) const {
+assert(CallArg);
+
+SmallString<100> Buf;
+llvm::raw_svector_ostream Os(Buf);
+
+Os << "Call argument for 'this' parameter is uncounted and unsafe.";
+
+const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
+
+PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+auto Report = std::make_unique(Bug, Os.str(), BSLoc);
+Report->addRange(CallArg->getSourceRange());
+BR->emitReport(std::move(Report));
+  }
 };
 } // namespace
 
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
new file mode 100644
index 00..e5e39e3faac714
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+
+#include "mock-types.h"
+
+class RefCounted {
+public:
+  void ref() const;
+  void deref() const;
+  void someFunction();
+};
+
+RefCounted* refCountedObj();
+
+void test()
+{
+  refCountedObj()->someFunction();
+  // expected-warning@-1{{Call argument for 'this' parameter is uncounted and 
unsafe}}
+}

>From c15967f8f09198c01aec8b0161cc08484c78f66a Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 13 Feb 2024 19:36:09 -0800
Subject: [PATCH 2/2] Address the review comment. Namely avoid calling
 getTypePtrOrNull and don't use raw_svector_ostream.

---
 .../Checkers/WebKit/UncountedCallArgsChecker.cpp | 9 ++---
 1 file chang

[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)

2024-02-13 Thread Ryosuke Niwa via cfe-commits

rniwa wrote:

Rebased.

https://github.com/llvm/llvm-project/pull/81400
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Check the safety of the object argument in a member function call. (PR #81400)

2024-02-13 Thread Ryosuke Niwa via cfe-commits

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

>From 6f94c583f5201fbd73156969fa410669d6e1be93 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 13 Feb 2024 20:05:18 -0800
Subject: [PATCH 1/3] [alpha.webkit.UncountedCallArgsChecker] Check the safety
 of the object argument in a member function call.

This PR makes alpha.webkit.UncountedCallArgsChecker eplicitly check the safety 
of the object argument in
a member function call.
---
 .../WebKit/UncountedCallArgsChecker.cpp   | 67 +--
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 18 +
 2 files changed, 66 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index f4e6191cf05a3c..d4bf3e2c2e75db 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -70,6 +70,15 @@ class UncountedCallArgsChecker
   // or std::function call operator).
   unsigned ArgIdx = isa(CE) && 
isa_and_nonnull(F);
 
+  if (auto *MemberCallExpr = dyn_cast(CE)) {
+auto *E = MemberCallExpr->getImplicitObjectArgument();
+auto *ArgType = MemberCallExpr->getObjectType().getTypePtrOrNull();
+std::optional IsUncounted =
+isUncounted(ArgType->getAsCXXRecordDecl());
+if (IsUncounted && *IsUncounted && !isPtrOriginSafe(E))
+  reportBugOnThis(E);
+  }
+
   for (auto P = F->param_begin();
// FIXME: Also check variadic function parameters.
// FIXME: Also check default function arguments. Probably a 
different
@@ -94,25 +103,7 @@ class UncountedCallArgsChecker
 if (auto *defaultArg = dyn_cast(Arg))
   Arg = defaultArg->getExpr();
 
-std::pair ArgOrigin =
-tryToFindPtrOrigin(Arg, true);
-
-// Temporary ref-counted object created as part of the call argument
-// would outlive the call.
-if (ArgOrigin.second)
-  continue;
-
-if (isa(ArgOrigin.first)) {
-  // foo(nullptr)
-  continue;
-}
-if (isa(ArgOrigin.first)) {
-  // FIXME: Check the value.
-  // foo(NULL)
-  continue;
-}
-
-if (isASafeCallArg(ArgOrigin.first))
+if (isPtrOriginSafe(Arg))
   continue;
 
 reportBug(Arg, *P);
@@ -120,6 +111,28 @@ class UncountedCallArgsChecker
 }
   }
 
+  bool isPtrOriginSafe(const Expr *Arg) const {
+std::pair ArgOrigin =
+tryToFindPtrOrigin(Arg, true);
+
+// Temporary ref-counted object created as part of the call argument
+// would outlive the call.
+if (ArgOrigin.second)
+  return true;
+
+if (isa(ArgOrigin.first)) {
+  // foo(nullptr)
+  return true;
+}
+if (isa(ArgOrigin.first)) {
+  // FIXME: Check the value.
+  // foo(NULL)
+  return true;
+}
+
+return isASafeCallArg(ArgOrigin.first);
+  }
+
   bool shouldSkipCall(const CallExpr *CE) const {
 if (CE->getNumArgs() == 0)
   return false;
@@ -196,6 +209,22 @@ class UncountedCallArgsChecker
 Report->addRange(CallArg->getSourceRange());
 BR->emitReport(std::move(Report));
   }
+
+  void reportBugOnThis(const Expr *CallArg) const {
+assert(CallArg);
+
+SmallString<100> Buf;
+llvm::raw_svector_ostream Os(Buf);
+
+Os << "Call argument for 'this' parameter is uncounted and unsafe.";
+
+const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
+
+PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+auto Report = std::make_unique(Bug, Os.str(), BSLoc);
+Report->addRange(CallArg->getSourceRange());
+BR->emitReport(std::move(Report));
+  }
 };
 } // namespace
 
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
new file mode 100644
index 00..e5e39e3faac714
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+
+#include "mock-types.h"
+
+class RefCounted {
+public:
+  void ref() const;
+  void deref() const;
+  void someFunction();
+};
+
+RefCounted* refCountedObj();
+
+void test()
+{
+  refCountedObj()->someFunction();
+  // expected-warning@-1{{Call argument for 'this' parameter is uncounted and 
unsafe}}
+}

>From c15967f8f09198c01aec8b0161cc08484c78f66a Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 13 Feb 2024 19:36:09 -0800
Subject: [PATCH 2/3] Address the review comment. Namely avoid calling
 getTypePtrOrNull and don't use raw_svector_ostream.

---
 .../Checkers/WebKit/UncountedCallArgsChecker.cpp | 9 ++---
 1 file chang

[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-05 Thread Ryosuke Niwa via cfe-commits

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

>From 234e301ab2721ddb2f4b43589785015a7d0aa304 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 19 Feb 2024 01:07:13 -0800
Subject: [PATCH 1/7] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted
 object references within trivial statements

This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and 
pointers to
a ref counted type which appears within "trival" statements. To do this, this 
PR extends
TrivialFunctionAnalysis so that it can also analyze "triviality" of statements 
as well as
that of functions Each Visit* function is now augmented with withCachedResult, 
which is
responsible for looking up and updating the cache for each Visit* functions.

As this PR dramatically improves the false positive rate of the checker, it 
also deletes
the code to ignore raw pointers and references within if and for statements.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 222 --
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 +-
 .../WebKit/UncountedLocalVarsChecker.cpp  |  69 +++---
 .../Analysis/Checkers/WebKit/mock-types.h |   2 +
 .../Checkers/WebKit/uncounted-local-vars.cpp  |  92 +++-
 5 files changed, 285 insertions(+), 121 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 01b191ab0eeaf4..6c9a8aedb38a4c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor
 
   // Returns false if at least one child is non-trivial.
   bool VisitChildren(const Stmt *S) {
-for (const Stmt *Child : S->children()) {
-  if (Child && !Visit(Child))
+return withCachedResult(S, [&]() {
+  for (const Stmt *Child : S->children()) {
+if (Child && !Visit(Child))
+  return false;
+  }
+  return true;
+});
+  }
+
+  bool VisitSubExpr(const Expr *Parent, const Expr *E) {
+return withCachedResult(Parent, [&]() {
+  if (!Visit(E))
 return false;
-}
+  return true;
+});
+  }
 
-return true;
+  template 
+  bool withCachedResult(const StmtType *S, CheckFunction Function) {
+// Insert false to the cache first to avoid infinite recursion.
+auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false));
+if (!IsNew)
+  return It->second;
+bool Result = Function();
+It->second = Result;
+return Result;
   }
 
 public:
-  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+  using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy;
+  using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy;
 
-  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+  TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache,
+ StatementCacheTy &StatementCache)
+  : FunctionCache(FunctionCache), StatementCache(StatementCache) {}
 
   bool VisitStmt(const Stmt *S) {
 // All statements are non-trivial unless overriden later.
@@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitReturnStmt(const ReturnStmt *RS) {
 // A return statement is allowed as long as the return value is trivial.
-if (auto *RV = RS->getRetValue())
-  return Visit(RV);
-return true;
+return withCachedResult(RS, [&]() {
+  if (auto *RV = RS->getRetValue())
+return Visit(RV);
+  return true;
+});
+  }
+
+  bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
+return VisitChildren(FS);
   }
 
   bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
   bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); }
+  bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); }
   bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
   bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
   bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
@@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
-UO->getOpcode() == UO_LNot)
-  return Visit(UO->getSubExpr());
-
-// Other operators are non-trivial.
-return false;
+return withCachedResult(UO, [&]() {
+  auto op = UO->getOpcode();
+  if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
+return Visit(UO->getSubExpr());
+  if (UO->isIncrementOp() || UO->isDecrementOp()) {
+if (auto *RefExpr = dyn_cast(UO->getSubExpr())) {
+  if (auto *Decl = dyn_cast(RefExpr->getDecl()))
+return Decl->i

[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-05 Thread Ryosuke Niwa via cfe-commits


@@ -474,4 +504,22 @@ bool TrivialFunctionAnalysis::isTrivialImpl(
   return Result;
 }
 
+bool TrivialFunctionAnalysis::isTrivialImpl(
+const Stmt *S, TrivialFunctionAnalysis::CacheTy &Cache) {
+  // If the statement isn't in the cache, conservatively assume that
+  // it's not trivial until analysis completes. Unlike a function case,
+  // we don't insert an entry into the cache until Visit returns
+  // since Visit* functions themselves make use of the cache.

rniwa wrote:

Sure, I need to make TrivialFunctionAnalysisVisitor reference the cache via a 
reference instead of via copy though.

https://github.com/llvm/llvm-project/pull/82229
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-06 Thread Ryosuke Niwa via cfe-commits


@@ -189,18 +202,16 @@ class UncountedLocalVarsChecker
 dyn_cast_or_null(Ref->getFoundDecl())) {
   const auto *MaybeGuardianArgType =
   MaybeGuardian->getType().getTypePtr();
-  if (!MaybeGuardianArgType)
-return;
-  const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
-  MaybeGuardianArgType->getAsCXXRecordDecl();
-  if (!MaybeGuardianArgCXXRecord)
-return;
-
-  if (MaybeGuardian->isLocalVarDecl() &&
-  (isRefCounted(MaybeGuardianArgCXXRecord) ||
-   isRefcountedStringsHack(MaybeGuardian)) &&
-  isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) {
-return;
+  if (MaybeGuardianArgType) {
+const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
+MaybeGuardianArgType->getAsCXXRecordDecl();
+if (MaybeGuardianArgCXXRecord) {
+  if (MaybeGuardian->isLocalVarDecl() &&
+  (isRefCounted(MaybeGuardianArgCXXRecord) ||
+   isRefcountedStringsHack(MaybeGuardian)) &&
+  isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian))
+return;
+}

rniwa wrote:

So before this PR, those early exits took place when `MaybeGuardianArgType` was 
`nullptr` or if `MaybeGuardianArgType` wasn't a `CXXRecordDecl`. This PR 
changes so that we only return when all these conditions hold true or when it's 
a `ParmVarDecl`.

https://github.com/llvm/llvm-project/pull/82229
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-06 Thread Ryosuke Niwa via cfe-commits


@@ -189,18 +202,16 @@ class UncountedLocalVarsChecker
 dyn_cast_or_null(Ref->getFoundDecl())) {
   const auto *MaybeGuardianArgType =
   MaybeGuardian->getType().getTypePtr();
-  if (!MaybeGuardianArgType)
-return;
-  const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
-  MaybeGuardianArgType->getAsCXXRecordDecl();
-  if (!MaybeGuardianArgCXXRecord)
-return;
-
-  if (MaybeGuardian->isLocalVarDecl() &&
-  (isRefCounted(MaybeGuardianArgCXXRecord) ||
-   isRefcountedStringsHack(MaybeGuardian)) &&
-  isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) {
-return;
+  if (MaybeGuardianArgType) {
+const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
+MaybeGuardianArgType->getAsCXXRecordDecl();
+if (MaybeGuardianArgCXXRecord) {
+  if (MaybeGuardian->isLocalVarDecl() &&
+  (isRefCounted(MaybeGuardianArgCXXRecord) ||
+   isRefcountedStringsHack(MaybeGuardian)) &&
+  isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian))
+return;
+}

rniwa wrote:

Specifically, without this change, we'd get the following error:
```
clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp Line 162 
(directive at clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp:163):
Local variable 'c' is uncounted and unsafe 
[alpha.webkit.UncountedLocalVarsChecker]
```

https://github.com/llvm/llvm-project/pull/82229
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-06 Thread Ryosuke Niwa via cfe-commits

rniwa wrote:

Actually, we need to cache the results for `VisitDeclRefExpr` as well to avoid 
infinite recursion.

https://github.com/llvm/llvm-project/pull/82229
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-06 Thread Ryosuke Niwa via cfe-commits

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

>From 234e301ab2721ddb2f4b43589785015a7d0aa304 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 19 Feb 2024 01:07:13 -0800
Subject: [PATCH 1/8] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted
 object references within trivial statements

This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and 
pointers to
a ref counted type which appears within "trival" statements. To do this, this 
PR extends
TrivialFunctionAnalysis so that it can also analyze "triviality" of statements 
as well as
that of functions Each Visit* function is now augmented with withCachedResult, 
which is
responsible for looking up and updating the cache for each Visit* functions.

As this PR dramatically improves the false positive rate of the checker, it 
also deletes
the code to ignore raw pointers and references within if and for statements.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 222 --
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 +-
 .../WebKit/UncountedLocalVarsChecker.cpp  |  69 +++---
 .../Analysis/Checkers/WebKit/mock-types.h |   2 +
 .../Checkers/WebKit/uncounted-local-vars.cpp  |  92 +++-
 5 files changed, 285 insertions(+), 121 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 01b191ab0eeaf4..6c9a8aedb38a4c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor
 
   // Returns false if at least one child is non-trivial.
   bool VisitChildren(const Stmt *S) {
-for (const Stmt *Child : S->children()) {
-  if (Child && !Visit(Child))
+return withCachedResult(S, [&]() {
+  for (const Stmt *Child : S->children()) {
+if (Child && !Visit(Child))
+  return false;
+  }
+  return true;
+});
+  }
+
+  bool VisitSubExpr(const Expr *Parent, const Expr *E) {
+return withCachedResult(Parent, [&]() {
+  if (!Visit(E))
 return false;
-}
+  return true;
+});
+  }
 
-return true;
+  template 
+  bool withCachedResult(const StmtType *S, CheckFunction Function) {
+// Insert false to the cache first to avoid infinite recursion.
+auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false));
+if (!IsNew)
+  return It->second;
+bool Result = Function();
+It->second = Result;
+return Result;
   }
 
 public:
-  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+  using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy;
+  using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy;
 
-  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+  TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache,
+ StatementCacheTy &StatementCache)
+  : FunctionCache(FunctionCache), StatementCache(StatementCache) {}
 
   bool VisitStmt(const Stmt *S) {
 // All statements are non-trivial unless overriden later.
@@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitReturnStmt(const ReturnStmt *RS) {
 // A return statement is allowed as long as the return value is trivial.
-if (auto *RV = RS->getRetValue())
-  return Visit(RV);
-return true;
+return withCachedResult(RS, [&]() {
+  if (auto *RV = RS->getRetValue())
+return Visit(RV);
+  return true;
+});
+  }
+
+  bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
+return VisitChildren(FS);
   }
 
   bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
   bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); }
+  bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); }
   bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
   bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
   bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
@@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
-UO->getOpcode() == UO_LNot)
-  return Visit(UO->getSubExpr());
-
-// Other operators are non-trivial.
-return false;
+return withCachedResult(UO, [&]() {
+  auto op = UO->getOpcode();
+  if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
+return Visit(UO->getSubExpr());
+  if (UO->isIncrementOp() || UO->isDecrementOp()) {
+if (auto *RefExpr = dyn_cast(UO->getSubExpr())) {
+  if (auto *Decl = dyn_cast(RefExpr->getDecl()))
+return Decl->i

[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-06 Thread Ryosuke Niwa via cfe-commits


@@ -305,19 +337,21 @@ class TrivialFunctionAnalysisVisitor
   }
 
   bool VisitDeclRefExpr(const DeclRefExpr *DRE) {
-if (auto *decl = DRE->getDecl()) {
-  if (isa(decl))
-return true;
-  if (isa(decl))
-return true;
-  if (auto *VD = dyn_cast(decl)) {
-if (VD->hasConstantInitialization() && VD->getEvaluatedValue())
+return WithCachedResult(DRE, [&]() {
+  if (auto *decl = DRE->getDecl()) {
+if (isa(decl))
   return true;
-auto *Init = VD->getInit();
-return !Init || Visit(Init);
+if (isa(decl))
+  return true;
+if (auto *VD = dyn_cast(decl)) {
+  if (VD->hasConstantInitialization() && VD->getEvaluatedValue())
+return true;
+  auto *Init = VD->getInit();
+  return !Init || Visit(Init);

rniwa wrote:

Oh, that is a very good point. I guess we can just always `return true` for 
`DeclRefExpr`.

https://github.com/llvm/llvm-project/pull/82229
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-06 Thread Ryosuke Niwa via cfe-commits

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

>From 234e301ab2721ddb2f4b43589785015a7d0aa304 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 19 Feb 2024 01:07:13 -0800
Subject: [PATCH 1/9] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted
 object references within trivial statements

This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and 
pointers to
a ref counted type which appears within "trival" statements. To do this, this 
PR extends
TrivialFunctionAnalysis so that it can also analyze "triviality" of statements 
as well as
that of functions Each Visit* function is now augmented with withCachedResult, 
which is
responsible for looking up and updating the cache for each Visit* functions.

As this PR dramatically improves the false positive rate of the checker, it 
also deletes
the code to ignore raw pointers and references within if and for statements.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 222 --
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 +-
 .../WebKit/UncountedLocalVarsChecker.cpp  |  69 +++---
 .../Analysis/Checkers/WebKit/mock-types.h |   2 +
 .../Checkers/WebKit/uncounted-local-vars.cpp  |  92 +++-
 5 files changed, 285 insertions(+), 121 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 01b191ab0eeaf4..6c9a8aedb38a4c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor
 
   // Returns false if at least one child is non-trivial.
   bool VisitChildren(const Stmt *S) {
-for (const Stmt *Child : S->children()) {
-  if (Child && !Visit(Child))
+return withCachedResult(S, [&]() {
+  for (const Stmt *Child : S->children()) {
+if (Child && !Visit(Child))
+  return false;
+  }
+  return true;
+});
+  }
+
+  bool VisitSubExpr(const Expr *Parent, const Expr *E) {
+return withCachedResult(Parent, [&]() {
+  if (!Visit(E))
 return false;
-}
+  return true;
+});
+  }
 
-return true;
+  template 
+  bool withCachedResult(const StmtType *S, CheckFunction Function) {
+// Insert false to the cache first to avoid infinite recursion.
+auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false));
+if (!IsNew)
+  return It->second;
+bool Result = Function();
+It->second = Result;
+return Result;
   }
 
 public:
-  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+  using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy;
+  using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy;
 
-  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+  TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache,
+ StatementCacheTy &StatementCache)
+  : FunctionCache(FunctionCache), StatementCache(StatementCache) {}
 
   bool VisitStmt(const Stmt *S) {
 // All statements are non-trivial unless overriden later.
@@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitReturnStmt(const ReturnStmt *RS) {
 // A return statement is allowed as long as the return value is trivial.
-if (auto *RV = RS->getRetValue())
-  return Visit(RV);
-return true;
+return withCachedResult(RS, [&]() {
+  if (auto *RV = RS->getRetValue())
+return Visit(RV);
+  return true;
+});
+  }
+
+  bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
+return VisitChildren(FS);
   }
 
   bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
   bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); }
+  bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); }
   bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
   bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
   bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
@@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
-UO->getOpcode() == UO_LNot)
-  return Visit(UO->getSubExpr());
-
-// Other operators are non-trivial.
-return false;
+return withCachedResult(UO, [&]() {
+  auto op = UO->getOpcode();
+  if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
+return Visit(UO->getSubExpr());
+  if (UO->isIncrementOp() || UO->isDecrementOp()) {
+if (auto *RefExpr = dyn_cast(UO->getSubExpr())) {
+  if (auto *Decl = dyn_cast(RefExpr->getDecl()))
+return Decl->i

[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-07 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa closed https://github.com/llvm/llvm-project/pull/82229
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Don't assume local variables are safe & treat guarded local variable as safe function arguments (PR #82305)

2024-03-07 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa closed https://github.com/llvm/llvm-project/pull/82305
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Don't assume local variables are safe & treat guarded local variable as safe function arguments (PR #82305)

2024-03-07 Thread Ryosuke Niwa via cfe-commits

rniwa wrote:

So this PR turned out to be not really correct because we can't get rid of 
local variable checker when calling a non-trivial function.

https://github.com/llvm/llvm-project/pull/82305
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Don't assume local variables are safe & treat guarded local variable as safe function arguments (PR #82305)

2024-03-07 Thread Ryosuke Niwa via cfe-commits

rniwa wrote:

haha, sorry for the confusion. I don't have any outstanding PR at this point. 
Thanks for all the reviews :)

https://github.com/llvm/llvm-project/pull/82305
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Allow a variable declaration in a trivial function. (PR #82291)

2024-02-27 Thread Ryosuke Niwa via cfe-commits

rniwa wrote:

Thanks for the review!

https://github.com/llvm/llvm-project/pull/82291
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Allow a variable declaration in a trivial function. (PR #82291)

2024-02-27 Thread Ryosuke Niwa via cfe-commits

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

>From 0a8cfb11a601e7a6ec59489b8ac15c40136002b2 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Sat, 17 Feb 2024 18:12:16 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Allow a variable
 declaration in a trivial function.

---
 .../Checkers/WebKit/PtrTypesSemantics.cpp   |  8 ++--
 .../Checkers/WebKit/uncounted-obj-arg.cpp   | 17 +
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index a7891d2da07c18..09c42434513177 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -309,8 +309,12 @@ class TrivialFunctionAnalysisVisitor
 return true;
   if (isa(decl))
 return true;
-  if (auto *VD = dyn_cast(decl))
-return VD->hasConstantInitialization() && VD->getEvaluatedValue();
+  if (auto *VD = dyn_cast(decl)) {
+if (VD->hasConstantInitialization() && VD->getEvaluatedValue())
+  return true;
+auto *Init = VD->getInit();
+return !Init || Visit(Init);
+  }
 }
 return false;
   }
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
index ac16a31293f3de..80a9a263dab140 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -199,6 +199,8 @@ class RefCounted {
   bool trivial23() const { return 
OptionSet::fromRaw(v).contains(Flags::Flag1); }
   int trivial24() const { ASSERT(v); return v; }
   unsigned trivial25() const { return __c11_atomic_load((volatile 
_Atomic(unsigned) *)&v, __ATOMIC_RELAXED); }
+  bool trivial26() { bool hasValue = v; return !hasValue; }
+  bool trivial27(int v) { bool value; value = v ? 1 : 0; return value; }
 
   static RefCounted& singleton() {
 static RefCounted s_RefCounted;
@@ -262,6 +264,15 @@ class RefCounted {
 return __c11_atomic_load((volatile _Atomic(unsigned) *)another(), 
__ATOMIC_RELAXED);
   }
 
+  void nonTrivial11() {
+Number num(0.3);
+  }
+
+  bool nonTrivial12() {
+bool val = otherFunction();
+return val;
+  }
+
   unsigned v { 0 };
   Number* number { nullptr };
   Enum enumValue { Enum::Value1 };
@@ -309,6 +320,8 @@ class UnrelatedClass {
 getFieldTrivial().trivial23(); // no-warning
 getFieldTrivial().trivial24(); // no-warning
 getFieldTrivial().trivial25(); // no-warning
+getFieldTrivial().trivial26(); // no-warning
+getFieldTrivial().trivial27(5); // no-warning
 RefCounted::singleton().trivial18(); // no-warning
 RefCounted::singleton().someFunction(); // no-warning
 
@@ -334,6 +347,10 @@ class UnrelatedClass {
 // expected-warning@-1{{Call argument for 'this' parameter is uncounted 
and unsafe}}
 getFieldTrivial().nonTrivial10();
 // expected-warning@-1{{Call argument for 'this' parameter is uncounted 
and unsafe}}
+getFieldTrivial().nonTrivial11();
+// expected-warning@-1{{Call argument for 'this' parameter is uncounted 
and unsafe}}
+getFieldTrivial().nonTrivial12();
+// expected-warning@-1{{Call argument for 'this' parameter is uncounted 
and unsafe}}
   }
 };
 

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Allow a variable declaration in a trivial function. (PR #82291)

2024-02-27 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa closed https://github.com/llvm/llvm-project/pull/82291
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-02-28 Thread Ryosuke Niwa via cfe-commits

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

>From 099640652b62c52160b12542a16eb66da90cfc93 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 19 Feb 2024 01:07:13 -0800
Subject: [PATCH 1/2] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted
 object references within trivial statements

This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and 
pointers to
a ref counted type which appears within "trival" statements. To do this, this 
PR extends
TrivialFunctionAnalysis so that it can also analyze "triviality" of statements 
as well as
that of functions Each Visit* function is now augmented with withCachedResult, 
which is
responsible for looking up and updating the cache for each Visit* functions.

As this PR dramatically improves the false positive rate of the checker, it 
also deletes
the code to ignore raw pointers and references within if and for statements.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 221 --
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 +-
 .../WebKit/UncountedLocalVarsChecker.cpp  |  69 +++---
 .../Analysis/Checkers/WebKit/mock-types.h |   2 +
 .../Checkers/WebKit/uncounted-local-vars.cpp  |  92 +++-
 5 files changed, 286 insertions(+), 119 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index a7891d2da07c18..cb89ce59a60878 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -244,18 +244,41 @@ class TrivialFunctionAnalysisVisitor
 
   // Returns false if at least one child is non-trivial.
   bool VisitChildren(const Stmt *S) {
-for (const Stmt *Child : S->children()) {
-  if (Child && !Visit(Child))
+return withCachedResult(S, [&]() {
+  for (const Stmt *Child : S->children()) {
+if (Child && !Visit(Child))
+  return false;
+  }
+  return true;
+});
+  }
+
+  bool VisitSubExpr(const Expr *Parent, const Expr *E) {
+return withCachedResult(Parent, [&]() {
+  if (!Visit(E))
 return false;
-}
+  return true;
+});
+  }
 
-return true;
+  template 
+  bool withCachedResult(const StmtType *S, CheckFunction Function) {
+// Insert false to the cache first to avoid infinite recursion.
+auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false));
+if (!IsNew)
+  return It->second;
+bool Result = Function();
+It->second = Result;
+return Result;
   }
 
 public:
-  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+  using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy;
+  using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy;
 
-  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+  TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache,
+ StatementCacheTy &StatementCache)
+  : FunctionCache(FunctionCache), StatementCache(StatementCache) {}
 
   bool VisitStmt(const Stmt *S) {
 // All statements are non-trivial unless overriden later.
@@ -271,13 +294,21 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitReturnStmt(const ReturnStmt *RS) {
 // A return statement is allowed as long as the return value is trivial.
-if (auto *RV = RS->getRetValue())
-  return Visit(RV);
-return true;
+return withCachedResult(RS, [&]() {
+  if (auto *RV = RS->getRetValue())
+return Visit(RV);
+  return true;
+});
+  }
+
+  bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
+return VisitChildren(FS);
   }
 
   bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
   bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); }
+  bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); }
   bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
   bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
   bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
@@ -285,17 +316,26 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
-UO->getOpcode() == UO_LNot)
-  return Visit(UO->getSubExpr());
-
-// Other operators are non-trivial.
-return false;
+return withCachedResult(UO, [&]() {
+  auto op = UO->getOpcode();
+  if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
+return Visit(UO->getSubExpr());
+  if (UO->isIncrementOp() || UO->isDecrementOp()) {
+if (auto *RefExpr = dyn_cast(UO->getSubExpr())) {
+  if (auto *Decl = dyn_cast(RefExpr->getDecl()))
+return Decl->i

[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-02-28 Thread Ryosuke Niwa via cfe-commits

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

>From 099640652b62c52160b12542a16eb66da90cfc93 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 19 Feb 2024 01:07:13 -0800
Subject: [PATCH 1/2] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted
 object references within trivial statements

This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and 
pointers to
a ref counted type which appears within "trival" statements. To do this, this 
PR extends
TrivialFunctionAnalysis so that it can also analyze "triviality" of statements 
as well as
that of functions Each Visit* function is now augmented with withCachedResult, 
which is
responsible for looking up and updating the cache for each Visit* functions.

As this PR dramatically improves the false positive rate of the checker, it 
also deletes
the code to ignore raw pointers and references within if and for statements.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 221 --
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 +-
 .../WebKit/UncountedLocalVarsChecker.cpp  |  69 +++---
 .../Analysis/Checkers/WebKit/mock-types.h |   2 +
 .../Checkers/WebKit/uncounted-local-vars.cpp  |  92 +++-
 5 files changed, 286 insertions(+), 119 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index a7891d2da07c18..cb89ce59a60878 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -244,18 +244,41 @@ class TrivialFunctionAnalysisVisitor
 
   // Returns false if at least one child is non-trivial.
   bool VisitChildren(const Stmt *S) {
-for (const Stmt *Child : S->children()) {
-  if (Child && !Visit(Child))
+return withCachedResult(S, [&]() {
+  for (const Stmt *Child : S->children()) {
+if (Child && !Visit(Child))
+  return false;
+  }
+  return true;
+});
+  }
+
+  bool VisitSubExpr(const Expr *Parent, const Expr *E) {
+return withCachedResult(Parent, [&]() {
+  if (!Visit(E))
 return false;
-}
+  return true;
+});
+  }
 
-return true;
+  template 
+  bool withCachedResult(const StmtType *S, CheckFunction Function) {
+// Insert false to the cache first to avoid infinite recursion.
+auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false));
+if (!IsNew)
+  return It->second;
+bool Result = Function();
+It->second = Result;
+return Result;
   }
 
 public:
-  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+  using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy;
+  using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy;
 
-  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+  TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache,
+ StatementCacheTy &StatementCache)
+  : FunctionCache(FunctionCache), StatementCache(StatementCache) {}
 
   bool VisitStmt(const Stmt *S) {
 // All statements are non-trivial unless overriden later.
@@ -271,13 +294,21 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitReturnStmt(const ReturnStmt *RS) {
 // A return statement is allowed as long as the return value is trivial.
-if (auto *RV = RS->getRetValue())
-  return Visit(RV);
-return true;
+return withCachedResult(RS, [&]() {
+  if (auto *RV = RS->getRetValue())
+return Visit(RV);
+  return true;
+});
+  }
+
+  bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
+return VisitChildren(FS);
   }
 
   bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
   bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); }
+  bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); }
   bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
   bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
   bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
@@ -285,17 +316,26 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
-UO->getOpcode() == UO_LNot)
-  return Visit(UO->getSubExpr());
-
-// Other operators are non-trivial.
-return false;
+return withCachedResult(UO, [&]() {
+  auto op = UO->getOpcode();
+  if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
+return Visit(UO->getSubExpr());
+  if (UO->isIncrementOp() || UO->isDecrementOp()) {
+if (auto *RefExpr = dyn_cast(UO->getSubExpr())) {
+  if (auto *Decl = dyn_cast(RefExpr->getDecl()))
+return Decl->i

[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-02-29 Thread Ryosuke Niwa via cfe-commits


@@ -70,15 +71,27 @@ bool isSingleton(const FunctionDecl *F);
 class TrivialFunctionAnalysis {
 public:
   /// \returns true if \p D is a "trivial" function.
-  bool isTrivial(const Decl *D) const { return isTrivialImpl(D, TheCache); }
+  bool isTrivial(const Decl *D) const {
+return isTrivialImpl(D, TheFunctionCache, TheStatementCache);
+  }
+
+  bool isTrivial(const Stmt *S) const {
+return isTrivialImpl(S, TheFunctionCache, TheStatementCache);
+  }
 
 private:
   friend class TrivialFunctionAnalysisVisitor;
 
-  using CacheTy = llvm::DenseMap;
-  mutable CacheTy TheCache{};
+  using FunctionCacheTy = llvm::DenseMap;
+  mutable FunctionCacheTy TheFunctionCache{};
+
+  using StatementCacheTy = llvm::DenseMap;
+  mutable StatementCacheTy TheStatementCache{};

rniwa wrote:

Okay, done that.

https://github.com/llvm/llvm-project/pull/82229
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-02-29 Thread Ryosuke Niwa via cfe-commits

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

>From fd171b82d03b29926984b5b835ad9c0ccf197536 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 19 Feb 2024 01:07:13 -0800
Subject: [PATCH 1/3] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted
 object references within trivial statements

This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and 
pointers to
a ref counted type which appears within "trival" statements. To do this, this 
PR extends
TrivialFunctionAnalysis so that it can also analyze "triviality" of statements 
as well as
that of functions Each Visit* function is now augmented with withCachedResult, 
which is
responsible for looking up and updating the cache for each Visit* functions.

As this PR dramatically improves the false positive rate of the checker, it 
also deletes
the code to ignore raw pointers and references within if and for statements.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 221 --
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 +-
 .../WebKit/UncountedLocalVarsChecker.cpp  |  69 +++---
 .../Analysis/Checkers/WebKit/mock-types.h |   2 +
 .../Checkers/WebKit/uncounted-local-vars.cpp  |  92 +++-
 5 files changed, 286 insertions(+), 119 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index defd83ec8e179c1..904781e6ea72ae2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor
 
   // Returns false if at least one child is non-trivial.
   bool VisitChildren(const Stmt *S) {
-for (const Stmt *Child : S->children()) {
-  if (Child && !Visit(Child))
+return withCachedResult(S, [&]() {
+  for (const Stmt *Child : S->children()) {
+if (Child && !Visit(Child))
+  return false;
+  }
+  return true;
+});
+  }
+
+  bool VisitSubExpr(const Expr *Parent, const Expr *E) {
+return withCachedResult(Parent, [&]() {
+  if (!Visit(E))
 return false;
-}
+  return true;
+});
+  }
 
-return true;
+  template 
+  bool withCachedResult(const StmtType *S, CheckFunction Function) {
+// Insert false to the cache first to avoid infinite recursion.
+auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false));
+if (!IsNew)
+  return It->second;
+bool Result = Function();
+It->second = Result;
+return Result;
   }
 
 public:
-  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+  using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy;
+  using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy;
 
-  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+  TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache,
+ StatementCacheTy &StatementCache)
+  : FunctionCache(FunctionCache), StatementCache(StatementCache) {}
 
   bool VisitStmt(const Stmt *S) {
 // All statements are non-trivial unless overriden later.
@@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitReturnStmt(const ReturnStmt *RS) {
 // A return statement is allowed as long as the return value is trivial.
-if (auto *RV = RS->getRetValue())
-  return Visit(RV);
-return true;
+return withCachedResult(RS, [&]() {
+  if (auto *RV = RS->getRetValue())
+return Visit(RV);
+  return true;
+});
+  }
+
+  bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
+return VisitChildren(FS);
   }
 
   bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
   bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); }
+  bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); }
   bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
   bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
   bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
@@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
-UO->getOpcode() == UO_LNot)
-  return Visit(UO->getSubExpr());
-
-// Other operators are non-trivial.
-return false;
+return withCachedResult(UO, [&]() {
+  auto op = UO->getOpcode();
+  if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
+return Visit(UO->getSubExpr());
+  if (UO->isIncrementOp() || UO->isDecrementOp()) {
+if (auto *RefExpr = dyn_cast(UO->getSubExpr())) {
+  if (auto *Decl = dyn_cast(RefExpr->getDecl()))
+return Decl-

[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-02-29 Thread Ryosuke Niwa via cfe-commits

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

>From fd171b82d03b29926984b5b835ad9c0ccf197536 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 19 Feb 2024 01:07:13 -0800
Subject: [PATCH 1/4] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted
 object references within trivial statements

This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and 
pointers to
a ref counted type which appears within "trival" statements. To do this, this 
PR extends
TrivialFunctionAnalysis so that it can also analyze "triviality" of statements 
as well as
that of functions Each Visit* function is now augmented with withCachedResult, 
which is
responsible for looking up and updating the cache for each Visit* functions.

As this PR dramatically improves the false positive rate of the checker, it 
also deletes
the code to ignore raw pointers and references within if and for statements.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 221 --
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 +-
 .../WebKit/UncountedLocalVarsChecker.cpp  |  69 +++---
 .../Analysis/Checkers/WebKit/mock-types.h |   2 +
 .../Checkers/WebKit/uncounted-local-vars.cpp  |  92 +++-
 5 files changed, 286 insertions(+), 119 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index defd83ec8e179c1..904781e6ea72ae2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor
 
   // Returns false if at least one child is non-trivial.
   bool VisitChildren(const Stmt *S) {
-for (const Stmt *Child : S->children()) {
-  if (Child && !Visit(Child))
+return withCachedResult(S, [&]() {
+  for (const Stmt *Child : S->children()) {
+if (Child && !Visit(Child))
+  return false;
+  }
+  return true;
+});
+  }
+
+  bool VisitSubExpr(const Expr *Parent, const Expr *E) {
+return withCachedResult(Parent, [&]() {
+  if (!Visit(E))
 return false;
-}
+  return true;
+});
+  }
 
-return true;
+  template 
+  bool withCachedResult(const StmtType *S, CheckFunction Function) {
+// Insert false to the cache first to avoid infinite recursion.
+auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false));
+if (!IsNew)
+  return It->second;
+bool Result = Function();
+It->second = Result;
+return Result;
   }
 
 public:
-  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+  using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy;
+  using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy;
 
-  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+  TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache,
+ StatementCacheTy &StatementCache)
+  : FunctionCache(FunctionCache), StatementCache(StatementCache) {}
 
   bool VisitStmt(const Stmt *S) {
 // All statements are non-trivial unless overriden later.
@@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitReturnStmt(const ReturnStmt *RS) {
 // A return statement is allowed as long as the return value is trivial.
-if (auto *RV = RS->getRetValue())
-  return Visit(RV);
-return true;
+return withCachedResult(RS, [&]() {
+  if (auto *RV = RS->getRetValue())
+return Visit(RV);
+  return true;
+});
+  }
+
+  bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
+return VisitChildren(FS);
   }
 
   bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
   bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); }
+  bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); }
   bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
   bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
   bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
@@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
-UO->getOpcode() == UO_LNot)
-  return Visit(UO->getSubExpr());
-
-// Other operators are non-trivial.
-return false;
+return withCachedResult(UO, [&]() {
+  auto op = UO->getOpcode();
+  if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
+return Visit(UO->getSubExpr());
+  if (UO->isIncrementOp() || UO->isDecrementOp()) {
+if (auto *RefExpr = dyn_cast(UO->getSubExpr())) {
+  if (auto *Decl = dyn_cast(RefExpr->getDecl()))
+return Decl-

[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-02-29 Thread Ryosuke Niwa via cfe-commits


@@ -171,6 +151,24 @@ class UncountedLocalVarsChecker
 
 std::optional IsUncountedPtr = isUncountedPtr(ArgType);
 if (IsUncountedPtr && *IsUncountedPtr) {
+
+  ASTContext &ctx = V->getASTContext();
+  for (DynTypedNodeList ancestors = ctx.getParents(*V); !ancestors.empty();
+   ancestors = ctx.getParents(*ancestors.begin())) {

rniwa wrote:

Okay, I've rewritten to do that instead. Indeed, this looks cleaner.

https://github.com/llvm/llvm-project/pull/82229
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-01 Thread Ryosuke Niwa via cfe-commits

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

>From fd171b82d03b29926984b5b835ad9c0ccf197536 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 19 Feb 2024 01:07:13 -0800
Subject: [PATCH 1/5] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted
 object references within trivial statements

This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and 
pointers to
a ref counted type which appears within "trival" statements. To do this, this 
PR extends
TrivialFunctionAnalysis so that it can also analyze "triviality" of statements 
as well as
that of functions Each Visit* function is now augmented with withCachedResult, 
which is
responsible for looking up and updating the cache for each Visit* functions.

As this PR dramatically improves the false positive rate of the checker, it 
also deletes
the code to ignore raw pointers and references within if and for statements.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 221 --
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 +-
 .../WebKit/UncountedLocalVarsChecker.cpp  |  69 +++---
 .../Analysis/Checkers/WebKit/mock-types.h |   2 +
 .../Checkers/WebKit/uncounted-local-vars.cpp  |  92 +++-
 5 files changed, 286 insertions(+), 119 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index defd83ec8e179c1..904781e6ea72ae2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor
 
   // Returns false if at least one child is non-trivial.
   bool VisitChildren(const Stmt *S) {
-for (const Stmt *Child : S->children()) {
-  if (Child && !Visit(Child))
+return withCachedResult(S, [&]() {
+  for (const Stmt *Child : S->children()) {
+if (Child && !Visit(Child))
+  return false;
+  }
+  return true;
+});
+  }
+
+  bool VisitSubExpr(const Expr *Parent, const Expr *E) {
+return withCachedResult(Parent, [&]() {
+  if (!Visit(E))
 return false;
-}
+  return true;
+});
+  }
 
-return true;
+  template 
+  bool withCachedResult(const StmtType *S, CheckFunction Function) {
+// Insert false to the cache first to avoid infinite recursion.
+auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false));
+if (!IsNew)
+  return It->second;
+bool Result = Function();
+It->second = Result;
+return Result;
   }
 
 public:
-  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+  using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy;
+  using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy;
 
-  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+  TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache,
+ StatementCacheTy &StatementCache)
+  : FunctionCache(FunctionCache), StatementCache(StatementCache) {}
 
   bool VisitStmt(const Stmt *S) {
 // All statements are non-trivial unless overriden later.
@@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitReturnStmt(const ReturnStmt *RS) {
 // A return statement is allowed as long as the return value is trivial.
-if (auto *RV = RS->getRetValue())
-  return Visit(RV);
-return true;
+return withCachedResult(RS, [&]() {
+  if (auto *RV = RS->getRetValue())
+return Visit(RV);
+  return true;
+});
+  }
+
+  bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
+return VisitChildren(FS);
   }
 
   bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
   bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); }
+  bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); }
   bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
   bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
   bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
@@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
-UO->getOpcode() == UO_LNot)
-  return Visit(UO->getSubExpr());
-
-// Other operators are non-trivial.
-return false;
+return withCachedResult(UO, [&]() {
+  auto op = UO->getOpcode();
+  if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
+return Visit(UO->getSubExpr());
+  if (UO->isIncrementOp() || UO->isDecrementOp()) {
+if (auto *RefExpr = dyn_cast(UO->getSubExpr())) {
+  if (auto *Decl = dyn_cast(RefExpr->getDecl()))
+return Decl-

[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-01 Thread Ryosuke Niwa via cfe-commits

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

>From fd171b82d03b29926984b5b835ad9c0ccf197536 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 19 Feb 2024 01:07:13 -0800
Subject: [PATCH 1/5] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted
 object references within trivial statements

This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and 
pointers to
a ref counted type which appears within "trival" statements. To do this, this 
PR extends
TrivialFunctionAnalysis so that it can also analyze "triviality" of statements 
as well as
that of functions Each Visit* function is now augmented with withCachedResult, 
which is
responsible for looking up and updating the cache for each Visit* functions.

As this PR dramatically improves the false positive rate of the checker, it 
also deletes
the code to ignore raw pointers and references within if and for statements.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 221 --
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 +-
 .../WebKit/UncountedLocalVarsChecker.cpp  |  69 +++---
 .../Analysis/Checkers/WebKit/mock-types.h |   2 +
 .../Checkers/WebKit/uncounted-local-vars.cpp  |  92 +++-
 5 files changed, 286 insertions(+), 119 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index defd83ec8e179c..904781e6ea72ae 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor
 
   // Returns false if at least one child is non-trivial.
   bool VisitChildren(const Stmt *S) {
-for (const Stmt *Child : S->children()) {
-  if (Child && !Visit(Child))
+return withCachedResult(S, [&]() {
+  for (const Stmt *Child : S->children()) {
+if (Child && !Visit(Child))
+  return false;
+  }
+  return true;
+});
+  }
+
+  bool VisitSubExpr(const Expr *Parent, const Expr *E) {
+return withCachedResult(Parent, [&]() {
+  if (!Visit(E))
 return false;
-}
+  return true;
+});
+  }
 
-return true;
+  template 
+  bool withCachedResult(const StmtType *S, CheckFunction Function) {
+// Insert false to the cache first to avoid infinite recursion.
+auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false));
+if (!IsNew)
+  return It->second;
+bool Result = Function();
+It->second = Result;
+return Result;
   }
 
 public:
-  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+  using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy;
+  using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy;
 
-  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+  TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache,
+ StatementCacheTy &StatementCache)
+  : FunctionCache(FunctionCache), StatementCache(StatementCache) {}
 
   bool VisitStmt(const Stmt *S) {
 // All statements are non-trivial unless overriden later.
@@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitReturnStmt(const ReturnStmt *RS) {
 // A return statement is allowed as long as the return value is trivial.
-if (auto *RV = RS->getRetValue())
-  return Visit(RV);
-return true;
+return withCachedResult(RS, [&]() {
+  if (auto *RV = RS->getRetValue())
+return Visit(RV);
+  return true;
+});
+  }
+
+  bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
+return VisitChildren(FS);
   }
 
   bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
   bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); }
+  bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); }
   bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
   bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
   bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
@@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
-UO->getOpcode() == UO_LNot)
-  return Visit(UO->getSubExpr());
-
-// Other operators are non-trivial.
-return false;
+return withCachedResult(UO, [&]() {
+  auto op = UO->getOpcode();
+  if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
+return Visit(UO->getSubExpr());
+  if (UO->isIncrementOp() || UO->isDecrementOp()) {
+if (auto *RefExpr = dyn_cast(UO->getSubExpr())) {
+  if (auto *Decl = dyn_cast(RefExpr->getDecl()))
+return Decl->i

[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-01 Thread Ryosuke Niwa via cfe-commits

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

>From 234e301ab2721ddb2f4b43589785015a7d0aa304 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 19 Feb 2024 01:07:13 -0800
Subject: [PATCH 1/5] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted
 object references within trivial statements

This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and 
pointers to
a ref counted type which appears within "trival" statements. To do this, this 
PR extends
TrivialFunctionAnalysis so that it can also analyze "triviality" of statements 
as well as
that of functions Each Visit* function is now augmented with withCachedResult, 
which is
responsible for looking up and updating the cache for each Visit* functions.

As this PR dramatically improves the false positive rate of the checker, it 
also deletes
the code to ignore raw pointers and references within if and for statements.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 222 --
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 +-
 .../WebKit/UncountedLocalVarsChecker.cpp  |  69 +++---
 .../Analysis/Checkers/WebKit/mock-types.h |   2 +
 .../Checkers/WebKit/uncounted-local-vars.cpp  |  92 +++-
 5 files changed, 285 insertions(+), 121 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 01b191ab0eeaf4..6c9a8aedb38a4c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor
 
   // Returns false if at least one child is non-trivial.
   bool VisitChildren(const Stmt *S) {
-for (const Stmt *Child : S->children()) {
-  if (Child && !Visit(Child))
+return withCachedResult(S, [&]() {
+  for (const Stmt *Child : S->children()) {
+if (Child && !Visit(Child))
+  return false;
+  }
+  return true;
+});
+  }
+
+  bool VisitSubExpr(const Expr *Parent, const Expr *E) {
+return withCachedResult(Parent, [&]() {
+  if (!Visit(E))
 return false;
-}
+  return true;
+});
+  }
 
-return true;
+  template 
+  bool withCachedResult(const StmtType *S, CheckFunction Function) {
+// Insert false to the cache first to avoid infinite recursion.
+auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false));
+if (!IsNew)
+  return It->second;
+bool Result = Function();
+It->second = Result;
+return Result;
   }
 
 public:
-  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+  using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy;
+  using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy;
 
-  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+  TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache,
+ StatementCacheTy &StatementCache)
+  : FunctionCache(FunctionCache), StatementCache(StatementCache) {}
 
   bool VisitStmt(const Stmt *S) {
 // All statements are non-trivial unless overriden later.
@@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitReturnStmt(const ReturnStmt *RS) {
 // A return statement is allowed as long as the return value is trivial.
-if (auto *RV = RS->getRetValue())
-  return Visit(RV);
-return true;
+return withCachedResult(RS, [&]() {
+  if (auto *RV = RS->getRetValue())
+return Visit(RV);
+  return true;
+});
+  }
+
+  bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
+return VisitChildren(FS);
   }
 
   bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
   bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); }
+  bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); }
   bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
   bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
   bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
@@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
-UO->getOpcode() == UO_LNot)
-  return Visit(UO->getSubExpr());
-
-// Other operators are non-trivial.
-return false;
+return withCachedResult(UO, [&]() {
+  auto op = UO->getOpcode();
+  if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
+return Visit(UO->getSubExpr());
+  if (UO->isIncrementOp() || UO->isDecrementOp()) {
+if (auto *RefExpr = dyn_cast(UO->getSubExpr())) {
+  if (auto *Decl = dyn_cast(RefExpr->getDecl()))
+return Decl->i

[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-01 Thread Ryosuke Niwa via cfe-commits

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

>From 234e301ab2721ddb2f4b43589785015a7d0aa304 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 19 Feb 2024 01:07:13 -0800
Subject: [PATCH 1/6] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted
 object references within trivial statements

This PR makes alpha.webkit.UncountedLocalVarsChecker ignore raw references and 
pointers to
a ref counted type which appears within "trival" statements. To do this, this 
PR extends
TrivialFunctionAnalysis so that it can also analyze "triviality" of statements 
as well as
that of functions Each Visit* function is now augmented with withCachedResult, 
which is
responsible for looking up and updating the cache for each Visit* functions.

As this PR dramatically improves the false positive rate of the checker, it 
also deletes
the code to ignore raw pointers and references within if and for statements.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 222 --
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 +-
 .../WebKit/UncountedLocalVarsChecker.cpp  |  69 +++---
 .../Analysis/Checkers/WebKit/mock-types.h |   2 +
 .../Checkers/WebKit/uncounted-local-vars.cpp  |  92 +++-
 5 files changed, 285 insertions(+), 121 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 01b191ab0eeaf4..6c9a8aedb38a4c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -245,18 +245,41 @@ class TrivialFunctionAnalysisVisitor
 
   // Returns false if at least one child is non-trivial.
   bool VisitChildren(const Stmt *S) {
-for (const Stmt *Child : S->children()) {
-  if (Child && !Visit(Child))
+return withCachedResult(S, [&]() {
+  for (const Stmt *Child : S->children()) {
+if (Child && !Visit(Child))
+  return false;
+  }
+  return true;
+});
+  }
+
+  bool VisitSubExpr(const Expr *Parent, const Expr *E) {
+return withCachedResult(Parent, [&]() {
+  if (!Visit(E))
 return false;
-}
+  return true;
+});
+  }
 
-return true;
+  template 
+  bool withCachedResult(const StmtType *S, CheckFunction Function) {
+// Insert false to the cache first to avoid infinite recursion.
+auto [It, IsNew] = StatementCache.insert(std::make_pair(S, false));
+if (!IsNew)
+  return It->second;
+bool Result = Function();
+It->second = Result;
+return Result;
   }
 
 public:
-  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+  using FunctionCacheTy = TrivialFunctionAnalysis::FunctionCacheTy;
+  using StatementCacheTy = TrivialFunctionAnalysis::StatementCacheTy;
 
-  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+  TrivialFunctionAnalysisVisitor(FunctionCacheTy &FunctionCache,
+ StatementCacheTy &StatementCache)
+  : FunctionCache(FunctionCache), StatementCache(StatementCache) {}
 
   bool VisitStmt(const Stmt *S) {
 // All statements are non-trivial unless overriden later.
@@ -272,13 +295,21 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitReturnStmt(const ReturnStmt *RS) {
 // A return statement is allowed as long as the return value is trivial.
-if (auto *RV = RS->getRetValue())
-  return Visit(RV);
-return true;
+return withCachedResult(RS, [&]() {
+  if (auto *RV = RS->getRetValue())
+return Visit(RV);
+  return true;
+});
+  }
+
+  bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
+return VisitChildren(FS);
   }
 
   bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
   bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitForStmt(const ForStmt *FS) { return VisitChildren(FS); }
+  bool VisitWhileStmt(const WhileStmt *WS) { return VisitChildren(WS); }
   bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
   bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
   bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
@@ -286,17 +317,26 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
-UO->getOpcode() == UO_LNot)
-  return Visit(UO->getSubExpr());
-
-// Other operators are non-trivial.
-return false;
+return withCachedResult(UO, [&]() {
+  auto op = UO->getOpcode();
+  if (op == UO_Deref || op == UO_AddrOf || op == UO_LNot)
+return Visit(UO->getSubExpr());
+  if (UO->isIncrementOp() || UO->isDecrementOp()) {
+if (auto *RefExpr = dyn_cast(UO->getSubExpr())) {
+  if (auto *Decl = dyn_cast(RefExpr->getDecl()))
+return Decl->i

[clang] [alpha.webkit.UncountedLocalVarsChecker] Allow uncounted object references within trivial statements (PR #82229)

2024-03-04 Thread Ryosuke Niwa via cfe-commits


@@ -253,6 +253,17 @@ class TrivialFunctionAnalysisVisitor
 return true;
   }
 
+  template 
+  bool WithCachedResult(const StmtType *S, CheckFunction Function) {

rniwa wrote:

Good point. Changed.

https://github.com/llvm/llvm-project/pull/82229
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Fix a crash in clang::isGetterOfRefCounted by checking nullptr in tryToFindPtrOrigin (PR #80768)

2024-02-05 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/80768

None

>From 4e10436ddd55f1b1bed2bb99856e1101b9462117 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 5 Feb 2024 16:07:09 -0800
Subject: [PATCH] Fix a crash in clang::isGetterOfRefCounted by checking
 nullptr in tryToFindPtrOrigin

---
 .../Checkers/WebKit/ASTUtils.cpp  | 14 +-
 .../WebKit/member-function-pointer-crash.cpp  | 26 +++
 2 files changed, 34 insertions(+), 6 deletions(-)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 64028b27702150..728772ed910afc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -34,13 +34,15 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
 }
 if (auto *call = dyn_cast(E)) {
   if (auto *memberCall = dyn_cast(call)) {
-std::optional IsGetterOfRefCt = 
isGetterOfRefCounted(memberCall->getMethodDecl());
-if (IsGetterOfRefCt && *IsGetterOfRefCt) {
-  E = memberCall->getImplicitObjectArgument();
-  if (StopAtFirstRefCountedObj) {
-return {E, true};
+if (auto *decl = memberCall->getMethodDecl()) {
+  std::optional IsGetterOfRefCt = 
isGetterOfRefCounted(memberCall->getMethodDecl());
+  if (IsGetterOfRefCt && *IsGetterOfRefCt) {
+E = memberCall->getImplicitObjectArgument();
+if (StopAtFirstRefCountedObj) {
+  return {E, true};
+}
+continue;
   }
-  continue;
 }
   }
 
diff --git 
a/clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp 
b/clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp
new file mode 100644
index 00..16d3b89b3ac4e7
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp
@@ -0,0 +1,26 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedLocalVarsChecker -verify %s
+
+#include "mock-types.h"
+
+class RenderStyle;
+
+class FillLayer {
+public:
+void ref() const;
+void deref() const;
+};
+
+class FillLayersPropertyWrapper {
+public:
+typedef const FillLayer& (RenderStyle::*LayersGetter)() const;
+
+private:
+bool canInterpolate(const RenderStyle& from) const
+{
+auto* fromLayer = &(from.*m_layersGetter)();
+// expected-warning@-1{{Local variable 'fromLayer' is uncounted and 
unsafe}}
+return true;
+}
+
+LayersGetter m_layersGetter;
+};

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Fix a crash in clang::isGetterOfRefCounted by checking nullptr in tryToFindPtrOrigin (PR #80768)

2024-02-05 Thread Ryosuke Niwa via cfe-commits

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

>From f9f11843c2d09775de20d47dc71c5e482a1ff8b4 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 5 Feb 2024 16:07:09 -0800
Subject: [PATCH] Fix a crash in clang::isGetterOfRefCounted by checking
 nullptr in tryToFindPtrOrigin

---
 .../Checkers/WebKit/ASTUtils.cpp  | 15 ++-
 .../WebKit/member-function-pointer-crash.cpp  | 26 +++
 2 files changed, 35 insertions(+), 6 deletions(-)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 64028b2770215..4526fac64735b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -34,13 +34,16 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
 }
 if (auto *call = dyn_cast(E)) {
   if (auto *memberCall = dyn_cast(call)) {
-std::optional IsGetterOfRefCt = 
isGetterOfRefCounted(memberCall->getMethodDecl());
-if (IsGetterOfRefCt && *IsGetterOfRefCt) {
-  E = memberCall->getImplicitObjectArgument();
-  if (StopAtFirstRefCountedObj) {
-return {E, true};
+if (auto *decl = memberCall->getMethodDecl()) {
+  std::optional IsGetterOfRefCt =
+  isGetterOfRefCounted(memberCall->getMethodDecl());
+  if (IsGetterOfRefCt && *IsGetterOfRefCt) {
+E = memberCall->getImplicitObjectArgument();
+if (StopAtFirstRefCountedObj) {
+  return {E, true};
+}
+continue;
   }
-  continue;
 }
   }
 
diff --git 
a/clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp 
b/clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp
new file mode 100644
index 0..16d3b89b3ac4e
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/member-function-pointer-crash.cpp
@@ -0,0 +1,26 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedLocalVarsChecker -verify %s
+
+#include "mock-types.h"
+
+class RenderStyle;
+
+class FillLayer {
+public:
+void ref() const;
+void deref() const;
+};
+
+class FillLayersPropertyWrapper {
+public:
+typedef const FillLayer& (RenderStyle::*LayersGetter)() const;
+
+private:
+bool canInterpolate(const RenderStyle& from) const
+{
+auto* fromLayer = &(from.*m_layersGetter)();
+// expected-warning@-1{{Local variable 'fromLayer' is uncounted and 
unsafe}}
+return true;
+}
+
+LayersGetter m_layersGetter;
+};

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Ignore assignment to Ref / RefPtr in alpha.webkit.UncountedCallArgsChecker. (PR #80810)

2024-02-06 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/80810

None

>From 81057ef7e57f0e61d4b3edb19a7e6dc2b196b824 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 6 Feb 2024 01:13:34 -0800
Subject: [PATCH] Ignore assignment to Ref / RefPtr in
 alpha.webkit.UncountedCallArgsChecker.

---
 .../WebKit/UncountedCallArgsChecker.cpp   |  9 +++
 .../Checkers/WebKit/assignment-to-refptr.cpp  | 71 +++
 2 files changed, 80 insertions(+)
 create mode 100644 clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 407b6ba7a76428..b9ccf46f52649f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -126,6 +126,15 @@ class UncountedCallArgsChecker
 // of object on LHS.
 if (auto *MemberOp = dyn_cast(CE)) {
   // Note: assignemnt to built-in type isn't derived from CallExpr.
+  if (MemberOp->getOperator() == OO_Equal) { // Ignore assignment to 
Ref/RefPtr.
+auto *callee = MemberOp->getDirectCallee();
+if (auto *calleeDecl = dyn_cast(callee)) {
+  if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
+if (isRefCounted(classDecl))
+  return true;
+  }
+}
+  }
   if (MemberOp->isAssignmentOp())
 return false;
 }
diff --git a/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp 
b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp
new file mode 100644
index 00..c8ad634a5493f5
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/assignment-to-refptr.cpp
@@ -0,0 +1,71 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+// expected-no-diagnostics
+
+template
+class RefPtr {
+public:
+inline constexpr RefPtr() : m_ptr(nullptr) { }
+inline RefPtr(T* ptr)
+  : m_ptr(ptr)
+{
+if (m_ptr)
+m_ptr->ref();
+}
+
+inline RefPtr(const RefPtr& o)
+: m_ptr(o.m_ptr)
+{
+if (m_ptr)
+m_ptr->ref();
+}
+
+inline ~RefPtr()
+{
+if (m_ptr)
+m_ptr->deref();
+}
+
+inline T* operator->() const { return m_ptr; }
+explicit operator bool() const { return m_ptr; }
+  
+RefPtr& operator=(const RefPtr&);
+RefPtr& operator=(T*);
+
+private:
+T* m_ptr;
+};
+
+template
+inline RefPtr& RefPtr::operator=(const RefPtr& o)
+{
+if (m_ptr)
+m_ptr->deref();
+m_ptr = o.m_ptr;
+if (m_ptr)
+m_ptr->ref();
+return *this;
+}
+
+template
+inline RefPtr& RefPtr::operator=(T* optr)
+{
+if (m_ptr)
+m_ptr->deref();
+m_ptr = optr;
+if (m_ptr)
+m_ptr->ref();
+return *this;
+}
+
+class Node {
+public:
+Node* nextSibling() const;
+
+void ref() const;
+void deref() const;
+};
+
+static void removeDetachedChildren(Node* firstChild)
+{
+for (RefPtr child = firstChild; child; child = child->nextSibling());
+}

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [analyzer] WebKit checkers: support ref and deref defined on different classes. (PR #68170)

2023-10-20 Thread Ryosuke Niwa via cfe-commits


@@ -70,29 +88,45 @@ std::optional isRefCountable(const CXXRecordDecl* R)
   if (!R)
 return std::nullopt;
 
-  if (hasPublicRefAndDeref(R))
+  bool hasRef = hasPublicRefMethod(R);
+  bool hasDeref = hasPublicDerefMethod(R);
+  if (hasRef && hasDeref)
 return true;
 
   CXXBasePaths Paths;
   Paths.setOrigin(const_cast(R));
 
   bool AnyInconclusiveBase = false;
-  const auto isRefCountableBase =
-  [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) {
-  std::optional IsRefCountable = 
clang::isRefCountable(Base);
-  if (!IsRefCountable) {
-  AnyInconclusiveBase = true;
-  return false;
-  }
-  return (*IsRefCountable) != nullptr;
+  const auto hasPublicRefInBase =
+  [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
+auto hasRefInBase = clang::hasPublicRefInBase(Base);
+if (!hasRefInBase) {
+  AnyInconclusiveBase = true;
+  return false;
+}
+return (*hasRefInBase) != nullptr;
   };
 
-  bool BasesResult = R->lookupInBases(isRefCountableBase, Paths,
-  /*LookupInDependent =*/true);
+  hasRef =
+  R->lookupInBases(hasPublicRefInBase, Paths, /*LookupInDependent =*/true);
+  if (AnyInconclusiveBase)
+return std::nullopt;
+
+  const auto hasPublicDerefInBase =
+  [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
+auto hasDerefInBase = clang::hasPublicDerefInBase(Base);
+if (!hasDerefInBase) {
+  AnyInconclusiveBase = true;
+  return false;
+}
+return (*hasDerefInBase) != nullptr;
+  };
+  hasDeref = R->lookupInBases(hasPublicDerefInBase, Paths,
+  /*LookupInDependent =*/true);

rniwa wrote:

Fixed.

https://github.com/llvm/llvm-project/pull/68170
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [analyzer] WebKit checkers: support ref and deref defined on different classes. (PR #68170)

2023-10-20 Thread Ryosuke Niwa via cfe-commits


@@ -18,24 +18,26 @@ using namespace clang;
 
 namespace {
 
-bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
+bool hasPublicRefMethod(const CXXRecordDecl *R) {
   assert(R);
   assert(R->hasDefinition());
 
-  bool hasRef = false;
-  bool hasDeref = false;
   for (const CXXMethodDecl *MD : R->methods()) {
 const auto MethodName = safeGetName(MD);
+if (MethodName == "ref" && MD->getAccess() == AS_public)
+  return true;
+  }
+  return false;
+}
 
-if (MethodName == "ref" && MD->getAccess() == AS_public) {
-  if (hasDeref)
-return true;
-  hasRef = true;
-} else if (MethodName == "deref" && MD->getAccess() == AS_public) {
-  if (hasRef)
-return true;
-  hasDeref = true;
-}
+bool hasPublicDerefMethod(const CXXRecordDecl *R) {

rniwa wrote:

Sure, done that.

https://github.com/llvm/llvm-project/pull/68170
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [analyzer] WebKit checkers: support ref and deref defined on different classes. (PR #68170)

2023-10-20 Thread Ryosuke Niwa via cfe-commits


@@ -44,9 +46,25 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
 
 namespace clang {
 
-std::optional
-isRefCountable(const CXXBaseSpecifier* Base)
-{
+std::optional
+hasPublicRefInBase(const CXXBaseSpecifier *Base) {
+  assert(Base);
+
+  const Type *T = Base->getType().getTypePtrOrNull();
+  if (!T)
+return std::nullopt;

rniwa wrote:

Hm... this seems like a much more involved refactoring so perhaps we should do 
it in a separate pass? WDYT?

https://github.com/llvm/llvm-project/pull/68170
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. (PR #69985)

2023-10-23 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/69985

None

>From d8236358e137967dbbd63606b7d43983709597e8 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 23 Oct 2023 16:38:10 -0700
Subject: [PATCH] Fix WebKit static analyzers to support ref and deref methods
 to be defined on different classes.

---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 61 ++-
 .../Checkers/WebKit/PtrTypesSemantics.h   |  8 +--
 .../WebKit/RefCntblBaseVirtualDtorChecker.cpp | 47 --
 ...virtual-dtor-ref-deref-on-diff-classes.cpp | 22 +++
 ...nted-members-ref-deref-on-diff-classes.cpp | 23 +++
 5 files changed, 125 insertions(+), 36 deletions(-)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-ref-deref-on-diff-classes.cpp
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/uncounted-members-ref-deref-on-diff-classes.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index c1f180f31338cb3..8c7eaa3b9a5369d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -18,24 +18,14 @@ using namespace clang;
 
 namespace {
 
-bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
+bool hasPublicMethodInBaseClass(const CXXRecordDecl *R, const char* 
NameToMatch) {
   assert(R);
   assert(R->hasDefinition());
 
-  bool hasRef = false;
-  bool hasDeref = false;
   for (const CXXMethodDecl *MD : R->methods()) {
 const auto MethodName = safeGetName(MD);
-
-if (MethodName == "ref" && MD->getAccess() == AS_public) {
-  if (hasDeref)
-return true;
-  hasRef = true;
-} else if (MethodName == "deref" && MD->getAccess() == AS_public) {
-  if (hasRef)
-return true;
-  hasDeref = true;
-}
+if (MethodName == NameToMatch && MD->getAccess() == AS_public)
+  return true;
   }
   return false;
 }
@@ -44,9 +34,8 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
 
 namespace clang {
 
-std::optional
-isRefCountable(const CXXBaseSpecifier* Base)
-{
+std::optional
+hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) {
   assert(Base);
 
   const Type *T = Base->getType().getTypePtrOrNull();
@@ -59,7 +48,7 @@ isRefCountable(const CXXBaseSpecifier* Base)
   if (!R->hasDefinition())
 return std::nullopt;
 
-  return hasPublicRefAndDeref(R) ? R : nullptr;
+  return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr;
 }
 
 std::optional isRefCountable(const CXXRecordDecl* R)
@@ -70,29 +59,45 @@ std::optional isRefCountable(const CXXRecordDecl* R)
   if (!R)
 return std::nullopt;
 
-  if (hasPublicRefAndDeref(R))
+  bool hasRef = hasPublicMethodInBaseClass(R, "ref");
+  bool hasDeref = hasPublicMethodInBaseClass(R, "deref");
+  if (hasRef && hasDeref)
 return true;
 
   CXXBasePaths Paths;
   Paths.setOrigin(const_cast(R));
 
   bool AnyInconclusiveBase = false;
-  const auto isRefCountableBase =
-  [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) {
-  std::optional IsRefCountable = 
clang::isRefCountable(Base);
-  if (!IsRefCountable) {
-  AnyInconclusiveBase = true;
-  return false;
-  }
-  return (*IsRefCountable) != nullptr;
+  const auto hasPublicRefInBase =
+  [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
+auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
+if (!hasRefInBase) {
+  AnyInconclusiveBase = true;
+  return false;
+}
+return (*hasRefInBase) != nullptr;
   };
 
-  bool BasesResult = R->lookupInBases(isRefCountableBase, Paths,
+  hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths,
   /*LookupInDependent =*/true);
   if (AnyInconclusiveBase)
 return std::nullopt;
 
-  return BasesResult;
+  const auto hasPublicDerefInBase =
+  [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
+auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
+if (!hasDerefInBase) {
+  AnyInconclusiveBase = true;
+  return false;
+}
+return (*hasDerefInBase) != nullptr;
+  };
+  hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths,
+  /*LookupInDependent =*/true);
+  if (AnyInconclusiveBase)
+return std::nullopt;
+
+  return hasRef && hasDeref;
 }
 
 bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 91e3ccf2ec3047e..45b21cc0918443b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ 

[clang] [analyzer] WebKit checkers: support ref and deref defined on different classes. (PR #68170)

2023-10-23 Thread Ryosuke Niwa via cfe-commits

rniwa wrote:

I couldn't take over this PR so I made a new PR at 
https://github.com/llvm/llvm-project/pull/69985.

https://github.com/llvm/llvm-project/pull/68170
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. (PR #69985)

2023-10-23 Thread Ryosuke Niwa via cfe-commits

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

>From 1832737f3e198386d1ebdeccd7d0ca55ef6094c0 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Mon, 23 Oct 2023 16:38:10 -0700
Subject: [PATCH] Fix WebKit static analyzers to support ref and deref methods
 to be defined on different classes.

---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 62 ++-
 .../Checkers/WebKit/PtrTypesSemantics.h   |  8 +--
 .../WebKit/RefCntblBaseVirtualDtorChecker.cpp | 47 --
 ...virtual-dtor-ref-deref-on-diff-classes.cpp | 22 +++
 ...nted-members-ref-deref-on-diff-classes.cpp | 23 +++
 5 files changed, 126 insertions(+), 36 deletions(-)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-ref-deref-on-diff-classes.cpp
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/uncounted-members-ref-deref-on-diff-classes.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index c1f180f31338cb3..d2b663410580008 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -18,24 +18,15 @@ using namespace clang;
 
 namespace {
 
-bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
+bool hasPublicMethodInBaseClass(const CXXRecordDecl *R,
+const char *NameToMatch) {
   assert(R);
   assert(R->hasDefinition());
 
-  bool hasRef = false;
-  bool hasDeref = false;
   for (const CXXMethodDecl *MD : R->methods()) {
 const auto MethodName = safeGetName(MD);
-
-if (MethodName == "ref" && MD->getAccess() == AS_public) {
-  if (hasDeref)
-return true;
-  hasRef = true;
-} else if (MethodName == "deref" && MD->getAccess() == AS_public) {
-  if (hasRef)
-return true;
-  hasDeref = true;
-}
+if (MethodName == NameToMatch && MD->getAccess() == AS_public)
+  return true;
   }
   return false;
 }
@@ -44,9 +35,8 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
 
 namespace clang {
 
-std::optional
-isRefCountable(const CXXBaseSpecifier* Base)
-{
+std::optional
+hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) {
   assert(Base);
 
   const Type *T = Base->getType().getTypePtrOrNull();
@@ -59,7 +49,7 @@ isRefCountable(const CXXBaseSpecifier* Base)
   if (!R->hasDefinition())
 return std::nullopt;
 
-  return hasPublicRefAndDeref(R) ? R : nullptr;
+  return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr;
 }
 
 std::optional isRefCountable(const CXXRecordDecl* R)
@@ -70,29 +60,45 @@ std::optional isRefCountable(const CXXRecordDecl* R)
   if (!R)
 return std::nullopt;
 
-  if (hasPublicRefAndDeref(R))
+  bool hasRef = hasPublicMethodInBaseClass(R, "ref");
+  bool hasDeref = hasPublicMethodInBaseClass(R, "deref");
+  if (hasRef && hasDeref)
 return true;
 
   CXXBasePaths Paths;
   Paths.setOrigin(const_cast(R));
 
   bool AnyInconclusiveBase = false;
-  const auto isRefCountableBase =
-  [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) {
-  std::optional IsRefCountable = 
clang::isRefCountable(Base);
-  if (!IsRefCountable) {
-  AnyInconclusiveBase = true;
-  return false;
-  }
-  return (*IsRefCountable) != nullptr;
+  const auto hasPublicRefInBase =
+  [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
+auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
+if (!hasRefInBase) {
+  AnyInconclusiveBase = true;
+  return false;
+}
+return (*hasRefInBase) != nullptr;
   };
 
-  bool BasesResult = R->lookupInBases(isRefCountableBase, Paths,
+  hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths,
   /*LookupInDependent =*/true);
   if (AnyInconclusiveBase)
 return std::nullopt;
 
-  return BasesResult;
+  const auto hasPublicDerefInBase =
+  [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
+auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
+if (!hasDerefInBase) {
+  AnyInconclusiveBase = true;
+  return false;
+}
+return (*hasDerefInBase) != nullptr;
+  };
+  hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths,
+  /*LookupInDependent =*/true);
+  if (AnyInconclusiveBase)
+return std::nullopt;
+
+  return hasRef && hasDeref;
 }
 
 bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 91e3ccf2ec3047e..45b21cc0918443b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebK

[clang] Add a new attribute value for suppressing WebKit's unsafe member variable warning (PR #70124)

2023-10-24 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/70124

None

>From 3b420fcb0a6d74dc034cfdf2d7b28e13e75f0e08 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Tue, 24 Oct 2023 13:51:50 -0700
Subject: [PATCH] Add a new attribute value for suppressing WebKit's unsafe
 member variable warning

---
 .../Checkers/WebKit/NoUncountedMembersChecker.cpp  | 7 +++
 clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp  | 1 +
 2 files changed, 8 insertions(+)

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
index 66d8588e2531589..6f03e4ac27c4613 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
@@ -75,6 +75,13 @@ class NoUncountedMemberChecker
   if (!MemberType)
 continue;
 
+  if (std::any_of(
+Member->specific_attr_begin(),
+Member->specific_attr_end(), [](const AnnotateAttr *Ann) 
{
+  return Ann->getAnnotation() == "webkit_uncountedmember_exception";
+}))
+  continue;
+
   if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
 // If we don't see the definition we just don't know.
 if (MemberCXXRD->hasDefinition()) {
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp
index a0ea61e0e2a13b1..b175f9491ab5b73 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp
@@ -16,6 +16,7 @@ namespace members {
 RefCountable& c = silenceWarningAboutInit;
 // expected-warning@-1{{Member variable 'c' in 'members::Foo' is a reference 
to ref-countable type 'RefCountable'}}
 Ref d;
+__attribute__((annotate("webkit_uncountedmember_exception"))) 
RefCountable* e;
   };
 
   template

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)

2024-02-14 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81808

This PR introduces the concept of a "trivial function" which applies to a 
function that only calls other trivial functions and contain literals and 
expressions that don't result in heap mutations (specifically it does not call 
deref). This is implemented using ConstStmtVisitor and checking each statement 
and expression's trivialness.

This PR also introduces the concept of a "ingleton function", which is a static 
member function or a free standing function which ends with the suffix 
"singleton". Such a function's return value is understood to be safe to call 
any function with.

>From 26f7904095ddd54ab54a94b3ae84db61d2135833 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 14 Feb 2024 16:21:33 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore
 trivial function calls.

This PR introduces the concept of a "trivial function" which applies to a 
function
that only calls other trivial functions and contain literals and expressions 
that
don't result in heap mutations (specifically it does not call deref). This is
implemented using ConstStmtVisitor and checking each statement and expression's
trivialness.

This PR also introduces the concept of a "ingleton function", which is a static
member function or a free standing function which ends with the suffix 
"singleton".
Such a function's return value is understood to be safe to call any function 
with.
---
 .../Checkers/WebKit/ASTUtils.cpp  |   4 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 246 ++
 .../Checkers/WebKit/PtrTypesSemantics.h   |  23 ++
 .../WebKit/UncountedCallArgsChecker.cpp   |   8 +-
 .../Analysis/Checkers/WebKit/call-args.cpp|  33 +--
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 
 6 files changed, 528 insertions(+), 17 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index b76c0551c77bb0..94eaa81af51772 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = call->getArg(0);
   continue;
 }
+
 if (isReturnValueRefCounted(callee))
   return {E, true};
 
+if (isSingleton(callee))
+  return {E, true};
+
 if (isPtrConversion(callee)) {
   E = call->getArg(0);
   continue;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 907244013d0871..b2169bf023c6e9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -12,6 +12,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtVisitor.h"
 #include 
 
 using namespace clang;
@@ -222,4 +223,249 @@ bool isPtrConversion(const FunctionDecl *F) {
   return false;
 }
 
+bool isSingleton(const FunctionDecl *F) {
+  assert(F);
+  // FIXME: check # of params == 1
+  if (auto *MethodDecl = dyn_cast(F)) {
+if (!MethodDecl->isStatic())
+  return false;
+  }
+  const auto &Name = safeGetName(F);
+  std::string SingletonStr = "singleton";
+  auto index = Name.find(SingletonStr);
+  return index != std::string::npos && index == Name.size() - 
SingletonStr.size();
+}
+
+
+// We only care about statements so let's use the simple
+// (non-recursive) visitor.
+class TrivialFunctionAnalysisVisitor
+: public ConstStmtVisitor {
+
+  // Returns false if at least one child is non-trivial.
+  bool VisitChildren(const Stmt *S) {
+for (const Stmt *Child : S->children()) {
+  if (Child && !Visit(Child))
+return false;
+}
+
+return true;
+  }
+
+public:
+  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+
+  TrivialFunctionAnalysisVisitor(CacheTy &Cache): Cache(Cache) {}
+
+  bool VisitStmt(const Stmt *S) {
+// All statements are non-trivial unless overriden later.
+// Don't even recurse into children by default.
+return false;
+  }
+
+  bool VisitDeclStmt(const DeclStmt *DS) {
+return VisitChildren(DS);
+  }
+
+  bool VisitCompoundStmt(const CompoundStmt *CS) {
+// A compound statement is allowed as long each individual sub-statement
+// is trivial.
+return VisitChildren(CS);
+  }
+
+  bool VisitReturnStmt(const ReturnStmt *RS) {
+// A return statement is allowed as long as the return value is trivial.
+return Visit(RS->getRetValue());
+  }
+
+  bool VisitDoStmt(const DoStmt *DS) {
+return VisitChildren(DS);
+  }
+
+  bool VisitIfStmt(const IfStmt *IS) {
+return VisitChildren(IS);
+  }
+
+  bool VisitSwitchStmt(const SwitchStmt *SS) {
+return VisitChildren(SS);
+  }
+  
+  bool Vi

[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)

2024-02-14 Thread Ryosuke Niwa via cfe-commits

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

>From 5a5b26fc3ed2c1c263cd3495b8844e2daeb2e54b Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 14 Feb 2024 16:21:33 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore
 trivial function calls.

This PR introduces the concept of a "trivial function" which applies to a 
function
that only calls other trivial functions and contain literals and expressions 
that
don't result in heap mutations (specifically it does not call deref). This is
implemented using ConstStmtVisitor and checking each statement and expression's
trivialness.

This PR also introduces the concept of a "ingleton function", which is a static
member function or a free standing function which ends with the suffix 
"singleton".
Such a function's return value is understood to be safe to call any function 
with.
---
 .../Checkers/WebKit/ASTUtils.cpp  |   4 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 208 
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 ++
 .../WebKit/UncountedCallArgsChecker.cpp   |   8 +-
 .../Analysis/Checkers/WebKit/call-args.cpp|  33 +--
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 ++
 6 files changed, 488 insertions(+), 17 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index b76c0551c77bb0..94eaa81af51772 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = call->getArg(0);
   continue;
 }
+
 if (isReturnValueRefCounted(callee))
   return {E, true};
 
+if (isSingleton(callee))
+  return {E, true};
+
 if (isPtrConversion(callee)) {
   E = call->getArg(0);
   continue;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 907244013d0871..ba02c7a786871d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -12,6 +12,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtVisitor.h"
 #include 
 
 using namespace clang;
@@ -222,4 +223,211 @@ bool isPtrConversion(const FunctionDecl *F) {
   return false;
 }
 
+bool isSingleton(const FunctionDecl *F) {
+  assert(F);
+  // FIXME: check # of params == 1
+  if (auto *MethodDecl = dyn_cast(F)) {
+if (!MethodDecl->isStatic())
+  return false;
+  }
+  const auto &Name = safeGetName(F);
+  std::string SingletonStr = "singleton";
+  auto index = Name.find(SingletonStr);
+  return index != std::string::npos &&
+ index == Name.size() - SingletonStr.size();
+}
+
+
+// We only care about statements so let's use the simple
+// (non-recursive) visitor.
+class TrivialFunctionAnalysisVisitor
+: public ConstStmtVisitor {
+
+  // Returns false if at least one child is non-trivial.
+  bool VisitChildren(const Stmt *S) {
+for (const Stmt *Child : S->children()) {
+  if (Child && !Visit(Child))
+return false;
+}
+
+return true;
+  }
+
+public:
+  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+
+  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+
+  bool VisitStmt(const Stmt *S) {
+// All statements are non-trivial unless overriden later.
+// Don't even recurse into children by default.
+return false;
+  }
+
+  bool VisitCompoundStmt(const CompoundStmt *CS) {
+// A compound statement is allowed as long each individual sub-statement
+// is trivial.
+return VisitChildren(CS);
+  }
+
+  bool VisitReturnStmt(const ReturnStmt *RS) {
+// A return statement is allowed as long as the return value is trivial.
+return Visit(RS->getRetValue());
+  }
+
+  bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
+  bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
+  bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
+  bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
+  bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); }
+
+  bool VisitUnaryOperator(const UnaryOperator *UO) {
+// Operator '*' and '!' are allowed as long as the operand is trivial.
+if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
+  return Visit(UO->getSubExpr());
+
+// Other operators are non-trivial.
+return false;
+  }
+
+  bool VisitBinaryOperator(const BinaryOperator *BO) {
+// Binary operators are trivial if their operands are trivial.
+return Visit(BO->getLHS()) && Visit(BO->getRHS(

[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)

2024-02-14 Thread Ryosuke Niwa via cfe-commits

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

>From 857decc27550e2b15938a7846a03561f9ad73f48 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 14 Feb 2024 16:21:33 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore
 trivial function calls.

This PR introduces the concept of a "trivial function" which applies to a 
function
that only calls other trivial functions and contain literals and expressions 
that
don't result in heap mutations (specifically it does not call deref). This is
implemented using ConstStmtVisitor and checking each statement and expression's
trivialness.

This PR also introduces the concept of a "ingleton function", which is a static
member function or a free standing function which ends with the suffix 
"singleton".
Such a function's return value is understood to be safe to call any function 
with.
---
 .../Checkers/WebKit/ASTUtils.cpp  |   4 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 207 
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 ++
 .../WebKit/UncountedCallArgsChecker.cpp   |   8 +-
 .../Analysis/Checkers/WebKit/call-args.cpp|  33 +--
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 ++
 6 files changed, 487 insertions(+), 17 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index b76c0551c77bb0..94eaa81af51772 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = call->getArg(0);
   continue;
 }
+
 if (isReturnValueRefCounted(callee))
   return {E, true};
 
+if (isSingleton(callee))
+  return {E, true};
+
 if (isPtrConversion(callee)) {
   E = call->getArg(0);
   continue;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 907244013d0871..17f59c2d304ddc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -12,6 +12,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtVisitor.h"
 #include 
 
 using namespace clang;
@@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) {
   return false;
 }
 
+bool isSingleton(const FunctionDecl *F) {
+  assert(F);
+  // FIXME: check # of params == 1
+  if (auto *MethodDecl = dyn_cast(F)) {
+if (!MethodDecl->isStatic())
+  return false;
+  }
+  const auto &Name = safeGetName(F);
+  std::string SingletonStr = "singleton";
+  auto index = Name.find(SingletonStr);
+  return index != std::string::npos &&
+ index == Name.size() - SingletonStr.size();
+}
+
+// We only care about statements so let's use the simple
+// (non-recursive) visitor.
+class TrivialFunctionAnalysisVisitor
+: public ConstStmtVisitor {
+
+  // Returns false if at least one child is non-trivial.
+  bool VisitChildren(const Stmt *S) {
+for (const Stmt *Child : S->children()) {
+  if (Child && !Visit(Child))
+return false;
+}
+
+return true;
+  }
+
+public:
+  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+
+  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+
+  bool VisitStmt(const Stmt *S) {
+// All statements are non-trivial unless overriden later.
+// Don't even recurse into children by default.
+return false;
+  }
+
+  bool VisitCompoundStmt(const CompoundStmt *CS) {
+// A compound statement is allowed as long each individual sub-statement
+// is trivial.
+return VisitChildren(CS);
+  }
+
+  bool VisitReturnStmt(const ReturnStmt *RS) {
+// A return statement is allowed as long as the return value is trivial.
+return Visit(RS->getRetValue());
+  }
+
+  bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
+  bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
+  bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
+  bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
+  bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); }
+
+  bool VisitUnaryOperator(const UnaryOperator *UO) {
+// Operator '*' and '!' are allowed as long as the operand is trivial.
+if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
+  return Visit(UO->getSubExpr());
+
+// Other operators are non-trivial.
+return false;
+  }
+
+  bool VisitBinaryOperator(const BinaryOperator *BO) {
+// Binary operators are trivial if their operands are trivial.
+return Visit(BO->getLHS()) && Visit(BO->getRHS())

[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)

2024-02-14 Thread Ryosuke Niwa via cfe-commits

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

>From 857decc27550e2b15938a7846a03561f9ad73f48 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 14 Feb 2024 16:21:33 -0800
Subject: [PATCH 1/2] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore
 trivial function calls.

This PR introduces the concept of a "trivial function" which applies to a 
function
that only calls other trivial functions and contain literals and expressions 
that
don't result in heap mutations (specifically it does not call deref). This is
implemented using ConstStmtVisitor and checking each statement and expression's
trivialness.

This PR also introduces the concept of a "ingleton function", which is a static
member function or a free standing function which ends with the suffix 
"singleton".
Such a function's return value is understood to be safe to call any function 
with.
---
 .../Checkers/WebKit/ASTUtils.cpp  |   4 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 207 
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 ++
 .../WebKit/UncountedCallArgsChecker.cpp   |   8 +-
 .../Analysis/Checkers/WebKit/call-args.cpp|  33 +--
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 ++
 6 files changed, 487 insertions(+), 17 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index b76c0551c77bb0..94eaa81af51772 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = call->getArg(0);
   continue;
 }
+
 if (isReturnValueRefCounted(callee))
   return {E, true};
 
+if (isSingleton(callee))
+  return {E, true};
+
 if (isPtrConversion(callee)) {
   E = call->getArg(0);
   continue;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 907244013d0871..17f59c2d304ddc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -12,6 +12,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtVisitor.h"
 #include 
 
 using namespace clang;
@@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) {
   return false;
 }
 
+bool isSingleton(const FunctionDecl *F) {
+  assert(F);
+  // FIXME: check # of params == 1
+  if (auto *MethodDecl = dyn_cast(F)) {
+if (!MethodDecl->isStatic())
+  return false;
+  }
+  const auto &Name = safeGetName(F);
+  std::string SingletonStr = "singleton";
+  auto index = Name.find(SingletonStr);
+  return index != std::string::npos &&
+ index == Name.size() - SingletonStr.size();
+}
+
+// We only care about statements so let's use the simple
+// (non-recursive) visitor.
+class TrivialFunctionAnalysisVisitor
+: public ConstStmtVisitor {
+
+  // Returns false if at least one child is non-trivial.
+  bool VisitChildren(const Stmt *S) {
+for (const Stmt *Child : S->children()) {
+  if (Child && !Visit(Child))
+return false;
+}
+
+return true;
+  }
+
+public:
+  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+
+  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+
+  bool VisitStmt(const Stmt *S) {
+// All statements are non-trivial unless overriden later.
+// Don't even recurse into children by default.
+return false;
+  }
+
+  bool VisitCompoundStmt(const CompoundStmt *CS) {
+// A compound statement is allowed as long each individual sub-statement
+// is trivial.
+return VisitChildren(CS);
+  }
+
+  bool VisitReturnStmt(const ReturnStmt *RS) {
+// A return statement is allowed as long as the return value is trivial.
+return Visit(RS->getRetValue());
+  }
+
+  bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
+  bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
+  bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
+  bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
+  bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); }
+
+  bool VisitUnaryOperator(const UnaryOperator *UO) {
+// Operator '*' and '!' are allowed as long as the operand is trivial.
+if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
+  return Visit(UO->getSubExpr());
+
+// Other operators are non-trivial.
+return false;
+  }
+
+  bool VisitBinaryOperator(const BinaryOperator *BO) {
+// Binary operators are trivial if their operands are trivial.
+return Visit(BO->getLHS()) && Visit(BO->getRH

[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)

2024-02-14 Thread Ryosuke Niwa via cfe-commits


@@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) {
   return false;
 }
 
+bool isSingleton(const FunctionDecl *F) {
+  assert(F);
+  // FIXME: check # of params == 1
+  if (auto *MethodDecl = dyn_cast(F)) {
+if (!MethodDecl->isStatic())
+  return false;
+  }
+  const auto &Name = safeGetName(F);
+  std::string SingletonStr = "singleton";
+  auto index = Name.find(SingletonStr);
+  return index != std::string::npos &&
+ index == Name.size() - SingletonStr.size();
+}
+
+// We only care about statements so let's use the simple
+// (non-recursive) visitor.
+class TrivialFunctionAnalysisVisitor
+: public ConstStmtVisitor {
+
+  // Returns false if at least one child is non-trivial.
+  bool VisitChildren(const Stmt *S) {
+for (const Stmt *Child : S->children()) {
+  if (Child && !Visit(Child))
+return false;
+}
+
+return true;
+  }
+
+public:
+  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+
+  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+
+  bool VisitStmt(const Stmt *S) {
+// All statements are non-trivial unless overriden later.
+// Don't even recurse into children by default.
+return false;
+  }
+
+  bool VisitCompoundStmt(const CompoundStmt *CS) {
+// A compound statement is allowed as long each individual sub-statement
+// is trivial.
+return VisitChildren(CS);
+  }
+
+  bool VisitReturnStmt(const ReturnStmt *RS) {
+// A return statement is allowed as long as the return value is trivial.
+return Visit(RS->getRetValue());

rniwa wrote:

Fixed! This as indeed the cause of the crash.

https://github.com/llvm/llvm-project/pull/81808
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)

2024-02-14 Thread Ryosuke Niwa via cfe-commits


@@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) {
   return false;
 }
 
+bool isSingleton(const FunctionDecl *F) {
+  assert(F);
+  // FIXME: check # of params == 1
+  if (auto *MethodDecl = dyn_cast(F)) {
+if (!MethodDecl->isStatic())
+  return false;
+  }
+  const auto &Name = safeGetName(F);
+  std::string SingletonStr = "singleton";
+  auto index = Name.find(SingletonStr);
+  return index != std::string::npos &&
+ index == Name.size() - SingletonStr.size();
+}
+
+// We only care about statements so let's use the simple
+// (non-recursive) visitor.
+class TrivialFunctionAnalysisVisitor
+: public ConstStmtVisitor {
+
+  // Returns false if at least one child is non-trivial.
+  bool VisitChildren(const Stmt *S) {
+for (const Stmt *Child : S->children()) {
+  if (Child && !Visit(Child))
+return false;
+}
+
+return true;
+  }
+
+public:
+  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+
+  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+
+  bool VisitStmt(const Stmt *S) {
+// All statements are non-trivial unless overriden later.
+// Don't even recurse into children by default.
+return false;
+  }
+
+  bool VisitCompoundStmt(const CompoundStmt *CS) {
+// A compound statement is allowed as long each individual sub-statement
+// is trivial.
+return VisitChildren(CS);
+  }
+
+  bool VisitReturnStmt(const ReturnStmt *RS) {
+// A return statement is allowed as long as the return value is trivial.
+return Visit(RS->getRetValue());
+  }
+
+  bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
+  bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
+  bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
+  bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
+  bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); }
+
+  bool VisitUnaryOperator(const UnaryOperator *UO) {
+// Operator '*' and '!' are allowed as long as the operand is trivial.
+if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
+  return Visit(UO->getSubExpr());
+
+// Other operators are non-trivial.
+return false;
+  }
+
+  bool VisitBinaryOperator(const BinaryOperator *BO) {
+// Binary operators are trivial if their operands are trivial.
+return Visit(BO->getLHS()) && Visit(BO->getRHS());
+  }
+
+  bool VisitConditionalOperator(const ConditionalOperator *CO) {
+// Ternary operators are trivial if their conditions & values are trivial.
+return VisitChildren(CO);
+  }
+
+  bool VisitDeclRefExpr(const DeclRefExpr *DRE) {
+if (auto *decl = DRE->getDecl()) {
+  if (isa(decl))
+return true;
+}
+return false;
+  }
+
+  bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) {
+// Any static_assert is considered trivial.
+return true;
+  }
+
+  bool VisitCallExpr(const CallExpr *CE) {
+if (auto *MCE = dyn_cast(CE))
+  return VisitCXXMemberCallExpr(MCE);

rniwa wrote:

Good point. Refactored.

https://github.com/llvm/llvm-project/pull/81808
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)

2024-02-14 Thread Ryosuke Niwa via cfe-commits

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

>From 857decc27550e2b15938a7846a03561f9ad73f48 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 14 Feb 2024 16:21:33 -0800
Subject: [PATCH 1/3] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore
 trivial function calls.

This PR introduces the concept of a "trivial function" which applies to a 
function
that only calls other trivial functions and contain literals and expressions 
that
don't result in heap mutations (specifically it does not call deref). This is
implemented using ConstStmtVisitor and checking each statement and expression's
trivialness.

This PR also introduces the concept of a "ingleton function", which is a static
member function or a free standing function which ends with the suffix 
"singleton".
Such a function's return value is understood to be safe to call any function 
with.
---
 .../Checkers/WebKit/ASTUtils.cpp  |   4 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 207 
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 ++
 .../WebKit/UncountedCallArgsChecker.cpp   |   8 +-
 .../Analysis/Checkers/WebKit/call-args.cpp|  33 +--
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 ++
 6 files changed, 487 insertions(+), 17 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index b76c0551c77bb0..94eaa81af51772 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = call->getArg(0);
   continue;
 }
+
 if (isReturnValueRefCounted(callee))
   return {E, true};
 
+if (isSingleton(callee))
+  return {E, true};
+
 if (isPtrConversion(callee)) {
   E = call->getArg(0);
   continue;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 907244013d0871..17f59c2d304ddc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -12,6 +12,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtVisitor.h"
 #include 
 
 using namespace clang;
@@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) {
   return false;
 }
 
+bool isSingleton(const FunctionDecl *F) {
+  assert(F);
+  // FIXME: check # of params == 1
+  if (auto *MethodDecl = dyn_cast(F)) {
+if (!MethodDecl->isStatic())
+  return false;
+  }
+  const auto &Name = safeGetName(F);
+  std::string SingletonStr = "singleton";
+  auto index = Name.find(SingletonStr);
+  return index != std::string::npos &&
+ index == Name.size() - SingletonStr.size();
+}
+
+// We only care about statements so let's use the simple
+// (non-recursive) visitor.
+class TrivialFunctionAnalysisVisitor
+: public ConstStmtVisitor {
+
+  // Returns false if at least one child is non-trivial.
+  bool VisitChildren(const Stmt *S) {
+for (const Stmt *Child : S->children()) {
+  if (Child && !Visit(Child))
+return false;
+}
+
+return true;
+  }
+
+public:
+  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+
+  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+
+  bool VisitStmt(const Stmt *S) {
+// All statements are non-trivial unless overriden later.
+// Don't even recurse into children by default.
+return false;
+  }
+
+  bool VisitCompoundStmt(const CompoundStmt *CS) {
+// A compound statement is allowed as long each individual sub-statement
+// is trivial.
+return VisitChildren(CS);
+  }
+
+  bool VisitReturnStmt(const ReturnStmt *RS) {
+// A return statement is allowed as long as the return value is trivial.
+return Visit(RS->getRetValue());
+  }
+
+  bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
+  bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
+  bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
+  bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
+  bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); }
+
+  bool VisitUnaryOperator(const UnaryOperator *UO) {
+// Operator '*' and '!' are allowed as long as the operand is trivial.
+if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
+  return Visit(UO->getSubExpr());
+
+// Other operators are non-trivial.
+return false;
+  }
+
+  bool VisitBinaryOperator(const BinaryOperator *BO) {
+// Binary operators are trivial if their operands are trivial.
+return Visit(BO->getLHS()) && Visit(BO->getRH

[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)

2024-02-14 Thread Ryosuke Niwa via cfe-commits

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

>From 857decc27550e2b15938a7846a03561f9ad73f48 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 14 Feb 2024 16:21:33 -0800
Subject: [PATCH 1/4] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore
 trivial function calls.

This PR introduces the concept of a "trivial function" which applies to a 
function
that only calls other trivial functions and contain literals and expressions 
that
don't result in heap mutations (specifically it does not call deref). This is
implemented using ConstStmtVisitor and checking each statement and expression's
trivialness.

This PR also introduces the concept of a "ingleton function", which is a static
member function or a free standing function which ends with the suffix 
"singleton".
Such a function's return value is understood to be safe to call any function 
with.
---
 .../Checkers/WebKit/ASTUtils.cpp  |   4 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 207 
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 ++
 .../WebKit/UncountedCallArgsChecker.cpp   |   8 +-
 .../Analysis/Checkers/WebKit/call-args.cpp|  33 +--
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 ++
 6 files changed, 487 insertions(+), 17 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index b76c0551c77bb0..94eaa81af51772 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = call->getArg(0);
   continue;
 }
+
 if (isReturnValueRefCounted(callee))
   return {E, true};
 
+if (isSingleton(callee))
+  return {E, true};
+
 if (isPtrConversion(callee)) {
   E = call->getArg(0);
   continue;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 907244013d0871..17f59c2d304ddc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -12,6 +12,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtVisitor.h"
 #include 
 
 using namespace clang;
@@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) {
   return false;
 }
 
+bool isSingleton(const FunctionDecl *F) {
+  assert(F);
+  // FIXME: check # of params == 1
+  if (auto *MethodDecl = dyn_cast(F)) {
+if (!MethodDecl->isStatic())
+  return false;
+  }
+  const auto &Name = safeGetName(F);
+  std::string SingletonStr = "singleton";
+  auto index = Name.find(SingletonStr);
+  return index != std::string::npos &&
+ index == Name.size() - SingletonStr.size();
+}
+
+// We only care about statements so let's use the simple
+// (non-recursive) visitor.
+class TrivialFunctionAnalysisVisitor
+: public ConstStmtVisitor {
+
+  // Returns false if at least one child is non-trivial.
+  bool VisitChildren(const Stmt *S) {
+for (const Stmt *Child : S->children()) {
+  if (Child && !Visit(Child))
+return false;
+}
+
+return true;
+  }
+
+public:
+  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+
+  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+
+  bool VisitStmt(const Stmt *S) {
+// All statements are non-trivial unless overriden later.
+// Don't even recurse into children by default.
+return false;
+  }
+
+  bool VisitCompoundStmt(const CompoundStmt *CS) {
+// A compound statement is allowed as long each individual sub-statement
+// is trivial.
+return VisitChildren(CS);
+  }
+
+  bool VisitReturnStmt(const ReturnStmt *RS) {
+// A return statement is allowed as long as the return value is trivial.
+return Visit(RS->getRetValue());
+  }
+
+  bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
+  bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
+  bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
+  bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
+  bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); }
+
+  bool VisitUnaryOperator(const UnaryOperator *UO) {
+// Operator '*' and '!' are allowed as long as the operand is trivial.
+if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
+  return Visit(UO->getSubExpr());
+
+// Other operators are non-trivial.
+return false;
+  }
+
+  bool VisitBinaryOperator(const BinaryOperator *BO) {
+// Binary operators are trivial if their operands are trivial.
+return Visit(BO->getLHS()) && Visit(BO->getRH

[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)

2024-02-14 Thread Ryosuke Niwa via cfe-commits

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

>From 857decc27550e2b15938a7846a03561f9ad73f48 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 14 Feb 2024 16:21:33 -0800
Subject: [PATCH 1/5] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore
 trivial function calls.

This PR introduces the concept of a "trivial function" which applies to a 
function
that only calls other trivial functions and contain literals and expressions 
that
don't result in heap mutations (specifically it does not call deref). This is
implemented using ConstStmtVisitor and checking each statement and expression's
trivialness.

This PR also introduces the concept of a "ingleton function", which is a static
member function or a free standing function which ends with the suffix 
"singleton".
Such a function's return value is understood to be safe to call any function 
with.
---
 .../Checkers/WebKit/ASTUtils.cpp  |   4 +
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 207 
 .../Checkers/WebKit/PtrTypesSemantics.h   |  21 ++
 .../WebKit/UncountedCallArgsChecker.cpp   |   8 +-
 .../Analysis/Checkers/WebKit/call-args.cpp|  33 +--
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 231 ++
 6 files changed, 487 insertions(+), 17 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index b76c0551c77bb0..94eaa81af51772 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -66,9 +66,13 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = call->getArg(0);
   continue;
 }
+
 if (isReturnValueRefCounted(callee))
   return {E, true};
 
+if (isSingleton(callee))
+  return {E, true};
+
 if (isPtrConversion(callee)) {
   E = call->getArg(0);
   continue;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 907244013d0871..17f59c2d304ddc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -12,6 +12,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtVisitor.h"
 #include 
 
 using namespace clang;
@@ -222,4 +223,210 @@ bool isPtrConversion(const FunctionDecl *F) {
   return false;
 }
 
+bool isSingleton(const FunctionDecl *F) {
+  assert(F);
+  // FIXME: check # of params == 1
+  if (auto *MethodDecl = dyn_cast(F)) {
+if (!MethodDecl->isStatic())
+  return false;
+  }
+  const auto &Name = safeGetName(F);
+  std::string SingletonStr = "singleton";
+  auto index = Name.find(SingletonStr);
+  return index != std::string::npos &&
+ index == Name.size() - SingletonStr.size();
+}
+
+// We only care about statements so let's use the simple
+// (non-recursive) visitor.
+class TrivialFunctionAnalysisVisitor
+: public ConstStmtVisitor {
+
+  // Returns false if at least one child is non-trivial.
+  bool VisitChildren(const Stmt *S) {
+for (const Stmt *Child : S->children()) {
+  if (Child && !Visit(Child))
+return false;
+}
+
+return true;
+  }
+
+public:
+  using CacheTy = TrivialFunctionAnalysis::CacheTy;
+
+  TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
+
+  bool VisitStmt(const Stmt *S) {
+// All statements are non-trivial unless overriden later.
+// Don't even recurse into children by default.
+return false;
+  }
+
+  bool VisitCompoundStmt(const CompoundStmt *CS) {
+// A compound statement is allowed as long each individual sub-statement
+// is trivial.
+return VisitChildren(CS);
+  }
+
+  bool VisitReturnStmt(const ReturnStmt *RS) {
+// A return statement is allowed as long as the return value is trivial.
+return Visit(RS->getRetValue());
+  }
+
+  bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
+  bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
+  bool VisitIfStmt(const IfStmt *IS) { return VisitChildren(IS); }
+  bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
+  bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
+  bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); }
+
+  bool VisitUnaryOperator(const UnaryOperator *UO) {
+// Operator '*' and '!' are allowed as long as the operand is trivial.
+if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
+  return Visit(UO->getSubExpr());
+
+// Other operators are non-trivial.
+return false;
+  }
+
+  bool VisitBinaryOperator(const BinaryOperator *BO) {
+// Binary operators are trivial if their operands are trivial.
+return Visit(BO->getLHS()) && Visit(BO->getRH

[clang] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions (PR #81829)

2024-02-14 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81829

Allow address-of operator (&), enum constant, and a reference to constant as 
well as materializing temporqary expression and an expression with cleanups to 
appear within a trivial function.

>From 793c72168db3a27ad189e9c95d7701d19cefec1e Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 14 Feb 2024 23:30:27 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial
 functions

Allow address-of operator (&), enum constant, and a reference to constant
as well as materializing temporqary expression and an expression with cleanups
to appear within a trivial function.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 21 +++-
 .../Analysis/Checkers/WebKit/mock-types.h |  1 +
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 95 ++-
 3 files changed, 115 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index bf6f9a64877c64..e8295938a855d7 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -285,7 +285,7 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
+if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf || 
UO->getOpcode() == UO_LNot)
   return Visit(UO->getSubExpr());
 
 // Other operators are non-trivial.
@@ -306,6 +306,10 @@ class TrivialFunctionAnalysisVisitor
 if (auto *decl = DRE->getDecl()) {
   if (isa(decl))
 return true;
+  if (isa(decl))
+return true;
+  if (auto *VD = dyn_cast(decl))
+return VD->hasConstantInitialization() && VD->getEvaluatedValue();
 }
 return false;
   }
@@ -377,6 +381,15 @@ class TrivialFunctionAnalysisVisitor
 return Visit(ECE->getSubExpr());
   }
 
+  bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT)
+  {
+return Visit(VMT->getSubExpr());
+  }
+
+  bool VisitExprWithCleanups(const ExprWithCleanups *EWC) {
+return Visit(EWC->getSubExpr());
+  }
+
   bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); }
 
   bool VisitInitListExpr(const InitListExpr *ILE) {
@@ -397,6 +410,12 @@ class TrivialFunctionAnalysisVisitor
 return true;
   }
 
+  bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E)
+  {
+// nullptr is trivial.
+return true;
+  }
+
   // Constant literal expressions are always trivial
   bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; }
   bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; }
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index cc40487614a83d..d08a997aa8c043 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -19,6 +19,7 @@ template  struct RefPtr {
   RefPtr(T *t) : t(t) {}
   T *get() { return t; }
   T *operator->() { return t; }
+  const T *operator->() const { return t; }
   T &operator*() { return *t; }
   RefPtr &operator=(T *) { return *this; }
   operator bool() { return t; }
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
index 156a2480901bf0..83c4414d1d01aa 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -1,7 +1,6 @@
 // RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
 
 #include "mock-types.h"
-//#include 
 
 void WTFBreakpointTrap();
 void WTFCrashWithInfo(int, const char*, const char*, int);
@@ -60,11 +59,86 @@ NO_RETURN_DUE_TO_CRASH ALWAYS_INLINE void 
WTFCrashWithInfo(int line, const char*
 WTFCrashWithInfoImpl(line, file, function, counter, wtfCrashArg(reason));
 }
 
+enum class Flags : unsigned short {
+  Flag1 = 1 << 0,
+  Flag2 = 1 << 1,
+  Flag3 = 1 << 2,
+};
+
+template class OptionSet {
+public:
+  using StorageType = unsigned short;
+
+  static constexpr OptionSet fromRaw(StorageType rawValue) {
+return OptionSet(static_cast(rawValue), FromRawValue);
+  }
+
+  constexpr OptionSet() = default;
+
+  constexpr OptionSet(E e)
+: m_storage(static_cast(e)) {
+  }
+
+  constexpr StorageType toRaw() const { return m_storage; }
+
+  constexpr bool isEmpty() const { return !m_storage; }
+
+  constexpr explicit operator bool() const { return !isEmpty(); }
+
+  constexpr bool contains(E option) const { return containsAny(option); }
+  constexpr bool containsAny(OptionSet optionSet) const {
+return !!(*this & optionSet);
+  }
+
+  constexpr bool containsAll(OptionSet o

[clang] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions (PR #81829)

2024-02-14 Thread Ryosuke Niwa via cfe-commits

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

>From 382ce72e206ca80e3414d5a141afa0f4f8b8 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 14 Feb 2024 23:30:27 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial
 functions

Allow address-of operator (&), enum constant, and a reference to constant
as well as materializing temporqary expression and an expression with cleanups
to appear within a trivial function.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 +++-
 .../Analysis/Checkers/WebKit/mock-types.h |  1 +
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 95 ++-
 3 files changed, 114 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index bf6f9a64877c64..6f236db0474079 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -285,7 +285,8 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
+if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
+UO->getOpcode() == UO_LNot)
   return Visit(UO->getSubExpr());
 
 // Other operators are non-trivial.
@@ -306,6 +307,10 @@ class TrivialFunctionAnalysisVisitor
 if (auto *decl = DRE->getDecl()) {
   if (isa(decl))
 return true;
+  if (isa(decl))
+return true;
+  if (auto *VD = dyn_cast(decl))
+return VD->hasConstantInitialization() && VD->getEvaluatedValue();
 }
 return false;
   }
@@ -377,6 +382,14 @@ class TrivialFunctionAnalysisVisitor
 return Visit(ECE->getSubExpr());
   }
 
+  bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT) {
+return Visit(VMT->getSubExpr());
+  }
+
+  bool VisitExprWithCleanups(const ExprWithCleanups *EWC) {
+return Visit(EWC->getSubExpr());
+  }
+
   bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); }
 
   bool VisitInitListExpr(const InitListExpr *ILE) {
@@ -397,6 +410,11 @@ class TrivialFunctionAnalysisVisitor
 return true;
   }
 
+  bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) {
+// nullptr is trivial.
+return true;
+  }
+
   // Constant literal expressions are always trivial
   bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; }
   bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; }
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index cc40487614a83d..d08a997aa8c043 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -19,6 +19,7 @@ template  struct RefPtr {
   RefPtr(T *t) : t(t) {}
   T *get() { return t; }
   T *operator->() { return t; }
+  const T *operator->() const { return t; }
   T &operator*() { return *t; }
   RefPtr &operator=(T *) { return *this; }
   operator bool() { return t; }
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
index 156a2480901bf0..83c4414d1d01aa 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -1,7 +1,6 @@
 // RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
 
 #include "mock-types.h"
-//#include 
 
 void WTFBreakpointTrap();
 void WTFCrashWithInfo(int, const char*, const char*, int);
@@ -60,11 +59,86 @@ NO_RETURN_DUE_TO_CRASH ALWAYS_INLINE void 
WTFCrashWithInfo(int line, const char*
 WTFCrashWithInfoImpl(line, file, function, counter, wtfCrashArg(reason));
 }
 
+enum class Flags : unsigned short {
+  Flag1 = 1 << 0,
+  Flag2 = 1 << 1,
+  Flag3 = 1 << 2,
+};
+
+template class OptionSet {
+public:
+  using StorageType = unsigned short;
+
+  static constexpr OptionSet fromRaw(StorageType rawValue) {
+return OptionSet(static_cast(rawValue), FromRawValue);
+  }
+
+  constexpr OptionSet() = default;
+
+  constexpr OptionSet(E e)
+: m_storage(static_cast(e)) {
+  }
+
+  constexpr StorageType toRaw() const { return m_storage; }
+
+  constexpr bool isEmpty() const { return !m_storage; }
+
+  constexpr explicit operator bool() const { return !isEmpty(); }
+
+  constexpr bool contains(E option) const { return containsAny(option); }
+  constexpr bool containsAny(OptionSet optionSet) const {
+return !!(*this & optionSet);
+  }
+
+  constexpr bool containsAll(OptionSet optionSet) const {
+return (*this & optionSet) == optionSet;
+  }
+
+  constexpr void add(OptionSet optionSet) { m_storage |= optionSet.m_storage; }
+
+  constexpr void remove(OptionSet op

[clang] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions (PR #81829)

2024-02-15 Thread Ryosuke Niwa via cfe-commits

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

>From 382ce72e206ca80e3414d5a141afa0f4f8b8 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 14 Feb 2024 23:30:27 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial
 functions

Allow address-of operator (&), enum constant, and a reference to constant
as well as materializing temporqary expression and an expression with cleanups
to appear within a trivial function.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 +++-
 .../Analysis/Checkers/WebKit/mock-types.h |  1 +
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 95 ++-
 3 files changed, 114 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index bf6f9a64877c64..6f236db0474079 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -285,7 +285,8 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
+if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
+UO->getOpcode() == UO_LNot)
   return Visit(UO->getSubExpr());
 
 // Other operators are non-trivial.
@@ -306,6 +307,10 @@ class TrivialFunctionAnalysisVisitor
 if (auto *decl = DRE->getDecl()) {
   if (isa(decl))
 return true;
+  if (isa(decl))
+return true;
+  if (auto *VD = dyn_cast(decl))
+return VD->hasConstantInitialization() && VD->getEvaluatedValue();
 }
 return false;
   }
@@ -377,6 +382,14 @@ class TrivialFunctionAnalysisVisitor
 return Visit(ECE->getSubExpr());
   }
 
+  bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT) {
+return Visit(VMT->getSubExpr());
+  }
+
+  bool VisitExprWithCleanups(const ExprWithCleanups *EWC) {
+return Visit(EWC->getSubExpr());
+  }
+
   bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); }
 
   bool VisitInitListExpr(const InitListExpr *ILE) {
@@ -397,6 +410,11 @@ class TrivialFunctionAnalysisVisitor
 return true;
   }
 
+  bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) {
+// nullptr is trivial.
+return true;
+  }
+
   // Constant literal expressions are always trivial
   bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; }
   bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; }
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index cc40487614a83d..d08a997aa8c043 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -19,6 +19,7 @@ template  struct RefPtr {
   RefPtr(T *t) : t(t) {}
   T *get() { return t; }
   T *operator->() { return t; }
+  const T *operator->() const { return t; }
   T &operator*() { return *t; }
   RefPtr &operator=(T *) { return *this; }
   operator bool() { return t; }
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
index 156a2480901bf0..83c4414d1d01aa 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -1,7 +1,6 @@
 // RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
 
 #include "mock-types.h"
-//#include 
 
 void WTFBreakpointTrap();
 void WTFCrashWithInfo(int, const char*, const char*, int);
@@ -60,11 +59,86 @@ NO_RETURN_DUE_TO_CRASH ALWAYS_INLINE void 
WTFCrashWithInfo(int line, const char*
 WTFCrashWithInfoImpl(line, file, function, counter, wtfCrashArg(reason));
 }
 
+enum class Flags : unsigned short {
+  Flag1 = 1 << 0,
+  Flag2 = 1 << 1,
+  Flag3 = 1 << 2,
+};
+
+template class OptionSet {
+public:
+  using StorageType = unsigned short;
+
+  static constexpr OptionSet fromRaw(StorageType rawValue) {
+return OptionSet(static_cast(rawValue), FromRawValue);
+  }
+
+  constexpr OptionSet() = default;
+
+  constexpr OptionSet(E e)
+: m_storage(static_cast(e)) {
+  }
+
+  constexpr StorageType toRaw() const { return m_storage; }
+
+  constexpr bool isEmpty() const { return !m_storage; }
+
+  constexpr explicit operator bool() const { return !isEmpty(); }
+
+  constexpr bool contains(E option) const { return containsAny(option); }
+  constexpr bool containsAny(OptionSet optionSet) const {
+return !!(*this & optionSet);
+  }
+
+  constexpr bool containsAll(OptionSet optionSet) const {
+return (*this & optionSet) == optionSet;
+  }
+
+  constexpr void add(OptionSet optionSet) { m_storage |= optionSet.m_storage; }
+
+  constexpr void remove(OptionSet op

[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)

2024-02-15 Thread Ryosuke Niwa via cfe-commits

rniwa wrote:

> @rniwa the test you modified in this change seems to be failing on at least 
> one bot. Can you take a look or revert if you need time to investigate?
> 
> https://lab.llvm.org/buildbot/#/builders/123/builds/25095

Oh, interesting. I suppose this is due to:
```
# | error: 'expected-error' diagnostics seen but not expected: 
# |   File 
C:\b\slave\clang-x64-windows-msvc\llvm-project\clang\test\Analysis\Checkers\WebKit\uncounted-obj-arg.cpp
 Line 110: cast from pointer to smaller type 'unsigned long' loses information
```

https://github.com/llvm/llvm-project/pull/81808
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Fix uncounted-obj-arg.cpp for Windows. (PR #81903)

2024-02-15 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/81903

None

>From e3a45512e1a10c341b7172ddd3c2c8d42368110b Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Thu, 15 Feb 2024 10:58:13 -0800
Subject: [PATCH] Fix uncounted-obj-arg.cpp for Windows.

---
 clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
index 156a2480901bf0..338e1b685122bc 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -107,7 +107,7 @@ class RefCounted {
 return 0;
   }
   void *trivial15() { return static_cast(this); }
-  unsigned long trivial16() { return reinterpret_cast(this); }
+  unsigned long trivial16() { return *reinterpret_cast(this); }
   RefCounted& trivial17() const { return const_cast(*this); }
   RefCounted& trivial18() const { RELEASE_ASSERT(this, "this must be not 
null"); return const_cast(*this); }
   void trivial19() const { return; }

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Detect & ignore trivial function calls. (PR #81808)

2024-02-15 Thread Ryosuke Niwa via cfe-commits

rniwa wrote:

Fixing the test in https://github.com/llvm/llvm-project/pull/81903

https://github.com/llvm/llvm-project/pull/81808
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions (PR #81829)

2024-02-15 Thread Ryosuke Niwa via cfe-commits

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

>From 382ce72e206ca80e3414d5a141afa0f4f8b8 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Wed, 14 Feb 2024 23:30:27 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial
 functions

Allow address-of operator (&), enum constant, and a reference to constant
as well as materializing temporqary expression and an expression with cleanups
to appear within a trivial function.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 20 +++-
 .../Analysis/Checkers/WebKit/mock-types.h |  1 +
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 95 ++-
 3 files changed, 114 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index bf6f9a64877c64..6f236db0474079 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -285,7 +285,8 @@ class TrivialFunctionAnalysisVisitor
 
   bool VisitUnaryOperator(const UnaryOperator *UO) {
 // Operator '*' and '!' are allowed as long as the operand is trivial.
-if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_LNot)
+if (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrOf ||
+UO->getOpcode() == UO_LNot)
   return Visit(UO->getSubExpr());
 
 // Other operators are non-trivial.
@@ -306,6 +307,10 @@ class TrivialFunctionAnalysisVisitor
 if (auto *decl = DRE->getDecl()) {
   if (isa(decl))
 return true;
+  if (isa(decl))
+return true;
+  if (auto *VD = dyn_cast(decl))
+return VD->hasConstantInitialization() && VD->getEvaluatedValue();
 }
 return false;
   }
@@ -377,6 +382,14 @@ class TrivialFunctionAnalysisVisitor
 return Visit(ECE->getSubExpr());
   }
 
+  bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT) {
+return Visit(VMT->getSubExpr());
+  }
+
+  bool VisitExprWithCleanups(const ExprWithCleanups *EWC) {
+return Visit(EWC->getSubExpr());
+  }
+
   bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); }
 
   bool VisitInitListExpr(const InitListExpr *ILE) {
@@ -397,6 +410,11 @@ class TrivialFunctionAnalysisVisitor
 return true;
   }
 
+  bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) {
+// nullptr is trivial.
+return true;
+  }
+
   // Constant literal expressions are always trivial
   bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; }
   bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; }
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index cc40487614a83d..d08a997aa8c043 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -19,6 +19,7 @@ template  struct RefPtr {
   RefPtr(T *t) : t(t) {}
   T *get() { return t; }
   T *operator->() { return t; }
+  const T *operator->() const { return t; }
   T &operator*() { return *t; }
   RefPtr &operator=(T *) { return *this; }
   operator bool() { return t; }
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
index 156a2480901bf0..83c4414d1d01aa 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -1,7 +1,6 @@
 // RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
 
 #include "mock-types.h"
-//#include 
 
 void WTFBreakpointTrap();
 void WTFCrashWithInfo(int, const char*, const char*, int);
@@ -60,11 +59,86 @@ NO_RETURN_DUE_TO_CRASH ALWAYS_INLINE void 
WTFCrashWithInfo(int line, const char*
 WTFCrashWithInfoImpl(line, file, function, counter, wtfCrashArg(reason));
 }
 
+enum class Flags : unsigned short {
+  Flag1 = 1 << 0,
+  Flag2 = 1 << 1,
+  Flag3 = 1 << 2,
+};
+
+template class OptionSet {
+public:
+  using StorageType = unsigned short;
+
+  static constexpr OptionSet fromRaw(StorageType rawValue) {
+return OptionSet(static_cast(rawValue), FromRawValue);
+  }
+
+  constexpr OptionSet() = default;
+
+  constexpr OptionSet(E e)
+: m_storage(static_cast(e)) {
+  }
+
+  constexpr StorageType toRaw() const { return m_storage; }
+
+  constexpr bool isEmpty() const { return !m_storage; }
+
+  constexpr explicit operator bool() const { return !isEmpty(); }
+
+  constexpr bool contains(E option) const { return containsAny(option); }
+  constexpr bool containsAny(OptionSet optionSet) const {
+return !!(*this & optionSet);
+  }
+
+  constexpr bool containsAll(OptionSet optionSet) const {
+return (*this & optionSet) == optionSet;
+  }
+
+  constexpr void add(OptionSet optionSet) { m_storage |= optionSet.m_storage; }
+
+  constexpr void remove(OptionSet op

[clang] [alpha.webkit.UncountedCallArgsChecker] Detect more trivial functions (PR #81829)

2024-02-15 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa closed https://github.com/llvm/llvm-project/pull/81829
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and atomic operations in a trivial function (PR #82063)

2024-02-16 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/82063

None

>From ddaf7bf835668b761e82c25232f2e281a002ff78 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Fri, 16 Feb 2024 14:54:55 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and
 atomic operations in a trivial function

---
 .../Checkers/WebKit/ASTUtils.cpp  |  4 ++
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 18 +
 .../WebKit/UncountedCallArgsChecker.cpp   |  9 +
 .../call-args-protected-return-value.cpp  |  6 ++-
 .../Analysis/Checkers/WebKit/mock-types.h | 20 +-
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 39 +--
 6 files changed, 81 insertions(+), 15 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 94eaa81af51772..1a9d6d3127fb7f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -23,6 +23,10 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = tempExpr->getSubExpr();
   continue;
 }
+if (auto *tempExpr = dyn_cast(E)) {
+  E = tempExpr->getSubExpr();
+  continue;
+}
 if (auto *cast = dyn_cast(E)) {
   if (StopAtFirstRefCountedObj) {
 if (auto *ConversionFunc =
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 6f236db0474079..c713293924c45e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -315,6 +315,10 @@ class TrivialFunctionAnalysisVisitor
 return false;
   }
 
+  bool VisitAtomicExpr(const AtomicExpr* E) {
+return VisitChildren(E);
+  }
+
   bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) {
 // Any static_assert is considered trivial.
 return true;
@@ -330,12 +334,18 @@ class TrivialFunctionAnalysisVisitor
 const auto &Name = safeGetName(Callee);
 
 if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" ||
+Name == "WTFReportAssertionFailure" ||
 Name == "compilerFenceForCrash" || Name == "__builtin_unreachable")
   return true;
 
 return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache);
   }
 
+  bool VisitPredefinedExpr(const PredefinedExpr *E) {
+// A predefined identifier such as "func" is considered trivial.
+return true;
+  }
+
   bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
 if (!checkArguments(MCE))
   return false;
@@ -356,6 +366,14 @@ class TrivialFunctionAnalysisVisitor
 return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache);
   }
 
+  bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr* E) {
+if (auto *Expr = E->getExpr()) {
+  if (!Visit(Expr))
+return false;
+}
+return true;
+  }
+
   bool checkArguments(const CallExpr *CE) {
 for (const Expr *Arg : CE->arguments()) {
   if (Arg && !Visit(Arg))
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 17a64e1b1b8e04..1bed21d18ed74d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -53,6 +53,15 @@ class UncountedCallArgsChecker
   bool shouldVisitTemplateInstantiations() const { return true; }
   bool shouldVisitImplicitCode() const { return false; }
 
+  bool VisitCXXMethodDecl(const CXXMethodDecl *D) {
+if (auto* Class = D->getParent()) {
+  auto name = safeGetName(Class);
+  if (isRefCounted(Class))
+return false; // Don't visit contents of Ref/RefPtr methods.
+}
+return true;
+  }
+
   bool VisitCallExpr(const CallExpr *CE) {
 Checker->visitCallExpr(CE);
 return true;
diff --git 
a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
index 1c4b3df211b1e3..6a8b7464845a26 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
@@ -5,12 +5,14 @@
 
 class RefCounted {
 public:
-  void ref();
-  void deref();
+  void ref() const;
+  void deref() const;
 };
 
 class Object {
 public:
+  void ref() const;
+  void deref() const;
   void someFunction(RefCounted&);
 };
 
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index d08a997aa8c043..82db67bb031dd6 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -5,7 +5,15 @@ template  struct Ref {
   T *t;
 
   Ref() :

[clang] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and atomic operations in a trivial function (PR #82063)

2024-02-16 Thread Ryosuke Niwa via cfe-commits

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

>From 69a593fc7a7e0c18bd2545353772d5da5588c1bb Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Fri, 16 Feb 2024 14:54:55 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and
 atomic operations in a trivial function

This PR permits the use of WebKit's ASSERT macros as well as std::atomic 
operations to appear
within a trivial function. It also skips checkers on methods of Ref/RefPtr 
types.
---
 .../Checkers/WebKit/ASTUtils.cpp  |  4 ++
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 18 +
 .../WebKit/UncountedCallArgsChecker.cpp   |  9 +
 .../call-args-protected-return-value.cpp  |  6 ++-
 .../Analysis/Checkers/WebKit/mock-types.h | 20 +-
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 39 +--
 6 files changed, 81 insertions(+), 15 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 94eaa81af51772..1a9d6d3127fb7f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -23,6 +23,10 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = tempExpr->getSubExpr();
   continue;
 }
+if (auto *tempExpr = dyn_cast(E)) {
+  E = tempExpr->getSubExpr();
+  continue;
+}
 if (auto *cast = dyn_cast(E)) {
   if (StopAtFirstRefCountedObj) {
 if (auto *ConversionFunc =
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 6f236db0474079..c713293924c45e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -315,6 +315,10 @@ class TrivialFunctionAnalysisVisitor
 return false;
   }
 
+  bool VisitAtomicExpr(const AtomicExpr* E) {
+return VisitChildren(E);
+  }
+
   bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) {
 // Any static_assert is considered trivial.
 return true;
@@ -330,12 +334,18 @@ class TrivialFunctionAnalysisVisitor
 const auto &Name = safeGetName(Callee);
 
 if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" ||
+Name == "WTFReportAssertionFailure" ||
 Name == "compilerFenceForCrash" || Name == "__builtin_unreachable")
   return true;
 
 return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache);
   }
 
+  bool VisitPredefinedExpr(const PredefinedExpr *E) {
+// A predefined identifier such as "func" is considered trivial.
+return true;
+  }
+
   bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
 if (!checkArguments(MCE))
   return false;
@@ -356,6 +366,14 @@ class TrivialFunctionAnalysisVisitor
 return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache);
   }
 
+  bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr* E) {
+if (auto *Expr = E->getExpr()) {
+  if (!Visit(Expr))
+return false;
+}
+return true;
+  }
+
   bool checkArguments(const CallExpr *CE) {
 for (const Expr *Arg : CE->arguments()) {
   if (Arg && !Visit(Arg))
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 17a64e1b1b8e04..1bed21d18ed74d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -53,6 +53,15 @@ class UncountedCallArgsChecker
   bool shouldVisitTemplateInstantiations() const { return true; }
   bool shouldVisitImplicitCode() const { return false; }
 
+  bool VisitCXXMethodDecl(const CXXMethodDecl *D) {
+if (auto* Class = D->getParent()) {
+  auto name = safeGetName(Class);
+  if (isRefCounted(Class))
+return false; // Don't visit contents of Ref/RefPtr methods.
+}
+return true;
+  }
+
   bool VisitCallExpr(const CallExpr *CE) {
 Checker->visitCallExpr(CE);
 return true;
diff --git 
a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
index 1c4b3df211b1e3..6a8b7464845a26 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
@@ -5,12 +5,14 @@
 
 class RefCounted {
 public:
-  void ref();
-  void deref();
+  void ref() const;
+  void deref() const;
 };
 
 class Object {
 public:
+  void ref() const;
+  void deref() const;
   void someFunction(RefCounted&);
 };
 
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index d08a997aa8c043..82db67bb031dd6 100644

[clang] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and atomic operations in a trivial function (PR #82063)

2024-02-16 Thread Ryosuke Niwa via cfe-commits

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

>From 8d304967acfd0881a4844d630dab5f954772490c Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Fri, 16 Feb 2024 14:54:55 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and
 atomic operations in a trivial function

This PR permits the use of WebKit's ASSERT macros as well as std::atomic 
operations to appear
within a trivial function. Also exempt ref() and deref() member function calls.
---
 .../Checkers/WebKit/ASTUtils.cpp  |  4 ++
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 18 +
 .../WebKit/UncountedCallArgsChecker.cpp   |  5 +++
 .../call-args-protected-return-value.cpp  |  6 ++-
 .../Analysis/Checkers/WebKit/mock-types.h | 20 +-
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 39 +--
 6 files changed, 77 insertions(+), 15 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 94eaa81af51772..1a9d6d3127fb7f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -23,6 +23,10 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = tempExpr->getSubExpr();
   continue;
 }
+if (auto *tempExpr = dyn_cast(E)) {
+  E = tempExpr->getSubExpr();
+  continue;
+}
 if (auto *cast = dyn_cast(E)) {
   if (StopAtFirstRefCountedObj) {
 if (auto *ConversionFunc =
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 6f236db0474079..c713293924c45e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -315,6 +315,10 @@ class TrivialFunctionAnalysisVisitor
 return false;
   }
 
+  bool VisitAtomicExpr(const AtomicExpr* E) {
+return VisitChildren(E);
+  }
+
   bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) {
 // Any static_assert is considered trivial.
 return true;
@@ -330,12 +334,18 @@ class TrivialFunctionAnalysisVisitor
 const auto &Name = safeGetName(Callee);
 
 if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" ||
+Name == "WTFReportAssertionFailure" ||
 Name == "compilerFenceForCrash" || Name == "__builtin_unreachable")
   return true;
 
 return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache);
   }
 
+  bool VisitPredefinedExpr(const PredefinedExpr *E) {
+// A predefined identifier such as "func" is considered trivial.
+return true;
+  }
+
   bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
 if (!checkArguments(MCE))
   return false;
@@ -356,6 +366,14 @@ class TrivialFunctionAnalysisVisitor
 return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache);
   }
 
+  bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr* E) {
+if (auto *Expr = E->getExpr()) {
+  if (!Visit(Expr))
+return false;
+}
+return true;
+  }
+
   bool checkArguments(const CallExpr *CE) {
 for (const Expr *Arg : CE->arguments()) {
   if (Arg && !Visit(Arg))
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 17a64e1b1b8e04..8d344f9b63961a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -73,6 +73,11 @@ class UncountedCallArgsChecker
   unsigned ArgIdx = isa(CE) && 
isa_and_nonnull(F);
 
   if (auto *MemberCallExpr = dyn_cast(CE)) {
+if (auto *MD = MemberCallExpr->getMethodDecl()) {
+  auto name = safeGetName(MD);
+  if (name == "ref" || name == "deref")
+return;
+}
 auto *E = MemberCallExpr->getImplicitObjectArgument();
 QualType ArgType = MemberCallExpr->getObjectType();
 std::optional IsUncounted =
diff --git 
a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
index 1c4b3df211b1e3..6a8b7464845a26 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
@@ -5,12 +5,14 @@
 
 class RefCounted {
 public:
-  void ref();
-  void deref();
+  void ref() const;
+  void deref() const;
 };
 
 class Object {
 public:
+  void ref() const;
+  void deref() const;
   void someFunction(RefCounted&);
 };
 
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index d08a997aa8c043..82db67bb031dd6 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebK

[clang] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and atomic operations in a trivial function (PR #82063)

2024-02-16 Thread Ryosuke Niwa via cfe-commits

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

>From c46f11b98f8ec159a19004952fb67705eb0988dd Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Fri, 16 Feb 2024 14:54:55 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Allow ASSERT and
 atomic operations in a trivial function

This PR permits the use of WebKit's ASSERT macros as well as std::atomic 
operations to appear
within a trivial function. Also exempt ref() and deref() member function calls.
---
 .../Checkers/WebKit/ASTUtils.cpp  |  4 ++
 .../Checkers/WebKit/PtrTypesSemantics.cpp | 16 
 .../WebKit/UncountedCallArgsChecker.cpp   |  5 +++
 .../call-args-protected-return-value.cpp  |  6 ++-
 .../Analysis/Checkers/WebKit/mock-types.h | 20 +-
 .../Checkers/WebKit/uncounted-obj-arg.cpp | 39 +--
 6 files changed, 75 insertions(+), 15 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 94eaa81af51772..1a9d6d3127fb7f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -23,6 +23,10 @@ tryToFindPtrOrigin(const Expr *E, bool 
StopAtFirstRefCountedObj) {
   E = tempExpr->getSubExpr();
   continue;
 }
+if (auto *tempExpr = dyn_cast(E)) {
+  E = tempExpr->getSubExpr();
+  continue;
+}
 if (auto *cast = dyn_cast(E)) {
   if (StopAtFirstRefCountedObj) {
 if (auto *ConversionFunc =
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 6f236db0474079..a7891d2da07c18 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -315,6 +315,8 @@ class TrivialFunctionAnalysisVisitor
 return false;
   }
 
+  bool VisitAtomicExpr(const AtomicExpr *E) { return VisitChildren(E); }
+
   bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) {
 // Any static_assert is considered trivial.
 return true;
@@ -330,12 +332,18 @@ class TrivialFunctionAnalysisVisitor
 const auto &Name = safeGetName(Callee);
 
 if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" ||
+Name == "WTFReportAssertionFailure" ||
 Name == "compilerFenceForCrash" || Name == "__builtin_unreachable")
   return true;
 
 return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache);
   }
 
+  bool VisitPredefinedExpr(const PredefinedExpr *E) {
+// A predefined identifier such as "func" is considered trivial.
+return true;
+  }
+
   bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
 if (!checkArguments(MCE))
   return false;
@@ -356,6 +364,14 @@ class TrivialFunctionAnalysisVisitor
 return TrivialFunctionAnalysis::isTrivialImpl(Callee, Cache);
   }
 
+  bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) {
+if (auto *Expr = E->getExpr()) {
+  if (!Visit(Expr))
+return false;
+}
+return true;
+  }
+
   bool checkArguments(const CallExpr *CE) {
 for (const Expr *Arg : CE->arguments()) {
   if (Arg && !Visit(Arg))
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 17a64e1b1b8e04..8d344f9b63961a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -73,6 +73,11 @@ class UncountedCallArgsChecker
   unsigned ArgIdx = isa(CE) && 
isa_and_nonnull(F);
 
   if (auto *MemberCallExpr = dyn_cast(CE)) {
+if (auto *MD = MemberCallExpr->getMethodDecl()) {
+  auto name = safeGetName(MD);
+  if (name == "ref" || name == "deref")
+return;
+}
 auto *E = MemberCallExpr->getImplicitObjectArgument();
 QualType ArgType = MemberCallExpr->getObjectType();
 std::optional IsUncounted =
diff --git 
a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
index 1c4b3df211b1e3..6a8b7464845a26 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-protected-return-value.cpp
@@ -5,12 +5,14 @@
 
 class RefCounted {
 public:
-  void ref();
-  void deref();
+  void ref() const;
+  void deref() const;
 };
 
 class Object {
 public:
+  void ref() const;
+  void deref() const;
   void someFunction(RefCounted&);
 };
 
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index d08a997aa8c043..82db67bb031dd6 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-ty

[clang] [alpha.webkit.UncountedCallArgsChecker] Ignore calls to WTF's container methods (PR #82156)

2024-02-18 Thread Ryosuke Niwa via cfe-commits

https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/82156

This PR makes the checker ignore / skip calls to methods of Web Template 
Platform's container types such as HashMap, HashSet, WeakHashSet, WeakHashMap, 
Vector, etc...

>From 907c06c6675ae2ca06ec70e005966b2bdd7125c1 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Sun, 18 Feb 2024 01:32:00 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Ignore calls to WTF's
 container methods

This PR makes the checker ignore / skip calls to methods of Web Template 
Platform's container types
such as HashMap, HashSet, WeakHashSet, WeakHashMap, Vector, etc...
---
 .../WebKit/UncountedCallArgsChecker.cpp   |  32 
 .../WebKit/call-args-wtf-containers.cpp   | 146 ++
 2 files changed, 178 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 17a64e1b1b8e04..33e640f4d64654 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -25,6 +25,11 @@ using namespace ento;
 
 namespace {
 
+bool stringEndsWith(const std::string& str, const std::string& suffix) {
+  auto index = str.rfind(suffix);
+  return index != std::string::npos && str.size() - suffix.size() == index;
+}
+
 class UncountedCallArgsChecker
 : public Checker> {
   BugType Bug{this,
@@ -165,6 +170,9 @@ class UncountedCallArgsChecker
 if (!Callee)
   return false;
 
+if (isMethodOnWTFContainerType(Callee))
+  return true;
+
 auto overloadedOperatorType = Callee->getOverloadedOperator();
 if (overloadedOperatorType == OO_EqualEqual ||
 overloadedOperatorType == OO_ExclaimEqual ||
@@ -193,6 +201,30 @@ class UncountedCallArgsChecker
 return false;
   }
 
+  bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {
+if (!isa(Decl))
+  return false;
+auto *ClassDecl = Decl->getParent();
+if (!ClassDecl || !isa(ClassDecl))
+  return false;
+
+auto *NsDecl = ClassDecl->getParent();
+if (!NsDecl || !isa(NsDecl))
+  return false;
+
+auto methodName = safeGetName(Decl);
+auto clsName = safeGetName(ClassDecl);
+auto nsName = safeGetName(NsDecl);
+// FIXME: These should be implemented via attributes.
+return nsName == "WTF" &&
+  (methodName == "find" || methodName == "findIf" ||
+  methodName == "reverseFind" || methodName == "reverseFindIf" ||
+  methodName == "get" || methodName == "inlineGet" ||
+  methodName == "contains" || methodName == "containsIf") &&
+  (stringEndsWith(clsName, "Vector") || stringEndsWith(clsName, "Set") ||
+  stringEndsWith(clsName, "Map"));
+  }
+
   void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
 assert(CallArg);
 
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp
new file mode 100644
index 00..0a63a789856127
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp
@@ -0,0 +1,146 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+
+#include "mock-types.h"
+
+namespace WTF {
+
+  template 
+  class HashSet {
+  public:
+template  T* find(U&) const;
+template  bool contains(U&) const;
+unsigned size() { return m_size; }
+template  void add(U&) const;
+template  void remove(U&) const;
+
+  private:
+T* m_table { nullptr };
+unsigned m_size { 0 };
+  };
+
+  template 
+  class HashMap {
+  public:
+struct Item {
+  T key;
+  S value;
+};
+
+template  Item* find(U&) const;
+template  bool contains(U&) const;
+template  S* get(U&) const;
+template  S* inlineGet(U&) const;
+template  void add(U&) const;
+template  void remove(U&) const;
+
+  private:
+Item* m_table { nullptr };
+  };
+
+  template 
+  class WeakHashSet {
+  public:
+template  T* find(U&) const;
+template  bool contains(U&) const;
+template  void add(U&) const;
+template  void remove(U&) const;
+  };
+
+  template 
+  class Vector {
+  public:
+unsigned size() { return m_size; }
+T& at(unsigned i) { return m_buffer[i]; }
+T& operator[](unsigned i) { return m_buffer[i]; }
+template  unsigned find(U&);
+template  unsigned reverseFind(U&);
+template  bool contains(U&);
+template  unsigned findIf(const MatchFunction& 
match)
+{
+  for (unsigned i = 0; i < m_size; ++i) {
+if (match(at(i)))
+  return i;
+  }
+  return static_cast(-1);
+}
+template  unsigned reverseFindIf(const 
MatchFunction& match)
+{
+  for (unsigned i = 0; i < m_size; ++i) {
+if (match(at(m_size

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

2024-02-18 Thread Ryosuke Niwa via cfe-commits

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

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.

>From d26f41816f443922569fc713d1cd919fd96ba124 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
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  | 46 +++
 3 files changed, 62 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..1b349d48a9f881 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -103,15 +103,18 @@ std::optional 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()) {
   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 isGetterOfRefCounted(const 
CXXMethodDecl* M)
   if (isa(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 conversion
 // FIXME: Currently allowing any Ref -> whatever cast.
-if (className == "Ref" || className == "RefPtr") {
+if (isRefType(className)) {
   if (auto *maybeRefToRawOperator = dyn_cast(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  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 00..f9c0f405968231
--- /dev/null
+++ b/clang/te

[clang] [alpha.webkit.UncountedCallArgsChecker] Ignore calls to WTF's container methods (PR #82156)

2024-02-18 Thread Ryosuke Niwa via cfe-commits

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

>From 51b65a767eee7bae07932c073702502a701d4ef2 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Sun, 18 Feb 2024 01:32:00 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Ignore calls to WTF's
 container methods

This PR makes the checker ignore / skip calls to methods of Web Template 
Framework's container types
such as HashMap, HashSet, WeakHashSet, WeakHashMap, Vector, etc...
---
 .../WebKit/UncountedCallArgsChecker.cpp   |  32 
 .../WebKit/call-args-wtf-containers.cpp   | 146 ++
 2 files changed, 178 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 17a64e1b1b8e04..0fed1e2c481332 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -25,6 +25,11 @@ using namespace ento;
 
 namespace {
 
+bool stringEndsWith(const std::string& str, const std::string &suffix) {
+  auto index = str.rfind(suffix);
+  return index != std::string::npos && str.size() - suffix.size() == index;
+}
+
 class UncountedCallArgsChecker
 : public Checker> {
   BugType Bug{this,
@@ -165,6 +170,9 @@ class UncountedCallArgsChecker
 if (!Callee)
   return false;
 
+if (isMethodOnWTFContainerType(Callee))
+  return true;
+
 auto overloadedOperatorType = Callee->getOverloadedOperator();
 if (overloadedOperatorType == OO_EqualEqual ||
 overloadedOperatorType == OO_ExclaimEqual ||
@@ -193,6 +201,30 @@ class UncountedCallArgsChecker
 return false;
   }
 
+  bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {
+if (!isa(Decl))
+  return false;
+auto *ClassDecl = Decl->getParent();
+if (!ClassDecl || !isa(ClassDecl))
+  return false;
+
+auto *NsDecl = ClassDecl->getParent();
+if (!NsDecl || !isa(NsDecl))
+  return false;
+
+auto methodName = safeGetName(Decl);
+auto clsName = safeGetName(ClassDecl);
+auto nsName = safeGetName(NsDecl);
+// FIXME: These should be implemented via attributes.
+return nsName == "WTF" &&
+   (methodName == "find" || methodName == "findIf" ||
+methodName == "reverseFind" || methodName == "reverseFindIf" ||
+methodName == "get" || methodName == "inlineGet" ||
+methodName == "contains" || methodName == "containsIf") &&
+   (stringEndsWith(clsName, "Vector") ||
+stringEndsWith(clsName, "Set") || stringEndsWith(clsName, "Map"));
+  }
+
   void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
 assert(CallArg);
 
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp
new file mode 100644
index 00..0a63a789856127
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp
@@ -0,0 +1,146 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+
+#include "mock-types.h"
+
+namespace WTF {
+
+  template 
+  class HashSet {
+  public:
+template  T* find(U&) const;
+template  bool contains(U&) const;
+unsigned size() { return m_size; }
+template  void add(U&) const;
+template  void remove(U&) const;
+
+  private:
+T* m_table { nullptr };
+unsigned m_size { 0 };
+  };
+
+  template 
+  class HashMap {
+  public:
+struct Item {
+  T key;
+  S value;
+};
+
+template  Item* find(U&) const;
+template  bool contains(U&) const;
+template  S* get(U&) const;
+template  S* inlineGet(U&) const;
+template  void add(U&) const;
+template  void remove(U&) const;
+
+  private:
+Item* m_table { nullptr };
+  };
+
+  template 
+  class WeakHashSet {
+  public:
+template  T* find(U&) const;
+template  bool contains(U&) const;
+template  void add(U&) const;
+template  void remove(U&) const;
+  };
+
+  template 
+  class Vector {
+  public:
+unsigned size() { return m_size; }
+T& at(unsigned i) { return m_buffer[i]; }
+T& operator[](unsigned i) { return m_buffer[i]; }
+template  unsigned find(U&);
+template  unsigned reverseFind(U&);
+template  bool contains(U&);
+template  unsigned findIf(const MatchFunction& 
match)
+{
+  for (unsigned i = 0; i < m_size; ++i) {
+if (match(at(i)))
+  return i;
+  }
+  return static_cast(-1);
+}
+template  unsigned reverseFindIf(const 
MatchFunction& match)
+{
+  for (unsigned i = 0; i < m_size; ++i) {
+if (match(at(m_size - i)))
+  return i;
+  }
+  return static_cast(-1);
+}
+template  bool containsIf(const MatchFunction& 
match)

[clang] [alpha.webkit.UncountedCallArgsChecker] Ignore calls to WTF's container methods (PR #82156)

2024-02-18 Thread Ryosuke Niwa via cfe-commits

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

>From d169fddf3896bd334bc4776059258116ac08096a Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa 
Date: Sun, 18 Feb 2024 01:32:00 -0800
Subject: [PATCH] [alpha.webkit.UncountedCallArgsChecker] Ignore calls to WTF's
 container methods

This PR makes the checker ignore / skip calls to methods of Web Template 
Framework's container types
such as HashMap, HashSet, WeakHashSet, WeakHashMap, Vector, etc...
---
 .../WebKit/UncountedCallArgsChecker.cpp   |  32 
 .../WebKit/call-args-wtf-containers.cpp   | 146 ++
 2 files changed, 178 insertions(+)
 create mode 100644 
clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 17a64e1b1b8e04..9ed7f2a4e065d3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -25,6 +25,11 @@ using namespace ento;
 
 namespace {
 
+bool stringEndsWith(const std::string &str, const std::string &suffix) {
+  auto index = str.rfind(suffix);
+  return index != std::string::npos && str.size() - suffix.size() == index;
+}
+
 class UncountedCallArgsChecker
 : public Checker> {
   BugType Bug{this,
@@ -165,6 +170,9 @@ class UncountedCallArgsChecker
 if (!Callee)
   return false;
 
+if (isMethodOnWTFContainerType(Callee))
+  return true;
+
 auto overloadedOperatorType = Callee->getOverloadedOperator();
 if (overloadedOperatorType == OO_EqualEqual ||
 overloadedOperatorType == OO_ExclaimEqual ||
@@ -193,6 +201,30 @@ class UncountedCallArgsChecker
 return false;
   }
 
+  bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const {
+if (!isa(Decl))
+  return false;
+auto *ClassDecl = Decl->getParent();
+if (!ClassDecl || !isa(ClassDecl))
+  return false;
+
+auto *NsDecl = ClassDecl->getParent();
+if (!NsDecl || !isa(NsDecl))
+  return false;
+
+auto methodName = safeGetName(Decl);
+auto clsName = safeGetName(ClassDecl);
+auto nsName = safeGetName(NsDecl);
+// FIXME: These should be implemented via attributes.
+return nsName == "WTF" &&
+   (methodName == "find" || methodName == "findIf" ||
+methodName == "reverseFind" || methodName == "reverseFindIf" ||
+methodName == "get" || methodName == "inlineGet" ||
+methodName == "contains" || methodName == "containsIf") &&
+   (stringEndsWith(clsName, "Vector") ||
+stringEndsWith(clsName, "Set") || stringEndsWith(clsName, "Map"));
+  }
+
   void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
 assert(CallArg);
 
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp
new file mode 100644
index 00..0a63a789856127
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp
@@ -0,0 +1,146 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UncountedCallArgsChecker -verify %s
+
+#include "mock-types.h"
+
+namespace WTF {
+
+  template 
+  class HashSet {
+  public:
+template  T* find(U&) const;
+template  bool contains(U&) const;
+unsigned size() { return m_size; }
+template  void add(U&) const;
+template  void remove(U&) const;
+
+  private:
+T* m_table { nullptr };
+unsigned m_size { 0 };
+  };
+
+  template 
+  class HashMap {
+  public:
+struct Item {
+  T key;
+  S value;
+};
+
+template  Item* find(U&) const;
+template  bool contains(U&) const;
+template  S* get(U&) const;
+template  S* inlineGet(U&) const;
+template  void add(U&) const;
+template  void remove(U&) const;
+
+  private:
+Item* m_table { nullptr };
+  };
+
+  template 
+  class WeakHashSet {
+  public:
+template  T* find(U&) const;
+template  bool contains(U&) const;
+template  void add(U&) const;
+template  void remove(U&) const;
+  };
+
+  template 
+  class Vector {
+  public:
+unsigned size() { return m_size; }
+T& at(unsigned i) { return m_buffer[i]; }
+T& operator[](unsigned i) { return m_buffer[i]; }
+template  unsigned find(U&);
+template  unsigned reverseFind(U&);
+template  bool contains(U&);
+template  unsigned findIf(const MatchFunction& 
match)
+{
+  for (unsigned i = 0; i < m_size; ++i) {
+if (match(at(i)))
+  return i;
+  }
+  return static_cast(-1);
+}
+template  unsigned reverseFindIf(const 
MatchFunction& match)
+{
+  for (unsigned i = 0; i < m_size; ++i) {
+if (match(at(m_size - i)))
+  return i;
+  }
+  return static_cast(-1);
+}
+template  bool containsIf(const MatchFunction& 
match)

  1   2   3   4   5   6   7   >