clang 20.0.0 (based on r547379) from build 12806354. Bug: http://b/379133546 Test: N/A Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
770 lines
26 KiB
C++
770 lines
26 KiB
C++
//=== 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_RETAINSUMMARYMANAGER_H
|
|
#define LLVM_CLANG_ANALYSIS_RETAINSUMMARYMANAGER_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"
|
|
#include <optional>
|
|
|
|
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
|
|
};
|
|
|
|
std::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.
|
|
std::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
|
|
/// std::nullopt, if none of the specified attributes are present.
|
|
/// Crashes if passed an attribute which is not explicitly handled.
|
|
template <class T>
|
|
std::optional<ObjKind> hasAnyEnabledAttrOf(const Decl *D, QualType QT);
|
|
|
|
template <class T1, class T2, class... Others>
|
|
std::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
|