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/AnalyzerOptions.h" 00015 #include "clang/Basic/FileManager.h" 00016 #include "clang/Basic/PlistSupport.h" 00017 #include "clang/Basic/SourceManager.h" 00018 #include "clang/Basic/Version.h" 00019 #include "clang/Lex/Preprocessor.h" 00020 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 00021 #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" 00022 #include "llvm/ADT/DenseMap.h" 00023 #include "llvm/ADT/SmallVector.h" 00024 #include "llvm/Support/Casting.h" 00025 using namespace clang; 00026 using namespace ento; 00027 using namespace markup; 00028 00029 namespace { 00030 class PlistDiagnostics : public PathDiagnosticConsumer { 00031 const std::string OutputFile; 00032 const LangOptions &LangOpts; 00033 const bool SupportsCrossFileDiagnostics; 00034 public: 00035 PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, 00036 const std::string& prefix, 00037 const LangOptions &LangOpts, 00038 bool supportsMultipleFiles); 00039 00040 virtual ~PlistDiagnostics() {} 00041 00042 void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, 00043 FilesMade *filesMade) override; 00044 00045 StringRef getName() const override { 00046 return "PlistDiagnostics"; 00047 } 00048 00049 PathGenerationScheme getGenerationScheme() const override { 00050 return Extensive; 00051 } 00052 bool supportsLogicalOpControlFlow() const override { return true; } 00053 bool supportsCrossFileDiagnostics() const override { 00054 return SupportsCrossFileDiagnostics; 00055 } 00056 }; 00057 } // end anonymous namespace 00058 00059 PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, 00060 const std::string& output, 00061 const LangOptions &LO, 00062 bool supportsMultipleFiles) 00063 : OutputFile(output), 00064 LangOpts(LO), 00065 SupportsCrossFileDiagnostics(supportsMultipleFiles) {} 00066 00067 void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, 00068 PathDiagnosticConsumers &C, 00069 const std::string& s, 00070 const Preprocessor &PP) { 00071 C.push_back(new PlistDiagnostics(AnalyzerOpts, s, 00072 PP.getLangOpts(), false)); 00073 } 00074 00075 void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, 00076 PathDiagnosticConsumers &C, 00077 const std::string &s, 00078 const Preprocessor &PP) { 00079 C.push_back(new PlistDiagnostics(AnalyzerOpts, s, 00080 PP.getLangOpts(), true)); 00081 } 00082 00083 static void ReportControlFlow(raw_ostream &o, 00084 const PathDiagnosticControlFlowPiece& P, 00085 const FIDMap& FM, 00086 const SourceManager &SM, 00087 const LangOptions &LangOpts, 00088 unsigned indent) { 00089 00090 Indent(o, indent) << "<dict>\n"; 00091 ++indent; 00092 00093 Indent(o, indent) << "<key>kind</key><string>control</string>\n"; 00094 00095 // Emit edges. 00096 Indent(o, indent) << "<key>edges</key>\n"; 00097 ++indent; 00098 Indent(o, indent) << "<array>\n"; 00099 ++indent; 00100 for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end(); 00101 I!=E; ++I) { 00102 Indent(o, indent) << "<dict>\n"; 00103 ++indent; 00104 00105 // Make the ranges of the start and end point self-consistent with adjacent edges 00106 // by forcing to use only the beginning of the range. This simplifies the layout 00107 // logic for clients. 00108 Indent(o, indent) << "<key>start</key>\n"; 00109 SourceLocation StartEdge = I->getStart().asRange().getBegin(); 00110 EmitRange(o, SM, LangOpts, CharSourceRange::getTokenRange(StartEdge), FM, 00111 indent + 1); 00112 00113 Indent(o, indent) << "<key>end</key>\n"; 00114 SourceLocation EndEdge = I->getEnd().asRange().getBegin(); 00115 EmitRange(o, SM, LangOpts, CharSourceRange::getTokenRange(EndEdge), FM, 00116 indent + 1); 00117 00118 --indent; 00119 Indent(o, indent) << "</dict>\n"; 00120 } 00121 --indent; 00122 Indent(o, indent) << "</array>\n"; 00123 --indent; 00124 00125 // Output any helper text. 00126 const std::string& s = P.getString(); 00127 if (!s.empty()) { 00128 Indent(o, indent) << "<key>alternate</key>"; 00129 EmitString(o, s) << '\n'; 00130 } 00131 00132 --indent; 00133 Indent(o, indent) << "</dict>\n"; 00134 } 00135 00136 static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, 00137 const FIDMap& FM, 00138 const SourceManager &SM, 00139 const LangOptions &LangOpts, 00140 unsigned indent, 00141 unsigned depth, 00142 bool isKeyEvent = false) { 00143 00144 Indent(o, indent) << "<dict>\n"; 00145 ++indent; 00146 00147 Indent(o, indent) << "<key>kind</key><string>event</string>\n"; 00148 00149 if (isKeyEvent) { 00150 Indent(o, indent) << "<key>key_event</key><true/>\n"; 00151 } 00152 00153 // Output the location. 00154 FullSourceLoc L = P.getLocation().asLocation(); 00155 00156 Indent(o, indent) << "<key>location</key>\n"; 00157 EmitLocation(o, SM, LangOpts, L, FM, indent); 00158 00159 // Output the ranges (if any). 00160 ArrayRef<SourceRange> Ranges = P.getRanges(); 00161 00162 if (!Ranges.empty()) { 00163 Indent(o, indent) << "<key>ranges</key>\n"; 00164 Indent(o, indent) << "<array>\n"; 00165 ++indent; 00166 for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); 00167 I != E; ++I) { 00168 EmitRange(o, SM, LangOpts, CharSourceRange::getTokenRange(*I), FM, 00169 indent + 1); 00170 } 00171 --indent; 00172 Indent(o, indent) << "</array>\n"; 00173 } 00174 00175 // Output the call depth. 00176 Indent(o, indent) << "<key>depth</key>"; 00177 EmitInteger(o, depth) << '\n'; 00178 00179 // Output the text. 00180 assert(!P.getString().empty()); 00181 Indent(o, indent) << "<key>extended_message</key>\n"; 00182 Indent(o, indent); 00183 EmitString(o, P.getString()) << '\n'; 00184 00185 // Output the short text. 00186 // FIXME: Really use a short string. 00187 Indent(o, indent) << "<key>message</key>\n"; 00188 Indent(o, indent); 00189 EmitString(o, P.getString()) << '\n'; 00190 00191 // Finish up. 00192 --indent; 00193 Indent(o, indent); o << "</dict>\n"; 00194 } 00195 00196 static void ReportPiece(raw_ostream &o, 00197 const PathDiagnosticPiece &P, 00198 const FIDMap& FM, const SourceManager &SM, 00199 const LangOptions &LangOpts, 00200 unsigned indent, 00201 unsigned depth, 00202 bool includeControlFlow, 00203 bool isKeyEvent = false); 00204 00205 static void ReportCall(raw_ostream &o, 00206 const PathDiagnosticCallPiece &P, 00207 const FIDMap& FM, const SourceManager &SM, 00208 const LangOptions &LangOpts, 00209 unsigned indent, 00210 unsigned depth) { 00211 00212 IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = 00213 P.getCallEnterEvent(); 00214 00215 if (callEnter) 00216 ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true, 00217 P.isLastInMainSourceFile()); 00218 00219 IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller = 00220 P.getCallEnterWithinCallerEvent(); 00221 00222 ++depth; 00223 00224 if (callEnterWithinCaller) 00225 ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts, 00226 indent, depth, true); 00227 00228 for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) 00229 ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true); 00230 00231 --depth; 00232 00233 IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = 00234 P.getCallExitEvent(); 00235 00236 if (callExit) 00237 ReportPiece(o, *callExit, FM, SM, LangOpts, indent, depth, true); 00238 } 00239 00240 static void ReportMacro(raw_ostream &o, 00241 const PathDiagnosticMacroPiece& P, 00242 const FIDMap& FM, const SourceManager &SM, 00243 const LangOptions &LangOpts, 00244 unsigned indent, 00245 unsigned depth) { 00246 00247 for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); 00248 I!=E; ++I) { 00249 ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, false); 00250 } 00251 } 00252 00253 static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, 00254 const FIDMap& FM, const SourceManager &SM, 00255 const LangOptions &LangOpts) { 00256 ReportPiece(o, P, FM, SM, LangOpts, 4, 0, true); 00257 } 00258 00259 static void ReportPiece(raw_ostream &o, 00260 const PathDiagnosticPiece &P, 00261 const FIDMap& FM, const SourceManager &SM, 00262 const LangOptions &LangOpts, 00263 unsigned indent, 00264 unsigned depth, 00265 bool includeControlFlow, 00266 bool isKeyEvent) { 00267 switch (P.getKind()) { 00268 case PathDiagnosticPiece::ControlFlow: 00269 if (includeControlFlow) 00270 ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, 00271 LangOpts, indent); 00272 break; 00273 case PathDiagnosticPiece::Call: 00274 ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts, 00275 indent, depth); 00276 break; 00277 case PathDiagnosticPiece::Event: 00278 ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts, 00279 indent, depth, isKeyEvent); 00280 break; 00281 case PathDiagnosticPiece::Macro: 00282 ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, 00283 indent, depth); 00284 break; 00285 } 00286 } 00287 00288 void PlistDiagnostics::FlushDiagnosticsImpl( 00289 std::vector<const PathDiagnostic *> &Diags, 00290 FilesMade *filesMade) { 00291 // Build up a set of FIDs that we use by scanning the locations and 00292 // ranges of the diagnostics. 00293 FIDMap FM; 00294 SmallVector<FileID, 10> Fids; 00295 const SourceManager* SM = nullptr; 00296 00297 if (!Diags.empty()) 00298 SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); 00299 00300 00301 for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), 00302 DE = Diags.end(); DI != DE; ++DI) { 00303 00304 const PathDiagnostic *D = *DI; 00305 00306 SmallVector<const PathPieces *, 5> WorkList; 00307 WorkList.push_back(&D->path); 00308 00309 while (!WorkList.empty()) { 00310 const PathPieces &path = *WorkList.pop_back_val(); 00311 00312 for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; 00313 ++I) { 00314 const PathDiagnosticPiece *piece = I->get(); 00315 AddFID(FM, Fids, *SM, piece->getLocation().asLocation()); 00316 ArrayRef<SourceRange> Ranges = piece->getRanges(); 00317 for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), 00318 E = Ranges.end(); I != E; ++I) { 00319 AddFID(FM, Fids, *SM, I->getBegin()); 00320 AddFID(FM, Fids, *SM, I->getEnd()); 00321 } 00322 00323 if (const PathDiagnosticCallPiece *call = 00324 dyn_cast<PathDiagnosticCallPiece>(piece)) { 00325 IntrusiveRefCntPtr<PathDiagnosticEventPiece> 00326 callEnterWithin = call->getCallEnterWithinCallerEvent(); 00327 if (callEnterWithin) 00328 AddFID(FM, Fids, *SM, callEnterWithin->getLocation().asLocation()); 00329 00330 WorkList.push_back(&call->path); 00331 } 00332 else if (const PathDiagnosticMacroPiece *macro = 00333 dyn_cast<PathDiagnosticMacroPiece>(piece)) { 00334 WorkList.push_back(¯o->subPieces); 00335 } 00336 } 00337 } 00338 } 00339 00340 // Open the file. 00341 std::error_code EC; 00342 llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text); 00343 if (EC) { 00344 llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; 00345 return; 00346 } 00347 00348 EmitPlistHeader(o); 00349 00350 // Write the root object: a <dict> containing... 00351 // - "clang_version", the string representation of clang version 00352 // - "files", an <array> mapping from FIDs to file names 00353 // - "diagnostics", an <array> containing the path diagnostics 00354 o << "<dict>\n" << 00355 " <key>clang_version</key>\n"; 00356 EmitString(o, getClangFullVersion()) << '\n'; 00357 o << " <key>files</key>\n" 00358 " <array>\n"; 00359 00360 for (FileID FID : Fids) 00361 EmitString(o << " ", SM->getFileEntryForID(FID)->getName()) << '\n'; 00362 00363 o << " </array>\n" 00364 " <key>diagnostics</key>\n" 00365 " <array>\n"; 00366 00367 for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), 00368 DE = Diags.end(); DI!=DE; ++DI) { 00369 00370 o << " <dict>\n" 00371 " <key>path</key>\n"; 00372 00373 const PathDiagnostic *D = *DI; 00374 00375 o << " <array>\n"; 00376 00377 for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); 00378 I != E; ++I) 00379 ReportDiag(o, **I, FM, *SM, LangOpts); 00380 00381 o << " </array>\n"; 00382 00383 // Output the bug type and bug category. 00384 o << " <key>description</key>"; 00385 EmitString(o, D->getShortDescription()) << '\n'; 00386 o << " <key>category</key>"; 00387 EmitString(o, D->getCategory()) << '\n'; 00388 o << " <key>type</key>"; 00389 EmitString(o, D->getBugType()) << '\n'; 00390 00391 // Output information about the semantic context where 00392 // the issue occurred. 00393 if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { 00394 // FIXME: handle blocks, which have no name. 00395 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { 00396 StringRef declKind; 00397 switch (ND->getKind()) { 00398 case Decl::CXXRecord: 00399 declKind = "C++ class"; 00400 break; 00401 case Decl::CXXMethod: 00402 declKind = "C++ method"; 00403 break; 00404 case Decl::ObjCMethod: 00405 declKind = "Objective-C method"; 00406 break; 00407 case Decl::Function: 00408 declKind = "function"; 00409 break; 00410 default: 00411 break; 00412 } 00413 if (!declKind.empty()) { 00414 const std::string &declName = ND->getDeclName().getAsString(); 00415 o << " <key>issue_context_kind</key>"; 00416 EmitString(o, declKind) << '\n'; 00417 o << " <key>issue_context</key>"; 00418 EmitString(o, declName) << '\n'; 00419 } 00420 00421 // Output the bug hash for issue unique-ing. Currently, it's just an 00422 // offset from the beginning of the function. 00423 if (const Stmt *Body = DeclWithIssue->getBody()) { 00424 00425 // If the bug uniqueing location exists, use it for the hash. 00426 // For example, this ensures that two leaks reported on the same line 00427 // will have different issue_hashes and that the hash will identify 00428 // the leak location even after code is added between the allocation 00429 // site and the end of scope (leak report location). 00430 PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); 00431 if (UPDLoc.isValid()) { 00432 FullSourceLoc UL(SM->getExpansionLoc(UPDLoc.asLocation()), 00433 *SM); 00434 FullSourceLoc UFunL(SM->getExpansionLoc( 00435 D->getUniqueingDecl()->getBody()->getLocStart()), *SM); 00436 o << " <key>issue_hash</key><string>" 00437 << UL.getExpansionLineNumber() - UFunL.getExpansionLineNumber() 00438 << "</string>\n"; 00439 00440 // Otherwise, use the location on which the bug is reported. 00441 } else { 00442 FullSourceLoc L(SM->getExpansionLoc(D->getLocation().asLocation()), 00443 *SM); 00444 FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM); 00445 o << " <key>issue_hash</key><string>" 00446 << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() 00447 << "</string>\n"; 00448 } 00449 00450 } 00451 } 00452 } 00453 00454 // Output the location of the bug. 00455 o << " <key>location</key>\n"; 00456 EmitLocation(o, *SM, LangOpts, D->getLocation().asLocation(), FM, 2); 00457 00458 // Output the diagnostic to the sub-diagnostic client, if any. 00459 if (!filesMade->empty()) { 00460 StringRef lastName; 00461 PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); 00462 if (files) { 00463 for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), 00464 CE = files->end(); CI != CE; ++CI) { 00465 StringRef newName = CI->first; 00466 if (newName != lastName) { 00467 if (!lastName.empty()) { 00468 o << " </array>\n"; 00469 } 00470 lastName = newName; 00471 o << " <key>" << lastName << "_files</key>\n"; 00472 o << " <array>\n"; 00473 } 00474 o << " <string>" << CI->second << "</string>\n"; 00475 } 00476 o << " </array>\n"; 00477 } 00478 } 00479 00480 // Close up the entry. 00481 o << " </dict>\n"; 00482 } 00483 00484 o << " </array>\n"; 00485 00486 // Finish. 00487 o << "</dict>\n</plist>"; 00488 }