Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -693,7 +693,7 @@ continue; if (!HasItem) { - Out << "[" << NL; + Out << '[' << NL; HasItem = true; } @@ -724,12 +724,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; @@ -742,7 +741,7 @@ continue; if (!HasItem) { - Out << "[" << NL; + Out << '[' << NL; HasItem = true; } @@ -761,17 +760,17 @@ const Expr *E = Key.first; Out << "\"stmt_id\": " << E->getID(Context); - // Kind - hack to display the current index - Out << ", \"kind\": \"Cur: " << Value - 1 << "\""; + // Kind + Out << ", \"kind\": null"; // Pretty-print Out << ", \"pretty\": "; - Out << "\"" << E->getStmtClassName() << " " + Out << "\"" << E->getStmtClassName() << ' ' << E->getSourceRange().printToString(Context.getSourceManager()) << " '" << QualType::getAsString(E->getType().split(), PP); Out << "'\""; - Out << ", \"value\": \"Next: " << Value << "\" }"; + Out << ", \"value\": \"Current index: " << Value - 1 << "\" }"; if (Key != LastKey) Out << ','; @@ -785,40 +784,168 @@ } } -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; + + 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\": \"Flattened size: " << Value << "\"}"; + + if (Key != LastKey) + Out << ','; + Out << NL; + } - auto &Context = getContext(); + 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; + if (Key != LCtx) + continue; + + Indent(Out, Space, IsDot) << "{ "; + + Out << "\"stmt_id\": null"; + Out << ", \"kind\": null"; + Out << ", \"pretty\": \"Current index: \""; + Out << ", \"value\": \"" << I.second << "\" }"; + + 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!"); + + if (LCtx && !State->get().isEmpty()) { + Indent(Out, Space, IsDot) << '\"' << jsonPropertyName << "\": "; + ++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". - } else { - Out << "null," << NL; + Indent(Out, Space, IsDot) << "]," << NL; // End of "jsonPropertyName". } +} + +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); @@ -3714,7 +3841,7 @@ OtherNode->getLocation().printJson(Out, /*NL=*/"\\l"); Out << ", \"tag\": "; if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag()) - Out << '\"' << Tag->getTagDescription() << "\""; + Out << '\"' << Tag->getTagDescription() << '\"'; else Out << "null"; Out << ", \"node_id\": " << OtherNode->getID() << 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 @@ -22,9 +22,7 @@ "program_state": { "store": null, "constraints": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": 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 @@ -15,9 +15,7 @@ "environment": null, "store": null, "constraints": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": null, "checker_messages": [ { "checker": "FooChecker", "messages": [ "Foo: Bar" @@ -74,9 +72,7 @@ "environment": null, "store": null, "constraints": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": 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 @@ -22,9 +22,7 @@ "program_state": { "store": null, "environment": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": 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 @@ -15,9 +15,7 @@ "program_state": { "store": null, "environment": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": null, "checker_messages": null, "constraints": [ { "symbol": "reg_$0", "range": "{ [0, 10] }" } @@ -54,9 +52,7 @@ "program_state": { "store": null, "environment": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": null, "checker_messages": null, "constraints": [ { "symbol": "reg_$0", "range": "{ [0, 5] }" } @@ -83,9 +79,7 @@ "store": null, "environment": null, "constraints": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": 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 @@ -44,9 +44,7 @@ "program_state": { "store": null, "constraints": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": 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 @@ -16,9 +16,7 @@ "program_state": { "store": null, "constraints": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": null, "checker_messages": null, "environment": { "pointer": "0x2", @@ -72,9 +70,7 @@ "program_state": { "store": null, "constraints": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": null, "checker_messages": null, "environment": { "pointer": "0x2", @@ -122,9 +118,7 @@ "program_state": { "store": null, "constraints": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": 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 @@ -32,9 +32,7 @@ "program_state": { "environment": null, "constraints": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": 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 @@ -18,9 +18,7 @@ "program_state": { "environment": null, "constraints": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": null, "checker_messages": null, "store": { "pointer": "0x2", @@ -73,9 +71,7 @@ "program_state": { "environment": null, "constraints": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": 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 @@ -22,9 +22,7 @@ "program_state": { "environment": null, "constraints": null, - "dynamic_types": null, - "constructing_objects": null, - "index_of_element": null, + "dynamic_types": 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 @@ -46,8 +46,6 @@ // CHECK-NEXT: "disequality_info": null, // CHECK-NEXT: "dynamic_types": null, // CHECK-NEXT: "dynamic_casts": null, -// CHECK-NEXT: "constructing_objects": null, -// CHECK-NEXT: "index_of_element": 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 @@ -262,48 +262,74 @@ class ProgramState: def __init__(self, state_id, json_ps): logging.debug('Adding ProgramState ' + str(state_id)) + + store_key = 'store' + env_key = 'environment' + constraints_key = 'constraints' + dyn_ty_key = 'dynamic_types' + ctor_key = 'constructing_objects' + ind_key = 'index_of_element' + init_loop_key = 'pending_init_loops' + dtor_key = 'pending_destructors' + msg_key = 'checker_messages' if json_ps is None: json_ps = { - 'store': None, - 'environment': None, - 'constraints': None, - 'dynamic_types': None, - 'constructing_objects': None, - 'index_of_element': None, - 'checker_messages': None + store_key: None, + env_key: None, + constraints_key: None, + dyn_ty_key: None, + ctor_key: None, + ind_key: None, + init_loop_key: None, + dtor_key: None, + msg_key: None } self.state_id = state_id - self.store = Store(json_ps['store']) \ - if json_ps['store'] is not None else None + self.store = Store(json_ps[store_key]) \ + if json_ps[store_key] is not None else None self.environment = \ - GenericEnvironment(json_ps['environment']['items']) \ - if json_ps['environment'] is not None else None + GenericEnvironment(json_ps[env_key]['items']) \ + if json_ps[env_key] is not None else None self.constraints = GenericMap([ - (c['symbol'], c['range']) for c in json_ps['constraints'] - ]) if json_ps['constraints'] is not None else None + (c['symbol'], c['range']) for c in json_ps[constraints_key] + ]) if json_ps[constraints_key] is not None else None self.dynamic_types = GenericMap([ (t['region'], '%s%s' % (t['dyn_type'], ' (or a sub-class)' if t['sub_classable'] else '')) - for t in json_ps['dynamic_types']]) \ - if json_ps['dynamic_types'] is not None else None + for t in json_ps[dyn_ty_key]]) \ + if json_ps[dyn_ty_key] is not None else None + + self.checker_messages = CheckerMessages(json_ps[msg_key]) \ + if json_ps[msg_key] is not None else None + + # State traits + # + # For traits we always check if a key exists because if a trait + # has no imformation, nothing will be printed in the .dot file + # we parse. self.constructing_objects = \ - GenericEnvironment(json_ps['constructing_objects']) \ - if json_ps['constructing_objects'] is not None else None + GenericEnvironment(json_ps[ctor_key]) \ + if ctor_key in json_ps and json_ps[ctor_key] is not None else None self.index_of_element = \ - GenericEnvironment(json_ps['index_of_element']) \ - if json_ps['index_of_element'] is not None else None + GenericEnvironment(json_ps[ind_key]) \ + if ind_key in json_ps and json_ps[ind_key] is not None else None + + self.pending_init_loops = \ + GenericEnvironment(json_ps[init_loop_key]) \ + if init_loop_key in json_ps and json_ps[init_loop_key] is not None else None - self.checker_messages = CheckerMessages(json_ps['checker_messages']) \ - if json_ps['checker_messages'] is not None else None + self.pending_destructors = \ + GenericEnvironment(json_ps[dtor_key]) \ + if dtor_key in json_ps and json_ps[dtor_key] is not None else None # A deserialized exploded graph node. Has a default constructor because it @@ -805,6 +831,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):