PlistDiagnostics.cpp revision 4e4d08403ca5cfd4d558fa2936215d3a4e5a528d
1//===--- 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 14#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" 15#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 16#include "clang/Basic/SourceManager.h" 17#include "clang/Basic/FileManager.h" 18#include "clang/Lex/Preprocessor.h" 19#include "llvm/Support/raw_ostream.h" 20#include "llvm/Support/Casting.h" 21#include "llvm/ADT/DenseMap.h" 22#include "llvm/ADT/SmallVector.h" 23using namespace clang; 24using namespace ento; 25 26typedef llvm::DenseMap<FileID, unsigned> FIDMap; 27 28 29namespace { 30 class PlistDiagnostics : public PathDiagnosticConsumer { 31 const std::string OutputFile; 32 const LangOptions &LangOpts; 33 OwningPtr<PathDiagnosticConsumer> SubPD; 34 bool flushed; 35 public: 36 PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, 37 PathDiagnosticConsumer *subPD); 38 39 virtual ~PlistDiagnostics() {} 40 41 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, 42 SmallVectorImpl<std::string> *FilesMade); 43 44 virtual StringRef getName() const { 45 return "PlistDiagnostics"; 46 } 47 48 PathGenerationScheme getGenerationScheme() const; 49 bool supportsLogicalOpControlFlow() const { return true; } 50 bool supportsAllBlockEdges() const { return true; } 51 virtual bool useVerboseDescription() const { return false; } 52 }; 53} // end anonymous namespace 54 55PlistDiagnostics::PlistDiagnostics(const std::string& output, 56 const LangOptions &LO, 57 PathDiagnosticConsumer *subPD) 58 : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false) {} 59 60PathDiagnosticConsumer* 61ento::createPlistDiagnosticConsumer(const std::string& s, const Preprocessor &PP, 62 PathDiagnosticConsumer *subPD) { 63 return new PlistDiagnostics(s, PP.getLangOpts(), subPD); 64} 65 66PathDiagnosticConsumer::PathGenerationScheme 67PlistDiagnostics::getGenerationScheme() const { 68 if (const PathDiagnosticConsumer *PD = SubPD.get()) 69 return PD->getGenerationScheme(); 70 71 return Extensive; 72} 73 74static void AddFID(FIDMap &FIDs, SmallVectorImpl<FileID> &V, 75 const SourceManager* SM, SourceLocation L) { 76 77 FileID FID = SM->getFileID(SM->getExpansionLoc(L)); 78 FIDMap::iterator I = FIDs.find(FID); 79 if (I != FIDs.end()) return; 80 FIDs[FID] = V.size(); 81 V.push_back(FID); 82} 83 84static unsigned GetFID(const FIDMap& FIDs, const SourceManager &SM, 85 SourceLocation L) { 86 FileID FID = SM.getFileID(SM.getExpansionLoc(L)); 87 FIDMap::const_iterator I = FIDs.find(FID); 88 assert(I != FIDs.end()); 89 return I->second; 90} 91 92static raw_ostream &Indent(raw_ostream &o, const unsigned indent) { 93 for (unsigned i = 0; i < indent; ++i) o << ' '; 94 return o; 95} 96 97static void EmitLocation(raw_ostream &o, const SourceManager &SM, 98 const LangOptions &LangOpts, 99 SourceLocation L, const FIDMap &FM, 100 unsigned indent, bool extend = false) { 101 102 FullSourceLoc Loc(SM.getExpansionLoc(L), const_cast<SourceManager&>(SM)); 103 104 // Add in the length of the token, so that we cover multi-char tokens. 105 unsigned offset = 106 extend ? Lexer::MeasureTokenLength(Loc, SM, LangOpts) - 1 : 0; 107 108 Indent(o, indent) << "<dict>\n"; 109 Indent(o, indent) << " <key>line</key><integer>" 110 << Loc.getExpansionLineNumber() << "</integer>\n"; 111 Indent(o, indent) << " <key>col</key><integer>" 112 << Loc.getExpansionColumnNumber() + offset << "</integer>\n"; 113 Indent(o, indent) << " <key>file</key><integer>" 114 << GetFID(FM, SM, Loc) << "</integer>\n"; 115 Indent(o, indent) << "</dict>\n"; 116} 117 118static void EmitLocation(raw_ostream &o, const SourceManager &SM, 119 const LangOptions &LangOpts, 120 const PathDiagnosticLocation &L, const FIDMap& FM, 121 unsigned indent, bool extend = false) { 122 EmitLocation(o, SM, LangOpts, L.asLocation(), FM, indent, extend); 123} 124 125static void EmitRange(raw_ostream &o, const SourceManager &SM, 126 const LangOptions &LangOpts, 127 PathDiagnosticRange R, const FIDMap &FM, 128 unsigned indent) { 129 Indent(o, indent) << "<array>\n"; 130 EmitLocation(o, SM, LangOpts, R.getBegin(), FM, indent+1); 131 EmitLocation(o, SM, LangOpts, R.getEnd(), FM, indent+1, !R.isPoint); 132 Indent(o, indent) << "</array>\n"; 133} 134 135static raw_ostream &EmitString(raw_ostream &o, 136 const std::string& s) { 137 o << "<string>"; 138 for (std::string::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) { 139 char c = *I; 140 switch (c) { 141 default: o << c; break; 142 case '&': o << "&"; break; 143 case '<': o << "<"; break; 144 case '>': o << ">"; break; 145 case '\'': o << "'"; break; 146 case '\"': o << """; break; 147 } 148 } 149 o << "</string>"; 150 return o; 151} 152 153static void ReportControlFlow(raw_ostream &o, 154 const PathDiagnosticControlFlowPiece& P, 155 const FIDMap& FM, 156 const SourceManager &SM, 157 const LangOptions &LangOpts, 158 unsigned indent) { 159 160 Indent(o, indent) << "<dict>\n"; 161 ++indent; 162 163 Indent(o, indent) << "<key>kind</key><string>control</string>\n"; 164 165 // Emit edges. 166 Indent(o, indent) << "<key>edges</key>\n"; 167 ++indent; 168 Indent(o, indent) << "<array>\n"; 169 ++indent; 170 for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end(); 171 I!=E; ++I) { 172 Indent(o, indent) << "<dict>\n"; 173 ++indent; 174 Indent(o, indent) << "<key>start</key>\n"; 175 EmitRange(o, SM, LangOpts, I->getStart().asRange(), FM, indent+1); 176 Indent(o, indent) << "<key>end</key>\n"; 177 EmitRange(o, SM, LangOpts, I->getEnd().asRange(), FM, indent+1); 178 --indent; 179 Indent(o, indent) << "</dict>\n"; 180 } 181 --indent; 182 Indent(o, indent) << "</array>\n"; 183 --indent; 184 185 // Output any helper text. 186 const std::string& s = P.getString(); 187 if (!s.empty()) { 188 Indent(o, indent) << "<key>alternate</key>"; 189 EmitString(o, s) << '\n'; 190 } 191 192 --indent; 193 Indent(o, indent) << "</dict>\n"; 194} 195 196static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, 197 const FIDMap& FM, 198 const SourceManager &SM, 199 const LangOptions &LangOpts, 200 unsigned indent) { 201 202 Indent(o, indent) << "<dict>\n"; 203 ++indent; 204 205 Indent(o, indent) << "<key>kind</key><string>event</string>\n"; 206 207 // Output the location. 208 FullSourceLoc L = P.getLocation().asLocation(); 209 210 Indent(o, indent) << "<key>location</key>\n"; 211 EmitLocation(o, SM, LangOpts, L, FM, indent); 212 213 // Output the ranges (if any). 214 PathDiagnosticPiece::range_iterator RI = P.ranges_begin(), 215 RE = P.ranges_end(); 216 217 if (RI != RE) { 218 Indent(o, indent) << "<key>ranges</key>\n"; 219 Indent(o, indent) << "<array>\n"; 220 ++indent; 221 for (; RI != RE; ++RI) 222 EmitRange(o, SM, LangOpts, *RI, FM, indent+1); 223 --indent; 224 Indent(o, indent) << "</array>\n"; 225 } 226 227 // Output the text. 228 assert(!P.getString().empty()); 229 Indent(o, indent) << "<key>extended_message</key>\n"; 230 Indent(o, indent); 231 EmitString(o, P.getString()) << '\n'; 232 233 // Output the short text. 234 // FIXME: Really use a short string. 235 Indent(o, indent) << "<key>message</key>\n"; 236 EmitString(o, P.getString()) << '\n'; 237 238 // Finish up. 239 --indent; 240 Indent(o, indent); o << "</dict>\n"; 241} 242 243static void ReportPiece(raw_ostream &o, 244 const PathDiagnosticPiece &P, 245 const FIDMap& FM, const SourceManager &SM, 246 const LangOptions &LangOpts, 247 unsigned indent, 248 bool includeControlFlow); 249 250static void ReportCall(raw_ostream &o, 251 const PathDiagnosticCallPiece &P, 252 const FIDMap& FM, const SourceManager &SM, 253 const LangOptions &LangOpts, 254 unsigned indent) { 255 256 IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = 257 P.getCallEnterEvent(); 258 259 if (callEnter) 260 ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, true); 261 262 IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller = 263 P.getCallEnterWithinCallerEvent(); 264 265 if (callEnterWithinCaller) 266 ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts, indent, true); 267 268 for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) 269 ReportPiece(o, **I, FM, SM, LangOpts, indent, true); 270 271 IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = 272 P.getCallExitEvent(); 273 274 if (callExit) 275 ReportPiece(o, *callExit, FM, SM, LangOpts, indent, true); 276} 277 278static void ReportMacro(raw_ostream &o, 279 const PathDiagnosticMacroPiece& P, 280 const FIDMap& FM, const SourceManager &SM, 281 const LangOptions &LangOpts, 282 unsigned indent) { 283 284 for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); 285 I!=E; ++I) { 286 ReportPiece(o, **I, FM, SM, LangOpts, indent, false); 287 } 288} 289 290static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, 291 const FIDMap& FM, const SourceManager &SM, 292 const LangOptions &LangOpts) { 293 ReportPiece(o, P, FM, SM, LangOpts, 4, true); 294} 295 296static void ReportPiece(raw_ostream &o, 297 const PathDiagnosticPiece &P, 298 const FIDMap& FM, const SourceManager &SM, 299 const LangOptions &LangOpts, 300 unsigned indent, 301 bool includeControlFlow) { 302 switch (P.getKind()) { 303 case PathDiagnosticPiece::ControlFlow: 304 if (includeControlFlow) 305 ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, 306 LangOpts, indent); 307 break; 308 case PathDiagnosticPiece::Call: 309 ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts, 310 indent); 311 break; 312 case PathDiagnosticPiece::Event: 313 ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts, 314 indent); 315 break; 316 case PathDiagnosticPiece::Macro: 317 ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, 318 indent); 319 break; 320 } 321} 322 323void PlistDiagnostics::FlushDiagnosticsImpl( 324 std::vector<const PathDiagnostic *> &Diags, 325 SmallVectorImpl<std::string> *FilesMade) { 326 // Build up a set of FIDs that we use by scanning the locations and 327 // ranges of the diagnostics. 328 FIDMap FM; 329 SmallVector<FileID, 10> Fids; 330 const SourceManager* SM = 0; 331 332 if (!Diags.empty()) 333 SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); 334 335 336 for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), 337 DE = Diags.end(); DI != DE; ++DI) { 338 339 const PathDiagnostic *D = *DI; 340 341 llvm::SmallVector<const PathPieces *, 5> WorkList; 342 WorkList.push_back(&D->path); 343 344 while (!WorkList.empty()) { 345 const PathPieces &path = *WorkList.back(); 346 WorkList.pop_back(); 347 348 for (PathPieces::const_iterator I = path.begin(), E = path.end(); 349 I!=E; ++I) { 350 const PathDiagnosticPiece *piece = I->getPtr(); 351 AddFID(FM, Fids, SM, piece->getLocation().asLocation()); 352 353 for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), 354 RE= piece->ranges_end(); RI != RE; ++RI) { 355 AddFID(FM, Fids, SM, RI->getBegin()); 356 AddFID(FM, Fids, SM, RI->getEnd()); 357 } 358 359 if (const PathDiagnosticCallPiece *call = 360 dyn_cast<PathDiagnosticCallPiece>(piece)) { 361 WorkList.push_back(&call->path); 362 } 363 else if (const PathDiagnosticMacroPiece *macro = 364 dyn_cast<PathDiagnosticMacroPiece>(piece)) { 365 WorkList.push_back(¯o->subPieces); 366 } 367 } 368 } 369 } 370 371 // Open the file. 372 std::string ErrMsg; 373 llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg); 374 if (!ErrMsg.empty()) { 375 llvm::errs() << "warning: could not create file: " << OutputFile << '\n'; 376 return; 377 } 378 379 // Write the plist header. 380 o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 381 "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " 382 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" 383 "<plist version=\"1.0\">\n"; 384 385 // Write the root object: a <dict> containing... 386 // - "files", an <array> mapping from FIDs to file names 387 // - "diagnostics", an <array> containing the path diagnostics 388 o << "<dict>\n" 389 " <key>files</key>\n" 390 " <array>\n"; 391 392 for (SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end(); 393 I!=E; ++I) { 394 o << " "; 395 EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n'; 396 } 397 398 o << " </array>\n" 399 " <key>diagnostics</key>\n" 400 " <array>\n"; 401 402 for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), 403 DE = Diags.end(); DI!=DE; ++DI) { 404 405 o << " <dict>\n" 406 " <key>path</key>\n"; 407 408 const PathDiagnostic *D = *DI; 409 410 o << " <array>\n"; 411 412 for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); 413 I != E; ++I) 414 ReportDiag(o, **I, FM, *SM, LangOpts); 415 416 o << " </array>\n"; 417 418 // Output the bug type and bug category. 419 o << " <key>description</key>"; 420 EmitString(o, D->getDescription()) << '\n'; 421 o << " <key>category</key>"; 422 EmitString(o, D->getCategory()) << '\n'; 423 o << " <key>type</key>"; 424 EmitString(o, D->getBugType()) << '\n'; 425 426 // Output the location of the bug. 427 o << " <key>location</key>\n"; 428 EmitLocation(o, *SM, LangOpts, D->getLocation(), FM, 2); 429 430 // Output the diagnostic to the sub-diagnostic client, if any. 431 if (SubPD) { 432 std::vector<const PathDiagnostic *> SubDiags; 433 SubDiags.push_back(D); 434 SmallVector<std::string, 1> SubFilesMade; 435 SubPD->FlushDiagnosticsImpl(SubDiags, &SubFilesMade); 436 437 if (!SubFilesMade.empty()) { 438 o << " <key>" << SubPD->getName() << "_files</key>\n"; 439 o << " <array>\n"; 440 for (size_t i = 0, n = SubFilesMade.size(); i < n ; ++i) 441 o << " <string>" << SubFilesMade[i] << "</string>\n"; 442 o << " </array>\n"; 443 } 444 } 445 446 // Close up the entry. 447 o << " </dict>\n"; 448 } 449 450 o << " </array>\n"; 451 452 // Finish. 453 o << "</dict>\n</plist>"; 454 455 if (FilesMade) 456 FilesMade->push_back(OutputFile); 457} 458