| //=== RetainSummaryManager.h - Summaries for reference counting ---*- C++ -*--// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines summaries implementation for retain counting, which |
| // implements a reference count checker for Core Foundation and Cocoa |
| // on (Mac OS X). |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_ANALYSIS_RETAINSUMMARY_MANAGER_H |
| #define LLVM_CLANG_ANALYSIS_RETAINSUMMARY_MANAGER_H |
| |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/FoldingSet.h" |
| #include "llvm/ADT/ImmutableMap.h" |
| #include "clang/AST/Attr.h" |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/DeclObjC.h" |
| #include "clang/AST/ParentMap.h" |
| #include "clang/Analysis/AnyCall.h" |
| #include "clang/Analysis/SelectorExtras.h" |
| #include "llvm/ADT/STLExtras.h" |
| |
| using namespace clang; |
| |
| namespace clang { |
| namespace ento { |
| |
| /// Determines the object kind of a tracked object. |
| enum class ObjKind { |
| /// Indicates that the tracked object is a CF object. |
| CF, |
| |
| /// Indicates that the tracked object is an Objective-C object. |
| ObjC, |
| |
| /// Indicates that the tracked object could be a CF or Objective-C object. |
| AnyObj, |
| |
| /// Indicates that the tracked object is a generalized object. |
| Generalized, |
| |
| /// Indicates that the tracking object is a descendant of a |
| /// referenced-counted OSObject, used in the Darwin kernel. |
| OS |
| }; |
| |
| enum ArgEffectKind { |
| /// There is no effect. |
| DoNothing, |
| |
| /// The argument is treated as if an -autorelease message had been sent to |
| /// the referenced object. |
| Autorelease, |
| |
| /// The argument is treated as if the referenced object was deallocated. |
| Dealloc, |
| |
| /// The argument has its reference count decreased by 1. |
| DecRef, |
| |
| /// The argument has its reference count decreased by 1 to model |
| /// a transferred bridge cast under ARC. |
| DecRefBridgedTransferred, |
| |
| /// The argument has its reference count increased by 1. |
| IncRef, |
| |
| /// The argument is a pointer to a retain-counted object; on exit, the new |
| /// value of the pointer is a +0 value. |
| UnretainedOutParameter, |
| |
| /// The argument is a pointer to a retain-counted object; on exit, the new |
| /// value of the pointer is a +1 value. |
| RetainedOutParameter, |
| |
| /// The argument is a pointer to a retain-counted object; on exit, the new |
| /// value of the pointer is a +1 value iff the return code is zero. |
| RetainedOutParameterOnZero, |
| |
| /// The argument is a pointer to a retain-counted object; on exit, the new |
| /// value of the pointer is a +1 value iff the return code is non-zero. |
| RetainedOutParameterOnNonZero, |
| |
| /// The argument is treated as potentially escaping, meaning that |
| /// even when its reference count hits 0 it should be treated as still |
| /// possibly being alive as someone else *may* be holding onto the object. |
| MayEscape, |
| |
| /// All typestate tracking of the object ceases. This is usually employed |
| /// when the effect of the call is completely unknown. |
| StopTracking, |
| |
| /// All typestate tracking of the object ceases. Unlike StopTracking, |
| /// this is also enforced when the method body is inlined. |
| /// |
| /// In some cases, we obtain a better summary for this checker |
| /// by looking at the call site than by inlining the function. |
| /// Signifies that we should stop tracking the symbol even if |
| /// the function is inlined. |
| StopTrackingHard, |
| |
| /// Performs the combined functionality of DecRef and StopTrackingHard. |
| /// |
| /// The models the effect that the called function decrements the reference |
| /// count of the argument and all typestate tracking on that argument |
| /// should cease. |
| DecRefAndStopTrackingHard, |
| }; |
| |
| /// An ArgEffect summarizes the retain count behavior on an argument or receiver |
| /// to a function or method. |
| class ArgEffect { |
| ArgEffectKind K; |
| ObjKind O; |
| public: |
| explicit ArgEffect(ArgEffectKind K = DoNothing, ObjKind O = ObjKind::AnyObj) |
| : K(K), O(O) {} |
| |
| ArgEffectKind getKind() const { return K; } |
| ObjKind getObjKind() const { return O; } |
| |
| ArgEffect withKind(ArgEffectKind NewK) { |
| return ArgEffect(NewK, O); |
| } |
| |
| bool operator==(const ArgEffect &Other) const { |
| return K == Other.K && O == Other.O; |
| } |
| }; |
| |
| /// RetEffect summarizes a call's retain/release behavior with respect |
| /// to its return value. |
| class RetEffect { |
| public: |
| enum Kind { |
| /// Indicates that no retain count information is tracked for |
| /// the return value. |
| NoRet, |
| |
| /// Indicates that the returned value is an owned (+1) symbol. |
| OwnedSymbol, |
| |
| /// Indicates that the returned value is an object with retain count |
| /// semantics but that it is not owned (+0). This is the default |
| /// for getters, etc. |
| NotOwnedSymbol, |
| |
| /// Indicates that the return value is an owned object when the |
| /// receiver is also a tracked object. |
| OwnedWhenTrackedReceiver, |
| |
| // Treat this function as returning a non-tracked symbol even if |
| // the function has been inlined. This is used where the call |
| // site summary is more precise than the summary indirectly produced |
| // by inlining the function |
| NoRetHard |
| }; |
| |
| private: |
| Kind K; |
| ObjKind O; |
| |
| RetEffect(Kind k, ObjKind o = ObjKind::AnyObj) : K(k), O(o) {} |
| |
| public: |
| Kind getKind() const { return K; } |
| |
| ObjKind getObjKind() const { return O; } |
| |
| bool isOwned() const { |
| return K == OwnedSymbol || K == OwnedWhenTrackedReceiver; |
| } |
| |
| bool notOwned() const { |
| return K == NotOwnedSymbol; |
| } |
| |
| bool operator==(const RetEffect &Other) const { |
| return K == Other.K && O == Other.O; |
| } |
| |
| static RetEffect MakeOwnedWhenTrackedReceiver() { |
| return RetEffect(OwnedWhenTrackedReceiver, ObjKind::ObjC); |
| } |
| |
| static RetEffect MakeOwned(ObjKind o) { |
| return RetEffect(OwnedSymbol, o); |
| } |
| static RetEffect MakeNotOwned(ObjKind o) { |
| return RetEffect(NotOwnedSymbol, o); |
| } |
| static RetEffect MakeNoRet() { |
| return RetEffect(NoRet); |
| } |
| static RetEffect MakeNoRetHard() { |
| return RetEffect(NoRetHard); |
| } |
| }; |
| |
| /// A key identifying a summary. |
| class ObjCSummaryKey { |
| IdentifierInfo* II; |
| Selector S; |
| public: |
| ObjCSummaryKey(IdentifierInfo* ii, Selector s) |
| : II(ii), S(s) {} |
| |
| ObjCSummaryKey(const ObjCInterfaceDecl *d, Selector s) |
| : II(d ? d->getIdentifier() : nullptr), S(s) {} |
| |
| ObjCSummaryKey(Selector s) |
| : II(nullptr), S(s) {} |
| |
| IdentifierInfo *getIdentifier() const { return II; } |
| Selector getSelector() const { return S; } |
| }; |
| |
| } // end namespace ento |
| } // end namespace clang |
| |
| using namespace ento; |
| |
| namespace llvm { |
| |
| //===----------------------------------------------------------------------===// |
| // Adapters for FoldingSet. |
| //===----------------------------------------------------------------------===// |
| template <> struct FoldingSetTrait<ArgEffect> { |
| static inline void Profile(const ArgEffect X, FoldingSetNodeID &ID) { |
| ID.AddInteger((unsigned) X.getKind()); |
| ID.AddInteger((unsigned) X.getObjKind()); |
| } |
| }; |
| template <> struct FoldingSetTrait<RetEffect> { |
| static inline void Profile(const RetEffect &X, FoldingSetNodeID &ID) { |
| ID.AddInteger((unsigned) X.getKind()); |
| ID.AddInteger((unsigned) X.getObjKind()); |
| } |
| }; |
| |
| template <> struct DenseMapInfo<ObjCSummaryKey> { |
| static inline ObjCSummaryKey getEmptyKey() { |
| return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getEmptyKey(), |
| DenseMapInfo<Selector>::getEmptyKey()); |
| } |
| |
| static inline ObjCSummaryKey getTombstoneKey() { |
| return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getTombstoneKey(), |
| DenseMapInfo<Selector>::getTombstoneKey()); |
| } |
| |
| static unsigned getHashValue(const ObjCSummaryKey &V) { |
| typedef std::pair<IdentifierInfo*, Selector> PairTy; |
| return DenseMapInfo<PairTy>::getHashValue(PairTy(V.getIdentifier(), |
| V.getSelector())); |
| } |
| |
| static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) { |
| return LHS.getIdentifier() == RHS.getIdentifier() && |
| LHS.getSelector() == RHS.getSelector(); |
| } |
| |
| }; |
| |
| } // end llvm namespace |
| |
| |
| namespace clang { |
| namespace ento { |
| |
| /// ArgEffects summarizes the effects of a function/method call on all of |
| /// its arguments. |
| typedef llvm::ImmutableMap<unsigned, ArgEffect> ArgEffects; |
| |
| /// Summary for a function with respect to ownership changes. |
| class RetainSummary { |
| /// Args - a map of (index, ArgEffect) pairs, where index |
| /// specifies the argument (starting from 0). This can be sparsely |
| /// populated; arguments with no entry in Args use 'DefaultArgEffect'. |
| ArgEffects Args; |
| |
| /// DefaultArgEffect - The default ArgEffect to apply to arguments that |
| /// do not have an entry in Args. |
| ArgEffect DefaultArgEffect; |
| |
| /// Receiver - If this summary applies to an Objective-C message expression, |
| /// this is the effect applied to the state of the receiver. |
| ArgEffect Receiver; |
| |
| /// Effect on "this" pointer - applicable only to C++ method calls. |
| ArgEffect This; |
| |
| /// Ret - The effect on the return value. Used to indicate if the |
| /// function/method call returns a new tracked symbol. |
| RetEffect Ret; |
| |
| public: |
| RetainSummary(ArgEffects A, |
| RetEffect R, |
| ArgEffect defaultEff, |
| ArgEffect ReceiverEff, |
| ArgEffect ThisEff) |
| : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), |
| This(ThisEff), Ret(R) {} |
| |
| /// getArg - Return the argument effect on the argument specified by |
| /// idx (starting from 0). |
| ArgEffect getArg(unsigned idx) const { |
| if (const ArgEffect *AE = Args.lookup(idx)) |
| return *AE; |
| |
| return DefaultArgEffect; |
| } |
| |
| void addArg(ArgEffects::Factory &af, unsigned idx, ArgEffect e) { |
| Args = af.add(Args, idx, e); |
| } |
| |
| /// setDefaultArgEffect - Set the default argument effect. |
| void setDefaultArgEffect(ArgEffect E) { |
| DefaultArgEffect = E; |
| } |
| |
| /// getRetEffect - Returns the effect on the return value of the call. |
| RetEffect getRetEffect() const { return Ret; } |
| |
| /// setRetEffect - Set the effect of the return value of the call. |
| void setRetEffect(RetEffect E) { Ret = E; } |
| |
| |
| /// Sets the effect on the receiver of the message. |
| void setReceiverEffect(ArgEffect e) { Receiver = e; } |
| |
| /// getReceiverEffect - Returns the effect on the receiver of the call. |
| /// This is only meaningful if the summary applies to an ObjCMessageExpr*. |
| ArgEffect getReceiverEffect() const { return Receiver; } |
| |
| /// \return the effect on the "this" receiver of the method call. |
| /// This is only meaningful if the summary applies to CXXMethodDecl*. |
| ArgEffect getThisEffect() const { return This; } |
| |
| ArgEffect getDefaultEffect() const { return DefaultArgEffect; } |
| |
| /// Set the effect of the method on "this". |
| void setThisEffect(ArgEffect e) { This = e; } |
| |
| bool isNoop() const { |
| return Ret == RetEffect::MakeNoRet() && Receiver.getKind() == DoNothing |
| && DefaultArgEffect.getKind() == MayEscape && This.getKind() == DoNothing |
| && Args.isEmpty(); |
| } |
| |
| /// Test if two retain summaries are identical. Note that merely equivalent |
| /// summaries are not necessarily identical (for example, if an explicit |
| /// argument effect matches the default effect). |
| bool operator==(const RetainSummary &Other) const { |
| return Args == Other.Args && DefaultArgEffect == Other.DefaultArgEffect && |
| Receiver == Other.Receiver && This == Other.This && Ret == Other.Ret; |
| } |
| |
| /// Profile this summary for inclusion in a FoldingSet. |
| void Profile(llvm::FoldingSetNodeID& ID) const { |
| ID.Add(Args); |
| ID.Add(DefaultArgEffect); |
| ID.Add(Receiver); |
| ID.Add(This); |
| ID.Add(Ret); |
| } |
| |
| /// A retain summary is simple if it has no ArgEffects other than the default. |
| bool isSimple() const { |
| return Args.isEmpty(); |
| } |
| |
| ArgEffects getArgEffects() const { return Args; } |
| |
| private: |
| ArgEffect getDefaultArgEffect() const { return DefaultArgEffect; } |
| |
| friend class RetainSummaryManager; |
| }; |
| |
| class ObjCSummaryCache { |
| typedef llvm::DenseMap<ObjCSummaryKey, const RetainSummary *> MapTy; |
| MapTy M; |
| public: |
| ObjCSummaryCache() {} |
| |
| const RetainSummary * find(const ObjCInterfaceDecl *D, Selector S) { |
| // Do a lookup with the (D,S) pair. If we find a match return |
| // the iterator. |
| ObjCSummaryKey K(D, S); |
| MapTy::iterator I = M.find(K); |
| |
| if (I != M.end()) |
| return I->second; |
| if (!D) |
| return nullptr; |
| |
| // Walk the super chain. If we find a hit with a parent, we'll end |
| // up returning that summary. We actually allow that key (null,S), as |
| // we cache summaries for the null ObjCInterfaceDecl* to allow us to |
| // generate initial summaries without having to worry about NSObject |
| // being declared. |
| // FIXME: We may change this at some point. |
| for (ObjCInterfaceDecl *C=D->getSuperClass() ;; C=C->getSuperClass()) { |
| if ((I = M.find(ObjCSummaryKey(C, S))) != M.end()) |
| break; |
| |
| if (!C) |
| return nullptr; |
| } |
| |
| // Cache the summary with original key to make the next lookup faster |
| // and return the iterator. |
| const RetainSummary *Summ = I->second; |
| M[K] = Summ; |
| return Summ; |
| } |
| |
| const RetainSummary *find(IdentifierInfo* II, Selector S) { |
| // FIXME: Class method lookup. Right now we don't have a good way |
| // of going between IdentifierInfo* and the class hierarchy. |
| MapTy::iterator I = M.find(ObjCSummaryKey(II, S)); |
| |
| if (I == M.end()) |
| I = M.find(ObjCSummaryKey(S)); |
| |
| return I == M.end() ? nullptr : I->second; |
| } |
| |
| const RetainSummary *& operator[](ObjCSummaryKey K) { |
| return M[K]; |
| } |
| |
| const RetainSummary *& operator[](Selector S) { |
| return M[ ObjCSummaryKey(S) ]; |
| } |
| }; |
| |
| class RetainSummaryTemplate; |
| |
| class RetainSummaryManager { |
| typedef llvm::DenseMap<const FunctionDecl*, const RetainSummary *> |
| FuncSummariesTy; |
| |
| typedef ObjCSummaryCache ObjCMethodSummariesTy; |
| |
| typedef llvm::FoldingSetNodeWrapper<RetainSummary> CachedSummaryNode; |
| |
| /// Ctx - The ASTContext object for the analyzed ASTs. |
| ASTContext &Ctx; |
| |
| /// Records whether or not the analyzed code runs in ARC mode. |
| const bool ARCEnabled; |
| |
| /// Track Objective-C and CoreFoundation objects. |
| const bool TrackObjCAndCFObjects; |
| |
| /// Track sublcasses of OSObject. |
| const bool TrackOSObjects; |
| |
| /// FuncSummaries - A map from FunctionDecls to summaries. |
| FuncSummariesTy FuncSummaries; |
| |
| /// ObjCClassMethodSummaries - A map from selectors (for instance methods) |
| /// to summaries. |
| ObjCMethodSummariesTy ObjCClassMethodSummaries; |
| |
| /// ObjCMethodSummaries - A map from selectors to summaries. |
| ObjCMethodSummariesTy ObjCMethodSummaries; |
| |
| /// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects, |
| /// and all other data used by the checker. |
| llvm::BumpPtrAllocator BPAlloc; |
| |
| /// AF - A factory for ArgEffects objects. |
| ArgEffects::Factory AF; |
| |
| /// ObjCAllocRetE - Default return effect for methods returning Objective-C |
| /// objects. |
| RetEffect ObjCAllocRetE; |
| |
| /// ObjCInitRetE - Default return effect for init methods returning |
| /// Objective-C objects. |
| RetEffect ObjCInitRetE; |
| |
| /// SimpleSummaries - Used for uniquing summaries that don't have special |
| /// effects. |
| llvm::FoldingSet<CachedSummaryNode> SimpleSummaries; |
| |
| /// Create an OS object at +1. |
| const RetainSummary *getOSSummaryCreateRule(const FunctionDecl *FD); |
| |
| /// Get an OS object at +0. |
| const RetainSummary *getOSSummaryGetRule(const FunctionDecl *FD); |
| |
| /// Increment the reference count on OS object. |
| const RetainSummary *getOSSummaryRetainRule(const FunctionDecl *FD); |
| |
| /// Decrement the reference count on OS object. |
| const RetainSummary *getOSSummaryReleaseRule(const FunctionDecl *FD); |
| |
| /// Free the OS object. |
| const RetainSummary *getOSSummaryFreeRule(const FunctionDecl *FD); |
| |
| const RetainSummary *getUnarySummary(const FunctionType* FT, |
| ArgEffectKind AE); |
| |
| const RetainSummary *getCFSummaryCreateRule(const FunctionDecl *FD); |
| const RetainSummary *getCFSummaryGetRule(const FunctionDecl *FD); |
| const RetainSummary *getCFCreateGetRuleSummary(const FunctionDecl *FD); |
| |
| const RetainSummary *getPersistentSummary(const RetainSummary &OldSumm); |
| |
| const RetainSummary * |
| getPersistentSummary(RetEffect RetEff, ArgEffects ScratchArgs, |
| ArgEffect ReceiverEff = ArgEffect(DoNothing), |
| ArgEffect DefaultEff = ArgEffect(MayEscape), |
| ArgEffect ThisEff = ArgEffect(DoNothing)) { |
| RetainSummary Summ(ScratchArgs, RetEff, DefaultEff, ReceiverEff, ThisEff); |
| return getPersistentSummary(Summ); |
| } |
| |
| const RetainSummary *getDoNothingSummary() { |
| return getPersistentSummary(RetEffect::MakeNoRet(), |
| ArgEffects(AF.getEmptyMap()), |
| ArgEffect(DoNothing), ArgEffect(DoNothing)); |
| } |
| |
| const RetainSummary *getDefaultSummary() { |
| return getPersistentSummary(RetEffect::MakeNoRet(), |
| ArgEffects(AF.getEmptyMap()), |
| ArgEffect(DoNothing), ArgEffect(MayEscape)); |
| } |
| |
| const RetainSummary *getPersistentStopSummary() { |
| return getPersistentSummary( |
| RetEffect::MakeNoRet(), ArgEffects(AF.getEmptyMap()), |
| ArgEffect(StopTracking), ArgEffect(StopTracking)); |
| } |
| |
| void InitializeClassMethodSummaries(); |
| void InitializeMethodSummaries(); |
| |
| void addNSObjectClsMethSummary(Selector S, const RetainSummary *Summ) { |
| ObjCClassMethodSummaries[S] = Summ; |
| } |
| |
| void addNSObjectMethSummary(Selector S, const RetainSummary *Summ) { |
| ObjCMethodSummaries[S] = Summ; |
| } |
| |
| void addClassMethSummary(const char* Cls, const char* name, |
| const RetainSummary *Summ, bool isNullary = true) { |
| IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); |
| Selector S = isNullary ? GetNullarySelector(name, Ctx) |
| : GetUnarySelector(name, Ctx); |
| ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; |
| } |
| |
| void addInstMethSummary(const char* Cls, const char* nullaryName, |
| const RetainSummary *Summ) { |
| IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); |
| Selector S = GetNullarySelector(nullaryName, Ctx); |
| ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; |
| } |
| |
| template <typename... Keywords> |
| void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy &Summaries, |
| const RetainSummary *Summ, Keywords *... Kws) { |
| Selector S = getKeywordSelector(Ctx, Kws...); |
| Summaries[ObjCSummaryKey(ClsII, S)] = Summ; |
| } |
| |
| template <typename... Keywords> |
| void addInstMethSummary(const char *Cls, const RetainSummary *Summ, |
| Keywords *... Kws) { |
| addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, Kws...); |
| } |
| |
| template <typename... Keywords> |
| void addClsMethSummary(const char *Cls, const RetainSummary *Summ, |
| Keywords *... Kws) { |
| addMethodSummary(&Ctx.Idents.get(Cls), ObjCClassMethodSummaries, Summ, |
| Kws...); |
| } |
| |
| template <typename... Keywords> |
| void addClsMethSummary(IdentifierInfo *II, const RetainSummary *Summ, |
| Keywords *... Kws) { |
| addMethodSummary(II, ObjCClassMethodSummaries, Summ, Kws...); |
| } |
| |
| const RetainSummary * generateSummary(const FunctionDecl *FD, |
| bool &AllowAnnotations); |
| |
| /// Return a summary for OSObject, or nullptr if not found. |
| const RetainSummary *getSummaryForOSObject(const FunctionDecl *FD, |
| StringRef FName, QualType RetTy); |
| |
| /// Return a summary for Objective-C or CF object, or nullptr if not found. |
| const RetainSummary *getSummaryForObjCOrCFObject( |
| const FunctionDecl *FD, |
| StringRef FName, |
| QualType RetTy, |
| const FunctionType *FT, |
| bool &AllowAnnotations); |
| |
| /// Apply the annotation of @c pd in function @c FD |
| /// to the resulting summary stored in out-parameter @c Template. |
| /// \return whether an annotation was applied. |
| bool applyParamAnnotationEffect(const ParmVarDecl *pd, unsigned parm_idx, |
| const NamedDecl *FD, |
| RetainSummaryTemplate &Template); |
| |
| public: |
| RetainSummaryManager(ASTContext &ctx, bool trackObjCAndCFObjects, |
| bool trackOSObjects) |
| : Ctx(ctx), ARCEnabled((bool)Ctx.getLangOpts().ObjCAutoRefCount), |
| TrackObjCAndCFObjects(trackObjCAndCFObjects), |
| TrackOSObjects(trackOSObjects), AF(BPAlloc), |
| ObjCAllocRetE(ARCEnabled ? RetEffect::MakeNotOwned(ObjKind::ObjC) |
| : RetEffect::MakeOwned(ObjKind::ObjC)), |
| ObjCInitRetE(ARCEnabled ? RetEffect::MakeNotOwned(ObjKind::ObjC) |
| : RetEffect::MakeOwnedWhenTrackedReceiver()) { |
| InitializeClassMethodSummaries(); |
| InitializeMethodSummaries(); |
| } |
| |
| enum class BehaviorSummary { |
| // Function does not return. |
| NoOp, |
| |
| // Function returns the first argument. |
| Identity, |
| |
| // Function returns "this" argument. |
| IdentityThis, |
| |
| // Function either returns zero, or the input parameter. |
| IdentityOrZero |
| }; |
| |
| Optional<BehaviorSummary> canEval(const CallExpr *CE, const FunctionDecl *FD, |
| bool &hasTrustedImplementationAnnotation); |
| |
| /// \return Whether the type corresponds to a known smart pointer |
| /// implementation (that is, everything about it is inlineable). |
| static bool isKnownSmartPointer(QualType QT); |
| |
| bool isTrustedReferenceCountImplementation(const Decl *FD); |
| |
| const RetainSummary *getSummary(AnyCall C, |
| bool HasNonZeroCallbackArg=false, |
| bool IsReceiverUnconsumedSelf=false, |
| QualType ReceiverType={}); |
| |
| RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } |
| |
| private: |
| |
| /// getMethodSummary - This version of getMethodSummary is used to query |
| /// the summary for the current method being analyzed. |
| const RetainSummary *getMethodSummary(const ObjCMethodDecl *MD); |
| |
| const RetainSummary *getFunctionSummary(const FunctionDecl *FD); |
| |
| const RetainSummary *getMethodSummary(Selector S, const ObjCInterfaceDecl *ID, |
| const ObjCMethodDecl *MD, |
| QualType RetTy, |
| ObjCMethodSummariesTy &CachedSummaries); |
| |
| const RetainSummary * |
| getInstanceMethodSummary(const ObjCMessageExpr *ME, QualType ReceiverType); |
| |
| const RetainSummary *getClassMethodSummary(const ObjCMessageExpr *ME); |
| |
| const RetainSummary *getStandardMethodSummary(const ObjCMethodDecl *MD, |
| Selector S, QualType RetTy); |
| |
| /// Determine if there is a special return effect for this function or method. |
| Optional<RetEffect> getRetEffectFromAnnotations(QualType RetTy, |
| const Decl *D); |
| |
| void updateSummaryFromAnnotations(const RetainSummary *&Summ, |
| const ObjCMethodDecl *MD); |
| |
| void updateSummaryFromAnnotations(const RetainSummary *&Summ, |
| const FunctionDecl *FD); |
| |
| const RetainSummary *updateSummaryForNonZeroCallbackArg(const RetainSummary *S, |
| AnyCall &C); |
| |
| /// Special case '[super init];' and '[self init];' |
| /// |
| /// Even though calling '[super init]' without assigning the result to self |
| /// and checking if the parent returns 'nil' is a bad pattern, it is common. |
| /// Additionally, our Self Init checker already warns about it. To avoid |
| /// overwhelming the user with messages from both checkers, we model the case |
| /// of '[super init]' in cases when it is not consumed by another expression |
| /// as if the call preserves the value of 'self'; essentially, assuming it can |
| /// never fail and return 'nil'. |
| /// Note, we don't want to just stop tracking the value since we want the |
| /// RetainCount checker to report leaks and use-after-free if SelfInit checker |
| /// is turned off. |
| void updateSummaryForReceiverUnconsumedSelf(const RetainSummary *&S); |
| |
| /// Set argument types for arguments which are not doing anything. |
| void updateSummaryForArgumentTypes(const AnyCall &C, const RetainSummary *&RS); |
| |
| /// Determine whether a declaration @c D of correspondent type (return |
| /// type for functions/methods) @c QT has any of the given attributes, |
| /// provided they pass necessary validation checks AND tracking the given |
| /// attribute is enabled. |
| /// Returns the object kind corresponding to the present attribute, or None, |
| /// if none of the specified attributes are present. |
| /// Crashes if passed an attribute which is not explicitly handled. |
| template <class T> |
| Optional<ObjKind> hasAnyEnabledAttrOf(const Decl *D, QualType QT); |
| |
| template <class T1, class T2, class... Others> |
| Optional<ObjKind> hasAnyEnabledAttrOf(const Decl *D, QualType QT); |
| |
| friend class RetainSummaryTemplate; |
| }; |
| |
| |
| // Used to avoid allocating long-term (BPAlloc'd) memory for default retain |
| // summaries. If a function or method looks like it has a default summary, but |
| // it has annotations, the annotations are added to the stack-based template |
| // and then copied into managed memory. |
| class RetainSummaryTemplate { |
| RetainSummaryManager &Manager; |
| const RetainSummary *&RealSummary; |
| RetainSummary ScratchSummary; |
| bool Accessed; |
| public: |
| RetainSummaryTemplate(const RetainSummary *&real, RetainSummaryManager &mgr) |
| : Manager(mgr), RealSummary(real), ScratchSummary(*real), Accessed(false) {} |
| |
| ~RetainSummaryTemplate() { |
| if (Accessed) |
| RealSummary = Manager.getPersistentSummary(ScratchSummary); |
| } |
| |
| RetainSummary &operator*() { |
| Accessed = true; |
| return ScratchSummary; |
| } |
| |
| RetainSummary *operator->() { |
| Accessed = true; |
| return &ScratchSummary; |
| } |
| }; |
| |
| } // end namespace ento |
| } // end namespace clang |
| |
| #endif |