1 //===--- PlistReporter.cpp - ARC Migrate Tool Plist Reporter ----*- 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 #include "Internals.h" 11 #include "clang/Lex/Lexer.h" 12 #include "clang/Basic/SourceManager.h" 13 #include "clang/Basic/FileManager.h" 14 15 using namespace clang; 16 using namespace arcmt; 17 using llvm::StringRef; 18 19 // FIXME: This duplicates significant functionality from PlistDiagnostics.cpp, 20 // it would be jolly good if there was a reusable PlistWriter or something. 21 22 typedef llvm::DenseMap<FileID, unsigned> FIDMap; 23 24 static void AddFID(FIDMap &FIDs, llvm::SmallVectorImpl<FileID> &V, 25 const SourceManager &SM, SourceLocation L) { 26 27 FileID FID = SM.getFileID(SM.getInstantiationLoc(L)); 28 FIDMap::iterator I = FIDs.find(FID); 29 if (I != FIDs.end()) return; 30 FIDs[FID] = V.size(); 31 V.push_back(FID); 32 } 33 34 static unsigned GetFID(const FIDMap& FIDs, const SourceManager &SM, 35 SourceLocation L) { 36 FileID FID = SM.getFileID(SM.getInstantiationLoc(L)); 37 FIDMap::const_iterator I = FIDs.find(FID); 38 assert(I != FIDs.end()); 39 return I->second; 40 } 41 42 static llvm::raw_ostream& Indent(llvm::raw_ostream& o, const unsigned indent) { 43 for (unsigned i = 0; i < indent; ++i) o << ' '; 44 return o; 45 } 46 47 static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM, 48 const LangOptions &LangOpts, 49 SourceLocation L, const FIDMap &FM, 50 unsigned indent, bool extend = false) { 51 52 FullSourceLoc Loc(SM.getInstantiationLoc(L), const_cast<SourceManager&>(SM)); 53 54 // Add in the length of the token, so that we cover multi-char tokens. 55 unsigned offset = 56 extend ? Lexer::MeasureTokenLength(Loc, SM, LangOpts) - 1 : 0; 57 58 Indent(o, indent) << "<dict>\n"; 59 Indent(o, indent) << " <key>line</key><integer>" 60 << Loc.getInstantiationLineNumber() << "</integer>\n"; 61 Indent(o, indent) << " <key>col</key><integer>" 62 << Loc.getInstantiationColumnNumber() + offset << "</integer>\n"; 63 Indent(o, indent) << " <key>file</key><integer>" 64 << GetFID(FM, SM, Loc) << "</integer>\n"; 65 Indent(o, indent) << "</dict>\n"; 66 } 67 68 static void EmitRange(llvm::raw_ostream& o, const SourceManager &SM, 69 const LangOptions &LangOpts, 70 CharSourceRange R, const FIDMap &FM, 71 unsigned indent) { 72 Indent(o, indent) << "<array>\n"; 73 EmitLocation(o, SM, LangOpts, R.getBegin(), FM, indent+1); 74 EmitLocation(o, SM, LangOpts, R.getEnd(), FM, indent+1, R.isTokenRange()); 75 Indent(o, indent) << "</array>\n"; 76 } 77 78 static llvm::raw_ostream& EmitString(llvm::raw_ostream& o, 79 StringRef s) { 80 o << "<string>"; 81 for (StringRef::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) { 82 char c = *I; 83 switch (c) { 84 default: o << c; break; 85 case '&': o << "&"; break; 86 case '<': o << "<"; break; 87 case '>': o << ">"; break; 88 case '\'': o << "'"; break; 89 case '\"': o << """; break; 90 } 91 } 92 o << "</string>"; 93 return o; 94 } 95 96 void arcmt::writeARCDiagsToPlist(const std::string &outPath, 97 llvm::ArrayRef<StoredDiagnostic> diags, 98 SourceManager &SM, 99 const LangOptions &LangOpts) { 100 DiagnosticIDs DiagIDs; 101 102 // Build up a set of FIDs that we use by scanning the locations and 103 // ranges of the diagnostics. 104 FIDMap FM; 105 llvm::SmallVector<FileID, 10> Fids; 106 107 for (llvm::ArrayRef<StoredDiagnostic>::iterator 108 I = diags.begin(), E = diags.end(); I != E; ++I) { 109 const StoredDiagnostic &D = *I; 110 111 AddFID(FM, Fids, SM, D.getLocation()); 112 113 for (StoredDiagnostic::range_iterator 114 RI = D.range_begin(), RE = D.range_end(); RI != RE; ++RI) { 115 AddFID(FM, Fids, SM, RI->getBegin()); 116 AddFID(FM, Fids, SM, RI->getEnd()); 117 } 118 } 119 120 std::string errMsg; 121 llvm::raw_fd_ostream o(outPath.c_str(), errMsg); 122 if (!errMsg.empty()) { 123 llvm::errs() << "error: could not create file: " << outPath << '\n'; 124 return; 125 } 126 127 // Write the plist header. 128 o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 129 "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " 130 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" 131 "<plist version=\"1.0\">\n"; 132 133 // Write the root object: a <dict> containing... 134 // - "files", an <array> mapping from FIDs to file names 135 // - "diagnostics", an <array> containing the diagnostics 136 o << "<dict>\n" 137 " <key>files</key>\n" 138 " <array>\n"; 139 140 for (llvm::SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end(); 141 I!=E; ++I) { 142 o << " "; 143 EmitString(o, SM.getFileEntryForID(*I)->getName()) << '\n'; 144 } 145 146 o << " </array>\n" 147 " <key>diagnostics</key>\n" 148 " <array>\n"; 149 150 for (llvm::ArrayRef<StoredDiagnostic>::iterator 151 DI = diags.begin(), DE = diags.end(); DI != DE; ++DI) { 152 153 const StoredDiagnostic &D = *DI; 154 155 if (D.getLevel() == Diagnostic::Ignored) 156 continue; 157 158 o << " <dict>\n"; 159 160 // Output the diagnostic. 161 o << " <key>description</key>"; 162 EmitString(o, D.getMessage()) << '\n'; 163 o << " <key>category</key>"; 164 EmitString(o, DiagIDs.getCategoryNameFromID( 165 DiagIDs.getCategoryNumberForDiag(D.getID()))) << '\n'; 166 o << " <key>type</key>"; 167 if (D.getLevel() >= Diagnostic::Error) 168 EmitString(o, "error") << '\n'; 169 else if (D.getLevel() == Diagnostic::Warning) 170 EmitString(o, "warning") << '\n'; 171 else 172 EmitString(o, "note") << '\n'; 173 174 // Output the location of the bug. 175 o << " <key>location</key>\n"; 176 EmitLocation(o, SM, LangOpts, D.getLocation(), FM, 2); 177 178 // Output the ranges (if any). 179 StoredDiagnostic::range_iterator RI = D.range_begin(), RE = D.range_end(); 180 181 if (RI != RE) { 182 o << " <key>ranges</key>\n"; 183 o << " <array>\n"; 184 for (; RI != RE; ++RI) 185 EmitRange(o, SM, LangOpts, *RI, FM, 4); 186 o << " </array>\n"; 187 } 188 189 // Close up the entry. 190 o << " </dict>\n"; 191 } 192 193 o << " </array>\n"; 194 195 // Finish. 196 o << "</dict>\n</plist>"; 197 } 198