summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
diff options
context:
space:
mode:
authorArtem Dergachev <artem.dergachev@gmail.com>2018-02-27 20:03:35 +0000
committerArtem Dergachev <artem.dergachev@gmail.com>2018-02-27 20:03:35 +0000
commit4068481bdb115194fe02c88f6b6dc12ebb4c9ddd (patch)
treefb634591e544128733b75f68d4f72c2f88ce1987 /clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
parent301991080e24ac402793ca6684daa2fc70647f8a (diff)
downloadbcm5719-llvm-4068481bdb115194fe02c88f6b6dc12ebb4c9ddd.tar.gz
bcm5719-llvm-4068481bdb115194fe02c88f6b6dc12ebb4c9ddd.zip
[CFG] NFC: Refactor ConstructionContext into a finite set of cases.
ConstructionContext is moved into a separate translation unit and is separated into multiple classes. The "old" "raw" ConstructionContext is renamed into ConstructionContextLayer - which corresponds to the idea of building the context gradually layer-by-layer, but it isn't easy to use in the clients. Once CXXConstructExpr is reached, layers that we've gathered so far are transformed into the actual, "new-style" "flat" ConstructionContext, which is put into the CFGConstructor element and has no layers whatsoever (until it actually needs them, eg. aggregate initialization). The new-style ConstructionContext is instead presented as a variety of sub-classes that enumerate different ways of constructing an object in C++. There are 5 of these supported for now, which is around a half of what needs to be supported. The layer-by-layer buildup process is still a little bit weird, but it hides all the weirdness in one place, that sounds like a good thing. Differential Revision: https://reviews.llvm.org/D43533 llvm-svn: 326238
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp')
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp130
1 files changed, 76 insertions, 54 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 2c1e858da25..14b4569b9a5 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/Analysis/ConstructionContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/ParentMap.h"
@@ -111,47 +112,20 @@ ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
// See if we're constructing an existing region by looking at the
// current construction context.
if (CC) {
- if (const Stmt *TriggerStmt = CC->getTriggerStmt()) {
- if (const CXXNewExpr *CNE = dyn_cast<CXXNewExpr>(TriggerStmt)) {
- if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
- // TODO: Detect when the allocator returns a null pointer.
- // Constructor shall not be called in this case.
- if (const SubRegion *MR = dyn_cast_or_null<SubRegion>(
- getCXXNewAllocatorValue(State, CNE, LCtx).getAsRegion())) {
- if (CNE->isArray()) {
- // TODO: In fact, we need to call the constructor for every
- // allocated element, not just the first one!
- CallOpts.IsArrayCtorOrDtor = true;
- return getStoreManager().GetElementZeroRegion(
- MR, CNE->getType()->getPointeeType());
- }
- return MR;
- }
- }
- } else if (auto *DS = dyn_cast<DeclStmt>(TriggerStmt)) {
- const auto *Var = cast<VarDecl>(DS->getSingleDecl());
- SVal LValue = State->getLValue(Var, LCtx);
- QualType Ty = Var->getType();
- LValue = makeZeroElementRegion(State, LValue, Ty,
- CallOpts.IsArrayCtorOrDtor);
- return LValue.getAsRegion();
- } else if (isa<ReturnStmt>(TriggerStmt)) {
- // TODO: We should construct into a CXXBindTemporaryExpr or a
- // MaterializeTemporaryExpr around the call-expression on the previous
- // stack frame. Currently we re-bind the temporary to the correct region
- // later, but that's not semantically correct. This of course does not
- // apply when we're in the top frame. But if we are in an inlined
- // function, we should be able to take the call-site CFG element,
- // and it should contain (but right now it wouldn't) some sort of
- // construction context that'd give us the right temporary expression.
- CallOpts.IsTemporaryCtorOrDtor = true;
- return MRMgr.getCXXTempObjectRegion(CE, LCtx);
- } else if (isa<CXXBindTemporaryExpr>(TriggerStmt)) {
- CallOpts.IsTemporaryCtorOrDtor = true;
- return MRMgr.getCXXTempObjectRegion(CE, LCtx);
- }
- // TODO: Consider other directly initialized elements.
- } else if (const CXXCtorInitializer *Init = CC->getTriggerInit()) {
+ switch (CC->getKind()) {
+ case ConstructionContext::SimpleVariableKind: {
+ const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
+ const auto *DS = DSCC->getDeclStmt();
+ const auto *Var = cast<VarDecl>(DS->getSingleDecl());
+ SVal LValue = State->getLValue(Var, LCtx);
+ QualType Ty = Var->getType();
+ LValue =
+ makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor);
+ return LValue.getAsRegion();
+ }
+ case ConstructionContext::ConstructorInitializerKind: {
+ const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
+ const auto *Init = ICC->getCXXCtorInitializer();
assert(Init->isAnyMemberInitializer());
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
Loc ThisPtr =
@@ -173,10 +147,45 @@ ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
CallOpts.IsArrayCtorOrDtor);
return FieldVal.getAsRegion();
}
-
- // FIXME: This will eventually need to handle new-expressions as well.
- // Don't forget to update the pre-constructor initialization code in
- // ExprEngine::VisitCXXConstructExpr.
+ case ConstructionContext::NewAllocatedObjectKind: {
+ if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
+ const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
+ const auto *NE = NECC->getCXXNewExpr();
+ // TODO: Detect when the allocator returns a null pointer.
+ // Constructor shall not be called in this case.
+ if (const SubRegion *MR = dyn_cast_or_null<SubRegion>(
+ getCXXNewAllocatorValue(State, NE, LCtx).getAsRegion())) {
+ if (NE->isArray()) {
+ // TODO: In fact, we need to call the constructor for every
+ // allocated element, not just the first one!
+ CallOpts.IsArrayCtorOrDtor = true;
+ return getStoreManager().GetElementZeroRegion(
+ MR, NE->getType()->getPointeeType());
+ }
+ return MR;
+ }
+ }
+ break;
+ }
+ case ConstructionContext::TemporaryObjectKind: {
+ // TODO: Support temporaries lifetime-extended via static references.
+ // They'd need a getCXXStaticTempObjectRegion().
+ CallOpts.IsTemporaryCtorOrDtor = true;
+ return MRMgr.getCXXTempObjectRegion(CE, LCtx);
+ }
+ case ConstructionContext::ReturnedValueKind: {
+ // TODO: We should construct into a CXXBindTemporaryExpr or a
+ // MaterializeTemporaryExpr around the call-expression on the previous
+ // stack frame. Currently we re-bind the temporary to the correct region
+ // later, but that's not semantically correct. This of course does not
+ // apply when we're in the top frame. But if we are in an inlined
+ // function, we should be able to take the call-site CFG element,
+ // and it should contain (but right now it wouldn't) some sort of
+ // construction context that'd give us the right temporary expression.
+ CallOpts.IsTemporaryCtorOrDtor = true;
+ return MRMgr.getCXXTempObjectRegion(CE, LCtx);
+ }
+ }
}
// If we couldn't find an existing region to construct into, assume we're
// constructing a temporary. Notify the caller of our failure.
@@ -227,6 +236,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
EvalCallOptions CallOpts;
auto C = getCurrentCFGElement().getAs<CFGConstructor>();
+ assert(C || getCurrentCFGElement().getAs<CFGStmt>());
const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr;
const CXXBindTemporaryExpr *BTE = nullptr;
@@ -235,15 +245,27 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
switch (CE->getConstructionKind()) {
case CXXConstructExpr::CK_Complete: {
Target = getRegionForConstructedObject(CE, Pred, CC, CallOpts);
- if (CC && AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG() &&
- !CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion &&
- CallOpts.IsTemporaryCtorOrDtor) {
- MTE = CC->getMaterializedTemporary();
- if (!MTE || MTE->getStorageDuration() == SD_FullExpression) {
- // If the temporary is lifetime-extended, don't save the BTE,
- // because we don't need a temporary destructor, but an automatic
- // destructor. The cast may fail because it may as well be a ReturnStmt.
- BTE = dyn_cast<CXXBindTemporaryExpr>(CC->getTriggerStmt());
+
+ // In case of temporary object construction, extract data necessary for
+ // destruction and lifetime extension.
+ if (const auto *TCC =
+ dyn_cast_or_null<TemporaryObjectConstructionContext>(CC)) {
+ assert(CallOpts.IsTemporaryCtorOrDtor);
+ assert(!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion);
+ if (AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG()) {
+ BTE = TCC->getCXXBindTemporaryExpr();
+ MTE = TCC->getMaterializedTemporaryExpr();
+ if (!BTE) {
+ // FIXME: lifetime extension for temporaries without destructors
+ // is not implemented yet.
+ MTE = nullptr;
+ }
+ if (MTE && MTE->getStorageDuration() != SD_FullExpression) {
+ // If the temporary is lifetime-extended, don't save the BTE,
+ // because we don't need a temporary destructor, but an automatic
+ // destructor.
+ BTE = nullptr;
+ }
}
}
break;
OpenPOWER on IntegriCloud