1 //===--- PlistReporter.cpp - ARC Migrate Tool Plist Reporter ----*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "Internals.h"
11 #include "clang/Basic/FileManager.h"
12 #include "clang/Basic/PlistSupport.h"
13 #include "clang/Basic/SourceManager.h"
14 #include "clang/Lex/Lexer.h"
15 using namespace clang;
16 using namespace arcmt;
17 using namespace markup;
18 
19 void arcmt::writeARCDiagsToPlist(const std::string &outPath,
20                                  ArrayRef<StoredDiagnostic> diags,
21                                  SourceManager &SM,
22                                  const LangOptions &LangOpts) {
23   DiagnosticIDs DiagIDs;
24 
25   // Build up a set of FIDs that we use by scanning the locations and
26   // ranges of the diagnostics.
27   FIDMap FM;
28   SmallVector<FileID, 10> Fids;
29 
30   for (ArrayRef<StoredDiagnostic>::iterator
31          I = diags.begin(), E = diags.end(); I != E; ++I) {
32     const StoredDiagnostic &D = *I;
33 
34     AddFID(FM, Fids, SM, D.getLocation());
35 
36     for (StoredDiagnostic::range_iterator
37            RI = D.range_begin(), RE = D.range_end(); RI != RE; ++RI) {
38       AddFID(FM, Fids, SM, RI->getBegin());
39       AddFID(FM, Fids, SM, RI->getEnd());
40     }
41   }
42 
43   std::string errMsg;
44   llvm::raw_fd_ostream o(outPath.c_str(), errMsg, llvm::sys::fs::F_Text);
45   if (!errMsg.empty()) {
46     llvm::errs() << "error: could not create file: " << outPath << '\n';
47     return;
48   }
49 
50   // Write the plist header.
51   o << PlistHeader;
52 
53   // Write the root object: a <dict> containing...
54   //  - "files", an <array> mapping from FIDs to file names
55   //  - "diagnostics", an <array> containing the diagnostics
56   o << "<dict>\n"
57        " <key>files</key>\n"
58        " <array>\n";
59 
60   for (SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end();
61        I!=E; ++I) {
62     o << "  ";
63     EmitString(o, SM.getFileEntryForID(*I)->getName()) << '\n';
64   }
65 
66   o << " </array>\n"
67        " <key>diagnostics</key>\n"
68        " <array>\n";
69 
70   for (ArrayRef<StoredDiagnostic>::iterator
71          DI = diags.begin(), DE = diags.end(); DI != DE; ++DI) {
72 
73     const StoredDiagnostic &D = *DI;
74 
75     if (D.getLevel() == DiagnosticsEngine::Ignored)
76       continue;
77 
78     o << "  <dict>\n";
79 
80     // Output the diagnostic.
81     o << "   <key>description</key>";
82     EmitString(o, D.getMessage()) << '\n';
83     o << "   <key>category</key>";
84     EmitString(o, DiagIDs.getCategoryNameFromID(
85                           DiagIDs.getCategoryNumberForDiag(D.getID()))) << '\n';
86     o << "   <key>type</key>";
87     if (D.getLevel() >= DiagnosticsEngine::Error)
88       EmitString(o, "error") << '\n';
89     else if (D.getLevel() == DiagnosticsEngine::Warning)
90       EmitString(o, "warning") << '\n';
91     else
92       EmitString(o, "note") << '\n';
93 
94     // Output the location of the bug.
95     o << "  <key>location</key>\n";
96     EmitLocation(o, SM, LangOpts, D.getLocation(), FM, 2);
97 
98     // Output the ranges (if any).
99     StoredDiagnostic::range_iterator RI = D.range_begin(), RE = D.range_end();
100 
101     if (RI != RE) {
102       o << "   <key>ranges</key>\n";
103       o << "   <array>\n";
104       for (; RI != RE; ++RI)
105         EmitRange(o, SM, LangOpts, *RI, FM, 4);
106       o << "   </array>\n";
107     }
108 
109     // Close up the entry.
110     o << "  </dict>\n";
111   }
112 
113   o << " </array>\n";
114 
115   // Finish.
116   o << "</dict>\n</plist>";
117 }
118