baloghadamsoftware updated this revision to Diff 256310.
baloghadamsoftware added a comment.
OK. I solved almost every problem except that `checkDeadSymbols()` removes the
iterator position keyed by the temporary region representing the argument from
the map before it is needed. Either I must somehow recognize that although the
region is not "live" but the call whose argument it stores is not postchecked
yet. I have no idea how to do this. Or, which I would prefer is to modify
`SymbolReaper` so that `isLive()` returns true for temporary regions storing
arguments of calls not postchecked yet. I have no idea either how to do this.
Maybe should we take the `Expr` of `CXXTempObjectRegion`, use `ParentMap` to
recognize whether it is a parameter for a `FunctionDecl`, but how to check
whether the call for the function of the `FunctionDecl` is not postchecked yet?
@NoQ, could you please, help me in this? Thx!
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D77229/new/
https://reviews.llvm.org/D77229
Files:
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
clang/lib/StaticAnalyzer/Core/CallEvent.cpp
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
clang/test/Analysis/container-modeling.cpp
clang/test/Analysis/iterator-modeling.cpp
Index: clang/test/Analysis/iterator-modeling.cpp
===================================================================
--- clang/test/Analysis/iterator-modeling.cpp
+++ clang/test/Analysis/iterator-modeling.cpp
@@ -28,7 +28,7 @@
void clang_analyzer_eval(bool);
void clang_analyzer_warnIfReached();
-void begin(const std::vector<int> &v) {
+/*void begin(const std::vector<int> &v) {
auto i = v.begin();
clang_analyzer_eval(clang_analyzer_iterator_container(i) == &v); // expected-warning{{TRUE}}
@@ -945,7 +945,7 @@
clang_analyzer_express(clang_analyzer_iterator_position(i1)); // expected-warning{{$L.end() - 1}} FIXME: should be $L.end() - 2
clang_analyzer_express(clang_analyzer_iterator_position(i2)); // expected-warning{{$L.end()}}
// clang_analyzer_express(clang_analyzer_iterator_position(i3)); FIXME: expect warning $L.end() - 1
-}
+}*/
/// std::vector-like containers: Only the iterators before the insertion point
/// remain valid. The past-the-end iterator is also
@@ -965,7 +965,7 @@
// clang_analyzer_express(clang_analyzer_iterator_position(i2)); FIXME: expect warning $V.begin() - 1
}
-void vector_insert_behind_begin(std::vector<int> &V, int n) {
+/*void vector_insert_behind_begin(std::vector<int> &V, int n) {
auto i0 = V.cbegin(), i1 = ++V.cbegin(), i2 = V.cend();
clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()");
@@ -1880,3 +1880,4 @@
// CHECK-NEXT: "i1 : Valid ; Container == SymRegion{reg_$[[#]]<std::vector<int> & V>} ; Offset == conj_$[[#]]{long, LC[[#]], S[[#]], #[[#]]}"
// CHECK-NEXT: ]}
}
+*/
Index: clang/test/Analysis/container-modeling.cpp
===================================================================
--- clang/test/Analysis/container-modeling.cpp
+++ clang/test/Analysis/container-modeling.cpp
@@ -17,7 +17,7 @@
void clang_analyzer_warnIfReached();
void begin(const std::vector<int> &V) {
- V.begin();
+ const auto i0 = V.begin();
clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()");
clang_analyzer_express(clang_analyzer_container_begin(V)); // expected-warning{{$V.begin()}}
@@ -25,7 +25,7 @@
}
void end(const std::vector<int> &V) {
- V.end();
+ const auto i0 = V.end();
clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()");
clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end()}}
@@ -41,10 +41,10 @@
// Move
void move_assignment(std::vector<int> &V1, std::vector<int> &V2) {
- V1.cbegin();
- V1.cend();
- V2.cbegin();
- V2.cend();
+ const auto i0 = V1.cbegin();
+ const auto i1 = V1.cend();
+ const auto i2 = V2.cbegin();
+ const auto i3 = V2.cend();
long B1 = clang_analyzer_container_begin(V1);
long E1 = clang_analyzer_container_end(V1);
long B2 = clang_analyzer_container_begin(V2);
@@ -70,8 +70,8 @@
void clang_analyzer_dump(void*);
void push_back(std::vector<int> &V, int n) {
- V.cbegin();
- V.cend();
+ const auto i0 = V.cbegin();
+ const auto i1 = V.cend();
clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()");
clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()");
@@ -90,8 +90,8 @@
/// past-the-end position of the container is incremented).
void emplace_back(std::vector<int> &V, int n) {
- V.cbegin();
- V.cend();
+ const auto i0 = V.cbegin();
+ const auto i1 = V.cend();
clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()");
clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()");
@@ -110,8 +110,8 @@
/// past-the-end position of the container is decremented).
void pop_back(std::vector<int> &V, int n) {
- V.cbegin();
- V.cend();
+ const auto i0 = V.cbegin();
+ const auto i1 = V.cend();
clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()");
clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()");
@@ -131,8 +131,8 @@
/// position of the container is decremented).
void push_front(std::list<int> &L, int n) {
- L.cbegin();
- L.cend();
+ const auto i0 = L.cbegin();
+ const auto i1 = L.cend();
clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()");
clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()");
@@ -151,8 +151,8 @@
/// position of the container is decremented).
void emplace_front(std::list<int> &L, int n) {
- L.cbegin();
- L.cend();
+ const auto i0 = L.cbegin();
+ const auto i1 = L.cend();
clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()");
clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()");
@@ -171,8 +171,8 @@
/// position of the container is incremented).
void pop_front(std::list<int> &L, int n) {
- L.cbegin();
- L.cend();
+ const auto i0 = L.cbegin();
+ const auto i1 = L.cend();
clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()");
clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()");
@@ -195,7 +195,7 @@
void push_back() {
std::vector<int> V;
- V.end();
+ const auto i0 = V.end();
clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()");
@@ -208,10 +208,10 @@
/// Track the right container only
void push_back1(std::vector<int> &V1, std::vector<int> &V2, int n) {
- V1.cbegin();
- V1.cend();
- V2.cbegin();
- V2.cend();
+ const auto i0 = V1.cbegin();
+ const auto i1 = V1.cend();
+ const auto i2 = V2.cbegin();
+ const auto i3 = V2.cend();
clang_analyzer_denote(clang_analyzer_container_begin(V1), "$V1.begin()");
@@ -222,10 +222,10 @@
}
void push_back2(std::vector<int> &V1, std::vector<int> &V2, int n) {
- V1.cbegin();
- V1.cend();
- V2.cbegin();
- V2.cend();
+ const auto i0 = V1.cbegin();
+ const auto i1 = V1.cend();
+ const auto i2 = V2.cbegin();
+ const auto i3 = V2.cend();
clang_analyzer_denote(clang_analyzer_container_begin(V1), "$V1.begin()");
clang_analyzer_denote(clang_analyzer_container_begin(V2), "$V2.begin()");
@@ -245,7 +245,7 @@
void clang_analyzer_printState();
void print_state(std::vector<int> &V) {
- V.cbegin();
+ const auto i0 = V.cbegin();
clang_analyzer_printState();
// CHECK: "checker_messages": [
@@ -263,3 +263,4 @@
// CHECK-NEXT: "SymRegion{reg_$[[#]]<std::vector<int> & V>} : [ conj_$[[#]]{long, LC[[#]], S[[#]], #[[#]]} .. conj_$[[#]]{long, LC[[#]], S[[#]], #[[#]]} ]"
// CHECK-NEXT: ]}
}
+
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -109,6 +109,96 @@
return LValue;
}
+Optional<SVal> ExprEngine::retrieveFromConstructionContext(
+ ProgramStateRef State, const LocationContext *LCtx,
+ const ConstructionContext *CC) const {
+ if (CC) {
+ switch (CC->getKind()) {
+ case ConstructionContext::CXX17ElidedCopyVariableKind:
+ case ConstructionContext::SimpleVariableKind: {
+ const auto *DSCC = cast<VariableConstructionContext>(CC);
+ const auto *DS = DSCC->getDeclStmt();
+ return getObjectUnderConstruction(State, DS, LCtx);
+ }
+ case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
+ case ConstructionContext::SimpleConstructorInitializerKind: {
+ const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
+ const auto *Init = ICC->getCXXCtorInitializer();
+ return getObjectUnderConstruction(State, Init, LCtx);
+ }
+ case ConstructionContext::SimpleReturnedValueKind:
+ case ConstructionContext::CXX17ElidedCopyReturnedValueKind: {
+ const StackFrameContext *SFC = LCtx->getStackFrame();
+ if (const LocationContext *CallerLCtx = SFC->getParent()) {
+ auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()]
+ .getAs<CFGCXXRecordTypedCall>();
+ if (!RTC) {
+ // We were unable to find the correct construction context for the
+ // call in the parent stack frame. This is equivalent to not being
+ // able to find construction context at all.
+ break;
+ }
+ if (isa<BlockInvocationContext>(CallerLCtx)) {
+ // Unwrap block invocation contexts. They're mostly part of
+ // the current stack frame.
+ CallerLCtx = CallerLCtx->getParent();
+ assert(!isa<BlockInvocationContext>(CallerLCtx));
+ }
+ return retrieveFromConstructionContext(
+ State, CallerLCtx, RTC->getConstructionContext());
+ }
+ break;
+ }
+ case ConstructionContext::ElidedTemporaryObjectKind: {
+ assert(AMgr.getAnalyzerOptions().ShouldElideConstructors);
+ const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC);
+ Optional<SVal> RetVal = retrieveFromConstructionContext(
+ State, LCtx, TCC->getConstructionContextAfterElision());
+ if (RetVal.hasValue())
+ return RetVal;
+
+ LLVM_FALLTHROUGH;
+ }
+ case ConstructionContext::SimpleTemporaryObjectKind: {
+ const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
+ const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
+ Optional<SVal> RetVal;
+ if (BTE) {
+ RetVal = getObjectUnderConstruction(State, BTE, LCtx);
+ if (RetVal.hasValue())
+ return RetVal;
+ }
+
+ const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
+ if (MTE)
+ RetVal = getObjectUnderConstruction(State, MTE, LCtx);
+
+ return RetVal;
+ }
+ case ConstructionContext::ArgumentKind: {
+ const auto *ACC = cast<ArgumentConstructionContext>(CC);
+ const Expr *E = ACC->getCallLikeExpr();
+ unsigned Idx = ACC->getIndex();
+ if (const auto *CE = dyn_cast<CallExpr>(E)) {
+ return getObjectUnderConstruction(State, {CE, Idx}, LCtx);
+ } else if (const auto *CCE = dyn_cast<CXXConstructExpr>(E)) {
+ return getObjectUnderConstruction(State, {CCE, Idx}, LCtx);
+ } else if (const auto *ME = dyn_cast<ObjCMessageExpr>(E)) {
+ return getObjectUnderConstruction(State, {ME, Idx}, LCtx);
+ } else if (const auto *BTE = ACC->getCXXBindTemporaryExpr()) {
+ return getObjectUnderConstruction(State, BTE, LCtx);
+ }
+
+ LLVM_FALLTHROUGH;
+ }
+ default:
+ return None;
+ }
+ }
+
+ return None;
+}
+
std::pair<ProgramStateRef, SVal> ExprEngine::handleConstructionContext(
const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
const ConstructionContext *CC, EvalCallOptions &CallOpts) {
@@ -137,7 +227,16 @@
case ConstructionContext::SimpleConstructorInitializerKind: {
const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
const auto *Init = ICC->getCXXCtorInitializer();
- assert(Init->isAnyMemberInitializer());
+ if (!Init->isAnyMemberInitializer()) {
+ const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
+ Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor,
+ LCtx->getStackFrame());
+ SVal ThisVal = State->getSVal(ThisPtr);
+ SVal BaseVal =
+ getStoreManager().evalDerivedToBase(ThisVal, E->getType(),
+ Init->isBaseVirtual());
+ return std::make_pair(State, BaseVal);
+ }
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
Loc ThisPtr =
SVB.getCXXThis(CurCtor, LCtx->getStackFrame());
Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -466,7 +466,7 @@
// incorrect handling of temporaries bound to default parameters.
assert(!State->get<ObjectsUnderConstruction>(Key) ||
Key.getItem().getKind() ==
- ConstructionContextItem::TemporaryDestructorKind);
+ ConstructionContextItem::TemporaryDestructorKind);
return State->set<ObjectsUnderConstruction>(Key, V);
}
@@ -602,7 +602,6 @@
const LocationContext *LCtx, const char *NL,
unsigned int Space, bool IsDot) const {
Indent(Out, Space, IsDot) << "\"constructing_objects\": ";
-
if (LCtx && !State->get<ObjectsUnderConstruction>().isEmpty()) {
++Space;
Out << '[' << NL;
Index: clang/lib/StaticAnalyzer/Core/CallEvent.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -257,6 +257,143 @@
return VR;
}
+const ConstructionContext
+*CallEvent::getConstructionContext(unsigned BlockCount) const {
+ const CFGBlock *Block;
+ unsigned Index;
+
+ if (const StackFrameContext *StackFrame = getCalleeStackFrame(BlockCount)) {
+ Block = StackFrame->getCallSiteBlock();
+ if (!Block) {
+ llvm::errs()<<"No Call Site Block.\n";
+ return nullptr;
+ }
+
+ Index = StackFrame->getIndex();
+ } else {
+ CFGStmtMap *Map =
+ getLocationContext()->getAnalysisDeclContext()->getCFGStmtMap();
+ Block = Map->getBlock(getOriginExpr());
+ if (!Block) {
+ llvm::errs()<<"No Call Site Block.\n";
+ return nullptr;
+ }
+
+ for (Index = 0; Index < Block->size(); ++Index) {
+ if (const auto CS = (*Block)[Index].getAs<CFGStmt>()) {
+ if (CS->getStmt() == getOriginExpr())
+ break;
+ }
+ }
+ }
+
+ if (Index == Block->size()) {
+ llvm::errs()<<" Stmt not found!\n";
+ return nullptr;
+ }
+
+ if(const auto Ctor = (*Block)[Index].getAs<CFGConstructor>()) {
+ return Ctor->getConstructionContext();
+ }
+
+ if (const auto RecCall = (*Block)[Index].getAs<CFGCXXRecordTypedCall>()) {
+ return RecCall->getConstructionContext();
+ }
+
+ llvm::errs()<<" Neither a Constructor nor a CFG C++ Record-Typed Call!\n";
+ return nullptr;
+}
+
+Optional<SVal>
+CallEvent::getReturnValueUnderConstruction(ExprEngine &Engine,
+ unsigned BlockCount) const {
+ const auto *CC = getConstructionContext(BlockCount);
+ if (!CC) {
+ llvm::errs()<<" No construction context!!!\n";
+ return None;
+ }
+
+ llvm::errs()<<" Retrieving Original\n";
+ Optional<SVal> RetVal =
+ Engine.retrieveFromConstructionContext(getState(), getLocationContext(),
+ CC);
+ if (RetVal.hasValue())
+ return RetVal;
+
+ ExprEngine::EvalCallOptions CallOpts;
+ ProgramStateRef State;
+ SVal NewVal;
+ llvm::errs()<<" Handling New\n";
+ std::tie(State, NewVal) =
+ Engine.handleConstructionContext(getOriginExpr(), getState(),
+ getLocationContext(), CC, CallOpts);
+ return NewVal;
+}
+
+const ConstructionContext
+*CallEvent::getArgConstructionContext(unsigned Index,
+ unsigned BlockCount) const {
+ const CFGBlock *Block;
+ unsigned CFGIndex;
+
+ CFGStmtMap *Map =
+ getLocationContext()->getAnalysisDeclContext()->getCFGStmtMap();
+ Block = Map->getBlock(getArgExpr(Index));
+ if (!Block) {
+ llvm::errs()<<"No Call Site Block.\n";
+ return nullptr;
+ }
+
+ for (CFGIndex = 0; CFGIndex < Block->size(); ++CFGIndex) {
+ if (const auto CS = (*Block)[CFGIndex].getAs<CFGStmt>()) {
+ if (CS->getStmt() == getArgExpr(Index))
+ break;
+ }
+ }
+
+ if (CFGIndex == Block->size()) {
+ llvm::errs()<<" Stmt not found!\n";
+ return nullptr;
+ }
+
+ if(const auto Ctor = (*Block)[CFGIndex].getAs<CFGConstructor>()) {
+ return Ctor->getConstructionContext();
+ }
+
+ if (const auto RecCall = (*Block)[CFGIndex].getAs<CFGCXXRecordTypedCall>()) {
+ return RecCall->getConstructionContext();
+ }
+
+ llvm::errs()<<" Neither a Constructor nor a CFG C++ Record-Typed Call!\n";
+ return nullptr;
+}
+
+Optional<SVal>
+CallEvent::getArgUnderConstruction(unsigned Index, ExprEngine &Engine,
+ unsigned BlockCount) const {
+ const auto *CC = getArgConstructionContext(Index, BlockCount);
+ if (!CC) {
+ llvm::errs()<<" No construction context!!!\n";
+ return None;
+ }
+
+ llvm::errs()<<" Retrieving Original\n";
+ Optional<SVal> RetVal =
+ Engine.retrieveFromConstructionContext(getState(), getLocationContext(),
+ CC);
+ if (RetVal.hasValue())
+ return RetVal;
+
+ ExprEngine::EvalCallOptions CallOpts;
+ ProgramStateRef State;
+ SVal NewVal;
+ llvm::errs()<<" Handling New\n";
+ std::tie(State, NewVal) =
+ Engine.handleConstructionContext(getArgExpr(Index), getState(),
+ getLocationContext(), CC, CallOpts);
+ return NewVal;
+}
+
/// Returns true if a type is a pointer-to-const or reference-to-const
/// with no further indirection.
static bool isPointerToConst(QualType Ty) {
Index: clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
+++ clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
@@ -24,12 +24,12 @@
namespace {
class STLAlgorithmModeling : public Checker<eval::Call> {
- bool evalFind(CheckerContext &C, const CallExpr *CE) const;
+ void evalFind(CheckerContext &C, const CallExpr *CE, SVal Begin,
+ SVal End) const;
- void Find(CheckerContext &C, const CallExpr *CE, unsigned paramNum) const;
-
- using FnCheck = bool (STLAlgorithmModeling::*)(CheckerContext &,
- const CallExpr *) const;
+ using FnCheck = void (STLAlgorithmModeling::*)(CheckerContext &,
+ const CallExpr *, SVal Begin,
+ SVal End) const;
const CallDescriptionMap<FnCheck> Callbacks = {
{{{"std", "find"}, 3}, &STLAlgorithmModeling::evalFind},
@@ -67,59 +67,87 @@
bool STLAlgorithmModeling::evalCall(const CallEvent &Call,
CheckerContext &C) const {
- const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
- if (!CE)
- return false;
-
- const FnCheck *Handler = Callbacks.lookup(Call);
- if (!Handler)
- return false;
-
- return (this->**Handler)(C, CE);
-}
-
-bool STLAlgorithmModeling::evalFind(CheckerContext &C,
- const CallExpr *CE) const {
// std::find()-like functions either take their primary range in the first
// two parameters, or if the first parameter is "execution policy" then in
// the second and third. This means that the second parameter must always be
// an iterator.
- if (!isIteratorType(CE->getArg(1)->getType()))
+ if (Call.getNumArgs() < 2 || !isIteratorType(Call.getArgExpr(1)->getType()))
return false;
+ unsigned ArgNum = 999;
+
// If no "execution policy" parameter is used then the first argument is the
// beginning of the range.
- if (isIteratorType(CE->getArg(0)->getType())) {
- Find(C, CE, 0);
- return true;
+ if (isIteratorType(Call.getArgExpr(0)->getType())) {
+ ArgNum = 1;
}
// If "execution policy" parameter is used then the second argument is the
// beginning of the range.
- if (isIteratorType(CE->getArg(2)->getType())) {
- Find(C, CE, 1);
- return true;
+ if (Call.getNumArgs() > 2 && isIteratorType(Call.getArgExpr(2)->getType())) {
+ ArgNum = 1;
+ }
+
+ if (ArgNum == 999)
+ return false;
+
+ Optional<SVal> ArgB = Call.getArgSVal(ArgNum);
+ llvm::errs()<<"Original ArgB: "<<*ArgB<<"\n";
+ if (ArgB->getAs<nonloc::LazyCompoundVal>()) {
+ const MemRegion *ArgLoc = Call.getParameterLocation(0, C.blockCount());
+ if (ArgLoc) {
+ ArgB = loc::MemRegionVal(ArgLoc);
+ } else {
+ ArgB = Call.getArgUnderConstruction(0, C.getExprEngine(),
+ C.blockCount());
+ if (!ArgB.hasValue())
+ return false;
+ }
}
+ llvm::errs()<<"Updated ArgB: "<<*ArgB<<"\n";
+
+ Optional<SVal> ArgE = Call.getArgSVal(ArgNum + 1);
+ llvm::errs()<<"Original ArgE: "<<*ArgE<<"\n";
+ if (ArgE->getAs<nonloc::LazyCompoundVal>()) {
+ const MemRegion *ArgLoc = Call.getParameterLocation(0, C.blockCount());
+ if (ArgLoc) {
+ ArgE = loc::MemRegionVal(ArgLoc);
+ } else {
+ ArgE = Call.getArgUnderConstruction(0, C.getExprEngine(),
+ C.blockCount());
+ if (!ArgE.hasValue())
+ return false;
+ }
+ }
+ llvm::errs()<<"Updated ArgE: "<<*ArgE<<"\n";
+
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return false;
- return false;
+ const FnCheck *Handler = Callbacks.lookup(Call);
+ if (!Handler)
+ return false;
+
+ (this->**Handler)(C, CE, *ArgB, *ArgE);
+ return true;
}
-void STLAlgorithmModeling::Find(CheckerContext &C, const CallExpr *CE,
- unsigned paramNum) const {
+void STLAlgorithmModeling::evalFind(CheckerContext &C, const CallExpr *CE,
+ SVal Begin, SVal End) const {
auto State = C.getState();
auto &SVB = C.getSValBuilder();
const auto *LCtx = C.getLocationContext();
SVal RetVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
- SVal Param = State->getSVal(CE->getArg(paramNum), LCtx);
-
+
auto StateFound = State->BindExpr(CE, LCtx, RetVal);
// If we have an iterator position for the range-begin argument then we can
// assume that in case of successful search the position of the found element
// is not ahead of it.
// FIXME: Reverse iterators
- const auto *Pos = getIteratorPosition(State, Param);
+ const auto *Pos = getIteratorPosition(State, Begin);
if (Pos) {
StateFound = createIteratorPosition(StateFound, RetVal, Pos->getContainer(),
CE, LCtx, C.blockCount());
@@ -135,13 +163,11 @@
StateFound = StateFound->assume(GreaterOrEqual.castAs<DefinedSVal>(), true);
}
- Param = State->getSVal(CE->getArg(paramNum + 1), LCtx);
-
// If we have an iterator position for the range-end argument then we can
// assume that in case of successful search the position of the found element
// is ahead of it.
// FIXME: Reverse iterators
- Pos = getIteratorPosition(State, Param);
+ Pos = getIteratorPosition(State, End);
if (Pos) {
StateFound = createIteratorPosition(StateFound, RetVal, Pos->getContainer(),
CE, LCtx, C.blockCount());
@@ -160,7 +186,7 @@
C.addTransition(StateFound);
if (AggressiveStdFindModeling) {
- auto StateNotFound = State->BindExpr(CE, LCtx, Param);
+ auto StateNotFound = State->BindExpr(CE, LCtx, End);
C.addTransition(StateNotFound);
}
}
Index: clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
+++ clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
@@ -83,8 +83,8 @@
namespace {
class IteratorModeling
- : public Checker<check::PostCall, check::PostStmt<MaterializeTemporaryExpr>,
- check::Bind, check::LiveSymbols, check::DeadSymbols> {
+ : public Checker<check::PostCall, check::Bind, check::LiveSymbols,
+ check::DeadSymbols> {
using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *,
SVal, SVal, SVal) const;
@@ -146,8 +146,6 @@
void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
- void checkPostStmt(const MaterializeTemporaryExpr *MTE,
- CheckerContext &C) const;
void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
};
@@ -156,8 +154,6 @@
ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
SymbolRef Sym2, bool Equal);
-bool isBoundThroughLazyCompoundVal(const Environment &Env,
- const MemRegion *Reg);
const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call);
} // namespace
@@ -190,14 +186,25 @@
auto State = C.getState();
+ llvm::errs()<<"Generic PostCall\n";
+ Optional<SVal> RetVal = Call.getReturnValue();
+ llvm::errs()<<"Original RetVal: "<<*RetVal<<"\n";
+ if (RetVal->getAs<nonloc::LazyCompoundVal>()) {
+ RetVal =
+ Call.getReturnValueUnderConstruction(C.getExprEngine(), C.blockCount());
+ if (!RetVal.hasValue())
+ RetVal = Call.getReturnValue();
+ }
+ llvm::errs()<<"Updated RetVal: "<<*RetVal<<"\n";
+
// Already bound to container?
- if (getIteratorPosition(State, Call.getReturnValue()))
+ if (getIteratorPosition(State, *RetVal))
return;
// Copy-like and move constructors
if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
- State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
+ State = setIteratorPosition(State, *RetVal, *Pos);
if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
State = removeIteratorPosition(State, Call.getArgSVal(0));
}
@@ -214,7 +221,7 @@
for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
if (isIteratorType(Call.getArgExpr(i)->getType())) {
if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
- assignToContainer(C, OrigExpr, Call.getReturnValue(),
+ assignToContainer(C, OrigExpr, *RetVal,
Pos->getContainer());
return;
}
@@ -225,6 +232,11 @@
void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S,
CheckerContext &C) const {
auto State = C.getState();
+ if (Val.getAs<nonloc::LazyCompoundVal>())
+ return;
+
+ llvm::errs()<<"Bind Old: "<<Val<<"\n";
+ llvm::errs()<<"Bind New: "<<Loc<<"\n";
const auto *Pos = getIteratorPosition(State, Val);
if (Pos) {
State = setIteratorPosition(State, Loc, *Pos);
@@ -238,26 +250,18 @@
}
}
-void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
- CheckerContext &C) const {
- /* Transfer iterator state to temporary objects */
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr()));
- if (!Pos)
- return;
- State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
- C.addTransition(State);
-}
-
void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
SymbolReaper &SR) const {
+ llvm::errs()<<"CheckLive\n";
// Keep symbolic expressions of iterator positions alive
auto RegionMap = State->get<IteratorRegionMap>();
for (const auto &Reg : RegionMap) {
const auto Offset = Reg.second.getOffset();
for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
- if (isa<SymbolData>(*i))
+ if (isa<SymbolData>(*i)) {
+ llvm::errs()<<" Keeping position "<<*i<<".\n";
SR.markLive(*i);
+ }
}
auto SymbolMap = State->get<IteratorSymbolMap>();
@@ -272,18 +276,17 @@
void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
CheckerContext &C) const {
+ llvm::errs()<<"CheckDead\n";
// Cleanup
auto State = C.getState();
auto RegionMap = State->get<IteratorRegionMap>();
for (const auto &Reg : RegionMap) {
+ llvm::errs()<<"Is live "<<Reg.first<<"?\n";
if (!SR.isLiveRegion(Reg.first)) {
- // The region behind the `LazyCompoundVal` is often cleaned up before
- // the `LazyCompoundVal` itself. If there are iterator positions keyed
- // by these regions their cleanup must be deferred.
- if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
- State = State->remove<IteratorRegionMap>(Reg.first);
- }
+ llvm::errs()<<" Removing position for "<<Reg.first<<" (Pos: "<<Reg.second.getOffset()<<").\n";
+ llvm::errs()<<" Base Region: "<<Reg.first->getBaseRegion()<<"\n";
+ State = State->remove<IteratorRegionMap>(Reg.first);
}
}
@@ -301,61 +304,74 @@
IteratorModeling::handleOverloadedOperator(CheckerContext &C,
const CallEvent &Call,
OverloadedOperatorKind Op) const {
- if (isSimpleComparisonOperator(Op)) {
- const auto *OrigExpr = Call.getOriginExpr();
- if (!OrigExpr)
- return;
+ const auto *OrigExpr = Call.getOriginExpr();
+ if (!OrigExpr)
+ return;
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- handleComparison(C, OrigExpr, Call.getReturnValue(),
- InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
- return;
- }
+ llvm::errs()<<"Overloaded Operator\n";
+ Optional<SVal> RetVal = Call.getReturnValue();
+ llvm::errs()<<"Original RetVal: "<<*RetVal<<"\n";
+ if (RetVal->getAs<nonloc::LazyCompoundVal>()) {
+ RetVal =
+ Call.getReturnValueUnderConstruction(C.getExprEngine(), C.blockCount());
+ if (!RetVal.hasValue())
+ RetVal = Call.getReturnValue();
+ }
+ llvm::errs()<<"Updated RetVal: "<<*RetVal<<"\n";
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call))
+ llvm::errs()<<"ThisVal: "<<InstCall->getCXXThisVal()<<"\n";
+
+ if (isSimpleComparisonOperator(Op)) {
+ const auto *OrigExpr = Call.getOriginExpr();
+ if (!OrigExpr)
+ return;
- handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
- Call.getArgSVal(1), Op);
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleComparison(C, OrigExpr, *RetVal,
+ InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
return;
- } else if (isRandomIncrOrDecrOperator(Op)) {
- const auto *OrigExpr = Call.getOriginExpr();
- if (!OrigExpr)
- return;
+ }
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (Call.getNumArgs() >= 1 &&
- Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
- handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
- InstCall->getCXXThisVal(), Call.getArgSVal(0));
- return;
- }
- } else {
- if (Call.getNumArgs() >= 2 &&
- Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
- handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
- Call.getArgSVal(0), Call.getArgSVal(1));
- return;
- }
+ handleComparison(C, OrigExpr, *RetVal, Call.getArgSVal(0),
+ Call.getArgSVal(1), Op);
+ return;
+ } else if (isRandomIncrOrDecrOperator(Op)) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ if (Call.getNumArgs() >= 1 &&
+ Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
+ handleRandomIncrOrDecr(C, OrigExpr, Op, *RetVal,
+ InstCall->getCXXThisVal(), Call.getArgSVal(0));
+ return;
}
- } else if (isIncrementOperator(Op)) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
- Call.getNumArgs());
+ } else {
+ if (Call.getNumArgs() >= 2 &&
+ Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
+ handleRandomIncrOrDecr(C, OrigExpr, Op, *RetVal,
+ Call.getArgSVal(0), Call.getArgSVal(1));
return;
}
-
- handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
+ }
+ } else if (isIncrementOperator(Op)) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleIncrement(C, *RetVal, InstCall->getCXXThisVal(),
Call.getNumArgs());
return;
- } else if (isDecrementOperator(Op)) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
- Call.getNumArgs());
- return;
- }
+ }
- handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
- Call.getNumArgs());
+ handleIncrement(C, *RetVal, Call.getArgSVal(0),
+ Call.getNumArgs());
+ return;
+ } else if (isDecrementOperator(Op)) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleDecrement(C, *RetVal, InstCall->getCXXThisVal(),
+ Call.getNumArgs());
return;
}
+
+ handleDecrement(C, *RetVal, Call.getArgSVal(0),
+ Call.getNumArgs());
+ return;
+ }
}
void
@@ -363,8 +379,19 @@
const CallEvent &Call,
const Expr *OrigExpr,
const AdvanceFn *Handler) const {
+ llvm::errs()<<"Advance-like Function\n";
+ Optional<SVal> RetVal = Call.getReturnValue();
+ llvm::errs()<<"Original RetVal: "<<*RetVal<<"\n";
+ if (RetVal->getAs<nonloc::LazyCompoundVal>()) {
+ RetVal =
+ Call.getReturnValueUnderConstruction(C.getExprEngine(), C.blockCount());
+ if (!RetVal.hasValue())
+ RetVal = Call.getReturnValue();
+ }
+ llvm::errs()<<"Updated RetVal: "<<*RetVal<<"\n";
+
if (!C.wasInlined) {
- (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
+ (this->**Handler)(C, OrigExpr, *RetVal,
Call.getArgSVal(0), Call.getArgSVal(1));
return;
}
@@ -375,7 +402,7 @@
if (IdInfo) {
if (IdInfo->getName() == "advance") {
if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) {
- (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
+ (this->**Handler)(C, OrigExpr, *RetVal,
Call.getArgSVal(0), Call.getArgSVal(1));
}
}
@@ -647,10 +674,8 @@
return State->remove<IteratorRegionMap>(Reg);
} else if (const auto Sym = Val.getAsSymbol()) {
return State->remove<IteratorSymbolMap>(Sym);
- } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
- return State->remove<IteratorRegionMap>(LCVal->getRegion());
}
- return nullptr;
+ return State;
}
ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
@@ -685,18 +710,6 @@
return NewState;
}
-bool isBoundThroughLazyCompoundVal(const Environment &Env,
- const MemRegion *Reg) {
- for (const auto &Binding : Env) {
- if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
- if (LCVal->getRegion() == Reg)
- return true;
- }
- }
-
- return false;
-}
-
const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) {
while (Node) {
ProgramPoint PP = Node->getLocation();
Index: clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
+++ clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
@@ -153,28 +153,26 @@
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
const SVal &Val) {
+ llvm::errs()<<" Getting position for "<<Val<<".\n";
if (auto Reg = Val.getAsRegion()) {
Reg = Reg->getMostDerivedObjectRegion();
return State->get<IteratorRegionMap>(Reg);
} else if (const auto Sym = Val.getAsSymbol()) {
return State->get<IteratorSymbolMap>(Sym);
- } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
- return State->get<IteratorRegionMap>(LCVal->getRegion());
}
return nullptr;
}
ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
const IteratorPosition &Pos) {
+ llvm::errs()<<" Setting position for "<<Val<<" (Pos: "<<Pos.getOffset()<<").\n";
if (auto Reg = Val.getAsRegion()) {
Reg = Reg->getMostDerivedObjectRegion();
return State->set<IteratorRegionMap>(Reg, Pos);
} else if (const auto Sym = Val.getAsSymbol()) {
return State->set<IteratorSymbolMap>(Sym, Pos);
- } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
- return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
}
- return nullptr;
+ return State;
}
ProgramStateRef createIteratorPosition(ProgramStateRef State, const SVal &Val,
Index: clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
+++ clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
@@ -163,6 +163,17 @@
if (!Func)
return;
+ llvm::errs()<<"Container PostCall: "<<Func->getNameAsString()<<"\n";
+ Optional<SVal> RetVal = Call.getReturnValue();
+ llvm::errs()<<"Original RetVal: "<<*RetVal<<"\n";
+ if (RetVal->getAs<nonloc::LazyCompoundVal>()) {
+ RetVal =
+ Call.getReturnValueUnderConstruction(C.getExprEngine(), C.blockCount());
+ if (!RetVal.hasValue())
+ RetVal = Call.getReturnValue();
+ }
+ llvm::errs()<<"Updated RetVal: "<<*RetVal<<"\n";
+
if (Func->isOverloadedOperator()) {
const auto Op = Func->getOverloadedOperator();
if (Op == OO_Equal) {
@@ -188,7 +199,23 @@
const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call);
if (Handler1) {
- (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
+ Optional<SVal> Arg = Call.getArgSVal(0);
+ llvm::errs()<<"Original Arg(0): "<<*Arg<<"\n";
+ if (Arg->getAs<nonloc::LazyCompoundVal>()) {
+ const MemRegion *ArgLoc = Call.getParameterLocation(0,
+ C.blockCount());
+ if (ArgLoc) {
+ Arg = loc::MemRegionVal(ArgLoc);
+ } else {
+ Arg = Call.getArgUnderConstruction(0, C.getExprEngine(),
+ C.blockCount());
+ if (!Arg.hasValue())
+ return;
+ }
+ }
+ llvm::errs()<<"Updated Arg(0): "<<*Arg<<"\n";
+
+ (this->**Handler1)(C, InstCall->getCXXThisVal(), *Arg);
return;
}
@@ -204,13 +231,13 @@
return;
if (isBeginCall(Func)) {
- handleBegin(C, OrigExpr, Call.getReturnValue(),
+ handleBegin(C, OrigExpr, *RetVal,
InstCall->getCXXThisVal());
return;
}
if (isEndCall(Func)) {
- handleEnd(C, OrigExpr, Call.getReturnValue(),
+ handleEnd(C, OrigExpr, *RetVal,
InstCall->getCXXThisVal());
return;
}
@@ -580,9 +607,13 @@
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Iter);
- if (!Pos)
+ llvm::errs()<<"Inserting to: "<<Iter;
+ if (!Pos) {
+ llvm::errs()<<" (No Pos)\n";
return;
+ }
+ llvm::errs()<<" (Pos: "<<Pos->getOffset()<<")\n";
// For deque-like containers invalidate all iterator positions. For
// vector-like containers invalidate iterator positions after the insertion.
if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -686,6 +686,21 @@
const CallEvent &Call,
const EvalCallOptions &CallOpts = {});
+ /// Update the program state with all the path-sensitive information
+ /// that's necessary to perform construction of an object with a given
+ /// syntactic construction context. If the construction context is unavailable
+ /// or unusable for any reason, a dummy temporary region is returned, and the
+ /// IsConstructorWithImproperlyModeledTargetRegion flag is set in \p CallOpts.
+ /// Returns the updated program state and the new object's this-region.
+ std::pair<ProgramStateRef, SVal> handleConstructionContext(
+ const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
+ const ConstructionContext *CC, EvalCallOptions &CallOpts);
+
+ ///
+ Optional<SVal> retrieveFromConstructionContext(
+ ProgramStateRef State, const LocationContext *LCtx,
+ const ConstructionContext *CC) const;
+
private:
ProgramStateRef finishArgumentConstruction(ProgramStateRef State,
const CallEvent &Call);
@@ -804,16 +819,6 @@
/// constructing into an existing region.
const CXXConstructExpr *findDirectConstructorForCurrentCFGElement();
- /// Update the program state with all the path-sensitive information
- /// that's necessary to perform construction of an object with a given
- /// syntactic construction context. If the construction context is unavailable
- /// or unusable for any reason, a dummy temporary region is returned, and the
- /// IsConstructorWithImproperlyModeledTargetRegion flag is set in \p CallOpts.
- /// Returns the updated program state and the new object's this-region.
- std::pair<ProgramStateRef, SVal> handleConstructionContext(
- const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
- const ConstructionContext *CC, EvalCallOptions &CallOpts);
-
/// Common code that handles either a CXXConstructExpr or a
/// CXXInheritedCtorInitExpr.
void handleConstructor(const Expr *E, ExplodedNode *Pred,
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
@@ -52,6 +52,10 @@
"We should not call the checkers on an empty state.");
}
+ ExprEngine &getExprEngine() {
+ return Eng;
+ }
+
AnalysisManager &getAnalysisManager() {
return Eng.getAnalysisManager();
}
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -431,6 +431,19 @@
return CallArgumentIndex;
}
+ /// If the call returns a C++ record type then the call has a construction
+ /// context from where the region of its return value can be retrieved.
+ const ConstructionContext *getConstructionContext(unsigned BlockCount) const;
+
+ const ConstructionContext
+ *getArgConstructionContext(unsigned Index, unsigned BlockCount) const;
+
+ Optional<SVal> getArgUnderConstruction(unsigned Index, ExprEngine &Engine,
+ unsigned BlockCount) const;
+
+ Optional<SVal> getReturnValueUnderConstruction(ExprEngine &Engine,
+ unsigned BlockCount) const;
+
// Iterator access to formal parameters and their types.
private:
struct GetTypeFn {
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits