Index: clang/include/clang/Analysis/AnalysisDeclContext.h =================================================================== --- clang/include/clang/Analysis/AnalysisDeclContext.h +++ clang/include/clang/Analysis/AnalysisDeclContext.h @@ -274,11 +274,17 @@ virtual void Profile(llvm::FoldingSetNodeID &ID) = 0; void dumpStack( - raw_ostream &OS, StringRef Indent = {}, const char *NL = "\n", - const char *Sep = "", + raw_ostream &Out, const char *NL = "\n", std::function printMoreInfoPerContext = [](const LocationContext *) {}) const; - void dumpStack() const; + + void print( + raw_ostream &Out, const char *NL = "\n", unsigned int Space = 0, + bool IsDot = false, + std::function printMoreInfoPerContext = + [](const LocationContext *) {}) const; + + void dump() const; public: static void ProfileCommon(llvm::FoldingSetNodeID &ID, Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h @@ -91,9 +91,9 @@ return ExprBindings == RHS.ExprBindings; } - void print(raw_ostream &Out, const char *NL, const char *Sep, - const ASTContext &Context, - const LocationContext *WithLC = nullptr) const; + void print(raw_ostream &Out, const ASTContext &Ctx, + const LocationContext *LCtx = nullptr, const char *NL = "\n", + unsigned int Space = 0, bool IsDot = false) const; }; class EnvironmentManager { Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -379,9 +379,9 @@ const CallEvent *Call) override; /// printState - Called by ProgramStateManager to print checker-specific data. - void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, - const char *Sep, - const LocationContext *LCtx = nullptr) override; + void printState(raw_ostream &Out, ProgramStateRef State, + const LocationContext *LCtx, const char *NL, + unsigned int Space, bool IsDot) const override; ProgramStateManager &getStateManager() override { return StateMgr; } Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h @@ -160,8 +160,8 @@ /// printState - Called by ProgramStateManager to print checker-specific data. virtual void printState(raw_ostream &Out, ProgramStateRef State, - const char *NL, const char *Sep, - const LocationContext *LCtx = nullptr) = 0; + 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: clang/lib/Analysis/AnalysisDeclContext.cpp =================================================================== --- clang/lib/Analysis/AnalysisDeclContext.cpp +++ clang/lib/Analysis/AnalysisDeclContext.cpp @@ -30,6 +30,7 @@ #include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" #include "clang/Analysis/Support/BumpVector.h" +#include "clang/Basic/JsonSupport.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" @@ -462,17 +463,17 @@ return false; } -static void printLocation(raw_ostream &OS, const SourceManager &SM, - SourceLocation SLoc) { - if (SLoc.isFileID() && SM.isInMainFile(SLoc)) - OS << "line " << SM.getExpansionLineNumber(SLoc); +static void printLocation(raw_ostream &Out, const SourceManager &SM, + SourceLocation Loc) { + if (Loc.isFileID() && SM.isInMainFile(Loc)) + Out << SM.getExpansionLineNumber(Loc); else - SLoc.print(OS, SM); + Loc.print(Out, SM); } -void LocationContext::dumpStack( - raw_ostream &OS, StringRef Indent, const char *NL, const char *Sep, - std::function printMoreInfoPerContext) const { +void LocationContext::dumpStack(raw_ostream &Out, const char *NL, + std::function + printMoreInfoPerContext) const { ASTContext &Ctx = getAnalysisDeclContext()->getASTContext(); PrintingPolicy PP(Ctx.getLangOpts()); PP.TerseOutput = 1; @@ -484,38 +485,89 @@ for (const LocationContext *LCtx = this; LCtx; LCtx = LCtx->getParent()) { switch (LCtx->getKind()) { case StackFrame: - OS << Indent << '#' << Frame << ' '; + Out << "\t#" << Frame << ' '; ++Frame; if (const auto *D = dyn_cast(LCtx->getDecl())) - OS << "Calling " << D->getQualifiedNameAsString(); + Out << "Calling " << D->getQualifiedNameAsString(); else - OS << "Calling anonymous code"; + Out << "Calling anonymous code"; if (const Stmt *S = cast(LCtx)->getCallSite()) { - OS << " at "; - printLocation(OS, SM, S->getBeginLoc()); + Out << " at line "; + printLocation(Out, SM, S->getBeginLoc()); } break; case Scope: - OS << "Entering scope"; + Out << "Entering scope"; break; case Block: - OS << "Invoking block"; + Out << "Invoking block"; if (const Decl *D = cast(LCtx)->getDecl()) { - OS << " defined at "; - printLocation(OS, SM, D->getBeginLoc()); + Out << " defined at line "; + printLocation(Out, SM, D->getBeginLoc()); } break; } - OS << NL; + Out << NL; printMoreInfoPerContext(LCtx); } } -LLVM_DUMP_METHOD void LocationContext::dumpStack() const { - dumpStack(llvm::errs()); +void LocationContext::print(raw_ostream &Out, const char *NL, + unsigned int Space, bool IsDot, + std::function + printMoreInfoPerContext) const { + ASTContext &Ctx = getAnalysisDeclContext()->getASTContext(); + PrintingPolicy PP(Ctx.getLangOpts()); + PP.TerseOutput = 1; + + const SourceManager &SM = + getAnalysisDeclContext()->getASTContext().getSourceManager(); + + unsigned Frame = 0; + for (const LocationContext *LCtx = this; LCtx; LCtx = LCtx->getParent()) { + Indent(Out, Space, IsDot) << "{ \"location_context\": \""; + switch (LCtx->getKind()) { + case StackFrame: + Out << '#' << Frame << " Call\", \"calling\": \""; + ++Frame; + if (const auto *D = dyn_cast(LCtx->getDecl())) + Out << D->getQualifiedNameAsString(); + else + Out << "anonymous code"; + + Out << "\", \"call_line\": "; + if (const Stmt *S = cast(LCtx)->getCallSite()) { + Out << '\"'; + printLocation(Out, SM, S->getBeginLoc()); + Out << '\"'; + } else { + Out << "null"; + } + + Out << ", \"items\": "; + break; + case Scope: + Out << "Entering scope\""; + break; + case Block: + Out << "Invoking block\""; + if (const Decl *D = cast(LCtx)->getDecl()) { + Out << ", \"decl_line\": "; + printLocation(Out, SM, D->getBeginLoc()); + Out << ' '; + } + break; + } + + printMoreInfoPerContext(LCtx); + + Out << "}," << NL; + } } +LLVM_DUMP_METHOD void LocationContext::dump() const { print(llvm::errs()); } + //===----------------------------------------------------------------------===// // Lazily generated map to query the external variables referenced by a Block. //===----------------------------------------------------------------------===// Index: clang/lib/StaticAnalyzer/Core/Environment.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/Environment.cpp +++ clang/lib/StaticAnalyzer/Core/Environment.cpp @@ -18,6 +18,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/JsonSupport.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" @@ -199,43 +200,79 @@ return NewEnv; } -void Environment::print(raw_ostream &Out, const char *NL, - const char *Sep, - const ASTContext &Context, - const LocationContext *WithLC) const { - if (ExprBindings.isEmpty()) +void Environment::print(raw_ostream &Out, const ASTContext &Ctx, + const LocationContext *LCtx, const char *NL, + unsigned int Space, bool IsDot) const { + Indent(Out, Space, IsDot) << "\"environment\": "; + ++Space; + + if (ExprBindings.isEmpty()) { + Out << "null," << NL; return; + } - if (!WithLC) { + if (!LCtx) { // Find the freshest location context. llvm::SmallPtrSet FoundContexts; - for (auto I : *this) { + for (const auto &I : *this) { const LocationContext *LC = I.first.getLocationContext(); if (FoundContexts.count(LC) == 0) { // This context is fresher than all other contexts so far. - WithLC = LC; + LCtx = LC; for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent()) FoundContexts.insert(LCI); } } } - assert(WithLC); + assert(LCtx); + + // Environment items begin + Out << '[' << NL; - PrintingPolicy PP = Context.getPrintingPolicy(); + PrintingPolicy PP = Ctx.getPrintingPolicy(); - Out << NL << "Expressions by stack frame:" << NL; - WithLC->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { - for (auto I : ExprBindings) { + LCtx->print(Out, NL, Space, IsDot, [&](const LocationContext *LC) { + // LCtx items begin + bool HasItem = false; + unsigned int InnerSpace = Space + 1; + + llvm::SmallString<256> TempBuf; + llvm::raw_svector_ostream TempOut(TempBuf); + + for (const auto &I : ExprBindings) { if (I.first.getLocationContext() != LC) continue; const Stmt *S = I.first.getStmt(); assert(S != nullptr && "Expected non-null Stmt"); - Out << "(LC" << LC->getID() << ", S" << S->getID(Context) << ") "; - S->printPretty(Out, /*Helper=*/nullptr, PP); - Out << " : " << I.second << NL; + if (!HasItem) { + HasItem = true; + Out << '[' << NL; + } + + Indent(Out, InnerSpace, IsDot) + << "{ \"lctx_id\": " << LC->getID() + << ", \"stmt_id\": " << S->getID(Ctx) << ", \"pretty\": "; + + // See whether the current statement is pretty-printable. + S->printPretty(TempOut, /*Helper=*/nullptr, PP); + if (!TempBuf.empty()) { + Out << '\"' << TempBuf.str().trim() << '\"'; + TempBuf.clear(); + } else { + Out << "null"; + } + + Out << ", \"value\": \"" << I.second << "\" }," << NL; } + + if (HasItem) + Indent(Out, --InnerSpace, IsDot) << ']'; + else + Out << "null "; }); + + Indent(Out, --Space, IsDot) << "]," << NL; } Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -558,19 +558,19 @@ } void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, - const char *NL, const char *Sep, - const LocationContext *LCtx) { + const LocationContext *LCtx, const char *NL, + unsigned int Space, bool IsDot) const { if (LCtx) { if (!State->get().isEmpty()) { - Out << Sep << "Objects under construction:" << NL; + Out << "Objects under construction:" << NL; - LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { + LCtx->print(Out, NL, Space, IsDot, [&](const LocationContext *LC) { printObjectsUnderConstructionForContext(Out, State, NL, LC); }); } } - getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep); + getCheckerManager().runCheckersForPrintState(Out, State, NL, ""); } void ExprEngine::processEndWorklist() { Index: clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h =================================================================== --- clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h +++ clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h @@ -32,9 +32,9 @@ assert(LCtx); } - void print(raw_ostream &OS) const override { - OS << "While analyzing stack: \n"; - LCtx->dumpStack(OS, "\t"); + void print(raw_ostream &Out) const override { + Out << "While analyzing stack: \n"; + LCtx->dumpStack(Out); } }; Index: clang/lib/StaticAnalyzer/Core/ProgramState.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ProgramState.cpp +++ clang/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -449,7 +449,7 @@ Mgr.getStoreManager().print(Out, getStore(), NL, Space, IsDot); // Print out the environment. - Env.print(Out, NL, Sep, Context, LCtx); + Env.print(Out, Context, LCtx, NL, Space, IsDot); // Print out the constraints. Mgr.getConstraintManager().print(this, Out, NL, Sep); @@ -458,7 +458,7 @@ printDynamicTypeInfo(this, Out, NL, Sep); // Print checker-specific data. - Mgr.getOwningEngine().printState(Out, this, NL, Sep, LCtx); + Mgr.getOwningEngine().printState(Out, this, LCtx, NL, Space, IsDot); } void ProgramState::printDOT(raw_ostream &Out, const LocationContext *LCtx, Index: clang/test/Analysis/expr-inspection.c =================================================================== --- clang/test/Analysis/expr-inspection.c +++ clang/test/Analysis/expr-inspection.c @@ -28,10 +28,11 @@ // CHECK-NEXT: { "kind": "Direct", "offset": 0, "value": "2 S32b" }, // CHECK-NEXT: ]}, // CHECK-NEXT: ], - -// CHECK: Expressions by stack frame: -// CHECK-NEXT: #0 Calling foo -// CHECK-NEXT: (LC1, S847) clang_analyzer_printState : &code{clang_analyzer_printState} +// CHECK-NEXT: "environment": [ +// CHECK-NEXT: { "location_context": "#0 Call", "calling": "foo", "call_line": null, "items": [ +// CHECK-NEXT: { "lctx_id": 1, "stmt_id": 847, "pretty": "clang_analyzer_printState", "value": "&code{clang_analyzer_printState}" }, +// CHECK-NEXT: ]}, +// CHECK-NEXT: ], // CHECK: Ranges of symbol values: // CHECK-NEXT: reg_$0 : { [-2147483648, 13] }