unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os, const PathDiagnosticMacroPiece& P, unsigned num) { for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); I!=E; ++I) { if (const PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(I->get())) { num = ProcessMacroPiece(os, *MP, num); continue; } if (PathDiagnosticEventPiece *EP = dyn_cast<PathDiagnosticEventPiece>(I->get())) { os << "<div class=\"msg msgEvent\" style=\"width:94%; " "margin-left:5px\">" "<table class=\"msgT\"><tr>" "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">"; EmitAlphaCounter(os, num++); os << "</div></td><td valign=\"top\">" << html::EscapeText(EP->getString()) << "</td></tr></table></div>\n"; } } return num; }
static void compute_path_size(const PathPieces &pieces, unsigned &size) { for (PathPieces::const_iterator it = pieces.begin(), et = pieces.end(); it != et; ++it) { const PathDiagnosticPiece *piece = it->getPtr(); if (const PathDiagnosticCallPiece *cp = dyn_cast<PathDiagnosticCallPiece>(piece)) { compute_path_size(cp->path, size); } else ++size; } }
void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, bool ShouldFlattenMacros) const { for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) { PathDiagnosticPiece *Piece = I->getPtr(); switch (Piece->getKind()) { case PathDiagnosticPiece::Call: { PathDiagnosticCallPiece *Call = cast<PathDiagnosticCallPiece>(Piece); IntrusiveRefCntPtr<PathDiagnosticEventPiece> CallEnter = Call->getCallEnterEvent(); if (CallEnter) Current.push_back(CallEnter); Call->path.flattenTo(Primary, Primary, ShouldFlattenMacros); IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = Call->getCallExitEvent(); if (callExit) Current.push_back(callExit); break; } case PathDiagnosticPiece::Macro: { PathDiagnosticMacroPiece *Macro = cast<PathDiagnosticMacroPiece>(Piece); if (ShouldFlattenMacros) { Macro->subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros); } else { Current.push_back(Piece); PathPieces NewPath; Macro->subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros); // FIXME: This probably shouldn't mutate the original path piece. Macro->subPieces = NewPath; } break; } case PathDiagnosticPiece::Event: case PathDiagnosticPiece::ControlFlow: Current.push_back(Piece); break; } } }
void PlistDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) { // Build up a set of FIDs that we use by scanning the locations and // ranges of the diagnostics. FIDMap FM; SmallVector<FileID, 10> Fids; const SourceManager* SM = nullptr; if (!Diags.empty()) SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), DE = Diags.end(); DI != DE; ++DI) { const PathDiagnostic *D = *DI; SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { const PathPieces &path = *WorkList.pop_back_val(); for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) { const PathDiagnosticPiece *piece = I->get(); AddFID(FM, Fids, *SM, piece->getLocation().asLocation()); ArrayRef<SourceRange> Ranges = piece->getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { AddFID(FM, Fids, *SM, I->getBegin()); AddFID(FM, Fids, *SM, I->getEnd()); } if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithin = call->getCallEnterWithinCallerEvent(); if (callEnterWithin) AddFID(FM, Fids, *SM, callEnterWithin->getLocation().asLocation()); WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { WorkList.push_back(¯o->subPieces); } } } } // Open the file. std::error_code EC; llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text); if (EC) { llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; return; } EmitPlistHeader(o); // Write the root object: a <dict> containing... // - "clang_version", the string representation of clang version // - "files", an <array> mapping from FIDs to file names // - "diagnostics", an <array> containing the path diagnostics o << "<dict>\n" << " <key>clang_version</key>\n"; EmitString(o, getClangFullVersion()) << '\n'; o << " <key>files</key>\n" " <array>\n"; for (FileID FID : Fids) EmitString(o << " ", SM->getFileEntryForID(FID)->getName()) << '\n'; o << " </array>\n" " <key>diagnostics</key>\n" " <array>\n"; for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), DE = Diags.end(); DI!=DE; ++DI) { o << " <dict>\n" " <key>path</key>\n"; const PathDiagnostic *D = *DI; o << " <array>\n"; for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); I != E; ++I) ReportDiag(o, **I, FM, *SM, LangOpts); o << " </array>\n"; // Output the bug type and bug category. o << " <key>description</key>"; EmitString(o, D->getShortDescription()) << '\n'; o << " <key>category</key>"; EmitString(o, D->getCategory()) << '\n'; o << " <key>type</key>"; EmitString(o, D->getBugType()) << '\n'; o << " <key>check_name</key>"; EmitString(o, D->getCheckName()) << '\n'; o << " <!-- This hash is experimental and going to change! -->\n"; o << " <key>issue_hash_content_of_line_in_context</key>"; PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); FullSourceLoc L(SM->getExpansionLoc(UPDLoc.isValid() ? UPDLoc.asLocation() : D->getLocation().asLocation()), *SM); const Decl *DeclWithIssue = D->getDeclWithIssue(); EmitString(o, GetIssueHash(*SM, L, D->getCheckName(), D->getBugType(), DeclWithIssue)) << '\n'; // Output information about the semantic context where // the issue occurred. if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { // FIXME: handle blocks, which have no name. if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { StringRef declKind; switch (ND->getKind()) { case Decl::CXXRecord: declKind = "C++ class"; break; case Decl::CXXMethod: declKind = "C++ method"; break; case Decl::ObjCMethod: declKind = "Objective-C method"; break; case Decl::Function: declKind = "function"; break; default: break; } if (!declKind.empty()) { const std::string &declName = ND->getDeclName().getAsString(); o << " <key>issue_context_kind</key>"; EmitString(o, declKind) << '\n'; o << " <key>issue_context</key>"; EmitString(o, declName) << '\n'; } // Output the bug hash for issue unique-ing. Currently, it's just an // offset from the beginning of the function. if (const Stmt *Body = DeclWithIssue->getBody()) { // If the bug uniqueing location exists, use it for the hash. // For example, this ensures that two leaks reported on the same line // will have different issue_hashes and that the hash will identify // the leak location even after code is added between the allocation // site and the end of scope (leak report location). if (UPDLoc.isValid()) { FullSourceLoc UFunL(SM->getExpansionLoc( D->getUniqueingDecl()->getBody()->getLocStart()), *SM); o << " <key>issue_hash_function_offset</key><string>" << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber() << "</string>\n"; // Otherwise, use the location on which the bug is reported. } else { FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM); o << " <key>issue_hash_function_offset</key><string>" << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() << "</string>\n"; } } } } // Output the location of the bug. o << " <key>location</key>\n"; EmitLocation(o, *SM, D->getLocation().asLocation(), FM, 2); // Output the diagnostic to the sub-diagnostic client, if any. if (!filesMade->empty()) { StringRef lastName; PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); if (files) { for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), CE = files->end(); CI != CE; ++CI) { StringRef newName = CI->first; if (newName != lastName) { if (!lastName.empty()) { o << " </array>\n"; } lastName = newName; o << " <key>" << lastName << "_files</key>\n"; o << " <array>\n"; } o << " <string>" << CI->second << "</string>\n"; } o << " </array>\n"; } } // Close up the entry. o << " </dict>\n"; } o << " </array>\n"; // Finish. o << "</dict>\n</plist>"; }
void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { llvm::OwningPtr<PathDiagnostic> OwningD(D); if (!D || D->path.empty()) return; // We need to flatten the locations (convert Stmt* to locations) because // the referenced statements may be freed by the time the diagnostics // are emitted. D->flattenLocations(); // If the PathDiagnosticConsumer does not support diagnostics that // cross file boundaries, prune out such diagnostics now. if (!supportsCrossFileDiagnostics()) { // Verify that the entire path is from the same FileID. FileID FID; const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager(); llvm::SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { const PathPieces &path = *WorkList.back(); WorkList.pop_back(); for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) { const PathDiagnosticPiece *piece = I->getPtr(); FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); if (FID.isInvalid()) { FID = SMgr.getFileID(L); } else if (SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? // Check the source ranges. ArrayRef<SourceRange> Ranges = piece->getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { SourceLocation L = SMgr.getExpansionLoc(I->getBegin()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? L = SMgr.getExpansionLoc(I->getEnd()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? } if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { WorkList.push_back(¯o->subPieces); } } } if (FID.isInvalid()) return; // FIXME: Emit a warning? } // Profile the node to see if we already have something matching it llvm::FoldingSetNodeID profile; D->Profile(profile); void *InsertPos = 0; if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { // Keep the PathDiagnostic with the shorter path. // Note, the enclosing routine is called in deterministic order, so the // results will be consistent between runs (no reason to break ties if the // size is the same). const unsigned orig_size = orig->full_size(); const unsigned new_size = D->full_size(); if (orig_size <= new_size) return; assert(orig != D); Diags.RemoveNode(orig); delete orig; } Diags.InsertNode(OwningD.take()); }
void PlistDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) { // Build up a set of FIDs that we use by scanning the locations and // ranges of the diagnostics. FIDMap FM; SmallVector<FileID, 10> Fids; const SourceManager* SM = 0; if (!Diags.empty()) SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), DE = Diags.end(); DI != DE; ++DI) { const PathDiagnostic *D = *DI; llvm::SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { const PathPieces &path = *WorkList.back(); WorkList.pop_back(); for (PathPieces::const_iterator I = path.begin(), E = path.end(); I!=E; ++I) { const PathDiagnosticPiece *piece = I->getPtr(); AddFID(FM, Fids, SM, piece->getLocation().asLocation()); ArrayRef<SourceRange> Ranges = piece->getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { AddFID(FM, Fids, SM, I->getBegin()); AddFID(FM, Fids, SM, I->getEnd()); } if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithin = call->getCallEnterWithinCallerEvent(); if (callEnterWithin) AddFID(FM, Fids, SM, callEnterWithin->getLocation().asLocation()); WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { WorkList.push_back(¯o->subPieces); } } } } // Open the file. std::string ErrMsg; llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg); if (!ErrMsg.empty()) { llvm::errs() << "warning: could not create file: " << OutputFile << '\n'; return; } // Write the plist header. o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" "<plist version=\"1.0\">\n"; // Write the root object: a <dict> containing... // - "files", an <array> mapping from FIDs to file names // - "diagnostics", an <array> containing the path diagnostics o << "<dict>\n" " <key>files</key>\n" " <array>\n"; for (SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end(); I!=E; ++I) { o << " "; EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n'; } o << " </array>\n" " <key>diagnostics</key>\n" " <array>\n"; for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), DE = Diags.end(); DI!=DE; ++DI) { o << " <dict>\n" " <key>path</key>\n"; const PathDiagnostic *D = *DI; o << " <array>\n"; for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); I != E; ++I) ReportDiag(o, **I, FM, *SM, LangOpts); o << " </array>\n"; // Output the bug type and bug category. o << " <key>description</key>"; EmitString(o, D->getShortDescription()) << '\n'; o << " <key>category</key>"; EmitString(o, D->getCategory()) << '\n'; o << " <key>type</key>"; EmitString(o, D->getBugType()) << '\n'; // Output information about the semantic context where // the issue occurred. if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { // FIXME: handle blocks, which have no name. if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { StringRef declKind; switch (ND->getKind()) { case Decl::CXXRecord: declKind = "C++ class"; break; case Decl::CXXMethod: declKind = "C++ method"; break; case Decl::ObjCMethod: declKind = "Objective-C method"; break; case Decl::Function: declKind = "function"; break; default: break; } if (!declKind.empty()) { const std::string &declName = ND->getDeclName().getAsString(); o << " <key>issue_context_kind</key>"; EmitString(o, declKind) << '\n'; o << " <key>issue_context</key>"; EmitString(o, declName) << '\n'; } // Output the bug hash for issue unique-ing. Currently, it's just an // offset from the beginning of the function. if (const Stmt *Body = DeclWithIssue->getBody()) { FullSourceLoc Loc(SM->getExpansionLoc(D->getLocation().asLocation()), *SM); FullSourceLoc FunLoc(SM->getExpansionLoc(Body->getLocStart()), *SM); o << " <key>issue_hash</key><integer>" << Loc.getExpansionLineNumber() - FunLoc.getExpansionLineNumber() << "</integer>\n"; } } } // Output the location of the bug. o << " <key>location</key>\n"; EmitLocation(o, *SM, LangOpts, D->getLocation(), FM, 2); // Output the diagnostic to the sub-diagnostic client, if any. if (!filesMade->empty()) { StringRef lastName; PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); if (files) { for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), CE = files->end(); CI != CE; ++CI) { StringRef newName = CI->first; if (newName != lastName) { if (!lastName.empty()) { o << " </array>\n"; } lastName = newName; o << " <key>" << lastName << "_files</key>\n"; o << " <array>\n"; } o << " <string>" << CI->second << "</string>\n"; } o << " </array>\n"; } } // Close up the entry. o << " </dict>\n"; } o << " </array>\n"; // Finish. o << "</dict>\n</plist>"; }
void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { llvm::OwningPtr<PathDiagnostic> OwningD(D); if (!D || D->path.empty()) return; // We need to flatten the locations (convert Stmt* to locations) because // the referenced statements may be freed by the time the diagnostics // are emitted. D->flattenLocations(); // If the PathDiagnosticConsumer does not support diagnostics that // cross file boundaries, prune out such diagnostics now. if (!supportsCrossFileDiagnostics()) { // Verify that the entire path is from the same FileID. FileID FID; const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager(); llvm::SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { const PathPieces &path = *WorkList.back(); WorkList.pop_back(); for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) { const PathDiagnosticPiece *piece = I->getPtr(); FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); if (FID.isInvalid()) { FID = SMgr.getFileID(L); } else if (SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? // Check the source ranges. for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), RE = piece->ranges_end(); RI != RE; ++RI) { SourceLocation L = SMgr.getExpansionLoc(RI->getBegin()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? L = SMgr.getExpansionLoc(RI->getEnd()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? } if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { WorkList.push_back(¯o->subPieces); } } } if (FID.isInvalid()) return; // FIXME: Emit a warning? } // Profile the node to see if we already have something matching it llvm::FoldingSetNodeID profile; D->Profile(profile); void *InsertPos = 0; if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { // Keep the PathDiagnostic with the shorter path. const unsigned orig_size = orig->full_size(); const unsigned new_size = D->full_size(); if (orig_size <= new_size) { bool shouldKeepOriginal = true; if (orig_size == new_size) { // Here we break ties in a fairly arbitrary, but deterministic, way. llvm::FoldingSetNodeID fullProfile, fullProfileOrig; D->FullProfile(fullProfile); orig->FullProfile(fullProfileOrig); if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash()) shouldKeepOriginal = false; } if (shouldKeepOriginal) return; } Diags.RemoveNode(orig); delete orig; } Diags.InsertNode(OwningD.take()); }