Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -726,12 +726,11 @@ static void printIndicesOfElementsToConstructJson( raw_ostream &Out, ProgramStateRef State, const char *NL, - const LocationContext *LCtx, const ASTContext &Context, - unsigned int Space = 0, bool IsDot = false) { + const LocationContext *LCtx, unsigned int Space = 0, bool IsDot = false) { using KeyT = std::pair; - PrintingPolicy PP = - LCtx->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); + const auto &Context = LCtx->getAnalysisDeclContext()->getASTContext(); + PrintingPolicy PP = Context.getPrintingPolicy(); ++Space; bool HasItem = false; @@ -787,40 +786,171 @@ } } -void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State, - const LocationContext *LCtx, const char *NL, - unsigned int Space, bool IsDot) const { - Indent(Out, Space, IsDot) << "\"constructing_objects\": "; +static void printPendingInitLoopJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, + const LocationContext *LCtx, + unsigned int Space = 0, + bool IsDot = false) { + using KeyT = std::pair; - if (LCtx && !State->get().isEmpty()) { - ++Space; - Out << '[' << NL; - LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { - printObjectsUnderConstructionJson(Out, State, NL, LC, Space, IsDot); - }); + const auto &Context = LCtx->getAnalysisDeclContext()->getASTContext(); + PrintingPolicy PP = Context.getPrintingPolicy(); - --Space; - Indent(Out, Space, IsDot) << "]," << NL; // End of "constructing_objects". - } else { - Out << "null," << NL; + ++Space; + bool HasItem = false; + + // Store the last key. + KeyT LastKey; + for (const auto &I : State->get()) { + const KeyT &Key = I.first; + if (Key.second != LCtx) + continue; + + if (!HasItem) { + Out << "[" << NL; + HasItem = true; + } + + LastKey = Key; } - Indent(Out, Space, IsDot) << "\"index_of_element\": "; - if (LCtx && !State->get().isEmpty()) { - ++Space; + for (const auto &I : State->get()) { + const KeyT &Key = I.first; + unsigned Value = I.second; + if (Key.second != LCtx) + continue; - auto &Context = getContext(); + Indent(Out, Space, IsDot) << "{ "; + + const CXXConstructExpr *E = Key.first; + Out << "\"stmt_id\": " << E->getID(Context); + + Out << ", \"kind\": null"; + Out << ", \"pretty\": "; + Out << "\"" << E->getStmtClassName() << " " + << E->getSourceRange().printToString(Context.getSourceManager()) << " '" + << QualType::getAsString(E->getType().split(), PP); + Out << "'\""; + + Out << ", \"value\": \"Size: " << Value << "\"}"; + + if (Key != LastKey) + Out << ','; + Out << NL; + } + + if (HasItem) + Indent(Out, --Space, IsDot) << ']'; // End of "location_context". + else { + Out << "null "; + } +} + +static void +printPendingArrayDestructionsJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, const LocationContext *LCtx, + unsigned int Space = 0, bool IsDot = false) { + using KeyT = const LocationContext *; + + ++Space; + bool HasItem = false; + + // Store the last key. + KeyT LastKey = nullptr; + for (const auto &I : State->get()) { + const KeyT &Key = I.first; + if (Key != LCtx) + continue; + + if (!HasItem) { + Out << "[" << NL; + HasItem = true; + } + + LastKey = Key; + } + + for (const auto &I : State->get()) { + const KeyT &Key = I.first; + PendingArrayDestructionData Value = I.second; + if (Key != LCtx) + continue; + + Indent(Out, Space, IsDot) << "{ "; + + Out << "\"stmt_id\": null"; + Out << ", \"kind\": null"; + Out << ", \"pretty\": \"Current index: " << Value.idx - 1 << "\""; + Out << ", \"value\": \"Size of the array: " << Value.size << "\" }"; + + if (Key != LastKey) + Out << ','; + Out << NL; + } + + if (HasItem) + Indent(Out, --Space, IsDot) << ']'; // End of "location_context". + else { + Out << "null "; + } +} + +/// A helper function to generalize program state trait printing. +/// The function invokes Printer as 'Printer(Out, State, NL, LC, Space, IsDot, +/// std::forward(args)...)'. \n One possible type for Printer is +/// 'void()(raw_ostream &, ProgramStateRef, const char *, const LocationContext +/// *, unsigned int, bool, ...)' \n \param Trait The state trait to be printed. +/// \param Printer A void function that prints Trait. +/// \param Args An additional parameter pack that is passed to Print upon +/// invocation. +template +static void printStateTraitWithLocationContextJson( + raw_ostream &Out, ProgramStateRef State, const LocationContext *LCtx, + const char *NL, unsigned int Space, bool IsDot, + const char *jsonPropertyName, Printer printer, Args &&...args) { + + using RequiredType = + void (*)(raw_ostream &, ProgramStateRef, const char *, + const LocationContext *, unsigned int, bool, Args &&...); + + // Try to do as much compile time checking as possible. + // FIXME: check for invocable instead of function? + static_assert(std::is_function>::value, + "Printer is not a function!"); + static_assert(std::is_convertible::value, + "Printer doesn't have the required type!"); + + Indent(Out, Space, IsDot) << "\"" << jsonPropertyName << "\": "; + if (LCtx && !State->get().isEmpty()) { + ++Space; Out << '[' << NL; LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { - printIndicesOfElementsToConstructJson(Out, State, NL, LC, Context, Space, - IsDot); + printer(Out, State, NL, LC, Space, IsDot, std::forward(args)...); }); --Space; - Indent(Out, Space, IsDot) << "]," << NL; // End of "index_of_element". + Indent(Out, Space, IsDot) << "]," << NL; // End of "jsonPropertyName". } else { Out << "null," << NL; } +} + +void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State, + const LocationContext *LCtx, const char *NL, + unsigned int Space, bool IsDot) const { + + printStateTraitWithLocationContextJson( + Out, State, LCtx, NL, Space, IsDot, "constructing_objects", + printObjectsUnderConstructionJson); + printStateTraitWithLocationContextJson( + Out, State, LCtx, NL, Space, IsDot, "index_of_element", + printIndicesOfElementsToConstructJson); + printStateTraitWithLocationContextJson( + Out, State, LCtx, NL, Space, IsDot, "pending_init_loops", + printPendingInitLoopJson); + printStateTraitWithLocationContextJson( + Out, State, LCtx, NL, Space, IsDot, "pending_destructors", + printPendingArrayDestructionsJson); getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space, IsDot); Index: clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot =================================================================== --- clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot +++ clang/test/Analysis/exploded-graph-rewriter/checker_messages.dot @@ -25,6 +25,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "environment": null, "checker_messages": [ { "checker": "alpha.core.FooChecker", "messages": [ Index: clang/test/Analysis/exploded-graph-rewriter/checker_messages_diff.dot =================================================================== --- clang/test/Analysis/exploded-graph-rewriter/checker_messages_diff.dot +++ clang/test/Analysis/exploded-graph-rewriter/checker_messages_diff.dot @@ -18,6 +18,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": [ { "checker": "FooChecker", "messages": [ "Foo: Bar" @@ -77,6 +79,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": [ { "checker": "FooChecker", "messages": [ "Foo: Bar", Index: clang/test/Analysis/exploded-graph-rewriter/constraints.dot =================================================================== --- clang/test/Analysis/exploded-graph-rewriter/constraints.dot +++ clang/test/Analysis/exploded-graph-rewriter/constraints.dot @@ -25,6 +25,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": null, "constraints": [ { "symbol": "reg_$0", "range": "{ [0, 0] }" } Index: clang/test/Analysis/exploded-graph-rewriter/constraints_diff.dot =================================================================== --- clang/test/Analysis/exploded-graph-rewriter/constraints_diff.dot +++ clang/test/Analysis/exploded-graph-rewriter/constraints_diff.dot @@ -18,6 +18,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": null, "constraints": [ { "symbol": "reg_$0", "range": "{ [0, 10] }" } @@ -57,6 +59,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": null, "constraints": [ { "symbol": "reg_$0", "range": "{ [0, 5] }" } @@ -86,6 +90,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": null } } Index: clang/test/Analysis/exploded-graph-rewriter/environment.dot =================================================================== --- clang/test/Analysis/exploded-graph-rewriter/environment.dot +++ clang/test/Analysis/exploded-graph-rewriter/environment.dot @@ -47,6 +47,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": null, "environment": { "pointer": "0x2", Index: clang/test/Analysis/exploded-graph-rewriter/environment_diff.dot =================================================================== --- clang/test/Analysis/exploded-graph-rewriter/environment_diff.dot +++ clang/test/Analysis/exploded-graph-rewriter/environment_diff.dot @@ -19,6 +19,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": null, "environment": { "pointer": "0x2", @@ -75,6 +77,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": null, "environment": { "pointer": "0x2", @@ -125,6 +129,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": null, "environment": { "pointer": "0x2", Index: clang/test/Analysis/exploded-graph-rewriter/store.dot =================================================================== --- clang/test/Analysis/exploded-graph-rewriter/store.dot +++ clang/test/Analysis/exploded-graph-rewriter/store.dot @@ -35,6 +35,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": null, "store": { "pointer": "0x2", Index: clang/test/Analysis/exploded-graph-rewriter/store_diff.dot =================================================================== --- clang/test/Analysis/exploded-graph-rewriter/store_diff.dot +++ clang/test/Analysis/exploded-graph-rewriter/store_diff.dot @@ -21,6 +21,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": null, "store": { "pointer": "0x2", @@ -76,6 +78,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": null, "store": { "pointer": "0x5", Index: clang/test/Analysis/exploded-graph-rewriter/topology.dot =================================================================== --- clang/test/Analysis/exploded-graph-rewriter/topology.dot +++ clang/test/Analysis/exploded-graph-rewriter/topology.dot @@ -25,6 +25,8 @@ "dynamic_types": null, "constructing_objects": null, "index_of_element": null, + "pending_init_loops": null, + "pending_destructors": null, "checker_messages": [ { "checker": "foo", "messages": ["bar"] } ], Index: clang/test/Analysis/expr-inspection.c =================================================================== --- clang/test/Analysis/expr-inspection.c +++ clang/test/Analysis/expr-inspection.c @@ -48,6 +48,8 @@ // CHECK-NEXT: "dynamic_casts": null, // CHECK-NEXT: "constructing_objects": null, // CHECK-NEXT: "index_of_element": null, +// CHECK-NEXT: "pending_init_loops": null, +// CHECK-NEXT: "pending_destructors": null, // CHECK-NEXT: "checker_messages": null // CHECK-NEXT: } Index: clang/utils/analyzer/exploded-graph-rewriter.py =================================================================== --- clang/utils/analyzer/exploded-graph-rewriter.py +++ clang/utils/analyzer/exploded-graph-rewriter.py @@ -271,6 +271,8 @@ 'dynamic_types': None, 'constructing_objects': None, 'index_of_element': None, + 'pending_init_loops': None, + 'pending_destructors': None, 'checker_messages': None } @@ -301,7 +303,15 @@ self.index_of_element = \ GenericEnvironment(json_ps['index_of_element']) \ if json_ps['index_of_element'] is not None else None - + + self.pending_init_loops = \ + GenericEnvironment(json_ps['pending_init_loops']) \ + if json_ps['pending_init_loops'] is not None else None + + self.pending_destructors = \ + GenericEnvironment(json_ps['pending_destructors']) \ + if json_ps['pending_destructors'] is not None else None + self.checker_messages = CheckerMessages(json_ps['checker_messages']) \ if json_ps['checker_messages'] is not None else None @@ -805,6 +815,12 @@ self.visit_environment_in_state('index_of_element', 'Indices Of Elements Under Construction', s, prev_s) + self.visit_environment_in_state('pending_init_loops', + 'Pending Array Init Loop Expressions', + s, prev_s) + self.visit_environment_in_state('pending_destructors', + 'Indices of Elements Under Destruction', + s, prev_s) self.visit_checker_messages_in_state(s, prev_s) def visit_node(self, node):