Browse Source

[analyzer] operator new: Add a new checker callback, check::NewAllocator.

The callback runs after operator new() and before the construction and allows
the checker to access the casted return value of operator new() (in the
sense of r322780) which is not available in the PostCall callback for the
allocator call.

Update MallocChecker to use the new callback instead of PostStmt<CXXNewExpr>,
which gets called after the constructor.

Differential Revision: https://reviews.llvm.org/D41406
rdar://problem/12180598


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@322787 91177308-0d34-0410-b5e6-96231b3b80d8
Artem Dergachev 7 years ago
parent
commit
0e0ad4e27a

+ 16 - 0
include/clang/StaticAnalyzer/Core/Checker.h

@@ -283,6 +283,22 @@ public:
   }
   }
 };
 };
 
 
+class NewAllocator {
+  template <typename CHECKER>
+  static void _checkNewAllocator(void *checker, const CXXNewExpr *NE,
+                                 SVal Target, CheckerContext &C) {
+    ((const CHECKER *)checker)->checkNewAllocator(NE, Target, C);
+  }
+
+public:
+  template <typename CHECKER>
+  static void _register(CHECKER *checker, CheckerManager &mgr) {
+    mgr._registerForNewAllocator(
+        CheckerManager::CheckNewAllocatorFunc(checker,
+                                              _checkNewAllocator<CHECKER>));
+  }
+};
+
 class LiveSymbols {
 class LiveSymbols {
   template <typename CHECKER>
   template <typename CHECKER>
   static void _checkLiveSymbols(void *checker, ProgramStateRef state,
   static void _checkLiveSymbols(void *checker, ProgramStateRef state,

+ 14 - 0
include/clang/StaticAnalyzer/Core/CheckerManager.h

@@ -303,6 +303,13 @@ public:
                                      ExplodedNodeSet &Dst, ExplodedNode *Pred,
                                      ExplodedNodeSet &Dst, ExplodedNode *Pred,
                                      ExprEngine &Eng);
                                      ExprEngine &Eng);
 
 
+  /// \brief Run checkers between C++ operator new and constructor calls.
+  void runCheckersForNewAllocator(const CXXNewExpr *NE, SVal Target,
+                                  ExplodedNodeSet &Dst,
+                                  ExplodedNode *Pred,
+                                  ExprEngine &Eng,
+                                  bool wasInlined = false);
+
   /// \brief Run checkers for live symbols.
   /// \brief Run checkers for live symbols.
   ///
   ///
   /// Allows modifying SymbolReaper object. For example, checkers can explicitly
   /// Allows modifying SymbolReaper object. For example, checkers can explicitly
@@ -437,6 +444,9 @@ public:
   
   
   typedef CheckerFn<void (const Stmt *, CheckerContext &)>
   typedef CheckerFn<void (const Stmt *, CheckerContext &)>
       CheckBranchConditionFunc;
       CheckBranchConditionFunc;
+
+  typedef CheckerFn<void (const CXXNewExpr *, SVal, CheckerContext &)>
+      CheckNewAllocatorFunc;
   
   
   typedef CheckerFn<void (SymbolReaper &, CheckerContext &)>
   typedef CheckerFn<void (SymbolReaper &, CheckerContext &)>
       CheckDeadSymbolsFunc;
       CheckDeadSymbolsFunc;
@@ -494,6 +504,8 @@ public:
 
 
   void _registerForBranchCondition(CheckBranchConditionFunc checkfn);
   void _registerForBranchCondition(CheckBranchConditionFunc checkfn);
 
 
+  void _registerForNewAllocator(CheckNewAllocatorFunc checkfn);
+
   void _registerForLiveSymbols(CheckLiveSymbolsFunc checkfn);
   void _registerForLiveSymbols(CheckLiveSymbolsFunc checkfn);
 
 
   void _registerForDeadSymbols(CheckDeadSymbolsFunc checkfn);
   void _registerForDeadSymbols(CheckDeadSymbolsFunc checkfn);
@@ -603,6 +615,8 @@ private:
 
 
   std::vector<CheckBranchConditionFunc> BranchConditionCheckers;
   std::vector<CheckBranchConditionFunc> BranchConditionCheckers;
 
 
+  std::vector<CheckNewAllocatorFunc> NewAllocatorCheckers;
+
   std::vector<CheckLiveSymbolsFunc> LiveSymbolsCheckers;
   std::vector<CheckLiveSymbolsFunc> LiveSymbolsCheckers;
 
 
   std::vector<CheckDeadSymbolsFunc> DeadSymbolsCheckers;
   std::vector<CheckDeadSymbolsFunc> DeadSymbolsCheckers;

+ 17 - 0
lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp

@@ -42,6 +42,7 @@ class CheckerDocumentation : public Checker< check::PreStmt<ReturnStmt>,
                                        check::PreCall,
                                        check::PreCall,
                                        check::PostCall,
                                        check::PostCall,
                                        check::BranchCondition,
                                        check::BranchCondition,
+                                       check::NewAllocator,
                                        check::Location,
                                        check::Location,
                                        check::Bind,
                                        check::Bind,
                                        check::DeadSymbols,
                                        check::DeadSymbols,
@@ -126,6 +127,22 @@ public:
   /// \brief Pre-visit of the condition statement of a branch (such as IfStmt).
   /// \brief Pre-visit of the condition statement of a branch (such as IfStmt).
   void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {}
   void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {}
 
 
+  /// \brief Post-visit the C++ operator new's allocation call.
+  ///
+  /// Execution of C++ operator new consists of the following phases: (1) call
+  /// default or overridden operator new() to allocate memory (2) cast the
+  /// return value of operator new() from void pointer type to class pointer
+  /// type, (3) assuming that the value is non-null, call the object's
+  /// constructor over this pointer, (4) declare that the value of the
+  /// new-expression is this pointer. This callback is called between steps
+  /// (2) and (3). Post-call for the allocator is called after step (1).
+  /// Pre-statement for the new-expression is called on step (4) when the value
+  /// of the expression is evaluated.
+  /// \param NE     The C++ new-expression that triggered the allocation.
+  /// \param Target The allocated region, casted to the class type.
+  void checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+                         CheckerContext &) const {}
+
   /// \brief Called on a load from and a store to a location.
   /// \brief Called on a load from and a store to a location.
   ///
   ///
   /// The method will be called each time a location (pointer) value is
   /// The method will be called each time a location (pointer) value is

+ 55 - 25
lib/StaticAnalyzer/Checkers/MallocChecker.cpp

@@ -162,6 +162,7 @@ class MallocChecker : public Checker<check::DeadSymbols,
                                      check::PreCall,
                                      check::PreCall,
                                      check::PostStmt<CallExpr>,
                                      check::PostStmt<CallExpr>,
                                      check::PostStmt<CXXNewExpr>,
                                      check::PostStmt<CXXNewExpr>,
+                                     check::NewAllocator,
                                      check::PreStmt<CXXDeleteExpr>,
                                      check::PreStmt<CXXDeleteExpr>,
                                      check::PostStmt<BlockExpr>,
                                      check::PostStmt<BlockExpr>,
                                      check::PostObjCMessage,
                                      check::PostObjCMessage,
@@ -207,6 +208,8 @@ public:
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
   void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
   void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
   void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
   void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
+  void checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+                         CheckerContext &C) const;
   void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
   void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
   void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
   void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
   void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
   void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
@@ -281,10 +284,18 @@ private:
   bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
   bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
   ///@}
   ///@}
 
 
+  /// \brief Process C++ operator new()'s allocation, which is the part of C++
+  /// new-expression that goes before the constructor.
+  void processNewAllocation(const CXXNewExpr *NE, CheckerContext &C,
+                            SVal Target) const;
+
   /// \brief Perform a zero-allocation check.
   /// \brief Perform a zero-allocation check.
+  /// The optional \p RetVal parameter specifies the newly allocated pointer
+  /// value; if unspecified, the value of expression \p E is used.
   ProgramStateRef ProcessZeroAllocation(CheckerContext &C, const Expr *E,
   ProgramStateRef ProcessZeroAllocation(CheckerContext &C, const Expr *E,
                                         const unsigned AllocationSizeArg,
                                         const unsigned AllocationSizeArg,
-                                        ProgramStateRef State) const;
+                                        ProgramStateRef State,
+                                        Optional<SVal> RetVal = None) const;
 
 
   ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
   ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
                                        const CallExpr *CE,
                                        const CallExpr *CE,
@@ -300,7 +311,7 @@ private:
                                       AllocationFamily Family = AF_Malloc);
                                       AllocationFamily Family = AF_Malloc);
 
 
   static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE,
   static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE,
-                                       ProgramStateRef State);
+                                       ProgramStateRef State, SVal Target);
 
 
   // Check if this malloc() for special flags. At present that means M_ZERO or
   // Check if this malloc() for special flags. At present that means M_ZERO or
   // __GFP_ZERO (in which case, treat it like calloc).
   // __GFP_ZERO (in which case, treat it like calloc).
@@ -309,9 +320,12 @@ private:
                       const ProgramStateRef &State) const;
                       const ProgramStateRef &State) const;
 
 
   /// Update the RefState to reflect the new memory allocation.
   /// Update the RefState to reflect the new memory allocation.
+  /// The optional \p RetVal parameter specifies the newly allocated pointer
+  /// value; if unspecified, the value of expression \p E is used.
   static ProgramStateRef
   static ProgramStateRef
   MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
   MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
-                       AllocationFamily Family = AF_Malloc);
+                       AllocationFamily Family = AF_Malloc,
+                       Optional<SVal> RetVal = None);
 
 
   ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
   ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
                               const OwnershipAttr* Att,
                               const OwnershipAttr* Att,
@@ -949,13 +963,15 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
 }
 }
 
 
 // Performs a 0-sized allocations check.
 // Performs a 0-sized allocations check.
-ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,
-                                               const Expr *E,
-                                               const unsigned AllocationSizeArg,
-                                               ProgramStateRef State) const {
+ProgramStateRef MallocChecker::ProcessZeroAllocation(
+    CheckerContext &C, const Expr *E, const unsigned AllocationSizeArg,
+    ProgramStateRef State, Optional<SVal> RetVal) const {
   if (!State)
   if (!State)
     return nullptr;
     return nullptr;
 
 
+  if (!RetVal)
+    RetVal = C.getSVal(E);
+
   const Expr *Arg = nullptr;
   const Expr *Arg = nullptr;
 
 
   if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
   if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
@@ -987,8 +1003,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,
       State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero));
       State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero));
 
 
   if (TrueState && !FalseState) {
   if (TrueState && !FalseState) {
-    SVal retVal = C.getSVal(E);
-    SymbolRef Sym = retVal.getAsLocSymbol();
+    SymbolRef Sym = RetVal->getAsLocSymbol();
     if (!Sym)
     if (!Sym)
       return State;
       return State;
 
 
@@ -1049,9 +1064,9 @@ static bool treatUnusedNewEscaped(const CXXNewExpr *NE) {
   return false;
   return false;
 }
 }
 
 
-void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
-                                  CheckerContext &C) const {
-
+void MallocChecker::processNewAllocation(const CXXNewExpr *NE,
+                                         CheckerContext &C,
+                                         SVal Target) const {
   if (NE->getNumPlacementArgs())
   if (NE->getNumPlacementArgs())
     for (CXXNewExpr::const_arg_iterator I = NE->placement_arg_begin(),
     for (CXXNewExpr::const_arg_iterator I = NE->placement_arg_begin(),
          E = NE->placement_arg_end(); I != E; ++I)
          E = NE->placement_arg_end(); I != E; ++I)
@@ -1071,37 +1086,48 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
   // MallocUpdateRefState() instead of MallocMemAux() which breakes the
   // MallocUpdateRefState() instead of MallocMemAux() which breakes the
   // existing binding.
   // existing binding.
   State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
   State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
-                                                           : AF_CXXNew);
-  State = addExtentSize(C, NE, State);
-  State = ProcessZeroAllocation(C, NE, 0, State);
+                                                           : AF_CXXNew, Target);
+  State = addExtentSize(C, NE, State, Target);
+  State = ProcessZeroAllocation(C, NE, 0, State, Target);
   C.addTransition(State);
   C.addTransition(State);
 }
 }
 
 
+void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
+                                  CheckerContext &C) const {
+  if (!C.getAnalysisManager().getAnalyzerOptions().mayInlineCXXAllocator())
+    processNewAllocation(NE, C, C.getSVal(NE));
+}
+
+void MallocChecker::checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+                                      CheckerContext &C) const {
+  if (!C.wasInlined)
+    processNewAllocation(NE, C, Target);
+}
+
 // Sets the extent value of the MemRegion allocated by
 // Sets the extent value of the MemRegion allocated by
 // new expression NE to its size in Bytes.
 // new expression NE to its size in Bytes.
 //
 //
 ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C,
 ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C,
                                              const CXXNewExpr *NE,
                                              const CXXNewExpr *NE,
-                                             ProgramStateRef State) {
+                                             ProgramStateRef State,
+                                             SVal Target) {
   if (!State)
   if (!State)
     return nullptr;
     return nullptr;
   SValBuilder &svalBuilder = C.getSValBuilder();
   SValBuilder &svalBuilder = C.getSValBuilder();
   SVal ElementCount;
   SVal ElementCount;
-  const LocationContext *LCtx = C.getLocationContext();
   const SubRegion *Region;
   const SubRegion *Region;
   if (NE->isArray()) {
   if (NE->isArray()) {
     const Expr *SizeExpr = NE->getArraySize();
     const Expr *SizeExpr = NE->getArraySize();
     ElementCount = C.getSVal(SizeExpr);
     ElementCount = C.getSVal(SizeExpr);
     // Store the extent size for the (symbolic)region
     // Store the extent size for the (symbolic)region
     // containing the elements.
     // containing the elements.
-    Region = (State->getSVal(NE, LCtx))
-                 .getAsRegion()
+    Region = Target.getAsRegion()
                  ->getAs<SubRegion>()
                  ->getAs<SubRegion>()
-                 ->getSuperRegion()
+                 ->StripCasts()
                  ->getAs<SubRegion>();
                  ->getAs<SubRegion>();
   } else {
   } else {
     ElementCount = svalBuilder.makeIntVal(1, true);
     ElementCount = svalBuilder.makeIntVal(1, true);
-    Region = (State->getSVal(NE, LCtx)).getAsRegion()->getAs<SubRegion>();
+    Region = Target.getAsRegion()->getAs<SubRegion>();
   }
   }
   assert(Region);
   assert(Region);
 
 
@@ -1261,18 +1287,22 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
 ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
 ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
                                                     const Expr *E,
                                                     const Expr *E,
                                                     ProgramStateRef State,
                                                     ProgramStateRef State,
-                                                    AllocationFamily Family) {
+                                                    AllocationFamily Family,
+                                                    Optional<SVal> RetVal) {
   if (!State)
   if (!State)
     return nullptr;
     return nullptr;
 
 
   // Get the return value.
   // Get the return value.
-  SVal retVal = C.getSVal(E);
+  if (!RetVal)
+    RetVal = C.getSVal(E);
 
 
   // We expect the malloc functions to return a pointer.
   // We expect the malloc functions to return a pointer.
-  if (!retVal.getAs<Loc>())
+  if (!RetVal->getAs<Loc>())
     return nullptr;
     return nullptr;
 
 
-  SymbolRef Sym = retVal.getAsLocSymbol();
+  SymbolRef Sym = RetVal->getAsLocSymbol();
+  // This is a return value of a function that was not inlined, such as malloc()
+  // or new(). We've checked that in the caller. Therefore, it must be a symbol.
   assert(Sym);
   assert(Sym);
 
 
   // Set the symbol's state to Allocated.
   // Set the symbol's state to Allocated.

+ 44 - 0
lib/StaticAnalyzer/Core/CheckerManager.cpp

@@ -469,6 +469,46 @@ void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition,
   expandGraphWithCheckers(C, Dst, Src);
   expandGraphWithCheckers(C, Dst, Src);
 }
 }
 
 
+namespace {
+  struct CheckNewAllocatorContext {
+    typedef std::vector<CheckerManager::CheckNewAllocatorFunc> CheckersTy;
+    const CheckersTy &Checkers;
+    const CXXNewExpr *NE;
+    SVal Target;
+    bool WasInlined;
+    ExprEngine &Eng;
+
+    CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+    CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+    CheckNewAllocatorContext(const CheckersTy &Checkers, const CXXNewExpr *NE,
+                             SVal Target, bool WasInlined, ExprEngine &Eng)
+        : Checkers(Checkers), NE(NE), Target(Target), WasInlined(WasInlined),
+          Eng(Eng) {}
+
+    void runChecker(CheckerManager::CheckNewAllocatorFunc checkFn,
+                    NodeBuilder &Bldr, ExplodedNode *Pred) {
+      // TODO: Does this deserve a custom program point? For now we're re-using
+      // PostImplicitCall because we're guaranteed to use the non-implicit
+      // PostStmt for the PostCall callback, because we have some sort of
+      // call site (CXXNewExpr) in this scenario.
+      ProgramPoint L = PostImplicitCall(NE->getOperatorNew(), NE->getLocStart(),
+                                        Pred->getLocationContext());
+      CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+      checkFn(NE, Target, C);
+    }
+  };
+}
+
+void CheckerManager::runCheckersForNewAllocator(
+    const CXXNewExpr *NE, SVal Target, ExplodedNodeSet &Dst, ExplodedNode *Pred,
+    ExprEngine &Eng, bool WasInlined) {
+  ExplodedNodeSet Src;
+  Src.insert(Pred);
+  CheckNewAllocatorContext C(NewAllocatorCheckers, NE, Target, WasInlined, Eng);
+  expandGraphWithCheckers(C, Dst, Src);
+}
+
 /// \brief Run checkers for live symbols.
 /// \brief Run checkers for live symbols.
 void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state,
 void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state,
                                                SymbolReaper &SymReaper) {
                                                SymbolReaper &SymReaper) {
@@ -711,6 +751,10 @@ void CheckerManager::_registerForBranchCondition(
   BranchConditionCheckers.push_back(checkfn);
   BranchConditionCheckers.push_back(checkfn);
 }
 }
 
 
+void CheckerManager::_registerForNewAllocator(CheckNewAllocatorFunc checkfn) {
+  NewAllocatorCheckers.push_back(checkfn);
+}
+
 void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) {
 void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) {
   LiveSymbolsCheckers.push_back(checkfn);
   LiveSymbolsCheckers.push_back(checkfn);
 }
 }

+ 7 - 2
lib/StaticAnalyzer/Core/ExprEngineCXX.cpp

@@ -505,8 +505,13 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
         setCXXNewAllocatorValue(State, CNE, LCtx, State->getSVal(CNE, LCtx)));
         setCXXNewAllocatorValue(State, CNE, LCtx, State->getSVal(CNE, LCtx)));
   }
   }
 
 
-  getCheckerManager().runCheckersForPostCall(Dst, DstPostValue,
-                                             *Call, *this);
+  ExplodedNodeSet DstPostPostCallCallback;
+  getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
+                                             DstPostValue, *Call, *this);
+  for (auto I : DstPostPostCallCallback) {
+    getCheckerManager().runCheckersForNewAllocator(
+        CNE, getCXXNewAllocatorValue(I->getState(), CNE, LCtx), Dst, I, *this);
+  }
 }
 }
 
 
 void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
 void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,

+ 17 - 4
lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

@@ -350,10 +350,23 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
     CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState);
     CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState);
 
 
     ExplodedNodeSet DstPostCall;
     ExplodedNodeSet DstPostCall;
-    getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode,
-                                               *UpdatedCall, *this,
-                                               /*WasInlined=*/true);
-
+    if (const CXXNewExpr *CNE = dyn_cast_or_null<CXXNewExpr>(CE)) {
+      ExplodedNodeSet DstPostPostCallCallback;
+      getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
+                                                 CEENode, *UpdatedCall, *this,
+                                                 /*WasInlined=*/true);
+      for (auto I : DstPostPostCallCallback) {
+        getCheckerManager().runCheckersForNewAllocator(
+            CNE, getCXXNewAllocatorValue(I->getState(), CNE,
+                                         calleeCtx->getParent()),
+            DstPostCall, I, *this,
+            /*WasInlined=*/true);
+      }
+    } else {
+      getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode,
+                                                 *UpdatedCall, *this,
+                                                 /*WasInlined=*/true);
+    }
     ExplodedNodeSet Dst;
     ExplodedNodeSet Dst;
     if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
     if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
       getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg,
       getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg,

+ 10 - 8
test/Analysis/NewDelete-custom.cpp

@@ -1,8 +1,10 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,unix.Malloc -std=c++11 -fblocks -verify %s
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,unix.Malloc -std=c++11 -fblocks -verify %s
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,unix.Malloc -std=c++11 -DLEAKS -fblocks -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,unix.Malloc -std=c++11 -DLEAKS=1 -fblocks -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,unix.Malloc -std=c++11 -analyzer-config c++-allocator-inlining=true -DALLOCATOR_INLINING=1 -fblocks -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,unix.Malloc -std=c++11 -analyzer-config c++-allocator-inlining=true -DLEAKS=1 -DALLOCATOR_INLINING=1 -fblocks -verify %s
 #include "Inputs/system-header-simulator-cxx.h"
 #include "Inputs/system-header-simulator-cxx.h"
 
 
-#ifndef LEAKS
+#if !LEAKS
 // expected-no-diagnostics
 // expected-no-diagnostics
 #endif
 #endif
 
 
@@ -26,7 +28,7 @@ void testNewMethod() {
 
 
   C *c3 = ::new C;
   C *c3 = ::new C;
 }
 }
-#ifdef LEAKS
+#if LEAKS && !ALLOCATOR_INLINING
 // expected-warning@-2{{Potential leak of memory pointed to by 'c3'}}
 // expected-warning@-2{{Potential leak of memory pointed to by 'c3'}}
 #endif
 #endif
 
 
@@ -37,7 +39,7 @@ void testOpNewArray() {
 void testNewExprArray() {
 void testNewExprArray() {
   int *p = new int[0];
   int *p = new int[0];
 }
 }
-#ifdef LEAKS
+#if LEAKS && !ALLOCATOR_INLINING
 // expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
 // expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
 #endif
 #endif
 
 
@@ -50,7 +52,7 @@ void testOpNew() {
 void testNewExpr() {
 void testNewExpr() {
   int *p = new int;
   int *p = new int;
 }
 }
-#ifdef LEAKS
+#if LEAKS && !ALLOCATOR_INLINING
 // expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
 // expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
 #endif
 #endif
 
 
@@ -59,21 +61,21 @@ void testNewExpr() {
 void testOpNewNoThrow() {
 void testOpNewNoThrow() {
   void *p = operator new(0, std::nothrow);
   void *p = operator new(0, std::nothrow);
 }
 }
-#ifdef LEAKS
+#if LEAKS
 // expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
 // expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
 #endif
 #endif
 
 
 void testNewExprNoThrow() {
 void testNewExprNoThrow() {
   int *p = new(std::nothrow) int;
   int *p = new(std::nothrow) int;
 }
 }
-#ifdef LEAKS
+#if LEAKS
 // expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
 // expected-warning@-2{{Potential leak of memory pointed to by 'p'}}
 #endif
 #endif
 
 
 //----- Custom placement operators
 //----- Custom placement operators
 void testOpNewPlacement() {
 void testOpNewPlacement() {
   void *p = operator new(0, 0.1); // no warn
   void *p = operator new(0, 0.1); // no warn
-} 
+}
 
 
 void testNewExprPlacement() {
 void testNewExprPlacement() {
   int *p = new(0.1) int; // no warn
   int *p = new(0.1) int; // no warn

+ 18 - 0
test/Analysis/new-ctor-malloc.cpp

@@ -0,0 +1,18 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection,unix.Malloc -analyzer-config c++-allocator-inlining=true -std=c++11 -verify %s
+
+void clang_analyzer_eval(bool);
+
+typedef __typeof__(sizeof(int)) size_t;
+
+void *malloc(size_t size);
+
+void *operator new(size_t size) throw() {
+  void *x = malloc(size);
+  if (!x)
+    return nullptr;
+  return x;
+}
+
+void checkNewAndConstructorInlining() {
+  int *s = new int;
+} // expected-warning {{Potential leak of memory pointed to by 's'}}