summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
Diffstat (limited to 'clang')
-rw-r--r--clang/lib/Analysis/CFG.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngine.cpp22
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp6
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp8
-rw-r--r--clang/test/Analysis/temp-obj-dtors-cfg-output.cpp51
-rw-r--r--clang/test/Analysis/temporaries.cpp70
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() {
OpenPOWER on IntegriCloud