diff options
Diffstat (limited to 'clang')
| -rw-r--r-- | clang/lib/Analysis/CFG.cpp | 5 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 22 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 6 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 8 | ||||
| -rw-r--r-- | clang/test/Analysis/temp-obj-dtors-cfg-output.cpp | 51 | ||||
| -rw-r--r-- | clang/test/Analysis/temporaries.cpp | 70 |
6 files changed, 147 insertions, 15 deletions
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 52706b01263..ad0acb7abe5 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -3765,8 +3765,9 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper* Helper, } else if (Optional<CFGTemporaryDtor> TE = E.getAs<CFGTemporaryDtor>()) { const CXXBindTemporaryExpr *BT = TE->getBindTemporaryExpr(); - OS << "~" << BT->getType()->getAsCXXRecordDecl()->getName() << "()"; - OS << " (Temporary object destructor)\n"; + OS << "~"; + BT->getType().print(OS, PrintingPolicy(Helper->getLangOpts())); + OS << "() (Temporary object destructor)\n"; } } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 6aa9174ad6e..79139df786e 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -601,7 +601,15 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, ExplodedNode *Pred, - ExplodedNodeSet &Dst) {} + ExplodedNodeSet &Dst) { + + QualType varType = D.getBindTemporaryExpr()->getSubExpr()->getType(); + + // 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, NULL, D.getBindTemporaryExpr(), + /*IsBase=*/ false, Pred, Dst); +} void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &DstTop) { @@ -1332,7 +1340,8 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF) { - PrettyStackTraceLocationContext StackCrashInfo(Pred->getLocationContext()); + const LocationContext *LCtx = Pred->getLocationContext(); + PrettyStackTraceLocationContext StackCrashInfo(LCtx); currBldrCtx = &BldCtx; // Check for NULL conditions; e.g. "for(;;)" @@ -1347,10 +1356,14 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, SVal TrueVal = SVB.makeTruthVal(true); SVal FalseVal = SVB.makeTruthVal(false); - // Resolve the condition in the precense of nested '||' and '&&'. if (const Expr *Ex = dyn_cast<Expr>(Condition)) Condition = Ex->IgnoreParens(); - Condition = ResolveCondition(Condition, BldCtx.getBlock()); + + // If the value is already available, we don't need to do anything. + if (Pred->getState()->getSVal(Condition, LCtx).isUnknownOrUndef()) { + // Resolve the condition in the presence of nested '||' and '&&'. + Condition = ResolveCondition(Condition, BldCtx.getBlock()); + } // Cast truth values to the correct type. if (const Expr *Ex = dyn_cast<Expr>(Condition)) { @@ -1360,7 +1373,6 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, getContext().getLogicalOperationType()); } - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), Condition->getLocStart(), "Error evaluating branch"); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 27963ade934..b128bee1429 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -91,6 +91,12 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, /// If the type is not an array type at all, the original value is returned. static SVal makeZeroElementRegion(ProgramStateRef State, SVal LValue, QualType &Ty) { + // FIXME: This check is just a temporary workaround, because + // ProcessTemporaryDtor sends us NULL regions. It will not be necessary once + // we can properly process temporary destructors. + if (!LValue.getAsRegion()) + return LValue; + SValBuilder &SVB = State->getStateManager().getSValBuilder(); ASTContext &Ctx = SVB.getContext(); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 4fb6523944f..08d239edab7 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -807,6 +807,14 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, 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<CXXDestructorCall>(Call)) { + if ((*currBldrCtx->getBlock())[currStmtIdx].getAs<CFGTemporaryDtor>()) + 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. diff --git a/clang/test/Analysis/temp-obj-dtors-cfg-output.cpp b/clang/test/Analysis/temp-obj-dtors-cfg-output.cpp index 1ddccb704b1..ff68a876e91 100644 --- a/clang/test/Analysis/temp-obj-dtors-cfg-output.cpp +++ b/clang/test/Analysis/temp-obj-dtors-cfg-output.cpp @@ -108,6 +108,24 @@ TestCtorInits::TestCtorInits() : a(int(A()) + int(B())) , b() {} +class NoReturn { +public: + ~NoReturn() __attribute__((noreturn)); + void f(); +}; + +void test_noreturn1() { + int a; + NoReturn().f(); + int b; +} + +void test_noreturn2() { + int a; + NoReturn(), 47; + int b; +} + // CHECK: [B1 (ENTRY)] // CHECK: Succs (1): B0 // CHECK: [B0 (EXIT)] @@ -846,3 +864,36 @@ TestCtorInits::TestCtorInits() // CHECK: [B0 (EXIT)] // CHECK: Preds (1): B1 +// CHECK: [B3 (ENTRY)] +// CHECK: Succs (1): B2 +// CHECK: [B1] +// CHECK: 1: int b; +// CHECK: Succs (1): B0 +// CHECK: [B2] +// CHECK: 1: int a; +// CHECK: 2: NoReturn() (CXXConstructExpr, class NoReturn) +// CHECK: 3: [B2.2] (BindTemporary) +// CHECK: 4: [B2.3].f +// CHECK: 5: [B2.4]() +// CHECK: 6: ~NoReturn() (Temporary object destructor) +// CHECK: Preds (1): B3 +// CHECK: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (2): B1 B2 + +// CHECK: [B3 (ENTRY)] +// CHECK: Succs (1): B2 +// CHECK: [B1] +// CHECK: 1: int b; +// CHECK: Succs (1): B0 +// CHECK: [B2] +// CHECK: 1: int a; +// CHECK: 2: NoReturn() (CXXConstructExpr, class NoReturn) +// CHECK: 3: [B2.2] (BindTemporary) +// CHECK: 4: 47 +// CHECK: 5: ... , [B2.4] +// CHECK: 6: ~NoReturn() (Temporary object destructor) +// CHECK: Preds (1): B3 +// CHECK: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (2): B1 B2 diff --git a/clang/test/Analysis/temporaries.cpp b/clang/test/Analysis/temporaries.cpp index 5e1771cc441..d5237e597cf 100644 --- a/clang/test/Analysis/temporaries.cpp +++ b/clang/test/Analysis/temporaries.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++03 %s // RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++11 %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -analyzer-config cfg-temporary-dtors=true %s -DTEMPORARY_DTORS extern bool clang_analyzer_eval(bool); @@ -123,23 +124,76 @@ namespace destructors { } } - void testConsistency(int i) { - struct NoReturnDtor { - ~NoReturnDtor() __attribute__((noreturn)); - }; - extern bool check(const NoReturnDtor &); - +#ifdef TEMPORARY_DTORS + struct NoReturnDtor { + ~NoReturnDtor() __attribute__((noreturn)); + }; + + void noReturnTemp(int *x) { + if (! x) NoReturnDtor(); + *x = 47; // no warning + } + + void noReturnInline(int **x) { + NoReturnDtor(); + } + + void callNoReturn() { + int *x; + noReturnInline(&x); + *x = 47; // no warning + } + + extern bool check(const NoReturnDtor &); + + void testConsistencyIf(int i) { if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor()))) clang_analyzer_eval(true); // expected-warning{{TRUE}} if (i != 5) return; if (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) { - // FIXME: Should be no-warning, because the noreturn destructor should - // fire on all paths. + clang_analyzer_eval(true); // no warning, unreachable code + } + } + + void testConsistencyTernary(int i) { + (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0; + + clang_analyzer_eval(true); // expected-warning{{TRUE}} + + if (i != 5) + return; + + (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0; + + clang_analyzer_eval(true); // no warning, unreachable code + } + + void testConsistencyNested(int i) { + extern bool compute(bool); + + if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor()))) + clang_analyzer_eval(true); // expected-warning{{TRUE}} + + if (i != 5) + return; + + if (compute(i == 5 && + (i == 4 || compute(true) || + compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) || + i != 4) { clang_analyzer_eval(true); // expected-warning{{TRUE}} } + + if (compute(i == 5 && + (i == 4 || i == 4 || + compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) || + i != 4) { + clang_analyzer_eval(true); // no warning, unreachable code + } } +#endif // TEMPORARY_DTORS } void testStaticMaterializeTemporaryExpr() { |

