clang API Documentation
00001 //===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- C++ -*-===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file defines the PlistDiagnostics object. 00011 // 00012 //===----------------------------------------------------------------------===// 00013 00014 #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" 00015 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 00016 #include "clang/Basic/SourceManager.h" 00017 #include "clang/Basic/FileManager.h" 00018 #include "clang/Lex/Preprocessor.h" 00019 #include "llvm/Support/raw_ostream.h" 00020 #include "llvm/Support/Casting.h" 00021 #include "llvm/ADT/DenseMap.h" 00022 #include "llvm/ADT/SmallVector.h" 00023 using namespace clang; 00024 using namespace ento; 00025 00026 typedef llvm::DenseMap<FileID, unsigned> FIDMap; 00027 00028 00029 namespace { 00030 class PlistDiagnostics : public PathDiagnosticConsumer { 00031 const std::string OutputFile; 00032 const LangOptions &LangOpts; 00033 OwningPtr<PathDiagnosticConsumer> SubPD; 00034 bool flushed; 00035 const bool SupportsCrossFileDiagnostics; 00036 public: 00037 PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, 00038 bool supportsMultipleFiles, 00039 PathDiagnosticConsumer *subPD); 00040 00041 virtual ~PlistDiagnostics() {} 00042 00043 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, 00044 SmallVectorImpl<std::string> *FilesMade); 00045 00046 virtual StringRef getName() const { 00047 return "PlistDiagnostics"; 00048 } 00049 00050 PathGenerationScheme getGenerationScheme() const; 00051 bool supportsLogicalOpControlFlow() const { return true; } 00052 bool supportsAllBlockEdges() const { return true; } 00053 virtual bool useVerboseDescription() const { return false; } 00054 virtual bool supportsCrossFileDiagnostics() const { 00055 return SupportsCrossFileDiagnostics; 00056 } 00057 }; 00058 } // end anonymous namespace 00059 00060 PlistDiagnostics::PlistDiagnostics(const std::string& output, 00061 const LangOptions &LO, 00062 bool supportsMultipleFiles, 00063 PathDiagnosticConsumer *subPD) 00064 : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false), 00065 SupportsCrossFileDiagnostics(supportsMultipleFiles) {} 00066 00067 PathDiagnosticConsumer* 00068 ento::createPlistDiagnosticConsumer(const std::string& s, const Preprocessor &PP, 00069 PathDiagnosticConsumer *subPD) { 00070 return new PlistDiagnostics(s, PP.getLangOpts(), false, subPD); 00071 } 00072 00073 PathDiagnosticConsumer* 00074 ento::createPlistMultiFileDiagnosticConsumer(const std::string &s, 00075 const Preprocessor &PP) { 00076 return new PlistDiagnostics(s, PP.getLangOpts(), true, 0); 00077 } 00078 00079 PathDiagnosticConsumer::PathGenerationScheme 00080 PlistDiagnostics::getGenerationScheme() const { 00081 if (const PathDiagnosticConsumer *PD = SubPD.get()) 00082 return PD->getGenerationScheme(); 00083 00084 return Extensive; 00085 } 00086 00087 static void AddFID(FIDMap &FIDs, SmallVectorImpl<FileID> &V, 00088 const SourceManager* SM, SourceLocation L) { 00089 00090 FileID FID = SM->getFileID(SM->getExpansionLoc(L)); 00091 FIDMap::iterator I = FIDs.find(FID); 00092 if (I != FIDs.end()) return; 00093 FIDs[FID] = V.size(); 00094 V.push_back(FID); 00095 } 00096 00097 static unsigned GetFID(const FIDMap& FIDs, const SourceManager &SM, 00098 SourceLocation L) { 00099 FileID FID = SM.getFileID(SM.getExpansionLoc(L)); 00100 FIDMap::const_iterator I = FIDs.find(FID); 00101 assert(I != FIDs.end()); 00102 return I->second; 00103 } 00104 00105 static raw_ostream &Indent(raw_ostream &o, const unsigned indent) { 00106 for (unsigned i = 0; i < indent; ++i) o << ' '; 00107 return o; 00108 } 00109 00110 static void EmitLocation(raw_ostream &o, const SourceManager &SM, 00111 const LangOptions &LangOpts, 00112 SourceLocation L, const FIDMap &FM, 00113 unsigned indent, bool extend = false) { 00114 00115 FullSourceLoc Loc(SM.getExpansionLoc(L), const_cast<SourceManager&>(SM)); 00116 00117 // Add in the length of the token, so that we cover multi-char tokens. 00118 unsigned offset = 00119 extend ? Lexer::MeasureTokenLength(Loc, SM, LangOpts) - 1 : 0; 00120 00121 Indent(o, indent) << "<dict>\n"; 00122 Indent(o, indent) << " <key>line</key><integer>" 00123 << Loc.getExpansionLineNumber() << "</integer>\n"; 00124 Indent(o, indent) << " <key>col</key><integer>" 00125 << Loc.getExpansionColumnNumber() + offset << "</integer>\n"; 00126 Indent(o, indent) << " <key>file</key><integer>" 00127 << GetFID(FM, SM, Loc) << "</integer>\n"; 00128 Indent(o, indent) << "</dict>\n"; 00129 } 00130 00131 static void EmitLocation(raw_ostream &o, const SourceManager &SM, 00132 const LangOptions &LangOpts, 00133 const PathDiagnosticLocation &L, const FIDMap& FM, 00134 unsigned indent, bool extend = false) { 00135 EmitLocation(o, SM, LangOpts, L.asLocation(), FM, indent, extend); 00136 } 00137 00138 static void EmitRange(raw_ostream &o, const SourceManager &SM, 00139 const LangOptions &LangOpts, 00140 PathDiagnosticRange R, const FIDMap &FM, 00141 unsigned indent) { 00142 Indent(o, indent) << "<array>\n"; 00143 EmitLocation(o, SM, LangOpts, R.getBegin(), FM, indent+1); 00144 EmitLocation(o, SM, LangOpts, R.getEnd(), FM, indent+1, !R.isPoint); 00145 Indent(o, indent) << "</array>\n"; 00146 } 00147 00148 static raw_ostream &EmitString(raw_ostream &o, StringRef s) { 00149 o << "<string>"; 00150 for (StringRef::const_iterator I = s.begin(), E = s.end(); I != E; ++I) { 00151 char c = *I; 00152 switch (c) { 00153 default: o << c; break; 00154 case '&': o << "&"; break; 00155 case '<': o << "<"; break; 00156 case '>': o << ">"; break; 00157 case '\'': o << "'"; break; 00158 case '\"': o << """; break; 00159 } 00160 } 00161 o << "</string>"; 00162 return o; 00163 } 00164 00165 static void ReportControlFlow(raw_ostream &o, 00166 const PathDiagnosticControlFlowPiece& P, 00167 const FIDMap& FM, 00168 const SourceManager &SM, 00169 const LangOptions &LangOpts, 00170 unsigned indent) { 00171 00172 Indent(o, indent) << "<dict>\n"; 00173 ++indent; 00174 00175 Indent(o, indent) << "<key>kind</key><string>control</string>\n"; 00176 00177 // Emit edges. 00178 Indent(o, indent) << "<key>edges</key>\n"; 00179 ++indent; 00180 Indent(o, indent) << "<array>\n"; 00181 ++indent; 00182 for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end(); 00183 I!=E; ++I) { 00184 Indent(o, indent) << "<dict>\n"; 00185 ++indent; 00186 Indent(o, indent) << "<key>start</key>\n"; 00187 EmitRange(o, SM, LangOpts, I->getStart().asRange(), FM, indent+1); 00188 Indent(o, indent) << "<key>end</key>\n"; 00189 EmitRange(o, SM, LangOpts, I->getEnd().asRange(), FM, indent+1); 00190 --indent; 00191 Indent(o, indent) << "</dict>\n"; 00192 } 00193 --indent; 00194 Indent(o, indent) << "</array>\n"; 00195 --indent; 00196 00197 // Output any helper text. 00198 const std::string& s = P.getString(); 00199 if (!s.empty()) { 00200 Indent(o, indent) << "<key>alternate</key>"; 00201 EmitString(o, s) << '\n'; 00202 } 00203 00204 --indent; 00205 Indent(o, indent) << "</dict>\n"; 00206 } 00207 00208 static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, 00209 const FIDMap& FM, 00210 const SourceManager &SM, 00211 const LangOptions &LangOpts, 00212 unsigned indent, 00213 unsigned depth) { 00214 00215 Indent(o, indent) << "<dict>\n"; 00216 ++indent; 00217 00218 Indent(o, indent) << "<key>kind</key><string>event</string>\n"; 00219 00220 // Output the location. 00221 FullSourceLoc L = P.getLocation().asLocation(); 00222 00223 Indent(o, indent) << "<key>location</key>\n"; 00224 EmitLocation(o, SM, LangOpts, L, FM, indent); 00225 00226 // Output the ranges (if any). 00227 PathDiagnosticPiece::range_iterator RI = P.ranges_begin(), 00228 RE = P.ranges_end(); 00229 00230 if (RI != RE) { 00231 Indent(o, indent) << "<key>ranges</key>\n"; 00232 Indent(o, indent) << "<array>\n"; 00233 ++indent; 00234 for (; RI != RE; ++RI) 00235 EmitRange(o, SM, LangOpts, *RI, FM, indent+1); 00236 --indent; 00237 Indent(o, indent) << "</array>\n"; 00238 } 00239 00240 // Output the call depth. 00241 Indent(o, indent) << "<key>depth</key>" 00242 << "<integer>" << depth << "</integer>\n"; 00243 00244 // Output the text. 00245 assert(!P.getString().empty()); 00246 Indent(o, indent) << "<key>extended_message</key>\n"; 00247 Indent(o, indent); 00248 EmitString(o, P.getString()) << '\n'; 00249 00250 // Output the short text. 00251 // FIXME: Really use a short string. 00252 Indent(o, indent) << "<key>message</key>\n"; 00253 EmitString(o, P.getString()) << '\n'; 00254 00255 // Finish up. 00256 --indent; 00257 Indent(o, indent); o << "</dict>\n"; 00258 } 00259 00260 static void ReportPiece(raw_ostream &o, 00261 const PathDiagnosticPiece &P, 00262 const FIDMap& FM, const SourceManager &SM, 00263 const LangOptions &LangOpts, 00264 unsigned indent, 00265 unsigned depth, 00266 bool includeControlFlow); 00267 00268 static void ReportCall(raw_ostream &o, 00269 const PathDiagnosticCallPiece &P, 00270 const FIDMap& FM, const SourceManager &SM, 00271 const LangOptions &LangOpts, 00272 unsigned indent, 00273 unsigned depth) { 00274 00275 IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = 00276 P.getCallEnterEvent(); 00277 00278 if (callEnter) 00279 ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true); 00280 00281 IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller = 00282 P.getCallEnterWithinCallerEvent(); 00283 00284 ++depth; 00285 00286 if (callEnterWithinCaller) 00287 ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts, 00288 indent, depth, true); 00289 00290 for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) 00291 ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true); 00292 00293 IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = 00294 P.getCallExitEvent(); 00295 00296 if (callExit) 00297 ReportPiece(o, *callExit, FM, SM, LangOpts, indent, depth, true); 00298 } 00299 00300 static void ReportMacro(raw_ostream &o, 00301 const PathDiagnosticMacroPiece& P, 00302 const FIDMap& FM, const SourceManager &SM, 00303 const LangOptions &LangOpts, 00304 unsigned indent, 00305 unsigned depth) { 00306 00307 for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); 00308 I!=E; ++I) { 00309 ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, false); 00310 } 00311 } 00312 00313 static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, 00314 const FIDMap& FM, const SourceManager &SM, 00315 const LangOptions &LangOpts) { 00316 ReportPiece(o, P, FM, SM, LangOpts, 4, 0, true); 00317 } 00318 00319 static void ReportPiece(raw_ostream &o, 00320 const PathDiagnosticPiece &P, 00321 const FIDMap& FM, const SourceManager &SM, 00322 const LangOptions &LangOpts, 00323 unsigned indent, 00324 unsigned depth, 00325 bool includeControlFlow) { 00326 switch (P.getKind()) { 00327 case PathDiagnosticPiece::ControlFlow: 00328 if (includeControlFlow) 00329 ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, 00330 LangOpts, indent); 00331 break; 00332 case PathDiagnosticPiece::Call: 00333 ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts, 00334 indent, depth); 00335 break; 00336 case PathDiagnosticPiece::Event: 00337 ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts, 00338 indent, depth); 00339 break; 00340 case PathDiagnosticPiece::Macro: 00341 ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, 00342 indent, depth); 00343 break; 00344 } 00345 } 00346 00347 void PlistDiagnostics::FlushDiagnosticsImpl( 00348 std::vector<const PathDiagnostic *> &Diags, 00349 SmallVectorImpl<std::string> *FilesMade) { 00350 // Build up a set of FIDs that we use by scanning the locations and 00351 // ranges of the diagnostics. 00352 FIDMap FM; 00353 SmallVector<FileID, 10> Fids; 00354 const SourceManager* SM = 0; 00355 00356 if (!Diags.empty()) 00357 SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); 00358 00359 00360 for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), 00361 DE = Diags.end(); DI != DE; ++DI) { 00362 00363 const PathDiagnostic *D = *DI; 00364 00365 llvm::SmallVector<const PathPieces *, 5> WorkList; 00366 WorkList.push_back(&D->path); 00367 00368 while (!WorkList.empty()) { 00369 const PathPieces &path = *WorkList.back(); 00370 WorkList.pop_back(); 00371 00372 for (PathPieces::const_iterator I = path.begin(), E = path.end(); 00373 I!=E; ++I) { 00374 const PathDiagnosticPiece *piece = I->getPtr(); 00375 AddFID(FM, Fids, SM, piece->getLocation().asLocation()); 00376 00377 for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), 00378 RE= piece->ranges_end(); RI != RE; ++RI) { 00379 AddFID(FM, Fids, SM, RI->getBegin()); 00380 AddFID(FM, Fids, SM, RI->getEnd()); 00381 } 00382 00383 if (const PathDiagnosticCallPiece *call = 00384 dyn_cast<PathDiagnosticCallPiece>(piece)) { 00385 WorkList.push_back(&call->path); 00386 } 00387 else if (const PathDiagnosticMacroPiece *macro = 00388 dyn_cast<PathDiagnosticMacroPiece>(piece)) { 00389 WorkList.push_back(¯o->subPieces); 00390 } 00391 } 00392 } 00393 } 00394 00395 // Open the file. 00396 std::string ErrMsg; 00397 llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg); 00398 if (!ErrMsg.empty()) { 00399 llvm::errs() << "warning: could not create file: " << OutputFile << '\n'; 00400 return; 00401 } 00402 00403 // Write the plist header. 00404 o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 00405 "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " 00406 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" 00407 "<plist version=\"1.0\">\n"; 00408 00409 // Write the root object: a <dict> containing... 00410 // - "files", an <array> mapping from FIDs to file names 00411 // - "diagnostics", an <array> containing the path diagnostics 00412 o << "<dict>\n" 00413 " <key>files</key>\n" 00414 " <array>\n"; 00415 00416 for (SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end(); 00417 I!=E; ++I) { 00418 o << " "; 00419 EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n'; 00420 } 00421 00422 o << " </array>\n" 00423 " <key>diagnostics</key>\n" 00424 " <array>\n"; 00425 00426 for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), 00427 DE = Diags.end(); DI!=DE; ++DI) { 00428 00429 o << " <dict>\n" 00430 " <key>path</key>\n"; 00431 00432 const PathDiagnostic *D = *DI; 00433 00434 o << " <array>\n"; 00435 00436 for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); 00437 I != E; ++I) 00438 ReportDiag(o, **I, FM, *SM, LangOpts); 00439 00440 o << " </array>\n"; 00441 00442 // Output the bug type and bug category. 00443 o << " <key>description</key>"; 00444 EmitString(o, D->getDescription()) << '\n'; 00445 o << " <key>category</key>"; 00446 EmitString(o, D->getCategory()) << '\n'; 00447 o << " <key>type</key>"; 00448 EmitString(o, D->getBugType()) << '\n'; 00449 00450 // Output information about the semantic context where 00451 // the issue occurred. 00452 if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { 00453 // FIXME: handle blocks, which have no name. 00454 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { 00455 StringRef declKind; 00456 switch (ND->getKind()) { 00457 case Decl::CXXRecord: 00458 declKind = "C++ class"; 00459 break; 00460 case Decl::CXXMethod: 00461 declKind = "C++ method"; 00462 break; 00463 case Decl::ObjCMethod: 00464 declKind = "Objective-C method"; 00465 break; 00466 case Decl::Function: 00467 declKind = "function"; 00468 break; 00469 default: 00470 break; 00471 } 00472 if (!declKind.empty()) { 00473 const std::string &declName = ND->getDeclName().getAsString(); 00474 o << " <key>issue_context_kind</key>"; 00475 EmitString(o, declKind) << '\n'; 00476 o << " <key>issue_context</key>"; 00477 EmitString(o, declName) << '\n'; 00478 } 00479 } 00480 } 00481 00482 // Output the location of the bug. 00483 o << " <key>location</key>\n"; 00484 EmitLocation(o, *SM, LangOpts, D->getLocation(), FM, 2); 00485 00486 // Output the diagnostic to the sub-diagnostic client, if any. 00487 if (SubPD) { 00488 std::vector<const PathDiagnostic *> SubDiags; 00489 SubDiags.push_back(D); 00490 SmallVector<std::string, 1> SubFilesMade; 00491 SubPD->FlushDiagnosticsImpl(SubDiags, &SubFilesMade); 00492 00493 if (!SubFilesMade.empty()) { 00494 o << " <key>" << SubPD->getName() << "_files</key>\n"; 00495 o << " <array>\n"; 00496 for (size_t i = 0, n = SubFilesMade.size(); i < n ; ++i) 00497 o << " <string>" << SubFilesMade[i] << "</string>\n"; 00498 o << " </array>\n"; 00499 } 00500 } 00501 00502 // Close up the entry. 00503 o << " </dict>\n"; 00504 } 00505 00506 o << " </array>\n"; 00507 00508 // Finish. 00509 o << "</dict>\n</plist>"; 00510 00511 if (FilesMade) 00512 FilesMade->push_back(OutputFile); 00513 }