196 lines
6.9 KiB
C++
196 lines
6.9 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Diag Sarif output file
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// Copyright 2004-2025 by Wilson Snyder. This program is free software; you
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
// Version 2.0.
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
//
|
|
//*************************************************************************
|
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
#include "V3DiagSarif.h"
|
|
|
|
#include "V3File.h"
|
|
#include "V3Os.h"
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
// ######################################################################
|
|
|
|
class V3DiagSarifImp final {
|
|
// MEMBERS
|
|
std::deque<VErrorMessage> m_messages;
|
|
std::set<V3ErrorCode> m_codes;
|
|
std::map<V3ErrorCode, int> m_codeIndex;
|
|
|
|
// METHODS
|
|
void calculate() {
|
|
for (auto& msgr : m_messages)
|
|
if (msgr.code().isNamed()) m_codes.emplace(msgr.code());
|
|
int i = 0;
|
|
for (const auto& code : m_codes) m_codeIndex[code] = i++;
|
|
}
|
|
|
|
void putRules(V3OutJsonFile& of) const {
|
|
for (const auto& code : m_codes) {
|
|
of.begin().put("id", code.ascii()).put("helpUri", code.url()).end();
|
|
}
|
|
}
|
|
|
|
void putText(V3OutJsonFile& of, const string& clean, const string& formatted) const {
|
|
const string markdown = "```\n" + formatted + "\n```\n";
|
|
of.put("text", VString::trimWhitespace(clean)).put("markdown", markdown);
|
|
}
|
|
|
|
void putLocation(V3OutJsonFile& of, const FileLine* fl) const {
|
|
const string filename = V3Os::filenameRealPath(fl->filename());
|
|
of.begin("physicalLocation");
|
|
of.begin("artifactLocation");
|
|
if (V3Os::filenameIsRel(filename)) { // SARIF spec says rel has no //
|
|
of.put("uri", "file:" + filename).put("uriBaseId", "%srcroot%");
|
|
} else {
|
|
of.put("uri", "file://" + filename);
|
|
}
|
|
of.end()
|
|
.begin("region")
|
|
.put("sourceLanguage", "systemverilog")
|
|
.put("startLine", fl->firstLineno())
|
|
.put("startColumn", fl->firstColumn());
|
|
if (fl->firstLineno() != fl->lastLineno()) of.put("endLine", fl->lastLineno());
|
|
of.put("endColumn", fl->lastColumn());
|
|
of.begin("snippit", '{');
|
|
putText(of, fl->prettySource(), V3Error::stripMetaText(fl->warnContextPrimary(), false));
|
|
of.end(); // snippit
|
|
of.end(); // region
|
|
of.end(); // artifactLocation
|
|
}
|
|
|
|
string substrToNextRelated(const string& str) {
|
|
const size_t pos = str.find("__WARNRELATED(", 0);
|
|
return (pos == std::string::npos) ? str : str.substr(0, pos);
|
|
}
|
|
|
|
void putResult(V3OutJsonFile& of, const VErrorMessage& msg) {
|
|
of.begin();
|
|
of.put("level", msg.code().severityInfo() ? "none"
|
|
: V3Error::isError(msg.code(), false) ? "error"
|
|
: "warning");
|
|
|
|
string text = msg.text();
|
|
// UINFO(9, "Result raw text " << text);
|
|
|
|
// We put the entire message including relatedLocations text
|
|
// into the primary message text, because viewers such as
|
|
// https://microsoft.github.io/sarif-web-component/
|
|
// currently appear to ignore relatedLocations.
|
|
const string first_clean = V3Error::stripMetaText(text, true);
|
|
const string first_fmt = V3Error::stripMetaText(text, false);
|
|
|
|
of.begin("message");
|
|
putText(of, first_clean, first_fmt);
|
|
of.end();
|
|
if (auto fl = msg.fileline()) {
|
|
of.begin("locations", '[').begin();
|
|
putLocation(of, fl);
|
|
of.end();
|
|
of.end();
|
|
}
|
|
|
|
if (text.find("__WARNRELATED(") != std::string::npos) {
|
|
of.begin("relatedLocations", '[');
|
|
size_t pos = 0;
|
|
while ((pos = text.find("__WARNRELATED(", pos)) != std::string::npos) {
|
|
const size_t start = pos + std::strlen("__WARNRELATED(");
|
|
size_t end = start;
|
|
while (end < text.size() && text[end] != ')') ++end;
|
|
const size_t index = std::atoi(text.substr(start, end + 1).c_str());
|
|
if (end < text.size()) end += std::strlen(")");
|
|
text = text.substr(end, text.size());
|
|
UASSERT_STATIC(index < msg.filelines().size(),
|
|
"Error message warnRelated without fileline");
|
|
|
|
const string related_clean
|
|
= V3Error::stripMetaText(substrToNextRelated(text), true);
|
|
const string related_fmt
|
|
= V3Error::stripMetaText(substrToNextRelated(text), false);
|
|
const FileLine* fl = msg.filelines()[index];
|
|
of.begin().begin("message");
|
|
putText(of, related_clean, related_fmt);
|
|
of.end(); // message
|
|
putLocation(of, fl);
|
|
of.end();
|
|
}
|
|
of.end();
|
|
}
|
|
|
|
if (msg.code().isNamed())
|
|
of.put("ruleId", msg.code().ascii()).put("ruleIndex", m_codeIndex[msg.code()]);
|
|
|
|
of.end();
|
|
}
|
|
|
|
// STATIC FUNCTIONS
|
|
public:
|
|
static V3DiagSarifImp& s() {
|
|
static V3DiagSarifImp s_s;
|
|
return s_s;
|
|
}
|
|
|
|
void pushMessage(const VErrorMessage& msg) { m_messages.push_back(msg); }
|
|
|
|
void output(bool success) {
|
|
V3OutJsonFile of{v3Global.opt.diagnosticsSarifOutput()};
|
|
calculate();
|
|
|
|
of.put("$schema", "https://json.schemastore.org/sarif-2.1.0-rtm.5.json")
|
|
.put("version", "2.1.0");
|
|
|
|
of.begin("runs", '[').begin();
|
|
|
|
of.begin("tool")
|
|
.begin("driver")
|
|
.put("name", "Verilator")
|
|
.put("version", VString::replaceSubstr(V3Options::version(), "Verilator ", ""))
|
|
.put("informationUri", "https://verilator.org");
|
|
|
|
of.begin("rules", '[');
|
|
putRules(of);
|
|
of.end() // rules
|
|
.end() // driver
|
|
.end(); //tool
|
|
|
|
of.begin("invocations", '[')
|
|
.begin()
|
|
.put("commandLine", v3Global.opt.allArgsString())
|
|
.put("executionSuccessful", success)
|
|
.end()
|
|
.end();
|
|
|
|
of.begin("results", '[');
|
|
for (auto& msgr : m_messages) putResult(of, msgr);
|
|
of.end();
|
|
|
|
of.end(); // runs ]
|
|
of.end(); // runs }
|
|
}
|
|
};
|
|
|
|
void V3DiagSarif::pushMessage(const VErrorMessage& msg) VL_MT_DISABLED {
|
|
if (!v3Global.opt.diagnosticsSarif()) return;
|
|
V3DiagSarifImp::s().pushMessage(msg);
|
|
}
|
|
|
|
void V3DiagSarif::output(bool success) {
|
|
if (!v3Global.opt.diagnosticsSarif()) return;
|
|
UINFO(2, __FUNCTION__ << ":");
|
|
V3DiagSarifImp::s().output(success);
|
|
}
|