[analyzer] print() JSONify: Checker messages implementation
authorCsaba Dabis <dabis.csaba98@gmail.com>
Wed, 29 May 2019 16:02:33 +0000 (16:02 +0000)
committerCsaba Dabis <dabis.csaba98@gmail.com>
Wed, 29 May 2019 16:02:33 +0000 (16:02 +0000)
Summary: -

Reviewers: NoQ, xazax.hun, ravikandhadai, baloghadamsoftware, Szelethus

Reviewed By: NoQ

Subscribers: szepet, rnkovacs, a.sidorin, mikhail.ramalho, donat.nagy,
             dkrupp

Tags: #clang

Differential Revision: https://reviews.llvm.org/D62086

llvm-svn: 361982

clang/include/clang/StaticAnalyzer/Core/CheckerManager.h
clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
clang/test/Analysis/expr-inspection.c
clang/test/Analysis/use-after-move.cpp

index 612286b..532b908 100644 (file)
@@ -406,16 +406,21 @@ public:
   ///
   /// 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 53d8720..cda9fe9 100644 (file)
@@ -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 @@ void CheckerManager::runCheckersOnEndOfTranslationUnit(
     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 07fc6f7..6fa7cf2 100644 (file)
@@ -633,7 +633,8 @@ void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State,
     Out << "null," << NL;
   }
 
-  getCheckerManager().runCheckersForPrintState(Out, State, NL, "");
+  getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space,
+                                                   IsDot);
 }
 
 void ExprEngine::processEndWorklist() {
index cf9c9f8..2837c30 100644 (file)
@@ -38,3 +38,5 @@ void foo(int x) {
 // CHECK-NEXT: ],
 // CHECK-NEXT: "dynamic_types": null,
 // CHECK-NEXT: "constructing_objects": null,
+// CHECK-NEXT: "checker_messages": null
+
index 5e4179b..ac4222b 100644 (file)
 // 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 @@ void simpleMoveCtorTest() {
   {
     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'}}
   }