[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:24:12 PDT 2026
https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/206378
>From 0ef34877a9c35f4776b7998b1152038008f102f8 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 | 11 +++---
clang/lib/Analysis/RetainSummaryManager.cpp | 35 +++++++++++--------
.../RetainCountChecker/RetainCountChecker.cpp | 18 +++++++---
.../Analysis/retain-release-class-method.m | 14 ++++++++
4 files changed, 53 insertions(+), 25 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..a0f3967998653 100644
--- a/clang/include/clang/Analysis/RetainSummaryManager.h
+++ b/clang/include/clang/Analysis/RetainSummaryManager.h
@@ -649,10 +649,10 @@ class RetainSummaryManager {
bool isTrustedReferenceCountImplementation(const Decl *FD);
- const RetainSummary *getSummary(AnyCall C,
- bool HasNonZeroCallbackArg=false,
- bool IsReceiverUnconsumedSelf=false,
- QualType ReceiverType={});
+ const RetainSummary *getSummary(AnyCall C, bool HasNonZeroCallbackArg = false,
+ bool IsReceiverUnconsumedSelf = false,
+ QualType ReceiverType = {},
+ bool ReceiverIsClassObj = false);
RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
@@ -670,7 +670,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..c7a438141449d 100644
--- a/clang/lib/Analysis/RetainSummaryManager.cpp
+++ b/clang/lib/Analysis/RetainSummaryManager.cpp
@@ -655,11 +655,9 @@ void RetainSummaryManager::updateSummaryForArgumentTypes(
}
}
-const RetainSummary *
-RetainSummaryManager::getSummary(AnyCall C,
- bool HasNonZeroCallbackArg,
- bool IsReceiverUnconsumedSelf,
- QualType ReceiverType) {
+const RetainSummary *RetainSummaryManager::getSummary(
+ AnyCall C, bool HasNonZeroCallbackArg, bool IsReceiverUnconsumedSelf,
+ QualType ReceiverType, bool ReceiverIsClassObj) {
const RetainSummary *Summ;
switch (C.getKind()) {
case AnyCall::Function:
@@ -678,7 +676,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);
}
@@ -698,7 +696,6 @@ RetainSummaryManager::getSummary(AnyCall C,
return Summ;
}
-
const RetainSummary *
RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) {
if (coreFoundation::followsCreateRule(FD))
@@ -1122,8 +1119,7 @@ RetainSummaryManager::getClassMethodSummary(const ObjCMessageExpr *ME) {
}
const RetainSummary *RetainSummaryManager::getInstanceMethodSummary(
- const ObjCMessageExpr *ME,
- QualType ReceiverType) {
+ const ObjCMessageExpr *ME, QualType ReceiverType, bool ReceiverIsClassObj) {
const ObjCInterfaceDecl *ReceiverClass = nullptr;
// We do better tracking of the type of the object than the core ExprEngine.
@@ -1133,17 +1129,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..2df0d9f153c32 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,17 @@ static bool isReceiverUnconsumedSelf(const CallEvent &Call) {
return false;
}
-const static RetainSummary *getSummary(RetainSummaryManager &Summaries,
+static const RetainSummary *getSummary(RetainSummaryManager &Summaries,
const CallEvent &Call,
- QualType ReceiverType) {
+ 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 +367,21 @@ 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