aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core/ExprEngine.cpp')
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngine.cpp156
1 files changed, 148 insertions, 8 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 45af22de50ae..d8f56f2f8cff 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -185,6 +185,17 @@ typedef llvm::ImmutableMap<ConstructedObjectKey, SVal>
REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction,
ObjectsUnderConstructionMap)
+// This trait is responsible for storing the index of the element that is to be
+// constructed in the next iteration. As a result a CXXConstructExpr is only
+// stored if it is array type. Also the index is the index of the continous
+// memory region, which is important for multi-dimensional arrays. E.g:: int
+// arr[2][2]; assume arr[1][1] will be the next element under construction, so
+// the index is 3.
+typedef llvm::ImmutableMap<
+ std::pair<const CXXConstructExpr *, const LocationContext *>, unsigned>
+ IndexOfElementToConstructMap;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(IndexOfElementToConstruct,
+ IndexOfElementToConstructMap)
//===----------------------------------------------------------------------===//
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
@@ -441,16 +452,65 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
return State;
}
+ProgramStateRef ExprEngine::setIndexOfElementToConstruct(
+ ProgramStateRef State, const CXXConstructExpr *E,
+ const LocationContext *LCtx, unsigned Idx) {
+ auto Key = std::make_pair(E, LCtx->getStackFrame());
+
+ assert(!State->contains<IndexOfElementToConstruct>(Key) || Idx > 0);
+
+ return State->set<IndexOfElementToConstruct>(Key, Idx);
+}
+
+Optional<unsigned>
+ExprEngine::getIndexOfElementToConstruct(ProgramStateRef State,
+ const CXXConstructExpr *E,
+ const LocationContext *LCtx) {
+
+ return Optional<unsigned>::create(
+ State->get<IndexOfElementToConstruct>({E, LCtx->getStackFrame()}));
+}
+
+ProgramStateRef
+ExprEngine::removeIndexOfElementToConstruct(ProgramStateRef State,
+ const CXXConstructExpr *E,
+ const LocationContext *LCtx) {
+ auto Key = std::make_pair(E, LCtx->getStackFrame());
+
+ assert(E && State->contains<IndexOfElementToConstruct>(Key));
+ return State->remove<IndexOfElementToConstruct>(Key);
+}
+
ProgramStateRef
ExprEngine::addObjectUnderConstruction(ProgramStateRef State,
const ConstructionContextItem &Item,
const LocationContext *LC, SVal V) {
ConstructedObjectKey Key(Item, LC->getStackFrame());
+
+ const CXXConstructExpr *E = nullptr;
+
+ if (auto DS = dyn_cast_or_null<DeclStmt>(Item.getStmtOrNull())) {
+ if (auto VD = dyn_cast_or_null<VarDecl>(DS->getSingleDecl()))
+ E = dyn_cast<CXXConstructExpr>(VD->getInit());
+ }
+
+ if (!E && !Item.getStmtOrNull()) {
+ auto CtorInit = Item.getCXXCtorInitializer();
+ E = dyn_cast<CXXConstructExpr>(CtorInit->getInit());
+ }
+
// FIXME: Currently the state might already contain the marker due to
// incorrect handling of temporaries bound to default parameters.
- assert(!State->get<ObjectsUnderConstruction>(Key) ||
- Key.getItem().getKind() ==
- ConstructionContextItem::TemporaryDestructorKind);
+ // The state will already contain the marker if we construct elements
+ // in an array, as we visit the same statement multiple times before
+ // the array declaration. The marker is removed when we exit the
+ // constructor call.
+ assert((!State->get<ObjectsUnderConstruction>(Key) ||
+ Key.getItem().getKind() ==
+ ConstructionContextItem::TemporaryDestructorKind ||
+ State->contains<IndexOfElementToConstruct>({E, LC})) &&
+ "The object is already marked as `UnderConstruction`, when it's not "
+ "supposed to!");
return State->set<ObjectsUnderConstruction>(Key, V);
}
@@ -582,6 +642,69 @@ printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State,
}
}
+static void printIndicesOfElementsToConstructJson(
+ raw_ostream &Out, ProgramStateRef State, const char *NL,
+ const LocationContext *LCtx, const ASTContext &Context,
+ unsigned int Space = 0, bool IsDot = false) {
+ using KeyT = std::pair<const Expr *, const LocationContext *>;
+
+ PrintingPolicy PP =
+ LCtx->getAnalysisDeclContext()->getASTContext().getPrintingPolicy();
+
+ ++Space;
+ bool HasItem = false;
+
+ // Store the last key.
+ KeyT LastKey;
+ for (const auto &I : State->get<IndexOfElementToConstruct>()) {
+ const KeyT &Key = I.first;
+ if (Key.second != LCtx)
+ continue;
+
+ if (!HasItem) {
+ Out << "[" << NL;
+ HasItem = true;
+ }
+
+ LastKey = Key;
+ }
+
+ for (const auto &I : State->get<IndexOfElementToConstruct>()) {
+ const KeyT &Key = I.first;
+ unsigned Value = I.second;
+ if (Key.second != LCtx)
+ continue;
+
+ Indent(Out, Space, IsDot) << "{ ";
+
+ // Expr
+ const Expr *E = Key.first;
+ Out << "\"stmt_id\": " << E->getID(Context);
+
+ // Kind - hack to display the current index
+ Out << ", \"kind\": \"Cur: " << Value - 1 << "\"";
+
+ // Pretty-print
+ Out << ", \"pretty\": ";
+ Out << "\"" << E->getStmtClassName() << " "
+ << E->getSourceRange().printToString(Context.getSourceManager()) << " '"
+ << QualType::getAsString(E->getType().split(), PP);
+ Out << "'\"";
+
+ Out << ", \"value\": \"Next: " << Value << "\" }";
+
+ if (Key != LastKey)
+ Out << ',';
+ Out << NL;
+ }
+
+ if (HasItem)
+ Indent(Out, --Space, IsDot) << ']'; // End of "location_context".
+ else {
+ Out << "null ";
+ }
+}
+
void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State,
const LocationContext *LCtx, const char *NL,
unsigned int Space, bool IsDot) const {
@@ -600,6 +723,23 @@ void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State,
Out << "null," << NL;
}
+ Indent(Out, Space, IsDot) << "\"index_of_element\": ";
+ if (LCtx && !State->get<IndexOfElementToConstruct>().isEmpty()) {
+ ++Space;
+
+ auto &Context = getContext();
+ Out << '[' << NL;
+ LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) {
+ printIndicesOfElementsToConstructJson(Out, State, NL, LC, Context, Space,
+ IsDot);
+ });
+
+ --Space;
+ Indent(Out, Space, IsDot) << "]," << NL; // End of "index_of_element".
+ } else {
+ Out << "null," << NL;
+ }
+
getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space,
IsDot);
}
@@ -961,8 +1101,9 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
// This workaround will just run the first destructor (which will still
// invalidate the entire array).
EvalCallOptions CallOpts;
- Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType,
- CallOpts.IsArrayCtorOrDtor).getAsRegion();
+ Region = makeElementRegion(state, loc::MemRegionVal(Region), varType,
+ CallOpts.IsArrayCtorOrDtor)
+ .getAsRegion();
VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(),
/*IsBase=*/false, Pred, Dst, CallOpts);
@@ -1045,8 +1186,7 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
// This workaround will just run the first destructor (which will still
// invalidate the entire array).
EvalCallOptions CallOpts;
- FieldVal = makeZeroElementRegion(State, FieldVal, T,
- CallOpts.IsArrayCtorOrDtor);
+ FieldVal = makeElementRegion(State, FieldVal, T, CallOpts.IsArrayCtorOrDtor);
VisitCXXDestructor(T, FieldVal.getAsRegion(), CurDtor->getBody(),
/*IsBase=*/false, Pred, Dst, CallOpts);
@@ -1105,7 +1245,7 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
CallOpts.IsArrayCtorOrDtor = true;
}
} else {
- // We'd eventually need to makeZeroElementRegion() trick here,
+ // We'd eventually need to makeElementRegion() trick here,
// but for now we don't have the respective construction contexts,
// so MR would always be null in this case. Do nothing for now.
}