]> granicus.if.org Git - clang/commitdiff
[analyzer] Move the last bits of CallOrObjCMessage over to CallEvent.
authorJordan Rose <jordan_rose@apple.com>
Mon, 2 Jul 2012 19:27:51 +0000 (19:27 +0000)
committerJordan Rose <jordan_rose@apple.com>
Mon, 2 Jul 2012 19:27:51 +0000 (19:27 +0000)
This involved refactoring some common pointer-escapes code onto CallEvent,
then having MallocChecker use those callbacks for whether or not to consider
a pointer's /ownership/ as escaping. This still needs to be pinned down, and
probably we want to make the new argumentsMayEscape() function a little more
discerning (content invalidation vs. ownership/metadata invalidation), but
this is a good improvement.

As a bonus, also remove CallOrObjCMessage from the source completely.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159557 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h
include/clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h
lib/StaticAnalyzer/Checkers/MallocChecker.cpp
lib/StaticAnalyzer/Core/CMakeLists.txt
lib/StaticAnalyzer/Core/Calls.cpp
lib/StaticAnalyzer/Core/ExplodedGraph.cpp
lib/StaticAnalyzer/Core/ExprEngine.cpp
lib/StaticAnalyzer/Core/ObjCMessage.cpp [deleted file]
test/Analysis/malloc.c

index 45b5fc70a2abee97eb81ebb8721bfa26c0d3861a..1327c8971c96d6ef396f4367ae2d3d356f2a85d2 100644 (file)
@@ -132,6 +132,15 @@ public:
   /// \brief Returns true if any of the arguments appear to represent callbacks.
   bool hasNonZeroCallbackArg() const;
 
+  /// \brief Returns true if any of the arguments are known to escape to long-
+  /// term storage, even if this method will not modify them.
+  // NOTE: The exact semantics of this are still being defined!
+  // We don't really want a list of hardcoded exceptions in the long run,
+  // but we don't want duplicated lists of known APIs in the short term either.
+  virtual bool argumentsMayEscape() const {
+    return hasNonZeroCallbackArg();
+  }
+
   /// \brief Returns a new state with all argument regions invalidated.
   ///
   /// This accepts an alternate state in case some processing has already
@@ -139,6 +148,10 @@ public:
   ProgramStateRef invalidateRegions(unsigned BlockCount,
                                     ProgramStateRef Orig = 0) const;
 
+  /// \brief Returns true if this is a statement that can be considered for
+  /// inlining.
+  static bool mayBeInlined(const Stmt *S);
+
   // Iterator access to parameter types.
 private:
   typedef std::const_mem_fun_t<QualType, ParmVarDecl> get_type_fun;
@@ -173,6 +186,8 @@ protected:
 public:
   virtual const FunctionDecl *getDecl() const = 0;
 
+  bool argumentsMayEscape() const;
+
   static bool classof(const CallEvent *CA) {
     return CA->getKind() >= CE_BEG_FUNCTION_CALLS &&
            CA->getKind() <= CE_END_FUNCTION_CALLS;
index 8fc4050c7e0830e5ae05411afee79d7f9d526f90..e567d2d498a091fbd486ea0c5f6939435b26e4f4 100644 (file)
@@ -157,143 +157,6 @@ public:
   }
 };
 
-/// \brief Common wrapper for a call expression, ObjC message, or C++ 
-/// constructor, mainly to provide a common interface for their arguments.
-class CallOrObjCMessage {
-  llvm::PointerUnion<const CallExpr *, const CXXConstructExpr *> CallE;
-  ObjCMessage Msg;
-  ProgramStateRef State;
-  const LocationContext *LCtx;
-
-  bool isCallbackArg(unsigned Idx, const Type *T) const;
-
-public:
-  CallOrObjCMessage(const CallExpr *callE, ProgramStateRef state,
-                    const LocationContext *lctx)
-    : CallE(callE), State(state), LCtx(lctx) {}
-  CallOrObjCMessage(const CXXConstructExpr *consE, ProgramStateRef state,
-                    const LocationContext *lctx)
-    : CallE(consE), State(state), LCtx(lctx) {}
-  CallOrObjCMessage(const ObjCMessage &msg, ProgramStateRef state,
-                    const LocationContext *lctx)
-    : CallE((CallExpr *)0), Msg(msg), State(state), LCtx(lctx) {}
-
-  QualType getResultType(ASTContext &ctx) const;
-  
-  bool isFunctionCall() const {
-    return CallE && CallE.is<const CallExpr *>();
-  }
-
-  bool isCXXConstructExpr() const {
-    return CallE && CallE.is<const CXXConstructExpr *>();
-  }
-
-  bool isObjCMessage() const {
-    return !CallE;
-  }
-
-  bool isCXXCall() const {
-    const CallExpr *ActualCallE = CallE.dyn_cast<const CallExpr *>();
-    return ActualCallE && isa<CXXMemberCallExpr>(ActualCallE);
-  }
-
-  /// Check if the callee is declared in the system header.
-  bool isInSystemHeader() const {
-    if (const Decl *D = getDecl()) {
-      const SourceManager &SM =
-        State->getStateManager().getContext().getSourceManager();
-      SourceLocation Loc = D->getLocation();
-      // Be careful: the implicit declarations of operator new/delete have
-      // invalid source locations but should still count as system files.
-      if (Loc.isValid())
-        return SM.isInSystemHeader(D->getLocation());
-      else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
-        return FD->isOverloadedOperator() && FD->isImplicit() && FD->isGlobal();
-    }
-    return false;
-  }
-
-  const Expr *getOriginExpr() const {
-    if (!CallE)
-      return Msg.getMessageExpr();
-    if (const CXXConstructExpr *Ctor =
-          CallE.dyn_cast<const CXXConstructExpr *>())
-      return Ctor;
-    return CallE.get<const CallExpr *>();
-  }
-  
-  SVal getFunctionCallee() const;
-  SVal getCXXCallee() const;
-  SVal getInstanceMessageReceiver(const LocationContext *LC) const;
-
-  /// Get the declaration of the function or method.
-  const Decl *getDecl() const;
-
-  unsigned getNumArgs() const {
-    if (!CallE)
-      return Msg.getNumArgs();
-    if (const CXXConstructExpr *Ctor =
-          CallE.dyn_cast<const CXXConstructExpr *>())
-      return Ctor->getNumArgs();
-    return CallE.get<const CallExpr *>()->getNumArgs();
-  }
-
-  SVal getArgSVal(unsigned i) const {
-    assert(i < getNumArgs());
-    if (!CallE)
-      return Msg.getArgSVal(i, LCtx, State);
-    return State->getSVal(getArg(i), LCtx);
-  }
-
-  const Expr *getArg(unsigned i) const {
-    assert(i < getNumArgs());
-    if (!CallE)
-      return Msg.getArgExpr(i);
-    if (const CXXConstructExpr *Ctor =
-          CallE.dyn_cast<const CXXConstructExpr *>())
-      return Ctor->getArg(i);
-    return CallE.get<const CallExpr *>()->getArg(i);
-  }
-
-  SourceRange getArgSourceRange(unsigned i) const {
-    assert(i < getNumArgs());
-    if (CallE)
-      return getArg(i)->getSourceRange();
-    return Msg.getArgSourceRange(i);
-  }
-
-  SourceRange getReceiverSourceRange() const {
-    assert(isObjCMessage());
-    return Msg.getReceiverSourceRange();
-  }
-
-  /// \brief Check if one of the arguments might be a callback.
-  bool hasNonZeroCallbackArg() const;
-
-
-  /// \brief Check if the name corresponds to a CoreFoundation or CoreGraphics 
-  /// function that allows objects to escape.
-  ///
-  /// Many methods allow a tracked object to escape.  For example:
-  ///
-  ///   CFMutableDictionaryRef x = CFDictionaryCreateMutable(..., customDeallocator);
-  ///   CFDictionaryAddValue(y, key, x);
-  ///
-  /// We handle this and similar cases with the following heuristic.  If the
-  /// function name contains "InsertValue", "SetValue", "AddValue",
-  /// "AppendValue", or "SetAttribute", then we assume that arguments may
-  /// escape.
-  //
-  // TODO: To reduce false negatives here, we should track the container
-  // allocation site and check if a proper deallocator was set there.
-  static bool isCFCGAllowingEscape(StringRef FName);
-
-  // Check if this kind of expression can be inlined by the analyzer.
-  static bool canBeInlined(const Stmt *S) {
-    return isa<CallExpr>(S);
-  }
-};
-
 }
 }
 
index 0e13ddcd7e6f195902e3081e7a7433c02558f5f9..29bb9c8f25adc9a627b5541b97f693414da16e1a 100644 (file)
@@ -1316,8 +1316,7 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
 // functions not handled by this checker.)
 bool MallocChecker::doesNotFreeMemory(const CallEvent *Call,
                                       ProgramStateRef State) const {
-  if (!Call)
-    return false;
+  assert(Call);
 
   // For now, assume that any C++ call can free memory.
   // TODO: If we want to be more optimistic here, we'll need to make sure that
@@ -1326,15 +1325,11 @@ bool MallocChecker::doesNotFreeMemory(const CallEvent *Call,
   if (!(isa<FunctionCall>(Call) || isa<ObjCMessageInvocation>(Call)))
     return false;
 
-  // If the call has a callback as an argument, assume the memory
-  // can be freed.
-  if (Call->hasNonZeroCallbackArg())
-    return false;
-
   // 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())
+    // If it's not a framework call, or if it takes a callback, assume it
+    // can free memory.
+    if (!Call->isInSystemHeader() || Call->hasNonZeroCallbackArg())
       return false;
 
     Selector S = Msg->getSelector();
@@ -1395,13 +1390,8 @@ bool MallocChecker::doesNotFreeMemory(const CallEvent *Call,
     return false;
   StringRef FName = II->getName();
 
-  // White list thread local storage.
-  if (FName.equals("pthread_setspecific"))
-    return false;
-  if (FName.equals("xpc_connection_set_context"))
-    return false;
-
   // White list the 'XXXNoCopy' CoreFoundation functions.
+  // We specifically check these before 
   if (FName.endswith("NoCopy")) {
     // Look for the deallocator argument. We know that the memory ownership
     // is not transferred only if the deallocator argument is
@@ -1417,18 +1407,13 @@ bool MallocChecker::doesNotFreeMemory(const CallEvent *Call,
     return false;
   }
 
-  // PR12101
-  // Many CoreFoundation and CoreGraphics might allow a tracked object
-  // to escape.
-  if (CallOrObjCMessage::isCFCGAllowingEscape(FName))
-    return false;
-
   // Associating streams with malloced buffers. The pointer can escape if
-  // 'closefn' is specified (and if that function does free memory).
+  // 'closefn' is specified (and if that function does free memory),
+  // but it will not if closefn is not specified.
   // Currently, we do not inspect the 'closefn' function (PR12101).
   if (FName == "funopen")
-    if (Call->getNumArgs() >= 4 && !Call->getArgSVal(4).isConstant(0))
-      return false;
+    if (Call->getNumArgs() >= 4 && Call->getArgSVal(4).isConstant(0))
+      return true;
 
   // Do not warn on pointers passed to 'setbuf' when used with std streams,
   // these leaks might be intentional when setting the buffer for stdio.
@@ -1457,9 +1442,11 @@ bool MallocChecker::doesNotFreeMemory(const CallEvent *Call,
     return false;
   }
 
-  // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
-  // be deallocated by NSMapRemove.
-  if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
+  // Handle cases where we know a buffer's /address/ can escape.
+  // Note that the above checks handle some special cases where we know that
+  // even though the address escapes, it's still our responsibility to free the
+  // buffer.
+  if (Call->argumentsMayEscape())
     return false;
 
   // Otherwise, assume that the function does not free memory.
index 0061ce10454bc7a8824fab734f54acf6468f0b44..4506d2791acaf7f9fe8e69b881899d77cd127d79 100644 (file)
@@ -25,7 +25,6 @@ add_clang_library(clangStaticAnalyzerCore
   FunctionSummary.cpp
   HTMLDiagnostics.cpp
   MemRegion.cpp
-  ObjCMessage.cpp
   PathDiagnostic.cpp
   PlistDiagnostics.cpp
   ProgramState.cpp
index ea28cfdf367403e61dbaed46bbd5a709c4033706..510f8340a2c2559a22faf1cd1b02ef781eefc254 100644 (file)
@@ -118,51 +118,12 @@ static bool isPointerToConst(QualType Ty) {
 
 // Try to retrieve the function declaration and find the function parameter
 // types which are pointers/references to a non-pointer const.
-// We do not invalidate the corresponding argument regions.
+// We will not invalidate the corresponding argument regions.
 static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs,
                                  const CallEvent &Call) {
-  const Decl *CallDecl = Call.getDecl();
-  if (!CallDecl)
-    return;
-
-  if (Call.hasNonZeroCallbackArg())
-    return;
-
-  if (const FunctionDecl *FDecl = dyn_cast<FunctionDecl>(CallDecl)) {
-    const IdentifierInfo *II = FDecl->getIdentifier();
-
-    // List the cases, where the region should be invalidated even if the
-    // argument is const.
-    // FIXME: This is conflating invalidating /contents/ and invalidating
-    // /metadata/. Now that we pass the CallEvent to the checkers, they
-    // should probably be doing this work themselves.
-    if (II) {
-      StringRef FName = II->getName();
-      //  - 'int pthread_setspecific(ptheread_key k, const void *)' stores a
-      // value into thread local storage. The value can later be retrieved with
-      // 'void *ptheread_getspecific(pthread_key)'. So even thought the
-      // parameter is 'const void *', the region escapes through the call.
-      //  - funopen - sets a buffer for future IO calls.
-      //  - ObjC functions that end with "NoCopy" can free memory, of the passed
-      // in buffer.
-      // - Many CF containers allow objects to escape through custom
-      // allocators/deallocators upon container construction.
-      // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
-      // be deallocated by NSMapRemove.
-      // - Any call that has a callback as one of the arguments.
-      if (FName == "pthread_setspecific" ||
-          FName == "funopen" ||
-          FName.endswith("NoCopy") ||
-          (FName.startswith("NS") &&
-           (FName.find("Insert") != StringRef::npos)) ||
-          CallOrObjCMessage::isCFCGAllowingEscape(FName))
-        return;
-    }
-  }
-
   unsigned Idx = 0;
   for (CallEvent::param_type_iterator I = Call.param_type_begin(),
-                                       E = Call.param_type_end();
+                                      E = Call.param_type_end();
        I != E; ++I, ++Idx) {
     if (isPointerToConst(*I))
       PreserveArgs.insert(Idx);
@@ -178,7 +139,8 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
 
   // Indexes of arguments whose values will be preserved by the call.
   llvm::SmallSet<unsigned, 1> PreserveArgs;
-  findPtrToConstParams(PreserveArgs, *this);
+  if (!argumentsMayEscape())
+    findPtrToConstParams(PreserveArgs, *this);
 
   for (unsigned Idx = 0, Count = getNumArgs(); Idx != Count; ++Idx) {
     if (PreserveArgs.count(Idx))
@@ -233,6 +195,11 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
                                    BlockCount, LCtx, /*Symbols=*/0, this);
 }
 
+bool CallEvent::mayBeInlined(const Stmt *S) {
+  return isa<CallExpr>(S);
+}
+
+
 CallEvent::param_iterator AnyFunctionCall::param_begin() const {
   const FunctionDecl *D = getDecl();
   if (!D)
@@ -257,6 +224,63 @@ QualType AnyFunctionCall::getDeclaredResultType() const {
   return D->getResultType();
 }
 
+bool AnyFunctionCall::argumentsMayEscape() const {
+  if (CallEvent::argumentsMayEscape())
+    return true;
+
+  const FunctionDecl *D = getDecl();
+  if (!D)
+    return true;
+
+  const IdentifierInfo *II = D->getIdentifier();
+  if (!II)
+    return true;
+
+  // This set of "escaping" APIs is 
+
+  // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a
+  //   value into thread local storage. The value can later be retrieved with
+  //   'void *ptheread_getspecific(pthread_key)'. So even thought the
+  //   parameter is 'const void *', the region escapes through the call.
+  if (II->isStr("pthread_setspecific"))
+    return true;
+
+  // - xpc_connection_set_context stores a value which can be retrieved later
+  //   with xpc_connection_get_context.
+  if (II->isStr("xpc_connection_set_context"))
+    return true;
+
+  // - funopen - sets a buffer for future IO calls.
+  if (II->isStr("funopen"))
+    return true;
+
+  StringRef FName = II->getName();
+
+  // - CoreFoundation functions that end with "NoCopy" can free a passed-in
+  //   buffer even if it is const.
+  if (FName.endswith("NoCopy"))
+    return true;
+
+  // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
+  //   be deallocated by NSMapRemove.
+  if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
+    return true;
+
+  // - Many CF containers allow objects to escape through custom
+  //   allocators/deallocators upon container construction. (PR12101)
+  if (FName.startswith("CF") || FName.startswith("CG")) {
+    return StrInStrNoCase(FName, "InsertValue")  != StringRef::npos ||
+           StrInStrNoCase(FName, "AddValue")     != StringRef::npos ||
+           StrInStrNoCase(FName, "SetValue")     != StringRef::npos ||
+           StrInStrNoCase(FName, "WithData")     != StringRef::npos ||
+           StrInStrNoCase(FName, "AppendValue")  != StringRef::npos ||
+           StrInStrNoCase(FName, "SetAttribute") != StringRef::npos;
+  }
+
+  return false;
+}
+
+
 const FunctionDecl *SimpleCall::getDecl() const {
   const FunctionDecl *D = CE->getDirectCallee();
   if (D)
@@ -265,6 +289,7 @@ const FunctionDecl *SimpleCall::getDecl() const {
   return getSVal(CE->getCallee()).getAsFunctionDecl();
 }
 
+
 void CXXMemberCall::addExtraInvalidatedRegions(RegionList &Regions) const {
   const Expr *Base = getOriginExpr()->getImplicitObjectArgument();
 
@@ -277,6 +302,7 @@ void CXXMemberCall::addExtraInvalidatedRegions(RegionList &Regions) const {
     Regions.push_back(R);
 }
 
+
 const BlockDataRegion *BlockCall::getBlockRegion() const {
   const Expr *Callee = getOriginExpr()->getCallee();
   const MemRegion *DataReg = getSVal(Callee).getAsRegion();
@@ -301,11 +327,13 @@ QualType BlockCall::getDeclaredResultType() const {
   return cast<FunctionType>(BlockTy->getPointeeType())->getResultType();
 }
 
+
 void CXXConstructorCall::addExtraInvalidatedRegions(RegionList &Regions) const {
   if (Target)
     Regions.push_back(Target);
 }
 
+
 CallEvent::param_iterator ObjCMessageInvocation::param_begin() const {
   const ObjCMethodDecl *D = getDecl();
   if (!D)
index 52f6ba4159d8553e504985d66c144425b64a906f..aeb47a131647d396dae61b637d3799049d7787be 100644 (file)
@@ -13,7 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Calls.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/ParentMap.h"
@@ -69,7 +69,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
   // (6) The 'GDM' is the same as the predecessor.
   // (7) The LocationContext is the same as the predecessor.
   // (8) The PostStmt is for a non-consumed Stmt or Expr.
-  // (9) The successor is a CallExpr StmtPoint (so that we would be able to
+  // (9) The successor is not a CallExpr StmtPoint (so that we would be able to
   //     find it when retrying a call with no inlining).
 
   // Conditions 1 and 2.
@@ -116,7 +116,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
   // Condition 9.
   const ProgramPoint SuccLoc = succ->getLocation();
   if (const StmtPoint *SP = dyn_cast<StmtPoint>(&SuccLoc))
-    if (CallOrObjCMessage::canBeInlined(SP->getStmt()))
+    if (CallEvent::mayBeInlined(SP->getStmt()))
       return false;
 
   return true;
index 74c65c8be03b3cf45b87e7f33460af70314cad4b..255d870186553357f46efdc9224b04f2a83e6a83 100644 (file)
@@ -18,6 +18,7 @@
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Calls.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
 #include "clang/AST/CharUnits.h"
@@ -241,7 +242,7 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
     return true;
     
   // Run before processing a call.
-  if (CallOrObjCMessage::canBeInlined(S.getStmt()))
+  if (CallEvent::mayBeInlined(S.getStmt()))
     return true;
 
   // Is this an expression that is consumed by another expression?  If so,
diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp
deleted file mode 100644 (file)
index a00eece..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-//===- ObjCMessage.cpp - Wrapper for ObjC messages and dot syntax -*- C++ -*--//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines ObjCMessage which serves as a common wrapper for ObjC
-// message expressions or implicit messages for loading/storing ObjC properties.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
-#include "clang/AST/DeclCXX.h"
-
-using namespace clang;
-using namespace ento;
-
-QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const {
-  QualType resultTy;
-  bool isLVal = false;
-
-  if (isObjCMessage()) {
-    resultTy = Msg.getResultType(ctx);
-  } else if (const CXXConstructExpr *Ctor =
-              CallE.dyn_cast<const CXXConstructExpr *>()) {
-    resultTy = Ctor->getType();
-  } else {
-    const CallExpr *FunctionCall = CallE.get<const CallExpr *>();
-
-    isLVal = FunctionCall->isGLValue();
-    const Expr *Callee = FunctionCall->getCallee();
-    if (const FunctionDecl *FD = State->getSVal(Callee, LCtx).getAsFunctionDecl())
-      resultTy = FD->getResultType();
-    else
-      resultTy = FunctionCall->getType();
-  }
-
-  if (isLVal)
-    resultTy = ctx.getPointerType(resultTy);
-
-  return resultTy;
-}
-
-SVal CallOrObjCMessage::getFunctionCallee() const {
-  assert(isFunctionCall());
-  assert(!isCXXCall());
-  const Expr *Fun = CallE.get<const CallExpr *>()->getCallee()->IgnoreParens();
-  return State->getSVal(Fun, LCtx);
-}
-
-SVal CallOrObjCMessage::getCXXCallee() const {
-  assert(isCXXCall());
-  const CallExpr *ActualCall = CallE.get<const CallExpr *>();
-  const Expr *callee =
-    cast<CXXMemberCallExpr>(ActualCall)->getImplicitObjectArgument();
-  
-  // FIXME: Will eventually need to cope with member pointers.  This is
-  // a limitation in getImplicitObjectArgument().
-  if (!callee)
-    return UnknownVal();
-  
-  return State->getSVal(callee, LCtx);
-}
-
-SVal
-CallOrObjCMessage::getInstanceMessageReceiver(const LocationContext *LC) const {
-  assert(isObjCMessage());
-  return Msg.getInstanceReceiverSVal(State, LC);
-}
-
-const Decl *CallOrObjCMessage::getDecl() const {
-  if (isCXXCall()) {
-    const CXXMemberCallExpr *CE =
-        cast<CXXMemberCallExpr>(CallE.dyn_cast<const CallExpr *>());
-    assert(CE);
-    return CE->getMethodDecl();
-  } else if (isObjCMessage()) {
-    return Msg.getMethodDecl();
-  } else if (isFunctionCall()) {
-    // In case of a C style call, use the path sensitive information to find
-    // the function declaration.
-    SVal CalleeVal = getFunctionCallee();
-    return CalleeVal.getAsFunctionDecl();
-  }
-  return 0;
-}
-
-bool CallOrObjCMessage::isCallbackArg(unsigned Idx, const Type *T) const {
-  // If the parameter is 0, it's harmless.
-  if (getArgSVal(Idx).isZeroConstant())
-    return false;
-    
-  // If a parameter is a block or a callback, assume it can modify pointer.
-  if (T->isBlockPointerType() ||
-      T->isFunctionPointerType() ||
-      T->isObjCSelType())
-    return true;
-
-  // Check if a callback is passed inside a struct (for both, struct passed by
-  // reference and by value). Dig just one level into the struct for now.
-  if (const PointerType *PT = dyn_cast<PointerType>(T))
-    T = PT->getPointeeType().getTypePtr();
-
-  if (const RecordType *RT = T->getAsStructureType()) {
-    const RecordDecl *RD = RT->getDecl();
-    for (RecordDecl::field_iterator I = RD->field_begin(),
-                                    E = RD->field_end(); I != E; ++I ) {
-      const Type *FieldT = I->getType().getTypePtr();
-      if (FieldT->isBlockPointerType() || FieldT->isFunctionPointerType())
-        return true;
-    }
-  }
-  return false;
-}
-
-bool CallOrObjCMessage::hasNonZeroCallbackArg() const {
-  unsigned NumOfArgs = getNumArgs();
-
-  // Process ObjC message first.
-  if (!CallE) {
-    const ObjCMethodDecl *D = Msg.getMethodDecl();
-    unsigned Idx = 0;
-    for (ObjCMethodDecl::param_const_iterator I = D->param_begin(),
-                                     E = D->param_end(); I != E; ++I, ++Idx) {
-      if (NumOfArgs <= Idx)
-        break;
-
-      if (isCallbackArg(Idx, (*I)->getType().getTypePtr()))
-        return true;
-    }
-    return false;
-  }
-
-  // Else, assume we are dealing with a Function call.
-  const FunctionDecl *FD = 0;
-  if (const CXXConstructExpr *Ctor =
-        CallE.dyn_cast<const CXXConstructExpr *>())
-    FD = Ctor->getConstructor();
-
-  const CallExpr * CE = CallE.get<const CallExpr *>();
-  FD = dyn_cast_or_null<FunctionDecl>(CE->getCalleeDecl());
-
-  // If calling using a function pointer, assume the function does not
-  // have a callback. TODO: We could check the types of the arguments here.
-  if (!FD)
-    return false;
-
-  unsigned Idx = 0;
-  for (FunctionDecl::param_const_iterator I = FD->param_begin(),
-                                      E = FD->param_end(); I != E; ++I, ++Idx) {
-    if (NumOfArgs <= Idx)
-      break;
-
-    if (isCallbackArg(Idx, (*I)->getType().getTypePtr()))
-      return true;
-  }
-  return false;
-}
-
-bool CallOrObjCMessage::isCFCGAllowingEscape(StringRef FName) {
-  if (!FName.startswith("CF") && !FName.startswith("CG"))
-    return false;
-
-  return StrInStrNoCase(FName, "InsertValue")  != StringRef::npos ||
-         StrInStrNoCase(FName, "AddValue")     != StringRef::npos ||
-         StrInStrNoCase(FName, "SetValue")     != StringRef::npos ||
-         StrInStrNoCase(FName, "WithData")     != StringRef::npos ||
-         StrInStrNoCase(FName, "AppendValue")  != StringRef::npos ||
-         StrInStrNoCase(FName, "SetAttribute") != StringRef::npos;
-}
-
-
index 9596751a0d6bc01c1aa4d391e4e23f515f42b33b..377642cc58c378a6edc039f49318e4657a9e8384 100644 (file)
@@ -717,6 +717,18 @@ FILE *useFunOpenNoReleaseFunction() {
     return f; // expected-warning{{leak}}
 }
 
+static int readNothing(void *_ctx, char *buf, int size) {
+  return 0;
+}
+FILE *useFunOpenReadNoRelease() {
+  void *ctx = malloc(sizeof(int));
+  FILE *f = funopen(ctx, readNothing, 0, 0, 0);
+  if (f == 0) {
+    free(ctx);
+  }
+  return f; // expected-warning{{leak}}
+}
+
 // Test setbuf, setvbuf.
 int my_main_no_warning() {
     char *p = malloc(100);