Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/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,22 +142,39 @@ 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 print(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); - } else { - const CXXCtorInitializer *I = getItem().getCXXCtorInitializer(); - OS << I->getAnyMember()->getNameAsString(); - } + Out << getItem().getIndex() << '\"'; + else + Out << "null"; + + // Pretty-print + Out << ", \"pretty\": \""; + + if (S) + S->printPretty(Out, Helper, PP); + else + Out << I->getAnyMember()->getNameAsString(); + + Out << '\"'; } void Profile(llvm::FoldingSetNodeID &ID) const { @@ -544,31 +562,53 @@ static void printObjectsUnderConstructionForContext(raw_ostream &Out, ProgramStateRef State, const char *NL, - const LocationContext *LC) { + const LocationContext *LCtx, + unsigned int Space = 0) { PrintingPolicy PP = - LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); - for (auto I : State->get()) { - ConstructedObjectKey Key = I.first; + LCtx->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); + + bool HasItem = false; + for (const auto &I : State->get()) { + const ConstructedObjectKey &Key = I.first; SVal Value = I.second; - if (Key.getLocationContext() != LC) + if (Key.getLocationContext() != LCtx) continue; + + if (!HasItem) { + Out << "[" << NL; + HasItem = true; + } + + Indent(Out, Space) << "{ "; Key.print(Out, nullptr, PP); - Out << " : " << Value << NL; + Out << ", \"value\": \"" << Value << "\" }," << NL; } + + if (HasItem) + Out << ']'; + else + Out << "null "; } void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, const LocationContext *LCtx, const char *NL, const char *Sep, unsigned int Space) const { - if (LCtx) { - if (!State->get().isEmpty()) { - Out << "Objects under construction:" << NL; - - LCtx->print(Out, NL, Sep, Space, [&](const LocationContext *LC) { - printObjectsUnderConstructionForContext(Out, State, NL, LC); - }); - } + Indent(Out, Space) + << "\"constructing_objects\":" + << " { \"title\": \"Objects under construction\", \"items\": "; + + ++Space; + if (LCtx && !State->get().isEmpty()) { + Out << '[' << NL; + LCtx->print(Out, NL, Sep, Space, [&](const LocationContext *LC) { + printObjectsUnderConstructionForContext(Out, State, NL, LC, Space); + }); + Out << ']'; + } else { + Out << "null "; } + --Space; + Indent(Out, Space) << "}," << NL; getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep); } Index: clang/test/Analysis/dump_egraph.cpp =================================================================== --- clang/test/Analysis/dump_egraph.cpp +++ clang/test/Analysis/dump_egraph.cpp @@ -16,7 +16,11 @@ 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\} +// TODO: Replace that with HTML output. + +// CHECK: \"constructing_objects\": \{ \"title\": \"Objects under construction\", \"items\": [\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;\n\", \"value\": \"&t\" + +// CHECK: \"constructing_objects\": \{ \"title\": \"Objects under construction\", \"items\": [\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\": \{ \"title\": \"Store\", \"items\": [\l \{ \"cluster\": \"t\", \"items\": [\l \{ \"kind\": \"Default\", \"offset\": 0, \"value\": \"conj_$3\{int, LC3, no stmt, #1\}\" Index: clang/test/Analysis/expr-inspection.c =================================================================== --- clang/test/Analysis/expr-inspection.c +++ clang/test/Analysis/expr-inspection.c @@ -37,3 +37,4 @@ // CHECK-NEXT: { "name": "reg_$0", "value": "{ [-2147483648, 13] }" }, // CHECK-NEXT: ]}, // CHECK-NEXT: "dynamic_types": { "title": "Dynamic types", "items": null }, +// CHECK-NEXT: "constructing_objects": { "title": "Objects under construction", "items": null },