Index: cfe/trunk/include/clang/StaticAnalyzer/Core/CheckerManager.h =================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ cfe/trunk/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -406,16 +406,21 @@ /// /// Unlike most other callbacks, any checker can simply implement the virtual /// method CheckerBase::printState if it has custom data to print. - /// \param Out The output stream + /// + /// \param Out The output stream /// \param State The state being printed - /// \param NL The preferred representation of a newline. - /// \param Sep The preferred separator between different kinds of data. - void runCheckersForPrintState(raw_ostream &Out, ProgramStateRef State, - const char *NL, const char *Sep); - -//===----------------------------------------------------------------------===// -// Internal registration functions for AST traversing. -//===----------------------------------------------------------------------===// + /// \param NL The preferred representation of a newline. + /// \param Sep The preferred separator between different messages. + /// \param Space The preferred space between the left side and the message. + /// \param IsDot Whether the message will be printed in 'dot' format. + void runCheckersForPrintStateJson(raw_ostream &Out, ProgramStateRef State, + const char *NL = "\n", + unsigned int Space = 0, + bool IsDot = false) const; + + //===----------------------------------------------------------------------===// + // Internal registration functions for AST traversing. + //===----------------------------------------------------------------------===// // Functions used by the registration mechanism, checkers should not touch // these directly. Index: cfe/trunk/lib/StaticAnalyzer/Core/CheckerManager.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -14,6 +14,7 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/Stmt.h" #include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/JsonSupport.h" #include "clang/Basic/LLVM.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -698,11 +699,73 @@ EndOfTranslationUnitChecker(TU, mgr, BR); } -void CheckerManager::runCheckersForPrintState(raw_ostream &Out, - ProgramStateRef State, - const char *NL, const char *Sep) { - for (const auto &CheckerTag : CheckerTags) - CheckerTag.second->printState(Out, State, NL, Sep); +void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out, + ProgramStateRef State, + const char *NL, + unsigned int Space, + bool IsDot) const { + Indent(Out, Space, IsDot) << "\"checker_messages\": "; + + // Create a temporary stream to see whether we have any message. + SmallString<1024> TempBuf; + llvm::raw_svector_ostream TempOut(TempBuf); + unsigned int InnerSpace = Space + 2; + + // Create the new-line in JSON with enough space. + SmallString<128> NewLine; + llvm::raw_svector_ostream NLOut(NewLine); + NLOut << "\", " << NL; // Inject the ending and a new line + Indent(NLOut, InnerSpace, IsDot) << "\""; // then begin the next message. + + ++Space; + bool HasMessage = false; + + // Store the last CheckerTag. + const void *LastCT = nullptr; + for (const auto &CT : CheckerTags) { + // See whether the current checker has a message. + CT.second->printState(TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/""); + + if (TempBuf.empty()) + continue; + + if (!HasMessage) { + Out << '[' << NL; + HasMessage = true; + } + + LastCT = &CT; + TempBuf.clear(); + } + + for (const auto &CT : CheckerTags) { + // See whether the current checker has a message. + CT.second->printState(TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/""); + + if (TempBuf.empty()) + continue; + + Indent(Out, Space, IsDot) + << "{ \"checker\": \"" << CT.second->getCheckName().getName() + << "\", \"messages\": [" << NL; + Indent(Out, InnerSpace, IsDot) + << '\"' << TempBuf.str().trim() << '\"' << NL; + Indent(Out, Space, IsDot) << "]}"; + + if (&CT != LastCT) + Out << ','; + Out << NL; + + TempBuf.clear(); + } + + // It is the last element of the 'program_state' so do not add a comma. + if (HasMessage) + Indent(Out, --Space, IsDot) << "]"; + else + Out << "null"; + + Out << NL; } //===----------------------------------------------------------------------===// Index: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -633,7 +633,8 @@ Out << "null," << NL; } - getCheckerManager().runCheckersForPrintState(Out, State, NL, ""); + getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space, + IsDot); } void ExprEngine::processEndWorklist() { Index: cfe/trunk/test/Analysis/expr-inspection.c =================================================================== --- cfe/trunk/test/Analysis/expr-inspection.c +++ cfe/trunk/test/Analysis/expr-inspection.c @@ -38,3 +38,5 @@ // CHECK-NEXT: ], // CHECK-NEXT: "dynamic_types": null, // CHECK-NEXT: "constructing_objects": null, +// CHECK-NEXT: "checker_messages": null + Index: cfe/trunk/test/Analysis/use-after-move.cpp =================================================================== --- cfe/trunk/test/Analysis/use-after-move.cpp +++ cfe/trunk/test/Analysis/use-after-move.cpp @@ -44,9 +44,18 @@ // CHECK-MOVE-INVALID-VALUE-SAME: "KnownsOnly", "KnownsAndLocals" or "All" // CHECK-MOVE-INVALID-VALUE-SAME: string value +// Tests checker-messages printing. +// RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.Move %s\ +// RUN: -std=c++11 -analyzer-output=text -analyzer-config eagerly-assume=false\ +// RUN: -analyzer-config exploration_strategy=dfs -DDFS\ +// RUN: -analyzer-config cplusplus.Move:WarnOn=All -DAGGRESSIVE_DFS\ +// RUN: -analyzer-checker core,cplusplus.SmartPtr,debug.ExprInspection\ +// RUN: -verify=expected,peaceful,aggressive %s 2>&1 | FileCheck %s + #include "Inputs/system-header-simulator-cxx.h" void clang_analyzer_warnIfReached(); +void clang_analyzer_printState(); class B { public: @@ -145,6 +154,19 @@ { A a; A b = std::move(a); // peaceful-note {{Object 'a' is moved}} + +#ifdef AGGRESSIVE_DFS + clang_analyzer_printState(); + +// CHECK: "checker_messages": [ +// CHECK-NEXT: { "checker": "cplusplus.Move", "messages": [ +// CHECK-NEXT: "Moved-from objects :", +// CHECK: "a: moved", +// CHECK: "" +// CHECK-NEXT: ]} +// CHECK-NEXT: ] +#endif + a.foo(); // peaceful-warning {{Method called on moved-from object 'a'}} // peaceful-note@-1 {{Method called on moved-from object 'a'}} }