Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -376,10 +376,10 @@ const LocationContext *LCtx, const CallEvent *Call) override; - /// printState - Called by ProgramStateManager to print checker-specific data. - void printState(raw_ostream &Out, ProgramStateRef State, - const LocationContext *LCtx, const char *NL, - unsigned int Space, bool IsDot) const override; + /// printJson - Called by ProgramStateManager to print checker-specific data. + void printJson(raw_ostream &Out, ProgramStateRef State, + const LocationContext *LCtx, const char *NL, + unsigned int Space, bool IsDot) const override; ProgramStateManager &getStateManager() override { return StateMgr; } Index: include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h @@ -158,10 +158,10 @@ const CallEvent *Call, RegionAndSymbolInvalidationTraits &HTraits) = 0; - /// printState - Called by ProgramStateManager to print checker-specific data. - virtual void printState(raw_ostream &Out, ProgramStateRef State, - const LocationContext *LCtx, const char *NL, - unsigned int Space, bool IsDot) const = 0; + /// printJson - Called by ProgramStateManager to print checker-specific data. + virtual void printJson(raw_ostream &Out, ProgramStateRef State, + const LocationContext *LCtx, const char *NL, + unsigned int Space, bool IsDot) const = 0; /// Called by CoreEngine when the analysis worklist is either empty or the // maximum number of analysis steps have been reached. Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -33,6 +33,7 @@ #include "clang/Analysis/ConstructionContext.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/JsonSupport.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/PrettyStackTrace.h" @@ -141,21 +142,47 @@ return getLocationContext()->getDecl()->getASTContext(); } - void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) { - OS << "(LC" << getLocationContext()->getID() << ','; - if (const Stmt *S = getItem().getStmtOrNull()) - OS << 'S' << S->getID(getASTContext()); + void printJson(llvm::raw_ostream &Out, PrinterHelper *Helper, + PrintingPolicy &PP) const { + const Stmt *S = getItem().getStmtOrNull(); + const CXXCtorInitializer *I = nullptr; + if (!S) + I = getItem().getCXXCtorInitializer(); + + // IDs + Out << "\"lctx_id\": " << getLocationContext()->getID() << ", "; + + if (S) + Out << "\"stmt_id\": " << S->getID(getASTContext()); else - OS << 'I' << getItem().getCXXCtorInitializer()->getID(getASTContext()); - OS << ',' << getItem().getKindAsString(); + Out << "\"init_id\": " << I->getID(getASTContext()); + + // Kind + Out << ", \"kind\": \"" << getItem().getKindAsString() + << "\", \"argument_index\": "; + if (getItem().getKind() == ConstructionContextItem::ArgumentKind) - OS << " #" << getItem().getIndex(); - OS << ") "; - if (const Stmt *S = getItem().getStmtOrNull()) { - S->printPretty(OS, Helper, PP); + Out << getItem().getIndex() << '\"'; + else + Out << "null"; + + // Pretty-print + Out << ", \"pretty\": \""; + + if (S) { + llvm::SmallString<256> TempBuf; + llvm::raw_svector_ostream TempOut(TempBuf); + + // See whether the current statement is pretty-printable. + S->printPretty(TempOut, Helper, PP); + if (!TempBuf.empty()) { + Out << TempBuf.str().trim() << '\"'; + TempBuf.clear(); + } else { + Out << "null"; + } } else { - const CXXCtorInitializer *I = getItem().getCXXCtorInitializer(); - OS << I->getAnyMember()->getNameAsString(); + Out << I->getAnyMember()->getNameAsString() << '\"'; } } @@ -541,33 +568,69 @@ LCtx, Call); } -static void printObjectsUnderConstructionForContext(raw_ostream &Out, - ProgramStateRef State, - const char *NL, - const LocationContext *LC) { +static void +printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, const LocationContext *LCtx, + unsigned int Space = 0, bool IsDot = false) { PrintingPolicy PP = - LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); - for (auto I : State->get()) { - ConstructedObjectKey Key = I.first; + LCtx->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); + + ++Space; + bool HasItem = false; + + // Store the last key. + const ConstructedObjectKey *LastKey = nullptr; + for (const auto &I : State->get()) { + const ConstructedObjectKey &Key = I.first; + if (Key.getLocationContext() != LCtx) + continue; + + if (!HasItem) { + Out << "[" << NL; + HasItem = true; + } + + LastKey = &Key; + } + + for (const auto &I : State->get()) { + const ConstructedObjectKey &Key = I.first; SVal Value = I.second; - if (Key.getLocationContext() != LC) + if (Key.getLocationContext() != LCtx) continue; - Key.print(Out, nullptr, PP); - Out << " : " << Value << NL; + + Indent(Out, Space, IsDot) << "{ "; + Key.printJson(Out, nullptr, PP); + Out << ", \"value\": \"" << Value << "\" }"; + + if (&Key != LastKey) + Out << ','; + Out << NL; + } + + if (HasItem) + Indent(Out, --Space, IsDot) << ']'; // End of "location_context". + else { + Out << "null "; } } -void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, - const LocationContext *LCtx, const char *NL, - unsigned int Space, bool IsDot) const { - if (LCtx) { - if (!State->get().isEmpty()) { - Out << "Objects under construction:" << NL; +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\": "; - LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { - printObjectsUnderConstructionForContext(Out, State, NL, LC); - }); - } + if (LCtx && !State->get().isEmpty()) { + ++Space; + Out << '[' << NL; + LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { + printObjectsUnderConstructionJson(Out, State, NL, LC, Space, IsDot); + }); + + --Space; + Indent(Out, Space, IsDot) << "]," << NL; // End of "constructing_objects". + } else { + Out << "null," << NL; } getCheckerManager().runCheckersForPrintState(Out, State, NL, ""); Index: lib/StaticAnalyzer/Core/ProgramState.cpp =================================================================== --- lib/StaticAnalyzer/Core/ProgramState.cpp +++ lib/StaticAnalyzer/Core/ProgramState.cpp @@ -458,7 +458,7 @@ printDynamicTypeInfoJson(Out, this, NL, Space, IsDot); // Print checker-specific data. - Mgr.getOwningEngine().printState(Out, this, LCtx, NL, Space, IsDot); + Mgr.getOwningEngine().printJson(Out, this, LCtx, NL, Space, IsDot); } void ProgramState::printDOT(raw_ostream &Out, const LocationContext *LCtx, Index: test/Analysis/dump_egraph.cpp =================================================================== --- test/Analysis/dump_egraph.cpp +++ test/Analysis/dump_egraph.cpp @@ -16,7 +16,9 @@ T t; } -// CHECK: (LC1,S{{[0-9]*}},construct into local variable) T t;\n : &t -// CHECK: (LC2,I{{[0-9]*}},construct into member variable) s : &t-\>s -// CHECK: conj_$5\{int, LC3, no stmt, #1\} +// CHECK: \"constructing_objects\": [\l  \{ \"location_context\": \"#0 Call\", \"calling\": \"foo\", \"call_line\": null, \"items\": [\l    \{ \"lctx_id\": 1, \"stmt_id\": 1155, \"kind\": \"construct into local variable\", \"argument_index\": null, \"pretty\": \"T t;\" + +// CHECK: \"constructing_objects\": [\l  \{ \"location_context\": \"#0 Call\", \"calling\": \"T::T\", \"call_line\": \"16\", \"items\": [\l    \{ \"lctx_id\": 2, \"init_id\": 1092, \"kind\": \"construct into member variable\", \"argument_index\": null, \"pretty\": \"s\", \"value\": \"&t-\>s\" + +// CHECK: \"store\": [\l  \{ \"cluster\": \"t\", \"items\": [\l    \{ \"kind\": \"Default\", \"offset\": 0, \"value\": \"conj_$3\{int, LC3, no stmt, #1\}\" Index: test/Analysis/expr-inspection.c =================================================================== --- test/Analysis/expr-inspection.c +++ test/Analysis/expr-inspection.c @@ -37,3 +37,4 @@ // CHECK-NEXT: { "symbol": "reg_$0", "range": "{ [-2147483648, 13] }" } // CHECK-NEXT: ], // CHECK-NEXT: "dynamic_types": null, +// CHECK-NEXT: "constructing_objects": null,