Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -659,6 +659,8 @@ /// Store the region returned by operator new() so that the constructor /// that follows it knew what location to initialize. The value should be /// cleared once the respective CXXNewExpr CFGStmt element is processed. + /// \p T is the type of the newly allocated object, which is not necessarily + /// obvious from \p V because the return type of operator new is void *. ProgramStateRef pushCXXNewAllocatorValue(ProgramStateRef State, const CXXNewExpr *CNE, const LocationContext *CallerLC, Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -119,9 +119,17 @@ if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { // TODO: Detect when the allocator returns a null pointer. // Constructor shall not be called in this case. - if (const MemRegion *MR = - peekCXXNewAllocatorValue(State, CNE, LCtx).getAsRegion()) + if (const SubRegion *MR = dyn_cast_or_null( + peekCXXNewAllocatorValue(State, CNE, LCtx).getAsRegion())) { + if (CNE->isArray()) { + // TODO: This code exists only to trigger the suppression for + // array constructors. In fact, we need to call the constructor + // for every allocated element, not just the first one! + return getStoreManager().GetElementZeroRegion( + MR, CNE->getType()->getPointeeType()); + } return MR; + } } } else if (auto *DS = dyn_cast(StmtElem->getStmt())) { if (const auto *Var = dyn_cast(DS->getSingleDecl())) { @@ -564,16 +572,20 @@ StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + SVal Result = symVal; + if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - const SubRegion *NewReg = - symVal.castAs().getRegionAs(); - QualType ObjTy = CNE->getType()->getAs()->getPointeeType(); - const ElementRegion *EleReg = - getStoreManager().GetElementZeroRegion(NewReg, ObjTy); - State = State->BindExpr(CNE, Pred->getLocationContext(), - loc::MemRegionVal(EleReg)); + if (!AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { + const SubRegion *NewReg = + symVal.castAs().getRegionAs(); + QualType ObjTy = CNE->getType()->getAs()->getPointeeType(); + const ElementRegion *EleReg = + getStoreManager().GetElementZeroRegion(NewReg, ObjTy); + Result = loc::MemRegionVal(EleReg); + } + State = State->BindExpr(CNE, Pred->getLocationContext(), Result); Bldr.generateNode(CNE, Pred, State); return; } @@ -582,7 +594,6 @@ // CXXNewExpr, we need to make sure that the constructed object is not // immediately invalidated here. (The placement call should happen before // the constructor call anyway.) - SVal Result = symVal; if (FD && FD->isReservedGlobalPlacementOperator()) { // Non-array placement new should always return the placement location. SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx); Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -277,12 +277,18 @@ state = state->BindExpr(CCE, callerCtx, ThisV); } - if (const CXXNewExpr *CNE = dyn_cast(CE)) { + if (const auto *CNE = dyn_cast(CE)) { // We are currently evaluating a CXXNewAllocator CFGElement. It takes a // while to reach the actual CXXNewExpr element from here, so keep the // region for later use. - state = pushCXXNewAllocatorValue(state, CNE, calleeCtx->getParent(), - state->getSVal(CE, callerCtx)); + // Additionally cast the return value of the inlined operator new + // (which is of type 'void *') to the correct object type. + SVal AllocV = state->getSVal(CNE, callerCtx); + AllocV = svalBuilder.evalCast( + AllocV, CNE->getType(), + getContext().getPointerType(getContext().VoidTy)); + + state = pushCXXNewAllocatorValue(state, CNE, calleeCtx->getParent(), AllocV); } } Index: test/Analysis/new-ctor-conservative.cpp =================================================================== --- test/Analysis/new-ctor-conservative.cpp +++ test/Analysis/new-ctor-conservative.cpp @@ -12,3 +12,18 @@ S *s = new S; clang_analyzer_eval(s->x == 1); // expected-warning{{TRUE}} } + +void checkNewPOD() { + int *i = new int; + clang_analyzer_eval(*i == 0); // expected-warning{{UNKNOWN}} + int *j = new int(); + clang_analyzer_eval(*j == 0); // expected-warning{{TRUE}} + int *k = new int(5); + clang_analyzer_eval(*k == 5); // expected-warning{{TRUE}} +} + +void checkNewArray() { + S *s = new S[10]; + // FIXME: Should be true once we inline array constructors. + clang_analyzer_eval(s[0].x == 1); // expected-warning{{UNKNOWN}} +} Index: test/Analysis/new-ctor-inlined.cpp =================================================================== --- test/Analysis/new-ctor-inlined.cpp +++ test/Analysis/new-ctor-inlined.cpp @@ -38,3 +38,18 @@ Sp *p = new Sp(new Sp(0)); clang_analyzer_eval(p->p->p == 0); // expected-warning{{TRUE}} } + +void checkNewPOD() { + int *i = new int; + clang_analyzer_eval(*i == 0); // expected-warning{{UNKNOWN}} + int *j = new int(); + clang_analyzer_eval(*j == 0); // expected-warning{{TRUE}} + int *k = new int(5); + clang_analyzer_eval(*k == 5); // expected-warning{{TRUE}} +} + +void checkTrivialCopy() { + S s; + S *t = new S(s); // no-crash + clang_analyzer_eval(t->x == 1); // expected-warning{{TRUE}} +}