Index: lib/Analysis/LiveVariables.cpp =================================================================== --- lib/Analysis/LiveVariables.cpp +++ lib/Analysis/LiveVariables.cpp @@ -409,6 +409,13 @@ val.liveDecls = DSetFact.add(val.liveDecls, Dtor->getVarDecl()); continue; } + if (Optional Dtor = + elem.getAs()) { + // Temporary objects need to survive until the destructor is called. + val.liveStmts = SSetFact.add(val.liveStmts, + Dtor->getBindTemporaryExpr()->getSubExpr()); + continue; + } if (!elem.getAs()) continue; Index: lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- lib/StaticAnalyzer/Core/CallEvent.cpp +++ lib/StaticAnalyzer/Core/CallEvent.cpp @@ -959,7 +959,6 @@ CFGElement E = (*B)[CalleeCtx->getIndex()]; assert(E.getAs() && "All other CFG elements should have exprs"); - assert(!E.getAs() && "We don't handle temporaries yet"); SValBuilder &SVB = State->getStateManager().getSValBuilder(); const CXXDestructorDecl *Dtor = cast(CalleeCtx->getDecl()); Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -676,13 +676,23 @@ State = State->remove( std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame())); StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State); - - QualType varType = D.getBindTemporaryExpr()->getSubExpr()->getType(); assert(CleanDtorState.size() == 1); ExplodedNode *CleanPred = *CleanDtorState.begin(); - // FIXME: Inlining of temporary destructors is not supported yet anyway, so - // we just put a NULL region for now. This will need to be changed later. - VisitCXXDestructor(varType, nullptr, D.getBindTemporaryExpr(), + + QualType varType = D.getBindTemporaryExpr()->getSubExpr()->getType(); + + const LocationContext *LCtx = CleanPred->getLocationContext(); + SVal Val = CleanPred->getState()->getSVal( + D.getBindTemporaryExpr()->getSubExpr(), LCtx->getCurrentStackFrame()); + const MemRegion *Region = nullptr; + // If the class does not have any members, there will not be a region + // for it bound in the environment. + if (Optional LCV = + Val.getAs()) { + Region = LCV->getRegion(); + } + + VisitCXXDestructor(varType, Region, D.getBindTemporaryExpr(), /*IsBase=*/false, CleanPred, Dst); } Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -637,12 +637,6 @@ if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) return CIP_DisallowedAlways; - // FIXME: This is a hack. We don't handle temporary destructors - // right now, so we shouldn't inline their constructors. - if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) - if (!Target || !isa(Target)) - return CIP_DisallowedOnce; - break; } case CE_CXXDestructor: { @@ -807,14 +801,6 @@ AnalysisDeclContextManager &ADCMgr = AMgr.getAnalysisDeclContextManager(); AnalysisDeclContext *CalleeADC = ADCMgr.getContext(D); - // Temporary object destructor processing is currently broken, so we never - // inline them. - // FIXME: Remove this once temp destructors are working. - if (isa(Call)) { - if ((*currBldrCtx->getBlock())[currStmtIdx].getAs()) - return false; - } - // The auto-synthesized bodies are essential to inline as they are // usually small and commonly used. Note: we should do this check early on to // ensure we always inline these calls. Index: lib/StaticAnalyzer/Core/ProgramState.cpp =================================================================== --- lib/StaticAnalyzer/Core/ProgramState.cpp +++ lib/StaticAnalyzer/Core/ProgramState.cpp @@ -506,16 +506,7 @@ } bool ScanReachableSymbols::scan(nonloc::LazyCompoundVal val) { - bool wasVisited = !visited.insert(val.getCVData()).second; - if (wasVisited) - return true; - - StoreManager &StoreMgr = state->getStateManager().getStoreManager(); - // FIXME: We don't really want to use getBaseRegion() here because pointer - // arithmetic doesn't apply, but scanReachableSymbols only accepts base - // regions right now. - const MemRegion *R = val.getRegion()->getBaseRegion(); - return StoreMgr.scanReachableSymbols(val.getStore(), R, *this); + return scan(val.getRegion()); } bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { Index: lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- lib/StaticAnalyzer/Core/RegionStore.cpp +++ lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1899,8 +1899,9 @@ QualType Ty = TR->getValueType(); if (Ty->isArrayType()) return bindArray(B, TR, V); - if (Ty->isStructureOrClassType()) + if (Ty->isStructureOrClassType()) { return bindStruct(B, TR, V); + } if (Ty->isVectorType()) return bindVector(B, TR, V); if (Ty->isUnionType()) @@ -2110,8 +2111,9 @@ // Handle lazy compound values and symbolic values. if (Optional LCV = V.getAs()) { - if (Optional NewB = tryBindSmallStruct(B, R, RD, *LCV)) + if (Optional NewB = tryBindSmallStruct(B, R, RD, *LCV)) { return *NewB; + } return bindAggregate(B, R, V); } if (V.getAs()) Index: test/Analysis/temporaries.cpp =================================================================== --- test/Analysis/temporaries.cpp +++ test/Analysis/temporaries.cpp @@ -3,7 +3,7 @@ // RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true %s -std=c++11 extern bool clang_analyzer_eval(bool); -extern bool clang_analyzer_warnIfReached(); +extern void clang_analyzer_warnIfReached(); struct Trivial { Trivial(int x) : value(x) {} @@ -104,9 +104,7 @@ #if __cplusplus >= 201103L clang_analyzer_eval(((HasCtor){1, 42}).y == 42); // expected-warning{{TRUE}} - // FIXME: should be TRUE, but we don't inline the constructors of - // temporaries because we can't model their destructors yet. - clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{TRUE}} #endif } } @@ -339,6 +337,35 @@ } } + struct WriteInDestructor { + WriteInDestructor(int *p) : p(p) {} + ~WriteInDestructor() { *p = 23; } // no warning + int *p; + }; + void testDtorInlining() { + int x; + (WriteInDestructor(&x)); + clang_analyzer_eval(x == 23); // expected-warning{{TRUE}} + } + + struct FlagDestructor { + FlagDestructor() : flag(false) {} + ~FlagDestructor() { + // FIXME: This currently doesn't trigger because when use(FlagDestructor) + // is inlined, the memory region of the by-value parameter is copied, but + // neither a constructor nor destructor is called. + if (flag) clang_analyzer_warnIfReached(); // no warning + } + bool flag; + }; + void use(FlagDestructor d) { + d.flag = true; + } + void testByValueArgs() { + FlagDestructor d; + use(d); + } + void testIfAtEndOfLoop() { int y = 0; while (true) {