diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core')
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/BugReporter.cpp | 66 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp | 99 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp | 26 |
3 files changed, 174 insertions, 17 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp index de0e87bb824..1d48bbcf604 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -3509,6 +3509,66 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { } } +/// Insert all lines participating in the function signature \p Signature +/// into \p ExecutedLines. +static void populateExecutedLinesWithFunctionSignature( + const Decl *Signature, SourceManager &SM, + std::unique_ptr<FilesToLineNumsMap> &ExecutedLines) { + + SourceRange SignatureSourceRange; + const Stmt* Body = Signature->getBody(); + if (auto FD = dyn_cast<FunctionDecl>(Signature)) { + SignatureSourceRange = FD->getSourceRange(); + } else if (auto OD = dyn_cast<ObjCMethodDecl>(Signature)) { + SignatureSourceRange = OD->getSourceRange(); + } else { + return; + } + SourceLocation Start = SignatureSourceRange.getBegin(); + SourceLocation End = Body ? Body->getSourceRange().getBegin() + : SignatureSourceRange.getEnd(); + unsigned StartLine = SM.getExpansionLineNumber(Start); + unsigned EndLine = SM.getExpansionLineNumber(End); + + FileID FID = SM.getFileID(SM.getExpansionLoc(Start)); + for (unsigned Line = StartLine; Line <= EndLine; Line++) + ExecutedLines->operator[](FID.getHashValue()).insert(Line); +} + +/// \return all executed lines including function signatures on the path +/// starting from \p N. +static std::unique_ptr<FilesToLineNumsMap> +findExecutedLines(SourceManager &SM, const ExplodedNode *N) { + auto ExecutedLines = llvm::make_unique<FilesToLineNumsMap>(); + + while (N) { + if (N->getFirstPred() == nullptr) { + + // First node: show signature of the entrance point. + const Decl *D = N->getLocationContext()->getDecl(); + populateExecutedLinesWithFunctionSignature(D, SM, ExecutedLines); + + } else if (auto CE = N->getLocationAs<CallEnter>()) { + + // Inlined function: show signature. + const Decl* D = CE->getCalleeContext()->getDecl(); + populateExecutedLinesWithFunctionSignature(D, SM, ExecutedLines); + + } else if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) { + + // Otherwise: show lines associated with the processed statement. + SourceLocation Loc = S->getSourceRange().getBegin(); + SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); + FileID FID = SM.getFileID(ExpansionLoc); + unsigned LineNo = SM.getExpansionLineNumber(ExpansionLoc); + ExecutedLines->operator[](FID.getHashValue()).insert(LineNo); + } + + N = N->getFirstPred(); + } + return ExecutedLines; +} + void BugReporter::FlushReport(BugReport *exampleReport, PathDiagnosticConsumer &PD, ArrayRef<BugReport*> bugReports) { @@ -3517,13 +3577,13 @@ void BugReporter::FlushReport(BugReport *exampleReport, // Probably doesn't make a difference in practice. BugType& BT = exampleReport->getBugType(); - std::unique_ptr<PathDiagnostic> D(new PathDiagnostic( + auto D = llvm::make_unique<PathDiagnostic>( exampleReport->getBugType().getCheckName(), exampleReport->getDeclWithIssue(), exampleReport->getBugType().getName(), exampleReport->getDescription(), exampleReport->getShortDescription(/*Fallback=*/false), BT.getCategory(), - exampleReport->getUniqueingLocation(), - exampleReport->getUniqueingDecl())); + exampleReport->getUniqueingLocation(), exampleReport->getUniqueingDecl(), + findExecutedLines(getSourceManager(), exampleReport->getErrorNode())); if (exampleReport->isPathSensitive()) { // Generate the full path diagnostic, using the generation scheme diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index ebf1487d4bf..635625107f0 100644 --- a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -94,6 +94,13 @@ public: /// \return Javascript for navigating the HTML report using j/k keys. std::string generateKeyboardNavigationJavascript(); + +private: + /// \return JavaScript for an option to only show relevant lines. + std::string showRelevantLinesJavascript(const PathDiagnostic &D); + + /// \return Executed lines from \p D in JSON format. + std::string serializeExecutedLines(const PathDiagnostic &D); }; } // end anonymous namespace @@ -343,6 +350,10 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), generateKeyboardNavigationJavascript()); + // Checkbox and javascript for filtering the output to the counterexample. + R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), + showRelevantLinesJavascript(D)); + // Add the name of the file as an <h1> tag. { std::string s; @@ -450,6 +461,94 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName()); } +std::string +HTMLDiagnostics::showRelevantLinesJavascript(const PathDiagnostic &D) { + std::string s; + llvm::raw_string_ostream os(s); + os << "<script type='text/javascript'>\n"; + os << serializeExecutedLines(D); + os << R"<<<( + +var filterCounterexample = function (hide) { + var tables = document.getElementsByClassName("code"); + for (var t=0; t<tables.length; t++) { + var table = tables[t]; + var file_id = table.getAttribute("data-fileid"); + var lines_in_fid = relevant_lines[file_id]; + if (!lines_in_fid) { + lines_in_fid = {}; + } + var lines = table.getElementsByClassName("codeline"); + for (var i=0; i<lines.length; i++) { + var el = lines[i]; + var lineNo = el.getAttribute("data-linenumber"); + if (!lines_in_fid[lineNo]) { + if (hide) { + el.setAttribute("hidden", ""); + } else { + el.removeAttribute("hidden"); + } + } + } + } +} + +window.addEventListener("keydown", function (event) { + if (event.defaultPrevented) { + return; + } + if (event.key == "S") { + var checked = document.getElementsByName("showCounterexample")[0].checked; + filterCounterexample(!checked); + document.getElementsByName("showCounterexample")[0].checked = !checked; + } else { + return; + } + event.preventDefault(); +}, true); + +document.addEventListener("DOMContentLoaded", function() { + document.querySelector('input[name="showCounterexample"]').onchange= + function (event) { + filterCounterexample(this.checked); + }; +}); +</script> + +<form> + <input type="checkbox" name="showCounterexample" /> + <label for="showCounterexample"> + Show only relevant lines + </label> +</form> +)<<<"; + + return os.str(); +} + +std::string HTMLDiagnostics::serializeExecutedLines(const PathDiagnostic &D) { + std::string s; + llvm::raw_string_ostream os(s); + os << "var relevant_lines = {"; + for (auto I = D.executedLines_begin(), + E = D.executedLines_end(); I != E; ++I) { + if (I != D.executedLines_begin()) + os << ", "; + + os << "\"" << I->first << "\": {"; + for (unsigned LineNo : I->second) { + if (LineNo != *(I->second.begin())) + os << ", "; + + os << "\"" << LineNo << "\": 1"; + } + os << "}"; + } + + os << "};"; + return os.str(); +} + void HTMLDiagnostics::RewriteFile(Rewriter &R, const SourceManager& SMgr, const PathPieces& path, FileID FID) { // Process the path. diff --git a/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 669748c0127..82ab910d1e3 100644 --- a/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -98,20 +98,18 @@ void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, PathDiagnostic::~PathDiagnostic() {} -PathDiagnostic::PathDiagnostic(StringRef CheckName, const Decl *declWithIssue, - StringRef bugtype, StringRef verboseDesc, - StringRef shortDesc, StringRef category, - PathDiagnosticLocation LocationToUnique, - const Decl *DeclToUnique) - : CheckName(CheckName), - DeclWithIssue(declWithIssue), - BugType(StripTrailingDots(bugtype)), - VerboseDesc(StripTrailingDots(verboseDesc)), - ShortDesc(StripTrailingDots(shortDesc)), - Category(StripTrailingDots(category)), - UniqueingLoc(LocationToUnique), - UniqueingDecl(DeclToUnique), - path(pathImpl) {} +PathDiagnostic::PathDiagnostic( + StringRef CheckName, const Decl *declWithIssue, StringRef bugtype, + StringRef verboseDesc, StringRef shortDesc, StringRef category, + PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique, + std::unique_ptr<FilesToLineNumsMap> ExecutedLines) + : CheckName(CheckName), DeclWithIssue(declWithIssue), + BugType(StripTrailingDots(bugtype)), + VerboseDesc(StripTrailingDots(verboseDesc)), + ShortDesc(StripTrailingDots(shortDesc)), + Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique), + UniqueingDecl(DeclToUnique), ExecutedLines(std::move(ExecutedLines)), + path(pathImpl) {} static PathDiagnosticCallPiece * getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP, |