Index: clang/include/clang/Analysis/ConstructionContext.h =================================================================== --- clang/include/clang/Analysis/ConstructionContext.h +++ clang/include/clang/Analysis/ConstructionContext.h @@ -120,7 +120,8 @@ ConstructionContextItem(const Expr *E, unsigned Index) : Data(E), Kind(ArgumentKind), Index(Index) { assert(isa(E) || isa(E) || - isa(E) || isa(E)); + isa(E) || isa(E) || + isa(E)); } ConstructionContextItem(const CXXCtorInitializer *Init) Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -1276,7 +1276,7 @@ getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State); /// Gets a call event for a function call, Objective-C method call, - /// or a 'new' call. + /// a 'new', or a 'delete' call. CallEventRef<> getCall(const Stmt *S, ProgramStateRef State, const LocationContext *LC); Index: clang/lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -1408,6 +1408,8 @@ return getSimpleCall(CE, State, LC); } else if (const auto *NE = dyn_cast(S)) { return getCXXAllocatorCall(NE, State, LC); + } else if (const auto *DE = dyn_cast(S)) { + return getCXXDeallocatorCall(DE, State, LC); } else if (const auto *ME = dyn_cast(S)) { return getObjCMethodCall(ME, State, LC); } else { Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -945,8 +945,17 @@ ExplodedNodeSet DstPreCall; getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this); + ExplodedNodeSet DstPostCall; - getCheckerManager().runCheckersForPostCall(Dst, DstPreCall, *Call, *this); + if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { + StmtNodeBuilder Bldr(DstPreCall, DstPostCall, *currBldrCtx); + for (ExplodedNode *I : DstPreCall) { + defaultEvalCall(Bldr, I, *Call); + } + } else { + DstPostCall = DstPreCall; + } + getCheckerManager().runCheckersForPostCall(Dst, DstPostCall, *Call, *this); } void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred, Index: clang/test/Analysis/cxxnewexpr-callback-inline.cpp =================================================================== --- clang/test/Analysis/cxxnewexpr-callback-inline.cpp +++ clang/test/Analysis/cxxnewexpr-callback-inline.cpp @@ -4,12 +4,15 @@ namespace std { void *malloc(size_t); + void free(void *); } void *operator new(size_t size) { return std::malloc(size); } +void operator delete(void *ptr) { std::free(ptr); } struct S { S() {} + ~S() {} }; void foo(); @@ -17,8 +20,12 @@ void test() { S *s = new S(); foo(); + delete s; } +/* +void test() { + S *s = new S(); // CHECK: PreCall (operator new) // CHECK-NEXT: PreCall (std::malloc) // CHECK-NEXT: PostCall (std::malloc) @@ -28,5 +35,15 @@ // CHECK-NEXT: PostCall (S::S) // CHECK-NEXT: PreStmt // CHECK-NEXT: PostStmt + foo(); // CHECK-NEXT: PreCall (foo) // CHECK-NEXT: PostCall (foo) + delete s; +// CHECK-NEXT: PreCall (S::~S) +// CHECK-NEXT: PostCall (S::~S) +// CHECK-NEXT: PreCall (operator delete) +// CHECK-NEXT: PreCall (std::free) +// CHECK-NEXT: PostCall (std::free) +// CHECK-NEXT: PostCall (operator delete) +} +*/ Index: clang/test/Analysis/cxxnewexpr-callback-noinline.cpp =================================================================== --- clang/test/Analysis/cxxnewexpr-callback-noinline.cpp +++ clang/test/Analysis/cxxnewexpr-callback-noinline.cpp @@ -3,13 +3,16 @@ #include "Inputs/system-header-simulator-cxx.h" namespace std { - void *malloc(size_t); -} +void *malloc(size_t); +void free(void *); +} // namespace std void *operator new(size_t size) { return std::malloc(size); } +void operator delete(void *ptr) { std::free(ptr); } struct S { S() {} + ~S() {} }; void foo(); @@ -17,13 +20,35 @@ void test() { S *s = new S(); foo(); + delete s; } +/* +void test() { + S *s = new S(); // CHECK: PreCall (S::S) // CHECK-NEXT: PostCall (S::S) // CHECK-NEXT: PreStmt // CHECK-NEXT: PostStmt + foo(); // CHECK-NEXT: PreCall (foo) // CHECK-NEXT: PostCall (foo) + delete s; +// CHECK-NEXT: PreCall (S::~S) +// CHECK-NEXT: PostCall (S::~S) +// CHECK-NEXT: PreCall (operator delete) +// CHECK-NEXT: PostCall (operator delete) +} + +void operator delete(void *ptr) { + std::free(ptr); +// CHECK-NEXT: PreCall (std::free) +// CHECK-NEXT: PostCall (std::free) +} + +void *operator new(size_t size) { + return std::malloc(size); // CHECK-NEXT: PreCall (std::malloc) // CHECK-NEXT: PostCall (std::malloc) +} +*/ Index: clang/test/Analysis/dtor.cpp =================================================================== --- clang/test/Analysis/dtor.cpp +++ clang/test/Analysis/dtor.cpp @@ -570,3 +570,32 @@ // no-crash } } // namespace dtor_over_loc_concrete_int + +// Test overriden new/delete operators +struct CustomOperators { + void *operator new(size_t count) { + return malloc(count); + } + + void operator delete(void *addr) { + free(addr); + } + +private: + int i; +}; + +void compliant() { + auto *a = new CustomOperators(); + delete a; +} + +void overrideLeak() { + auto *a = new CustomOperators(); +} // expected-warning{{Potential leak of memory pointed to by 'a'}} + +void overrideDoubleDelete() { + auto *a = new CustomOperators(); + delete a; + delete a; // expected-warning@581 {{Attempt to free released memory}} +}