+ Improved test coverage for cplusplus.NewDelete checker.
llvm-svn: 178244
return false;
}
+// Tells if the callee is one of the following:
+// 1) A global non-placement new/delete operator function.
+// 2) A global placement operator function with the single placement argument
+// of type std::nothrow_t.
bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD,
ASTContext &C) const {
if (!FD)
Kind != OO_Delete && Kind != OO_Array_Delete)
return false;
- // Skip custom new operators.
- if (!FD->isImplicit() &&
- !C.getSourceManager().isInSystemHeader(FD->getLocStart()))
+ // Skip all operator new/delete methods.
+ if (isa<CXXMethodDecl>(FD))
return false;
// Return true if tested operator is a standard placement nothrow operator.
unsigned blockCount = currBldrCtx->blockCount();
const LocationContext *LCtx = Pred->getLocationContext();
- DefinedOrUnknownSVal symVal = svalBuilder.conjureSymbolVal(0, CNE, LCtx,
- CNE->getType(),
- blockCount);
- ProgramStateRef State = Pred->getState();
+ DefinedOrUnknownSVal symVal = UnknownVal();
+ FunctionDecl *FD = CNE->getOperatorNew();
+
+ bool IsStandardGlobalOpNewFunction = false;
+ if (FD && !isa<CXXMethodDecl>(FD) && !FD->isVariadic()) {
+ if (FD->getNumParams() == 2) {
+ QualType T = FD->getParamDecl(1)->getType();
+ if (const IdentifierInfo *II = T.getBaseTypeIdentifier())
+ // NoThrow placement new behaves as a standard new.
+ IsStandardGlobalOpNewFunction = II->getName().equals("nothrow_t");
+ }
+ else
+ // Placement forms are considered non-standard.
+ IsStandardGlobalOpNewFunction = (FD->getNumParams() == 1);
+ }
+
+ // We assume all standard global 'operator new' functions allocate memory in
+ // heap. We realize this is an approximation that might not correctly model
+ // a custom global allocator.
+ if (IsStandardGlobalOpNewFunction)
+ symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount);
+ else
+ symVal = svalBuilder.conjureSymbolVal(0, CNE, LCtx, CNE->getType(),
+ blockCount);
+ ProgramStateRef State = Pred->getState();
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXAllocatorCall> Call =
CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
// is not declared as non-throwing, failures /must/ be signalled by
// exceptions, and thus the return value will never be NULL.
// C++11 [basic.stc.dynamic.allocation]p3.
- FunctionDecl *FD = CNE->getOperatorNew();
if (FD && getContext().getLangOpts().CXXExceptions) {
QualType Ty = FD->getType();
if (const FunctionProtoType *ProtoType = Ty->getAs<FunctionProtoType>())
// check for leaks
//------------------
-void testGlobalExprNewBeforeOverload1() {
- int *p = new int;
-} // expected-warning{{Memory is never released; potential leak}}
-
-void testGlobalExprNewBeforeOverload2() {
- int *p = ::new int;
+//----- Standard non-placement operators
+void testGlobalOpNew() {
+ void *p = operator new(0);
} // expected-warning{{Memory is never released; potential leak}}
-void testGlobalOpNewBeforeOverload() {
- void *p = operator new(0);
+void testGlobalOpNewArray() {
+ void *p = operator new[](0);
} // expected-warning{{Memory is never released; potential leak}}
-void testMemIsOnHeap() {
+void testGlobalNewExpr() {
int *p = new int;
- if (global != p)
- global = p;
} // expected-warning{{Memory is never released; potential leak}}
-//FIXME: currently a memory region for 'new' is not a heap region, that lead to
-//false-positive 'memory leak' ('global != p' is not evaluated to true and 'p'
-//does not escape)
-void *operator new(std::size_t);
-void *operator new(std::size_t, double d);
-void *operator new[](std::size_t);
-void *operator new[](std::size_t, double d);
-
-void testExprPlacementNew() {
- int i;
- int *p1 = new(&i) int; // no warn - standard placement new
+void testGlobalNewExprArray() {
+ int *p = new int[0];
+} // expected-warning{{Memory is never released; potential leak}}
- int *p2 = new(1.0) int; // no warn - overloaded placement new
+//----- Standard nothrow placement operators
+void testGlobalNoThrowPlacementOpNewBeforeOverload() {
+ void *p = operator new(0, std::nothrow);
+} // expected-warning{{Memory is never released; potential leak}}
- int *p3 = new (std::nothrow) int;
+void testGlobalNoThrowPlacementExprNewBeforeOverload() {
+ int *p = new(std::nothrow) int;
} // expected-warning{{Memory is never released; potential leak}}
-void testExprPlacementNewArray() {
+
+//----- Standard pointer placement operators
+void testGlobalPointerPlacementNew() {
int i;
- int *p1 = new(&i) int[1]; // no warn - standard placement new[]
- int *p2 = new(1.0) int[1]; // no warn - overloaded placement new[]
+ void *p1 = operator new(0, &i); // no warn
- int *p3 = new (std::nothrow) int[1];
-} // expected-warning{{Memory is never released; potential leak}}
+ void *p2 = operator new[](0, &i); // no warn
-void testCustomOpNew() {
- void *p = operator new(0); // no warn - call to a custom new
-}
+ int *p3 = new(&i) int; // no warn
-void testGlobalExprNew() {
- void *p = ::new int; // no warn - call to a custom new
+ int *p4 = new(&i) int[0]; // no warn
}
-void testCustomExprNew() {
- int *p = new int; // no warn - call to a custom new
+//----- Other cases
+void testNewMemoryIsInHeap() {
+ int *p = new int;
+ if (global != p) // condition is always true as 'p' wraps a heap region that
+ // is different from a region wrapped by 'global'
+ global = p; // pointer escapes
}
-void testGlobalExprNewArray() {
- void *p = ::new int[1]; // no warn - call to a custom new
-}
+struct PtrWrapper {
+ int *x;
+
+ PtrWrapper(int *input) : x(input) {}
+};
-void testOverloadedExprNewArray() {
- int *p = new int[1]; // no warn - call to a custom new
+void testNewInvalidationPlacement(PtrWrapper *w) {
+ // Ensure that we don't consider this a leak.
+ new (w) PtrWrapper(new int); // no warn
}
//---------------
// malloc()/free() are subjects of unix.Malloc and unix.MallocWithAnnotations
void testMallocFreeNoWarn() {
int i;
- free(&i); // no-warning
+ free(&i); // no warn
int *p1 = (int *)malloc(sizeof(int));
- free(++p1); // no-warning
+ free(++p1); // no warn
int *p2 = (int *)malloc(sizeof(int));
free(p2);
- free(p2); // no-warning
+ free(p2); // no warn
- int *p3 = (int *)malloc(sizeof(int)); // no-warning
+ int *p3 = (int *)malloc(sizeof(int)); // no warn
}
-void testFreeNewed() {
+//----- Test free standard new
+void testFreeOpNew() {
+ void *p = operator new(0);
+ free(p);
+} // expected-warning{{Memory is never released; potential leak}}
+// FIXME: Pointer should escape
+
+void testFreeNewExpr() {
int *p = new int;
- free(p); // pointer escaped, no-warning
-}
+ free(p);
+} // expected-warning{{Memory is never released; potential leak}}
+// FIXME: Pointer should escape
void testObjcFreeNewed() {
int *p = new int;
--- /dev/null
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete -analyzer-store region -std=c++11 -fblocks -verify %s
+#include "Inputs/system-header-simulator-cxx.h"
+
+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, double d);
+
+class C {
+public:
+ void *operator new(std::size_t);
+};
+
+void testNewMethod() {
+ void *p1 = C::operator new(0); // no warn
+
+ C *p2 = new C; // no warn
+
+ C *c3 = ::new C;
+} // expected-warning{{Memory is never released; potential leak}}
+
+void testOpNewArray() {
+ void *p = operator new[](0);
+} //FIXME: expected 'Memory is never released; potential leak'
+
+void testNewExprArray() {
+ int *p = new int[0];
+} // expected-warning{{Memory is never released; potential leak}}
+
+//----- Custom non-placement operators
+void testOpNew() {
+ void *p = operator new(0);
+} //FIXME: expected 'Memory is never released; potential leak'
+
+void testNewExpr() {
+ int *p = new int;
+} // expected-warning{{Memory is never released; potential leak}}
+
+//----- Custom NoThrow placement operators
+void testOpNewNoThrow() {
+ void *p = operator new(0, std::nothrow);
+} // expected-warning{{Memory is never released; potential leak}}
+
+void testNewExprNoThrow() {
+ int *p = new(std::nothrow) int;
+} // expected-warning{{Memory is never released; potential leak}}
+
+//----- Custom placement operators
+void testOpNewPlacement() {
+ void *p = operator new(0, 0.1); // no warn
+}
+
+void testNewExprPlacement() {
+ int *p = new(0.1) int; // no warn
+}
--- /dev/null
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.NewDelete -analyzer-store region -std=c++11 -fblocks -verify %s
+// expected-no-diagnostics
+
+namespace std {
+ typedef __typeof__(sizeof(int)) size_t;
+}
+
+void *operator new(std::size_t, ...);
+void *operator new[](std::size_t, ...);
+
+void testGlobalCustomVariadicNew() {
+ void *p1 = operator new(0); // no warn
+
+ void *p2 = operator new[](0); // no warn
+
+ int *p3 = new int; // no warn
+
+ int *p4 = new int[0]; // no warn
+}