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 14249423Sdim#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" 15243830Sdim#include "clang/Basic/FileManager.h" 16218887Sdim#include "clang/Basic/SourceManager.h" 17243830Sdim#include "clang/Basic/Version.h" 18218887Sdim#include "clang/Lex/Preprocessor.h" 19249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 20249423Sdim#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" 21218887Sdim#include "llvm/ADT/DenseMap.h" 22218887Sdim#include "llvm/ADT/SmallVector.h" 23249423Sdim#include "llvm/Support/Casting.h" 24249423Sdim#include "llvm/Support/raw_ostream.h" 25218887Sdimusing namespace clang; 26218887Sdimusing namespace ento; 27218887Sdim 28218887Sdimtypedef llvm::DenseMap<FileID, unsigned> FIDMap; 29218887Sdim 30218887Sdim 31218887Sdimnamespace { 32226633Sdim class PlistDiagnostics : public PathDiagnosticConsumer { 33218887Sdim const std::string OutputFile; 34218887Sdim const LangOptions &LangOpts; 35234353Sdim const bool SupportsCrossFileDiagnostics; 36218887Sdim public: 37249423Sdim PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, 38249423Sdim const std::string& prefix, 39249423Sdim const LangOptions &LangOpts, 40239462Sdim bool supportsMultipleFiles); 41218887Sdim 42234353Sdim virtual ~PlistDiagnostics() {} 43218887Sdim 44234353Sdim void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, 45239462Sdim FilesMade *filesMade); 46218887Sdim 47226633Sdim virtual StringRef getName() const { 48218887Sdim return "PlistDiagnostics"; 49218887Sdim } 50218887Sdim 51239462Sdim PathGenerationScheme getGenerationScheme() const { return Extensive; } 52218887Sdim bool supportsLogicalOpControlFlow() const { return true; } 53218887Sdim bool supportsAllBlockEdges() const { return true; } 54234353Sdim virtual bool supportsCrossFileDiagnostics() const { 55234353Sdim return SupportsCrossFileDiagnostics; 56234353Sdim } 57218887Sdim }; 58218887Sdim} // end anonymous namespace 59218887Sdim 60249423SdimPlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, 61249423Sdim const std::string& output, 62218887Sdim const LangOptions &LO, 63239462Sdim bool supportsMultipleFiles) 64249423Sdim : OutputFile(output), 65249423Sdim LangOpts(LO), 66234353Sdim SupportsCrossFileDiagnostics(supportsMultipleFiles) {} 67218887Sdim 68249423Sdimvoid ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, 69249423Sdim PathDiagnosticConsumers &C, 70239462Sdim const std::string& s, 71239462Sdim const Preprocessor &PP) { 72249423Sdim C.push_back(new PlistDiagnostics(AnalyzerOpts, s, 73249423Sdim PP.getLangOpts(), false)); 74218887Sdim} 75218887Sdim 76249423Sdimvoid ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, 77249423Sdim PathDiagnosticConsumers &C, 78239462Sdim const std::string &s, 79239462Sdim const Preprocessor &PP) { 80249423Sdim C.push_back(new PlistDiagnostics(AnalyzerOpts, s, 81249423Sdim PP.getLangOpts(), true)); 82234353Sdim} 83234353Sdim 84226633Sdimstatic void AddFID(FIDMap &FIDs, SmallVectorImpl<FileID> &V, 85218887Sdim const SourceManager* SM, SourceLocation L) { 86218887Sdim 87226633Sdim FileID FID = SM->getFileID(SM->getExpansionLoc(L)); 88218887Sdim FIDMap::iterator I = FIDs.find(FID); 89218887Sdim if (I != FIDs.end()) return; 90218887Sdim FIDs[FID] = V.size(); 91218887Sdim V.push_back(FID); 92218887Sdim} 93218887Sdim 94218887Sdimstatic unsigned GetFID(const FIDMap& FIDs, const SourceManager &SM, 95218887Sdim SourceLocation L) { 96226633Sdim FileID FID = SM.getFileID(SM.getExpansionLoc(L)); 97218887Sdim FIDMap::const_iterator I = FIDs.find(FID); 98218887Sdim assert(I != FIDs.end()); 99218887Sdim return I->second; 100218887Sdim} 101218887Sdim 102226633Sdimstatic raw_ostream &Indent(raw_ostream &o, const unsigned indent) { 103218887Sdim for (unsigned i = 0; i < indent; ++i) o << ' '; 104218887Sdim return o; 105218887Sdim} 106218887Sdim 107226633Sdimstatic void EmitLocation(raw_ostream &o, const SourceManager &SM, 108218887Sdim const LangOptions &LangOpts, 109218887Sdim SourceLocation L, const FIDMap &FM, 110218887Sdim unsigned indent, bool extend = false) { 111218887Sdim 112226633Sdim FullSourceLoc Loc(SM.getExpansionLoc(L), const_cast<SourceManager&>(SM)); 113218887Sdim 114218887Sdim // Add in the length of the token, so that we cover multi-char tokens. 115218887Sdim unsigned offset = 116218887Sdim extend ? Lexer::MeasureTokenLength(Loc, SM, LangOpts) - 1 : 0; 117218887Sdim 118218887Sdim Indent(o, indent) << "<dict>\n"; 119218887Sdim Indent(o, indent) << " <key>line</key><integer>" 120226633Sdim << Loc.getExpansionLineNumber() << "</integer>\n"; 121218887Sdim Indent(o, indent) << " <key>col</key><integer>" 122226633Sdim << Loc.getExpansionColumnNumber() + offset << "</integer>\n"; 123218887Sdim Indent(o, indent) << " <key>file</key><integer>" 124218887Sdim << GetFID(FM, SM, Loc) << "</integer>\n"; 125218887Sdim Indent(o, indent) << "</dict>\n"; 126218887Sdim} 127218887Sdim 128226633Sdimstatic void EmitLocation(raw_ostream &o, const SourceManager &SM, 129218887Sdim const LangOptions &LangOpts, 130218887Sdim const PathDiagnosticLocation &L, const FIDMap& FM, 131218887Sdim unsigned indent, bool extend = false) { 132218887Sdim EmitLocation(o, SM, LangOpts, L.asLocation(), FM, indent, extend); 133218887Sdim} 134218887Sdim 135226633Sdimstatic void EmitRange(raw_ostream &o, const SourceManager &SM, 136218887Sdim const LangOptions &LangOpts, 137218887Sdim PathDiagnosticRange R, const FIDMap &FM, 138218887Sdim unsigned indent) { 139218887Sdim Indent(o, indent) << "<array>\n"; 140218887Sdim EmitLocation(o, SM, LangOpts, R.getBegin(), FM, indent+1); 141218887Sdim EmitLocation(o, SM, LangOpts, R.getEnd(), FM, indent+1, !R.isPoint); 142218887Sdim Indent(o, indent) << "</array>\n"; 143218887Sdim} 144218887Sdim 145234353Sdimstatic raw_ostream &EmitString(raw_ostream &o, StringRef s) { 146218887Sdim o << "<string>"; 147234353Sdim for (StringRef::const_iterator I = s.begin(), E = s.end(); I != E; ++I) { 148218887Sdim char c = *I; 149218887Sdim switch (c) { 150218887Sdim default: o << c; break; 151218887Sdim case '&': o << "&"; break; 152218887Sdim case '<': o << "<"; break; 153218887Sdim case '>': o << ">"; break; 154218887Sdim case '\'': o << "'"; break; 155218887Sdim case '\"': o << """; break; 156218887Sdim } 157218887Sdim } 158218887Sdim o << "</string>"; 159218887Sdim return o; 160218887Sdim} 161218887Sdim 162226633Sdimstatic void ReportControlFlow(raw_ostream &o, 163218887Sdim const PathDiagnosticControlFlowPiece& P, 164218887Sdim const FIDMap& FM, 165218887Sdim const SourceManager &SM, 166218887Sdim const LangOptions &LangOpts, 167218887Sdim unsigned indent) { 168218887Sdim 169218887Sdim Indent(o, indent) << "<dict>\n"; 170218887Sdim ++indent; 171218887Sdim 172218887Sdim Indent(o, indent) << "<key>kind</key><string>control</string>\n"; 173218887Sdim 174218887Sdim // Emit edges. 175218887Sdim Indent(o, indent) << "<key>edges</key>\n"; 176218887Sdim ++indent; 177218887Sdim Indent(o, indent) << "<array>\n"; 178218887Sdim ++indent; 179218887Sdim for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end(); 180218887Sdim I!=E; ++I) { 181218887Sdim Indent(o, indent) << "<dict>\n"; 182218887Sdim ++indent; 183239462Sdim 184239462Sdim // Make the ranges of the start and end point self-consistent with adjacent edges 185239462Sdim // by forcing to use only the beginning of the range. This simplifies the layout 186239462Sdim // logic for clients. 187218887Sdim Indent(o, indent) << "<key>start</key>\n"; 188239462Sdim SourceLocation StartEdge = I->getStart().asRange().getBegin(); 189239462Sdim EmitRange(o, SM, LangOpts, SourceRange(StartEdge, StartEdge), FM, indent+1); 190239462Sdim 191218887Sdim Indent(o, indent) << "<key>end</key>\n"; 192239462Sdim SourceLocation EndEdge = I->getEnd().asRange().getBegin(); 193239462Sdim EmitRange(o, SM, LangOpts, SourceRange(EndEdge, EndEdge), FM, indent+1); 194239462Sdim 195218887Sdim --indent; 196218887Sdim Indent(o, indent) << "</dict>\n"; 197218887Sdim } 198218887Sdim --indent; 199218887Sdim Indent(o, indent) << "</array>\n"; 200218887Sdim --indent; 201218887Sdim 202218887Sdim // Output any helper text. 203218887Sdim const std::string& s = P.getString(); 204218887Sdim if (!s.empty()) { 205218887Sdim Indent(o, indent) << "<key>alternate</key>"; 206218887Sdim EmitString(o, s) << '\n'; 207218887Sdim } 208218887Sdim 209218887Sdim --indent; 210218887Sdim Indent(o, indent) << "</dict>\n"; 211218887Sdim} 212218887Sdim 213226633Sdimstatic void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, 214218887Sdim const FIDMap& FM, 215218887Sdim const SourceManager &SM, 216218887Sdim const LangOptions &LangOpts, 217234353Sdim unsigned indent, 218234353Sdim unsigned depth) { 219218887Sdim 220218887Sdim Indent(o, indent) << "<dict>\n"; 221218887Sdim ++indent; 222218887Sdim 223218887Sdim Indent(o, indent) << "<key>kind</key><string>event</string>\n"; 224218887Sdim 225218887Sdim // Output the location. 226218887Sdim FullSourceLoc L = P.getLocation().asLocation(); 227218887Sdim 228218887Sdim Indent(o, indent) << "<key>location</key>\n"; 229218887Sdim EmitLocation(o, SM, LangOpts, L, FM, indent); 230218887Sdim 231218887Sdim // Output the ranges (if any). 232239462Sdim ArrayRef<SourceRange> Ranges = P.getRanges(); 233218887Sdim 234239462Sdim if (!Ranges.empty()) { 235218887Sdim Indent(o, indent) << "<key>ranges</key>\n"; 236218887Sdim Indent(o, indent) << "<array>\n"; 237218887Sdim ++indent; 238239462Sdim for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); 239239462Sdim I != E; ++I) { 240239462Sdim EmitRange(o, SM, LangOpts, *I, FM, indent+1); 241239462Sdim } 242218887Sdim --indent; 243218887Sdim Indent(o, indent) << "</array>\n"; 244218887Sdim } 245234353Sdim 246234353Sdim // Output the call depth. 247234353Sdim Indent(o, indent) << "<key>depth</key>" 248234353Sdim << "<integer>" << depth << "</integer>\n"; 249218887Sdim 250218887Sdim // Output the text. 251218887Sdim assert(!P.getString().empty()); 252218887Sdim Indent(o, indent) << "<key>extended_message</key>\n"; 253218887Sdim Indent(o, indent); 254218887Sdim EmitString(o, P.getString()) << '\n'; 255218887Sdim 256218887Sdim // Output the short text. 257218887Sdim // FIXME: Really use a short string. 258218887Sdim Indent(o, indent) << "<key>message</key>\n"; 259243830Sdim Indent(o, indent); 260218887Sdim EmitString(o, P.getString()) << '\n'; 261234353Sdim 262218887Sdim // Finish up. 263218887Sdim --indent; 264218887Sdim Indent(o, indent); o << "</dict>\n"; 265218887Sdim} 266218887Sdim 267234353Sdimstatic void ReportPiece(raw_ostream &o, 268234353Sdim const PathDiagnosticPiece &P, 269234353Sdim const FIDMap& FM, const SourceManager &SM, 270234353Sdim const LangOptions &LangOpts, 271234353Sdim unsigned indent, 272234353Sdim unsigned depth, 273234353Sdim bool includeControlFlow); 274234353Sdim 275234353Sdimstatic void ReportCall(raw_ostream &o, 276234353Sdim const PathDiagnosticCallPiece &P, 277234353Sdim const FIDMap& FM, const SourceManager &SM, 278234353Sdim const LangOptions &LangOpts, 279234353Sdim unsigned indent, 280234353Sdim unsigned depth) { 281234353Sdim 282234353Sdim IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = 283234353Sdim P.getCallEnterEvent(); 284234353Sdim 285234353Sdim if (callEnter) 286234353Sdim ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true); 287234353Sdim 288234353Sdim IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller = 289234353Sdim P.getCallEnterWithinCallerEvent(); 290234353Sdim 291234353Sdim ++depth; 292234353Sdim 293234353Sdim if (callEnterWithinCaller) 294234353Sdim ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts, 295234353Sdim indent, depth, true); 296234353Sdim 297234353Sdim for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) 298234353Sdim ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true); 299251662Sdim 300251662Sdim --depth; 301234353Sdim 302234353Sdim IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = 303234353Sdim P.getCallExitEvent(); 304234353Sdim 305234353Sdim if (callExit) 306234353Sdim ReportPiece(o, *callExit, FM, SM, LangOpts, indent, depth, true); 307234353Sdim} 308234353Sdim 309226633Sdimstatic void ReportMacro(raw_ostream &o, 310218887Sdim const PathDiagnosticMacroPiece& P, 311218887Sdim const FIDMap& FM, const SourceManager &SM, 312218887Sdim const LangOptions &LangOpts, 313234353Sdim unsigned indent, 314234353Sdim unsigned depth) { 315218887Sdim 316234353Sdim for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); 317218887Sdim I!=E; ++I) { 318234353Sdim ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, false); 319218887Sdim } 320218887Sdim} 321218887Sdim 322226633Sdimstatic void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, 323218887Sdim const FIDMap& FM, const SourceManager &SM, 324218887Sdim const LangOptions &LangOpts) { 325234353Sdim ReportPiece(o, P, FM, SM, LangOpts, 4, 0, true); 326234353Sdim} 327218887Sdim 328234353Sdimstatic void ReportPiece(raw_ostream &o, 329234353Sdim const PathDiagnosticPiece &P, 330234353Sdim const FIDMap& FM, const SourceManager &SM, 331234353Sdim const LangOptions &LangOpts, 332234353Sdim unsigned indent, 333234353Sdim unsigned depth, 334234353Sdim bool includeControlFlow) { 335218887Sdim switch (P.getKind()) { 336234353Sdim case PathDiagnosticPiece::ControlFlow: 337234353Sdim if (includeControlFlow) 338234353Sdim ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, 339234353Sdim LangOpts, indent); 340234353Sdim break; 341234353Sdim case PathDiagnosticPiece::Call: 342234353Sdim ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts, 343234353Sdim indent, depth); 344234353Sdim break; 345234353Sdim case PathDiagnosticPiece::Event: 346234353Sdim ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts, 347234353Sdim indent, depth); 348234353Sdim break; 349234353Sdim case PathDiagnosticPiece::Macro: 350234353Sdim ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, 351234353Sdim indent, depth); 352234353Sdim break; 353218887Sdim } 354218887Sdim} 355218887Sdim 356234353Sdimvoid PlistDiagnostics::FlushDiagnosticsImpl( 357234353Sdim std::vector<const PathDiagnostic *> &Diags, 358239462Sdim FilesMade *filesMade) { 359218887Sdim // Build up a set of FIDs that we use by scanning the locations and 360218887Sdim // ranges of the diagnostics. 361218887Sdim FIDMap FM; 362226633Sdim SmallVector<FileID, 10> Fids; 363218887Sdim const SourceManager* SM = 0; 364218887Sdim 365234353Sdim if (!Diags.empty()) 366234353Sdim SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); 367218887Sdim 368234353Sdim 369234353Sdim for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), 370234353Sdim DE = Diags.end(); DI != DE; ++DI) { 371218887Sdim 372218887Sdim const PathDiagnostic *D = *DI; 373218887Sdim 374249423Sdim SmallVector<const PathPieces *, 5> WorkList; 375234353Sdim WorkList.push_back(&D->path); 376218887Sdim 377234353Sdim while (!WorkList.empty()) { 378234353Sdim const PathPieces &path = *WorkList.back(); 379234353Sdim WorkList.pop_back(); 380234353Sdim 381234353Sdim for (PathPieces::const_iterator I = path.begin(), E = path.end(); 382234353Sdim I!=E; ++I) { 383234353Sdim const PathDiagnosticPiece *piece = I->getPtr(); 384234353Sdim AddFID(FM, Fids, SM, piece->getLocation().asLocation()); 385239462Sdim ArrayRef<SourceRange> Ranges = piece->getRanges(); 386239462Sdim for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), 387239462Sdim E = Ranges.end(); I != E; ++I) { 388239462Sdim AddFID(FM, Fids, SM, I->getBegin()); 389239462Sdim AddFID(FM, Fids, SM, I->getEnd()); 390234353Sdim } 391234353Sdim 392234353Sdim if (const PathDiagnosticCallPiece *call = 393234353Sdim dyn_cast<PathDiagnosticCallPiece>(piece)) { 394239462Sdim IntrusiveRefCntPtr<PathDiagnosticEventPiece> 395239462Sdim callEnterWithin = call->getCallEnterWithinCallerEvent(); 396239462Sdim if (callEnterWithin) 397239462Sdim AddFID(FM, Fids, SM, callEnterWithin->getLocation().asLocation()); 398239462Sdim 399234353Sdim WorkList.push_back(&call->path); 400234353Sdim } 401234353Sdim else if (const PathDiagnosticMacroPiece *macro = 402234353Sdim dyn_cast<PathDiagnosticMacroPiece>(piece)) { 403234353Sdim WorkList.push_back(¯o->subPieces); 404234353Sdim } 405218887Sdim } 406218887Sdim } 407218887Sdim } 408218887Sdim 409218887Sdim // Open the file. 410218887Sdim std::string ErrMsg; 411218887Sdim llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg); 412218887Sdim if (!ErrMsg.empty()) { 413234353Sdim llvm::errs() << "warning: could not create file: " << OutputFile << '\n'; 414218887Sdim return; 415218887Sdim } 416218887Sdim 417218887Sdim // Write the plist header. 418218887Sdim o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 419218887Sdim "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " 420218887Sdim "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" 421218887Sdim "<plist version=\"1.0\">\n"; 422218887Sdim 423218887Sdim // Write the root object: a <dict> containing... 424243830Sdim // - "clang_version", the string representation of clang version 425218887Sdim // - "files", an <array> mapping from FIDs to file names 426218887Sdim // - "diagnostics", an <array> containing the path diagnostics 427243830Sdim o << "<dict>\n" << 428243830Sdim " <key>clang_version</key>\n"; 429243830Sdim EmitString(o, getClangFullVersion()) << '\n'; 430243830Sdim o << " <key>files</key>\n" 431218887Sdim " <array>\n"; 432218887Sdim 433226633Sdim for (SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end(); 434218887Sdim I!=E; ++I) { 435218887Sdim o << " "; 436218887Sdim EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n'; 437218887Sdim } 438218887Sdim 439218887Sdim o << " </array>\n" 440218887Sdim " <key>diagnostics</key>\n" 441218887Sdim " <array>\n"; 442218887Sdim 443234353Sdim for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), 444234353Sdim DE = Diags.end(); DI!=DE; ++DI) { 445218887Sdim 446218887Sdim o << " <dict>\n" 447218887Sdim " <key>path</key>\n"; 448218887Sdim 449218887Sdim const PathDiagnostic *D = *DI; 450218887Sdim 451218887Sdim o << " <array>\n"; 452218887Sdim 453234353Sdim for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); 454234353Sdim I != E; ++I) 455234353Sdim ReportDiag(o, **I, FM, *SM, LangOpts); 456218887Sdim 457218887Sdim o << " </array>\n"; 458218887Sdim 459218887Sdim // Output the bug type and bug category. 460218887Sdim o << " <key>description</key>"; 461243830Sdim EmitString(o, D->getShortDescription()) << '\n'; 462218887Sdim o << " <key>category</key>"; 463218887Sdim EmitString(o, D->getCategory()) << '\n'; 464218887Sdim o << " <key>type</key>"; 465218887Sdim EmitString(o, D->getBugType()) << '\n'; 466234353Sdim 467234353Sdim // Output information about the semantic context where 468234353Sdim // the issue occurred. 469234353Sdim if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { 470234353Sdim // FIXME: handle blocks, which have no name. 471234353Sdim if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { 472234353Sdim StringRef declKind; 473234353Sdim switch (ND->getKind()) { 474234353Sdim case Decl::CXXRecord: 475234353Sdim declKind = "C++ class"; 476234353Sdim break; 477234353Sdim case Decl::CXXMethod: 478234353Sdim declKind = "C++ method"; 479234353Sdim break; 480234353Sdim case Decl::ObjCMethod: 481234353Sdim declKind = "Objective-C method"; 482234353Sdim break; 483234353Sdim case Decl::Function: 484234353Sdim declKind = "function"; 485234353Sdim break; 486234353Sdim default: 487234353Sdim break; 488234353Sdim } 489234353Sdim if (!declKind.empty()) { 490234353Sdim const std::string &declName = ND->getDeclName().getAsString(); 491234353Sdim o << " <key>issue_context_kind</key>"; 492234353Sdim EmitString(o, declKind) << '\n'; 493234353Sdim o << " <key>issue_context</key>"; 494234353Sdim EmitString(o, declName) << '\n'; 495234353Sdim } 496239462Sdim 497239462Sdim // Output the bug hash for issue unique-ing. Currently, it's just an 498239462Sdim // offset from the beginning of the function. 499239462Sdim if (const Stmt *Body = DeclWithIssue->getBody()) { 500249423Sdim 501249423Sdim // If the bug uniqueing location exists, use it for the hash. 502249423Sdim // For example, this ensures that two leaks reported on the same line 503249423Sdim // will have different issue_hashes and that the hash will identify 504249423Sdim // the leak location even after code is added between the allocation 505249423Sdim // site and the end of scope (leak report location). 506249423Sdim PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); 507249423Sdim if (UPDLoc.isValid()) { 508249423Sdim FullSourceLoc UL(SM->getExpansionLoc(UPDLoc.asLocation()), 509249423Sdim *SM); 510249423Sdim FullSourceLoc UFunL(SM->getExpansionLoc( 511249423Sdim D->getUniqueingDecl()->getBody()->getLocStart()), *SM); 512249423Sdim o << " <key>issue_hash</key><string>" 513249423Sdim << UL.getExpansionLineNumber() - UFunL.getExpansionLineNumber() 514249423Sdim << "</string>\n"; 515249423Sdim 516249423Sdim // Otherwise, use the location on which the bug is reported. 517249423Sdim } else { 518249423Sdim FullSourceLoc L(SM->getExpansionLoc(D->getLocation().asLocation()), 519239462Sdim *SM); 520249423Sdim FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM); 521249423Sdim o << " <key>issue_hash</key><string>" 522249423Sdim << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() 523249423Sdim << "</string>\n"; 524249423Sdim } 525249423Sdim 526239462Sdim } 527234353Sdim } 528234353Sdim } 529218887Sdim 530218887Sdim // Output the location of the bug. 531218887Sdim o << " <key>location</key>\n"; 532218887Sdim EmitLocation(o, *SM, LangOpts, D->getLocation(), FM, 2); 533218887Sdim 534218887Sdim // Output the diagnostic to the sub-diagnostic client, if any. 535239462Sdim if (!filesMade->empty()) { 536239462Sdim StringRef lastName; 537243830Sdim PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); 538243830Sdim if (files) { 539243830Sdim for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), 540243830Sdim CE = files->end(); CI != CE; ++CI) { 541243830Sdim StringRef newName = CI->first; 542243830Sdim if (newName != lastName) { 543243830Sdim if (!lastName.empty()) { 544243830Sdim o << " </array>\n"; 545243830Sdim } 546243830Sdim lastName = newName; 547243830Sdim o << " <key>" << lastName << "_files</key>\n"; 548243830Sdim o << " <array>\n"; 549243830Sdim } 550243830Sdim o << " <string>" << CI->second << "</string>\n"; 551239462Sdim } 552243830Sdim o << " </array>\n"; 553218887Sdim } 554218887Sdim } 555218887Sdim 556218887Sdim // Close up the entry. 557218887Sdim o << " </dict>\n"; 558218887Sdim } 559218887Sdim 560218887Sdim o << " </array>\n"; 561218887Sdim 562218887Sdim // Finish. 563243830Sdim o << "</dict>\n</plist>"; 564218887Sdim} 565