16fdd2bd5SGeorge Karpenkov //== RetainSummaryManager.cpp - Summaries for reference counting --*- C++ -*--//
26fdd2bd5SGeorge Karpenkov //
36fdd2bd5SGeorge Karpenkov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
46fdd2bd5SGeorge Karpenkov // See https://llvm.org/LICENSE.txt for license information.
56fdd2bd5SGeorge Karpenkov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66fdd2bd5SGeorge Karpenkov //
76fdd2bd5SGeorge Karpenkov //===----------------------------------------------------------------------===//
86fdd2bd5SGeorge Karpenkov //
96fdd2bd5SGeorge Karpenkov // This file defines summaries implementation for retain counting, which
106fdd2bd5SGeorge Karpenkov // implements a reference count checker for Core Foundation, Cocoa
116fdd2bd5SGeorge Karpenkov // and OSObject (on Mac OS X).
126fdd2bd5SGeorge Karpenkov //
136fdd2bd5SGeorge Karpenkov //===----------------------------------------------------------------------===//
146fdd2bd5SGeorge Karpenkov
156fdd2bd5SGeorge Karpenkov #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
166fdd2bd5SGeorge Karpenkov #include "clang/Analysis/RetainSummaryManager.h"
176fdd2bd5SGeorge Karpenkov #include "clang/AST/Attr.h"
186fdd2bd5SGeorge Karpenkov #include "clang/AST/DeclCXX.h"
196fdd2bd5SGeorge Karpenkov #include "clang/AST/DeclObjC.h"
206fdd2bd5SGeorge Karpenkov #include "clang/AST/ParentMap.h"
216fdd2bd5SGeorge Karpenkov #include "clang/ASTMatchers/ASTMatchFinder.h"
22a1580d7bSKazu Hirata #include <optional>
236fdd2bd5SGeorge Karpenkov
246fdd2bd5SGeorge Karpenkov using namespace clang;
256fdd2bd5SGeorge Karpenkov using namespace ento;
266fdd2bd5SGeorge Karpenkov
276fdd2bd5SGeorge Karpenkov template <class T>
isOneOf()286fdd2bd5SGeorge Karpenkov constexpr static bool isOneOf() {
296fdd2bd5SGeorge Karpenkov return false;
306fdd2bd5SGeorge Karpenkov }
316fdd2bd5SGeorge Karpenkov
326fdd2bd5SGeorge Karpenkov /// Helper function to check whether the class is one of the
336fdd2bd5SGeorge Karpenkov /// rest of varargs.
346fdd2bd5SGeorge Karpenkov template <class T, class P, class... ToCompare>
isOneOf()356fdd2bd5SGeorge Karpenkov constexpr static bool isOneOf() {
36108e41d9SNathan James return std::is_same_v<T, P> || isOneOf<T, ToCompare...>();
376fdd2bd5SGeorge Karpenkov }
386fdd2bd5SGeorge Karpenkov
396fdd2bd5SGeorge Karpenkov namespace {
406fdd2bd5SGeorge Karpenkov
416fdd2bd5SGeorge Karpenkov /// Fake attribute class for RC* attributes.
426fdd2bd5SGeorge Karpenkov struct GeneralizedReturnsRetainedAttr {
classof__anone71418ec0111::GeneralizedReturnsRetainedAttr436fdd2bd5SGeorge Karpenkov static bool classof(const Attr *A) {
446fdd2bd5SGeorge Karpenkov if (auto AA = dyn_cast<AnnotateAttr>(A))
456fdd2bd5SGeorge Karpenkov return AA->getAnnotation() == "rc_ownership_returns_retained";
466fdd2bd5SGeorge Karpenkov return false;
476fdd2bd5SGeorge Karpenkov }
486fdd2bd5SGeorge Karpenkov };
496fdd2bd5SGeorge Karpenkov
506fdd2bd5SGeorge Karpenkov struct GeneralizedReturnsNotRetainedAttr {
classof__anone71418ec0111::GeneralizedReturnsNotRetainedAttr516fdd2bd5SGeorge Karpenkov static bool classof(const Attr *A) {
526fdd2bd5SGeorge Karpenkov if (auto AA = dyn_cast<AnnotateAttr>(A))
536fdd2bd5SGeorge Karpenkov return AA->getAnnotation() == "rc_ownership_returns_not_retained";
546fdd2bd5SGeorge Karpenkov return false;
556fdd2bd5SGeorge Karpenkov }
566fdd2bd5SGeorge Karpenkov };
576fdd2bd5SGeorge Karpenkov
586fdd2bd5SGeorge Karpenkov struct GeneralizedConsumedAttr {
classof__anone71418ec0111::GeneralizedConsumedAttr596fdd2bd5SGeorge Karpenkov static bool classof(const Attr *A) {
606fdd2bd5SGeorge Karpenkov if (auto AA = dyn_cast<AnnotateAttr>(A))
616fdd2bd5SGeorge Karpenkov return AA->getAnnotation() == "rc_ownership_consumed";
626fdd2bd5SGeorge Karpenkov return false;
636fdd2bd5SGeorge Karpenkov }
646fdd2bd5SGeorge Karpenkov };
656fdd2bd5SGeorge Karpenkov
666fdd2bd5SGeorge Karpenkov }
676fdd2bd5SGeorge Karpenkov
686fdd2bd5SGeorge Karpenkov template <class T>
hasAnyEnabledAttrOf(const Decl * D,QualType QT)696ad0788cSKazu Hirata std::optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D,
706fdd2bd5SGeorge Karpenkov QualType QT) {
716fdd2bd5SGeorge Karpenkov ObjKind K;
726fdd2bd5SGeorge Karpenkov if (isOneOf<T, CFConsumedAttr, CFReturnsRetainedAttr,
736fdd2bd5SGeorge Karpenkov CFReturnsNotRetainedAttr>()) {
746fdd2bd5SGeorge Karpenkov if (!TrackObjCAndCFObjects)
7534e0d057SKazu Hirata return std::nullopt;
766fdd2bd5SGeorge Karpenkov
776fdd2bd5SGeorge Karpenkov K = ObjKind::CF;
786fdd2bd5SGeorge Karpenkov } else if (isOneOf<T, NSConsumedAttr, NSConsumesSelfAttr,
796fdd2bd5SGeorge Karpenkov NSReturnsAutoreleasedAttr, NSReturnsRetainedAttr,
806fdd2bd5SGeorge Karpenkov NSReturnsNotRetainedAttr, NSConsumesSelfAttr>()) {
816fdd2bd5SGeorge Karpenkov
826fdd2bd5SGeorge Karpenkov if (!TrackObjCAndCFObjects)
8334e0d057SKazu Hirata return std::nullopt;
846fdd2bd5SGeorge Karpenkov
856fdd2bd5SGeorge Karpenkov if (isOneOf<T, NSReturnsRetainedAttr, NSReturnsAutoreleasedAttr,
866fdd2bd5SGeorge Karpenkov NSReturnsNotRetainedAttr>() &&
876fdd2bd5SGeorge Karpenkov !cocoa::isCocoaObjectRef(QT))
8834e0d057SKazu Hirata return std::nullopt;
896fdd2bd5SGeorge Karpenkov K = ObjKind::ObjC;
906fdd2bd5SGeorge Karpenkov } else if (isOneOf<T, OSConsumedAttr, OSConsumesThisAttr,
916fdd2bd5SGeorge Karpenkov OSReturnsNotRetainedAttr, OSReturnsRetainedAttr,
926fdd2bd5SGeorge Karpenkov OSReturnsRetainedOnZeroAttr,
936fdd2bd5SGeorge Karpenkov OSReturnsRetainedOnNonZeroAttr>()) {
946fdd2bd5SGeorge Karpenkov if (!TrackOSObjects)
9534e0d057SKazu Hirata return std::nullopt;
966fdd2bd5SGeorge Karpenkov K = ObjKind::OS;
976fdd2bd5SGeorge Karpenkov } else if (isOneOf<T, GeneralizedReturnsNotRetainedAttr,
986fdd2bd5SGeorge Karpenkov GeneralizedReturnsRetainedAttr,
996fdd2bd5SGeorge Karpenkov GeneralizedConsumedAttr>()) {
1006fdd2bd5SGeorge Karpenkov K = ObjKind::Generalized;
1016fdd2bd5SGeorge Karpenkov } else {
1026fdd2bd5SGeorge Karpenkov llvm_unreachable("Unexpected attribute");
1036fdd2bd5SGeorge Karpenkov }
1046fdd2bd5SGeorge Karpenkov if (D->hasAttr<T>())
1056fdd2bd5SGeorge Karpenkov return K;
10634e0d057SKazu Hirata return std::nullopt;
1076fdd2bd5SGeorge Karpenkov }
1086fdd2bd5SGeorge Karpenkov
1096fdd2bd5SGeorge Karpenkov template <class T1, class T2, class... Others>
hasAnyEnabledAttrOf(const Decl * D,QualType QT)1106ad0788cSKazu Hirata std::optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D,
1116fdd2bd5SGeorge Karpenkov QualType QT) {
1126fdd2bd5SGeorge Karpenkov if (auto Out = hasAnyEnabledAttrOf<T1>(D, QT))
1136fdd2bd5SGeorge Karpenkov return Out;
1146fdd2bd5SGeorge Karpenkov return hasAnyEnabledAttrOf<T2, Others...>(D, QT);
1156fdd2bd5SGeorge Karpenkov }
1166fdd2bd5SGeorge Karpenkov
1176fdd2bd5SGeorge Karpenkov const RetainSummary *
getPersistentSummary(const RetainSummary & OldSumm)1186fdd2bd5SGeorge Karpenkov RetainSummaryManager::getPersistentSummary(const RetainSummary &OldSumm) {
1196fdd2bd5SGeorge Karpenkov // Unique "simple" summaries -- those without ArgEffects.
1206fdd2bd5SGeorge Karpenkov if (OldSumm.isSimple()) {
1216fdd2bd5SGeorge Karpenkov ::llvm::FoldingSetNodeID ID;
1226fdd2bd5SGeorge Karpenkov OldSumm.Profile(ID);
1236fdd2bd5SGeorge Karpenkov
1246fdd2bd5SGeorge Karpenkov void *Pos;
1256fdd2bd5SGeorge Karpenkov CachedSummaryNode *N = SimpleSummaries.FindNodeOrInsertPos(ID, Pos);
1266fdd2bd5SGeorge Karpenkov
1276fdd2bd5SGeorge Karpenkov if (!N) {
1286fdd2bd5SGeorge Karpenkov N = (CachedSummaryNode *) BPAlloc.Allocate<CachedSummaryNode>();
1296fdd2bd5SGeorge Karpenkov new (N) CachedSummaryNode(OldSumm);
1306fdd2bd5SGeorge Karpenkov SimpleSummaries.InsertNode(N, Pos);
1316fdd2bd5SGeorge Karpenkov }
1326fdd2bd5SGeorge Karpenkov
1336fdd2bd5SGeorge Karpenkov return &N->getValue();
1346fdd2bd5SGeorge Karpenkov }
1356fdd2bd5SGeorge Karpenkov
1366fdd2bd5SGeorge Karpenkov RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>();
1376fdd2bd5SGeorge Karpenkov new (Summ) RetainSummary(OldSumm);
1386fdd2bd5SGeorge Karpenkov return Summ;
1396fdd2bd5SGeorge Karpenkov }
1406fdd2bd5SGeorge Karpenkov
isSubclass(const Decl * D,StringRef ClassName)1416fdd2bd5SGeorge Karpenkov static bool isSubclass(const Decl *D,
1426fdd2bd5SGeorge Karpenkov StringRef ClassName) {
1436fdd2bd5SGeorge Karpenkov using namespace ast_matchers;
144adcd0268SBenjamin Kramer DeclarationMatcher SubclassM =
145adcd0268SBenjamin Kramer cxxRecordDecl(isSameOrDerivedFrom(std::string(ClassName)));
1466fdd2bd5SGeorge Karpenkov return !(match(SubclassM, *D, D->getASTContext()).empty());
1476fdd2bd5SGeorge Karpenkov }
1486fdd2bd5SGeorge Karpenkov
isExactClass(const Decl * D,StringRef ClassName)14950f17e9dSGeorgeta Igna static bool isExactClass(const Decl *D, StringRef ClassName) {
15050f17e9dSGeorgeta Igna using namespace ast_matchers;
15150f17e9dSGeorgeta Igna DeclarationMatcher sameClassM =
15250f17e9dSGeorgeta Igna cxxRecordDecl(hasName(std::string(ClassName)));
15350f17e9dSGeorgeta Igna return !(match(sameClassM, *D, D->getASTContext()).empty());
1546fdd2bd5SGeorge Karpenkov }
1556fdd2bd5SGeorge Karpenkov
isOSObjectSubclass(const Decl * D)15650f17e9dSGeorgeta Igna static bool isOSObjectSubclass(const Decl *D) {
15750f17e9dSGeorgeta Igna return D && isSubclass(D, "OSMetaClassBase") &&
15850f17e9dSGeorgeta Igna !isExactClass(D, "OSMetaClass");
1596fdd2bd5SGeorge Karpenkov }
1606fdd2bd5SGeorge Karpenkov
isOSObjectDynamicCast(StringRef S)16150f17e9dSGeorgeta Igna static bool isOSObjectDynamicCast(StringRef S) { return S == "safeMetaCast"; }
16250f17e9dSGeorgeta Igna
isOSObjectRequiredCast(StringRef S)163b03854f8SArtem Dergachev static bool isOSObjectRequiredCast(StringRef S) {
164b03854f8SArtem Dergachev return S == "requiredMetaCast";
165b03854f8SArtem Dergachev }
166b03854f8SArtem Dergachev
isOSObjectThisCast(StringRef S)1676fdd2bd5SGeorge Karpenkov static bool isOSObjectThisCast(StringRef S) {
1686fdd2bd5SGeorge Karpenkov return S == "metaCast";
1696fdd2bd5SGeorge Karpenkov }
1706fdd2bd5SGeorge Karpenkov
171d37ff4e8SGeorge Karpenkov
isOSObjectPtr(QualType QT)172d37ff4e8SGeorge Karpenkov static bool isOSObjectPtr(QualType QT) {
173d37ff4e8SGeorge Karpenkov return isOSObjectSubclass(QT->getPointeeCXXRecordDecl());
174d37ff4e8SGeorge Karpenkov }
175d37ff4e8SGeorge Karpenkov
isISLObjectRef(QualType Ty)176d37ff4e8SGeorge Karpenkov static bool isISLObjectRef(QualType Ty) {
177f3dcc235SKazu Hirata return StringRef(Ty.getAsString()).starts_with("isl_");
178d37ff4e8SGeorge Karpenkov }
179d37ff4e8SGeorge Karpenkov
isOSIteratorSubclass(const Decl * D)1806fdd2bd5SGeorge Karpenkov static bool isOSIteratorSubclass(const Decl *D) {
1816fdd2bd5SGeorge Karpenkov return isSubclass(D, "OSIterator");
1826fdd2bd5SGeorge Karpenkov }
1836fdd2bd5SGeorge Karpenkov
hasRCAnnotation(const Decl * D,StringRef rcAnnotation)1846fdd2bd5SGeorge Karpenkov static bool hasRCAnnotation(const Decl *D, StringRef rcAnnotation) {
1856fdd2bd5SGeorge Karpenkov for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) {
1866fdd2bd5SGeorge Karpenkov if (Ann->getAnnotation() == rcAnnotation)
1876fdd2bd5SGeorge Karpenkov return true;
1886fdd2bd5SGeorge Karpenkov }
1896fdd2bd5SGeorge Karpenkov return false;
1906fdd2bd5SGeorge Karpenkov }
1916fdd2bd5SGeorge Karpenkov
isRetain(const FunctionDecl * FD,StringRef FName)1926fdd2bd5SGeorge Karpenkov static bool isRetain(const FunctionDecl *FD, StringRef FName) {
193ed1539c6SKazu Hirata return FName.starts_with_insensitive("retain") ||
194ed1539c6SKazu Hirata FName.ends_with_insensitive("retain");
1956fdd2bd5SGeorge Karpenkov }
1966fdd2bd5SGeorge Karpenkov
isRelease(const FunctionDecl * FD,StringRef FName)1976fdd2bd5SGeorge Karpenkov static bool isRelease(const FunctionDecl *FD, StringRef FName) {
198ed1539c6SKazu Hirata return FName.starts_with_insensitive("release") ||
199ed1539c6SKazu Hirata FName.ends_with_insensitive("release");
2006fdd2bd5SGeorge Karpenkov }
2016fdd2bd5SGeorge Karpenkov
isAutorelease(const FunctionDecl * FD,StringRef FName)2026fdd2bd5SGeorge Karpenkov static bool isAutorelease(const FunctionDecl *FD, StringRef FName) {
203ed1539c6SKazu Hirata return FName.starts_with_insensitive("autorelease") ||
204ed1539c6SKazu Hirata FName.ends_with_insensitive("autorelease");
2056fdd2bd5SGeorge Karpenkov }
2066fdd2bd5SGeorge Karpenkov
isMakeCollectable(StringRef FName)2076fdd2bd5SGeorge Karpenkov static bool isMakeCollectable(StringRef FName) {
208e5c7c171SMartin Storsjö return FName.contains_insensitive("MakeCollectable");
2096fdd2bd5SGeorge Karpenkov }
2106fdd2bd5SGeorge Karpenkov
2116fdd2bd5SGeorge Karpenkov /// A function is OSObject related if it is declared on a subclass
2126fdd2bd5SGeorge Karpenkov /// of OSObject, or any of the parameters is a subclass of an OSObject.
isOSObjectRelated(const CXXMethodDecl * MD)2136fdd2bd5SGeorge Karpenkov static bool isOSObjectRelated(const CXXMethodDecl *MD) {
2146fdd2bd5SGeorge Karpenkov if (isOSObjectSubclass(MD->getParent()))
2156fdd2bd5SGeorge Karpenkov return true;
2166fdd2bd5SGeorge Karpenkov
2176fdd2bd5SGeorge Karpenkov for (ParmVarDecl *Param : MD->parameters()) {
2186fdd2bd5SGeorge Karpenkov QualType PT = Param->getType()->getPointeeType();
2196fdd2bd5SGeorge Karpenkov if (!PT.isNull())
2206fdd2bd5SGeorge Karpenkov if (CXXRecordDecl *RD = PT->getAsCXXRecordDecl())
2216fdd2bd5SGeorge Karpenkov if (isOSObjectSubclass(RD))
2226fdd2bd5SGeorge Karpenkov return true;
2236fdd2bd5SGeorge Karpenkov }
2246fdd2bd5SGeorge Karpenkov
2256fdd2bd5SGeorge Karpenkov return false;
2266fdd2bd5SGeorge Karpenkov }
2276fdd2bd5SGeorge Karpenkov
2286fdd2bd5SGeorge Karpenkov bool
isKnownSmartPointer(QualType QT)2296fdd2bd5SGeorge Karpenkov RetainSummaryManager::isKnownSmartPointer(QualType QT) {
2306fdd2bd5SGeorge Karpenkov QT = QT.getCanonicalType();
2316fdd2bd5SGeorge Karpenkov const auto *RD = QT->getAsCXXRecordDecl();
2326fdd2bd5SGeorge Karpenkov if (!RD)
2336fdd2bd5SGeorge Karpenkov return false;
2346fdd2bd5SGeorge Karpenkov const IdentifierInfo *II = RD->getIdentifier();
2356fdd2bd5SGeorge Karpenkov if (II && II->getName() == "smart_ptr")
2366fdd2bd5SGeorge Karpenkov if (const auto *ND = dyn_cast<NamespaceDecl>(RD->getDeclContext()))
2376fdd2bd5SGeorge Karpenkov if (ND->getNameAsString() == "os")
2386fdd2bd5SGeorge Karpenkov return true;
2396fdd2bd5SGeorge Karpenkov return false;
2406fdd2bd5SGeorge Karpenkov }
2416fdd2bd5SGeorge Karpenkov
2426fdd2bd5SGeorge Karpenkov const RetainSummary *
getSummaryForOSObject(const FunctionDecl * FD,StringRef FName,QualType RetTy)2436fdd2bd5SGeorge Karpenkov RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD,
2446fdd2bd5SGeorge Karpenkov StringRef FName, QualType RetTy) {
24548e7a2faSArtem Dergachev assert(TrackOSObjects &&
24648e7a2faSArtem Dergachev "Requesting a summary for an OSObject but OSObjects are not tracked");
24748e7a2faSArtem Dergachev
2486fdd2bd5SGeorge Karpenkov if (RetTy->isPointerType()) {
2496fdd2bd5SGeorge Karpenkov const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl();
2506fdd2bd5SGeorge Karpenkov if (PD && isOSObjectSubclass(PD)) {
251b03854f8SArtem Dergachev if (isOSObjectDynamicCast(FName) || isOSObjectRequiredCast(FName) ||
252b03854f8SArtem Dergachev isOSObjectThisCast(FName))
2536fdd2bd5SGeorge Karpenkov return getDefaultSummary();
2546fdd2bd5SGeorge Karpenkov
25548e7a2faSArtem Dergachev // TODO: Add support for the slightly common *Matching(table) idiom.
25648e7a2faSArtem Dergachev // Cf. IOService::nameMatching() etc. - these function have an unusual
25748e7a2faSArtem Dergachev // contract of returning at +0 or +1 depending on their last argument.
258f3dcc235SKazu Hirata if (FName.ends_with("Matching")) {
25948e7a2faSArtem Dergachev return getPersistentStopSummary();
26048e7a2faSArtem Dergachev }
26148e7a2faSArtem Dergachev
26248e7a2faSArtem Dergachev // All objects returned with functions *not* starting with 'get',
26348e7a2faSArtem Dergachev // or iterators, are returned at +1.
264f3dcc235SKazu Hirata if ((!FName.starts_with("get") && !FName.starts_with("Get")) ||
2656fdd2bd5SGeorge Karpenkov isOSIteratorSubclass(PD)) {
2666fdd2bd5SGeorge Karpenkov return getOSSummaryCreateRule(FD);
2676fdd2bd5SGeorge Karpenkov } else {
2686fdd2bd5SGeorge Karpenkov return getOSSummaryGetRule(FD);
2696fdd2bd5SGeorge Karpenkov }
2706fdd2bd5SGeorge Karpenkov }
2716fdd2bd5SGeorge Karpenkov }
2726fdd2bd5SGeorge Karpenkov
2736fdd2bd5SGeorge Karpenkov if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
2746fdd2bd5SGeorge Karpenkov const CXXRecordDecl *Parent = MD->getParent();
27548e7a2faSArtem Dergachev if (Parent && isOSObjectSubclass(Parent)) {
2760f3bbbaeSGeorge Karpenkov if (FName == "release" || FName == "taggedRelease")
2776fdd2bd5SGeorge Karpenkov return getOSSummaryReleaseRule(FD);
2786fdd2bd5SGeorge Karpenkov
2790f3bbbaeSGeorge Karpenkov if (FName == "retain" || FName == "taggedRetain")
2806fdd2bd5SGeorge Karpenkov return getOSSummaryRetainRule(FD);
2816fdd2bd5SGeorge Karpenkov
2826fdd2bd5SGeorge Karpenkov if (FName == "free")
2836fdd2bd5SGeorge Karpenkov return getOSSummaryFreeRule(FD);
2846fdd2bd5SGeorge Karpenkov
2856fdd2bd5SGeorge Karpenkov if (MD->getOverloadedOperator() == OO_New)
2866fdd2bd5SGeorge Karpenkov return getOSSummaryCreateRule(MD);
2876fdd2bd5SGeorge Karpenkov }
2886fdd2bd5SGeorge Karpenkov }
2896fdd2bd5SGeorge Karpenkov
2906fdd2bd5SGeorge Karpenkov return nullptr;
2916fdd2bd5SGeorge Karpenkov }
2926fdd2bd5SGeorge Karpenkov
getSummaryForObjCOrCFObject(const FunctionDecl * FD,StringRef FName,QualType RetTy,const FunctionType * FT,bool & AllowAnnotations)2936fdd2bd5SGeorge Karpenkov const RetainSummary *RetainSummaryManager::getSummaryForObjCOrCFObject(
2946fdd2bd5SGeorge Karpenkov const FunctionDecl *FD,
2956fdd2bd5SGeorge Karpenkov StringRef FName,
2966fdd2bd5SGeorge Karpenkov QualType RetTy,
2976fdd2bd5SGeorge Karpenkov const FunctionType *FT,
2986fdd2bd5SGeorge Karpenkov bool &AllowAnnotations) {
2996fdd2bd5SGeorge Karpenkov
3006fdd2bd5SGeorge Karpenkov ArgEffects ScratchArgs(AF.getEmptyMap());
3016fdd2bd5SGeorge Karpenkov
3026fdd2bd5SGeorge Karpenkov std::string RetTyName = RetTy.getAsString();
3036fdd2bd5SGeorge Karpenkov if (FName == "pthread_create" || FName == "pthread_setspecific") {
3047f25a882SArtem Dergachev // It's not uncommon to pass a tracked object into the thread
3057f25a882SArtem Dergachev // as 'void *arg', and then release it inside the thread.
3067f25a882SArtem Dergachev // FIXME: We could build a much more precise model for these functions.
3076fdd2bd5SGeorge Karpenkov return getPersistentStopSummary();
3086fdd2bd5SGeorge Karpenkov } else if(FName == "NSMakeCollectable") {
3096fdd2bd5SGeorge Karpenkov // Handle: id NSMakeCollectable(CFTypeRef)
3106fdd2bd5SGeorge Karpenkov AllowAnnotations = false;
3116fdd2bd5SGeorge Karpenkov return RetTy->isObjCIdType() ? getUnarySummary(FT, DoNothing)
3126fdd2bd5SGeorge Karpenkov : getPersistentStopSummary();
3136fdd2bd5SGeorge Karpenkov } else if (FName == "CMBufferQueueDequeueAndRetain" ||
3146fdd2bd5SGeorge Karpenkov FName == "CMBufferQueueDequeueIfDataReadyAndRetain") {
3157f25a882SArtem Dergachev // These API functions are known to NOT act as a CFRetain wrapper.
3167f25a882SArtem Dergachev // They simply make a new object owned by the caller.
3176fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF),
3186fdd2bd5SGeorge Karpenkov ScratchArgs,
3196fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing),
3206fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing));
3216fdd2bd5SGeorge Karpenkov } else if (FName == "CFPlugInInstanceCreate") {
3226fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs);
3236fdd2bd5SGeorge Karpenkov } else if (FName == "IORegistryEntrySearchCFProperty" ||
3246fdd2bd5SGeorge Karpenkov (RetTyName == "CFMutableDictionaryRef" &&
3256fdd2bd5SGeorge Karpenkov (FName == "IOBSDNameMatching" || FName == "IOServiceMatching" ||
3266fdd2bd5SGeorge Karpenkov FName == "IOServiceNameMatching" ||
3276fdd2bd5SGeorge Karpenkov FName == "IORegistryEntryIDMatching" ||
3286fdd2bd5SGeorge Karpenkov FName == "IOOpenFirmwarePathMatching"))) {
3297f25a882SArtem Dergachev // Yes, these IOKit functions return CF objects.
3307f25a882SArtem Dergachev // They also violate the CF naming convention.
3316fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs,
3326fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing), ArgEffect(DoNothing));
3336fdd2bd5SGeorge Karpenkov } else if (FName == "IOServiceGetMatchingService" ||
3346fdd2bd5SGeorge Karpenkov FName == "IOServiceGetMatchingServices") {
3357f25a882SArtem Dergachev // These IOKit functions accept CF objects as arguments.
3367f25a882SArtem Dergachev // They also consume them without an appropriate annotation.
3376fdd2bd5SGeorge Karpenkov ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(DecRef, ObjKind::CF));
3386fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(),
3396fdd2bd5SGeorge Karpenkov ScratchArgs,
3406fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing), ArgEffect(DoNothing));
3416fdd2bd5SGeorge Karpenkov } else if (FName == "IOServiceAddNotification" ||
3426fdd2bd5SGeorge Karpenkov FName == "IOServiceAddMatchingNotification") {
3437f25a882SArtem Dergachev // More IOKit functions suddenly accepting (and even more suddenly,
3447f25a882SArtem Dergachev // consuming) CF objects.
3456fdd2bd5SGeorge Karpenkov ScratchArgs = AF.add(ScratchArgs, 2, ArgEffect(DecRef, ObjKind::CF));
3466fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(),
3476fdd2bd5SGeorge Karpenkov ScratchArgs,
3486fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing), ArgEffect(DoNothing));
3496fdd2bd5SGeorge Karpenkov } else if (FName == "CVPixelBufferCreateWithBytes") {
3506fdd2bd5SGeorge Karpenkov // Eventually this can be improved by recognizing that the pixel
3516fdd2bd5SGeorge Karpenkov // buffer passed to CVPixelBufferCreateWithBytes is released via
3526fdd2bd5SGeorge Karpenkov // a callback and doing full IPA to make sure this is done correctly.
3537f25a882SArtem Dergachev // Note that it's passed as a 'void *', so it's hard to annotate.
3547f25a882SArtem Dergachev // FIXME: This function also has an out parameter that returns an
3556fdd2bd5SGeorge Karpenkov // allocated object.
3566fdd2bd5SGeorge Karpenkov ScratchArgs = AF.add(ScratchArgs, 7, ArgEffect(StopTracking));
3576fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(),
3586fdd2bd5SGeorge Karpenkov ScratchArgs,
3596fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing), ArgEffect(DoNothing));
3606fdd2bd5SGeorge Karpenkov } else if (FName == "CGBitmapContextCreateWithData") {
3617f25a882SArtem Dergachev // This is similar to the CVPixelBufferCreateWithBytes situation above.
3626fdd2bd5SGeorge Karpenkov // Eventually this can be improved by recognizing that 'releaseInfo'
3636fdd2bd5SGeorge Karpenkov // passed to CGBitmapContextCreateWithData is released via
3646fdd2bd5SGeorge Karpenkov // a callback and doing full IPA to make sure this is done correctly.
3656fdd2bd5SGeorge Karpenkov ScratchArgs = AF.add(ScratchArgs, 8, ArgEffect(ArgEffect(StopTracking)));
3666fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs,
3676fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing), ArgEffect(DoNothing));
3686fdd2bd5SGeorge Karpenkov } else if (FName == "CVPixelBufferCreateWithPlanarBytes") {
3697f25a882SArtem Dergachev // Same as CVPixelBufferCreateWithBytes, just more arguments.
3706fdd2bd5SGeorge Karpenkov ScratchArgs = AF.add(ScratchArgs, 12, ArgEffect(StopTracking));
3716fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(),
3726fdd2bd5SGeorge Karpenkov ScratchArgs,
3736fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing), ArgEffect(DoNothing));
3748b8841e1SArtem Dergachev } else if (FName == "VTCompressionSessionEncodeFrame" ||
3758b8841e1SArtem Dergachev FName == "VTCompressionSessionEncodeMultiImageFrame") {
3768b8841e1SArtem Dergachev // The context argument passed to VTCompressionSessionEncodeFrame() et.al.
3776fdd2bd5SGeorge Karpenkov // is passed to the callback specified when creating the session
3786fdd2bd5SGeorge Karpenkov // (e.g. with VTCompressionSessionCreate()) which can release it.
3796fdd2bd5SGeorge Karpenkov // To account for this possibility, conservatively stop tracking
3806fdd2bd5SGeorge Karpenkov // the context.
3816fdd2bd5SGeorge Karpenkov ScratchArgs = AF.add(ScratchArgs, 5, ArgEffect(StopTracking));
3826fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(),
3836fdd2bd5SGeorge Karpenkov ScratchArgs,
3846fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing), ArgEffect(DoNothing));
3856fdd2bd5SGeorge Karpenkov } else if (FName == "dispatch_set_context" ||
3866fdd2bd5SGeorge Karpenkov FName == "xpc_connection_set_context") {
3877f25a882SArtem Dergachev // The analyzer currently doesn't have a good way to reason about
3887f25a882SArtem Dergachev // dispatch_set_finalizer_f() which typically cleans up the context.
389e0ac46e6SMehdi Amini // If we pass a context object that is memory managed, stop tracking it.
3907f25a882SArtem Dergachev // Same with xpc_connection_set_finalizer_f().
3916fdd2bd5SGeorge Karpenkov ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(StopTracking));
3926fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(),
3936fdd2bd5SGeorge Karpenkov ScratchArgs,
3946fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing), ArgEffect(DoNothing));
395f3dcc235SKazu Hirata } else if (FName.starts_with("NSLog")) {
3966fdd2bd5SGeorge Karpenkov return getDoNothingSummary();
397f3dcc235SKazu Hirata } else if (FName.starts_with("NS") && FName.contains("Insert")) {
39803862133SQuinn Pham // Allowlist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
3990f1c1be1SAaron Ballman // be deallocated by NSMapRemove.
4006fdd2bd5SGeorge Karpenkov ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(StopTracking));
4016fdd2bd5SGeorge Karpenkov ScratchArgs = AF.add(ScratchArgs, 2, ArgEffect(StopTracking));
4026fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(),
4036fdd2bd5SGeorge Karpenkov ScratchArgs, ArgEffect(DoNothing),
4046fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing));
4056fdd2bd5SGeorge Karpenkov }
4066fdd2bd5SGeorge Karpenkov
4076fdd2bd5SGeorge Karpenkov if (RetTy->isPointerType()) {
4086fdd2bd5SGeorge Karpenkov
4096fdd2bd5SGeorge Karpenkov // For CoreFoundation ('CF') types.
4106fdd2bd5SGeorge Karpenkov if (cocoa::isRefType(RetTy, "CF", FName)) {
4116fdd2bd5SGeorge Karpenkov if (isRetain(FD, FName)) {
4126fdd2bd5SGeorge Karpenkov // CFRetain isn't supposed to be annotated. However, this may as
4136fdd2bd5SGeorge Karpenkov // well be a user-made "safe" CFRetain function that is incorrectly
4146fdd2bd5SGeorge Karpenkov // annotated as cf_returns_retained due to lack of better options.
4156fdd2bd5SGeorge Karpenkov // We want to ignore such annotation.
4166fdd2bd5SGeorge Karpenkov AllowAnnotations = false;
4176fdd2bd5SGeorge Karpenkov
4186fdd2bd5SGeorge Karpenkov return getUnarySummary(FT, IncRef);
4196fdd2bd5SGeorge Karpenkov } else if (isAutorelease(FD, FName)) {
4206fdd2bd5SGeorge Karpenkov // The headers use cf_consumed, but we can fully model CFAutorelease
4216fdd2bd5SGeorge Karpenkov // ourselves.
4226fdd2bd5SGeorge Karpenkov AllowAnnotations = false;
4236fdd2bd5SGeorge Karpenkov
4246fdd2bd5SGeorge Karpenkov return getUnarySummary(FT, Autorelease);
4256fdd2bd5SGeorge Karpenkov } else if (isMakeCollectable(FName)) {
4266fdd2bd5SGeorge Karpenkov AllowAnnotations = false;
4276fdd2bd5SGeorge Karpenkov return getUnarySummary(FT, DoNothing);
4286fdd2bd5SGeorge Karpenkov } else {
4296fdd2bd5SGeorge Karpenkov return getCFCreateGetRuleSummary(FD);
4306fdd2bd5SGeorge Karpenkov }
4316fdd2bd5SGeorge Karpenkov }
4326fdd2bd5SGeorge Karpenkov
4336fdd2bd5SGeorge Karpenkov // For CoreGraphics ('CG') and CoreVideo ('CV') types.
4346fdd2bd5SGeorge Karpenkov if (cocoa::isRefType(RetTy, "CG", FName) ||
4356fdd2bd5SGeorge Karpenkov cocoa::isRefType(RetTy, "CV", FName)) {
4366fdd2bd5SGeorge Karpenkov if (isRetain(FD, FName))
4376fdd2bd5SGeorge Karpenkov return getUnarySummary(FT, IncRef);
4386fdd2bd5SGeorge Karpenkov else
4396fdd2bd5SGeorge Karpenkov return getCFCreateGetRuleSummary(FD);
4406fdd2bd5SGeorge Karpenkov }
4416fdd2bd5SGeorge Karpenkov
4426fdd2bd5SGeorge Karpenkov // For all other CF-style types, use the Create/Get
4436fdd2bd5SGeorge Karpenkov // rule for summaries but don't support Retain functions
4446fdd2bd5SGeorge Karpenkov // with framework-specific prefixes.
4456fdd2bd5SGeorge Karpenkov if (coreFoundation::isCFObjectRef(RetTy)) {
4466fdd2bd5SGeorge Karpenkov return getCFCreateGetRuleSummary(FD);
4476fdd2bd5SGeorge Karpenkov }
4486fdd2bd5SGeorge Karpenkov
4496fdd2bd5SGeorge Karpenkov if (FD->hasAttr<CFAuditedTransferAttr>()) {
4506fdd2bd5SGeorge Karpenkov return getCFCreateGetRuleSummary(FD);
4516fdd2bd5SGeorge Karpenkov }
4526fdd2bd5SGeorge Karpenkov }
4536fdd2bd5SGeorge Karpenkov
4546fdd2bd5SGeorge Karpenkov // Check for release functions, the only kind of functions that we care
4556fdd2bd5SGeorge Karpenkov // about that don't return a pointer type.
456f3dcc235SKazu Hirata if (FName.starts_with("CG") || FName.starts_with("CF")) {
4576fdd2bd5SGeorge Karpenkov // Test for 'CGCF'.
458f3dcc235SKazu Hirata FName = FName.substr(FName.starts_with("CGCF") ? 4 : 2);
4596fdd2bd5SGeorge Karpenkov
4606fdd2bd5SGeorge Karpenkov if (isRelease(FD, FName))
4616fdd2bd5SGeorge Karpenkov return getUnarySummary(FT, DecRef);
4626fdd2bd5SGeorge Karpenkov else {
4636fdd2bd5SGeorge Karpenkov assert(ScratchArgs.isEmpty());
4646fdd2bd5SGeorge Karpenkov // Remaining CoreFoundation and CoreGraphics functions.
4656fdd2bd5SGeorge Karpenkov // We use to assume that they all strictly followed the ownership idiom
4666fdd2bd5SGeorge Karpenkov // and that ownership cannot be transferred. While this is technically
4676fdd2bd5SGeorge Karpenkov // correct, many methods allow a tracked object to escape. For example:
4686fdd2bd5SGeorge Karpenkov //
4696fdd2bd5SGeorge Karpenkov // CFMutableDictionaryRef x = CFDictionaryCreateMutable(...);
4706fdd2bd5SGeorge Karpenkov // CFDictionaryAddValue(y, key, x);
4716fdd2bd5SGeorge Karpenkov // CFRelease(x);
4726fdd2bd5SGeorge Karpenkov // ... it is okay to use 'x' since 'y' has a reference to it
4736fdd2bd5SGeorge Karpenkov //
4746fdd2bd5SGeorge Karpenkov // We handle this and similar cases with the follow heuristic. If the
4756fdd2bd5SGeorge Karpenkov // function name contains "InsertValue", "SetValue", "AddValue",
4766fdd2bd5SGeorge Karpenkov // "AppendValue", or "SetAttribute", then we assume that arguments may
4776fdd2bd5SGeorge Karpenkov // "escape." This means that something else holds on to the object,
4786fdd2bd5SGeorge Karpenkov // allowing it be used even after its local retain count drops to 0.
4796fdd2bd5SGeorge Karpenkov ArgEffectKind E =
4806fdd2bd5SGeorge Karpenkov (StrInStrNoCase(FName, "InsertValue") != StringRef::npos ||
4816fdd2bd5SGeorge Karpenkov StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
4826fdd2bd5SGeorge Karpenkov StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
4836fdd2bd5SGeorge Karpenkov StrInStrNoCase(FName, "AppendValue") != StringRef::npos ||
4846fdd2bd5SGeorge Karpenkov StrInStrNoCase(FName, "SetAttribute") != StringRef::npos)
4856fdd2bd5SGeorge Karpenkov ? MayEscape
4866fdd2bd5SGeorge Karpenkov : DoNothing;
4876fdd2bd5SGeorge Karpenkov
4886fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs,
4896fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing), ArgEffect(E, ObjKind::CF));
4906fdd2bd5SGeorge Karpenkov }
4916fdd2bd5SGeorge Karpenkov }
4926fdd2bd5SGeorge Karpenkov
4936fdd2bd5SGeorge Karpenkov return nullptr;
4946fdd2bd5SGeorge Karpenkov }
4956fdd2bd5SGeorge Karpenkov
4966fdd2bd5SGeorge Karpenkov const RetainSummary *
generateSummary(const FunctionDecl * FD,bool & AllowAnnotations)4976fdd2bd5SGeorge Karpenkov RetainSummaryManager::generateSummary(const FunctionDecl *FD,
4986fdd2bd5SGeorge Karpenkov bool &AllowAnnotations) {
4996fdd2bd5SGeorge Karpenkov // We generate "stop" summaries for implicitly defined functions.
5006fdd2bd5SGeorge Karpenkov if (FD->isImplicit())
5016fdd2bd5SGeorge Karpenkov return getPersistentStopSummary();
5026fdd2bd5SGeorge Karpenkov
5036fdd2bd5SGeorge Karpenkov const IdentifierInfo *II = FD->getIdentifier();
5046fdd2bd5SGeorge Karpenkov
5056fdd2bd5SGeorge Karpenkov StringRef FName = II ? II->getName() : "";
5066fdd2bd5SGeorge Karpenkov
5076fdd2bd5SGeorge Karpenkov // Strip away preceding '_'. Doing this here will effect all the checks
5086fdd2bd5SGeorge Karpenkov // down below.
5096fdd2bd5SGeorge Karpenkov FName = FName.substr(FName.find_first_not_of('_'));
5106fdd2bd5SGeorge Karpenkov
5116fdd2bd5SGeorge Karpenkov // Inspect the result type. Strip away any typedefs.
5123517d105SArtem Dergachev const auto *FT = FD->getType()->castAs<FunctionType>();
5136fdd2bd5SGeorge Karpenkov QualType RetTy = FT->getReturnType();
5146fdd2bd5SGeorge Karpenkov
5156fdd2bd5SGeorge Karpenkov if (TrackOSObjects)
5166fdd2bd5SGeorge Karpenkov if (const RetainSummary *S = getSummaryForOSObject(FD, FName, RetTy))
5176fdd2bd5SGeorge Karpenkov return S;
5186fdd2bd5SGeorge Karpenkov
5196fdd2bd5SGeorge Karpenkov if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
5206794aa70SGeorge Karpenkov if (!isOSObjectRelated(MD))
5216fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(),
5226fdd2bd5SGeorge Karpenkov ArgEffects(AF.getEmptyMap()),
5236fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing),
5246fdd2bd5SGeorge Karpenkov ArgEffect(StopTracking),
5256fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing));
5266fdd2bd5SGeorge Karpenkov
5276794aa70SGeorge Karpenkov if (TrackObjCAndCFObjects)
5286794aa70SGeorge Karpenkov if (const RetainSummary *S =
5296794aa70SGeorge Karpenkov getSummaryForObjCOrCFObject(FD, FName, RetTy, FT, AllowAnnotations))
5306794aa70SGeorge Karpenkov return S;
5316794aa70SGeorge Karpenkov
5326fdd2bd5SGeorge Karpenkov return getDefaultSummary();
5336fdd2bd5SGeorge Karpenkov }
5346fdd2bd5SGeorge Karpenkov
5356fdd2bd5SGeorge Karpenkov const RetainSummary *
getFunctionSummary(const FunctionDecl * FD)5366fdd2bd5SGeorge Karpenkov RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
5376fdd2bd5SGeorge Karpenkov // If we don't know what function we're calling, use our default summary.
5386fdd2bd5SGeorge Karpenkov if (!FD)
5396fdd2bd5SGeorge Karpenkov return getDefaultSummary();
5406fdd2bd5SGeorge Karpenkov
5416fdd2bd5SGeorge Karpenkov // Look up a summary in our cache of FunctionDecls -> Summaries.
5426fdd2bd5SGeorge Karpenkov FuncSummariesTy::iterator I = FuncSummaries.find(FD);
5436fdd2bd5SGeorge Karpenkov if (I != FuncSummaries.end())
5446fdd2bd5SGeorge Karpenkov return I->second;
5456fdd2bd5SGeorge Karpenkov
5466fdd2bd5SGeorge Karpenkov // No summary? Generate one.
5476fdd2bd5SGeorge Karpenkov bool AllowAnnotations = true;
5486fdd2bd5SGeorge Karpenkov const RetainSummary *S = generateSummary(FD, AllowAnnotations);
5496fdd2bd5SGeorge Karpenkov
5506fdd2bd5SGeorge Karpenkov // Annotations override defaults.
5516fdd2bd5SGeorge Karpenkov if (AllowAnnotations)
5526fdd2bd5SGeorge Karpenkov updateSummaryFromAnnotations(S, FD);
5536fdd2bd5SGeorge Karpenkov
5546fdd2bd5SGeorge Karpenkov FuncSummaries[FD] = S;
5556fdd2bd5SGeorge Karpenkov return S;
5566fdd2bd5SGeorge Karpenkov }
5576fdd2bd5SGeorge Karpenkov
5586fdd2bd5SGeorge Karpenkov //===----------------------------------------------------------------------===//
5596fdd2bd5SGeorge Karpenkov // Summary creation for functions (largely uses of Core Foundation).
5606fdd2bd5SGeorge Karpenkov //===----------------------------------------------------------------------===//
5616fdd2bd5SGeorge Karpenkov
getStopTrackingHardEquivalent(ArgEffect E)5626fdd2bd5SGeorge Karpenkov static ArgEffect getStopTrackingHardEquivalent(ArgEffect E) {
5636fdd2bd5SGeorge Karpenkov switch (E.getKind()) {
5646fdd2bd5SGeorge Karpenkov case DoNothing:
5656fdd2bd5SGeorge Karpenkov case Autorelease:
5666fdd2bd5SGeorge Karpenkov case DecRefBridgedTransferred:
5676fdd2bd5SGeorge Karpenkov case IncRef:
5686fdd2bd5SGeorge Karpenkov case UnretainedOutParameter:
5696fdd2bd5SGeorge Karpenkov case RetainedOutParameter:
5706fdd2bd5SGeorge Karpenkov case RetainedOutParameterOnZero:
5716fdd2bd5SGeorge Karpenkov case RetainedOutParameterOnNonZero:
5726fdd2bd5SGeorge Karpenkov case MayEscape:
5736fdd2bd5SGeorge Karpenkov case StopTracking:
5746fdd2bd5SGeorge Karpenkov case StopTrackingHard:
5756fdd2bd5SGeorge Karpenkov return E.withKind(StopTrackingHard);
5766fdd2bd5SGeorge Karpenkov case DecRef:
5776fdd2bd5SGeorge Karpenkov case DecRefAndStopTrackingHard:
5786fdd2bd5SGeorge Karpenkov return E.withKind(DecRefAndStopTrackingHard);
5796fdd2bd5SGeorge Karpenkov case Dealloc:
5806fdd2bd5SGeorge Karpenkov return E.withKind(Dealloc);
5816fdd2bd5SGeorge Karpenkov }
5826fdd2bd5SGeorge Karpenkov
5836fdd2bd5SGeorge Karpenkov llvm_unreachable("Unknown ArgEffect kind");
5846fdd2bd5SGeorge Karpenkov }
5856fdd2bd5SGeorge Karpenkov
586b0fc58b5SGeorge Karpenkov const RetainSummary *
updateSummaryForNonZeroCallbackArg(const RetainSummary * S,AnyCall & C)587b0fc58b5SGeorge Karpenkov RetainSummaryManager::updateSummaryForNonZeroCallbackArg(const RetainSummary *S,
588b0fc58b5SGeorge Karpenkov AnyCall &C) {
589b0fc58b5SGeorge Karpenkov ArgEffect RecEffect = getStopTrackingHardEquivalent(S->getReceiverEffect());
590b0fc58b5SGeorge Karpenkov ArgEffect DefEffect = getStopTrackingHardEquivalent(S->getDefaultArgEffect());
5916fdd2bd5SGeorge Karpenkov
5926fdd2bd5SGeorge Karpenkov ArgEffects ScratchArgs(AF.getEmptyMap());
5936fdd2bd5SGeorge Karpenkov ArgEffects CustomArgEffects = S->getArgEffects();
5946fdd2bd5SGeorge Karpenkov for (ArgEffects::iterator I = CustomArgEffects.begin(),
5956fdd2bd5SGeorge Karpenkov E = CustomArgEffects.end();
5966fdd2bd5SGeorge Karpenkov I != E; ++I) {
5976fdd2bd5SGeorge Karpenkov ArgEffect Translated = getStopTrackingHardEquivalent(I->second);
5986fdd2bd5SGeorge Karpenkov if (Translated.getKind() != DefEffect.getKind())
5996fdd2bd5SGeorge Karpenkov ScratchArgs = AF.add(ScratchArgs, I->first, Translated);
6006fdd2bd5SGeorge Karpenkov }
6016fdd2bd5SGeorge Karpenkov
6026fdd2bd5SGeorge Karpenkov RetEffect RE = RetEffect::MakeNoRetHard();
6036fdd2bd5SGeorge Karpenkov
6046fdd2bd5SGeorge Karpenkov // Special cases where the callback argument CANNOT free the return value.
6056fdd2bd5SGeorge Karpenkov // This can generally only happen if we know that the callback will only be
6066fdd2bd5SGeorge Karpenkov // called when the return value is already being deallocated.
6076fdd2bd5SGeorge Karpenkov if (const IdentifierInfo *Name = C.getIdentifier()) {
6086fdd2bd5SGeorge Karpenkov // When the CGBitmapContext is deallocated, the callback here will free
6096fdd2bd5SGeorge Karpenkov // the associated data buffer.
6106fdd2bd5SGeorge Karpenkov // The callback in dispatch_data_create frees the buffer, but not
6116fdd2bd5SGeorge Karpenkov // the data object.
6126fdd2bd5SGeorge Karpenkov if (Name->isStr("CGBitmapContextCreateWithData") ||
6136fdd2bd5SGeorge Karpenkov Name->isStr("dispatch_data_create"))
6146fdd2bd5SGeorge Karpenkov RE = S->getRetEffect();
6156fdd2bd5SGeorge Karpenkov }
616b0fc58b5SGeorge Karpenkov
617b0fc58b5SGeorge Karpenkov return getPersistentSummary(RE, ScratchArgs, RecEffect, DefEffect);
6186fdd2bd5SGeorge Karpenkov }
6196fdd2bd5SGeorge Karpenkov
updateSummaryForReceiverUnconsumedSelf(const RetainSummary * & S)620b0fc58b5SGeorge Karpenkov void RetainSummaryManager::updateSummaryForReceiverUnconsumedSelf(
621b0fc58b5SGeorge Karpenkov const RetainSummary *&S) {
6226fdd2bd5SGeorge Karpenkov
623b0fc58b5SGeorge Karpenkov RetainSummaryTemplate Template(S, *this);
624b0fc58b5SGeorge Karpenkov
625b0fc58b5SGeorge Karpenkov Template->setReceiverEffect(ArgEffect(DoNothing));
626b0fc58b5SGeorge Karpenkov Template->setRetEffect(RetEffect::MakeNoRet());
6276fdd2bd5SGeorge Karpenkov }
6286fdd2bd5SGeorge Karpenkov
629d37ff4e8SGeorge Karpenkov
updateSummaryForArgumentTypes(const AnyCall & C,const RetainSummary * & RS)630d37ff4e8SGeorge Karpenkov void RetainSummaryManager::updateSummaryForArgumentTypes(
631d37ff4e8SGeorge Karpenkov const AnyCall &C, const RetainSummary *&RS) {
632d37ff4e8SGeorge Karpenkov RetainSummaryTemplate Template(RS, *this);
633d37ff4e8SGeorge Karpenkov
634d37ff4e8SGeorge Karpenkov unsigned parm_idx = 0;
635d37ff4e8SGeorge Karpenkov for (auto pi = C.param_begin(), pe = C.param_end(); pi != pe;
636d37ff4e8SGeorge Karpenkov ++pi, ++parm_idx) {
637d37ff4e8SGeorge Karpenkov QualType QT = (*pi)->getType();
638d37ff4e8SGeorge Karpenkov
639d37ff4e8SGeorge Karpenkov // Skip already created values.
640d37ff4e8SGeorge Karpenkov if (RS->getArgEffects().contains(parm_idx))
641d37ff4e8SGeorge Karpenkov continue;
642d37ff4e8SGeorge Karpenkov
643d37ff4e8SGeorge Karpenkov ObjKind K = ObjKind::AnyObj;
644d37ff4e8SGeorge Karpenkov
645d37ff4e8SGeorge Karpenkov if (isISLObjectRef(QT)) {
646d37ff4e8SGeorge Karpenkov K = ObjKind::Generalized;
647d37ff4e8SGeorge Karpenkov } else if (isOSObjectPtr(QT)) {
648d37ff4e8SGeorge Karpenkov K = ObjKind::OS;
649d37ff4e8SGeorge Karpenkov } else if (cocoa::isCocoaObjectRef(QT)) {
650d37ff4e8SGeorge Karpenkov K = ObjKind::ObjC;
651d37ff4e8SGeorge Karpenkov } else if (coreFoundation::isCFObjectRef(QT)) {
652d37ff4e8SGeorge Karpenkov K = ObjKind::CF;
653d37ff4e8SGeorge Karpenkov }
654d37ff4e8SGeorge Karpenkov
655d37ff4e8SGeorge Karpenkov if (K != ObjKind::AnyObj)
656d37ff4e8SGeorge Karpenkov Template->addArg(AF, parm_idx,
657d37ff4e8SGeorge Karpenkov ArgEffect(RS->getDefaultArgEffect().getKind(), K));
658d37ff4e8SGeorge Karpenkov }
659d37ff4e8SGeorge Karpenkov }
660d37ff4e8SGeorge Karpenkov
6616fdd2bd5SGeorge Karpenkov const RetainSummary *
getSummary(AnyCall C,bool HasNonZeroCallbackArg,bool IsReceiverUnconsumedSelf,QualType ReceiverType)6626fdd2bd5SGeorge Karpenkov RetainSummaryManager::getSummary(AnyCall C,
6636fdd2bd5SGeorge Karpenkov bool HasNonZeroCallbackArg,
6646fdd2bd5SGeorge Karpenkov bool IsReceiverUnconsumedSelf,
6656fdd2bd5SGeorge Karpenkov QualType ReceiverType) {
6666fdd2bd5SGeorge Karpenkov const RetainSummary *Summ;
6676fdd2bd5SGeorge Karpenkov switch (C.getKind()) {
6686fdd2bd5SGeorge Karpenkov case AnyCall::Function:
6696fdd2bd5SGeorge Karpenkov case AnyCall::Constructor:
670a82ffe9dSArtem Dergachev case AnyCall::InheritedConstructor:
6716fdd2bd5SGeorge Karpenkov case AnyCall::Allocator:
6726fdd2bd5SGeorge Karpenkov case AnyCall::Deallocator:
6736fdd2bd5SGeorge Karpenkov Summ = getFunctionSummary(cast_or_null<FunctionDecl>(C.getDecl()));
6746fdd2bd5SGeorge Karpenkov break;
6756fdd2bd5SGeorge Karpenkov case AnyCall::Block:
6766fdd2bd5SGeorge Karpenkov case AnyCall::Destructor:
6776fdd2bd5SGeorge Karpenkov // FIXME: These calls are currently unsupported.
6786fdd2bd5SGeorge Karpenkov return getPersistentStopSummary();
6796fdd2bd5SGeorge Karpenkov case AnyCall::ObjCMethod: {
6802e466678SGeorge Karpenkov const auto *ME = cast_or_null<ObjCMessageExpr>(C.getExpr());
6812e466678SGeorge Karpenkov if (!ME) {
68277eae6d4SGeorge Karpenkov Summ = getMethodSummary(cast<ObjCMethodDecl>(C.getDecl()));
6832e466678SGeorge Karpenkov } else if (ME->isInstanceMessage()) {
6846fdd2bd5SGeorge Karpenkov Summ = getInstanceMethodSummary(ME, ReceiverType);
6852e466678SGeorge Karpenkov } else {
6866fdd2bd5SGeorge Karpenkov Summ = getClassMethodSummary(ME);
6872e466678SGeorge Karpenkov }
6886fdd2bd5SGeorge Karpenkov break;
6896fdd2bd5SGeorge Karpenkov }
6906fdd2bd5SGeorge Karpenkov }
6916fdd2bd5SGeorge Karpenkov
692b0fc58b5SGeorge Karpenkov if (HasNonZeroCallbackArg)
693b0fc58b5SGeorge Karpenkov Summ = updateSummaryForNonZeroCallbackArg(Summ, C);
694b0fc58b5SGeorge Karpenkov
695b0fc58b5SGeorge Karpenkov if (IsReceiverUnconsumedSelf)
696b0fc58b5SGeorge Karpenkov updateSummaryForReceiverUnconsumedSelf(Summ);
6976fdd2bd5SGeorge Karpenkov
698d37ff4e8SGeorge Karpenkov updateSummaryForArgumentTypes(C, Summ);
699d37ff4e8SGeorge Karpenkov
7006fdd2bd5SGeorge Karpenkov assert(Summ && "Unknown call type?");
7016fdd2bd5SGeorge Karpenkov return Summ;
7026fdd2bd5SGeorge Karpenkov }
7036fdd2bd5SGeorge Karpenkov
7046fdd2bd5SGeorge Karpenkov
7056fdd2bd5SGeorge Karpenkov const RetainSummary *
getCFCreateGetRuleSummary(const FunctionDecl * FD)7066fdd2bd5SGeorge Karpenkov RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) {
7076fdd2bd5SGeorge Karpenkov if (coreFoundation::followsCreateRule(FD))
7086fdd2bd5SGeorge Karpenkov return getCFSummaryCreateRule(FD);
7096fdd2bd5SGeorge Karpenkov
7106fdd2bd5SGeorge Karpenkov return getCFSummaryGetRule(FD);
7116fdd2bd5SGeorge Karpenkov }
7126fdd2bd5SGeorge Karpenkov
isTrustedReferenceCountImplementation(const Decl * FD)7136fdd2bd5SGeorge Karpenkov bool RetainSummaryManager::isTrustedReferenceCountImplementation(
71477eae6d4SGeorge Karpenkov const Decl *FD) {
7156fdd2bd5SGeorge Karpenkov return hasRCAnnotation(FD, "rc_ownership_trusted_implementation");
7166fdd2bd5SGeorge Karpenkov }
7176fdd2bd5SGeorge Karpenkov
7186ad0788cSKazu Hirata std::optional<RetainSummaryManager::BehaviorSummary>
canEval(const CallExpr * CE,const FunctionDecl * FD,bool & hasTrustedImplementationAnnotation)7196fdd2bd5SGeorge Karpenkov RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD,
7206fdd2bd5SGeorge Karpenkov bool &hasTrustedImplementationAnnotation) {
7216fdd2bd5SGeorge Karpenkov
7226fdd2bd5SGeorge Karpenkov IdentifierInfo *II = FD->getIdentifier();
7236fdd2bd5SGeorge Karpenkov if (!II)
72434e0d057SKazu Hirata return std::nullopt;
7256fdd2bd5SGeorge Karpenkov
7266fdd2bd5SGeorge Karpenkov StringRef FName = II->getName();
7276fdd2bd5SGeorge Karpenkov FName = FName.substr(FName.find_first_not_of('_'));
7286fdd2bd5SGeorge Karpenkov
7296fdd2bd5SGeorge Karpenkov QualType ResultTy = CE->getCallReturnType(Ctx);
7306fdd2bd5SGeorge Karpenkov if (ResultTy->isObjCIdType()) {
7316fdd2bd5SGeorge Karpenkov if (II->isStr("NSMakeCollectable"))
7326fdd2bd5SGeorge Karpenkov return BehaviorSummary::Identity;
7336fdd2bd5SGeorge Karpenkov } else if (ResultTy->isPointerType()) {
7346fdd2bd5SGeorge Karpenkov // Handle: (CF|CG|CV)Retain
7356fdd2bd5SGeorge Karpenkov // CFAutorelease
7366fdd2bd5SGeorge Karpenkov // It's okay to be a little sloppy here.
7376fdd2bd5SGeorge Karpenkov if (FName == "CMBufferQueueDequeueAndRetain" ||
7386fdd2bd5SGeorge Karpenkov FName == "CMBufferQueueDequeueIfDataReadyAndRetain") {
7397f25a882SArtem Dergachev // These API functions are known to NOT act as a CFRetain wrapper.
7407f25a882SArtem Dergachev // They simply make a new object owned by the caller.
74134e0d057SKazu Hirata return std::nullopt;
7426fdd2bd5SGeorge Karpenkov }
743f2192b20SArtem Dergachev if (CE->getNumArgs() == 1 &&
744f2192b20SArtem Dergachev (cocoa::isRefType(ResultTy, "CF", FName) ||
7456fdd2bd5SGeorge Karpenkov cocoa::isRefType(ResultTy, "CG", FName) ||
746f2192b20SArtem Dergachev cocoa::isRefType(ResultTy, "CV", FName)) &&
747f2192b20SArtem Dergachev (isRetain(FD, FName) || isAutorelease(FD, FName) ||
748f2192b20SArtem Dergachev isMakeCollectable(FName)))
7496fdd2bd5SGeorge Karpenkov return BehaviorSummary::Identity;
7506fdd2bd5SGeorge Karpenkov
7516fdd2bd5SGeorge Karpenkov // safeMetaCast is called by OSDynamicCast.
7526fdd2bd5SGeorge Karpenkov // We assume that OSDynamicCast is either an identity (cast is OK,
7536fdd2bd5SGeorge Karpenkov // the input was non-zero),
7546fdd2bd5SGeorge Karpenkov // or that it returns zero (when the cast failed, or the input
7556fdd2bd5SGeorge Karpenkov // was zero).
7566fdd2bd5SGeorge Karpenkov if (TrackOSObjects) {
7576fdd2bd5SGeorge Karpenkov if (isOSObjectDynamicCast(FName) && FD->param_size() >= 1) {
7586fdd2bd5SGeorge Karpenkov return BehaviorSummary::IdentityOrZero;
759b03854f8SArtem Dergachev } else if (isOSObjectRequiredCast(FName) && FD->param_size() >= 1) {
760b03854f8SArtem Dergachev return BehaviorSummary::Identity;
7616fdd2bd5SGeorge Karpenkov } else if (isOSObjectThisCast(FName) && isa<CXXMethodDecl>(FD) &&
7626fdd2bd5SGeorge Karpenkov !cast<CXXMethodDecl>(FD)->isStatic()) {
7636fdd2bd5SGeorge Karpenkov return BehaviorSummary::IdentityThis;
7646fdd2bd5SGeorge Karpenkov }
7656fdd2bd5SGeorge Karpenkov }
7666fdd2bd5SGeorge Karpenkov
7676fdd2bd5SGeorge Karpenkov const FunctionDecl* FDD = FD->getDefinition();
7686fdd2bd5SGeorge Karpenkov if (FDD && isTrustedReferenceCountImplementation(FDD)) {
7696fdd2bd5SGeorge Karpenkov hasTrustedImplementationAnnotation = true;
7706fdd2bd5SGeorge Karpenkov return BehaviorSummary::Identity;
7716fdd2bd5SGeorge Karpenkov }
7726fdd2bd5SGeorge Karpenkov }
7736fdd2bd5SGeorge Karpenkov
7746fdd2bd5SGeorge Karpenkov if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
7756fdd2bd5SGeorge Karpenkov const CXXRecordDecl *Parent = MD->getParent();
7766fdd2bd5SGeorge Karpenkov if (TrackOSObjects && Parent && isOSObjectSubclass(Parent))
7776fdd2bd5SGeorge Karpenkov if (FName == "release" || FName == "retain")
7786fdd2bd5SGeorge Karpenkov return BehaviorSummary::NoOp;
7796fdd2bd5SGeorge Karpenkov }
7806fdd2bd5SGeorge Karpenkov
78134e0d057SKazu Hirata return std::nullopt;
7826fdd2bd5SGeorge Karpenkov }
7836fdd2bd5SGeorge Karpenkov
7846fdd2bd5SGeorge Karpenkov const RetainSummary *
getUnarySummary(const FunctionType * FT,ArgEffectKind AE)7856fdd2bd5SGeorge Karpenkov RetainSummaryManager::getUnarySummary(const FunctionType* FT,
7866fdd2bd5SGeorge Karpenkov ArgEffectKind AE) {
7876fdd2bd5SGeorge Karpenkov
7886fdd2bd5SGeorge Karpenkov // Unary functions have no arg effects by definition.
7896fdd2bd5SGeorge Karpenkov ArgEffects ScratchArgs(AF.getEmptyMap());
7906fdd2bd5SGeorge Karpenkov
791d8e5a0c4SZarko Todorovski // Verify that this is *really* a unary function. This can
7926fdd2bd5SGeorge Karpenkov // happen if people do weird things.
7936fdd2bd5SGeorge Karpenkov const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(FT);
7946fdd2bd5SGeorge Karpenkov if (!FTP || FTP->getNumParams() != 1)
7956fdd2bd5SGeorge Karpenkov return getPersistentStopSummary();
7966fdd2bd5SGeorge Karpenkov
7976fdd2bd5SGeorge Karpenkov ArgEffect Effect(AE, ObjKind::CF);
7986fdd2bd5SGeorge Karpenkov
7996fdd2bd5SGeorge Karpenkov ScratchArgs = AF.add(ScratchArgs, 0, Effect);
8006fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(),
8016fdd2bd5SGeorge Karpenkov ScratchArgs,
8026fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing), ArgEffect(DoNothing));
8036fdd2bd5SGeorge Karpenkov }
8046fdd2bd5SGeorge Karpenkov
8056fdd2bd5SGeorge Karpenkov const RetainSummary *
getOSSummaryRetainRule(const FunctionDecl * FD)8066fdd2bd5SGeorge Karpenkov RetainSummaryManager::getOSSummaryRetainRule(const FunctionDecl *FD) {
8076fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(),
8086fdd2bd5SGeorge Karpenkov AF.getEmptyMap(),
8096fdd2bd5SGeorge Karpenkov /*ReceiverEff=*/ArgEffect(DoNothing),
8106fdd2bd5SGeorge Karpenkov /*DefaultEff=*/ArgEffect(DoNothing),
8116fdd2bd5SGeorge Karpenkov /*ThisEff=*/ArgEffect(IncRef, ObjKind::OS));
8126fdd2bd5SGeorge Karpenkov }
8136fdd2bd5SGeorge Karpenkov
8146fdd2bd5SGeorge Karpenkov const RetainSummary *
getOSSummaryReleaseRule(const FunctionDecl * FD)8156fdd2bd5SGeorge Karpenkov RetainSummaryManager::getOSSummaryReleaseRule(const FunctionDecl *FD) {
8166fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(),
8176fdd2bd5SGeorge Karpenkov AF.getEmptyMap(),
8186fdd2bd5SGeorge Karpenkov /*ReceiverEff=*/ArgEffect(DoNothing),
8196fdd2bd5SGeorge Karpenkov /*DefaultEff=*/ArgEffect(DoNothing),
8206fdd2bd5SGeorge Karpenkov /*ThisEff=*/ArgEffect(DecRef, ObjKind::OS));
8216fdd2bd5SGeorge Karpenkov }
8226fdd2bd5SGeorge Karpenkov
8236fdd2bd5SGeorge Karpenkov const RetainSummary *
getOSSummaryFreeRule(const FunctionDecl * FD)8246fdd2bd5SGeorge Karpenkov RetainSummaryManager::getOSSummaryFreeRule(const FunctionDecl *FD) {
8256fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNoRet(),
8266fdd2bd5SGeorge Karpenkov AF.getEmptyMap(),
8276fdd2bd5SGeorge Karpenkov /*ReceiverEff=*/ArgEffect(DoNothing),
8286fdd2bd5SGeorge Karpenkov /*DefaultEff=*/ArgEffect(DoNothing),
8296fdd2bd5SGeorge Karpenkov /*ThisEff=*/ArgEffect(Dealloc, ObjKind::OS));
8306fdd2bd5SGeorge Karpenkov }
8316fdd2bd5SGeorge Karpenkov
8326fdd2bd5SGeorge Karpenkov const RetainSummary *
getOSSummaryCreateRule(const FunctionDecl * FD)8336fdd2bd5SGeorge Karpenkov RetainSummaryManager::getOSSummaryCreateRule(const FunctionDecl *FD) {
8346fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeOwned(ObjKind::OS),
8356fdd2bd5SGeorge Karpenkov AF.getEmptyMap());
8366fdd2bd5SGeorge Karpenkov }
8376fdd2bd5SGeorge Karpenkov
8386fdd2bd5SGeorge Karpenkov const RetainSummary *
getOSSummaryGetRule(const FunctionDecl * FD)8396fdd2bd5SGeorge Karpenkov RetainSummaryManager::getOSSummaryGetRule(const FunctionDecl *FD) {
8406fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::OS),
8416fdd2bd5SGeorge Karpenkov AF.getEmptyMap());
8426fdd2bd5SGeorge Karpenkov }
8436fdd2bd5SGeorge Karpenkov
8446fdd2bd5SGeorge Karpenkov const RetainSummary *
getCFSummaryCreateRule(const FunctionDecl * FD)8456fdd2bd5SGeorge Karpenkov RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) {
8466fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF),
8476fdd2bd5SGeorge Karpenkov ArgEffects(AF.getEmptyMap()));
8486fdd2bd5SGeorge Karpenkov }
8496fdd2bd5SGeorge Karpenkov
8506fdd2bd5SGeorge Karpenkov const RetainSummary *
getCFSummaryGetRule(const FunctionDecl * FD)8516fdd2bd5SGeorge Karpenkov RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) {
8526fdd2bd5SGeorge Karpenkov return getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::CF),
8536fdd2bd5SGeorge Karpenkov ArgEffects(AF.getEmptyMap()),
8546fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing), ArgEffect(DoNothing));
8556fdd2bd5SGeorge Karpenkov }
8566fdd2bd5SGeorge Karpenkov
8576fdd2bd5SGeorge Karpenkov
8586fdd2bd5SGeorge Karpenkov
8596fdd2bd5SGeorge Karpenkov
8606fdd2bd5SGeorge Karpenkov //===----------------------------------------------------------------------===//
8616fdd2bd5SGeorge Karpenkov // Summary creation for Selectors.
8626fdd2bd5SGeorge Karpenkov //===----------------------------------------------------------------------===//
8636fdd2bd5SGeorge Karpenkov
8646ad0788cSKazu Hirata std::optional<RetEffect>
getRetEffectFromAnnotations(QualType RetTy,const Decl * D)8656fdd2bd5SGeorge Karpenkov RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy,
8666fdd2bd5SGeorge Karpenkov const Decl *D) {
8676fdd2bd5SGeorge Karpenkov if (hasAnyEnabledAttrOf<NSReturnsRetainedAttr>(D, RetTy))
8686fdd2bd5SGeorge Karpenkov return ObjCAllocRetE;
8696fdd2bd5SGeorge Karpenkov
8706fdd2bd5SGeorge Karpenkov if (auto K = hasAnyEnabledAttrOf<CFReturnsRetainedAttr, OSReturnsRetainedAttr,
8716fdd2bd5SGeorge Karpenkov GeneralizedReturnsRetainedAttr>(D, RetTy))
8726fdd2bd5SGeorge Karpenkov return RetEffect::MakeOwned(*K);
8736fdd2bd5SGeorge Karpenkov
8746fdd2bd5SGeorge Karpenkov if (auto K = hasAnyEnabledAttrOf<
8756fdd2bd5SGeorge Karpenkov CFReturnsNotRetainedAttr, OSReturnsNotRetainedAttr,
8766fdd2bd5SGeorge Karpenkov GeneralizedReturnsNotRetainedAttr, NSReturnsNotRetainedAttr,
8776fdd2bd5SGeorge Karpenkov NSReturnsAutoreleasedAttr>(D, RetTy))
8786fdd2bd5SGeorge Karpenkov return RetEffect::MakeNotOwned(*K);
8796fdd2bd5SGeorge Karpenkov
8806fdd2bd5SGeorge Karpenkov if (const auto *MD = dyn_cast<CXXMethodDecl>(D))
8816fdd2bd5SGeorge Karpenkov for (const auto *PD : MD->overridden_methods())
8826fdd2bd5SGeorge Karpenkov if (auto RE = getRetEffectFromAnnotations(RetTy, PD))
8836fdd2bd5SGeorge Karpenkov return RE;
8846fdd2bd5SGeorge Karpenkov
88534e0d057SKazu Hirata return std::nullopt;
8866fdd2bd5SGeorge Karpenkov }
8876fdd2bd5SGeorge Karpenkov
8881cb15b10SAaron Puchert /// \return Whether the chain of typedefs starting from @c QT
8891cb15b10SAaron Puchert /// has a typedef with a given name @c Name.
hasTypedefNamed(QualType QT,StringRef Name)8906fdd2bd5SGeorge Karpenkov static bool hasTypedefNamed(QualType QT,
8916fdd2bd5SGeorge Karpenkov StringRef Name) {
89215f3cd6bSMatheus Izvekov while (auto *T = QT->getAs<TypedefType>()) {
8936fdd2bd5SGeorge Karpenkov const auto &Context = T->getDecl()->getASTContext();
8946fdd2bd5SGeorge Karpenkov if (T->getDecl()->getIdentifier() == &Context.Idents.get(Name))
8956fdd2bd5SGeorge Karpenkov return true;
8966fdd2bd5SGeorge Karpenkov QT = T->getDecl()->getUnderlyingType();
8976fdd2bd5SGeorge Karpenkov }
8986fdd2bd5SGeorge Karpenkov return false;
8996fdd2bd5SGeorge Karpenkov }
9006fdd2bd5SGeorge Karpenkov
getCallableReturnType(const NamedDecl * ND)9016fdd2bd5SGeorge Karpenkov static QualType getCallableReturnType(const NamedDecl *ND) {
9026fdd2bd5SGeorge Karpenkov if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
9036fdd2bd5SGeorge Karpenkov return FD->getReturnType();
9046fdd2bd5SGeorge Karpenkov } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(ND)) {
9056fdd2bd5SGeorge Karpenkov return MD->getReturnType();
9066fdd2bd5SGeorge Karpenkov } else {
9076fdd2bd5SGeorge Karpenkov llvm_unreachable("Unexpected decl");
9086fdd2bd5SGeorge Karpenkov }
9096fdd2bd5SGeorge Karpenkov }
9106fdd2bd5SGeorge Karpenkov
applyParamAnnotationEffect(const ParmVarDecl * pd,unsigned parm_idx,const NamedDecl * FD,RetainSummaryTemplate & Template)9116fdd2bd5SGeorge Karpenkov bool RetainSummaryManager::applyParamAnnotationEffect(
9126fdd2bd5SGeorge Karpenkov const ParmVarDecl *pd, unsigned parm_idx, const NamedDecl *FD,
9136fdd2bd5SGeorge Karpenkov RetainSummaryTemplate &Template) {
9146fdd2bd5SGeorge Karpenkov QualType QT = pd->getType();
9156fdd2bd5SGeorge Karpenkov if (auto K =
9166fdd2bd5SGeorge Karpenkov hasAnyEnabledAttrOf<NSConsumedAttr, CFConsumedAttr, OSConsumedAttr,
9176fdd2bd5SGeorge Karpenkov GeneralizedConsumedAttr>(pd, QT)) {
9186fdd2bd5SGeorge Karpenkov Template->addArg(AF, parm_idx, ArgEffect(DecRef, *K));
9196fdd2bd5SGeorge Karpenkov return true;
9206fdd2bd5SGeorge Karpenkov } else if (auto K = hasAnyEnabledAttrOf<
9216fdd2bd5SGeorge Karpenkov CFReturnsRetainedAttr, OSReturnsRetainedAttr,
9226fdd2bd5SGeorge Karpenkov OSReturnsRetainedOnNonZeroAttr, OSReturnsRetainedOnZeroAttr,
9236fdd2bd5SGeorge Karpenkov GeneralizedReturnsRetainedAttr>(pd, QT)) {
9246fdd2bd5SGeorge Karpenkov
9256fdd2bd5SGeorge Karpenkov // For OSObjects, we try to guess whether the object is created based
9266fdd2bd5SGeorge Karpenkov // on the return value.
9276fdd2bd5SGeorge Karpenkov if (K == ObjKind::OS) {
9286fdd2bd5SGeorge Karpenkov QualType QT = getCallableReturnType(FD);
9296fdd2bd5SGeorge Karpenkov
9306fdd2bd5SGeorge Karpenkov bool HasRetainedOnZero = pd->hasAttr<OSReturnsRetainedOnZeroAttr>();
9316fdd2bd5SGeorge Karpenkov bool HasRetainedOnNonZero = pd->hasAttr<OSReturnsRetainedOnNonZeroAttr>();
9326fdd2bd5SGeorge Karpenkov
9336fdd2bd5SGeorge Karpenkov // The usual convention is to create an object on non-zero return, but
9346fdd2bd5SGeorge Karpenkov // it's reverted if the typedef chain has a typedef kern_return_t,
9356fdd2bd5SGeorge Karpenkov // because kReturnSuccess constant is defined as zero.
9366fdd2bd5SGeorge Karpenkov // The convention can be overwritten by custom attributes.
9376fdd2bd5SGeorge Karpenkov bool SuccessOnZero =
9386fdd2bd5SGeorge Karpenkov HasRetainedOnZero ||
9396fdd2bd5SGeorge Karpenkov (hasTypedefNamed(QT, "kern_return_t") && !HasRetainedOnNonZero);
9406fdd2bd5SGeorge Karpenkov bool ShouldSplit = !QT.isNull() && !QT->isVoidType();
9416fdd2bd5SGeorge Karpenkov ArgEffectKind AK = RetainedOutParameter;
9426fdd2bd5SGeorge Karpenkov if (ShouldSplit && SuccessOnZero) {
9436fdd2bd5SGeorge Karpenkov AK = RetainedOutParameterOnZero;
9446fdd2bd5SGeorge Karpenkov } else if (ShouldSplit && (!SuccessOnZero || HasRetainedOnNonZero)) {
9456fdd2bd5SGeorge Karpenkov AK = RetainedOutParameterOnNonZero;
9466fdd2bd5SGeorge Karpenkov }
9476fdd2bd5SGeorge Karpenkov Template->addArg(AF, parm_idx, ArgEffect(AK, ObjKind::OS));
9486fdd2bd5SGeorge Karpenkov }
9496fdd2bd5SGeorge Karpenkov
9506fdd2bd5SGeorge Karpenkov // For others:
9516fdd2bd5SGeorge Karpenkov // Do nothing. Retained out parameters will either point to a +1 reference
9526fdd2bd5SGeorge Karpenkov // or NULL, but the way you check for failure differs depending on the
9536fdd2bd5SGeorge Karpenkov // API. Consequently, we don't have a good way to track them yet.
9546fdd2bd5SGeorge Karpenkov return true;
9556fdd2bd5SGeorge Karpenkov } else if (auto K = hasAnyEnabledAttrOf<CFReturnsNotRetainedAttr,
9566fdd2bd5SGeorge Karpenkov OSReturnsNotRetainedAttr,
9576fdd2bd5SGeorge Karpenkov GeneralizedReturnsNotRetainedAttr>(
9586fdd2bd5SGeorge Karpenkov pd, QT)) {
9596fdd2bd5SGeorge Karpenkov Template->addArg(AF, parm_idx, ArgEffect(UnretainedOutParameter, *K));
9606fdd2bd5SGeorge Karpenkov return true;
9616fdd2bd5SGeorge Karpenkov }
9626fdd2bd5SGeorge Karpenkov
9636fdd2bd5SGeorge Karpenkov if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
9646fdd2bd5SGeorge Karpenkov for (const auto *OD : MD->overridden_methods()) {
9656fdd2bd5SGeorge Karpenkov const ParmVarDecl *OP = OD->parameters()[parm_idx];
9666fdd2bd5SGeorge Karpenkov if (applyParamAnnotationEffect(OP, parm_idx, OD, Template))
9676fdd2bd5SGeorge Karpenkov return true;
9686fdd2bd5SGeorge Karpenkov }
9696fdd2bd5SGeorge Karpenkov }
9706fdd2bd5SGeorge Karpenkov
9716fdd2bd5SGeorge Karpenkov return false;
9726fdd2bd5SGeorge Karpenkov }
9736fdd2bd5SGeorge Karpenkov
9746fdd2bd5SGeorge Karpenkov void
updateSummaryFromAnnotations(const RetainSummary * & Summ,const FunctionDecl * FD)9756fdd2bd5SGeorge Karpenkov RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
9766fdd2bd5SGeorge Karpenkov const FunctionDecl *FD) {
9776fdd2bd5SGeorge Karpenkov if (!FD)
9786fdd2bd5SGeorge Karpenkov return;
9796fdd2bd5SGeorge Karpenkov
9806fdd2bd5SGeorge Karpenkov assert(Summ && "Must have a summary to add annotations to.");
9816fdd2bd5SGeorge Karpenkov RetainSummaryTemplate Template(Summ, *this);
9826fdd2bd5SGeorge Karpenkov
9836fdd2bd5SGeorge Karpenkov // Effects on the parameters.
9846fdd2bd5SGeorge Karpenkov unsigned parm_idx = 0;
9856fdd2bd5SGeorge Karpenkov for (auto pi = FD->param_begin(),
9866fdd2bd5SGeorge Karpenkov pe = FD->param_end(); pi != pe; ++pi, ++parm_idx)
9876fdd2bd5SGeorge Karpenkov applyParamAnnotationEffect(*pi, parm_idx, FD, Template);
9886fdd2bd5SGeorge Karpenkov
9896fdd2bd5SGeorge Karpenkov QualType RetTy = FD->getReturnType();
9906ad0788cSKazu Hirata if (std::optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, FD))
9916fdd2bd5SGeorge Karpenkov Template->setRetEffect(*RetE);
9926fdd2bd5SGeorge Karpenkov
9936fdd2bd5SGeorge Karpenkov if (hasAnyEnabledAttrOf<OSConsumesThisAttr>(FD, RetTy))
9946fdd2bd5SGeorge Karpenkov Template->setThisEffect(ArgEffect(DecRef, ObjKind::OS));
9956fdd2bd5SGeorge Karpenkov }
9966fdd2bd5SGeorge Karpenkov
9976fdd2bd5SGeorge Karpenkov void
updateSummaryFromAnnotations(const RetainSummary * & Summ,const ObjCMethodDecl * MD)9986fdd2bd5SGeorge Karpenkov RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
9996fdd2bd5SGeorge Karpenkov const ObjCMethodDecl *MD) {
10006fdd2bd5SGeorge Karpenkov if (!MD)
10016fdd2bd5SGeorge Karpenkov return;
10026fdd2bd5SGeorge Karpenkov
10036fdd2bd5SGeorge Karpenkov assert(Summ && "Must have a valid summary to add annotations to");
10046fdd2bd5SGeorge Karpenkov RetainSummaryTemplate Template(Summ, *this);
10056fdd2bd5SGeorge Karpenkov
10066fdd2bd5SGeorge Karpenkov // Effects on the receiver.
10076fdd2bd5SGeorge Karpenkov if (hasAnyEnabledAttrOf<NSConsumesSelfAttr>(MD, MD->getReturnType()))
10086fdd2bd5SGeorge Karpenkov Template->setReceiverEffect(ArgEffect(DecRef, ObjKind::ObjC));
10096fdd2bd5SGeorge Karpenkov
10106fdd2bd5SGeorge Karpenkov // Effects on the parameters.
10116fdd2bd5SGeorge Karpenkov unsigned parm_idx = 0;
10126fdd2bd5SGeorge Karpenkov for (auto pi = MD->param_begin(), pe = MD->param_end(); pi != pe;
10136fdd2bd5SGeorge Karpenkov ++pi, ++parm_idx)
10146fdd2bd5SGeorge Karpenkov applyParamAnnotationEffect(*pi, parm_idx, MD, Template);
10156fdd2bd5SGeorge Karpenkov
10166fdd2bd5SGeorge Karpenkov QualType RetTy = MD->getReturnType();
10176ad0788cSKazu Hirata if (std::optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, MD))
10186fdd2bd5SGeorge Karpenkov Template->setRetEffect(*RetE);
10196fdd2bd5SGeorge Karpenkov }
10206fdd2bd5SGeorge Karpenkov
10216fdd2bd5SGeorge Karpenkov const RetainSummary *
getStandardMethodSummary(const ObjCMethodDecl * MD,Selector S,QualType RetTy)10226fdd2bd5SGeorge Karpenkov RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD,
10236fdd2bd5SGeorge Karpenkov Selector S, QualType RetTy) {
10246fdd2bd5SGeorge Karpenkov // Any special effects?
10256fdd2bd5SGeorge Karpenkov ArgEffect ReceiverEff = ArgEffect(DoNothing, ObjKind::ObjC);
10266fdd2bd5SGeorge Karpenkov RetEffect ResultEff = RetEffect::MakeNoRet();
10276fdd2bd5SGeorge Karpenkov
10286fdd2bd5SGeorge Karpenkov // Check the method family, and apply any default annotations.
10296fdd2bd5SGeorge Karpenkov switch (MD ? MD->getMethodFamily() : S.getMethodFamily()) {
10306fdd2bd5SGeorge Karpenkov case OMF_None:
10316fdd2bd5SGeorge Karpenkov case OMF_initialize:
10326fdd2bd5SGeorge Karpenkov case OMF_performSelector:
10336fdd2bd5SGeorge Karpenkov // Assume all Objective-C methods follow Cocoa Memory Management rules.
10346fdd2bd5SGeorge Karpenkov // FIXME: Does the non-threaded performSelector family really belong here?
10356fdd2bd5SGeorge Karpenkov // The selector could be, say, @selector(copy).
10366fdd2bd5SGeorge Karpenkov if (cocoa::isCocoaObjectRef(RetTy))
10376fdd2bd5SGeorge Karpenkov ResultEff = RetEffect::MakeNotOwned(ObjKind::ObjC);
10386fdd2bd5SGeorge Karpenkov else if (coreFoundation::isCFObjectRef(RetTy)) {
10396fdd2bd5SGeorge Karpenkov // ObjCMethodDecl currently doesn't consider CF objects as valid return
10406fdd2bd5SGeorge Karpenkov // values for alloc, new, copy, or mutableCopy, so we have to
10416fdd2bd5SGeorge Karpenkov // double-check with the selector. This is ugly, but there aren't that
10426fdd2bd5SGeorge Karpenkov // many Objective-C methods that return CF objects, right?
10436fdd2bd5SGeorge Karpenkov if (MD) {
10446fdd2bd5SGeorge Karpenkov switch (S.getMethodFamily()) {
10456fdd2bd5SGeorge Karpenkov case OMF_alloc:
10466fdd2bd5SGeorge Karpenkov case OMF_new:
10476fdd2bd5SGeorge Karpenkov case OMF_copy:
10486fdd2bd5SGeorge Karpenkov case OMF_mutableCopy:
10496fdd2bd5SGeorge Karpenkov ResultEff = RetEffect::MakeOwned(ObjKind::CF);
10506fdd2bd5SGeorge Karpenkov break;
10516fdd2bd5SGeorge Karpenkov default:
10526fdd2bd5SGeorge Karpenkov ResultEff = RetEffect::MakeNotOwned(ObjKind::CF);
10536fdd2bd5SGeorge Karpenkov break;
10546fdd2bd5SGeorge Karpenkov }
10556fdd2bd5SGeorge Karpenkov } else {
10566fdd2bd5SGeorge Karpenkov ResultEff = RetEffect::MakeNotOwned(ObjKind::CF);
10576fdd2bd5SGeorge Karpenkov }
10586fdd2bd5SGeorge Karpenkov }
10596fdd2bd5SGeorge Karpenkov break;
10606fdd2bd5SGeorge Karpenkov case OMF_init:
10616fdd2bd5SGeorge Karpenkov ResultEff = ObjCInitRetE;
10626fdd2bd5SGeorge Karpenkov ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC);
10636fdd2bd5SGeorge Karpenkov break;
10646fdd2bd5SGeorge Karpenkov case OMF_alloc:
10656fdd2bd5SGeorge Karpenkov case OMF_new:
10666fdd2bd5SGeorge Karpenkov case OMF_copy:
10676fdd2bd5SGeorge Karpenkov case OMF_mutableCopy:
10686fdd2bd5SGeorge Karpenkov if (cocoa::isCocoaObjectRef(RetTy))
10696fdd2bd5SGeorge Karpenkov ResultEff = ObjCAllocRetE;
10706fdd2bd5SGeorge Karpenkov else if (coreFoundation::isCFObjectRef(RetTy))
10716fdd2bd5SGeorge Karpenkov ResultEff = RetEffect::MakeOwned(ObjKind::CF);
10726fdd2bd5SGeorge Karpenkov break;
10736fdd2bd5SGeorge Karpenkov case OMF_autorelease:
10746fdd2bd5SGeorge Karpenkov ReceiverEff = ArgEffect(Autorelease, ObjKind::ObjC);
10756fdd2bd5SGeorge Karpenkov break;
10766fdd2bd5SGeorge Karpenkov case OMF_retain:
10776fdd2bd5SGeorge Karpenkov ReceiverEff = ArgEffect(IncRef, ObjKind::ObjC);
10786fdd2bd5SGeorge Karpenkov break;
10796fdd2bd5SGeorge Karpenkov case OMF_release:
10806fdd2bd5SGeorge Karpenkov ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC);
10816fdd2bd5SGeorge Karpenkov break;
10826fdd2bd5SGeorge Karpenkov case OMF_dealloc:
10836fdd2bd5SGeorge Karpenkov ReceiverEff = ArgEffect(Dealloc, ObjKind::ObjC);
10846fdd2bd5SGeorge Karpenkov break;
10856fdd2bd5SGeorge Karpenkov case OMF_self:
10866fdd2bd5SGeorge Karpenkov // -self is handled specially by the ExprEngine to propagate the receiver.
10876fdd2bd5SGeorge Karpenkov break;
10886fdd2bd5SGeorge Karpenkov case OMF_retainCount:
10896fdd2bd5SGeorge Karpenkov case OMF_finalize:
10906fdd2bd5SGeorge Karpenkov // These methods don't return objects.
10916fdd2bd5SGeorge Karpenkov break;
10926fdd2bd5SGeorge Karpenkov }
10936fdd2bd5SGeorge Karpenkov
10946fdd2bd5SGeorge Karpenkov // If one of the arguments in the selector has the keyword 'delegate' we
10956fdd2bd5SGeorge Karpenkov // should stop tracking the reference count for the receiver. This is
10966fdd2bd5SGeorge Karpenkov // because the reference count is quite possibly handled by a delegate
10976fdd2bd5SGeorge Karpenkov // method.
10986fdd2bd5SGeorge Karpenkov if (S.isKeywordSelector()) {
10996fdd2bd5SGeorge Karpenkov for (unsigned i = 0, e = S.getNumArgs(); i != e; ++i) {
11006fdd2bd5SGeorge Karpenkov StringRef Slot = S.getNameForSlot(i);
1101*234da203SKazu Hirata if (Slot.ends_with_insensitive("delegate")) {
11026fdd2bd5SGeorge Karpenkov if (ResultEff == ObjCInitRetE)
11036fdd2bd5SGeorge Karpenkov ResultEff = RetEffect::MakeNoRetHard();
11046fdd2bd5SGeorge Karpenkov else
11056fdd2bd5SGeorge Karpenkov ReceiverEff = ArgEffect(StopTrackingHard, ObjKind::ObjC);
11066fdd2bd5SGeorge Karpenkov }
11076fdd2bd5SGeorge Karpenkov }
11086fdd2bd5SGeorge Karpenkov }
11096fdd2bd5SGeorge Karpenkov
11106fdd2bd5SGeorge Karpenkov if (ReceiverEff.getKind() == DoNothing &&
11116fdd2bd5SGeorge Karpenkov ResultEff.getKind() == RetEffect::NoRet)
11126fdd2bd5SGeorge Karpenkov return getDefaultSummary();
11136fdd2bd5SGeorge Karpenkov
11146fdd2bd5SGeorge Karpenkov return getPersistentSummary(ResultEff, ArgEffects(AF.getEmptyMap()),
11156fdd2bd5SGeorge Karpenkov ArgEffect(ReceiverEff), ArgEffect(MayEscape));
11166fdd2bd5SGeorge Karpenkov }
11176fdd2bd5SGeorge Karpenkov
11186fdd2bd5SGeorge Karpenkov const RetainSummary *
getClassMethodSummary(const ObjCMessageExpr * ME)11196fdd2bd5SGeorge Karpenkov RetainSummaryManager::getClassMethodSummary(const ObjCMessageExpr *ME) {
11206fdd2bd5SGeorge Karpenkov assert(!ME->isInstanceMessage());
11216fdd2bd5SGeorge Karpenkov const ObjCInterfaceDecl *Class = ME->getReceiverInterface();
11226fdd2bd5SGeorge Karpenkov
11236fdd2bd5SGeorge Karpenkov return getMethodSummary(ME->getSelector(), Class, ME->getMethodDecl(),
11246fdd2bd5SGeorge Karpenkov ME->getType(), ObjCClassMethodSummaries);
11256fdd2bd5SGeorge Karpenkov }
11266fdd2bd5SGeorge Karpenkov
getInstanceMethodSummary(const ObjCMessageExpr * ME,QualType ReceiverType)11276fdd2bd5SGeorge Karpenkov const RetainSummary *RetainSummaryManager::getInstanceMethodSummary(
11286fdd2bd5SGeorge Karpenkov const ObjCMessageExpr *ME,
11296fdd2bd5SGeorge Karpenkov QualType ReceiverType) {
11306fdd2bd5SGeorge Karpenkov const ObjCInterfaceDecl *ReceiverClass = nullptr;
11316fdd2bd5SGeorge Karpenkov
11326fdd2bd5SGeorge Karpenkov // We do better tracking of the type of the object than the core ExprEngine.
11336fdd2bd5SGeorge Karpenkov // See if we have its type in our private state.
11346fdd2bd5SGeorge Karpenkov if (!ReceiverType.isNull())
11356fdd2bd5SGeorge Karpenkov if (const auto *PT = ReceiverType->getAs<ObjCObjectPointerType>())
11366fdd2bd5SGeorge Karpenkov ReceiverClass = PT->getInterfaceDecl();
11376fdd2bd5SGeorge Karpenkov
11386fdd2bd5SGeorge Karpenkov // If we don't know what kind of object this is, fall back to its static type.
11396fdd2bd5SGeorge Karpenkov if (!ReceiverClass)
11406fdd2bd5SGeorge Karpenkov ReceiverClass = ME->getReceiverInterface();
11416fdd2bd5SGeorge Karpenkov
11426fdd2bd5SGeorge Karpenkov // FIXME: The receiver could be a reference to a class, meaning that
11436fdd2bd5SGeorge Karpenkov // we should use the class method.
11446fdd2bd5SGeorge Karpenkov // id x = [NSObject class];
11456fdd2bd5SGeorge Karpenkov // [x performSelector:... withObject:... afterDelay:...];
11466fdd2bd5SGeorge Karpenkov Selector S = ME->getSelector();
11476fdd2bd5SGeorge Karpenkov const ObjCMethodDecl *Method = ME->getMethodDecl();
11486fdd2bd5SGeorge Karpenkov if (!Method && ReceiverClass)
11496fdd2bd5SGeorge Karpenkov Method = ReceiverClass->getInstanceMethod(S);
11506fdd2bd5SGeorge Karpenkov
11516fdd2bd5SGeorge Karpenkov return getMethodSummary(S, ReceiverClass, Method, ME->getType(),
11526fdd2bd5SGeorge Karpenkov ObjCMethodSummaries);
11536fdd2bd5SGeorge Karpenkov }
11546fdd2bd5SGeorge Karpenkov
11556fdd2bd5SGeorge Karpenkov const RetainSummary *
getMethodSummary(Selector S,const ObjCInterfaceDecl * ID,const ObjCMethodDecl * MD,QualType RetTy,ObjCMethodSummariesTy & CachedSummaries)11566fdd2bd5SGeorge Karpenkov RetainSummaryManager::getMethodSummary(Selector S,
11576fdd2bd5SGeorge Karpenkov const ObjCInterfaceDecl *ID,
11586fdd2bd5SGeorge Karpenkov const ObjCMethodDecl *MD, QualType RetTy,
11596fdd2bd5SGeorge Karpenkov ObjCMethodSummariesTy &CachedSummaries) {
11606fdd2bd5SGeorge Karpenkov
11616fdd2bd5SGeorge Karpenkov // Objective-C method summaries are only applicable to ObjC and CF objects.
11626fdd2bd5SGeorge Karpenkov if (!TrackObjCAndCFObjects)
11636fdd2bd5SGeorge Karpenkov return getDefaultSummary();
11646fdd2bd5SGeorge Karpenkov
11656fdd2bd5SGeorge Karpenkov // Look up a summary in our summary cache.
11666fdd2bd5SGeorge Karpenkov const RetainSummary *Summ = CachedSummaries.find(ID, S);
11676fdd2bd5SGeorge Karpenkov
11686fdd2bd5SGeorge Karpenkov if (!Summ) {
11696fdd2bd5SGeorge Karpenkov Summ = getStandardMethodSummary(MD, S, RetTy);
11706fdd2bd5SGeorge Karpenkov
11716fdd2bd5SGeorge Karpenkov // Annotations override defaults.
11726fdd2bd5SGeorge Karpenkov updateSummaryFromAnnotations(Summ, MD);
11736fdd2bd5SGeorge Karpenkov
11746fdd2bd5SGeorge Karpenkov // Memoize the summary.
11756fdd2bd5SGeorge Karpenkov CachedSummaries[ObjCSummaryKey(ID, S)] = Summ;
11766fdd2bd5SGeorge Karpenkov }
11776fdd2bd5SGeorge Karpenkov
11786fdd2bd5SGeorge Karpenkov return Summ;
11796fdd2bd5SGeorge Karpenkov }
11806fdd2bd5SGeorge Karpenkov
InitializeClassMethodSummaries()11816fdd2bd5SGeorge Karpenkov void RetainSummaryManager::InitializeClassMethodSummaries() {
11826fdd2bd5SGeorge Karpenkov ArgEffects ScratchArgs = AF.getEmptyMap();
11836fdd2bd5SGeorge Karpenkov
11846fdd2bd5SGeorge Karpenkov // Create the [NSAssertionHandler currentHander] summary.
11856fdd2bd5SGeorge Karpenkov addClassMethSummary("NSAssertionHandler", "currentHandler",
11866fdd2bd5SGeorge Karpenkov getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::ObjC),
11876fdd2bd5SGeorge Karpenkov ScratchArgs));
11886fdd2bd5SGeorge Karpenkov
11896fdd2bd5SGeorge Karpenkov // Create the [NSAutoreleasePool addObject:] summary.
11906fdd2bd5SGeorge Karpenkov ScratchArgs = AF.add(ScratchArgs, 0, ArgEffect(Autorelease));
11916fdd2bd5SGeorge Karpenkov addClassMethSummary("NSAutoreleasePool", "addObject",
11926fdd2bd5SGeorge Karpenkov getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs,
11936fdd2bd5SGeorge Karpenkov ArgEffect(DoNothing),
11946fdd2bd5SGeorge Karpenkov ArgEffect(Autorelease)));
11956fdd2bd5SGeorge Karpenkov }
11966fdd2bd5SGeorge Karpenkov
InitializeMethodSummaries()11976fdd2bd5SGeorge Karpenkov void RetainSummaryManager::InitializeMethodSummaries() {
11986fdd2bd5SGeorge Karpenkov
11996fdd2bd5SGeorge Karpenkov ArgEffects ScratchArgs = AF.getEmptyMap();
12006fdd2bd5SGeorge Karpenkov // Create the "init" selector. It just acts as a pass-through for the
12016fdd2bd5SGeorge Karpenkov // receiver.
12026fdd2bd5SGeorge Karpenkov const RetainSummary *InitSumm = getPersistentSummary(
12036fdd2bd5SGeorge Karpenkov ObjCInitRetE, ScratchArgs, ArgEffect(DecRef, ObjKind::ObjC));
12046fdd2bd5SGeorge Karpenkov addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm);
12056fdd2bd5SGeorge Karpenkov
12066fdd2bd5SGeorge Karpenkov // awakeAfterUsingCoder: behaves basically like an 'init' method. It
12076fdd2bd5SGeorge Karpenkov // claims the receiver and returns a retained object.
12086fdd2bd5SGeorge Karpenkov addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx),
12096fdd2bd5SGeorge Karpenkov InitSumm);
12106fdd2bd5SGeorge Karpenkov
12116fdd2bd5SGeorge Karpenkov // The next methods are allocators.
12126fdd2bd5SGeorge Karpenkov const RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE,
12136fdd2bd5SGeorge Karpenkov ScratchArgs);
12146fdd2bd5SGeorge Karpenkov const RetainSummary *CFAllocSumm =
12156fdd2bd5SGeorge Karpenkov getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs);
12166fdd2bd5SGeorge Karpenkov
12176fdd2bd5SGeorge Karpenkov // Create the "retain" selector.
12186fdd2bd5SGeorge Karpenkov RetEffect NoRet = RetEffect::MakeNoRet();
12196fdd2bd5SGeorge Karpenkov const RetainSummary *Summ = getPersistentSummary(
12206fdd2bd5SGeorge Karpenkov NoRet, ScratchArgs, ArgEffect(IncRef, ObjKind::ObjC));
12216fdd2bd5SGeorge Karpenkov addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ);
12226fdd2bd5SGeorge Karpenkov
12236fdd2bd5SGeorge Karpenkov // Create the "release" selector.
12246fdd2bd5SGeorge Karpenkov Summ = getPersistentSummary(NoRet, ScratchArgs,
12256fdd2bd5SGeorge Karpenkov ArgEffect(DecRef, ObjKind::ObjC));
12266fdd2bd5SGeorge Karpenkov addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ);
12276fdd2bd5SGeorge Karpenkov
12286fdd2bd5SGeorge Karpenkov // Create the -dealloc summary.
12296fdd2bd5SGeorge Karpenkov Summ = getPersistentSummary(NoRet, ScratchArgs, ArgEffect(Dealloc,
12306fdd2bd5SGeorge Karpenkov ObjKind::ObjC));
12316fdd2bd5SGeorge Karpenkov addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ);
12326fdd2bd5SGeorge Karpenkov
12336fdd2bd5SGeorge Karpenkov // Create the "autorelease" selector.
12346fdd2bd5SGeorge Karpenkov Summ = getPersistentSummary(NoRet, ScratchArgs, ArgEffect(Autorelease,
12356fdd2bd5SGeorge Karpenkov ObjKind::ObjC));
12366fdd2bd5SGeorge Karpenkov addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ);
12376fdd2bd5SGeorge Karpenkov
12386fdd2bd5SGeorge Karpenkov // For NSWindow, allocated objects are (initially) self-owned.
12396fdd2bd5SGeorge Karpenkov // FIXME: For now we opt for false negatives with NSWindow, as these objects
12406fdd2bd5SGeorge Karpenkov // self-own themselves. However, they only do this once they are displayed.
12416fdd2bd5SGeorge Karpenkov // Thus, we need to track an NSWindow's display status.
12426fdd2bd5SGeorge Karpenkov const RetainSummary *NoTrackYet =
12436fdd2bd5SGeorge Karpenkov getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs,
12446fdd2bd5SGeorge Karpenkov ArgEffect(StopTracking), ArgEffect(StopTracking));
12456fdd2bd5SGeorge Karpenkov
12466fdd2bd5SGeorge Karpenkov addClassMethSummary("NSWindow", "alloc", NoTrackYet);
12476fdd2bd5SGeorge Karpenkov
12486fdd2bd5SGeorge Karpenkov // For NSPanel (which subclasses NSWindow), allocated objects are not
12496fdd2bd5SGeorge Karpenkov // self-owned.
12506fdd2bd5SGeorge Karpenkov // FIXME: For now we don't track NSPanels. object for the same reason
12516fdd2bd5SGeorge Karpenkov // as for NSWindow objects.
12526fdd2bd5SGeorge Karpenkov addClassMethSummary("NSPanel", "alloc", NoTrackYet);
12536fdd2bd5SGeorge Karpenkov
12546fdd2bd5SGeorge Karpenkov // For NSNull, objects returned by +null are singletons that ignore
12556fdd2bd5SGeorge Karpenkov // retain/release semantics. Just don't track them.
12566fdd2bd5SGeorge Karpenkov addClassMethSummary("NSNull", "null", NoTrackYet);
12576fdd2bd5SGeorge Karpenkov
12586fdd2bd5SGeorge Karpenkov // Don't track allocated autorelease pools, as it is okay to prematurely
12596fdd2bd5SGeorge Karpenkov // exit a method.
12606fdd2bd5SGeorge Karpenkov addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet);
12616fdd2bd5SGeorge Karpenkov addClassMethSummary("NSAutoreleasePool", "allocWithZone", NoTrackYet, false);
12626fdd2bd5SGeorge Karpenkov addClassMethSummary("NSAutoreleasePool", "new", NoTrackYet);
12636fdd2bd5SGeorge Karpenkov
12646fdd2bd5SGeorge Karpenkov // Create summaries QCRenderer/QCView -createSnapShotImageOfType:
12656fdd2bd5SGeorge Karpenkov addInstMethSummary("QCRenderer", AllocSumm, "createSnapshotImageOfType");
12666fdd2bd5SGeorge Karpenkov addInstMethSummary("QCView", AllocSumm, "createSnapshotImageOfType");
12676fdd2bd5SGeorge Karpenkov
12686fdd2bd5SGeorge Karpenkov // Create summaries for CIContext, 'createCGImage' and
12696fdd2bd5SGeorge Karpenkov // 'createCGLayerWithSize'. These objects are CF objects, and are not
12706fdd2bd5SGeorge Karpenkov // automatically garbage collected.
12716fdd2bd5SGeorge Karpenkov addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect");
12726fdd2bd5SGeorge Karpenkov addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect",
12736fdd2bd5SGeorge Karpenkov "format", "colorSpace");
12746fdd2bd5SGeorge Karpenkov addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", "info");
12756fdd2bd5SGeorge Karpenkov }
12766fdd2bd5SGeorge Karpenkov
12776fdd2bd5SGeorge Karpenkov const RetainSummary *
getMethodSummary(const ObjCMethodDecl * MD)12786fdd2bd5SGeorge Karpenkov RetainSummaryManager::getMethodSummary(const ObjCMethodDecl *MD) {
12796fdd2bd5SGeorge Karpenkov const ObjCInterfaceDecl *ID = MD->getClassInterface();
12806fdd2bd5SGeorge Karpenkov Selector S = MD->getSelector();
12816fdd2bd5SGeorge Karpenkov QualType ResultTy = MD->getReturnType();
12826fdd2bd5SGeorge Karpenkov
12836fdd2bd5SGeorge Karpenkov ObjCMethodSummariesTy *CachedSummaries;
12846fdd2bd5SGeorge Karpenkov if (MD->isInstanceMethod())
12856fdd2bd5SGeorge Karpenkov CachedSummaries = &ObjCMethodSummaries;
12866fdd2bd5SGeorge Karpenkov else
12876fdd2bd5SGeorge Karpenkov CachedSummaries = &ObjCClassMethodSummaries;
12886fdd2bd5SGeorge Karpenkov
12896fdd2bd5SGeorge Karpenkov return getMethodSummary(S, ID, MD, ResultTy, *CachedSummaries);
12906fdd2bd5SGeorge Karpenkov }
1291