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; } 53234353Sdim virtual bool supportsCrossFileDiagnostics() const { 54234353Sdim return SupportsCrossFileDiagnostics; 55234353Sdim } 56218887Sdim }; 57218887Sdim} // end anonymous namespace 58218887Sdim 59249423SdimPlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, 60249423Sdim const std::string& output, 61218887Sdim const LangOptions &LO, 62239462Sdim bool supportsMultipleFiles) 63249423Sdim : OutputFile(output), 64249423Sdim LangOpts(LO), 65234353Sdim SupportsCrossFileDiagnostics(supportsMultipleFiles) {} 66218887Sdim 67249423Sdimvoid ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, 68249423Sdim PathDiagnosticConsumers &C, 69239462Sdim const std::string& s, 70239462Sdim const Preprocessor &PP) { 71249423Sdim C.push_back(new PlistDiagnostics(AnalyzerOpts, s, 72249423Sdim PP.getLangOpts(), false)); 73218887Sdim} 74218887Sdim 75249423Sdimvoid ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, 76249423Sdim PathDiagnosticConsumers &C, 77239462Sdim const std::string &s, 78239462Sdim const Preprocessor &PP) { 79249423Sdim C.push_back(new PlistDiagnostics(AnalyzerOpts, s, 80249423Sdim PP.getLangOpts(), true)); 81234353Sdim} 82234353Sdim 83226633Sdimstatic void AddFID(FIDMap &FIDs, SmallVectorImpl<FileID> &V, 84218887Sdim const SourceManager* SM, SourceLocation L) { 85218887Sdim 86226633Sdim FileID FID = SM->getFileID(SM->getExpansionLoc(L)); 87218887Sdim FIDMap::iterator I = FIDs.find(FID); 88218887Sdim if (I != FIDs.end()) return; 89218887Sdim FIDs[FID] = V.size(); 90218887Sdim V.push_back(FID); 91218887Sdim} 92218887Sdim 93218887Sdimstatic unsigned GetFID(const FIDMap& FIDs, const SourceManager &SM, 94218887Sdim SourceLocation L) { 95226633Sdim FileID FID = SM.getFileID(SM.getExpansionLoc(L)); 96218887Sdim FIDMap::const_iterator I = FIDs.find(FID); 97218887Sdim assert(I != FIDs.end()); 98218887Sdim return I->second; 99218887Sdim} 100218887Sdim 101226633Sdimstatic raw_ostream &Indent(raw_ostream &o, const unsigned indent) { 102218887Sdim for (unsigned i = 0; i < indent; ++i) o << ' '; 103218887Sdim return o; 104218887Sdim} 105218887Sdim 106226633Sdimstatic void EmitLocation(raw_ostream &o, const SourceManager &SM, 107218887Sdim const LangOptions &LangOpts, 108218887Sdim SourceLocation L, const FIDMap &FM, 109218887Sdim unsigned indent, bool extend = false) { 110218887Sdim 111226633Sdim FullSourceLoc Loc(SM.getExpansionLoc(L), const_cast<SourceManager&>(SM)); 112218887Sdim 113218887Sdim // Add in the length of the token, so that we cover multi-char tokens. 114218887Sdim unsigned offset = 115218887Sdim extend ? Lexer::MeasureTokenLength(Loc, SM, LangOpts) - 1 : 0; 116218887Sdim 117218887Sdim Indent(o, indent) << "<dict>\n"; 118218887Sdim Indent(o, indent) << " <key>line</key><integer>" 119226633Sdim << Loc.getExpansionLineNumber() << "</integer>\n"; 120218887Sdim Indent(o, indent) << " <key>col</key><integer>" 121226633Sdim << Loc.getExpansionColumnNumber() + offset << "</integer>\n"; 122218887Sdim Indent(o, indent) << " <key>file</key><integer>" 123218887Sdim << GetFID(FM, SM, Loc) << "</integer>\n"; 124218887Sdim Indent(o, indent) << "</dict>\n"; 125218887Sdim} 126218887Sdim 127226633Sdimstatic void EmitLocation(raw_ostream &o, const SourceManager &SM, 128218887Sdim const LangOptions &LangOpts, 129218887Sdim const PathDiagnosticLocation &L, const FIDMap& FM, 130218887Sdim unsigned indent, bool extend = false) { 131218887Sdim EmitLocation(o, SM, LangOpts, L.asLocation(), FM, indent, extend); 132218887Sdim} 133218887Sdim 134226633Sdimstatic void EmitRange(raw_ostream &o, const SourceManager &SM, 135218887Sdim const LangOptions &LangOpts, 136218887Sdim PathDiagnosticRange R, const FIDMap &FM, 137218887Sdim unsigned indent) { 138218887Sdim Indent(o, indent) << "<array>\n"; 139218887Sdim EmitLocation(o, SM, LangOpts, R.getBegin(), FM, indent+1); 140218887Sdim EmitLocation(o, SM, LangOpts, R.getEnd(), FM, indent+1, !R.isPoint); 141218887Sdim Indent(o, indent) << "</array>\n"; 142218887Sdim} 143218887Sdim 144234353Sdimstatic raw_ostream &EmitString(raw_ostream &o, StringRef s) { 145218887Sdim o << "<string>"; 146234353Sdim for (StringRef::const_iterator I = s.begin(), E = s.end(); I != E; ++I) { 147218887Sdim char c = *I; 148218887Sdim switch (c) { 149218887Sdim default: o << c; break; 150218887Sdim case '&': o << "&"; break; 151218887Sdim case '<': o << "<"; break; 152218887Sdim case '>': o << ">"; break; 153218887Sdim case '\'': o << "'"; break; 154218887Sdim case '\"': o << """; break; 155218887Sdim } 156218887Sdim } 157218887Sdim o << "</string>"; 158218887Sdim return o; 159218887Sdim} 160218887Sdim 161226633Sdimstatic void ReportControlFlow(raw_ostream &o, 162218887Sdim const PathDiagnosticControlFlowPiece& P, 163218887Sdim const FIDMap& FM, 164218887Sdim const SourceManager &SM, 165218887Sdim const LangOptions &LangOpts, 166218887Sdim unsigned indent) { 167218887Sdim 168218887Sdim Indent(o, indent) << "<dict>\n"; 169218887Sdim ++indent; 170218887Sdim 171218887Sdim Indent(o, indent) << "<key>kind</key><string>control</string>\n"; 172218887Sdim 173218887Sdim // Emit edges. 174218887Sdim Indent(o, indent) << "<key>edges</key>\n"; 175218887Sdim ++indent; 176218887Sdim Indent(o, indent) << "<array>\n"; 177218887Sdim ++indent; 178218887Sdim for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end(); 179218887Sdim I!=E; ++I) { 180218887Sdim Indent(o, indent) << "<dict>\n"; 181218887Sdim ++indent; 182239462Sdim 183239462Sdim // Make the ranges of the start and end point self-consistent with adjacent edges 184239462Sdim // by forcing to use only the beginning of the range. This simplifies the layout 185239462Sdim // logic for clients. 186218887Sdim Indent(o, indent) << "<key>start</key>\n"; 187239462Sdim SourceLocation StartEdge = I->getStart().asRange().getBegin(); 188239462Sdim EmitRange(o, SM, LangOpts, SourceRange(StartEdge, StartEdge), FM, indent+1); 189239462Sdim 190218887Sdim Indent(o, indent) << "<key>end</key>\n"; 191239462Sdim SourceLocation EndEdge = I->getEnd().asRange().getBegin(); 192239462Sdim EmitRange(o, SM, LangOpts, SourceRange(EndEdge, EndEdge), FM, indent+1); 193239462Sdim 194218887Sdim --indent; 195218887Sdim Indent(o, indent) << "</dict>\n"; 196218887Sdim } 197218887Sdim --indent; 198218887Sdim Indent(o, indent) << "</array>\n"; 199218887Sdim --indent; 200218887Sdim 201218887Sdim // Output any helper text. 202218887Sdim const std::string& s = P.getString(); 203218887Sdim if (!s.empty()) { 204218887Sdim Indent(o, indent) << "<key>alternate</key>"; 205218887Sdim EmitString(o, s) << '\n'; 206218887Sdim } 207218887Sdim 208218887Sdim --indent; 209218887Sdim Indent(o, indent) << "</dict>\n"; 210218887Sdim} 211218887Sdim 212226633Sdimstatic void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, 213218887Sdim const FIDMap& FM, 214218887Sdim const SourceManager &SM, 215218887Sdim const LangOptions &LangOpts, 216234353Sdim unsigned indent, 217263508Sdim unsigned depth, 218263508Sdim bool isKeyEvent = false) { 219218887Sdim 220218887Sdim Indent(o, indent) << "<dict>\n"; 221218887Sdim ++indent; 222218887Sdim 223218887Sdim Indent(o, indent) << "<key>kind</key><string>event</string>\n"; 224218887Sdim 225263508Sdim if (isKeyEvent) { 226263508Sdim Indent(o, indent) << "<key>key_event</key><true/>\n"; 227263508Sdim } 228263508Sdim 229218887Sdim // Output the location. 230218887Sdim FullSourceLoc L = P.getLocation().asLocation(); 231218887Sdim 232218887Sdim Indent(o, indent) << "<key>location</key>\n"; 233218887Sdim EmitLocation(o, SM, LangOpts, L, FM, indent); 234218887Sdim 235218887Sdim // Output the ranges (if any). 236239462Sdim ArrayRef<SourceRange> Ranges = P.getRanges(); 237218887Sdim 238239462Sdim if (!Ranges.empty()) { 239218887Sdim Indent(o, indent) << "<key>ranges</key>\n"; 240218887Sdim Indent(o, indent) << "<array>\n"; 241218887Sdim ++indent; 242239462Sdim for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); 243239462Sdim I != E; ++I) { 244239462Sdim EmitRange(o, SM, LangOpts, *I, FM, indent+1); 245239462Sdim } 246218887Sdim --indent; 247218887Sdim Indent(o, indent) << "</array>\n"; 248218887Sdim } 249234353Sdim 250234353Sdim // Output the call depth. 251234353Sdim Indent(o, indent) << "<key>depth</key>" 252234353Sdim << "<integer>" << depth << "</integer>\n"; 253218887Sdim 254218887Sdim // Output the text. 255218887Sdim assert(!P.getString().empty()); 256218887Sdim Indent(o, indent) << "<key>extended_message</key>\n"; 257218887Sdim Indent(o, indent); 258218887Sdim EmitString(o, P.getString()) << '\n'; 259218887Sdim 260218887Sdim // Output the short text. 261218887Sdim // FIXME: Really use a short string. 262218887Sdim Indent(o, indent) << "<key>message</key>\n"; 263243830Sdim Indent(o, indent); 264218887Sdim EmitString(o, P.getString()) << '\n'; 265234353Sdim 266218887Sdim // Finish up. 267218887Sdim --indent; 268218887Sdim Indent(o, indent); o << "</dict>\n"; 269218887Sdim} 270218887Sdim 271234353Sdimstatic void ReportPiece(raw_ostream &o, 272234353Sdim const PathDiagnosticPiece &P, 273234353Sdim const FIDMap& FM, const SourceManager &SM, 274234353Sdim const LangOptions &LangOpts, 275234353Sdim unsigned indent, 276234353Sdim unsigned depth, 277263508Sdim bool includeControlFlow, 278263508Sdim bool isKeyEvent = false); 279234353Sdim 280234353Sdimstatic void ReportCall(raw_ostream &o, 281234353Sdim const PathDiagnosticCallPiece &P, 282234353Sdim const FIDMap& FM, const SourceManager &SM, 283234353Sdim const LangOptions &LangOpts, 284234353Sdim unsigned indent, 285234353Sdim unsigned depth) { 286234353Sdim 287234353Sdim IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = 288234353Sdim P.getCallEnterEvent(); 289234353Sdim 290234353Sdim if (callEnter) 291263508Sdim ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true, 292263508Sdim P.isLastInMainSourceFile()); 293234353Sdim 294234353Sdim IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller = 295234353Sdim P.getCallEnterWithinCallerEvent(); 296234353Sdim 297234353Sdim ++depth; 298234353Sdim 299234353Sdim if (callEnterWithinCaller) 300234353Sdim ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts, 301234353Sdim indent, depth, true); 302234353Sdim 303234353Sdim for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) 304234353Sdim ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true); 305251662Sdim 306251662Sdim --depth; 307234353Sdim 308234353Sdim IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = 309234353Sdim P.getCallExitEvent(); 310234353Sdim 311234353Sdim if (callExit) 312234353Sdim ReportPiece(o, *callExit, FM, SM, LangOpts, indent, depth, true); 313234353Sdim} 314234353Sdim 315226633Sdimstatic void ReportMacro(raw_ostream &o, 316218887Sdim const PathDiagnosticMacroPiece& P, 317218887Sdim const FIDMap& FM, const SourceManager &SM, 318218887Sdim const LangOptions &LangOpts, 319234353Sdim unsigned indent, 320234353Sdim unsigned depth) { 321218887Sdim 322234353Sdim for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); 323218887Sdim I!=E; ++I) { 324234353Sdim ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, false); 325218887Sdim } 326218887Sdim} 327218887Sdim 328226633Sdimstatic void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, 329218887Sdim const FIDMap& FM, const SourceManager &SM, 330218887Sdim const LangOptions &LangOpts) { 331234353Sdim ReportPiece(o, P, FM, SM, LangOpts, 4, 0, true); 332234353Sdim} 333218887Sdim 334234353Sdimstatic void ReportPiece(raw_ostream &o, 335234353Sdim const PathDiagnosticPiece &P, 336234353Sdim const FIDMap& FM, const SourceManager &SM, 337234353Sdim const LangOptions &LangOpts, 338234353Sdim unsigned indent, 339234353Sdim unsigned depth, 340263508Sdim bool includeControlFlow, 341263508Sdim bool isKeyEvent) { 342218887Sdim switch (P.getKind()) { 343234353Sdim case PathDiagnosticPiece::ControlFlow: 344234353Sdim if (includeControlFlow) 345234353Sdim ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, 346234353Sdim LangOpts, indent); 347234353Sdim break; 348234353Sdim case PathDiagnosticPiece::Call: 349234353Sdim ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts, 350234353Sdim indent, depth); 351234353Sdim break; 352234353Sdim case PathDiagnosticPiece::Event: 353234353Sdim ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts, 354263508Sdim indent, depth, isKeyEvent); 355234353Sdim break; 356234353Sdim case PathDiagnosticPiece::Macro: 357234353Sdim ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, 358234353Sdim indent, depth); 359234353Sdim break; 360218887Sdim } 361218887Sdim} 362218887Sdim 363234353Sdimvoid PlistDiagnostics::FlushDiagnosticsImpl( 364234353Sdim std::vector<const PathDiagnostic *> &Diags, 365239462Sdim FilesMade *filesMade) { 366218887Sdim // Build up a set of FIDs that we use by scanning the locations and 367218887Sdim // ranges of the diagnostics. 368218887Sdim FIDMap FM; 369226633Sdim SmallVector<FileID, 10> Fids; 370218887Sdim const SourceManager* SM = 0; 371218887Sdim 372234353Sdim if (!Diags.empty()) 373234353Sdim SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); 374218887Sdim 375234353Sdim 376234353Sdim for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), 377234353Sdim DE = Diags.end(); DI != DE; ++DI) { 378218887Sdim 379218887Sdim const PathDiagnostic *D = *DI; 380218887Sdim 381249423Sdim SmallVector<const PathPieces *, 5> WorkList; 382234353Sdim WorkList.push_back(&D->path); 383218887Sdim 384234353Sdim while (!WorkList.empty()) { 385263508Sdim const PathPieces &path = *WorkList.pop_back_val(); 386263508Sdim 387263508Sdim for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; 388263508Sdim ++I) { 389234353Sdim const PathDiagnosticPiece *piece = I->getPtr(); 390234353Sdim AddFID(FM, Fids, SM, piece->getLocation().asLocation()); 391239462Sdim ArrayRef<SourceRange> Ranges = piece->getRanges(); 392239462Sdim for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), 393239462Sdim E = Ranges.end(); I != E; ++I) { 394239462Sdim AddFID(FM, Fids, SM, I->getBegin()); 395239462Sdim AddFID(FM, Fids, SM, I->getEnd()); 396234353Sdim } 397234353Sdim 398234353Sdim if (const PathDiagnosticCallPiece *call = 399234353Sdim dyn_cast<PathDiagnosticCallPiece>(piece)) { 400239462Sdim IntrusiveRefCntPtr<PathDiagnosticEventPiece> 401239462Sdim callEnterWithin = call->getCallEnterWithinCallerEvent(); 402239462Sdim if (callEnterWithin) 403239462Sdim AddFID(FM, Fids, SM, callEnterWithin->getLocation().asLocation()); 404239462Sdim 405234353Sdim WorkList.push_back(&call->path); 406234353Sdim } 407234353Sdim else if (const PathDiagnosticMacroPiece *macro = 408234353Sdim dyn_cast<PathDiagnosticMacroPiece>(piece)) { 409234353Sdim WorkList.push_back(¯o->subPieces); 410234353Sdim } 411218887Sdim } 412218887Sdim } 413218887Sdim } 414218887Sdim 415218887Sdim // Open the file. 416218887Sdim std::string ErrMsg; 417218887Sdim llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg); 418218887Sdim if (!ErrMsg.empty()) { 419234353Sdim llvm::errs() << "warning: could not create file: " << OutputFile << '\n'; 420218887Sdim return; 421218887Sdim } 422218887Sdim 423218887Sdim // Write the plist header. 424218887Sdim o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 425218887Sdim "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " 426218887Sdim "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" 427218887Sdim "<plist version=\"1.0\">\n"; 428218887Sdim 429218887Sdim // Write the root object: a <dict> containing... 430243830Sdim // - "clang_version", the string representation of clang version 431218887Sdim // - "files", an <array> mapping from FIDs to file names 432218887Sdim // - "diagnostics", an <array> containing the path diagnostics 433243830Sdim o << "<dict>\n" << 434243830Sdim " <key>clang_version</key>\n"; 435243830Sdim EmitString(o, getClangFullVersion()) << '\n'; 436243830Sdim o << " <key>files</key>\n" 437218887Sdim " <array>\n"; 438218887Sdim 439226633Sdim for (SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end(); 440218887Sdim I!=E; ++I) { 441218887Sdim o << " "; 442218887Sdim EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n'; 443218887Sdim } 444218887Sdim 445218887Sdim o << " </array>\n" 446218887Sdim " <key>diagnostics</key>\n" 447218887Sdim " <array>\n"; 448218887Sdim 449234353Sdim for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), 450234353Sdim DE = Diags.end(); DI!=DE; ++DI) { 451218887Sdim 452218887Sdim o << " <dict>\n" 453218887Sdim " <key>path</key>\n"; 454218887Sdim 455218887Sdim const PathDiagnostic *D = *DI; 456218887Sdim 457218887Sdim o << " <array>\n"; 458218887Sdim 459234353Sdim for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); 460234353Sdim I != E; ++I) 461234353Sdim ReportDiag(o, **I, FM, *SM, LangOpts); 462218887Sdim 463218887Sdim o << " </array>\n"; 464218887Sdim 465218887Sdim // Output the bug type and bug category. 466218887Sdim o << " <key>description</key>"; 467243830Sdim EmitString(o, D->getShortDescription()) << '\n'; 468218887Sdim o << " <key>category</key>"; 469218887Sdim EmitString(o, D->getCategory()) << '\n'; 470218887Sdim o << " <key>type</key>"; 471218887Sdim EmitString(o, D->getBugType()) << '\n'; 472234353Sdim 473234353Sdim // Output information about the semantic context where 474234353Sdim // the issue occurred. 475234353Sdim if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { 476234353Sdim // FIXME: handle blocks, which have no name. 477234353Sdim if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { 478234353Sdim StringRef declKind; 479234353Sdim switch (ND->getKind()) { 480234353Sdim case Decl::CXXRecord: 481234353Sdim declKind = "C++ class"; 482234353Sdim break; 483234353Sdim case Decl::CXXMethod: 484234353Sdim declKind = "C++ method"; 485234353Sdim break; 486234353Sdim case Decl::ObjCMethod: 487234353Sdim declKind = "Objective-C method"; 488234353Sdim break; 489234353Sdim case Decl::Function: 490234353Sdim declKind = "function"; 491234353Sdim break; 492234353Sdim default: 493234353Sdim break; 494234353Sdim } 495234353Sdim if (!declKind.empty()) { 496234353Sdim const std::string &declName = ND->getDeclName().getAsString(); 497234353Sdim o << " <key>issue_context_kind</key>"; 498234353Sdim EmitString(o, declKind) << '\n'; 499234353Sdim o << " <key>issue_context</key>"; 500234353Sdim EmitString(o, declName) << '\n'; 501234353Sdim } 502239462Sdim 503239462Sdim // Output the bug hash for issue unique-ing. Currently, it's just an 504239462Sdim // offset from the beginning of the function. 505239462Sdim if (const Stmt *Body = DeclWithIssue->getBody()) { 506249423Sdim 507249423Sdim // If the bug uniqueing location exists, use it for the hash. 508249423Sdim // For example, this ensures that two leaks reported on the same line 509249423Sdim // will have different issue_hashes and that the hash will identify 510249423Sdim // the leak location even after code is added between the allocation 511249423Sdim // site and the end of scope (leak report location). 512249423Sdim PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); 513249423Sdim if (UPDLoc.isValid()) { 514249423Sdim FullSourceLoc UL(SM->getExpansionLoc(UPDLoc.asLocation()), 515249423Sdim *SM); 516249423Sdim FullSourceLoc UFunL(SM->getExpansionLoc( 517249423Sdim D->getUniqueingDecl()->getBody()->getLocStart()), *SM); 518249423Sdim o << " <key>issue_hash</key><string>" 519249423Sdim << UL.getExpansionLineNumber() - UFunL.getExpansionLineNumber() 520249423Sdim << "</string>\n"; 521249423Sdim 522249423Sdim // Otherwise, use the location on which the bug is reported. 523249423Sdim } else { 524249423Sdim FullSourceLoc L(SM->getExpansionLoc(D->getLocation().asLocation()), 525239462Sdim *SM); 526249423Sdim FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM); 527249423Sdim o << " <key>issue_hash</key><string>" 528249423Sdim << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() 529249423Sdim << "</string>\n"; 530249423Sdim } 531249423Sdim 532239462Sdim } 533234353Sdim } 534234353Sdim } 535218887Sdim 536218887Sdim // Output the location of the bug. 537218887Sdim o << " <key>location</key>\n"; 538218887Sdim EmitLocation(o, *SM, LangOpts, D->getLocation(), FM, 2); 539218887Sdim 540218887Sdim // Output the diagnostic to the sub-diagnostic client, if any. 541239462Sdim if (!filesMade->empty()) { 542239462Sdim StringRef lastName; 543243830Sdim PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); 544243830Sdim if (files) { 545243830Sdim for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), 546243830Sdim CE = files->end(); CI != CE; ++CI) { 547243830Sdim StringRef newName = CI->first; 548243830Sdim if (newName != lastName) { 549243830Sdim if (!lastName.empty()) { 550243830Sdim o << " </array>\n"; 551243830Sdim } 552243830Sdim lastName = newName; 553243830Sdim o << " <key>" << lastName << "_files</key>\n"; 554243830Sdim o << " <array>\n"; 555243830Sdim } 556243830Sdim o << " <string>" << CI->second << "</string>\n"; 557239462Sdim } 558243830Sdim o << " </array>\n"; 559218887Sdim } 560218887Sdim } 561218887Sdim 562218887Sdim // Close up the entry. 563218887Sdim o << " </dict>\n"; 564218887Sdim } 565218887Sdim 566218887Sdim o << " </array>\n"; 567218887Sdim 568218887Sdim // Finish. 569243830Sdim o << "</dict>\n</plist>"; 570218887Sdim} 571