r330766 - [analyzer] Add support for the note diagnostic pieces to plist output format.

Artem Dergachev via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 24 13:45:48 PDT 2018


Author: dergachev
Date: Tue Apr 24 13:45:48 2018
New Revision: 330766

URL: http://llvm.org/viewvc/llvm-project?rev=330766&view=rev
Log:
[analyzer] Add support for the note diagnostic pieces to plist output format.

Note diagnostic pieces are an additional way of highlighting code sections to
the user. They aren't part of the normal path diagnostic sequence. They can
also be attached to path-insensitive reports.

Notes are already supported by the text output and scan-build.

Expanding our machine-readable plist output format to be able to represent notes
opens up the possibility for various analyzer GUIs to pick them up.

Patch by Umann Kristóf!

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

Modified:
    cfe/trunk/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
    cfe/trunk/test/Analysis/copypaste/plist-diagnostics.cpp

Modified: cfe/trunk/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp?rev=330766&r1=330765&r2=330766&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp Tue Apr 24 13:45:48 2018
@@ -84,6 +84,41 @@ void ento::createPlistMultiFileDiagnosti
                                    PP.getLangOpts(), true));
 }
 
+static void EmitRanges(raw_ostream &o,
+                       const ArrayRef<SourceRange> Ranges,
+                       const FIDMap& FM,
+                       const SourceManager &SM,
+                       const LangOptions &LangOpts,
+                       unsigned indent) {
+
+  if (Ranges.empty())
+    return;
+
+  Indent(o, indent) << "<key>ranges</key>\n";
+  Indent(o, indent) << "<array>\n";
+  ++indent;
+  for (auto &R : Ranges)
+    EmitRange(o, SM,
+              Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
+              FM, indent + 1);
+  --indent;
+  Indent(o, indent) << "</array>\n";
+}
+
+static void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent) {
+  // Output the text.
+  assert(!Message.empty());
+  Indent(o, indent) << "<key>extended_message</key>\n";
+  Indent(o, indent);
+  EmitString(o, Message) << '\n';
+
+  // Output the short text.
+  // FIXME: Really use a short string.
+  Indent(o, indent) << "<key>message</key>\n";
+  Indent(o, indent);
+  EmitString(o, Message) << '\n';
+}
+
 static void ReportControlFlow(raw_ostream &o,
                               const PathDiagnosticControlFlowPiece& P,
                               const FIDMap& FM,
@@ -138,7 +173,7 @@ static void ReportControlFlow(raw_ostrea
   Indent(o, indent) << "</dict>\n";
 }
 
-static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
+static void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P,
                         const FIDMap& FM,
                         const SourceManager &SM,
                         const LangOptions &LangOpts,
@@ -163,34 +198,14 @@ static void ReportEvent(raw_ostream &o,
 
   // Output the ranges (if any).
   ArrayRef<SourceRange> Ranges = P.getRanges();
-
-  if (!Ranges.empty()) {
-    Indent(o, indent) << "<key>ranges</key>\n";
-    Indent(o, indent) << "<array>\n";
-    ++indent;
-    for (auto &R : Ranges)
-      EmitRange(o, SM,
-                Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
-                FM, indent + 1);
-    --indent;
-    Indent(o, indent) << "</array>\n";
-  }
+  EmitRanges(o, Ranges, FM, SM, LangOpts, indent);
 
   // Output the call depth.
   Indent(o, indent) << "<key>depth</key>";
   EmitInteger(o, depth) << '\n';
 
   // Output the text.
-  assert(!P.getString().empty());
-  Indent(o, indent) << "<key>extended_message</key>\n";
-  Indent(o, indent);
-  EmitString(o, P.getString()) << '\n';
-
-  // Output the short text.
-  // FIXME: Really use a short string.
-  Indent(o, indent) << "<key>message</key>\n";
-  Indent(o, indent);
-  EmitString(o, P.getString()) << '\n';
+  EmitMessage(o, P.getString(), indent);
 
   // Finish up.
   --indent;
@@ -246,6 +261,34 @@ static void ReportMacro(raw_ostream &o,
   }
 }
 
+static void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
+                        const FIDMap& FM,
+                        const SourceManager &SM,
+                        const LangOptions &LangOpts,
+                        unsigned indent,
+                        unsigned depth) {
+
+  Indent(o, indent) << "<dict>\n";
+  ++indent;
+
+  // Output the location.
+  FullSourceLoc L = P.getLocation().asLocation();
+
+  Indent(o, indent) << "<key>location</key>\n";
+  EmitLocation(o, SM, L, FM, indent);
+
+  // Output the ranges (if any).
+  ArrayRef<SourceRange> Ranges = P.getRanges();
+  EmitRanges(o, Ranges, FM, SM, LangOpts, indent);
+
+  // Output the text.
+  EmitMessage(o, P.getString(), indent);
+
+  // Finish up.
+  --indent;
+  Indent(o, indent); o << "</dict>\n";
+}
+
 static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P,
                        const FIDMap& FM, const SourceManager &SM,
                        const LangOptions &LangOpts) {
@@ -271,7 +314,7 @@ static void ReportPiece(raw_ostream &o,
                  indent, depth);
       break;
     case PathDiagnosticPiece::Event:
-      ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts,
+      ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts,
                   indent, depth, isKeyEvent);
       break;
     case PathDiagnosticPiece::Macro:
@@ -279,7 +322,8 @@ static void ReportPiece(raw_ostream &o,
                   indent, depth);
       break;
     case PathDiagnosticPiece::Note:
-      // FIXME: Extend the plist format to support those.
+      ReportNote(o, cast<PathDiagnosticNotePiece>(P), FM, SM, LangOpts,
+                  indent, depth);
       break;
   }
 }
@@ -364,15 +408,39 @@ void PlistDiagnostics::FlushDiagnosticsI
   for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
        DE = Diags.end(); DI!=DE; ++DI) {
 
-    o << "  <dict>\n"
-         "   <key>path</key>\n";
+    o << "  <dict>\n";
 
     const PathDiagnostic *D = *DI;
+    const PathPieces &PP = D->path;
+
+    assert(std::is_partitioned(
+             PP.begin(), PP.end(),
+             [](const std::shared_ptr<PathDiagnosticPiece> &E)
+               { return E->getKind() == PathDiagnosticPiece::Note; }) &&
+           "PathDiagnostic is not partitioned so that notes precede the rest");
+
+    PathPieces::const_iterator FirstNonNote = std::partition_point(
+        PP.begin(), PP.end(),
+        [](const std::shared_ptr<PathDiagnosticPiece> &E)
+          { return E->getKind() == PathDiagnosticPiece::Note; });
+
+    PathPieces::const_iterator I = PP.begin();
+
+    if (FirstNonNote != PP.begin()) {
+      o << "   <key>notes</key>\n"
+           "   <array>\n";
+
+      for (; I != FirstNonNote; ++I)
+        ReportDiag(o, **I, FM, *SM, LangOpts);
+
+      o << "   </array>\n";
+    }
+
+    o << "   <key>path</key>\n";
 
     o << "   <array>\n";
 
-    for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end();
-         I != E; ++I)
+    for (PathPieces::const_iterator E = PP.end(); I != E; ++I)
       ReportDiag(o, **I, FM, *SM, LangOpts);
 
     o << "   </array>\n";

Modified: cfe/trunk/test/Analysis/copypaste/plist-diagnostics.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/copypaste/plist-diagnostics.cpp?rev=330766&r1=330765&r2=330766&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/copypaste/plist-diagnostics.cpp (original)
+++ cfe/trunk/test/Analysis/copypaste/plist-diagnostics.cpp Tue Apr 24 13:45:48 2018
@@ -17,12 +17,39 @@ int maxClone(int a, int b) { // expected
   return b;
 }
 
-// FIXME: This plist output doesn't include the extra note on line 13.
-// It should be updated once the format for extra notes in plists is defined.
-
 // CHECK:  <key>diagnostics</key>
 // CHECK-NEXT:  <array>
 // CHECK-NEXT:   <dict>
+// CHECK-NEXT:    <key>notes</key>
+// CHECK-NEXT:    <array>
+// CHECK-NEXT:     <dict>
+// CHECK-NEXT:      <key>location</key>
+// CHECK-NEXT:      <dict>
+// CHECK-NEXT:       <key>line</key><integer>13</integer>
+// CHECK-NEXT:       <key>col</key><integer>28</integer>
+// CHECK-NEXT:       <key>file</key><integer>0</integer>
+// CHECK-NEXT:      </dict>
+// CHECK-NEXT:      <key>ranges</key>
+// CHECK-NEXT:      <array>
+// CHECK-NEXT:        <array>
+// CHECK-NEXT:         <dict>
+// CHECK-NEXT:          <key>line</key><integer>13</integer>
+// CHECK-NEXT:          <key>col</key><integer>28</integer>
+// CHECK-NEXT:          <key>file</key><integer>0</integer>
+// CHECK-NEXT:         </dict>
+// CHECK-NEXT:         <dict>
+// CHECK-NEXT:          <key>line</key><integer>18</integer>
+// CHECK-NEXT:          <key>col</key><integer>1</integer>
+// CHECK-NEXT:          <key>file</key><integer>0</integer>
+// CHECK-NEXT:         </dict>
+// CHECK-NEXT:        </array>
+// CHECK-NEXT:      </array>
+// CHECK-NEXT:      <key>extended_message</key>
+// CHECK-NEXT:      <string>Similar code here</string>
+// CHECK-NEXT:      <key>message</key>
+// CHECK-NEXT:      <string>Similar code here</string>
+// CHECK-NEXT:     </dict>
+// CHECK-NEXT:    </array>
 // CHECK-NEXT:    <key>path</key>
 // CHECK-NEXT:    <array>
 // CHECK-NEXT:     <dict>




More information about the cfe-commits mailing list