Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/include/clang/Analysis/PathDiagnostic.h
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,10 @@ class PathDiagnostic : public llvm::FoldingSetNode {
return UniqueingDecl;
}

/// Get a hash that identifies the issue.
SmallString<32> getIssueHash(const SourceManager &SrcMgr,
const LangOptions &LangOpts) const;

void flattenLocations() {
Loc.flatten();
for (const auto &I : pathImpl)
Expand Down
15 changes: 15 additions & 0 deletions clang/include/clang/Basic/Sarif.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ class SarifResult {
uint32_t RuleIdx;
std::string RuleId;
std::string DiagnosticMessage;
std::string HostedViewerURI;
llvm::SmallDenseMap<StringRef, std::string, 4> PartialFingerprints;
llvm::SmallVector<CharSourceRange, 8> Locations;
llvm::SmallVector<ThreadFlow, 8> ThreadFlows;
std::optional<SarifResultLevel> LevelOverride;
Expand All @@ -347,6 +349,11 @@ class SarifResult {
return *this;
}

SarifResult setHostedViewerURI(llvm::StringRef URI) {
HostedViewerURI = URI.str();
return *this;
}

SarifResult setLocations(llvm::ArrayRef<CharSourceRange> DiagLocs) {
#ifndef NDEBUG
for (const auto &Loc : DiagLocs) {
Expand All @@ -366,6 +373,12 @@ class SarifResult {
LevelOverride = TheLevel;
return *this;
}

SarifResult addPartialFingerprint(llvm::StringRef key,
llvm::StringRef value) {
PartialFingerprints[key] = value;
return *this;
}
};

/// This class handles creating a valid SARIF document given various input
Expand Down Expand Up @@ -475,6 +488,8 @@ class SarifDocumentWriter {
/// reported diagnostics, resulting in an expensive call.
llvm::json::Object createDocument();

static std::string fileNameToURI(llvm::StringRef Filename);

private:
/// Source Manager to use for the current SARIF document.
const SourceManager &SourceMgr;
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/Analysis/PathDiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "clang/AST/Type.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/IssueHash.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
Expand Down Expand Up @@ -1075,6 +1076,19 @@ unsigned PathDiagnostic::full_size() {
return size;
}

SmallString<32>
PathDiagnostic::getIssueHash(const SourceManager &SrcMgr,
const LangOptions &LangOpts) const {
PathDiagnosticLocation UPDLoc = getUniqueingLoc();
FullSourceLoc FullLoc(
SrcMgr.getExpansionLoc(UPDLoc.isValid() ? UPDLoc.asLocation()
: getLocation().asLocation()),
SrcMgr);

return clang::getIssueHash(FullLoc, getCheckerName(), getBugType(),
getDeclWithIssue(), LangOpts);
}

//===----------------------------------------------------------------------===//
// FoldingSet profiling methods.
//===----------------------------------------------------------------------===//
Expand Down
16 changes: 15 additions & 1 deletion clang/lib/Basic/Sarif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ static std::string percentEncodeURICharacter(char C) {
/// \param Filename The filename to be represented as URI.
///
/// \return RFC3986 URI representing the input file name.
static std::string fileNameToURI(StringRef Filename) {
std::string SarifDocumentWriter::fileNameToURI(StringRef Filename) {
SmallString<32> Ret = StringRef("file://");

// Get the root name to see if it has a URI authority.
Expand Down Expand Up @@ -391,13 +391,27 @@ void SarifDocumentWriter::appendResult(const SarifResult &Result) {
json::Object Ret{{"message", createMessage(Result.DiagnosticMessage)},
{"ruleIndex", static_cast<int64_t>(RuleIdx)},
{"ruleId", Rule.Id}};

if (!Result.HostedViewerURI.empty()) {
Ret["hostedViewerUri"] = Result.HostedViewerURI;
}

if (!Result.Locations.empty()) {
json::Array Locs;
for (auto &Range : Result.Locations) {
Locs.emplace_back(createLocation(createPhysicalLocation(Range)));
}
Ret["locations"] = std::move(Locs);
}

if (!Result.PartialFingerprints.empty()) {
json::Object fingerprints = {};
for (auto &pair : Result.PartialFingerprints) {
fingerprints[pair.first] = pair.second;
}
Ret["partialFingerprints"] = std::move(fingerprints);
}

if (!Result.ThreadFlows.empty())
Ret["codeFlows"] = json::Array{createCodeFlow(Result.ThreadFlows)};

Expand Down
28 changes: 12 additions & 16 deletions clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

#include "HTMLDiagnostics.h"
#include "PlistDiagnostics.h"
#include "SarifDiagnostics.h"
#include "clang/AST/Decl.h"
Expand Down Expand Up @@ -82,7 +83,7 @@ class HTMLDiagnostics : public PathDiagnosticConsumer {
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) override;

StringRef getName() const override { return "HTMLDiagnostics"; }
StringRef getName() const override { return HTML_DIAGNOSTICS_NAME; }

bool supportsCrossFileDiagnostics() const override {
return SupportsCrossFileDiagnostics;
Expand Down Expand Up @@ -254,18 +255,6 @@ void HTMLDiagnostics::FlushDiagnosticsImpl(
ReportDiag(*Diag, filesMade);
}

static llvm::SmallString<32> getIssueHash(const PathDiagnostic &D,
const Preprocessor &PP) {
SourceManager &SMgr = PP.getSourceManager();
PathDiagnosticLocation UPDLoc = D.getUniqueingLoc();
FullSourceLoc L(SMgr.getExpansionLoc(UPDLoc.isValid()
? UPDLoc.asLocation()
: D.getLocation().asLocation()),
SMgr);
return getIssueHash(L, D.getCheckerName(), D.getBugType(),
D.getDeclWithIssue(), PP.getLangOpts());
}

void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
FilesMade *filesMade) {
// Create the HTML directory if it is missing.
Expand Down Expand Up @@ -310,7 +299,8 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
}
}

SmallString<32> IssueHash = getIssueHash(D, PP);
SmallString<32> IssueHash =
D.getIssueHash(PP.getSourceManager(), PP.getLangOpts());
auto [It, IsNew] = EmittedHashes.insert(IssueHash);
if (!IsNew) {
// We've already emitted a duplicate issue. It'll get overwritten anyway.
Expand Down Expand Up @@ -369,6 +359,12 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
if (EC != llvm::errc::file_exists) {
llvm::errs() << "warning: could not create file in '" << Directory
<< "': " << EC.message() << '\n';
} else if (filesMade) {
// Record that we created the file so that it gets referenced in the
// plist and SARIF reports for every translation unit that found the
// issue.
filesMade->addDiagnostic(D, getName(),
llvm::sys::path::filename(ResultPath));
}
return;
}
Expand Down Expand Up @@ -679,8 +675,8 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic &D, Rewriter &R,

os << "\n<!-- FUNCTIONNAME " << declName << " -->\n";

os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " << getIssueHash(D, PP)
<< " -->\n";
os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT "
<< D.getIssueHash(PP.getSourceManager(), PP.getLangOpts()) << " -->\n";

os << "\n<!-- BUGLINE "
<< LineNumber
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//==- HTMLDiagnostics.h - HTML Diagnostics for Paths ---------------*- C++ -*-//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_HTMLDIAGNOSTICS_H
#define LLVM_CLANG_LIB_STATICANALYZER_CORE_HTMLDIAGNOSTICS_H

#define HTML_DIAGNOSTICS_NAME "HTMLDiagnostics"

#endif
10 changes: 4 additions & 6 deletions clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -706,13 +706,11 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
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()),
? UPDLoc.asLocation()
: D->getLocation().asLocation()),
SM);
const Decl *DeclWithIssue = D->getDeclWithIssue();
EmitString(o, getIssueHash(L, D->getCheckerName(), D->getBugType(),
DeclWithIssue, LangOpts))
<< '\n';

EmitString(o, D->getIssueHash(SM, LangOpts)) << '\n';

// Output information about the semantic context where
// the issue occurred.
Expand Down
46 changes: 40 additions & 6 deletions clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
//===----------------------------------------------------------------------===//

#include "SarifDiagnostics.h"
#include "HTMLDiagnostics.h"
#include "clang/Analysis/IssueHash.h"
#include "clang/Analysis/MacroExpansionContext.h"
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/Sarif.h"
Expand All @@ -31,12 +33,13 @@ namespace {
class SarifDiagnostics : public PathDiagnosticConsumer {
std::string OutputFile;
const LangOptions &LO;
const SourceManager &SM;
SarifDocumentWriter SarifWriter;

public:
SarifDiagnostics(const std::string &Output, const LangOptions &LO,
const SourceManager &SM)
: OutputFile(Output), LO(LO), SarifWriter(SM) {}
: OutputFile(Output), LO(LO), SM(SM), SarifWriter(SM) {}
~SarifDiagnostics() override = default;

void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
Expand All @@ -46,6 +49,11 @@ class SarifDiagnostics : public PathDiagnosticConsumer {
PathGenerationScheme getGenerationScheme() const override { return Minimal; }
bool supportsLogicalOpControlFlow() const override { return true; }
bool supportsCrossFileDiagnostics() const override { return true; }

private:
SarifResult createResult(const PathDiagnostic *Diag,
const StringMap<uint32_t> &RuleMapping,
const LangOptions &LO, FilesMade *FM);
};
} // end anonymous namespace

Expand Down Expand Up @@ -173,27 +181,53 @@ createRuleMapping(const std::vector<const PathDiagnostic *> &Diags,
return RuleMapping;
}

static SarifResult createResult(const PathDiagnostic *Diag,
const StringMap<uint32_t> &RuleMapping,
const LangOptions &LO) {
static const llvm::StringRef IssueHashKey = "clang/issueHash/v1";

SarifResult
SarifDiagnostics::createResult(const PathDiagnostic *Diag,
const StringMap<uint32_t> &RuleMapping,
const LangOptions &LO, FilesMade *FM) {

StringRef CheckName = Diag->getCheckerName();
uint32_t RuleIdx = RuleMapping.lookup(CheckName);
auto Range = convertTokenRangeToCharRange(
Diag->getLocation().asRange(), Diag->getLocation().getManager(), LO);

SmallVector<ThreadFlow, 8> Flows = createThreadFlows(Diag, LO);

auto IssueHash = Diag->getIssueHash(SM, LO);

std::string HtmlReportURL;
if (FM && !FM->empty()) {
// Find the HTML report that was generated for this issue, if one exists.
PDFileEntry::ConsumerFiles *Files = FM->getFiles(*Diag);
if (Files) {
auto HtmlFile =
std::find_if(Files->cbegin(), Files->cend(), [](auto &File) {
return File.first == HTML_DIAGNOSTICS_NAME;
});
if (HtmlFile != Files->cend()) {
SmallString<128> HtmlReportPath =
llvm::sys::path::parent_path(OutputFile);
llvm::sys::path::append(HtmlReportPath, HtmlFile->second);
HtmlReportURL = SarifDocumentWriter::fileNameToURI(HtmlReportPath);
}
}
}

auto Result = SarifResult::create(RuleIdx)
.setRuleId(CheckName)
.setDiagnosticMessage(Diag->getVerboseDescription())
.setDiagnosticLevel(SarifResultLevel::Warning)
.setLocations({Range})
.addPartialFingerprint(IssueHashKey, IssueHash)
.setHostedViewerURI(HtmlReportURL)
.setThreadFlows(Flows);
return Result;
}

void SarifDiagnostics::FlushDiagnosticsImpl(
std::vector<const PathDiagnostic *> &Diags, FilesMade *) {
std::vector<const PathDiagnostic *> &Diags, FilesMade *FM) {
// We currently overwrite the file if it already exists. However, it may be
// useful to add a feature someday that allows the user to append a run to an
// existing SARIF file. One danger from that approach is that the size of the
Expand All @@ -210,7 +244,7 @@ void SarifDiagnostics::FlushDiagnosticsImpl(
SarifWriter.createRun("clang", "clang static analyzer", ToolVersion);
StringMap<uint32_t> RuleMapping = createRuleMapping(Diags, SarifWriter);
for (const PathDiagnostic *D : Diags) {
SarifResult Result = createResult(D, RuleMapping, LO);
SarifResult Result = createResult(D, RuleMapping, LO, FM);
SarifWriter.appendResult(Result);
}
auto Document = SarifWriter.createDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
{
"artifacts": [
{
"length": 425,
"length": -1,
"location": {
"index": 0,
"uri": "file:///[...]/sarif-diagnostics-taint-test.c"
},
"mimeType": "text/plain",
"roles": [
Expand All @@ -31,6 +32,7 @@
"physicalLocation": {
"artifactLocation": {
"index": 0,
"uri": "file:///[...]/sarif-diagnostics-taint-test.c"
},
"region": {
"endColumn": 6,
Expand All @@ -50,6 +52,7 @@
"physicalLocation": {
"artifactLocation": {
"index": 0,
"uri": "file:///[...]/sarif-diagnostics-taint-test.c"
},
"region": {
"endColumn": 18,
Expand All @@ -71,6 +74,7 @@
"physicalLocation": {
"artifactLocation": {
"index": 0,
"uri": "file:///[...]/sarif-diagnostics-taint-test.c"
},
"region": {
"endColumn": 18,
Expand All @@ -84,6 +88,9 @@
"message": {
"text": "tainted"
},
"partialFingerprints": {
"clang/issueHash/v1": "5c964815b8d6db3989bacdd308e657d0"
},
"ruleId": "debug.TaintTest",
"ruleIndex": 0
}
Expand All @@ -108,8 +115,10 @@
"name": "debug.TaintTest"
}
],
"version": "[clang version]"
}
}
}
],
"version": "[SARIF version]"
}
Loading