Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -801,13 +801,14 @@ } void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { - if (C.wasInlined) - return; - const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return; + if (C.wasInlined && FD->getDeclName().getCXXOverloadedOperator() != OO_New && + FD->getDeclName().getCXXOverloadedOperator() != OO_Array_New) + return; + ProgramStateRef State = C.getState(); bool ReleasedAllocatedMemory = false; @@ -1100,6 +1101,18 @@ ->getAs() ->getSuperRegion() ->getAs(); + // FIXME: Since 'ExprEngine::VisitCXXNewAllocator' has not yet been fully + // implemented, the custom operator new[] may return Non-ElementRegion after + // its inline call. When 'ExprEngine::VisitCXXNewAllocator' is fully + // implemented, the following 'if' statement should be deleted. + if (!Region) { + assert(NE->isArray() && + !C.getSourceManager().isInSystemHeader( + NE->getOperatorNew()->getLocStart()) && + "The operator new[] can return non-ElementRegion only when it is " + "a custom version and is inlined."); + return nullptr; + } } else { ElementCount = svalBuilder.makeIntVal(1, true); Region = (State->getSVal(NE, LCtx)).getAsRegion()->getAs(); @@ -1275,8 +1288,16 @@ return nullptr; SymbolRef Sym = retVal.getAsLocSymbol(); - assert(Sym); - + // Special case when the 'c++-allocator-inlining' config option sets true and + // the c++ allocator return a Null pointer. + if (!Sym) { + assert(cosnt CXXNewExpr *CNE = dyn_cast(E) && + !C.getSourceManager().isInSystemHeader( + CNE->getOperatorNew()->getLocStart()) && + "Only custom operator new call can be inlined and return a Null " + "pointer!"); + return nullptr; + } // Set the symbol's state to Allocated. return State->set(Sym, RefState::getAllocated(Family, E)); } @@ -1488,7 +1509,7 @@ R = R->StripCasts(); - // Blocks might show up as heap data, but should not be free()d + // Blocks might show up as heap data, but should not be freed if (isa(R)) { ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); return nullptr; Index: test/Analysis/Inputs/system-header-simulator-cxx.h =================================================================== --- test/Analysis/Inputs/system-header-simulator-cxx.h +++ test/Analysis/Inputs/system-header-simulator-cxx.h @@ -584,6 +584,8 @@ } +void* operator new(std::size_t) throw(std::bad_alloc); +void* operator new[](std::size_t) throw(std::bad_alloc); void* operator new(std::size_t, const std::nothrow_t&) throw(); void* operator new[](std::size_t, const std::nothrow_t&) throw(); void operator delete(void*, const std::nothrow_t&) throw(); Index: test/Analysis/NewDelete-custom.cpp =================================================================== --- test/Analysis/NewDelete-custom.cpp +++ test/Analysis/NewDelete-custom.cpp @@ -1,5 +1,6 @@ -// 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,unix.Malloc -std=c++11 -fcxx-exceptions -fblocks -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,unix.Malloc -fcxx-exceptions -std=c++11 -DLEAKS -fblocks -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,unix.Malloc -fcxx-exceptions -std=c++11 -analyzer-config c++-allocator-inlining=true -DLEAKS -fblocks -verify %s #include "Inputs/system-header-simulator-cxx.h" #ifndef LEAKS @@ -9,9 +10,24 @@ void *allocator(std::size_t size); -void *operator new[](std::size_t size) throw() { return allocator(size); } -void *operator new(std::size_t size) throw() { return allocator(size); } -void *operator new(std::size_t size, std::nothrow_t& nothrow) throw() { return allocator(size); } +void *operator new[](std::size_t size) throw(std::bad_alloc) { return allocator(size); } +void *operator new(std::size_t size) throw(std::bad_alloc) { return allocator(size); } +void *operator new[](std::size_t size, const std::nothrow_t ¬hrow) throw() { + void *p = 0; + try { + p = allocator(size); + } catch (...) { + } + return p; +} +void *operator new(std::size_t size, const std::nothrow_t ¬hrow) throw() { + void *p = 0; + try { + p = allocator(size); + } catch (...) { + } + return p; +} void *operator new(std::size_t, double d); class C { @@ -31,8 +47,11 @@ #endif void testOpNewArray() { - void *p = operator new[](0); // call is inlined, no warn + void *p = operator new[](0); } +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'p'}} +#endif void testNewExprArray() { int *p = new int[0]; @@ -44,8 +63,11 @@ //----- Custom non-placement operators void testOpNew() { - void *p = operator new(0); // call is inlined, no warn + void *p = operator new(0); } +#ifdef LEAKS +// expected-warning@-2{{Potential leak of memory pointed to by 'p'}} +#endif void testNewExpr() { int *p = new int;