PathDiagnosticPiece* BugReporterVisitor::getDefaultEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager()); BugReport::ranges_iterator Beg, End; llvm::tie(Beg, End) = BR.getRanges(); // Only add the statement itself as a range if we didn't specify any // special ranges for this report. PathDiagnosticPiece *P = new PathDiagnosticEventPiece(L, BR.getDescription(), Beg == End); for (; Beg != End; ++Beg) P->addRange(*Beg); return P; }
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; } } }
PathDiagnosticPiece* BugReporterVisitor::getDefaultEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { const ProgramPoint &PP = EndPathNode->getLocation(); PathDiagnosticLocation L; if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&PP)) { const CFGBlock *block = BE->getBlock(); if (block->getBlockID() == 0) { L = PathDiagnosticLocation::createDeclEnd(PP.getLocationContext(), BRC.getSourceManager()); } } if (!L.isValid()) { const Stmt *S = BR.getStmt(); if (!S) return NULL; L = PathDiagnosticLocation(S, BRC.getSourceManager(), PP.getLocationContext()); } BugReport::ranges_iterator Beg, End; llvm::tie(Beg, End) = BR.getRanges(); // Only add the statement itself as a range if we didn't specify any // special ranges for this report. PathDiagnosticPiece *P = new PathDiagnosticEventPiece(L, BR.getDescription(), Beg == End); for (; Beg != End; ++Beg) P->addRange(*Beg); return P; }
void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel, const DiagnosticInfo &Info) { // Default implementation (Warnings/errors count). DiagnosticClient::HandleDiagnostic(DiagLevel, Info); // Create a PathDiagnostic with a single piece. PathDiagnostic* D = new PathDiagnostic(); const char *LevelStr; switch (DiagLevel) { default: case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); case Diagnostic::Note: LevelStr = "note: "; break; case Diagnostic::Warning: LevelStr = "warning: "; break; case Diagnostic::Error: LevelStr = "error: "; break; case Diagnostic::Fatal: LevelStr = "fatal error: "; break; } llvm::SmallString<100> StrC; StrC += LevelStr; Info.FormatDiagnostic(StrC); PathDiagnosticPiece *P = new PathDiagnosticEventPiece(FullSourceLoc(Info.getLocation(), Info.getSourceManager()), StrC.str()); for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) P->addRange(Info.getRange(i).getAsRange()); for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i) P->addFixItHint(Info.getFixItHint(i)); D->push_front(P); HandlePathDiagnostic(D); }
static void ReportDiag(llvm::raw_ostream& o, const PathDiagnosticPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts) { unsigned indent = 4; switch (P.getKind()) { case PathDiagnosticPiece::ControlFlow: ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, LangOpts, indent); break; case PathDiagnosticPiece::Event: ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts, indent); break; case PathDiagnosticPiece::Macro: ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, indent); break; } }
static void ReportEvent(llvm::raw_ostream& o, const PathDiagnosticPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts, unsigned indent) { Indent(o, indent) << "<dict>\n"; ++indent; Indent(o, indent) << "<key>kind</key><string>event</string>\n"; // Output the location. FullSourceLoc L = P.getLocation().asLocation(); Indent(o, indent) << "<key>location</key>\n"; EmitLocation(o, SM, LangOpts, L, FM, indent); // Output the ranges (if any). PathDiagnosticPiece::range_iterator RI = P.ranges_begin(), RE = P.ranges_end(); if (RI != RE) { Indent(o, indent) << "<key>ranges</key>\n"; Indent(o, indent) << "<array>\n"; ++indent; for (; RI != RE; ++RI) EmitRange(o, SM, LangOpts, *RI, FM, indent+1); --indent; Indent(o, indent) << "</array>\n"; } // Output the text. assert(!P.getString().empty()); Indent(o, indent) << "<key>extended_message</key>\n"; Indent(o, indent); EmitString(o, P.getString()) << '\n'; // Output the short text. // FIXME: Really use a short string. Indent(o, indent) << "<key>message</key>\n"; EmitString(o, P.getString()) << '\n'; // Finish up. --indent; Indent(o, indent); o << "</dict>\n"; }
static llvm::Optional<bool> comparePiece(const PathDiagnosticPiece &X, const PathDiagnosticPiece &Y) { if (X.getKind() != Y.getKind()) return X.getKind() < Y.getKind(); FullSourceLoc XL = X.getLocation().asLocation(); FullSourceLoc YL = Y.getLocation().asLocation(); if (XL != YL) return XL.isBeforeInTranslationUnitThan(YL); if (X.getString() != Y.getString()) return X.getString() < Y.getString(); if (X.getRanges().size() != Y.getRanges().size()) return X.getRanges().size() < Y.getRanges().size(); const SourceManager &SM = XL.getManager(); for (unsigned i = 0, n = X.getRanges().size(); i < n; ++i) { SourceRange XR = X.getRanges()[i]; SourceRange YR = Y.getRanges()[i]; if (XR != YR) { if (XR.getBegin() != YR.getBegin()) return SM.isBeforeInTranslationUnit(XR.getBegin(), YR.getBegin()); return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd()); } } switch (X.getKind()) { case clang::ento::PathDiagnosticPiece::ControlFlow: return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X), cast<PathDiagnosticControlFlowPiece>(Y)); case clang::ento::PathDiagnosticPiece::Event: return llvm::Optional<bool>(); case clang::ento::PathDiagnosticPiece::Macro: return compareMacro(cast<PathDiagnosticMacroPiece>(X), cast<PathDiagnosticMacroPiece>(Y)); case clang::ento::PathDiagnosticPiece::Call: return compareCall(cast<PathDiagnosticCallPiece>(X), cast<PathDiagnosticCallPiece>(Y)); } llvm_unreachable("all cases handled"); }
void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, const PathDiagnosticPiece& P, unsigned num, unsigned max) { // For now, just draw a box above the line in question, and emit the // warning. FullSourceLoc Pos = P.getLocation().asLocation(); if (!Pos.isValid()) return; SourceManager &SM = R.getSourceMgr(); assert(&Pos.getManager() == &SM && "SourceManagers are different!"); std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedExpansionLoc(Pos); if (LPosInfo.first != BugFileID) return; const llvm::MemoryBuffer *Buf = SM.getBuffer(LPosInfo.first); const char* FileStart = Buf->getBufferStart(); // Compute the column number. Rewind from the current position to the start // of the line. unsigned ColNo = SM.getColumnNumber(LPosInfo.first, LPosInfo.second); const char *TokInstantiationPtr =Pos.getExpansionLoc().getCharacterData(); const char *LineStart = TokInstantiationPtr-ColNo; // Compute LineEnd. const char *LineEnd = TokInstantiationPtr; const char* FileEnd = Buf->getBufferEnd(); while (*LineEnd != '\n' && LineEnd != FileEnd) ++LineEnd; // Compute the margin offset by counting tabs and non-tabs. unsigned PosNo = 0; for (const char* c = LineStart; c != TokInstantiationPtr; ++c) PosNo += *c == '\t' ? 8 : 1; // Create the html for the message. const char *Kind = 0; switch (P.getKind()) { case PathDiagnosticPiece::Call: llvm_unreachable("Calls should already be handled"); case PathDiagnosticPiece::Event: Kind = "Event"; break; case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break; // Setting Kind to "Control" is intentional. case PathDiagnosticPiece::Macro: Kind = "Control"; break; } std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\""; if (num == max) os << "EndPath"; else os << "Path" << num; os << "\" class=\"msg"; if (Kind) os << " msg" << Kind; os << "\" style=\"margin-left:" << PosNo << "ex"; // Output a maximum size. if (!isa<PathDiagnosticMacroPiece>(P)) { // Get the string and determining its maximum substring. const std::string& Msg = P.getString(); unsigned max_token = 0; unsigned cnt = 0; unsigned len = Msg.size(); for (std::string::const_iterator I=Msg.begin(), E=Msg.end(); I!=E; ++I) switch (*I) { default: ++cnt; continue; case ' ': case '\t': case '\n': if (cnt > max_token) max_token = cnt; cnt = 0; } if (cnt > max_token) max_token = cnt; // Determine the approximate size of the message bubble in em. unsigned em; const unsigned max_line = 120; if (max_token >= max_line) em = max_token / 2; else { unsigned characters = max_line; unsigned lines = len / max_line; if (lines > 0) { for (; characters > max_token; --characters) if (len / characters > lines) { ++characters; break; } } em = characters / 2; } if (em < max_line/2) os << "; max-width:" << em << "em"; } else os << "; max-width:100em"; os << "\">"; if (max > 1) { os << "<table class=\"msgT\"><tr><td valign=\"top\">"; os << "<div class=\"PathIndex"; if (Kind) os << " PathIndex" << Kind; os << "\">" << num << "</div>"; if (num > 1) { os << "</td><td><div class=\"PathNav\"><a href=\"#Path" << (num - 1) << "\" title=\"Previous event (" << (num - 1) << ")\">←</a></div></td>"; } os << "</td><td>"; } if (const PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(&P)) { os << "Within the expansion of the macro '"; // Get the name of the macro by relexing it. { FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc(); assert(L.isFileID()); StringRef BufferInfo = L.getBufferData(); std::pair<FileID, unsigned> LocInfo = L.getDecomposedLoc(); const char* MacroName = LocInfo.second + BufferInfo.data(); Lexer rawLexer(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(), BufferInfo.begin(), MacroName, BufferInfo.end()); Token TheTok; rawLexer.LexFromRawLexer(TheTok); for (unsigned i = 0, n = TheTok.getLength(); i < n; ++i) os << MacroName[i]; } os << "':\n"; if (max > 1) { os << "</td>"; if (num < max) { os << "<td><div class=\"PathNav\"><a href=\"#"; if (num == max - 1) os << "EndPath"; else os << "Path" << (num + 1); os << "\" title=\"Next event (" << (num + 1) << ")\">→</a></div></td>"; } os << "</tr></table>"; } // Within a macro piece. Write out each event. ProcessMacroPiece(os, *MP, 0); } else { os << html::EscapeText(P.getString()); if (max > 1) { os << "</td>"; if (num < max) { os << "<td><div class=\"PathNav\"><a href=\"#"; if (num == max - 1) os << "EndPath"; else os << "Path" << (num + 1); os << "\" title=\"Next event (" << (num + 1) << ")\">→</a></div></td>"; } os << "</tr></table>"; } } os << "</div></td></tr>"; // Insert the new html. unsigned DisplayPos = LineEnd - FileStart; SourceLocation Loc = SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos); R.InsertTextBefore(Loc, os.str()); // Now highlight the ranges. ArrayRef<SourceRange> Ranges = P.getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { HighlightRange(R, LPosInfo.first, *I); } }