diff options
author | Artem Dergachev <artem.dergachev@gmail.com> | 2018-02-27 20:03:35 +0000 |
---|---|---|
committer | Artem Dergachev <artem.dergachev@gmail.com> | 2018-02-27 20:03:35 +0000 |
commit | 4068481bdb115194fe02c88f6b6dc12ebb4c9ddd (patch) | |
tree | fb634591e544128733b75f68d4f72c2f88ce1987 /clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | |
parent | 301991080e24ac402793ca6684daa2fc70647f8a (diff) | |
download | bcm5719-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.cpp | 130 |
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; |