Support SARIF JSON diagnostic output with `--diagnostics-sarif`. (#6017)
This commit is contained in:
parent
7a5c223ccf
commit
66667b6172
1
Changes
1
Changes
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
================
|
||||
|
|
|
@ -798,6 +798,7 @@ libtcmalloc
|
|||
libverilated
|
||||
linkers
|
||||
linter
|
||||
linters
|
||||
linux
|
||||
liu
|
||||
livelock
|
||||
|
@ -975,6 +976,7 @@ runtimes
|
|||
rw
|
||||
sVerilator
|
||||
saif
|
||||
sarif
|
||||
sawatzke
|
||||
sc
|
||||
scalared
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -247,6 +247,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3DfgPasses.o \
|
||||
V3DfgPeephole.o \
|
||||
V3DfgRegularize.o \
|
||||
V3DiagSarif.o \
|
||||
V3DupFinder.o \
|
||||
V3EmitCMain.o \
|
||||
V3EmitCMake.o \
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
156
src/V3Error.cpp
156
src/V3Error.cpp
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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; });
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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.
|
|
@ -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()
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
|
@ -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()
|
Loading…
Reference in New Issue