[clang] [Clang] Resolve FIXME: Use class method when receiver is reference to class (PR #206378)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Jun 28 16:22:17 PDT 2026
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 <gfunni234 at gmail.com>
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
+
+ at interface NSObject
++ (id)class;
++ (id)alloc;
+- (id)init;
+- (void)release;
+- (id)performSelector:(SEL)aSelector;
+ at end
+
+void testClassMethodRetained() {
+ id x = [NSObject class];
+ id y = [x alloc]; // expected-warning{{Potential leak of an object}}
+}
More information about the cfe-commits
mailing list