baloghadamsoftware updated this revision to Diff 258798.
baloghadamsoftware added a comment.
I commented out lines which prevent creation of `StackFrameContext` for
non-inlined functions. Now everything works regarding the iterator checkers. I
also simplified things and created new methods for `CallEvent`:
`getArgObject()` and `getReturnObject()`.
Two tests fail now: `temporaries.cpp` emits `TRUE` in lines `894` and `918` (I
wonder whether they are correct) and `explain-svals.cpp` emits `lazily frozen
compound value of parameter ''` in line `96` which is surely incorrect.
The main problem remains to solve is what we already discussed: either to find
always the same `Decl` or create a new kind of region for arguments.
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/ExprEngine.h
clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp
clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.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/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.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
@@ -1862,7 +1862,7 @@
void clang_analyzer_printState();
void print_state(std::vector<int> &V) {
- const auto i0 = V.cbegin();
+ auto i0 = V.cbegin();
clang_analyzer_printState();
// CHECK: "checker_messages": [
@@ -1871,7 +1871,8 @@
// CHECK-NEXT: "i0 : Valid ; Container == SymRegion{reg_$[[#]]<std::vector<int> & V>} ; Offset == conj_$[[#]]{long, LC[[#]], S[[#]], #[[#]]}"
// CHECK-NEXT: ]}
- const auto i1 = V.cend();
+ ++i0;
+ auto i1 = V.cend();
clang_analyzer_printState();
// CHECK: "checker_messages": [
@@ -1879,4 +1880,6 @@
// CHECK-NEXT: "Iterator Positions :",
// CHECK-NEXT: "i1 : Valid ; Container == SymRegion{reg_$[[#]]<std::vector<int> & V>} ; Offset == conj_$[[#]]{long, LC[[#]], S[[#]], #[[#]]}"
// CHECK-NEXT: ]}
+
+ --i1;
}
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/ExprEngineCallAndReturn.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -538,8 +538,8 @@
SVal VV = *V;
(void)VV;
assert(cast<VarRegion>(VV.castAs<loc::MemRegionVal>().getRegion())
- ->getStackFrame()->getParent()
- ->getStackFrame() == LC->getStackFrame());
+ ->getStackFrame()->getParent()
+ ->getStackFrame() == LC->getStackFrame());
State = finishObjectConstruction(State, {E, I}, LC);
}
}
Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -109,6 +109,97 @@
return LValue;
}
+Optional<SVal> ExprEngine::retrieveFromConstructionContext(
+ ProgramStateRef State, const LocationContext *LCtx,
+ const ConstructionContext *CC) {
+ 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(State->getAnalysisManager().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) {
@@ -354,8 +445,7 @@
CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx);
if (auto OptV = getArgLoc(Caller))
V = *OptV;
- else
- break;
+ else break;
State = addObjectUnderConstruction(State, {CE, Idx}, LCtx, V);
} else if (const auto *CCE = dyn_cast<CXXConstructExpr>(E)) {
// Don't bother figuring out the target region for the future
Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -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
@@ -178,15 +178,15 @@
// situation because there's a loss of precision anyway because we cannot
// inline the call.
RuntimeDefinition RD = getRuntimeDefinition();
- if (!RD.getDecl())
- return nullptr;
+ // if (!RD.getDecl())
+ // return nullptr;
AnalysisDeclContext *ADC =
LCtx->getAnalysisDeclContext()->getManager()->getContext(D);
// TODO: For now we skip virtual functions, because this also rises
// the problem of which decl to use, but now it's across different classes.
- if (RD.mayHaveOtherDefinitions() || RD.getDecl() != ADC->getDecl())
+ if (RD.getDecl() && (RD.mayHaveOtherDefinitions() || RD.getDecl() != ADC->getDecl()))
return nullptr;
return ADC;
@@ -228,11 +228,11 @@
// We cannot construct a VarRegion without a stack frame.
if (!SFC)
return nullptr;
+ // Retrieve parameters of the definition, which are different from
+ // CallEvent's parameters() because getDecl() isn't necessarily
+ // the definition. SFC contains the definition that would be used
+ // during analysis.
- // Retrieve parameters of the definition, which are different from
- // CallEvent's parameters() because getDecl() isn't necessarily
- // the definition. SFC contains the definition that would be used
- // during analysis.
const Decl *D = SFC->getDecl();
// TODO: Refactor into a virtual method of CallEvent, like parameters().
@@ -257,6 +257,65 @@
return VR;
}
+const ConstructionContext
+*CallEvent::getConstructionContext(unsigned BlockCount) const {
+ const CFGBlock *Block;
+ unsigned Index;
+
+ const StackFrameContext *StackFrame = getCalleeStackFrame(BlockCount);
+ if (!StackFrame)
+ return nullptr;
+
+ Block = StackFrame->getCallSiteBlock();
+ if (!Block)
+ return nullptr;
+
+ Index = StackFrame->getIndex();
+
+ if(const auto Ctor = (*Block)[Index].getAs<CFGConstructor>()) {
+ return Ctor->getConstructionContext();
+ }
+
+ if (const auto RecCall = (*Block)[Index].getAs<CFGCXXRecordTypedCall>()) {
+ return RecCall->getConstructionContext();
+ }
+
+ return nullptr;
+}
+
+Optional<SVal>
+CallEvent::getReturnValueUnderConstruction(unsigned BlockCount) const {
+ const auto *CC = getConstructionContext(BlockCount);
+ if (!CC)
+ return None;
+
+ Optional<SVal> RetVal =
+ ExprEngine::retrieveFromConstructionContext(getState(),
+ getLocationContext(), CC);
+ return RetVal;
+}
+
+Optional<SVal> CallEvent::getArgObject(unsigned Index,
+ unsigned BlockCount) const {
+ SVal Arg = getArgSVal(Index);
+ if (Arg.getAsSymbol() || Arg.getAsRegion())
+ return Arg;
+
+ const MemRegion *ArgLoc = getParameterLocation(Index, BlockCount);
+ if (ArgLoc)
+ return loc::MemRegionVal(ArgLoc);
+
+ return None;
+}
+
+Optional<SVal> CallEvent::getReturnObject(unsigned BlockCount) const {
+ SVal RetVal = getReturnValue();
+ if (RetVal.getAsSymbol() || RetVal.getAsRegion())
+ return RetVal;
+
+ return getReturnValueUnderConstruction(BlockCount);
+}
+
/// 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,81 @@
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 = 0;
}
// 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 (ArgNum > 0 &&
+ Call.getNumArgs() > 2 && isIteratorType(Call.getArgExpr(2)->getType())) {
+ ArgNum = 1;
}
- return false;
+ 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(ArgNum, C.blockCount());
+ if (!ArgLoc)
+ return false;
+
+ ArgB = loc::MemRegionVal(ArgLoc);
+ }
+ 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(ArgNum + 1,
+ C.blockCount());
+ if (!ArgLoc)
+ return false;
+
+ ArgE = loc::MemRegionVal(ArgLoc);
+ }
+ llvm::errs()<<"Updated ArgE: "<<*ArgE<<"\n";
+
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ 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 +157,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 +180,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/MismatchedIteratorChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
@@ -32,10 +32,8 @@
std::unique_ptr<BugType> MismatchedBugType;
- void verifyMatch(CheckerContext &C, const SVal &Iter,
- const MemRegion *Cont) const;
- void verifyMatch(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const;
+ void verifyMatch(CheckerContext &C, SVal Iter, const MemRegion *Cont) const;
+ void verifyMatch(CheckerContext &C, SVal Iter1, SVal Iter2) const;
void reportBug(const StringRef &Message, const SVal &Val1,
const SVal &Val2, CheckerContext &C,
ExplodedNode *ErrNode) const;
@@ -65,51 +63,70 @@
if (!Func)
return;
+ // If the call has no arguments, there is nothing to check here
+ if (Call.getNumArgs() < 1)
+ return;
+
if (Func->isOverloadedOperator() &&
isComparisonOperator(Func->getOverloadedOperator())) {
// Check for comparisons of iterators of different containers
+ Optional<SVal> Arg0 = Call.getArgObject(0, C.blockCount());
+ if (!Arg0.hasValue())
+ return;
+
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (Call.getNumArgs() < 1)
- return;
-
if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
!isIteratorType(Call.getArgExpr(0)->getType()))
return;
- verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
+ verifyMatch(C, InstCall->getCXXThisVal(), *Arg0);
} else {
if (Call.getNumArgs() < 2)
return;
+ Optional<SVal> Arg1 = Call.getArgObject(1, C.blockCount());
+ if (!Arg1.hasValue())
+ return;
+
if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
!isIteratorType(Call.getArgExpr(1)->getType()))
return;
- verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
+ verifyMatch(C, *Arg0, *Arg1);
}
} else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
if (!ContReg)
return;
+
+ Optional<SVal> Arg0 = Call.getArgObject(0, C.blockCount());
+ if (!Arg0.hasValue())
+ return;
+
// Check for erase, insert and emplace using iterator of another container
if (isEraseCall(Func) || isEraseAfterCall(Func)) {
- verifyMatch(C, Call.getArgSVal(0),
- InstCall->getCXXThisVal().getAsRegion());
+ verifyMatch(C, *Arg0, InstCall->getCXXThisVal().getAsRegion());
if (Call.getNumArgs() == 2) {
- verifyMatch(C, Call.getArgSVal(1),
- InstCall->getCXXThisVal().getAsRegion());
+ Optional<SVal> Arg1 = Call.getArgObject(1, C.blockCount());
+ if (!Arg1.hasValue())
+ return;
+
+ verifyMatch(C, *Arg1, InstCall->getCXXThisVal().getAsRegion());
}
} else if (isInsertCall(Func)) {
- verifyMatch(C, Call.getArgSVal(0),
- InstCall->getCXXThisVal().getAsRegion());
+ verifyMatch(C, *Arg0, InstCall->getCXXThisVal().getAsRegion());
if (Call.getNumArgs() == 3 &&
isIteratorType(Call.getArgExpr(1)->getType()) &&
isIteratorType(Call.getArgExpr(2)->getType())) {
- verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
+ Optional<SVal> Arg1 = Call.getArgObject(1, C.blockCount());
+ Optional<SVal> Arg2 = Call.getArgObject(2, C.blockCount());
+ if (!Arg1.hasValue() || !Arg2.hasValue())
+ return;
+
+ verifyMatch(C, *Arg1, *Arg2);
}
} else if (isEmplaceCall(Func)) {
- verifyMatch(C, Call.getArgSVal(0),
- InstCall->getCXXThisVal().getAsRegion());
+ verifyMatch(C, *Arg0, InstCall->getCXXThisVal().getAsRegion());
}
} else if (isa<CXXConstructorCall>(&Call)) {
// Check match of first-last iterator pair in a constructor of a container
@@ -128,7 +145,12 @@
!isIteratorType(Call.getArgExpr(1)->getType()))
return;
- verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
+ Optional<SVal> Arg0 = Call.getArgObject(0, C.blockCount());
+ Optional<SVal> Arg1 = Call.getArgObject(1, C.blockCount());
+ if (!Arg0.hasValue() || !Arg1.hasValue())
+ return;
+
+ verifyMatch(C, *Arg0, *Arg1);
} else {
// The main purpose of iterators is to abstract away from different
// containers and provide a (maybe limited) uniform access to them.
@@ -166,7 +188,7 @@
if (!isIteratorType(TAType))
continue;
- SVal LHS = UndefinedVal();
+ Optional<SVal> LHS = None;
// For every template parameter which is an iterator type in the
// instantiation look for all functions' parameters' type by it and
@@ -178,17 +200,22 @@
if (!ParamType ||
ParamType->getReplacedParameter()->getDecl() != TPDecl)
continue;
- if (LHS.isUndef()) {
- LHS = Call.getArgSVal(J);
+
+ const Optional<SVal> ArgJ = Call.getArgObject(J, C.blockCount());
+ if (!ArgJ.hasValue())
+ continue;
+
+ if (!LHS.hasValue()) {
+ LHS = ArgJ;
} else {
- verifyMatch(C, LHS, Call.getArgSVal(J));
+ verifyMatch(C, *LHS, *ArgJ);
}
}
}
}
}
-void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
+void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, SVal Iter,
const MemRegion *Cont) const {
// Verify match between a container and the container of an iterator
Cont = Cont->getMostDerivedObjectRegion();
@@ -219,14 +246,13 @@
if (!N) {
return;
}
- reportBug("Container accessed using foreign iterator argument.",
- Iter, Cont, C, N);
+ reportBug("Container accessed using foreign iterator argument.", Iter, Cont,
+ C, N);
}
}
-void MismatchedIteratorChecker::verifyMatch(CheckerContext &C,
- const SVal &Iter1,
- const SVal &Iter2) const {
+void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, SVal Iter1,
+ SVal Iter2) const {
// Verify match between the containers of two iterators
auto State = C.getState();
const auto *Pos1 = getIteratorPosition(State, Iter1);
Index: clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
@@ -76,14 +76,18 @@
if (!Func)
return;
+ Optional<SVal> Arg0 = None;
+ if (Call.getNumArgs() >= 1)
+ Arg0 = Call.getArgObject(0, C.blockCount());
+
if (Func->isOverloadedOperator()) {
if (isIncrementOperator(Func->getOverloadedOperator())) {
// Check for out-of-range incrementions
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
verifyIncrement(C, InstCall->getCXXThisVal());
} else {
- if (Call.getNumArgs() >= 1) {
- verifyIncrement(C, Call.getArgSVal(0));
+ if (Arg0.hasValue()) {
+ verifyIncrement(C, *Arg0);
}
}
} else if (isDecrementOperator(Func->getOverloadedOperator())) {
@@ -91,8 +95,8 @@
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
verifyDecrement(C, InstCall->getCXXThisVal());
} else {
- if (Call.getNumArgs() >= 1) {
- verifyDecrement(C, Call.getArgSVal(0));
+ if (Arg0.hasValue()) {
+ verifyDecrement(C, *Arg0);
}
}
} else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
@@ -105,29 +109,31 @@
Call.getArgSVal(0));
}
} else {
- if (Call.getNumArgs() >= 2 &&
+ if (Arg0.hasValue() && Call.getNumArgs() >= 2 &&
Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
- Call.getArgSVal(0), Call.getArgSVal(1));
+ *Arg0, Call.getArgSVal(1));
}
}
} else if (isDereferenceOperator(Func->getOverloadedOperator())) {
// Check for dereference of out-of-range iterators
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
verifyDereference(C, InstCall->getCXXThisVal());
- } else {
- verifyDereference(C, Call.getArgSVal(0));
+ } else if (Arg0.hasValue()) {
+ verifyDereference(C, *Arg0);
}
}
} else {
const AdvanceFn *Verifier = AdvanceFunctions.lookup(Call);
if (Verifier) {
+ Optional<SVal> Arg0 = Call.getArgObject(0, C.blockCount());
+
if (Call.getNumArgs() > 1) {
- (this->**Verifier)(C, Call.getArgSVal(0), Call.getArgSVal(1));
+ (this->**Verifier)(C, *Arg0, Call.getArgSVal(1));
} else {
auto &BVF = C.getSValBuilder().getBasicValueFactory();
(this->**Verifier)(
- C, Call.getArgSVal(0),
+ C, *Arg0,
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
}
}
Index: clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
+++ clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
@@ -83,11 +83,12 @@
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;
+ Optional<SVal>, SVal,
+ SVal) const;
void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call,
OverloadedOperatorKind Op) const;
@@ -96,25 +97,25 @@
const AdvanceFn *Handler) const;
void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal,
- const SVal &LVal, const SVal &RVal,
- OverloadedOperatorKind Op) const;
+ SVal LVal, SVal RVal, OverloadedOperatorKind Op) const;
void processComparison(CheckerContext &C, ProgramStateRef State,
- SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
+ SymbolRef Sym1, SymbolRef Sym2, SVal RetVal,
OverloadedOperatorKind Op) const;
- void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
- bool Postfix) const;
- void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
- bool Postfix) const;
+ void handleIncrement(CheckerContext &C, Optional<SVal> RetVal,
+ SVal Iter, bool Postfix) const;
+ void handleDecrement(CheckerContext &C, Optional<SVal> RetVal,
+ SVal Iter, bool Postfix) const;
void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
- OverloadedOperatorKind Op, const SVal &RetVal,
- const SVal &LHS, const SVal &RHS) const;
- void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
- SVal Amount) const;
- void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
- SVal Amount) const;
- void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
- SVal Amount) const;
- void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
+ OverloadedOperatorKind Op,
+ Optional<SVal> RetVal, SVal LHS,
+ SVal RHS) const;
+ void handleAdvance(CheckerContext &C, const Expr *CE, Optional<SVal> RetVal,
+ SVal Iter, SVal Amount) const;
+ void handlePrev(CheckerContext &C, const Expr *CE, Optional<SVal> RetVal,
+ SVal Iter, SVal Amount) const;
+ void handleNext(CheckerContext &C, const Expr *CE, Optional<SVal> RetVal,
+ SVal Iter, SVal Amount) const;
+ void assignToContainer(CheckerContext &C, const Expr *CE, SVal RetVal,
const MemRegion *Cont) const;
bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const;
void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
@@ -146,8 +147,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 +155,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
@@ -191,15 +188,23 @@
auto State = C.getState();
// Already bound to container?
- if (getIteratorPosition(State, Call.getReturnValue()))
+ Optional<SVal> RetVal = Call.getReturnObject(C.blockCount());
+ if (!RetVal.hasValue())
+ return;
+
+ 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);
+ Optional<SVal> Arg = Call.getArgObject(0, C.blockCount());
+ if (!Arg.hasValue())
+ return;
+
+ if (const auto *Pos = getIteratorPosition(State, *Arg)) {
+ State = setIteratorPosition(State, *RetVal, *Pos);
if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
- State = removeIteratorPosition(State, Call.getArgSVal(0));
+ State = removeIteratorPosition(State, *Arg);
}
C.addTransition(State);
return;
@@ -213,11 +218,16 @@
// FIXME: Add a more conservative mode
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(),
- Pos->getContainer());
+ Optional<SVal> Arg = Call.getArgObject(i, C.blockCount());
+ if (!Arg.hasValue())
return;
- }
+
+ const auto *Pos = getIteratorPosition(State, *Arg);
+ if (!Pos)
+ return;
+
+ assignToContainer(C, OrigExpr, *RetVal, Pos->getContainer());
+ return;
}
}
}
@@ -225,6 +235,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,17 +253,6 @@
}
}
-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 {
// Keep symbolic expressions of iterator positions alive
@@ -256,8 +260,9 @@
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)) {
SR.markLive(*i);
+ }
}
auto SymbolMap = State->get<IteratorSymbolMap>();
@@ -278,12 +283,7 @@
auto RegionMap = State->get<IteratorRegionMap>();
for (const auto &Reg : RegionMap) {
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);
- }
+ State = State->remove<IteratorRegionMap>(Reg.first);
}
}
@@ -301,61 +301,83 @@
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;
- }
+ if (isSimpleComparisonOperator(Op)) {
+ const auto *OrigExpr = Call.getOriginExpr();
+ if (!OrigExpr)
+ return;
- handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
- Call.getArgSVal(1), Op);
+ Optional<SVal> Arg0 = Call.getArgObject(0, C.blockCount());
+ if (!Arg0.hasValue())
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;
- }
- }
- } else if (isIncrementOperator(Op)) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
- Call.getNumArgs());
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleComparison(C, OrigExpr, Call.getReturnValue(),
+ InstCall->getCXXThisVal(), *Arg0, Op);
+ return;
+ }
+
+ Optional<SVal> Arg1 = Call.getArgObject(0, C.blockCount());
+ if (!Arg1.hasValue())
+ return;
+
+ handleComparison(C, OrigExpr, Call.getReturnValue(), *Arg0, *Arg1, 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,
+ Call.getReturnObject(C.blockCount()),
+ InstCall->getCXXThisVal(),
+ Call.getArgSVal(0));
return;
}
+ } else {
+ if (Call.getNumArgs() >= 2 &&
+ Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
+ Optional<SVal> Arg0 = Call.getArgObject(0, C.blockCount());
+ if (!Arg0.hasValue())
+ return;
- handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
- Call.getNumArgs());
- return;
- } else if (isDecrementOperator(Op)) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
- Call.getNumArgs());
+ handleRandomIncrOrDecr(C, OrigExpr, Op,
+ Call.getReturnObject(C.blockCount()), *Arg0,
+ Call.getArgSVal(1));
return;
}
+ }
+ } else if (isIncrementOperator(Op)) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleIncrement(C, Call.getReturnObject(C.blockCount()),
+ InstCall->getCXXThisVal(), Call.getNumArgs());
+ return;
+ }
- handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
- Call.getNumArgs());
+ Optional<SVal> Arg0 = Call.getArgObject(0, C.blockCount());
+ if (!Arg0.hasValue())
+ return;
+
+ handleIncrement(C, Call.getReturnObject(C.blockCount()), *Arg0,
+ Call.getNumArgs());
+ return;
+ } else if (isDecrementOperator(Op)) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleDecrement(C, Call.getReturnObject(C.blockCount()),
+ InstCall->getCXXThisVal(), Call.getNumArgs());
return;
}
+
+ Optional<SVal> Arg0 = Call.getArgObject(0, C.blockCount());
+ if (!Arg0.hasValue())
+ return;
+
+ handleDecrement(C, Call.getReturnObject(C.blockCount()), *Arg0,
+ Call.getNumArgs());
+ return;
+ }
}
void
@@ -363,9 +385,13 @@
const CallEvent &Call,
const Expr *OrigExpr,
const AdvanceFn *Handler) const {
- if (!C.wasInlined) {
- (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
- Call.getArgSVal(0), Call.getArgSVal(1));
+ Optional<SVal> Arg0 = Call.getArgObject(0, C.blockCount());
+ if (!Arg0.hasValue())
+ return;
+
+ if (!C.wasInlined) {
+ (this->**Handler)(C, OrigExpr, Call.getReturnObject(C.blockCount()), *Arg0,
+ Call.getArgSVal(1));
return;
}
@@ -374,23 +400,22 @@
const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier();
if (IdInfo) {
if (IdInfo->getName() == "advance") {
- if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) {
- (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
- Call.getArgSVal(0), Call.getArgSVal(1));
+ if (noChangeInAdvance(C, *Arg0, OrigExpr)) {
+ (this->**Handler)(C, OrigExpr, Call.getReturnObject(C.blockCount()),
+ *Arg0, Call.getArgSVal(1));
}
}
}
}
void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
- SVal RetVal, const SVal &LVal,
- const SVal &RVal,
- OverloadedOperatorKind Op) const {
+ SVal RetVal, SVal LVal, SVal RVal,
+ OverloadedOperatorKind Op) const {
// Record the operands and the operator of the comparison for the next
// evalAssume, if the result is a symbolic expression. If it is a concrete
// value (only one branch is possible), then transfer the state between
// the operands according to the operator and the result
- auto State = C.getState();
+ auto State = C.getState();
const auto *LPos = getIteratorPosition(State, LVal);
const auto *RPos = getIteratorPosition(State, RVal);
const MemRegion *Cont = nullptr;
@@ -437,7 +462,7 @@
void IteratorModeling::processComparison(CheckerContext &C,
ProgramStateRef State, SymbolRef Sym1,
- SymbolRef Sym2, const SVal &RetVal,
+ SymbolRef Sym2, SVal RetVal,
OverloadedOperatorKind Op) const {
if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
if ((State = relateSymbols(State, Sym1, Sym2,
@@ -465,8 +490,8 @@
}
}
-void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
- const SVal &Iter, bool Postfix) const {
+void IteratorModeling::handleIncrement(CheckerContext &C, Optional<SVal> RetVal,
+ SVal Iter, bool Postfix) const {
// Increment the symbolic expressions which represents the position of the
// iterator
auto State = C.getState();
@@ -487,12 +512,13 @@
"Iterator should have position after successful advancement");
State = setIteratorPosition(State, Iter, *NewPos);
- State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
+ if (RetVal.hasValue())
+ State = setIteratorPosition(State, *RetVal, Postfix ? *Pos : *NewPos);
C.addTransition(State);
}
-void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
- const SVal &Iter, bool Postfix) const {
+void IteratorModeling::handleDecrement(CheckerContext &C, Optional<SVal> RetVal,
+ SVal Iter, bool Postfix) const {
// Decrement the symbolic expressions which represents the position of the
// iterator
auto State = C.getState();
@@ -513,64 +539,69 @@
"Iterator should have position after successful advancement");
State = setIteratorPosition(State, Iter, *NewPos);
- State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos);
+ if (RetVal.hasValue())
+ State = setIteratorPosition(State, *RetVal, Postfix ? *Pos : *NewPos);
C.addTransition(State);
}
void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C,
const Expr *CE,
OverloadedOperatorKind Op,
- const SVal &RetVal,
- const SVal &LHS,
- const SVal &RHS) const {
+ Optional<SVal> RetVal, SVal Iter,
+ SVal Amount) const {
// Increment or decrement the symbolic expressions which represents the
// position of the iterator
auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, LHS);
+ const auto *Pos = getIteratorPosition(State, Iter);
if (!Pos)
return;
- const auto *value = &RHS;
- if (auto loc = RHS.getAs<Loc>()) {
- const auto val = State->getRawSVal(*loc);
- value = &val;
+ const auto *AmountVal = &Amount;
+ if (auto L = Amount.getAs<Loc>()) {
+ const auto AmountV = State->getRawSVal(*L);
+ AmountVal = &AmountV;
}
- auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
+ Optional<SVal> TgtVal =
+ (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iter : RetVal;
+ if (!TgtVal.hasValue())
+ return;
auto NewState =
- advancePosition(State, LHS, Op, *value);
+ advancePosition(State, Iter, Op, *AmountVal);
if (NewState) {
- const auto *NewPos = getIteratorPosition(NewState, LHS);
+ const auto *NewPos = getIteratorPosition(NewState, Iter);
assert(NewPos &&
"Iterator should have position after successful advancement");
- State = setIteratorPosition(NewState, TgtVal, *NewPos);
+ State = setIteratorPosition(NewState, *TgtVal, *NewPos);
C.addTransition(State);
} else {
- assignToContainer(C, CE, TgtVal, Pos->getContainer());
+ assignToContainer(C, CE, *TgtVal, Pos->getContainer());
}
}
void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE,
- SVal RetVal, SVal Iter,
+ Optional<SVal> RetVal, SVal Iter,
SVal Amount) const {
handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount);
}
void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE,
- SVal RetVal, SVal Iter, SVal Amount) const {
+ Optional<SVal> RetVal, SVal Iter,
+ SVal Amount) const {
handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount);
}
void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE,
- SVal RetVal, SVal Iter, SVal Amount) const {
+ Optional<SVal> RetVal, SVal Iter,
+ SVal Amount) const {
handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount);
}
void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
- const SVal &RetVal,
+ SVal RetVal,
const MemRegion *Cont) const {
Cont = Cont->getMostDerivedObjectRegion();
@@ -621,6 +652,7 @@
Pos.getContainer()->dumpToStream(Out);
Out<<" ; Offset == ";
Pos.getOffset()->dumpToStream(Out);
+ Out<<"\n";
}
for (const auto &Reg : RegionMap) {
@@ -631,6 +663,7 @@
Pos.getContainer()->dumpToStream(Out);
Out<<" ; Offset == ";
Pos.getOffset()->dumpToStream(Out);
+ Out<<"\n";
}
}
}
@@ -647,10 +680,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 +716,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
@@ -158,8 +158,6 @@
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;
}
@@ -171,10 +169,8 @@
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/InvalidatedIteratorChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp
@@ -60,7 +60,11 @@
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
verifyAccess(C, InstCall->getCXXThisVal());
} else {
- verifyAccess(C, Call.getArgSVal(0));
+ Optional<SVal> Arg0 = Call.getArgObject(0, C.blockCount());
+ if (!Arg0.hasValue())
+ return;
+
+ verifyAccess(C, *Arg0);
}
}
}
Index: clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
+++ clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
@@ -32,9 +32,9 @@
class ContainerModeling
: public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> {
- void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal,
+ void handleBegin(CheckerContext &C, const Expr *CE, Optional<SVal> RetVal,
SVal Cont) const;
- void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal,
+ void handleEnd(CheckerContext &C, const Expr *CE, Optional<SVal> RetVal,
SVal Cont) const;
void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr,
SVal OldCont = UndefinedVal()) const;
@@ -163,6 +163,7 @@
if (!Func)
return;
+ llvm::errs()<<"Container PostCall: "<<Func->getNameAsString()<<"\n";
if (Func->isOverloadedOperator()) {
const auto Op = Func->getOverloadedOperator();
if (Op == OO_Equal) {
@@ -179,6 +180,22 @@
}
} else {
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ const auto *OrigExpr = Call.getOriginExpr();
+ if (!OrigExpr)
+ return;
+
+ if (isBeginCall(Func)) {
+ handleBegin(C, OrigExpr, Call.getReturnObject(C.blockCount()),
+ InstCall->getCXXThisVal());
+ return;
+ }
+
+ if (isEndCall(Func)) {
+ handleEnd(C, OrigExpr, Call.getReturnObject(C.blockCount()),
+ InstCall->getCXXThisVal());
+ return;
+ }
+
const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call);
if (Handler0) {
(this->**Handler0)(C, InstCall->getCXXThisVal(),
@@ -186,32 +203,29 @@
return;
}
- const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call);
- if (Handler1) {
- (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
+ if (Call.getNumArgs() < 1)
return;
- }
- const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call);
- if (Handler2) {
- (this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0),
- Call.getArgSVal(1));
+ Optional<SVal> Arg0 = Call.getArgObject(0, C.blockCount());
+ if (!Arg0.hasValue())
+ return;
+
+ const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call);
+ if (Handler1) {
+ (this->**Handler1)(C, InstCall->getCXXThisVal(), *Arg0);
return;
}
- const auto *OrigExpr = Call.getOriginExpr();
- if (!OrigExpr)
+ if (Call.getNumArgs() < 2)
return;
- if (isBeginCall(Func)) {
- handleBegin(C, OrigExpr, Call.getReturnValue(),
- InstCall->getCXXThisVal());
+ Optional<SVal> Arg1 = Call.getArgObject(1, C.blockCount());
+ if (!Arg1.hasValue())
return;
- }
- if (isEndCall(Func)) {
- handleEnd(C, OrigExpr, Call.getReturnValue(),
- InstCall->getCXXThisVal());
+ const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call);
+ if (Handler2) {
+ (this->**Handler2)(C, InstCall->getCXXThisVal(), *Arg0, *Arg1);
return;
}
}
@@ -257,7 +271,7 @@
}
void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE,
- SVal RetVal, SVal Cont) const {
+ Optional<SVal> RetVal, SVal Cont) const {
const auto *ContReg = Cont.getAsRegion();
if (!ContReg)
return;
@@ -273,13 +287,16 @@
C.getLocationContext(), C.blockCount());
BeginSym = getContainerBegin(State, ContReg);
}
- State = setIteratorPosition(State, RetVal,
- IteratorPosition::getPosition(ContReg, BeginSym));
+
+ if (RetVal.hasValue())
+ State =
+ setIteratorPosition(State, *RetVal,
+ IteratorPosition::getPosition(ContReg, BeginSym));
C.addTransition(State);
}
void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE,
- SVal RetVal, SVal Cont) const {
+ Optional<SVal> RetVal, SVal Cont) const {
const auto *ContReg = Cont.getAsRegion();
if (!ContReg)
return;
@@ -295,8 +312,10 @@
C.getLocationContext(), C.blockCount());
EndSym = getContainerEnd(State, ContReg);
}
- State = setIteratorPosition(State, RetVal,
- IteratorPosition::getPosition(ContReg, EndSym));
+
+ if (RetVal.hasValue())
+ State = setIteratorPosition(State, *RetVal,
+ IteratorPosition::getPosition(ContReg, EndSym));
C.addTransition(State);
}
@@ -580,9 +599,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,11 @@
const CallEvent &Call,
const EvalCallOptions &CallOpts = {});
+ ///
+ static Optional<SVal> retrieveFromConstructionContext(
+ ProgramStateRef State, const LocationContext *LCtx,
+ const ConstructionContext *CC);
+
private:
ProgramStateRef finishArgumentConstruction(ProgramStateRef State,
const CallEvent &Call);
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,16 @@
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;
+
+ Optional<SVal> getReturnValueUnderConstruction(unsigned BlockCount) const;
+
+ Optional<SVal> getArgObject(unsigned Index, unsigned BlockCount) const;
+
+ Optional<SVal> getReturnObject(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