aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Rose <jordan_rose@apple.com>2012-07-02 19:27:35 +0000
committerJordan Rose <jordan_rose@apple.com>2012-07-02 19:27:35 +0000
commit740d490593e0de8732a697c9f77b90ddd463863b (patch)
tree1c4003bd0e0d2be292345e6ac90af5628e1734c0
parent7518b3784ed2176aad8dcabe0685c6e02c5f1043 (diff)
[analyzer] Add a new abstraction over all types of calls: CallEvent
This is intended to replace CallOrObjCMessage, and is eventually intended to be used for anything that cares more about /what/ is being called than /how/ it's being called. For example, inlining destructors should be the same as inlining blocks, and checking __attribute__((nonnull)) should apply to the allocator calls generated by operator new. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159554 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/StaticAnalyzer/Core/Checker.h2
-rw-r--r--include/clang/StaticAnalyzer/Core/CheckerManager.h4
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h356
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h8
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h6
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/Store.h4
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h2
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp248
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Core/CMakeLists.txt1
-rw-r--r--lib/StaticAnalyzer/Core/Calls.cpp353
-rw-r--r--lib/StaticAnalyzer/Core/CheckerManager.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp6
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp188
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineObjC.cpp15
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp4
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp6
-rw-r--r--test/Analysis/blocks-no-inline.c13
21 files changed, 890 insertions, 340 deletions
diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h
index 76d8c15f75..fb224ef158 100644
--- a/include/clang/StaticAnalyzer/Core/Checker.h
+++ b/include/clang/StaticAnalyzer/Core/Checker.h
@@ -266,7 +266,7 @@ class RegionChanges {
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> Explicits,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) {
+ const CallEvent *Call) {
return ((const CHECKER *)checker)->checkRegionChanges(state, invalidated,
Explicits, Regions, Call);
}
diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h
index b9d6a32918..3202fbe6b8 100644
--- a/include/clang/StaticAnalyzer/Core/CheckerManager.h
+++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h
@@ -294,7 +294,7 @@ public:
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call);
+ const CallEvent *Call);
/// \brief Run checkers for handling assumptions on symbolic values.
ProgramStateRef runCheckersForEvalAssume(ProgramStateRef state,
@@ -373,7 +373,7 @@ public:
const StoreManager::InvalidatedSymbols *symbols,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call)>
+ const CallEvent *Call)>
CheckRegionChangesFunc;
typedef CheckerFn<bool (ProgramStateRef)> WantsRegionChangeUpdateFunc;
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h
new file mode 100644
index 0000000000..45b5fc70a2
--- /dev/null
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h
@@ -0,0 +1,356 @@
+//===- Calls.h - Wrapper for all function and method calls --------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file This file defines CallEvent and its subclasses, which represent path-
+/// sensitive instances of different kinds of function and method calls
+/// (C, C++, and Objective-C).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_STATICANALYZER_PATHSENSITIVE_CALL
+#define LLVM_CLANG_STATICANALYZER_PATHSENSITIVE_CALL
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+
+namespace clang {
+namespace ento {
+
+enum CallEventKind {
+ CE_Function,
+ CE_CXXMember,
+ CE_Block,
+ CE_BEG_SIMPLE_CALLS = CE_Function,
+ CE_END_SIMPLE_CALLS = CE_Block,
+ CE_CXXConstructor,
+ CE_BEG_FUNCTION_CALLS = CE_Function,
+ CE_END_FUNCTION_CALLS = CE_CXXConstructor,
+ CE_ObjCMessage
+};
+
+/// \brief Represents an abstract call to a function or method along a
+/// particular path.
+class CallEvent {
+public:
+ typedef CallEventKind Kind;
+
+protected:
+ ProgramStateRef State;
+ const LocationContext *LCtx;
+ const Kind K;
+
+ CallEvent(ProgramStateRef state, const LocationContext *lctx, Kind k)
+ : State(state), LCtx(lctx), K(k) {}
+ virtual ~CallEvent() {}
+
+ /// \brief Get the value of arbitrary expressions at this point in the path.
+ SVal getSVal(const Stmt *S) const {
+ return State->getSVal(S, LCtx);
+ }
+
+ typedef SmallVectorImpl<const MemRegion *> RegionList;
+
+ /// \brief Used to specify non-argument regions that will be invalidated as a
+ /// result of this call.
+ virtual void addExtraInvalidatedRegions(RegionList &Regions) const {}
+
+ typedef const ParmVarDecl * const *param_iterator;
+ virtual param_iterator param_begin() const = 0;
+ virtual param_iterator param_end() const = 0;
+
+ virtual QualType getDeclaredResultType() const { return QualType(); }
+
+public:
+ /// \brief Returns the declaration of the function or method that will be
+ /// called. May be null.
+ virtual const Decl *getDecl() const = 0;
+
+ /// \brief Returns the expression whose value will be the result of this call.
+ /// May be null.
+ virtual const Expr *getOriginExpr() const = 0;
+
+ /// \brief Returns the number of arguments (explicit and implicit).
+ ///
+ /// Note that this may be greater than the number of parameters in the
+ /// callee's declaration, and that it may include arguments not written in
+ /// the source.
+ virtual unsigned getNumArgs() const = 0;
+
+ /// \brief Returns true if the callee is known to be from a system header.
+ bool isInSystemHeader() const {
+ const Decl *D = getDecl();
+ if (!D)
+ return false;
+
+ SourceLocation Loc = D->getLocation();
+ if (Loc.isValid()) {
+ const SourceManager &SM =
+ State->getStateManager().getContext().getSourceManager();
+ return SM.isInSystemHeader(D->getLocation());
+ }
+
+ // Special case for implicitly-declared global operator new/delete.
+ // These should be considered system functions.
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
+ return FD->isOverloadedOperator() && FD->isImplicit() && FD->isGlobal();
+
+ return false;
+ }
+
+ /// \brief Returns the kind of call this is.
+ Kind getKind() const { return K; }
+
+ /// \brief Returns the value of a given argument at the time of the call.
+ virtual SVal getArgSVal(unsigned Index) const;
+
+ /// \brief Returns the expression associated with a given argument.
+ /// May be null if this expression does not appear in the source.
+ virtual const Expr *getArgExpr(unsigned Index) const {
+ return 0;
+ }
+
+ /// \brief Returns the source range for errors associated with this argument.
+ /// May be invalid if the argument is not written in the source.
+ // FIXME: Is it better to return an invalid range or the range of the origin
+ // expression?
+ virtual SourceRange getArgSourceRange(unsigned Index) const;
+
+ /// \brief Returns the result type, adjusted for references.
+ QualType getResultType() const;
+
+ /// \brief Returns true if any of the arguments appear to represent callbacks.
+ bool hasNonZeroCallbackArg() const;
+
+ /// \brief Returns a new state with all argument regions invalidated.
+ ///
+ /// This accepts an alternate state in case some processing has already
+ /// occurred.
+ ProgramStateRef invalidateRegions(unsigned BlockCount,
+ ProgramStateRef Orig = 0) const;
+
+ // Iterator access to parameter types.
+private:
+ typedef std::const_mem_fun_t<QualType, ParmVarDecl> get_type_fun;
+
+public:
+ typedef llvm::mapped_iterator<param_iterator, get_type_fun>
+ param_type_iterator;
+
+ param_type_iterator param_type_begin() const {
+ return llvm::map_iterator(param_begin(),
+ get_type_fun(&ParmVarDecl::getType));
+ }
+ param_type_iterator param_type_end() const {
+ return llvm::map_iterator(param_end(), get_type_fun(&ParmVarDecl::getType));
+ }
+
+ static bool classof(const CallEvent *) { return true; }
+};
+
+/// \brief Represents a call to any sort of function that might have a
+/// FunctionDecl.
+class AnyFunctionCall : public CallEvent {
+protected:
+ AnyFunctionCall(ProgramStateRef St, const LocationContext *LCtx, Kind K)
+ : CallEvent(St, LCtx, K) {}
+
+ param_iterator param_begin() const;
+ param_iterator param_end() const;
+
+ QualType getDeclaredResultType() const;
+
+public:
+ virtual const FunctionDecl *getDecl() const = 0;
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() >= CE_BEG_FUNCTION_CALLS &&
+ CA->getKind() <= CE_END_FUNCTION_CALLS;
+ }
+};
+
+/// \brief Represents a call to a written as a CallExpr.
+class SimpleCall : public AnyFunctionCall {
+ const CallExpr *CE;
+
+protected:
+ SimpleCall(const CallExpr *ce, ProgramStateRef St,
+ const LocationContext *LCtx, Kind K)
+ : AnyFunctionCall(St, LCtx, K), CE(ce) {
+ }
+
+public:
+ const CallExpr *getOriginExpr() const { return CE; }
+
+ const FunctionDecl *getDecl() const;
+
+ unsigned getNumArgs() const { return CE->getNumArgs(); }
+
+ const Expr *getArgExpr(unsigned Index) const {
+ return CE->getArg(Index);
+ }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() >= CE_BEG_SIMPLE_CALLS &&
+ CA->getKind() <= CE_END_SIMPLE_CALLS;
+ }
+};
+
+/// \brief Represents a C function or static C++ member function call.
+///
+/// Example: \c fun()
+class FunctionCall : public SimpleCall {
+public:
+ FunctionCall(const CallExpr *CE, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : SimpleCall(CE, St, LCtx, CE_Function) {}
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() == CE_Function;
+ }
+};
+
+/// \brief Represents a non-static C++ member function call.
+///
+/// Example: \c obj.fun()
+class CXXMemberCall : public SimpleCall {
+protected:
+ void addExtraInvalidatedRegions(RegionList &Regions) const;
+
+public:
+ CXXMemberCall(const CXXMemberCallExpr *CE, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : SimpleCall(CE, St, LCtx, CE_CXXMember) {}
+
+ const CXXMemberCallExpr *getOriginExpr() const {
+ return cast<CXXMemberCallExpr>(SimpleCall::getOriginExpr());
+ }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() == CE_CXXMember;
+ }
+};
+
+/// \brief Represents a call to a block.
+///
+/// Example: \c ^{ /* ... */ }()
+class BlockCall : public SimpleCall {
+protected:
+ void addExtraInvalidatedRegions(RegionList &Regions) const;
+
+ param_iterator param_begin() const;
+ param_iterator param_end() const;
+
+ QualType getDeclaredResultType() const;
+
+public:
+ BlockCall(const CallExpr *CE, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : SimpleCall(CE, St, LCtx, CE_Block) {
+ assert(isa<BlockDataRegion>(getSVal(CE->getCallee()).getAsRegion()));
+ }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() == CE_Block;
+ }
+
+private:
+ const BlockDataRegion *getBlockRegion() const;
+};
+
+/// \brief Represents a call to a C++ constructor.
+///
+/// Example: \c T(1)
+class CXXConstructorCall : public AnyFunctionCall {
+ const CXXConstructExpr *CE;
+ const MemRegion *Target;
+
+protected:
+ void addExtraInvalidatedRegions(RegionList &Regions) const;
+
+public:
+ CXXConstructorCall(const CXXConstructExpr *ce, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : AnyFunctionCall(St, LCtx, CE_CXXConstructor), CE(ce), Target(0) {}
+ CXXConstructorCall(const CXXConstructExpr *ce, const MemRegion *target,
+ ProgramStateRef St, const LocationContext *LCtx)
+ : AnyFunctionCall(St, LCtx, CE_CXXConstructor), CE(ce), Target(target) {}
+
+ const CXXConstructExpr *getOriginExpr() const { return CE; }
+
+ const CXXConstructorDecl *getDecl() const {
+ return CE->getConstructor();
+ }
+
+ unsigned getNumArgs() const { return CE->getNumArgs(); }
+
+ const Expr *getArgExpr(unsigned Index) const {
+ return CE->getArg(Index);
+ }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() == CE_CXXConstructor;
+ }
+};
+
+/// \brief Represents any expression that causes an Objective-C message send.
+//
+// This class is mostly passthrough to ObjCMessage, because /that/ class is the
+// adapter for the different kinds of Objective-C messages in the system. The
+// difference here is that like other CallActions this refers to a specific
+// (path-sensitive) message send, while ObjCMessage is simply a wrapper for the
+// various (path-insensitive) expressions that are implemented using messages.
+class ObjCMessageInvocation : public CallEvent {
+ ObjCMessage Msg;
+
+protected:
+ void addExtraInvalidatedRegions(RegionList &Regions) const;
+
+ param_iterator param_begin() const;
+ param_iterator param_end() const;
+
+ QualType getDeclaredResultType() const;
+
+public:
+ ObjCMessageInvocation(const ObjCMessage &msg, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : CallEvent(St, LCtx, CE_ObjCMessage), Msg(msg) {}
+
+ Selector getSelector() const { return Msg.getSelector(); }
+ bool isInstanceMessage() const { return Msg.isInstanceMessage(); }
+ const ObjCMethodDecl *getDecl() const { return Msg.getMethodDecl(); }
+ unsigned getNumArgs() const { return Msg.getNumArgs(); }
+ const Expr *getArgExpr(unsigned Index) const { return Msg.getArgExpr(Index); }
+
+ // FIXME: for emitting warnings and such this may not be the best idea.
+ const Expr *getOriginExpr() const { return Msg.getMessageExpr(); }
+
+ SVal getReceiverSVal() const;
+
+ SourceRange getReceiverSourceRange() const {
+ return Msg.getReceiverSourceRange();
+ }
+
+ const ObjCInterfaceDecl *getReceiverInterface() const {
+ return Msg.getReceiverInterface();
+ }
+
+ static bool classof(const CallEvent *CA) {
+ return CA->getKind() == CE_ObjCMessage;
+ }
+};
+
+} // end namespace ento
+} // end namespace clang
+
+#endif
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 0cccd41d3c..46fb70deae 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -41,7 +41,7 @@ class ObjCForCollectionStmt;
namespace ento {
class AnalysisManager;
-class CallOrObjCMessage;
+class CallEvent;
class ObjCMessage;
class ExprEngine : public SubEngine {
@@ -240,7 +240,7 @@ public:
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call);
+ const CallEvent *Call);
/// printState - Called by ProgramStateManager to print checker-specific data.
void printState(raw_ostream &Out, ProgramStateRef State,
@@ -437,10 +437,6 @@ protected:
ExplodedNode *Pred, ProgramStateRef state,
bool GenSink);
- ProgramStateRef invalidateArguments(ProgramStateRef State,
- const CallOrObjCMessage &Call,
- const LocationContext *LC);
-
ProgramStateRef MarkBranch(ProgramStateRef state,
const Stmt *Terminator,
const LocationContext *LCtx,
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
index 44190c6709..3dd2775aa4 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
@@ -35,7 +35,7 @@ class ASTContext;
namespace ento {
-class CallOrObjCMessage;
+class CallEvent;
typedef ConstraintManager* (*ConstraintManagerCreator)(ProgramStateManager&,
SubEngine&);
@@ -219,7 +219,7 @@ public:
const Expr *E, unsigned BlockCount,
const LocationContext *LCtx,
StoreManager::InvalidatedSymbols *IS = 0,
- const CallOrObjCMessage *Call = 0) const;
+ const CallEvent *Call = 0) const;
/// enterStackFrame - Returns the state for entry to the given stack frame,
/// preserving the current state.
@@ -381,7 +381,7 @@ private:
const Expr *E, unsigned BlockCount,
const LocationContext *LCtx,
StoreManager::InvalidatedSymbols &IS,
- const CallOrObjCMessage *Call) const;
+ const CallEvent *Call) const;
};
//===----------------------------------------------------------------------===//
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
index b8a745168e..35097aca6c 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
@@ -29,7 +29,7 @@ class StackFrameContext;
namespace ento {
-class CallOrObjCMessage;
+class CallEvent;
class ProgramState;
class ProgramStateManager;
class SubRegionMap;
@@ -194,7 +194,7 @@ public:
const Expr *E, unsigned Count,
const LocationContext *LCtx,
InvalidatedSymbols &IS,
- const CallOrObjCMessage *Call,
+ const CallEvent *Call,
InvalidatedRegions *Invalidated) = 0;
/// enterStackFrame - Let the StoreManager to do something when execution
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
index baf57d4906..68b81f19a4 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
@@ -105,7 +105,7 @@ public:
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) = 0;
+ const CallEvent *Call) = 0;
inline ProgramStateRef
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 942280d03d..69373749a2 100644
--- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -66,7 +66,7 @@ public:
const StoreManager::InvalidatedSymbols *,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const;
+ const CallEvent *Call) const;
typedef void (CStringChecker::*FnCheck)(CheckerContext &,
const CallExpr *) const;
@@ -1895,7 +1895,7 @@ CStringChecker::checkRegionChanges(ProgramStateRef state,
const StoreManager::InvalidatedSymbols *,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const {
+ const CallEvent *Call) const {
CStringLength::EntryMap Entries = state->get<CStringLength>();
if (Entries.isEmpty())
return state;
diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
index 8d46e5ec30..61582d028a 100644
--- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
@@ -206,7 +206,7 @@ public:
const StoreManager::InvalidatedSymbols *Invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const {
+ const CallEvent *Call) const {
return State;
}
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 1aa9b824e3..0e13ddcd7e 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -18,7 +18,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Calls.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
@@ -140,7 +140,7 @@ public:
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const;
+ const CallEvent *Call) const;
bool wantsRegionChangeUpdate(ProgramStateRef state) const {
return true;
}
@@ -200,7 +200,7 @@ private:
/// Check if the function is not known to us. So, for example, we could
/// conservatively assume it can free/reallocate it's pointer arguments.
- bool doesNotFreeMemory(const CallOrObjCMessage *Call,
+ bool doesNotFreeMemory(const CallEvent *Call,
ProgramStateRef State) const;
static bool SummarizeValue(raw_ostream &os, SVal V);
@@ -481,7 +481,8 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
C.addTransition(State);
}
-static bool isFreeWhenDoneSetToZero(CallOrObjCMessage Call, Selector &S) {
+static bool isFreeWhenDoneSetToZero(const ObjCMessageInvocation &Call,
+ Selector &S) {
for (unsigned i = 1; i < S.getNumArgs(); ++i)
if (S.getNameForSlot(i).equals("freeWhenDone"))
if (Call.getArgSVal(i).isConstant(0))
@@ -491,12 +492,12 @@ static bool isFreeWhenDoneSetToZero(CallOrObjCMessage Call, Selector &S) {
}
void MallocChecker::checkPreObjCMessage(const ObjCMessage &Msg,
- CheckerContext &C) const {
+ CheckerContext &C) const {
const ObjCMethodDecl *MD = Msg.getMethodDecl();
if (!MD)
return;
- CallOrObjCMessage Call(Msg, C.getState(), C.getLocationContext());
+ ObjCMessageInvocation Call(Msg, C.getState(), C.getLocationContext());
Selector S = Msg.getSelector();
// If the first selector is dataWithBytesNoCopy, assume that the memory will
@@ -509,7 +510,7 @@ void MallocChecker::checkPreObjCMessage(const ObjCMessage &Msg,
S.getNameForSlot(0) == "initWithCharactersNoCopy") &&
!isFreeWhenDoneSetToZero(Call, S)){
unsigned int argIdx = 0;
- C.addTransition(FreeMemAux(C, Call.getArg(argIdx),
+ C.addTransition(FreeMemAux(C, Call.getArgExpr(argIdx),
Msg.getMessageExpr(), C.getState(), true));
}
}
@@ -1310,10 +1311,10 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
}
// Check if the function is known to us. So, for example, we could
-// conservatively assume it can free/reallocate it's pointer arguments.
+// conservatively assume it can free/reallocate its pointer arguments.
// (We assume that the pointers cannot escape through calls to system
// functions not handled by this checker.)
-bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
+bool MallocChecker::doesNotFreeMemory(const CallEvent *Call,
ProgramStateRef State) const {
if (!Call)
return false;
@@ -1322,118 +1323,23 @@ bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
// TODO: If we want to be more optimistic here, we'll need to make sure that
// regions escape to C++ containers. They seem to do that even now, but for
// mysterious reasons.
- if (Call->isCXXCall())
+ if (!(isa<FunctionCall>(Call) || isa<ObjCMessageInvocation>(Call)))
return false;
- const Decl *D = Call->getDecl();
- if (!D)
+ // If the call has a callback as an argument, assume the memory
+ // can be freed.
+ if (Call->hasNonZeroCallbackArg())
return false;
- ASTContext &ASTC = State->getStateManager().getContext();
-
- // If it's one of the allocation functions we can reason about, we model
- // its behavior explicitly.
- if (isa<FunctionDecl>(D) && isMemFunction(cast<FunctionDecl>(D), ASTC)) {
- return true;
- }
-
- // If it's not a system call, assume it frees memory.
- if (!Call->isInSystemHeader())
- return false;
-
- // Process C/ObjC functions.
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
- // White list the system functions whose arguments escape.
- const IdentifierInfo *II = FD->getIdentifier();
- if (!II)
- return true;
- StringRef FName = II->getName();
-
- // White list thread local storage.
- if (FName.equals("pthread_setspecific"))
- return false;
-
- // White list xpc connection context.
- // TODO: Ensure that the deallocation actually happens, need to reason
- // about "xpc_connection_set_finalizer_f".
- if (FName.equals("xpc_connection_set_context"))
- return false;
-
- // White list the 'XXXNoCopy' ObjC functions.
- if (FName.endswith("NoCopy")) {
- // Look for the deallocator argument. We know that the memory ownership
- // is not transferred only if the deallocator argument is
- // 'kCFAllocatorNull'.
- for (unsigned i = 1; i < Call->getNumArgs(); ++i) {
- const Expr *ArgE = Call->getArg(i)->IgnoreParenCasts();
- if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) {
- StringRef DeallocatorName = DE->getFoundDecl()->getName();
- if (DeallocatorName == "kCFAllocatorNull")
- return true;
- }
- }
- return false;
- }
-
- // PR12101
- // Many CoreFoundation and CoreGraphics might allow a tracked object
- // to escape.
- if (Call->isCFCGAllowingEscape(FName))
- return false;
-
- // Associating streams with malloced buffers. The pointer can escape if
- // 'closefn' is specified (and if that function does free memory).
- // Currently, we do not inspect the 'closefn' function (PR12101).
- if (FName == "funopen")
- if (Call->getNumArgs() >= 4 && !Call->getArgSVal(4).isConstant(0))
- return false;
-
- // Do not warn on pointers passed to 'setbuf' when used with std streams,
- // these leaks might be intentional when setting the buffer for stdio.
- // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer
- if (FName == "setbuf" || FName =="setbuffer" ||
- FName == "setlinebuf" || FName == "setvbuf") {
- if (Call->getNumArgs() >= 1)
- if (const DeclRefExpr *Arg =
- dyn_cast<DeclRefExpr>(Call->getArg(0)->IgnoreParenCasts()))
- if (const VarDecl *D = dyn_cast<VarDecl>(Arg->getDecl()))
- if (D->getCanonicalDecl()->getName().find("std")
- != StringRef::npos)
- return false;
- }
-
- // A bunch of other functions which either take ownership of a pointer or
- // wrap the result up in a struct or object, meaning it can be freed later.
- // (See RetainCountChecker.) Not all the parameters here are invalidated,
- // but the Malloc checker cannot differentiate between them. The right way
- // of doing this would be to implement a pointer escapes callback.
- if (FName == "CGBitmapContextCreate" ||
- FName == "CGBitmapContextCreateWithData" ||
- FName == "CVPixelBufferCreateWithBytes" ||
- FName == "CVPixelBufferCreateWithPlanarBytes" ||
- FName == "OSAtomicEnqueue") {
- return false;
- }
-
- // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
- // be deallocated by NSMapRemove.
- if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
+ // Check Objective-C messages by selector name.
+ if (const ObjCMessageInvocation *Msg = dyn_cast<ObjCMessageInvocation>(Call)){
+ // If it's not a framework call, assume it frees memory.
+ if (!Call->isInSystemHeader())
return false;
- // If the call has a callback as an argument, assume the memory
- // can be freed.
- if (Call->hasNonZeroCallbackArg())
- return false;
-
- // Otherwise, assume that the function does not free memory.
- // Most system calls, do not free the memory.
- return true;
+ Selector S = Msg->getSelector();
- // Process ObjC functions.
- } else if (const ObjCMethodDecl * ObjCD = dyn_cast<ObjCMethodDecl>(D)) {
- Selector S = ObjCD->getSelector();
-
- // White list the ObjC functions which do free memory.
+ // Whitelist the ObjC methods which do free memory.
// - Anything containing 'freeWhenDone' param set to 1.
// Ex: dataWithBytesNoCopy:length:freeWhenDone.
for (unsigned i = 1; i < S.getNumArgs(); ++i) {
@@ -1448,33 +1354,117 @@ bool MallocChecker::doesNotFreeMemory(const CallOrObjCMessage *Call,
// If the first selector ends with NoCopy, assume that the ownership is
// transferred as well.
// Ex: [NSData dataWithBytesNoCopy:bytes length:10];
- if (S.getNameForSlot(0).endswith("NoCopy")) {