blob: 1adc99cb6399d189f6cdcc1c801080d6f6471a8b [file] [log] [blame]
Ted Kremenek5d866252008-11-03 22:33:57 +00001//===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- 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// This file defines the PlistDiagnostics object.
11//
12//===----------------------------------------------------------------------===//
13
Daniel Dunbare1bd4e62009-03-02 06:16:29 +000014#include "clang/Frontend/PathDiagnosticClients.h"
Ted Kremenek5d866252008-11-03 22:33:57 +000015#include "clang/Analysis/PathDiagnostic.h"
16#include "clang/Basic/SourceManager.h"
17#include "clang/Basic/FileManager.h"
18#include "llvm/Support/Compiler.h"
19#include "llvm/Support/raw_ostream.h"
Ted Kremenek082cb8d2009-03-12 18:41:53 +000020#include "llvm/Support/Casting.h"
Ted Kremenek5d866252008-11-03 22:33:57 +000021#include "llvm/System/Path.h"
22#include "llvm/ADT/DenseMap.h"
23#include "llvm/ADT/SmallVector.h"
Ted Kremenek5d866252008-11-03 22:33:57 +000024using namespace clang;
Ted Kremenek082cb8d2009-03-12 18:41:53 +000025using llvm::cast;
Chris Lattner2b2453a2009-01-17 06:22:33 +000026
27typedef llvm::DenseMap<FileID, unsigned> FIDMap;
Ted Kremenek5d866252008-11-03 22:33:57 +000028
Ted Kremenek4fc82c82008-11-03 23:18:07 +000029namespace clang {
30 class Preprocessor;
31 class PreprocessorFactory;
32}
33
Ted Kremenek5d866252008-11-03 22:33:57 +000034namespace {
35 class VISIBILITY_HIDDEN PlistDiagnostics : public PathDiagnosticClient {
Ted Kremenekddf32da2009-01-21 00:42:24 +000036 std::vector<const PathDiagnostic*> BatchedDiags;
37 const std::string OutputFile;
Ted Kremenek5d866252008-11-03 22:33:57 +000038 public:
39 PlistDiagnostics(const std::string& prefix);
Ted Kremenekddf32da2009-01-21 00:42:24 +000040 ~PlistDiagnostics();
Ted Kremenek5d866252008-11-03 22:33:57 +000041 void HandlePathDiagnostic(const PathDiagnostic* D);
42 };
43} // end anonymous namespace
44
Ted Kremenekddf32da2009-01-21 00:42:24 +000045PlistDiagnostics::PlistDiagnostics(const std::string& output)
46 : OutputFile(output) {}
Ted Kremenek5d866252008-11-03 22:33:57 +000047
Ted Kremenek4fc82c82008-11-03 23:18:07 +000048PathDiagnosticClient*
49clang::CreatePlistDiagnosticClient(const std::string& s,
50 Preprocessor*, PreprocessorFactory*) {
Ted Kremenek5d866252008-11-03 22:33:57 +000051 return new PlistDiagnostics(s);
52}
53
Chris Lattnera11d6172009-01-19 07:46:45 +000054static void AddFID(FIDMap &FIDs, llvm::SmallVectorImpl<FileID> &V,
Ted Kremenekc472d792009-01-23 20:06:20 +000055 SourceManager* SM, SourceLocation L) {
Ted Kremenek5d866252008-11-03 22:33:57 +000056
Ted Kremenekc472d792009-01-23 20:06:20 +000057 FileID FID = SM->getFileID(SM->getInstantiationLoc(L));
Chris Lattner2b2453a2009-01-17 06:22:33 +000058 FIDMap::iterator I = FIDs.find(FID);
Ted Kremenek5d866252008-11-03 22:33:57 +000059 if (I != FIDs.end()) return;
Chris Lattner2b2453a2009-01-17 06:22:33 +000060 FIDs[FID] = V.size();
61 V.push_back(FID);
Ted Kremenek5d866252008-11-03 22:33:57 +000062}
63
Ted Kremenekc472d792009-01-23 20:06:20 +000064static unsigned GetFID(const FIDMap& FIDs, SourceManager* SM, SourceLocation L){
65 FileID FID = SM->getFileID(SM->getInstantiationLoc(L));
Chris Lattner2b2453a2009-01-17 06:22:33 +000066 FIDMap::const_iterator I = FIDs.find(FID);
67 assert(I != FIDs.end());
Ted Kremenek5d866252008-11-03 22:33:57 +000068 return I->second;
69}
70
71static llvm::raw_ostream& Indent(llvm::raw_ostream& o, const unsigned indent) {
Ted Kremenek082cb8d2009-03-12 18:41:53 +000072 for (unsigned i = 0; i < indent; ++i) o << ' ';
Ted Kremenek5d866252008-11-03 22:33:57 +000073 return o;
74}
75
Ted Kremenekc472d792009-01-23 20:06:20 +000076static void EmitLocation(llvm::raw_ostream& o, SourceManager* SM,
Ted Kremenek5d866252008-11-03 22:33:57 +000077 SourceLocation L, const FIDMap& FM,
78 const unsigned indent) {
79
80 Indent(o, indent) << "<dict>\n";
81 Indent(o, indent) << " <key>line</key><integer>"
Ted Kremenekc472d792009-01-23 20:06:20 +000082 << SM->getInstantiationLineNumber(L) << "</integer>\n";
Ted Kremenek5d866252008-11-03 22:33:57 +000083 Indent(o, indent) << " <key>col</key><integer>"
Ted Kremenekc472d792009-01-23 20:06:20 +000084 << SM->getInstantiationColumnNumber(L) << "</integer>\n";
Ted Kremenek5d866252008-11-03 22:33:57 +000085 Indent(o, indent) << " <key>file</key><integer>"
86 << GetFID(FM, SM, L) << "</integer>\n";
87 Indent(o, indent) << "</dict>\n";
88}
89
Ted Kremenekc472d792009-01-23 20:06:20 +000090static void EmitRange(llvm::raw_ostream& o, SourceManager* SM, SourceRange R,
Ted Kremenek5d866252008-11-03 22:33:57 +000091 const FIDMap& FM, const unsigned indent) {
92
93 Indent(o, indent) << "<array>\n";
94 EmitLocation(o, SM, R.getBegin(), FM, indent+1);
95 EmitLocation(o, SM, R.getEnd(), FM, indent+1);
96 Indent(o, indent) << "</array>\n";
97}
98
Ted Kremenek082cb8d2009-03-12 18:41:53 +000099static void ReportControlFlow(llvm::raw_ostream& o,
100 const PathDiagnosticControlFlowPiece& P,
101 const FIDMap& FM, SourceManager *SM,
102 unsigned indent) {
Ted Kremenek5d866252008-11-03 22:33:57 +0000103
Ted Kremenek5d866252008-11-03 22:33:57 +0000104 Indent(o, indent) << "<dict>\n";
105 ++indent;
106
Ted Kremenek082cb8d2009-03-12 18:41:53 +0000107 Indent(o, indent) << "<key>kind</key><string>control</string>\n";
108
109 // Output the start and end locations.
110 Indent(o, indent) << "<key>start</key>\n";
111 EmitLocation(o, SM, P.getStartLocation(), FM, indent);
112 Indent(o, indent) << "<key>end</key>\n";
113 EmitLocation(o, SM, P.getEndLocation(), FM, indent);
114
115 // Output any helper text.
116 const std::string& s = P.getString();
117 if (!s.empty()) {
118 Indent(o, indent) << "<key>alternate</key><string>" << s << "</string>\n";
119 }
120
121 --indent;
122 Indent(o, indent) << "</dict>\n";
123}
124
125static void ReportEvent(llvm::raw_ostream& o, const PathDiagnosticPiece& P,
126 const FIDMap& FM, SourceManager* SM, unsigned indent) {
127
128 Indent(o, indent) << "<dict>\n";
129 ++indent;
130
131 Indent(o, indent) << "<key>kind</key><string>event</string>\n";
132
Ted Kremenek5d866252008-11-03 22:33:57 +0000133 // Output the location.
134 FullSourceLoc L = P.getLocation();
Ted Kremenek082cb8d2009-03-12 18:41:53 +0000135
Ted Kremenek5d866252008-11-03 22:33:57 +0000136 Indent(o, indent) << "<key>location</key>\n";
Chris Lattner59ddeab2009-01-16 23:06:35 +0000137 EmitLocation(o, SM, L, FM, indent);
Ted Kremenek082cb8d2009-03-12 18:41:53 +0000138
Ted Kremenek5d866252008-11-03 22:33:57 +0000139 // Output the ranges (if any).
140 PathDiagnosticPiece::range_iterator RI = P.ranges_begin(),
Ted Kremenek082cb8d2009-03-12 18:41:53 +0000141 RE = P.ranges_end();
Ted Kremenek5d866252008-11-03 22:33:57 +0000142
143 if (RI != RE) {
144 Indent(o, indent) << "<key>ranges</key>\n";
145 Indent(o, indent) << "<array>\n";
Ted Kremenekd671c5a2009-02-02 21:45:32 +0000146 ++indent;
Ted Kremenek5d866252008-11-03 22:33:57 +0000147 for ( ; RI != RE; ++RI ) EmitRange(o, SM, *RI, FM, indent+1);
Ted Kremenekd671c5a2009-02-02 21:45:32 +0000148 --indent;
Ted Kremenek5d866252008-11-03 22:33:57 +0000149 Indent(o, indent) << "</array>\n";
150 }
151
152 // Output the text.
Ted Kremenek082cb8d2009-03-12 18:41:53 +0000153 assert(!P.getString().empty());
Ted Kremenek61dc71a2009-03-19 00:42:56 +0000154 Indent(o, indent) << "<key>extended_message</key>\n";
155 Indent(o, indent) << "<string>" << P.getString() << "</string>\n";
156
157 // Output the short text.
158 // FIXME: Really use a short string.
Ted Kremenek5d866252008-11-03 22:33:57 +0000159 Indent(o, indent) << "<key>message</key>\n";
Ted Kremenek4fc82c82008-11-03 23:18:07 +0000160 Indent(o, indent) << "<string>" << P.getString() << "</string>\n";
Ted Kremenek082cb8d2009-03-12 18:41:53 +0000161
Ted Kremenek5d866252008-11-03 22:33:57 +0000162 // Finish up.
163 --indent;
164 Indent(o, indent); o << "</dict>\n";
165}
166
Ted Kremenek082cb8d2009-03-12 18:41:53 +0000167static void ReportMacro(llvm::raw_ostream& o,
168 const PathDiagnosticMacroPiece& P,
169 const FIDMap& FM, SourceManager *SM,
170 unsigned indent) {
171
172 for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end();
173 I!=E; ++I) {
174
175 switch ((*I)->getKind()) {
176 default:
177 break;
178 case PathDiagnosticPiece::Event:
179 ReportEvent(o, cast<PathDiagnosticEventPiece>(**I), FM, SM, indent);
180 break;
181 case PathDiagnosticPiece::Macro:
182 ReportMacro(o, cast<PathDiagnosticMacroPiece>(**I), FM, SM, indent);
183 break;
184 }
185 }
186}
187
188static void ReportDiag(llvm::raw_ostream& o, const PathDiagnosticPiece& P,
189 const FIDMap& FM, SourceManager* SM) {
190
191 unsigned indent = 4;
192
193 switch (P.getKind()) {
194 case PathDiagnosticPiece::ControlFlow:
195 ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM,
196 indent);
197 break;
198 case PathDiagnosticPiece::Event:
199 ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, indent);
200 break;
201 case PathDiagnosticPiece::Macro:
202 ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, indent);
203 break;
204 }
205}
206
Ted Kremenek5d866252008-11-03 22:33:57 +0000207void PlistDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) {
Ted Kremenekddf32da2009-01-21 00:42:24 +0000208 if (!D)
209 return;
Ted Kremenek5d866252008-11-03 22:33:57 +0000210
Ted Kremenekddf32da2009-01-21 00:42:24 +0000211 if (D->empty()) {
212 delete D;
213 return;
Ted Kremenek5d866252008-11-03 22:33:57 +0000214 }
215
Ted Kremenekddf32da2009-01-21 00:42:24 +0000216 BatchedDiags.push_back(D);
217}
Ted Kremenek5d866252008-11-03 22:33:57 +0000218
Ted Kremenekddf32da2009-01-21 00:42:24 +0000219PlistDiagnostics::~PlistDiagnostics() {
Ted Kremenek5d866252008-11-03 22:33:57 +0000220
221 // Build up a set of FIDs that we use by scanning the locations and
222 // ranges of the diagnostics.
223 FIDMap FM;
Chris Lattner2b2453a2009-01-17 06:22:33 +0000224 llvm::SmallVector<FileID, 10> Fids;
Ted Kremenekc472d792009-01-23 20:06:20 +0000225 SourceManager* SM = 0;
226
227 if (!BatchedDiags.empty())
228 SM = &(*BatchedDiags.begin())->begin()->getLocation().getManager();
Ted Kremenek5d866252008-11-03 22:33:57 +0000229
Ted Kremenekddf32da2009-01-21 00:42:24 +0000230 for (std::vector<const PathDiagnostic*>::iterator DI = BatchedDiags.begin(),
231 DE = BatchedDiags.end(); DI != DE; ++DI) {
232
233 const PathDiagnostic *D = *DI;
234
235 for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I!=E; ++I) {
236 AddFID(FM, Fids, SM, I->getLocation());
237
238 for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(),
239 RE=I->ranges_end(); RI!=RE; ++RI) {
240 AddFID(FM, Fids, SM, RI->getBegin());
241 AddFID(FM, Fids, SM, RI->getEnd());
242 }
Ted Kremenek5d866252008-11-03 22:33:57 +0000243 }
244 }
245
Ted Kremenekddf32da2009-01-21 00:42:24 +0000246 // Open the file.
Ted Kremenek5d866252008-11-03 22:33:57 +0000247 std::string ErrMsg;
Ted Kremenekddf32da2009-01-21 00:42:24 +0000248 llvm::raw_fd_ostream o(OutputFile.c_str(), false, ErrMsg);
Ted Kremenek5d866252008-11-03 22:33:57 +0000249 if (!ErrMsg.empty()) {
Ted Kremenekddf32da2009-01-21 00:42:24 +0000250 llvm::errs() << "warning: could not creat file: " << OutputFile << '\n';
Ted Kremenek5d866252008-11-03 22:33:57 +0000251 return;
252 }
253
254 // Write the plist header.
255 o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
Ted Kremenekddf32da2009-01-21 00:42:24 +0000256 "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
257 "http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
258 "<plist version=\"1.0\">\n";
Ted Kremenek5d866252008-11-03 22:33:57 +0000259
260 // Write the root object: a <dict> containing...
261 // - "files", an <array> mapping from FIDs to file names
262 // - "diagnostics", an <array> containing the path diagnostics
263 o << "<dict>\n"
264 " <key>files</key>\n"
265 " <array>\n";
266
Chris Lattner2b2453a2009-01-17 06:22:33 +0000267 for (llvm::SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end();
Ted Kremenek5d866252008-11-03 22:33:57 +0000268 I!=E; ++I)
Ted Kremenekc472d792009-01-23 20:06:20 +0000269 o << " <string>" << SM->getFileEntryForID(*I)->getName() << "</string>\n";
Ted Kremenek5d866252008-11-03 22:33:57 +0000270
271 o << " </array>\n"
272 " <key>diagnostics</key>\n"
273 " <array>\n";
274
Ted Kremenekddf32da2009-01-21 00:42:24 +0000275 for (std::vector<const PathDiagnostic*>::iterator DI=BatchedDiags.begin(),
276 DE = BatchedDiags.end(); DI!=DE; ++DI) {
277
278 o << " <dict>\n"
279 " <key>path</key>\n";
280
281 const PathDiagnostic *D = *DI;
282 // Create an owning smart pointer for 'D' just so that we auto-free it
283 // when we exit this method.
284 llvm::OwningPtr<PathDiagnostic> OwnedD(const_cast<PathDiagnostic*>(D));
285
286 o << " <array>\n";
287
288 for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I)
289 ReportDiag(o, *I, FM, SM);
290
291 o << " </array>\n";
292
293 // Output the bug type and bug category.
Ted Kremenek082cb8d2009-03-12 18:41:53 +0000294 o << " <key>description</key><string>" << D->getDescription()
Ted Kremenekddf32da2009-01-21 00:42:24 +0000295 << "</string>\n"
Ted Kremenek082cb8d2009-03-12 18:41:53 +0000296 << " <key>category</key><string>" << D->getCategory()
Ted Kremenekddf32da2009-01-21 00:42:24 +0000297 << "</string>\n"
Ted Kremenek082cb8d2009-03-12 18:41:53 +0000298 << " <key>type</key><string>" << D->getBugType()
299 << "</string>\n"
300 << " </dict>\n";
301
Ted Kremenekddf32da2009-01-21 00:42:24 +0000302 }
Ted Kremenek5d866252008-11-03 22:33:57 +0000303
304 o << " </array>\n";
Ted Kremenekddf32da2009-01-21 00:42:24 +0000305
Ted Kremenek5d866252008-11-03 22:33:57 +0000306 // Finish.
Ted Kremenek4fc82c82008-11-03 23:18:07 +0000307 o << "</dict>\n</plist>";
Ted Kremenek5d866252008-11-03 22:33:57 +0000308}