clang API Documentation

PlistDiagnostics.cpp
Go to the documentation of this file.
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(&macro->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 }