///
/// 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.
#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"
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;
}
//===----------------------------------------------------------------------===//
Out << "null," << NL;
}
- getCheckerManager().runCheckersForPrintState(Out, State, NL, "");
+ getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space,
+ IsDot);
}
void ExprEngine::processEndWorklist() {
// CHECK-NEXT: ],
// CHECK-NEXT: "dynamic_types": null,
// CHECK-NEXT: "constructing_objects": null,
+// CHECK-NEXT: "checker_messages": null
+
// 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:
{
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'}}
}