PlistDiagnostics.cpp revision 218887
1218887Sdim//===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- C++ -*-===// 2218887Sdim// 3218887Sdim// The LLVM Compiler Infrastructure 4218887Sdim// 5218887Sdim// This file is distributed under the University of Illinois Open Source 6218887Sdim// License. See LICENSE.TXT for details. 7218887Sdim// 8218887Sdim//===----------------------------------------------------------------------===// 9218887Sdim// 10218887Sdim// This file defines the PlistDiagnostics object. 11218887Sdim// 12218887Sdim//===----------------------------------------------------------------------===// 13218887Sdim 14243830Sdim#include "clang/StaticAnalyzer/Core/PathDiagnosticClients.h" 15276479Sdim#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 16218887Sdim#include "clang/Basic/SourceManager.h" 17243830Sdim#include "clang/Basic/FileManager.h" 18218887Sdim#include "clang/Lex/Preprocessor.h" 19249423Sdim#include "llvm/Support/raw_ostream.h" 20296417Sdim#include "llvm/Support/Casting.h" 21249423Sdim#include "llvm/ADT/DenseMap.h" 22218887Sdim#include "llvm/ADT/SmallVector.h" 23249423Sdimusing namespace clang; 24218887Sdimusing namespace ento; 25218887Sdimusing llvm::cast; 26276479Sdim 27218887Sdimtypedef llvm::DenseMap<FileID, unsigned> FIDMap; 28218887Sdim 29226633Sdimnamespace clang { 30218887Sdim class Preprocessor; 31218887Sdim} 32234353Sdim 33218887Sdimnamespace { 34249423Sdimstruct CompareDiagnostics { 35249423Sdim // Compare if 'X' is "<" than 'Y'. 36249423Sdim bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const { 37239462Sdim // First compare by location 38218887Sdim const FullSourceLoc &XLoc = X->getLocation().asLocation(); 39288943Sdim const FullSourceLoc &YLoc = Y->getLocation().asLocation(); 40218887Sdim if (XLoc < YLoc) 41234353Sdim return true; 42276479Sdim if (XLoc != YLoc) 43276479Sdim return false; 44280031Sdim 45218887Sdim // Next, compare by bug type. 46218887Sdim llvm::StringRef XBugType = X->getBugType(); 47218887Sdim llvm::StringRef YBugType = Y->getBugType(); 48276479Sdim if (XBugType < YBugType) 49276479Sdim return true; 50276479Sdim if (XBugType != YBugType) 51276479Sdim return false; 52276479Sdim 53234353Sdim // Next, compare by bug description. 54234353Sdim llvm::StringRef XDesc = X->getDescription(); 55218887Sdim llvm::StringRef YDesc = Y->getDescription(); 56218887Sdim if (XDesc < YDesc) 57218887Sdim return true; 58249423Sdim if (XDesc != YDesc) 59249423Sdim return false; 60218887Sdim 61239462Sdim // FIXME: Further refine by comparing PathDiagnosticPieces? 62249423Sdim return false; 63249423Sdim } 64234353Sdim}; 65218887Sdim} 66249423Sdim 67249423Sdimnamespace { 68239462Sdim class PlistDiagnostics : public PathDiagnosticClient { 69239462Sdim std::vector<const PathDiagnostic*> BatchedDiags; 70249423Sdim const std::string OutputFile; 71249423Sdim const LangOptions &LangOpts; 72218887Sdim llvm::OwningPtr<PathDiagnosticClient> SubPD; 73218887Sdim bool flushed; 74249423Sdim public: 75249423Sdim PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, 76239462Sdim PathDiagnosticClient *subPD); 77239462Sdim 78249423Sdim ~PlistDiagnostics() { FlushDiagnostics(NULL); } 79249423Sdim 80234353Sdim void FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade); 81234353Sdim 82226633Sdim void HandlePathDiagnostic(const PathDiagnostic* D); 83218887Sdim 84218887Sdim virtual llvm::StringRef getName() const { 85218887Sdim return "PlistDiagnostics"; 86218887Sdim } 87218887Sdim 88218887Sdim PathGenerationScheme getGenerationScheme() const; 89218887Sdim bool supportsLogicalOpControlFlow() const { return true; } 90218887Sdim bool supportsAllBlockEdges() const { return true; } 91218887Sdim virtual bool useVerboseDescription() const { return false; } 92218887Sdim }; 93218887Sdim} // end anonymous namespace 94218887Sdim 95218887SdimPlistDiagnostics::PlistDiagnostics(const std::string& output, 96218887Sdim const LangOptions &LO, 97218887Sdim PathDiagnosticClient *subPD) 98218887Sdim : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false) {} 99218887Sdim 100218887SdimPathDiagnosticClient* 101218887Sdimento::createPlistDiagnosticClient(const std::string& s, const Preprocessor &PP, 102218887Sdim PathDiagnosticClient *subPD) { 103239462Sdim return new PlistDiagnostics(s, PP.getLangOptions(), subPD); 104239462Sdim} 105239462Sdim 106239462SdimPathDiagnosticClient::PathGenerationScheme 107218887SdimPlistDiagnostics::getGenerationScheme() const { 108288943Sdim if (const PathDiagnosticClient *PD = SubPD.get()) 109288943Sdim return PD->getGenerationScheme(); 110288943Sdim 111276479Sdim return Extensive; 112239462Sdim} 113218887Sdim 114288943Sdimstatic void AddFID(FIDMap &FIDs, llvm::SmallVectorImpl<FileID> &V, 115288943Sdim const SourceManager* SM, SourceLocation L) { 116276479Sdim 117239462Sdim FileID FID = SM->getFileID(SM->getInstantiationLoc(L)); 118218887Sdim FIDMap::iterator I = FIDs.find(FID); 119218887Sdim if (I != FIDs.end()) return; 120218887Sdim FIDs[FID] = V.size(); 121218887Sdim V.push_back(FID); 122218887Sdim} 123218887Sdim 124218887Sdimstatic unsigned GetFID(const FIDMap& FIDs, const SourceManager &SM, 125218887Sdim SourceLocation L) { 126309124Sdim FileID FID = SM.getFileID(SM.getInstantiationLoc(L)); 127218887Sdim FIDMap::const_iterator I = FIDs.find(FID); 128218887Sdim assert(I != FIDs.end()); 129218887Sdim return I->second; 130218887Sdim} 131218887Sdim 132218887Sdimstatic llvm::raw_ostream& Indent(llvm::raw_ostream& o, const unsigned indent) { 133218887Sdim for (unsigned i = 0; i < indent; ++i) o << ' '; 134218887Sdim return o; 135218887Sdim} 136226633Sdim 137218887Sdimstatic void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM, 138218887Sdim const LangOptions &LangOpts, 139218887Sdim SourceLocation L, const FIDMap &FM, 140234353Sdim unsigned indent, bool extend = false) { 141261991Sdim 142261991Sdim FullSourceLoc Loc(SM.getInstantiationLoc(L), const_cast<SourceManager&>(SM)); 143218887Sdim 144218887Sdim // Add in the length of the token, so that we cover multi-char tokens. 145218887Sdim unsigned offset = 146218887Sdim extend ? Lexer::MeasureTokenLength(Loc, SM, LangOpts) - 1 : 0; 147218887Sdim 148218887Sdim Indent(o, indent) << "<dict>\n"; 149261991Sdim Indent(o, indent) << " <key>line</key><integer>" 150261991Sdim << Loc.getInstantiationLineNumber() << "</integer>\n"; 151261991Sdim Indent(o, indent) << " <key>col</key><integer>" 152261991Sdim << Loc.getInstantiationColumnNumber() + offset << "</integer>\n"; 153218887Sdim Indent(o, indent) << " <key>file</key><integer>" 154218887Sdim << GetFID(FM, SM, Loc) << "</integer>\n"; 155218887Sdim Indent(o, indent) << "</dict>\n"; 156218887Sdim} 157288943Sdim 158218887Sdimstatic void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM, 159218887Sdim const LangOptions &LangOpts, 160239462Sdim const PathDiagnosticLocation &L, const FIDMap& FM, 161218887Sdim unsigned indent, bool extend = false) { 162239462Sdim EmitLocation(o, SM, LangOpts, L.asLocation(), FM, indent, extend); 163218887Sdim} 164218887Sdim 165218887Sdimstatic void EmitRange(llvm::raw_ostream& o, const SourceManager &SM, 166288943Sdim const LangOptions &LangOpts, 167288943Sdim PathDiagnosticRange R, const FIDMap &FM, 168288943Sdim unsigned indent) { 169288943Sdim Indent(o, indent) << "<array>\n"; 170218887Sdim EmitLocation(o, SM, LangOpts, R.getBegin(), FM, indent+1); 171218887Sdim EmitLocation(o, SM, LangOpts, R.getEnd(), FM, indent+1, !R.isPoint); 172218887Sdim Indent(o, indent) << "</array>\n"; 173296417Sdim} 174234353Sdim 175276479Sdimstatic llvm::raw_ostream& EmitString(llvm::raw_ostream& o, 176276479Sdim const std::string& s) { 177218887Sdim o << "<string>"; 178218887Sdim for (std::string::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) { 179218887Sdim char c = *I; 180218887Sdim switch (c) { 181218887Sdim default: o << c; break; 182218887Sdim case '&': o << "&"; break; 183218887Sdim case '<': o << "<"; break; 184218887Sdim case '>': o << ">"; break; 185218887Sdim case '\'': o << "'"; break; 186218887Sdim case '\"': o << """; break; 187243830Sdim } 188218887Sdim } 189296417Sdim o << "</string>"; 190218887Sdim return o; 191218887Sdim} 192218887Sdim 193218887Sdimstatic void ReportControlFlow(llvm::raw_ostream& o, 194218887Sdim const PathDiagnosticControlFlowPiece& P, 195234353Sdim const FIDMap& FM, 196234353Sdim const SourceManager &SM, 197234353Sdim const LangOptions &LangOpts, 198234353Sdim unsigned indent) { 199234353Sdim 200234353Sdim Indent(o, indent) << "<dict>\n"; 201261991Sdim ++indent; 202261991Sdim 203234353Sdim Indent(o, indent) << "<key>kind</key><string>control</string>\n"; 204234353Sdim 205234353Sdim // Emit edges. 206234353Sdim Indent(o, indent) << "<key>edges</key>\n"; 207234353Sdim ++indent; 208234353Sdim Indent(o, indent) << "<array>\n"; 209234353Sdim ++indent; 210296417Sdim for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end(); 211314564Sdim I!=E; ++I) { 212261991Sdim Indent(o, indent) << "<dict>\n"; 213261991Sdim ++indent; 214234353Sdim Indent(o, indent) << "<key>start</key>\n"; 215296417Sdim EmitRange(o, SM, LangOpts, I->getStart().asRange(), FM, indent+1); 216234353Sdim Indent(o, indent) << "<key>end</key>\n"; 217296417Sdim EmitRange(o, SM, LangOpts, I->getEnd().asRange(), FM, indent+1); 218314564Sdim --indent; 219234353Sdim Indent(o, indent) << "</dict>\n"; 220234353Sdim } 221296417Sdim --indent; 222234353Sdim Indent(o, indent) << "</array>\n"; 223234353Sdim --indent; 224251662Sdim 225251662Sdim // Output any helper text. 226296417Sdim const std::string& s = P.getString(); 227314564Sdim if (!s.empty()) { 228234353Sdim Indent(o, indent) << "<key>alternate</key>"; 229234353Sdim EmitString(o, s) << '\n'; 230234353Sdim } 231226633Sdim 232218887Sdim --indent; 233218887Sdim Indent(o, indent) << "</dict>\n"; 234218887Sdim} 235234353Sdim 236234353Sdimstatic void ReportEvent(llvm::raw_ostream& o, const PathDiagnosticPiece& P, 237218887Sdim const FIDMap& FM, 238234353Sdim const SourceManager &SM, 239218887Sdim const LangOptions &LangOpts, 240234353Sdim unsigned indent) { 241218887Sdim 242218887Sdim Indent(o, indent) << "<dict>\n"; 243218887Sdim ++indent; 244226633Sdim 245218887Sdim Indent(o, indent) << "<key>kind</key><string>event</string>\n"; 246218887Sdim 247234353Sdim // Output the location. 248234353Sdim FullSourceLoc L = P.getLocation().asLocation(); 249218887Sdim 250234353Sdim Indent(o, indent) << "<key>location</key>\n"; 251234353Sdim EmitLocation(o, SM, LangOpts, L, FM, indent); 252234353Sdim 253234353Sdim // Output the ranges (if any). 254234353Sdim PathDiagnosticPiece::range_iterator RI = P.ranges_begin(), 255234353Sdim RE = P.ranges_end(); 256261991Sdim 257261991Sdim if (RI != RE) { 258218887Sdim Indent(o, indent) << "<key>ranges</key>\n"; 259234353Sdim Indent(o, indent) << "<array>\n"; 260234353Sdim ++indent; 261234353Sdim for (; RI != RE; ++RI) 262234353Sdim EmitRange(o, SM, LangOpts, *RI, FM, indent+1); 263234353Sdim --indent; 264234353Sdim Indent(o, indent) << "</array>\n"; 265234353Sdim } 266234353Sdim 267234353Sdim // Output the text. 268234353Sdim assert(!P.getString().empty()); 269234353Sdim Indent(o, indent) << "<key>extended_message</key>\n"; 270261991Sdim Indent(o, indent); 271234353Sdim EmitString(o, P.getString()) << '\n'; 272234353Sdim 273234353Sdim // Output the short text. 274234353Sdim // FIXME: Really use a short string. 275234353Sdim Indent(o, indent) << "<key>message</key>\n"; 276314564Sdim EmitString(o, P.getString()) << '\n'; 277314564Sdim 278314564Sdim // Finish up. 279218887Sdim --indent; 280218887Sdim Indent(o, indent); o << "</dict>\n"; 281218887Sdim} 282234353Sdim 283234353Sdimstatic void ReportMacro(llvm::raw_ostream& o, 284239462Sdim const PathDiagnosticMacroPiece& P, 285218887Sdim const FIDMap& FM, const SourceManager &SM, 286218887Sdim const LangOptions &LangOpts, 287218887Sdim unsigned indent) { 288226633Sdim 289276479Sdim for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end(); 290218887Sdim I!=E; ++I) { 291234353Sdim 292296417Sdim switch ((*I)->getKind()) { 293218887Sdim default: 294314564Sdim break; 295314564Sdim case PathDiagnosticPiece::Event: 296314564Sdim ReportEvent(o, cast<PathDiagnosticEventPiece>(**I), FM, SM, LangOpts, 297314564Sdim indent); 298314564Sdim break; 299314564Sdim case PathDiagnosticPiece::Macro: 300314564Sdim ReportMacro(o, cast<PathDiagnosticMacroPiece>(**I), FM, SM, LangOpts, 301314564Sdim indent); 302296417Sdim break; 303314564Sdim } 304218887Sdim } 305249423Sdim} 306234353Sdim 307218887Sdimstatic void ReportDiag(llvm::raw_ostream& o, const PathDiagnosticPiece& P, 308234353Sdim const FIDMap& FM, const SourceManager &SM, 309314564Sdim const LangOptions &LangOpts) { 310261991Sdim 311314564Sdim unsigned indent = 4; 312314564Sdim 313314564Sdim switch (P.getKind()) { 314234353Sdim case PathDiagnosticPiece::ControlFlow: 315314564Sdim ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, 316314564Sdim LangOpts, indent); 317314564Sdim break; 318314564Sdim case PathDiagnosticPiece::Event: 319239462Sdim ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts, 320314564Sdim indent); 321314564Sdim break; 322314564Sdim case PathDiagnosticPiece::Macro: 323314564Sdim ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, 324314564Sdim indent); 325314564Sdim break; 326314564Sdim } 327234353Sdim} 328218887Sdim 329218887Sdimvoid PlistDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) { 330218887Sdim if (!D) 331218887Sdim return; 332218887Sdim 333280031Sdim if (D->empty()) { 334280031Sdim delete D; 335280031Sdim return; 336280031Sdim } 337218887Sdim 338218887Sdim // We need to flatten the locations (convert Stmt* to locations) because 339218887Sdim // the referenced statements may be freed by the time the diagnostics 340276479Sdim // are emitted. 341218887Sdim const_cast<PathDiagnostic*>(D)->flattenLocations(); 342218887Sdim BatchedDiags.push_back(D); 343243830Sdim} 344218887Sdim 345218887Sdimvoid PlistDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string> 346243830Sdim *FilesMade) { 347243830Sdim 348243830Sdim if (flushed) 349243830Sdim return; 350218887Sdim 351218887Sdim flushed = true; 352276479Sdim 353276479Sdim // Sort the diagnostics so that they are always emitted in a deterministic 354218887Sdim // order. 355218887Sdim if (!BatchedDiags.empty()) 356218887Sdim std::sort(BatchedDiags.begin(), BatchedDiags.end(), CompareDiagnostics()); 357218887Sdim 358218887Sdim // Build up a set of FIDs that we use by scanning the locations and 359234353Sdim // ranges of the diagnostics. 360234353Sdim FIDMap FM; 361218887Sdim llvm::SmallVector<FileID, 10> Fids; 362218887Sdim const SourceManager* SM = 0; 363218887Sdim 364218887Sdim if (!BatchedDiags.empty()) 365218887Sdim SM = &(*BatchedDiags.begin())->begin()->getLocation().getManager(); 366218887Sdim 367218887Sdim for (std::vector<const PathDiagnostic*>::iterator DI = BatchedDiags.begin(), 368218887Sdim DE = BatchedDiags.end(); DI != DE; ++DI) { 369296417Sdim 370234353Sdim const PathDiagnostic *D = *DI; 371234353Sdim 372218887Sdim for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I!=E; ++I) { 373218887Sdim AddFID(FM, Fids, SM, I->getLocation().asLocation()); 374218887Sdim 375218887Sdim for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(), 376218887Sdim RE=I->ranges_end(); RI!=RE; ++RI) { 377243830Sdim AddFID(FM, Fids, SM, RI->getBegin()); 378218887Sdim AddFID(FM, Fids, SM, RI->getEnd()); 379218887Sdim } 380218887Sdim } 381218887Sdim } 382288943Sdim 383288943Sdim // Open the file. 384296417Sdim std::string ErrMsg; 385296417Sdim llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg); 386296417Sdim if (!ErrMsg.empty()) { 387296417Sdim llvm::errs() << "warning: could not creat file: " << OutputFile << '\n'; 388296417Sdim return; 389296417Sdim } 390296417Sdim 391296417Sdim // Write the plist header. 392296417Sdim o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 393296417Sdim "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " 394296417Sdim "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" 395296417Sdim "<plist version=\"1.0\">\n"; 396296417Sdim 397234353Sdim // Write the root object: a <dict> containing... 398234353Sdim // - "files", an <array> mapping from FIDs to file names 399234353Sdim // - "diagnostics", an <array> containing the path diagnostics 400234353Sdim o << "<dict>\n" 401234353Sdim " <key>files</key>\n" 402234353Sdim " <array>\n"; 403234353Sdim 404234353Sdim for (llvm::SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end(); 405234353Sdim I!=E; ++I) { 406234353Sdim o << " "; 407234353Sdim EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n'; 408234353Sdim } 409234353Sdim 410234353Sdim o << " </array>\n" 411234353Sdim " <key>diagnostics</key>\n" 412234353Sdim " <array>\n"; 413234353Sdim 414234353Sdim for (std::vector<const PathDiagnostic*>::iterator DI=BatchedDiags.begin(), 415234353Sdim DE = BatchedDiags.end(); DI!=DE; ++DI) { 416234353Sdim 417234353Sdim o << " <dict>\n" 418234353Sdim " <key>path</key>\n"; 419234353Sdim 420234353Sdim const PathDiagnostic *D = *DI; 421234353Sdim // Create an owning smart pointer for 'D' just so that we auto-free it 422234353Sdim // when we exit this method. 423234353Sdim llvm::OwningPtr<PathDiagnostic> OwnedD(const_cast<PathDiagnostic*>(D)); 424234353Sdim 425234353Sdim o << " <array>\n"; 426239462Sdim 427239462Sdim for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I) 428239462Sdim ReportDiag(o, *I, FM, *SM, LangOpts); 429239462Sdim 430296417Sdim o << " </array>\n"; 431249423Sdim 432249423Sdim // Output the bug type and bug category. 433249423Sdim o << " <key>description</key>"; 434249423Sdim EmitString(o, D->getDescription()) << '\n'; 435249423Sdim o << " <key>category</key>"; 436249423Sdim EmitString(o, D->getCategory()) << '\n'; 437249423Sdim o << " <key>type</key>"; 438249423Sdim EmitString(o, D->getBugType()) << '\n'; 439296417Sdim 440296417Sdim // Output the location of the bug. 441249423Sdim o << " <key>location</key>\n"; 442249423Sdim EmitLocation(o, *SM, LangOpts, D->getLocation(), FM, 2); 443249423Sdim 444249423Sdim // Output the diagnostic to the sub-diagnostic client, if any. 445249423Sdim if (SubPD) { 446296417Sdim SubPD->HandlePathDiagnostic(OwnedD.take()); 447249423Sdim llvm::SmallVector<std::string, 1> SubFilesMade; 448249423Sdim SubPD->FlushDiagnostics(SubFilesMade); 449249423Sdim 450249423Sdim if (!SubFilesMade.empty()) { 451239462Sdim o << " <key>" << SubPD->getName() << "_files</key>\n"; 452234353Sdim o << " <array>\n"; 453234353Sdim for (size_t i = 0, n = SubFilesMade.size(); i < n ; ++i) 454218887Sdim o << " <string>" << SubFilesMade[i] << "</string>\n"; 455218887Sdim o << " </array>\n"; 456218887Sdim } 457288943Sdim } 458218887Sdim 459218887Sdim // Close up the entry. 460239462Sdim o << " </dict>\n"; 461239462Sdim } 462243830Sdim 463243830Sdim o << " </array>\n"; 464243830Sdim 465243830Sdim // Finish. 466243830Sdim o << "</dict>\n</plist>"; 467243830Sdim 468243830Sdim if (FilesMade) 469243830Sdim FilesMade->push_back(OutputFile); 470243830Sdim 471243830Sdim BatchedDiags.clear(); 472243830Sdim} 473243830Sdim