Support SARIF JSON diagnostic output with `--diagnostics-sarif`. (#6017)

This commit is contained in:
Wilson Snyder 2025-05-18 04:46:15 +09:00 committed by GitHub
parent 7a5c223ccf
commit 66667b6172
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 797 additions and 84 deletions

View File

@ -15,6 +15,7 @@ Verilator 5.037 devel
* Support constrained random for associative arrays (#5985) (#5986). [Yilou Wang]
* Support assignments to concatenations with impure RHS (#6002). [Ryszard Rozak, Antmicro Ltd.]
* Support SARIF JSON diagnostic output with `--diagnostics-sarif`.
* Add BADVLTPRAGMA on unknown Verilator pragmas (#5945). [Shou-Li Hsu]
* Add PROCINITASSIGN on initial assignments to process variables (#2481). [Niraj Menon]
* Fix --x-initial and --x-assign random stability (#2662) (#5958). [Todd Strader]

View File

@ -345,6 +345,8 @@ detailed descriptions of these arguments.
--decorations <level> Set output comment and spacing level
--default-language <lang> Default language to parse
+define+<var>=<value> Set preprocessor define
--diagnostics-sarif Enable SARIF diagnostics output
--diagnostics-sarif-output <filename> Set SARIF diagnostics output file
--dpi-hdr-only Only produce the DPI header file
--dump-<srcfile> Enable dumping everything in source file
--dump-defines Show preprocessor defines with -E

View File

@ -393,6 +393,22 @@ Summary:
standard across Verilog tools while :vlopt:`-D <-D<var>>` is similar to
:command:`gcc -D`.
.. option:: --diagnostics-sarif
Enables diagnostics output into a Static Analysis Results Interchange
Format (SARIF) file, a standard, JSON-based format for the output of
static analysis tools such as linters. See
[SARIF](http://sarifweb.azurewebsites.net/),
[sarif-tools](https://github.com/microsoft/sarif-tools), and the [SARIF
web-based viewer](https://microsoft.github.io/sarif-web-component/).
.. option:: --diagnostics-sarif-output <filename>
Specifies the filename for the SARIF output file (`.sarif`) of
:vlopt:`--diagnostics-sarif`. Using this option automatically sets
:vlopt:`--diagnostics-sarif`. If not specified, output defaults to
:file:`<prefix>.sarif`.
.. option:: --dpi-hdr-only
Only generate the DPI header file. This option does not affect on the

View File

@ -112,6 +112,8 @@ In specific debug and other modes, it also creates:
.. list-table::
* - *{prefix}*\ .sarif
- SARIF diagnostics (from --diagnostics-sarif)
* - *{prefix}*\ .tree.json
- JSON tree information (from --json-only)
* - *{prefix}*\ .tree.meta.json

View File

@ -159,7 +159,7 @@ Those developing Verilator itself may also want these (see internals.rst):
sudo apt-get install clang clang-format-14 cmake gdb gprof graphviz lcov
sudo apt-get install python3-clang python3-distro yapf3 bear jq
sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff sarif-tools
sudo pip3 install git+https://github.com/antmicro/astsee.git
cpan install Pod::Perldoc

View File

@ -68,6 +68,10 @@ source code corresponding to the error, prefixed by the line number and a "
| ". Following this is typically an arrow and ~ pointing at the error on
the source line directly above.
Instead of parsing this text diagnostic output, tools that need to
understand Verilator's warning output should read the SARIF JSON output
created with :vlopt:`--diagnostics-sarif`.
List Of Warnings
================

View File

@ -798,6 +798,7 @@ libtcmalloc
libverilated
linkers
linter
linters
linux
liu
livelock
@ -975,6 +976,7 @@ runtimes
rw
sVerilator
saif
sarif
sawatzke
sc
scalared

View File

@ -77,6 +77,7 @@ set(HEADERS
V3DfgPatternStats.h
V3DfgPeephole.h
V3DfgVertices.h
V3DiagSarif.h
V3DupFinder.h
V3EmitC.h
V3EmitCBase.h
@ -95,8 +96,8 @@ set(HEADERS
V3FileLine.h
V3Force.h
V3Fork.h
V3FunctionTraits.h
V3FuncOpt.h
V3FunctionTraits.h
V3Gate.h
V3Global.h
V3Graph.h
@ -133,8 +134,8 @@ set(HEADERS
V3OptionParser.h
V3Options.h
V3Order.h
V3OrderInternal.h
V3OrderGraph.h
V3OrderInternal.h
V3OrderMoveGraph.h
V3Os.h
V3PairingHeap.h
@ -234,8 +235,8 @@ set(COMMON_SOURCES
V3DfgPasses.cpp
V3DfgPeephole.cpp
V3DfgRegularize.cpp
V3DiagSarif.cpp
V3DupFinder.cpp
V3Timing.cpp
V3EmitCBase.cpp
V3EmitCConstPool.cpp
V3EmitCFunc.cpp
@ -297,6 +298,10 @@ set(COMMON_SOURCES
V3OrderSerial.cpp
V3Os.cpp
V3Param.cpp
V3ParseGrammar.cpp
V3ParseImp.cpp
V3ParseLex.cpp
V3PreProc.cpp
V3PreShell.cpp
V3Premit.cpp
V3ProtectLib.cpp
@ -320,13 +325,14 @@ set(COMMON_SOURCES
V3StatsReport.cpp
V3String.cpp
V3Subst.cpp
V3TSP.cpp
V3Table.cpp
V3Task.cpp
V3ThreadPool.cpp
V3Timing.cpp
V3Trace.cpp
V3TraceDecl.cpp
V3Tristate.cpp
V3TSP.cpp
V3Udp.cpp
V3Undriven.cpp
V3Unknown.cpp
@ -336,10 +342,6 @@ set(COMMON_SOURCES
V3Width.cpp
V3WidthCommit.cpp
V3WidthSel.cpp
V3ParseImp.cpp
V3ParseGrammar.cpp
V3ParseLex.cpp
V3PreProc.cpp
)
set(COVERAGE_SOURCES VlcMain.cpp)

View File

@ -247,6 +247,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3DfgPasses.o \
V3DfgPeephole.o \
V3DfgRegularize.o \
V3DiagSarif.o \
V3DupFinder.o \
V3EmitCMain.o \
V3EmitCMake.o \

View File

@ -1453,7 +1453,7 @@ void AstNode::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().
const string instanceStrExtra
= m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr();
if (!m_fileline) {
V3Error::v3errorEnd(str, instanceStrExtra);
V3Error::v3errorEnd(str, instanceStrExtra, nullptr);
} else {
std::ostringstream nsstr;
nsstr << str.str();

195
src/V3DiagSarif.cpp Normal file
View File

@ -0,0 +1,195 @@
// -*- 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 << endl << endl);
// 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__ << ": " << endl);
V3DiagSarifImp::s().output(success);
}

33
src/V3DiagSarif.h Normal file
View File

@ -0,0 +1,33 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Diag Sarif output file
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-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
//
//*************************************************************************
#ifndef VERILATOR_V3DIAGSARIF_H_
#define VERILATOR_V3DIAGSARIF_H_
#include "config_build.h"
#include "verilatedos.h"
#include <V3Error.h>
//============================================================================
class V3DiagSarif final {
public:
static void pushMessage(const VErrorMessage& msg) VL_MT_DISABLED;
static void output(bool success) VL_MT_DISABLED;
};
#endif // Guard

View File

@ -19,6 +19,7 @@
#include "V3Os.h"
#ifndef V3ERROR_NO_GLOBAL_
# include "V3Ast.h"
# include "V3DiagSarif.h"
# include "V3Global.h"
# include "V3Stats.h"
VL_DEFINE_DEBUG_FUNCTIONS;
@ -70,7 +71,7 @@ bool V3ErrorGuarded::isError(V3ErrorCode code, bool supp) VL_REQUIRES(m_mutex) {
}
string V3ErrorGuarded::msgPrefix() VL_REQUIRES(m_mutex) {
const V3ErrorCode code = m_errorCode;
const V3ErrorCode code = m_message.code();
const bool supp = m_errorSuppressed;
if (supp) {
return "-arning-suppressed: ";
@ -112,6 +113,7 @@ void V3ErrorGuarded::vlAbortOrExit() VL_REQUIRES(m_mutex) {
}
string V3ErrorGuarded::warnMoreSpaces() VL_REQUIRES(m_mutex) {
UASSERT_STATIC(!m_message.isClear(), "warnMore() outside of v3errorPrep...v3errorEnd");
return string(msgPrefix().size(), ' ');
}
@ -124,42 +126,49 @@ void V3ErrorGuarded::suppressThisWarning() VL_REQUIRES(m_mutex) {
void V3ErrorGuarded::v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex) {
m_errorStr.str("");
m_errorCode = code;
UASSERT_STATIC(m_message.isClear(), "Attempted v3errorPrep inside v3errorPrep...v3errorEnd");
m_message.init(code);
m_errorContexted = false;
m_errorSuppressed = false;
}
void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra)
void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra, FileLine* fileline)
VL_REQUIRES(m_mutex) {
static bool s_firedTooMany = false;
v3errorEndGuts(sstr, extra);
v3errorEndGuts(sstr, extra, fileline);
m_message.clear();
if (errorLimit() && errorCount() >= errorLimit() && !s_firedTooMany) {
s_firedTooMany = true;
// Recurses here
v3errorEnd((v3errorPrep(V3ErrorCode::EC_FATALMANY),
(v3errorStr() << "Exiting due to too many errors encountered; --error-limit="
<< errorCount() << std::endl),
v3errorStr()));
v3errorStr()),
"", nullptr);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
}
}
// cppcheck-has-bug-suppress constParameter
void V3ErrorGuarded::v3errorEndGuts(std::ostringstream& sstr, const string& extra)
VL_REQUIRES(m_mutex) {
void V3ErrorGuarded::v3errorEndGuts(std::ostringstream& sstr, const string& extra,
FileLine* fileline) VL_REQUIRES(m_mutex) {
// 'extra' is appended to the message, and is is excluded in check for
// duplicate messages. Currently used for reporting instance name.
#if defined(__COVERITY__) || defined(__cppcheck__)
if (m_errorCode == V3ErrorCode::EC_FATAL) __coverity_panic__(x);
#endif
UASSERT_STATIC(!m_message.isClear(), "v3errorEnd() outside of v3errorPrep...v3errorEnd");
m_message.fileline(fileline);
// Skip suppressed messages
if (m_errorSuppressed
// On debug, show only non default-off warning to prevent pages of warnings
&& (!debug() || debug() < 3 || m_errorCode.defaultsOff()))
&& (!debug() || debug() < 3 || m_message.code().defaultsOff()))
return;
string msg = msgPrefix() + sstr.str();
string msg
= V3Error::warnContextBegin() + msgPrefix() + V3Error::warnContextEnd() + sstr.str();
// If suppressed print only first line to reduce verbosity
string firstLine = msg;
@ -182,14 +191,7 @@ void V3ErrorGuarded::v3errorEndGuts(std::ostringstream& sstr, const string& extr
msg.erase(pos);
}
}
// Trailing newline (generally not on messages) & remove dup newlines
{
msg += '\n'; // Trailing newlines generally not put on messages so add
string::size_type pos;
while ((pos = msg.find("\n\n")) != string::npos) msg.erase(pos + 1, 1);
while ((pos = msg_additional.find("\n\n")) != string::npos)
msg_additional.erase(pos + 1, 1);
}
msg += '\n'; // Trailing newlines generally not put on messages so add
if (!extra.empty() && !m_errorSuppressed) {
string extraMsg = VString::replaceSubstr(extra, V3Error::warnMore(), warnMoreSpaces());
extraMsg = warnMoreSpaces() + extraMsg + "\n";
@ -197,53 +199,64 @@ void V3ErrorGuarded::v3errorEndGuts(std::ostringstream& sstr, const string& extr
msg.insert(pos + 1, extraMsg);
}
// Output
string text;
if (
#ifndef V3ERROR_NO_GLOBAL_
!(v3Global.opt.quietExit() && m_errorCode == V3ErrorCode::EC_FATALMANY)
!(v3Global.opt.quietExit() && m_message.code() == V3ErrorCode::EC_FATALMANY)
#else
true
#endif
) {
std::cerr << msg;
}
if (!m_errorSuppressed && !m_errorCode.severityInfo()) {
const bool anError = isError(m_errorCode, m_errorSuppressed);
if (m_errorCode != V3ErrorCode::EC_FATALMANY // Not verbose on final too-many-errors error
&& !m_describedEachWarn[m_errorCode]) {
m_describedEachWarn[m_errorCode] = true;
if (m_errorCode >= V3ErrorCode::EC_FIRST_NAMED) {
std::cerr << warnMoreSpaces() << "... For " << (anError ? "error" : "warning")
<< " description see " << m_errorCode.url() << endl;
} else if (m_errCount >= 1 && m_errorCode.severityFatal() && !m_tellInternal) {
)
text += msg;
if (m_errorSuppressed || m_message.code().severityInfo()) {
std::cerr << V3Error::stripMetaText(text, false);
m_message.text(text);
#ifndef V3ERROR_NO_GLOBAL_
V3DiagSarif::pushMessage(m_message);
#endif
} else {
const bool anError = isError(m_message.code(), m_errorSuppressed);
if (m_message.code()
!= V3ErrorCode::EC_FATALMANY // Not verbose on final too-many-errors error
&& !m_describedEachWarn[m_message.code()]) {
m_describedEachWarn[m_message.code()] = true;
if (m_message.code() >= V3ErrorCode::EC_FIRST_NAMED) {
text += warnMoreSpaces() + "... For " + (anError ? "error" : "warning")
+ " description see " + m_message.code().url() + '\n';
} else if (m_errCount >= 1 && m_message.code().severityFatal() && !m_tellInternal) {
m_tellInternal = true;
std::cerr << warnMoreSpaces()
<< "... This fatal error may be caused by the earlier error(s);"
" resolve those first."
<< endl;
text += warnMoreSpaces()
+ "... This fatal error may be caused by the earlier error(s);"
" resolve those first.\n";
} else if (!m_tellManual) {
m_tellManual = true;
std::cerr << warnMoreSpaces() << "... See the manual at " << m_errorCode.url()
<< " for more assistance." << endl;
text += warnMoreSpaces() + "... See the manual at " + m_message.code().url()
+ " for more assistance.\n";
}
if (!m_pretendError[m_errorCode] && !m_errorCode.hardError()) {
std::cerr << warnMoreSpaces() << "... Use \"/* verilator lint_off "
<< m_errorCode.ascii()
<< " */\" and lint_on around source to disable this message." << endl;
if (m_errorCode.dangerous()) {
std::cerr << warnMoreSpaces() << "*** See " << m_errorCode.url()
<< " before disabling this,\n";
std::cerr << warnMoreSpaces()
<< "else you may end up with different sim results." << endl;
if (!m_pretendError[m_message.code()] && !m_message.code().hardError()) {
text += warnMoreSpaces() + "... Use \"/* verilator lint_off "
+ m_message.code().ascii()
+ " */\" and lint_on around source to disable this message.\n";
if (m_message.code().dangerous()) {
text += warnMoreSpaces() + "*** See " + m_message.code().url()
+ " before disabling this,\n";
text += warnMoreSpaces() + "else you may end up with different sim results.\n";
}
}
}
if (!msg_additional.empty()) std::cerr << msg_additional;
if (!msg_additional.empty()) text += msg_additional;
std::cerr << V3Error::stripMetaText(text, false);
m_message.text(text);
#ifndef V3ERROR_NO_GLOBAL_
V3DiagSarif::pushMessage(m_message);
#endif
if (anError) {
incErrors();
} else {
incWarnings();
}
if (m_errorCode.severityFatal()) {
if (m_message.code().severityFatal()) {
static bool inFatal = false;
if (!inFatal) {
inFatal = true;
@ -264,6 +277,7 @@ void V3ErrorGuarded::v3errorEndGuts(std::ostringstream& sstr, const string& extr
}
vlAbortOrExit();
}
V3DiagSarif::output(false);
#endif
}
@ -295,6 +309,45 @@ string V3Error::lineStr(const char* filename, int lineno) VL_PURE {
return out.str();
}
string V3Error::stripMetaText(const string& text, bool stripContext) VL_PURE {
string result;
result.reserve(text.size());
int inBegins = 0;
for (string::size_type pos = 0; pos < text.size();) {
// string::starts_with is C++20
if (0 == text.compare(pos, std::strlen("__WARN"), "__WARN")) {
if (0 == text.compare(pos, std::strlen("__WARNRELATED("), "__WARNRELATED(")) {
while (pos < text.size() && text[pos] != ')') ++pos;
if (pos < text.size() && text[pos] == ')') ++pos;
continue;
}
if (0
== text.compare(pos, std::strlen(V3Error::WARN_CONTEXT_BEGIN),
V3Error::WARN_CONTEXT_BEGIN)) {
pos += std::strlen(V3Error::WARN_CONTEXT_BEGIN);
++inBegins;
continue;
}
if (0
== text.compare(pos, std::strlen(V3Error::WARN_CONTEXT_END),
V3Error::WARN_CONTEXT_END)) {
pos += std::strlen(V3Error::WARN_CONTEXT_END);
// No assert, is not VL_PURE
// UASSERT_STATIC(inBegins, "warnContextEnd() outside of warnContextBegin()");
--inBegins;
continue;
}
}
if (!stripContext || inBegins == 0) {
// Remove double newlines
if (!(text[pos] == '\n' && !result.empty() && result[result.size() - 1] == '\n'))
result += text[pos];
}
++pos;
}
return result;
}
void V3Error::abortIfWarnings() {
const bool exwarn = warnFatal() && warnCount();
if (errorCount() && exwarn) {
@ -324,16 +377,17 @@ std::ostringstream& V3Error::v3errorPrepFileLine(V3ErrorCode code, const char* f
v3errorPrep(code) << file << ":" << std::dec << line << ": ";
return v3errorStr();
}
void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra) VL_RELEASE(s().m_mutex) {
s().v3errorEnd(sstr, extra);
void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra, FileLine* fileline)
VL_RELEASE(s().m_mutex) {
s().v3errorEnd(sstr, extra, fileline);
V3Error::s().m_mutex.unlock();
}
void v3errorEnd(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex) {
V3Error::v3errorEnd(sstr);
V3Error::v3errorEnd(sstr, "", nullptr);
}
void v3errorEndFatal(std::ostringstream& sstr) VL_RELEASE(V3Error::s().m_mutex) {
V3Error::v3errorEnd(sstr);
V3Error::v3errorEnd(sstr, "", nullptr);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
}

View File

@ -316,6 +316,42 @@ inline std::ostream& operator<<(std::ostream& os, const V3ErrorCode& rhs) {
// ######################################################################
class VErrorMessage final {
// TYPES
using FileLines = std::deque<const FileLine*>;
// MEMBERS
V3ErrorCode m_code; // Which warning
string m_text; // Warning message text
FileLine* m_fileline; // Primary warning fileline
FileLines m_filelines; // Additional referenced filelines
public:
// CONSTRUCTORS
VErrorMessage() { clear(); }
~VErrorMessage() = default;
void clear() { init(V3ErrorCode::EC_MIN); }
void init(V3ErrorCode code) {
m_code = code;
m_text = "";
m_fileline = nullptr;
m_filelines.clear();
}
// ACCESSORS
V3ErrorCode code() const { return m_code; }
bool isClear() const { return m_code == V3ErrorCode::EC_MIN; }
string text() const { return m_text; }
void text(const string& msg) { m_text = msg; }
FileLine* fileline() const { return m_fileline; }
void fileline(FileLine* fl) { m_fileline = fl; }
FileLines filelines() const { return m_filelines; }
// Returns identifier for use in warnRelated()
int pushFileline(const FileLine* fl) {
m_filelines.emplace_back(fl);
return m_filelines.size() - 1;
}
};
// ######################################################################
class V3ErrorGuarded final {
// Should only be used by V3ErrorGuarded::m_mutex is already locked
// contains guarded members
@ -328,11 +364,11 @@ public:
private:
static constexpr unsigned MAX_ERRORS = 50; // Fatal after this may errors
// MEMBERS
// Tell user to see manual, 0=not yet, 1=doit, 2=disable
bool m_tellManual VL_GUARDED_BY(m_mutex) = false;
bool m_tellInternal VL_GUARDED_BY(m_mutex) = false;
V3ErrorCode m_errorCode VL_GUARDED_BY(m_mutex)
= V3ErrorCode::EC_FATAL; // Error string being formed will abort
VErrorMessage m_message VL_GUARDED_BY(m_mutex); // Message being formed
bool m_errorSuppressed VL_GUARDED_BY(m_mutex)
= false; // Error being formed should be suppressed
MessagesSet m_messages VL_GUARDED_BY(m_mutex); // Errors outputted, to remove dups
@ -351,10 +387,13 @@ private:
bool m_warnFatal VL_GUARDED_BY(m_mutex) = true; // Option: --warnFatal Warnings are fatal
std::ostringstream m_errorStr VL_GUARDED_BY(m_mutex); // Error string being formed
// METHODS
void v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex);
std::ostringstream& v3errorStr() VL_REQUIRES(m_mutex) { return m_errorStr; }
void v3errorEnd(std::ostringstream& sstr, const string& extra = "") VL_REQUIRES(m_mutex);
void v3errorEndGuts(std::ostringstream& sstr, const string& extra) VL_REQUIRES(m_mutex);
void v3errorEnd(std::ostringstream& sstr, const string& extra, FileLine* fileline)
VL_REQUIRES(m_mutex);
void v3errorEndGuts(std::ostringstream& sstr, const string& extra, FileLine* fileline)
VL_REQUIRES(m_mutex);
public:
V3RecursiveMutex m_mutex; // Make sure only single thread is in class
@ -387,7 +426,7 @@ public:
void errorLimit(int level) VL_REQUIRES(m_mutex) { m_errorLimit = level; }
bool warnFatal() VL_REQUIRES(m_mutex) { return m_warnFatal; }
void warnFatal(bool flag) VL_REQUIRES(m_mutex) { m_warnFatal = flag; }
V3ErrorCode errorCode() VL_REQUIRES(m_mutex) { return m_errorCode; }
V3ErrorCode errorCode() VL_REQUIRES(m_mutex) { return m_message.code(); }
bool errorContexted() VL_REQUIRES(m_mutex) { return m_errorContexted; }
int warnCount() VL_REQUIRES(m_mutex) { return m_warnCount; }
bool errorSuppressed() VL_REQUIRES(m_mutex) { return m_errorSuppressed; }
@ -399,6 +438,10 @@ public:
m_describedEachWarn[code] = flag;
}
void suppressThisWarning() VL_REQUIRES(m_mutex);
string warnRelated(const FileLine* fl) VL_REQUIRES(m_mutex) {
const int id = m_message.pushFileline(fl);
return "__WARNRELATED("s + std::to_string(id) + ")";
}
string warnContextNone() VL_REQUIRES(m_mutex) {
errorContexted(true);
return "";
@ -479,6 +522,10 @@ public:
s().incWarnings();
}
static void init();
static bool isError(V3ErrorCode code, bool supp) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const V3RecursiveLockGuard guard{s().m_mutex};
return s().isError(code, supp);
}
static void abortIfErrors() {
if (errorCount()) abortIfWarnings();
}
@ -493,6 +540,7 @@ public:
s().pretendError(code, flag);
}
static string lineStr(const char* filename, int lineno) VL_PURE;
static string stripMetaText(const string& text, bool stripMeta) VL_PURE;
static V3ErrorCode errorCode() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const V3RecursiveLockGuard guard{s().m_mutex};
return s().errorCode();
@ -503,7 +551,14 @@ public:
}
// When printing an error/warning, print prefix for multiline message
static string warnMore() VL_MT_SAFE { return "__WARNMORE__"; }
static constexpr const char* WARN_MORE = "__WARNMORE__";
static string warnMore() VL_MT_SAFE { return WARN_MORE; }
// When printing an error/warning, mark beginning of context information (can nest)
static constexpr const char* WARN_CONTEXT_BEGIN = "__WARNBEGIN__";
static string warnContextBegin() VL_MT_SAFE { return WARN_CONTEXT_BEGIN; }
// When printing an error/warning, mark end of context information (can nest)
static constexpr const char* WARN_CONTEXT_END = "__WARNEND__";
static string warnContextEnd() VL_MT_SAFE { return WARN_CONTEXT_END; }
// This function marks place in error message from which point message
// should be printed after information on the error code.
// The post-processing is done in v3errorEnd function.
@ -521,7 +576,7 @@ public:
VL_ACQUIRE(s().m_mutex);
static std::ostringstream& v3errorStr() VL_REQUIRES(s().m_mutex) { return s().v3errorStr(); }
// static, but often overridden in classes.
static void v3errorEnd(std::ostringstream& sstr, const string& extra = "")
static void v3errorEnd(std::ostringstream& sstr, const string& extra, FileLine* fileline)
VL_RELEASE(s().m_mutex);
static void vlAbort();
};

View File

@ -224,7 +224,7 @@ string FileLine::xmlDetailedLocation() const {
string FileLine::lineDirectiveStrg(int enterExit) const {
return "`line "s + cvtToStr(lastLineno()) + " \""
+ V3OutFormatter::quoteNameControls(filename()) + "\" " + cvtToStr(enterExit) + "\n";
+ V3OutFormatter::quoteNameControls(filename()) + "\" " + cvtToStr(enterExit) + '\n';
}
void FileLine::lineDirective(const char* textp, int& enterExitRef) {
@ -422,15 +422,15 @@ void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra)
// duplicate messages. Currently used for reporting instance name.
std::ostringstream nsstr; // sstr with fileline prefix and context
std::ostringstream wsstr; // sstr for waiver (no fileline) with context
if (lastLineno()) nsstr << this;
if (lastLineno()) nsstr << V3Error::warnContextBegin() << this << V3Error::warnContextEnd();
nsstr << sstr.str();
wsstr << sstr.str();
nsstr << "\n";
wsstr << "\n";
nsstr << '\n';
wsstr << '\n';
std::ostringstream extrass; // extra spaced out for prefix
if (!extra.empty()) {
extrass << std::setw(ascii().length()) << " "
<< ": " << extra;
extrass << V3Error::warnContextBegin() << std::setw(ascii().length()) << " "
<< ": " << V3Error::warnContextEnd() << extra;
}
if (warnIsOff(V3Error::s().errorCode())) {
V3Error::s().suppressThisWarning();
@ -440,26 +440,29 @@ void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra)
wsstr << add;
nsstr << add;
}
m_waive = V3Config::waive(this, V3Error::s().errorCode(), wsstr.str());
const string waiverText = V3Error::stripMetaText(wsstr.str(), false);
m_waive = V3Config::waive(this, V3Error::s().errorCode(), waiverText);
if (m_waive) {
V3Error::s().suppressThisWarning();
} else {
V3Waiver::addEntry(V3Error::s().errorCode(), filename(), wsstr.str());
V3Waiver::addEntry(V3Error::s().errorCode(), filename(), waiverText);
}
}
V3Error::v3errorEnd(nsstr, extrass.str());
V3Error::v3errorEnd(nsstr, extrass.str(), this);
}
string FileLine::warnMore() const VL_REQUIRES(V3Error::s().m_mutex) {
if (lastLineno()) {
return V3Error::warnMore() + string(ascii().size(), ' ') + ": ";
return V3Error::warnContextBegin() + V3Error::warnMore() + string(ascii().size(), ' ')
+ ": " + V3Error::warnContextEnd();
} else {
return V3Error::warnMore();
}
}
string FileLine::warnOther() const VL_REQUIRES(V3Error::s().m_mutex) {
if (lastLineno()) {
return V3Error::warnMore() + ascii() + ": ";
return V3Error::s().warnRelated(this) + V3Error::warnContextBegin() + V3Error::warnMore()
+ ascii() + ": " + V3Error::warnContextEnd();
} else {
return V3Error::warnMore();
}
@ -501,7 +504,7 @@ string FileLine::warnContext() const {
&& sourceLine.length() >= static_cast<size_t>(lastColumn() - 1)) {
string linestr = cvtToStr(firstLineno());
while (linestr.size() < 5) linestr = ' ' + linestr;
out += linestr + " | " + sourceLine + "\n";
out += linestr + " | " + sourceLine + '\n';
out += std::string(linestr.size(), ' ') + " | ";
out += string((firstColumn() - 1), ' ') + '^';
// Can't use UASSERT_OBJ used in warnings already inside the error end handler
@ -509,10 +512,11 @@ string FileLine::warnContext() const {
// Note lastColumn() can be <= firstColumn() in some weird preproc expansions
out += string((lastColumn() - firstColumn() - 1), '~');
}
out += "\n";
out += '\n';
}
return V3Error::warnContextBegin() + out + V3Error::warnContextEnd();
}
return out;
return "";
}
string FileLine::warnContextParent() const VL_REQUIRES(V3Error::s().m_mutex) {

View File

@ -112,7 +112,7 @@ void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error
if (FileLine* const flp = fileline()) {
flp->v3errorEnd(nsstr);
} else {
V3Error::v3errorEnd(nsstr);
V3Error::v3errorEnd(nsstr, "", nullptr);
}
}
void V3GraphVertex::v3errorEndFatal(std::ostringstream& str) const

View File

@ -80,7 +80,7 @@ void V3Number::v3errorEnd(const std::ostringstream& str) const VL_RELEASE(V3Erro
} else if (m_fileline) {
m_fileline->v3errorEnd(nsstr);
} else {
V3Error::v3errorEnd(nsstr);
V3Error::v3errorEnd(nsstr, "", nullptr);
}
}

View File

@ -1276,6 +1276,11 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-decoration", CbCall, [this, fl]() { decorations(fl, "medium"); });
DECL_OPTION("-decorations", CbVal, [this, fl](const char* optp) { decorations(fl, optp); });
DECL_OPTION("-no-decoration", CbCall, [this, fl]() { decorations(fl, "none"); });
DECL_OPTION("-diagnostics-sarif", OnOff, &m_diagnosticsSarif);
DECL_OPTION("-diagnostics-sarif-output", CbVal, [this](const char* optp) {
m_diagnosticsSarifOutput = optp;
m_diagnosticsSarif = true;
});
DECL_OPTION("-dpi-hdr-only", OnOff, &m_dpiHdrOnly);
DECL_OPTION("-dump-", CbPartialMatch, [this](const char* optp) { m_dumpLevel[optp] = 3; });
DECL_OPTION("-no-dump-", CbPartialMatch, [this](const char* optp) { m_dumpLevel[optp] = 0; });

View File

@ -252,6 +252,7 @@ private:
bool m_debugWidth = false; // main switch: --debug-width
bool m_decoration = true; // main switch: --decoration
bool m_decorationNodes = false; // main switch: --decoration=nodes
bool m_diagnosticsSarif = false; // main switch: --diagnostics-sarif
bool m_dpiHdrOnly = false; // main switch: --dpi-hdr-only
bool m_emitAccessors = false; // main switch: --emit-accessors
bool m_exe = false; // main switch: --exe
@ -354,6 +355,7 @@ private:
int m_compLimitParens = 240; // compiler selection; number of nested parens
string m_buildDepBin; // main switch: --build-dep-bin {filename}
string m_diagnosticsSarifOutput; // main switch: --diagnostics-sarif-output
string m_exeName; // main switch: -o {name}
string m_flags; // main switch: -f {name}
string m_hierParamsFile; // main switch: --hierarchical-params-file
@ -519,6 +521,7 @@ public:
bool debugWidth() const VL_PURE { return m_debugWidth; }
bool decoration() const VL_MT_SAFE { return m_decoration; }
bool decorationNodes() const VL_MT_SAFE { return m_decorationNodes; }
bool diagnosticsSarif() const VL_MT_SAFE { return m_diagnosticsSarif; }
bool dpiHdrOnly() const { return m_dpiHdrOnly; }
bool dumpDefines() const { return m_dumpLevel.count("defines") && m_dumpLevel.at("defines"); }
bool dumpTreeDot() const {
@ -632,6 +635,10 @@ public:
int compLimitMembers() const VL_MT_SAFE { return m_compLimitMembers; }
int compLimitParens() const { return m_compLimitParens; }
string diagnosticsSarifOutput() const VL_MT_SAFE {
return m_diagnosticsSarifOutput.empty() ? makeDir() + "/" + prefix() + ".sarif"
: m_diagnosticsSarifOutput;
}
string exeName() const { return m_exeName != "" ? m_exeName : prefix(); }
string hierParamFile() const { return m_hierParamsFile; }
string jsonOnlyOutput() const { return m_jsonOnlyOutput; }

View File

@ -230,6 +230,32 @@ string VString::removeWhitespace(const string& str) {
return result;
}
string VString::trimWhitespace(const string& str) {
string result;
result.reserve(str.size());
string add;
bool newline = false;
for (const char c : str) {
if (newline && std::isspace(c)) continue;
if (c == '\n') {
add = "\n";
newline = true;
continue;
}
if (std::isspace(c)) {
add += c;
continue;
}
if (!add.empty()) {
result += add;
newline = false;
add.clear();
}
result += c;
}
return result;
}
bool VString::isIdentifier(const string& str) {
for (const char c : str) {
if (!isIdentifierChar(c)) return false;

View File

@ -116,6 +116,8 @@ public:
static string spaceUnprintable(const string& str) VL_PURE;
// Remove any whitespace
static string removeWhitespace(const string& str);
// Trim leading/trailing whitespace on each line
static string trimWhitespace(const string& str);
// Return true if only identifer or ""
static bool isIdentifier(const string& str);
// Return true if char is valid character in C identifiers

View File

@ -42,6 +42,7 @@
#include "V3DepthBlock.h"
#include "V3Descope.h"
#include "V3DfgOptimizer.h"
#include "V3DiagSarif.h"
#include "V3EmitC.h"
#include "V3EmitCMain.h"
#include "V3EmitCMake.h"
@ -861,6 +862,8 @@ int main(int argc, char** argv) {
execBuildJob();
}
V3DiagSarif::output(true);
// Explicitly release resources
V3PreShell::shutdown();
v3Global.shutdown();

View File

@ -2294,11 +2294,14 @@ class VlTest:
line = re.sub(r'\r', '<#013>', line)
line = re.sub(r'Command Failed[^\n]+', 'Command Failed', line)
line = re.sub(r'Version: Verilator[^\n]+', 'Version: Verilator ###', line)
line = re.sub(r'"version": "[^"]+"', '"version": "###"', line)
line = re.sub(r'CPU Time: +[0-9.]+ seconds[^\n]+', 'CPU Time: ###', line)
line = re.sub(r'\?v=[0-9.]+', '?v=latest', line) # warning URL
line = re.sub(r'_h[0-9a-f]{8}_', '_h########_', line)
line = re.sub(r'%Error: /[^: ]+/([^/:])', r'%Error: .../\1',
line) # Avoid absolute paths
line = re.sub(r'("file://)/[^: ]+/([^/:])', r'\1/.../\2',
line) # Avoid absolute paths
line = re.sub(r' \/[^ ]+\/verilated_std.sv', ' verilated_std.sv', line)
#
(line, n) = re.subn(r'Exiting due to.*', r"Exiting due to", line)

View File

@ -22,6 +22,8 @@ def formats():
warns = {}
lnmatch = 0
for filename in test.glob_some(files):
if re.search(r'\.sarif\.out', filename):
continue
wholefile = test.file_contents(filename)
filename = os.path.basename(filename)
if re.search(r'(Exiting due to|%Error|%Warning)', wholefile):

View File

@ -0,0 +1,23 @@
%Warning-MODDUP: t/t_sarif.v:21:8: Duplicate declaration of module: 't'
21 | module t;
| ^
t/t_sarif.v:7:8: ... Location of original declaration
7 | module t(
| ^
... For warning description see https://verilator.org/warn/MODDUP?v=latest
... Use "/* verilator lint_off MODDUP */" and lint_on around source to disable this message.
%Warning-WIDTHTRUNC: t/t_sarif.v:12:23: Operator ASSIGNW expects 2 bits on the Assign RHS, but Assign RHS's CONST '5'h1f' generates 5 bits.
: ... note: In instance 't'
12 | wire [1:0] trunced = 5'b11111;
| ^
... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest
... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message.
%Warning-MULTIDRIVEN: t/t_sarif.v:10:18: Signal has multiple driving blocks with different clocking: 'multidriven'
t/t_sarif.v:15:6: ... Location of first driving block
15 | multidriven <= '1;
| ^~~~~~~~~~~
t/t_sarif.v:17:6: ... Location of other driving block
17 | multidriven <= '0;
| ^~~~~~~~~~~
... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest
... Use "/* verilator lint_off MULTIDRIVEN */" and lint_on around source to disable this message.

41
test_regress/t/t_sarif.py Executable file
View File

@ -0,0 +1,41 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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
import vltest_bootstrap
test.scenarios('vlt')
test.lint(verilator_flags2=['-Wno-fatal --diagnostics-sarif'],
expect_filename=test.golden_filename)
sarif_filename = test.obj_dir + "/" + test.vm_prefix + ".sarif"
# Make sure V3Error meta comments aren't in any outputs
test.file_grep_not(test.compile_log_filename, r'__WARN')
test.file_grep_not(sarif_filename, r'__WARN')
test.files_identical(sarif_filename, "t/" + test.name + ".sarif.out", "logfile")
# Check that sarif parses
nout = test.run_capture("sarif --version", check=False)
version_match = re.search(r'SARIF tools', nout, re.IGNORECASE)
if not version_match:
test.skip("sarif is not installed")
html_filename = test.obj_dir + "/validation.html"
test.run(cmd=['sarif', 'html', sarif_filename, '--output', html_filename])
# Validator:
# https://sarifweb.azurewebsites.net/Validation
# Rewrite
# test.run(cmd=['sarif copy t/t_sarif.out --output ' + test.obj_dir + '/t_sarif.out.rewrite'])
test.passes()

View File

@ -0,0 +1,188 @@
{
"$schema": "https://json.schemastore.org/sarif-2.1.0-rtm.5.json",
"version": "###",
"runs": [
{
"tool": {
"driver": {
"name": "Verilator",
"version": "###",
"informationUri": "https://verilator.org",
"rules": [
{
"id": "MODDUP",
"helpUri": "https://verilator.org/warn/MODDUP?v=latest"
},
{
"id": "MULTIDRIVEN",
"helpUri": "https://verilator.org/warn/MULTIDRIVEN?v=latest"
},
{
"id": "WIDTHTRUNC",
"helpUri": "https://verilator.org/warn/WIDTHTRUNC?v=latest"
}
]
}
},
"invocations": [
{
"commandLine": "--prefix Vt_sarif -cc -Mdir obj_vlt/t_sarif --fdedup --debug-check --comp-limit-members 10 --x-assign unique -Wno-fatal --diagnostics-sarif --clk clk -f input.vc +define+TEST_OBJ_DIR=obj_vlt/t_sarif +define+TEST_DUMPFILE=obj_vlt/t_sarif/simx.vcd t/t_sarif.v +librescan +notimingchecks +libext+.v -y t +incdir+t",
"executionSuccessful": true
}
],
"results": [
{
"level": "warning",
"message": {
"text": "Duplicate declaration of module: 't'\n... Location of original declaration\n... For warning description see https://verilator.org/warn/MODDUP?v=latest\n... Use \"/* verilator lint_off MODDUP */\" and lint_on around source to disable this message.",
"markdown": "```\n%Warning-MODDUP: t/t_sarif.v:21:8: Duplicate declaration of module: 't'\n 21 | module t; \n | ^\n t/t_sarif.v:7:8: ... Location of original declaration\n 7 | module t(\n | ^\n ... For warning description see https://verilator.org/warn/MODDUP?v=latest\n ... Use \"/* verilator lint_off MODDUP */\" and lint_on around source to disable this message.\n\n```\n"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "file:///.../t_sarif.v"
},
"region": {
"sourceLanguage": "systemverilog",
"startLine": 21,
"startColumn": 8,
"endColumn": 9,
"snippit": {
"text": "module t;",
"markdown": "```\n 21 | module t; \n | ^\n\n```\n"
}
}
}
}
],
"relatedLocations": [
{
"message": {
"text": "... Location of original declaration\n... For warning description see https://verilator.org/warn/MODDUP?v=latest\n... Use \"/* verilator lint_off MODDUP */\" and lint_on around source to disable this message.",
"markdown": "```\n t/t_sarif.v:7:8: ... Location of original declaration\n 7 | module t(\n | ^\n ... For warning description see https://verilator.org/warn/MODDUP?v=latest\n ... Use \"/* verilator lint_off MODDUP */\" and lint_on around source to disable this message.\n\n```\n"
},
"physicalLocation": {
"artifactLocation": {
"uri": "file:///.../t_sarif.v"
},
"region": {
"sourceLanguage": "systemverilog",
"startLine": 7,
"startColumn": 8,
"endColumn": 9,
"snippit": {
"text": "module t(",
"markdown": "```\n 7 | module t(\n | ^\n\n```\n"
}
}
}
}
],
"ruleId": "MODDUP",
"ruleIndex": 0
},
{
"level": "warning",
"message": {
"text": "Operator ASSIGNW expects 2 bits on the Assign RHS, but Assign RHS's CONST '5'h1f' generates 5 bits.\n... note: In instance 't'\n... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest\n... Use \"/* verilator lint_off WIDTHTRUNC */\" and lint_on around source to disable this message.",
"markdown": "```\n%Warning-WIDTHTRUNC: t/t_sarif.v:12:23: Operator ASSIGNW expects 2 bits on the Assign RHS, but Assign RHS's CONST '5'h1f' generates 5 bits.\n : ... note: In instance 't'\n 12 | wire [1:0] trunced = 5'b11111; \n | ^\n ... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest\n ... Use \"/* verilator lint_off WIDTHTRUNC */\" and lint_on around source to disable this message.\n\n```\n"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "file:///.../t_sarif.v"
},
"region": {
"sourceLanguage": "systemverilog",
"startLine": 12,
"startColumn": 23,
"endColumn": 24,
"snippit": {
"text": " wire [1:0] trunced = 5'b11111;",
"markdown": "```\n 12 | wire [1:0] trunced = 5'b11111; \n | ^\n\n```\n"
}
}
}
}
],
"ruleId": "WIDTHTRUNC",
"ruleIndex": 2
},
{
"level": "warning",
"message": {
"text": "Signal has multiple driving blocks with different clocking: 'multidriven'\n... Location of first driving block\n... Location of other driving block\n... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest\n... Use \"/* verilator lint_off MULTIDRIVEN */\" and lint_on around source to disable this message.",
"markdown": "```\n%Warning-MULTIDRIVEN: t/t_sarif.v:10:18: Signal has multiple driving blocks with different clocking: 'multidriven'\n t/t_sarif.v:15:6: ... Location of first driving block\n 15 | multidriven <= '1;\n | ^~~~~~~~~~~\n t/t_sarif.v:17:6: ... Location of other driving block\n 17 | multidriven <= '0;\n | ^~~~~~~~~~~\n ... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest\n ... Use \"/* verilator lint_off MULTIDRIVEN */\" and lint_on around source to disable this message.\n\n```\n"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "file:///.../t_sarif.v"
},
"region": {
"sourceLanguage": "systemverilog",
"startLine": 10,
"startColumn": 18,
"endColumn": 29,
"snippit": {
"text": " output logic multidriven);",
"markdown": "```\n 10 | output logic multidriven);\n | ^~~~~~~~~~~\n\n```\n"
}
}
}
}
],
"relatedLocations": [
{
"message": {
"text": "... Location of first driving block",
"markdown": "```\n t/t_sarif.v:15:6: ... Location of first driving block\n 15 | multidriven <= '1;\n | ^~~~~~~~~~~\n\n```\n"
},
"physicalLocation": {
"artifactLocation": {
"uri": "file:///.../t_sarif.v"
},
"region": {
"sourceLanguage": "systemverilog",
"startLine": 15,
"startColumn": 6,
"endColumn": 17,
"snippit": {
"text": " multidriven <= '1;",
"markdown": "```\n 15 | multidriven <= '1;\n | ^~~~~~~~~~~\n\n```\n"
}
}
}
},
{
"message": {
"text": "... Location of other driving block\n... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest\n... Use \"/* verilator lint_off MULTIDRIVEN */\" and lint_on around source to disable this message.",
"markdown": "```\n t/t_sarif.v:17:6: ... Location of other driving block\n 17 | multidriven <= '0;\n | ^~~~~~~~~~~\n ... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest\n ... Use \"/* verilator lint_off MULTIDRIVEN */\" and lint_on around source to disable this message.\n\n```\n"
},
"physicalLocation": {
"artifactLocation": {
"uri": "file:///.../t_sarif.v"
},
"region": {
"sourceLanguage": "systemverilog",
"startLine": 17,
"startColumn": 6,
"endColumn": 17,
"snippit": {
"text": " multidriven <= '0;",
"markdown": "```\n 17 | multidriven <= '0;\n | ^~~~~~~~~~~\n\n```\n"
}
}
}
}
],
"ruleId": "MULTIDRIVEN",
"ruleIndex": 1
}
]
}
]
}

22
test_regress/t/t_sarif.v Normal file
View File

@ -0,0 +1,22 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2009 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t(
input clk1,
input clk2,
output logic multidriven);
wire [1:0] trunced = 5'b11111; // Warned
always @ (posedge clk1)
multidriven <= '1;
always @ (posedge clk2)
multidriven <= '0;
endmodule
module t; // BAD duplicate
endmodule

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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
import vltest_bootstrap
test.scenarios('vlt')
test.top_filename = "t/t_sarif.v"
test.lint(
verilator_flags2=['-Wno-fatal', '--diagnostics-sarif-output', test.obj_dir + "/my.sarif"])
test.file_grep(test.obj_dir + "/my.sarif", "t_sarif.v")
test.passes()