https://github.com/AZero13 created 
https://github.com/llvm/llvm-project/pull/206378

If the receiver is a class reference, use the class method.

>From 5929e8727b4df0250a178073eb9cf988e6692ba7 Mon Sep 17 00:00:00 2001
From: AZero13 <[email protected]>
Date: Sun, 28 Jun 2026 19:21:44 -0400
Subject: [PATCH] [Clang] Resolve FIXME: Use class method when receiver is
 reference to class

If the receiver is a class reference, use the class method.
---
 .../clang/Analysis/RetainSummaryManager.h     |  6 ++--
 clang/lib/Analysis/RetainSummaryManager.cpp   | 29 +++++++++++++------
 .../RetainCountChecker/RetainCountChecker.cpp | 18 ++++++++----
 .../Analysis/retain-release-class-method.m    | 14 +++++++++
 4 files changed, 50 insertions(+), 17 deletions(-)
 create mode 100644 clang/test/Analysis/retain-release-class-method.m

diff --git a/clang/include/clang/Analysis/RetainSummaryManager.h 
b/clang/include/clang/Analysis/RetainSummaryManager.h
index 797685ac7f2ef..7b787eb9645ae 100644
--- a/clang/include/clang/Analysis/RetainSummaryManager.h
+++ b/clang/include/clang/Analysis/RetainSummaryManager.h
@@ -652,7 +652,8 @@ class RetainSummaryManager {
   const RetainSummary *getSummary(AnyCall C,
                                   bool HasNonZeroCallbackArg=false,
                                   bool IsReceiverUnconsumedSelf=false,
-                                  QualType ReceiverType={});
+                                  QualType ReceiverType={},
+                                  bool ReceiverIsClassObj=false);
 
   RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
 
@@ -670,7 +671,8 @@ class RetainSummaryManager {
                                         ObjCMethodSummariesTy 
&CachedSummaries);
 
   const RetainSummary *
-  getInstanceMethodSummary(const ObjCMessageExpr *ME, QualType ReceiverType);
+  getInstanceMethodSummary(const ObjCMessageExpr *ME, QualType ReceiverType,
+                           bool ReceiverIsClassObj = false);
 
   const RetainSummary *getClassMethodSummary(const ObjCMessageExpr *ME);
 
diff --git a/clang/lib/Analysis/RetainSummaryManager.cpp 
b/clang/lib/Analysis/RetainSummaryManager.cpp
index b622027ee7e1e..ecada3bb22b41 100644
--- a/clang/lib/Analysis/RetainSummaryManager.cpp
+++ b/clang/lib/Analysis/RetainSummaryManager.cpp
@@ -659,7 +659,8 @@ const RetainSummary *
 RetainSummaryManager::getSummary(AnyCall C,
                                  bool HasNonZeroCallbackArg,
                                  bool IsReceiverUnconsumedSelf,
-                                 QualType ReceiverType) {
+                                 QualType ReceiverType,
+                                 bool ReceiverIsClassObj) {
   const RetainSummary *Summ;
   switch (C.getKind()) {
   case AnyCall::Function:
@@ -678,7 +679,7 @@ RetainSummaryManager::getSummary(AnyCall C,
     if (!ME) {
       Summ = getMethodSummary(cast<ObjCMethodDecl>(C.getDecl()));
     } else if (ME->isInstanceMessage()) {
-      Summ = getInstanceMethodSummary(ME, ReceiverType);
+      Summ = getInstanceMethodSummary(ME, ReceiverType, ReceiverIsClassObj);
     } else {
       Summ = getClassMethodSummary(ME);
     }
@@ -1123,7 +1124,8 @@ RetainSummaryManager::getClassMethodSummary(const 
ObjCMessageExpr *ME) {
 
 const RetainSummary *RetainSummaryManager::getInstanceMethodSummary(
     const ObjCMessageExpr *ME,
-    QualType ReceiverType) {
+    QualType ReceiverType,
+    bool ReceiverIsClassObj) {
   const ObjCInterfaceDecl *ReceiverClass = nullptr;
 
   // We do better tracking of the type of the object than the core ExprEngine.
@@ -1133,17 +1135,26 @@ const RetainSummary 
*RetainSummaryManager::getInstanceMethodSummary(
       ReceiverClass = PT->getInterfaceDecl();
 
   // If we don't know what kind of object this is, fall back to its static 
type.
-  if (!ReceiverClass)
+  if (!ReceiverClass) {
     ReceiverClass = ME->getReceiverInterface();
+    if (ME->getReceiverType()->isObjCClassType())
+      ReceiverIsClassObj = true;
+  }
 
-  // FIXME: The receiver could be a reference to a class, meaning that
-  //  we should use the class method.
-  // id x = [NSObject class];
-  // [x performSelector:... withObject:... afterDelay:...];
   Selector S = ME->getSelector();
   const ObjCMethodDecl *Method = ME->getMethodDecl();
-  if (!Method && ReceiverClass)
+  if (ReceiverIsClassObj) {
+    if (ReceiverClass)
+      Method = ReceiverClass->getClassMethod(S);
+    else if (Method && Method->isInstanceMethod())
+      Method = nullptr;
+  } else if (!Method && ReceiverClass) {
     Method = ReceiverClass->getInstanceMethod(S);
+  }
+
+  if (ReceiverIsClassObj)
+    return getMethodSummary(S, ReceiverClass, Method, ME->getType(),
+                            ObjCClassMethodSummaries);
 
   return getMethodSummary(S, ReceiverClass, Method, ME->getType(),
                           ObjCMethodSummaries);
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
index 1ebf4787da67c..86f54de92ac6f 100644
--- 
a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
+++ 
b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
@@ -13,6 +13,7 @@
 
 #include "RetainCountChecker.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
 #include <optional>
 
 using namespace clang;
@@ -347,15 +348,16 @@ static bool isReceiverUnconsumedSelf(const CallEvent 
&Call) {
   return false;
 }
 
-const static RetainSummary *getSummary(RetainSummaryManager &Summaries,
-                                       const CallEvent &Call,
-                                       QualType ReceiverType) {
+static const RetainSummary *
+getSummary(RetainSummaryManager &Summaries, const CallEvent &Call,
+           QualType ReceiverType, bool ReceiverIsClassObj = false) {
   const Expr *CE = Call.getOriginExpr();
   AnyCall C =
       CE ? *AnyCall::forExpr(CE)
          : AnyCall(cast<CXXDestructorDecl>(Call.getDecl()));
   return Summaries.getSummary(C, Call.hasNonZeroCallbackArg(),
-                              isReceiverUnconsumedSelf(Call), ReceiverType);
+                              isReceiverUnconsumedSelf(Call), ReceiverType,
+                              ReceiverIsClassObj);
 }
 
 void RetainCountChecker::checkPostCall(const CallEvent &Call,
@@ -364,16 +366,20 @@ void RetainCountChecker::checkPostCall(const CallEvent 
&Call,
 
   // Leave null if no receiver.
   QualType ReceiverType;
+  bool ReceiverIsClassObj = false;
   if (const auto *MC = dyn_cast<ObjCMethodCall>(&Call)) {
     if (MC->isInstanceMessage()) {
       SVal ReceiverV = MC->getReceiverSVal();
-      if (SymbolRef Sym = ReceiverV.getAsLocSymbol())
+      if (SymbolRef Sym = ReceiverV.getAsLocSymbol()) {
         if (const RefVal *T = getRefBinding(C.getState(), Sym))
           ReceiverType = T->getType();
+        if (getClassObjectDynamicTypeInfo(C.getState(), Sym))
+          ReceiverIsClassObj = true;
+      }
     }
   }
 
-  const RetainSummary *Summ = getSummary(Summaries, Call, ReceiverType);
+  const RetainSummary *Summ = getSummary(Summaries, Call, ReceiverType, 
ReceiverIsClassObj);
 
   if (C.wasInlined) {
     processSummaryOfInlined(*Summ, Call, C);
diff --git a/clang/test/Analysis/retain-release-class-method.m 
b/clang/test/Analysis/retain-release-class-method.m
new file mode 100644
index 0000000000000..f62593186dac5
--- /dev/null
+++ b/clang/test/Analysis/retain-release-class-method.m
@@ -0,0 +1,14 @@
+// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 
-analyzer-checker=core,osx.cocoa.RetainCount -verify %s
+
+@interface NSObject
++ (id)class;
++ (id)alloc;
+- (id)init;
+- (void)release;
+- (id)performSelector:(SEL)aSelector;
+@end
+
+void testClassMethodRetained() {
+  id x = [NSObject class];
+  id y = [x alloc]; // expected-warning{{Potential leak of an object}}
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to