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
