Add `lint_off --contents` in configuration files. (#5606)

This commit is contained in:
Wilson Snyder 2024-11-12 20:21:16 -05:00
parent a5b2cb6ddf
commit 0bf413b260
13 changed files with 226 additions and 33 deletions

View File

@ -19,6 +19,7 @@ Verilator 5.031 devel
* Support `pure constraint`.
* Add `--no-std-package` as subset-alias of `--no-std`.
* Add `--waiver-multiline` for context-sensitive `--waiver-output`.
* Add `lint_off --contents` in configuration files. (#5606)
* Add error on illegal enum base type (#3010). [Iztok Jeras]
* Add error on `wait` with missing `.triggered` (#4457).
* Add error when improperly storing to parameter (#5147). [Gökçe Aydos]

View File

@ -1364,7 +1364,7 @@ Summary:
.. option:: --no-std
Prevents parsing standard input files, alias for
:vlopt:`--no-std-package`. This may be extended to prevent reading other
:opt:`--no-std-package`. This may be extended to prevent reading other
standardized files in future versions.
.. option:: --no-std-package
@ -1651,7 +1651,7 @@ Summary:
.. option:: --waiver-multiline
When using :vlopt:`--waiver-output`, include a match
When using :vlopt:`--waiver-output \<filename\>`, include a match
expression that includes the entire multiline error message as a match
regular expression, as opposed to the default of only matching the first
line of the error message. This provides a starting point for creating
@ -2088,7 +2088,7 @@ The grammar of configuration commands is as follows:
.. option:: lint_off [-rule <message>] [-file "<filename>" [-lines <line> [ - <line>]]]
.. option:: lint_off [-rule <message>] [-file "<filename>"] [-match "<wildcard>"]
.. option:: lint_off [-rule <message>] [-file "<filename>"] [-contents "<wildcard>"] [-match "<wildcard>"]
Enable/disables the specified lint warning, in the specified filename
(or wildcard with '\*' or '?', or all files if omitted) and range of
@ -2101,9 +2101,19 @@ The grammar of configuration commands is as follows:
:vlopt:`-Wno-lint`) are enabled/disabled. This will override all later
lint warning enables for the specified region.
If :code:`-contents` is provided, the input files must contain the given
wildcard (with '\*' or '?'), and are waived in case they match, provided
the :code:`-rule`, :code:`-file`, and :code:`-contents` also match. The
wildcard should be designed to match a single line; it is unspecified if
the wildcard is allowed to match across multiple lines. The input
contents does not include :vlopt:`--std` standard files, nor
configuration files (with :code:`verilator_config`). Typical use for
this is to match a version number present in the Verilog sources, so
that the waiver will only apply to that version of the sources.
If :code:`-match` is provided, the linter warnings are matched against
the given wildcard (with '\*' or '?'), and are waived in case they
match, provided the :code:`-rule` and :code:`-file`
match, provided the :code:`-rule`, :code:`-file`, and :code:`-contents`
also match. The wildcard is compared across the entire multi-line
message; see :vlopt:`--waiver-multiline`.

View File

@ -118,6 +118,70 @@ public:
using V3ConfigVarResolver = V3ConfigWildcardResolver<V3ConfigVar>;
//======================================================================
class WildcardContents final {
// Not mutex protected, current calling from V3Config::waive is protected by error's mutex
// MEMBERS
std::map<const std::string, bool> m_mapPatterns; // Pattern match results
std::deque<string> m_lines; // Source text lines
// METHODS
static WildcardContents& s() { // Singleton
static WildcardContents s_s;
return s_s;
}
void clearCacheImp() { m_mapPatterns.clear(); }
void pushTextImp(const string& text) {
// Similar code in VFileContent::pushText()
// Any leftover text is stored on largest line (might be "")
const string leftover = m_lines.back() + text;
m_lines.pop_back();
// Insert line-by-line
string::size_type line_start = 0;
while (true) {
const string::size_type line_end = leftover.find('\n', line_start);
if (line_end != string::npos) {
const string oneline(leftover, line_start, line_end - line_start + 1);
if (oneline.size() > 1) m_lines.push_back(oneline); // Keeps newline
UINFO(9, "Push[+" << (m_lines.size() - 1) << "]: " << oneline);
line_start = line_end + 1;
} else {
break;
}
}
// Keep leftover for next time
m_lines.emplace_back(string(leftover, line_start)); // Might be ""
clearCacheImp();
}
bool resolveUncachedImp(const string& name) {
for (const string& i : m_lines) {
if (VString::wildmatch(i, name)) return true;
}
return false;
}
bool resolveCachedImp(const string& name) {
// Lookup if it was resolved before, typically is
const auto pair = m_mapPatterns.emplace(name, false);
bool& entryr = pair.first->second;
// Resolve entry when first requested, cache the result
if (pair.second) entryr = resolveUncachedImp(name);
return entryr;
}
public:
WildcardContents() {
m_lines.emplace_back(""); // start with no leftover
}
~WildcardContents() = default;
// Return true iff name in parsed contents
static bool resolve(const string& name) { return s().resolveCachedImp(name); }
// Add arbitrary text (need not be line-by-line)
static void pushText(const string& text) { s().pushTextImp(text); }
};
//######################################################################
// Function or task: Have variables and properties
@ -256,11 +320,28 @@ std::ostream& operator<<(std::ostream& os, const V3ConfigIgnoresLine& rhs) {
// and multiple attributes can be attached to a line
using V3ConfigLineAttribute = std::bitset<VPragmaType::ENUM_SIZE>;
class WaiverSetting final {
public:
V3ErrorCode m_code; // Error code
string m_contents; // --contents regexp
string m_match; // --match regexp
WaiverSetting(V3ErrorCode code, const string& contents, const string& match)
: m_code{code}
, m_contents{contents}
, m_match{match} {}
~WaiverSetting() = default;
WaiverSetting& operator=(const WaiverSetting& rhs) {
m_code = rhs.m_code;
m_contents = rhs.m_contents;
m_match = rhs.m_match;
return *this;
}
};
// File entity
class V3ConfigFile final {
using LineAttrMap = std::map<int, V3ConfigLineAttribute>; // Map line->bitset of attributes
using IgnLines = std::multiset<V3ConfigIgnoresLine>; // list of {line,code,on}
using WaiverSetting = std::pair<V3ErrorCode, std::string>; // Waive code if string matches
using Waivers = std::vector<WaiverSetting>; // List of {code,wildcard string}
LineAttrMap m_lineAttrs; // Attributes to line mapping
@ -299,12 +380,12 @@ public:
m_ignLines.insert(V3ConfigIgnoresLine{code, lineno, on});
m_lastIgnore.it = m_ignLines.begin();
}
void addIgnoreMatch(V3ErrorCode code, const string& match) {
void addIgnoreMatch(V3ErrorCode code, const string& contents, const string& match) {
// Since Verilator 5.031 the error message compared has context, so
// allow old rules to still match using a final '*'
string newMatch = match;
if (newMatch.empty() || newMatch.back() != '*') newMatch += '*';
m_waivers.emplace_back(code, newMatch);
m_waivers.emplace_back(WaiverSetting{code, contents, newMatch});
}
void applyBlock(AstNodeBlock* nodep) {
@ -342,8 +423,9 @@ public:
bool waive(V3ErrorCode code, const string& match) {
if (code.hardError()) return false;
for (const auto& itr : m_waivers) {
if ((code.isUnder(itr.first) || (itr.first == V3ErrorCode::I_LINT))
&& VString::wildmatch(match, itr.second)) {
if ((code.isUnder(itr.m_code) || (itr.m_code == V3ErrorCode::I_LINT))
&& VString::wildmatch(match, itr.m_match)
&& WildcardContents::resolve(itr.m_contents)) {
return true;
}
}
@ -516,8 +598,9 @@ void V3Config::addIgnore(V3ErrorCode code, bool on, const string& filename, int
}
}
void V3Config::addIgnoreMatch(V3ErrorCode code, const string& filename, const string& match) {
V3ConfigResolver::s().files().at(filename).addIgnoreMatch(code, match);
void V3Config::addIgnoreMatch(V3ErrorCode code, const string& filename, const string& contents,
const string& match) {
V3ConfigResolver::s().files().at(filename).addIgnoreMatch(code, contents, match);
}
void V3Config::addInline(FileLine* fl, const string& module, const string& ftask, bool on) {
@ -651,6 +734,8 @@ bool V3Config::getScopeTraceOn(const string& scope) {
return V3ConfigResolver::s().scopeTraces().getScopeTraceOn(scope);
}
void V3Config::contentsPushText(const string& text) { return WildcardContents::pushText(text); }
bool V3Config::waive(FileLine* filelinep, V3ErrorCode code, const string& message) {
V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filelinep->filename());
if (!filep) return false;

View File

@ -34,7 +34,8 @@ public:
static void addCoverageBlockOff(const string& file, int lineno);
static void addCoverageBlockOff(const string& module, const string& blockname);
static void addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max);
static void addIgnoreMatch(V3ErrorCode code, const string& filename, const string& match);
static void addIgnoreMatch(V3ErrorCode code, const string& filename, const string& contents,
const string& match);
static void addInline(FileLine* fl, const string& module, const string& ftask, bool on);
static void addModulePragma(const string& module, VPragmaType pragma);
static void addProfileData(FileLine* fl, const string& model, const string& key,
@ -53,6 +54,9 @@ public:
static uint64_t getProfileData(const string& model, const string& key);
static FileLine* getProfileDataFileLine();
static bool getScopeTraceOn(const string& scope);
static void contentsPushText(const string& text);
static bool waive(FileLine* filelinep, V3ErrorCode code, const string& message);
};

View File

@ -147,6 +147,7 @@ FileLineSingleton::msgEnSetIdx_t FileLineSingleton::msgEnAnd(msgEnSetIdx_t lhsId
// VFileContents class functions
void VFileContent::pushText(const string& text) {
// Similar code in WildcardContents::pushText()
if (m_lines.size() == 0) {
m_lines.emplace_back(""); // no such thing as line [0]
m_lines.emplace_back(""); // start with no leftover

View File

@ -824,6 +824,21 @@ void V3PreProcImp::openFile(FileLine*, VInFilter* filterp, const string& filenam
flsp->newContent();
for (const string& i : wholefile) flsp->contentp()->pushText(i);
// Save contents for V3Config --contents
if (filename != V3Options::getStdPackagePath()) {
bool containsVlt = false;
for (const string& i : wholefile) {
// TODO this is overly sensitive, might be in a comment
if (i.find("`verilator_config") != string::npos) {
containsVlt = true;
break;
}
}
if (!containsVlt) {
for (const string& i : wholefile) V3Config::contentsPushText(i);
}
}
// Create new stream structure
m_lexp->scanNewFile(flsp);
addLineComment(1); // Enter

View File

@ -137,6 +137,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"tracing_on" { FL; return yVLT_TRACING_ON; }
-?"-block" { FL; return yVLT_D_BLOCK; }
-?"-contents" { FL; return yVLT_D_CONTENTS; }
-?"-cost" { FL; return yVLT_D_COST; }
-?"-file" { FL; return yVLT_D_FILE; }
-?"-function" { FL; return yVLT_D_FUNCTION; }

View File

@ -495,6 +495,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
%token<fl> yVLT_TRACING_ON "tracing_on"
%token<fl> yVLT_D_BLOCK "--block"
%token<fl> yVLT_D_CONTENTS "--contents"
%token<fl> yVLT_D_COST "--cost"
%token<fl> yVLT_D_FILE "--file"
%token<fl> yVLT_D_FUNCTION "--function"
@ -7572,7 +7573,19 @@ vltItem:
{ if (($1 == V3ErrorCode::I_COVERAGE) || ($1 == V3ErrorCode::I_TRACING)) {
$<fl>1->v3error("Argument -match only supported for lint_off");
} else {
V3Config::addIgnoreMatch($1, *$2, *$3);
V3Config::addIgnoreMatch($1, *$2, "", *$3);
}}
| vltOffFront vltDFile vltDContents
{ if (($1 == V3ErrorCode::I_COVERAGE) || ($1 == V3ErrorCode::I_TRACING)) {
$<fl>1->v3error("Argument -match only supported for lint_off");
} else {
V3Config::addIgnoreMatch($1, *$2, *$3, "*");
}}
| vltOffFront vltDFile vltDContents vltDMatch
{ if (($1 == V3ErrorCode::I_COVERAGE) || ($1 == V3ErrorCode::I_TRACING)) {
$<fl>1->v3error("Argument -match only supported for lint_off");
} else {
V3Config::addIgnoreMatch($1, *$2, *$3, *$4);
}}
| vltOffFront vltDScope
{ if ($1 != V3ErrorCode::I_TRACING) {
@ -7660,6 +7673,10 @@ vltDBlock<strp>: // --block <arg>
yVLT_D_BLOCK str { $$ = $2; }
;
vltDContents<strp>:
yVLT_D_CONTENTS str { $$ = $2; }
;
vltDCost<nump>: // --cost <arg>
yVLT_D_COST yaINTNUM { $$ = $2; }
;

View File

@ -4,28 +4,38 @@
// any use, without warranty, 2024 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`ifdef _T_UVM_TODO_VLT_ `else
`define _T_UVM_TODO_VLT_
`verilator_config
// Apply these rules to only UVM base files
`define VLT_UVM_FILES -file "*/uvm_*.svh" -contents "*UVM_VERSION_STRING*"
// Whole-file waivers
lint_off -rule WIDTHEXPAND -file "*/uvm_*.svh"
lint_off -rule WIDTHTRUNC -file "*/uvm_*.svh"
lint_off -rule WIDTHEXPAND `VLT_UVM_FILES
lint_off -rule WIDTHTRUNC `VLT_UVM_FILES
// Context-sensitive waivers
lint_off -rule CASEINCOMPLETE -file "*/uvm_*.svh" -match "* case ({is_R, is_W})*"
lint_off -rule CASEINCOMPLETE -file "*/uvm_*.svh" -match "* case(orig_severity)*"
lint_off -rule CASTCONST -file "*/uvm_*.svh" -match "*class{}uvm_callback*"
lint_off -rule CASTCONST -file "*/uvm_*.svh" -match "*class{}uvm_component*"
lint_off -rule CASTCONST -file "*/uvm_*.svh" -match "*class{}uvm_event*"
lint_off -rule CASTCONST -file "*/uvm_*.svh" -match "*class{}uvm_report_object*"
lint_off -rule CASTCONST -file "*/uvm_*.svh" -match "*class{}uvm_sequence_item*"
lint_off -rule MISINDENT -file "*/uvm_*.svh" -match "* foreach (abstractions[i])*"
lint_off -rule MISINDENT -file "*/uvm_*.svh" -match "* foreach (lock_list[i])*"
lint_off -rule MISINDENT -file "*/uvm_*.svh" -match "* rw_access.data=*"
lint_off -rule MISINDENT -file "*/uvm_*.svh" -match "* uvm_cmdline_proc =*"
lint_off -rule REALCVT -file "*/uvm_*.svh" -match "* m_time *"
lint_off -rule REALCVT -file "*/uvm_*.svh" -match "*$realtime*"
lint_off -rule SYMRSVDWORD -file "*/uvm_*.svh" -match "*'delete'*"
lint_off -rule SYMRSVDWORD -file "*/uvm_*.svh" -match "*'list'*"
lint_off -rule SYMRSVDWORD -file "*/uvm_*.svh" -match "*'map'*"
lint_off -rule SYMRSVDWORD -file "*/uvm_*.svh" -match "*'override'*"
lint_off -rule SYMRSVDWORD -file "*/uvm_*.svh" -match "*'volatile'*"
lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case ({is_R, is_W})*"
lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case(orig_severity)*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_callback*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_component*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_event*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_report_object*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_sequence_item*"
lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (abstractions[i])*"
lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (lock_list[i])*"
lint_off -rule MISINDENT `VLT_UVM_FILES -match "* rw_access.data=*"
lint_off -rule MISINDENT `VLT_UVM_FILES -match "* uvm_cmdline_proc =*"
lint_off -rule REALCVT `VLT_UVM_FILES -match "* m_time *"
lint_off -rule REALCVT `VLT_UVM_FILES -match "*$realtime*"
lint_off -rule SYMRSVDWORD `VLT_UVM_FILES -match "*'delete'*"
lint_off -rule SYMRSVDWORD `VLT_UVM_FILES -match "*'list'*"
lint_off -rule SYMRSVDWORD `VLT_UVM_FILES -match "*'map'*"
lint_off -rule SYMRSVDWORD `VLT_UVM_FILES -match "*'override'*"
lint_off -rule SYMRSVDWORD `VLT_UVM_FILES -match "*'volatile'*"
`undef VLT_UVM_FILES
`endif // Guard

View File

@ -0,0 +1,7 @@
%Warning-UNUSEDSIGNAL: t/t_vlt_match_contents.v:11:10: Signal is not driven, nor used: 'usignal_contents_mismatch'
: ... note: In instance 't'
11 | logic usignal_contents_mismatch;
| ^~~~~~~~~~~~~~~~~~~~~~~~~
... For warning description see https://verilator.org/warn/UNUSEDSIGNAL?v=latest
... Use "/* verilator lint_off UNUSEDSIGNAL */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -0,0 +1,18 @@
#!/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=["--lint-only -Wall t/t_vlt_match_contents.vlt"],
fails=True,
expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,12 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Ethan Sifferman.
// SPDX-License-Identifier: CC0-1.0
string MATCH_VERSION = "10.20";
module t;
logic usignal_contents_suppress; // Suppressed with -contents
logic usignal_contents_mismatch; // Doesn't match -contents
endmodule

View File

@ -0,0 +1,12 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Ethan Sifferman.
// SPDX-License-Identifier: CC0-1.0
`verilator_config
lint_off -rule DECLFILENAME -file "*/t_vlt_match_contents.v" -contents "* MATCH_VERSION*"
lint_off -rule UNUSEDSIGNAL -file "*/t_vlt_match_contents.v" -contents "* MATCH_VERSION*" -match "*MATCH_VERSION*"
lint_off -rule UNUSEDSIGNAL -file "*/t_vlt_match_contents.v" -contents "* MATCH_VERSION*" -match "*usignal_contents_suppress*"
lint_off -rule UNUSEDSIGNAL -file "*/t_vlt_match_contents.v" -contents "* NOT_VERSION*" -match "*usignal_contents_mismatch*"