Fix warning lint directive ordering and consistency (#4185) (#5368) (#5610) (#6876).

This commit is contained in:
Wilson Snyder 2025-12-30 20:31:34 -05:00 committed by GitHub
parent e6114b6bbb
commit 4080284e53
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 515 additions and 108 deletions

View File

@ -59,6 +59,7 @@ Verilator 5.043 devel
* Optimize inlining small C functions and add `-inline-cfuncs` (#6815). [Jose Drowne]
* Fix generate function(s) inside of generate blocks (#1011) (#6789). [em2machine]
* Fix typedef derived from type defined inside interface (#3441) (#6776). [em2machine]
* Fix warning lint directive ordering and consistency (#4185) (#5368) (#5610) (#6876).
* Fix extern function that returns parameterized class (#4924).
* Fix type deduction for variable parameterized classes (#6281) (#6813). [em2machine]
* Fix randomize called within func/task (#6144) (#6753). [Yilou Wang]
@ -128,6 +129,7 @@ Verilator 5.043 devel
* Fix `randc` on extended class (#6852).
* Fix typedef `::` class reference error (#6862).
* Fix missing include on FreeBSD (#6864).
* Fix `--Wwarn-lint` to be inverse of `--Wno-lint`.
Verilator 5.042 2025-11-02

View File

@ -2410,14 +2410,8 @@ The grammar of control commands is as follows:
(or wildcard with '\*' or '?', or all files if omitted) and range of
line numbers (or all lines if omitted).
If a warning is disabled with lint_off, it will not be printed, even if the
source contains a lint_on metacomment. The control file directives and
metacomments are interpreted separately and do not interact. A warning is
emitted only if not disabled either in a control file or via metacomments.
If the ``-rule`` is omitted, all lint warnings (see list in
:vlopt:`-Wno-lint`) are enabled/disabled. This will override all later
lint warning enables for the specified region.
:vlopt:`-Wno-lint`) are enabled/disabled.
If ``-contents`` is provided, the input files must contain the given
wildcard (with '\*' or '?'), and are waived in case they match, provided
@ -2435,6 +2429,21 @@ The grammar of control commands is as follows:
wildcard is compared across the entire multi-line message; see
:vlopt:`--waiver-multiline`.
When there are overlapping conflicting lint_on/lint_off directives, they
are resolved in the following priority order:
* All lint_on/lint_off without a ``-file``, or with a ``-file "\*"``,
are processed in order of parsing.
* All lint_on/lint_off with ``-file "non-\*"`` are processed in order of
parsing.
* All lint_off with ``--match`` in order of parsing.
If a warning is disabled with lint_off, it will not be printed, even if
the source contains a lint_on metacomment. The control file directives
and metacomments are interpreted separately and do not interact. A
warning is emitted only if not disabled either in a control file or via
metacomments.
Before version 4.026, ``-rule`` was named ``-msg``, and
``-msg`` remained a deprecated alias until Version 5.000.

View File

@ -46,7 +46,7 @@ public:
V3ControlWildcardResolver() = default;
~V3ControlWildcardResolver() = default;
/// Update into maps from other
// Update this resolved file's item map by inserting other's (wildcarded filename's) items
void update(const V3ControlWildcardResolver& other) VL_MT_SAFE_EXCLUDES(m_mutex)
VL_EXCLUDES(other.m_mutex) {
V3LockGuard lock{m_mutex};
@ -72,13 +72,15 @@ public:
std::unique_ptr<T>& entryr = pair.first->second;
// Resolve entry when first requested, cache the result
if (pair.second) {
// Update the entity with all matches in the patterns
// Inserted: update the entity with all matches in the patterns
for (const auto& patEnt : m_mapPatterns) {
if (VString::wildmatch(name, patEnt.first)) {
if (!entryr) entryr.reset(new T{});
entryr->update(patEnt.second);
}
}
// Perform final actions that needed all updates completed
if (entryr) entryr->updateFinalize();
}
return entryr.get();
}
@ -95,6 +97,7 @@ public:
m_attrs.reserve(m_attrs.size() + other.m_attrs.size());
m_attrs.insert(m_attrs.end(), other.m_attrs.begin(), other.m_attrs.end());
}
void updateFinalize() {}
// Apply all attributes to the variable
void apply(AstVar* varp) const {
for (const VAttrType attr : m_attrs) {
@ -194,6 +197,7 @@ public:
m_ports.update(f.m_ports);
m_vars.update(f.m_vars);
}
void updateFinalize() {}
V3ControlVarResolver& params() { return m_params; }
V3ControlVarResolver& ports() { return m_ports; }
@ -245,6 +249,7 @@ public:
m_modPragmas.insert(*it);
}
}
void updateFinalize() {}
V3ControlFTaskResolver& ftasks() { return m_tasks; }
V3ControlVarResolver& params() { return m_params; }
@ -308,28 +313,220 @@ using V3ControlModuleResolver = V3ControlWildcardResolver<V3ControlModule>;
// lint/coverage/tracing on/off
class V3ControlIgnoresLine final {
public:
const int m_lineno; // Line number to make change at
const int m_lineMin; // Minimum line number to make change at
const int m_lineMax; // Maximum line number to make change at (inclusive)
const V3ErrorCode m_code; // Error code
const bool m_on; // True to enable message
V3ControlIgnoresLine(V3ErrorCode code, int lineno, bool on)
: m_lineno{lineno}
V3ControlIgnoresLine(V3ErrorCode code, int linemin, int linemax, bool on)
: m_lineMin{linemin}
, m_lineMax{linemax}
, m_code{code}
, m_on{on} {}
~V3ControlIgnoresLine() = default;
bool operator<(const V3ControlIgnoresLine& rh) const {
if (m_lineno < rh.m_lineno) return true;
if (m_lineno > rh.m_lineno) return false;
if (m_code < rh.m_code) return true;
if (m_code > rh.m_code) return false;
// Always turn "on" before "off" so that overlapping lines will end
// up finally with the error "off"
return (m_on > rh.m_on);
bool everyLine() const { return m_lineMin == 0 && m_lineMax == 0; }
bool inRange(int lineno) const {
return (lineno >= m_lineMin && lineno <= m_lineMax) || everyLine();
}
};
std::ostream& operator<<(std::ostream& os, const V3ControlIgnoresLine& rhs) {
return os << rhs.m_lineno << ", " << rhs.m_code << ", " << rhs.m_on;
return os << rhs.m_lineMin << "-" << rhs.m_lineMax << ", " << rhs.m_code << ", " << rhs.m_on;
}
// Ignore line settings, index is parse order
// Single global deque to avoid copying when update()
static std::vector<V3ControlIgnoresLine> controlIgnLines;
using IgnIndices = std::vector<uint32_t>; // List of {s_ignLines indces}
class VIntervalTree final {
struct Entry final {
// Classic centered interval tree, referencing an index to controlIgnLines
int m_center; // Line number of central point
std::unique_ptr<Entry> m_leftp; // Entry with all its rules/entries < this center
std::unique_ptr<Entry> m_rightp; // Entry with all its rules/entries > this center
IgnIndices m_byMin; // Rules overlapping center sorted by min line number
IgnIndices m_byMax; // Rules overlapping center sorted by max line number
explicit Entry(int center)
: m_center{center} {}
};
std::unique_ptr<Entry> m_rootp; // Root of interval tree
IgnIndices m_everyLines; // All-line disables
private:
std::unique_ptr<Entry> buildTree(const IgnIndices& points) {
if (points.empty()) return nullptr;
int minval = std::numeric_limits<int>::max();
int maxval = std::numeric_limits<int>::min();
for (const auto& it : points) {
const V3ControlIgnoresLine& cign = controlIgnLines[it];
if (cign.everyLine()) {
m_everyLines.emplace_back(it);
continue;
}
minval = std::min(minval, cign.m_lineMin);
maxval = std::max(maxval, cign.m_lineMax);
}
int center = minval + (maxval - minval) / 2;
auto entp = std::unique_ptr<Entry>(new Entry{center});
IgnIndices leftPoints, rightPoints;
for (const auto& it : points) {
const V3ControlIgnoresLine& cign = controlIgnLines[it];
if (cign.everyLine()) continue;
// UINFO(9, "Inserting for center " << center << " point " << it);
if (cign.m_lineMax < center)
leftPoints.emplace_back(it);
else if (cign.m_lineMin > center)
rightPoints.emplace_back(it);
else {
entp->m_byMin.push_back(it);
entp->m_byMax.push_back(it);
}
}
std::sort(entp->m_byMin.begin(), entp->m_byMin.end(), [](uint32_t a, uint32_t b) {
return controlIgnLines[a].m_lineMin < controlIgnLines[b].m_lineMin;
});
std::sort(entp->m_byMax.begin(), entp->m_byMax.end(), [](uint32_t a, uint32_t b) {
return controlIgnLines[a].m_lineMax > controlIgnLines[b].m_lineMax;
});
entp->m_leftp = buildTree(leftPoints);
entp->m_rightp = buildTree(rightPoints);
return entp;
}
void findTree(int lineno, Entry* entp, IgnIndices& resultsr, int& nextChanger) const {
// UINFO(9, "Find " << lineno << " center " << entp->m_center);
if (lineno < entp->m_center) {
nextChanger = std::min(nextChanger, entp->m_center);
for (const auto& it : entp->m_byMin) {
const V3ControlIgnoresLine& cign = controlIgnLines[it];
if (cign.m_lineMin <= lineno) {
resultsr.emplace_back(it);
} else {
nextChanger = std::min(nextChanger, cign.m_lineMin);
break;
}
}
if (entp->m_leftp) findTree(lineno, entp->m_leftp.get(), resultsr, nextChanger);
} else if (lineno > entp->m_center) {
for (const auto& it : entp->m_byMax) {
const V3ControlIgnoresLine& cign = controlIgnLines[it];
if (cign.m_lineMax >= lineno) {
nextChanger = std::min(nextChanger, cign.m_lineMax + 1);
resultsr.emplace_back(it);
} else {
break;
}
}
if (entp->m_rightp) findTree(lineno, entp->m_rightp.get(), resultsr, nextChanger);
} else {
for (const auto& it : entp->m_byMin) {
const V3ControlIgnoresLine& cign = controlIgnLines[it];
resultsr.emplace_back(it);
if (cign.m_lineMax >= lineno)
nextChanger = std::min(nextChanger, cign.m_lineMax + 1);
}
if (entp->m_leftp) findTree(lineno, entp->m_leftp.get(), resultsr, nextChanger);
if (entp->m_rightp) findTree(lineno, entp->m_rightp.get(), resultsr, nextChanger);
}
}
public:
// CONSTRUCTORS
VIntervalTree() {}
~VIntervalTree() = default;
// METHODS
void clear() { m_rootp = nullptr; }
void build(const IgnIndices& points) {
clear();
m_rootp = buildTree(points);
}
void find(int lineno, IgnIndices& resultsr, int& nextChanger) const {
resultsr = m_everyLines;
nextChanger = std::numeric_limits<int>::max();
if (m_rootp) findTree(lineno, m_rootp.get(), resultsr, nextChanger);
// Sort indices, so can process in parse order
std::sort(resultsr.begin(), resultsr.end());
}
static void selfTest() {
// 0 10 20 30 40 50
// i0: . 10-10 . . . .
// i1: . . 20-20 . . .
// i2: . . . . 40-40 .
// i3: . 10------------30 . .
// i4: . . 20------------40 .
// i5:(below) 0---------------------------------------0
IgnIndices data;
std::vector<std::pair<int, int>> points
= {{10, 10}, {20, 20}, {40, 40}, {10, 30}, {20, 40}};
for (auto& it : points) {
controlIgnLines.emplace_back(
V3ControlIgnoresLine{V3ErrorCode::I_LINT, it.first, it.second, true});
data.emplace_back(static_cast<uint32_t>(controlIgnLines.size() - 1));
}
VIntervalTree tree;
tree.build(data);
IgnIndices results;
int nextChange = 0;
tree.find(0, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 0);
UASSERT_SELFTEST(int, nextChange, 10);
tree.find(10, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 2);
UASSERT_SELFTEST(int, results[0], 0);
UASSERT_SELFTEST(int, results[1], 3);
UASSERT_SELFTEST(int, nextChange, 11);
tree.find(11, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 1);
UASSERT_SELFTEST(int, results[0], 3);
UASSERT_SELFTEST(int, nextChange, 15); // Center, or would be 20
tree.find(20, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 3);
UASSERT_SELFTEST(int, results[0], 1);
UASSERT_SELFTEST(int, results[1], 3);
UASSERT_SELFTEST(int, results[2], 4);
UASSERT_SELFTEST(int, nextChange, 21);
tree.find(21, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 2);
UASSERT_SELFTEST(int, results[0], 3);
UASSERT_SELFTEST(int, results[1], 4);
UASSERT_SELFTEST(int, nextChange, 25); // Center, or would be 30
tree.find(30, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 2);
UASSERT_SELFTEST(int, results[0], 3);
UASSERT_SELFTEST(int, results[1], 4);
UASSERT_SELFTEST(int, nextChange, 31);
tree.find(40, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 2);
UASSERT_SELFTEST(int, results[0], 2);
UASSERT_SELFTEST(int, results[1], 4);
UASSERT_SELFTEST(int, nextChange, 41);
tree.find(41, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 0);
UASSERT_SELFTEST(int, nextChange, std::numeric_limits<int>::max());
//
points = {{0, 0}};
for (auto& it : points) {
controlIgnLines.emplace_back(
V3ControlIgnoresLine{V3ErrorCode::I_LINT, it.first, it.second, true});
data.emplace_back(static_cast<uint32_t>(controlIgnLines.size() - 1));
}
tree.build(data);
//
tree.find(50, results, nextChange);
UASSERT_SELFTEST(size_t, results.size(), 1);
UASSERT_SELFTEST(int, results[0], 5);
}
};
// Some attributes are attached to entities of the occur on a fileline
// and multiple attributes can be attached to a line
using V3ControlLineAttribute = std::bitset<VPragmaType::_ENUM_SIZE>;
@ -355,16 +552,17 @@ public:
// File entity
class V3ControlFile final {
using LineAttrMap = std::map<int, V3ControlLineAttribute>; // Map line->bitset of attributes
using IgnLines = std::multiset<V3ControlIgnoresLine>; // list of {line,code,on}
using Waivers = std::vector<WaiverSetting>; // List of {code,wildcard string}
LineAttrMap m_lineAttrs; // Attributes to line mapping
IgnLines m_ignLines; // Ignore line settings
IgnIndices m_ignIndices; // s_ignLines that apply to this specific file
Waivers m_waivers; // Waive messages
VIntervalTree m_intervalTree; // Tree of indices to IgnLines
struct {
int lineno; // Last line number
IgnLines::const_iterator it; // Point with next linenumber > current line number
int filenameno = -1; // Last filename
int lineno = -1; // Last linenumber
int nextChange = -1; // Line number of next change
} m_lastIgnore; // Last ignore line run
// Match a given line and attribute to the map, line 0 is any
@ -375,24 +573,24 @@ class V3ControlFile final {
}
public:
V3ControlFile() {
m_lastIgnore.lineno = -1;
m_lastIgnore.it = m_ignLines.begin();
}
V3ControlFile() {}
void update(const V3ControlFile& file) {
// Copy in all Attributes
// Copy in all attributes and waivers
for (const auto& itr : file.m_lineAttrs) m_lineAttrs[itr.first] |= itr.second;
// Copy in all ignores
for (const auto& ignLine : file.m_ignLines) m_ignLines.insert(ignLine);
// Update the iterator after the list has changed
m_lastIgnore.it = m_ignLines.begin();
m_waivers.reserve(m_waivers.size() + file.m_waivers.size());
m_waivers.insert(m_waivers.end(), file.m_waivers.begin(), file.m_waivers.end());
// Copy in all ignore references
m_ignIndices.reserve(m_ignIndices.size() + file.m_ignIndices.size());
m_ignIndices.insert(m_ignIndices.end(), file.m_ignIndices.begin(),
file.m_ignIndices.end());
// updateFinalize() will soon build tree
}
void updateFinalize() { m_intervalTree.build(m_ignIndices); }
void addLineAttribute(int lineno, VPragmaType attr) { m_lineAttrs[lineno].set(attr); }
void addIgnore(V3ErrorCode code, int lineno, bool on) {
m_ignLines.insert(V3ControlIgnoresLine{code, lineno, on});
m_lastIgnore.it = m_ignLines.begin();
void addIgnore(V3ErrorCode code, int min, int max, bool on) {
controlIgnLines.emplace_back(V3ControlIgnoresLine{code, min, max, on});
m_ignIndices.emplace_back(static_cast<uint32_t>(controlIgnLines.size() - 1));
m_lastIgnore.nextChange = -1;
}
void addIgnoreMatch(V3ErrorCode code, const string& contents, const string& match) {
// Since Verilator 5.031 the error message compared has context, so
@ -425,22 +623,41 @@ public:
}
void applyIgnores(FileLine* filelinep) {
// HOT routine, called each parsed token line of this filename
if (m_lastIgnore.lineno != filelinep->lineno()) {
// UINFO(9, " ApplyIgnores for " << filelinep->ascii());
// Process all on/offs for lines up to and including the current line
const int curlineno = filelinep->lastLineno();
for (; m_lastIgnore.it != m_ignLines.end(); ++m_lastIgnore.it) {
if (m_lastIgnore.it->m_lineno > curlineno) break;
// UINFO(9, " Hit " << *m_lastIgnore.it);
filelinep->warnOnCtrl(m_lastIgnore.it->m_code, m_lastIgnore.it->m_on);
}
if (false && debug() >= 9) {
for (IgnLines::const_iterator it = m_lastIgnore.it; it != m_ignLines.end(); ++it) {
UINFO(9, " NXT " << *it);
}
}
m_lastIgnore.lineno = filelinep->lastLineno();
if (filelinep->filenameno() == m_lastIgnore.filenameno
&& filelinep->lineno() == m_lastIgnore.lineno)
return; // Short circuit, no change, no debug
// For speed, compute line number of next potential change, and skip if before then
if (filelinep->filenameno() == m_lastIgnore.filenameno
&& filelinep->lineno() < m_lastIgnore.nextChange) {
m_lastIgnore.lineno = filelinep->lineno();
UINFO(9, " ApplyIgnores for " << filelinep->ascii() << " (no change predicted)");
return;
}
UINFO(9, " ApplyIgnores for " << filelinep->ascii());
m_lastIgnore.filenameno = filelinep->filenameno();
m_lastIgnore.lineno = filelinep->lineno();
m_lastIgnore.nextChange = std::numeric_limits<int>::max();
// Find all on/offs. Unlike lint pragmas we calculate the entire
// bitset of errors to enable, instead of individual enable/disable changes.
IgnIndices results;
m_intervalTree.find(filelinep->lineno(), results /*ref*/, m_lastIgnore.nextChange /*ref*/);
// Process all on/offs for lines up to and including the current line
VErrorBitSet bitset{VErrorBitSet::AllOnes{}};
for (const auto& it : results) {
const V3ControlIgnoresLine& cign = controlIgnLines[it];
UASSERT(cign.inRange(filelinep->lineno()),
"Interval tree returned lines outside range");
UINFO(9, " Hit " << cign);
cign.m_code.forDelegateCodes(
[&](V3ErrorCode subcode) { bitset.set(subcode, cign.m_on); });
}
filelinep->warnSetCtrlBitSet(bitset);
m_lastIgnore.nextChange = std::max(m_lastIgnore.nextChange, filelinep->lineno() + 1);
UINFO(9, " AppliedIgnores " << filelinep << " next@ln " << std::dec
<< m_lastIgnore.nextChange
<< " Messages: " << bitset.ascii());
}
bool waive(V3ErrorCode code, const string& message) {
if (code.hardError()) return false;
@ -648,17 +865,14 @@ void V3Control::addCaseFull(const string& filename, int lineno) {
V3ControlFile& file = V3ControlResolver::s().files().at(filename);
file.addLineAttribute(lineno, VPragmaType::FULL_CASE);
}
void V3Control::addCaseParallel(const string& filename, int lineno) {
V3ControlFile& file = V3ControlResolver::s().files().at(filename);
file.addLineAttribute(lineno, VPragmaType::PARALLEL_CASE);
}
void V3Control::addCoverageBlockOff(const string& filename, int lineno) {
V3ControlFile& file = V3ControlResolver::s().files().at(filename);
file.addLineAttribute(lineno, VPragmaType::COVERAGE_BLOCK_OFF);
}
void V3Control::addCoverageBlockOff(const string& module, const string& blockname) {
V3ControlResolver::s().modules().at(module).addCoverageBlockOff(blockname);
}
@ -668,16 +882,16 @@ void V3Control::addHierWorkers(FileLine* fl, const string& model, int workers) {
}
void V3Control::addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max) {
if (filename == "*") {
UINFO(9, "addIgnore " << code << " " << min << "-" << max << " fn=" << filename);
if (filename == "*") { // For "lint_off/lint_on [--rule x]"
FileLine::globalWarnOff(code, !on);
} else {
V3ControlResolver::s().files().at(filename).addIgnore(code, min, on);
if (max) V3ControlResolver::s().files().at(filename).addIgnore(code, max, !on);
} else { // For "lint_off/lint_on [--rule x] --file y [-lines min[-max]]"
V3ControlResolver::s().files().at(filename).addIgnore(code, min, max, on);
}
}
void V3Control::addIgnoreMatch(V3ErrorCode code, const string& filename, const string& contents,
const string& match) {
// For "lint_off --rule x --file y --match z", no support for lint_on
V3ControlResolver::s().files().at(filename).addIgnoreMatch(code, contents, match);
}
@ -700,7 +914,6 @@ void V3Control::addModulePragma(const string& module, VPragmaType pragma) {
void V3Control::addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) {
V3ControlResolver::s().addProfileData(fl, hierDpi, cost);
}
void V3Control::addProfileData(FileLine* fl, const string& model, const string& key,
uint64_t cost) {
V3ControlResolver::s().addProfileData(fl, model, key, cost);
@ -796,7 +1009,6 @@ void V3Control::applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep) {
V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modname);
if (modp) modp->applyBlock(nodep);
}
void V3Control::applyCoverageBlock(AstNodeModule* modulep, AstGenBlock* nodep) {
const string& filename = nodep->fileline()->filename();
V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename);
@ -885,3 +1097,5 @@ bool V3Control::waive(const FileLine* filelinep, V3ErrorCode code, const string&
if (!filep) return false;
return filep->waive(code, message);
}
void V3Control::selfTest() { VIntervalTree::selfTest(); }

View File

@ -75,6 +75,8 @@ public:
static uint64_t getCurrentHierBlockCost();
static bool waive(const FileLine* filelinep, V3ErrorCode code, const string& message);
static void selfTest();
};
#endif // Guard

View File

@ -272,15 +272,15 @@ public:
return (pretendError() || m_e == EC_FATALSRC || m_e == SIDEEFFECT || m_e == SYMRSVDWORD
|| m_e == ZERODLY);
}
// Warnings that are lint only
// Warnings that are lint only; includes all style warnings
bool lintError() const VL_MT_SAFE {
return (m_e == ALWCOMBORDER || m_e == ASCRANGE || m_e == ASSIGNEQEXPR || m_e == BSSPACE
|| m_e == CASEINCOMPLETE || m_e == CASEOVERLAP || m_e == CASEWITHX || m_e == CASEX
|| m_e == CASTCONST || m_e == CMPCONST || m_e == COLONPLUS || m_e == IMPLICIT
|| m_e == IMPLICITSTATIC || m_e == LATCH || m_e == MISINDENT || m_e == NEWERSTD
|| m_e == PREPROCZERO || m_e == PINMISSING || m_e == REALCVT || m_e == STATICVAR
|| m_e == UNSIGNED || m_e == WIDTH || m_e == WIDTHTRUNC || m_e == WIDTHEXPAND
|| m_e == WIDTHXZEXPAND);
return (styleError() || m_e == ALWCOMBORDER || m_e == ASCRANGE || m_e == ASSIGNEQEXPR
|| m_e == BSSPACE || m_e == CASEINCOMPLETE || m_e == CASEOVERLAP
|| m_e == CASEWITHX || m_e == CASEX || m_e == CASTCONST || m_e == CMPCONST
|| m_e == COLONPLUS || m_e == IMPLICIT || m_e == IMPLICITSTATIC || m_e == LATCH
|| m_e == MISINDENT || m_e == NEWERSTD || m_e == PREPROCZERO || m_e == PINMISSING
|| m_e == REALCVT || m_e == STATICVAR || m_e == UNSIGNED || m_e == WIDTH
|| m_e == WIDTHTRUNC || m_e == WIDTHEXPAND || m_e == WIDTHXZEXPAND);
}
// Warnings that are style only
bool styleError() const VL_MT_SAFE {
@ -313,6 +313,18 @@ public:
action(V3ErrorCode{COVERIGN});
action(V3ErrorCode{SPECIFYIGN});
return;
case V3ErrorCode::I_LINT:
for (int i = V3ErrorCode::EC_MIN; i < V3ErrorCode::_ENUM_MAX; ++i) {
const V3ErrorCode subcode{i};
if (subcode.lintError()) action(subcode);
}
return;
case V3ErrorCode::I_STYLE:
for (int i = V3ErrorCode::EC_MIN; i < V3ErrorCode::_ENUM_MAX; ++i) {
const V3ErrorCode subcode{i};
if (subcode.styleError()) action(subcode);
}
return;
case V3ErrorCode::UNUSED:
action(V3ErrorCode{UNUSEDGENVAR});
action(V3ErrorCode{UNUSEDLOOP});
@ -333,6 +345,8 @@ public:
switch (other) {
case V3ErrorCode::E_UNSUPPORTED:
return m_e == E_UNSUPPORTED || m_e == COVERIGN || m_e == SPECIFYIGN;
case V3ErrorCode::I_LINT: return lintError();
case V3ErrorCode::I_STYLE: return styleError();
case V3ErrorCode::UNUSED:
return m_e == UNUSEDGENVAR || m_e == UNUSEDLOOP || m_e == UNUSEDPARAM
|| m_e == UNUSEDSIGNAL;
@ -361,7 +375,9 @@ class VErrorBitSet final {
: m_bitset{lhs.m_bitset & rhs.m_bitset} {}
public:
class AllOnes {};
VErrorBitSet() {}
explicit VErrorBitSet(AllOnes) { m_bitset.set(); }
~VErrorBitSet() = default;
bool test(V3ErrorCode code) const { return m_bitset[code]; }
void set(V3ErrorCode code, bool flag) { m_bitset[code] = flag; }
@ -371,6 +387,13 @@ public:
}
VErrorBitSet operator&(const VErrorBitSet& rhs) const { return VErrorBitSet{*this, rhs}; }
bool operator==(const VErrorBitSet& rhs) const { return m_bitset == rhs.m_bitset; }
string ascii() const { // LCOV_EXCL_START
string result;
for (int i = V3ErrorCode::EC_MIN; i < V3ErrorCode::_ENUM_MAX; ++i) {
if (!test(V3ErrorCode{i})) result += " !"s + V3ErrorCode{i}.ascii();
}
return result;
} // LCOV_EXCL_STOP
};
// ######################################################################

View File

@ -130,7 +130,7 @@ FileLineSingleton::msgEnSetIdx_t FileLineSingleton::defaultMsgEnIndex() VL_MT_SA
// "-Wall" and the like only adjust the code subset, so use default enablement there
msgEnBitSet.set(MsgEnBitSet::Subset::CODE, code, !code.defaultsOff());
// The control file subset is only adjusted by the control files, everything enabled by
// default
// default. (V3Control also likewise creates with this)
msgEnBitSet.set(MsgEnBitSet::Subset::CTRL, code, true);
}
return addMsgEnBitSet(msgEnBitSet);
@ -145,12 +145,23 @@ FileLineSingleton::msgEnSetIdx_t FileLineSingleton::msgEnSetBit(msgEnSetIdx_t se
if (msgEn(setIdx).test(subset, subcode) != value) same = false;
});
if (same) return setIdx;
// Make new mask of all delegated codes at once (to avoid extra indicies if looped above this)
// Make new mask of all delegated codes at once (to avoid extra indices if looped above this)
MsgEnBitSet msgEnBitSet{msgEn(setIdx)};
code.forDelegateCodes([&](V3ErrorCode subcode) { msgEnBitSet.set(subset, subcode, value); });
return addMsgEnBitSet(msgEnBitSet);
}
FileLineSingleton::msgEnSetIdx_t FileLineSingleton::msgSetCtrlBitSet(msgEnSetIdx_t setIdx,
const VErrorBitSet& bitset) {
const MsgEnBitSet::Subset subset = MsgEnBitSet::Subset::CTRL;
// See if state matches existing
if (msgEn(setIdx).getAll(subset) == bitset) return setIdx;
// Make new mask of all delegated codes at once (to avoid extra indices if looped above this)
MsgEnBitSet msgEnBitSet{msgEn(setIdx)};
msgEnBitSet.setAll(subset, bitset);
return addMsgEnBitSet(msgEnBitSet);
}
FileLineSingleton::msgEnSetIdx_t FileLineSingleton::msgEnAnd(msgEnSetIdx_t lhsIdx,
msgEnSetIdx_t rhsIdx) {
const MsgEnBitSet& lhs = msgEn(lhsIdx);
@ -395,26 +406,9 @@ string FileLine::warnOffParse(const string& msgs, bool turnOff) {
return result;
}
void FileLine::warnLintOff(bool turnOff) {
for (int codei = V3ErrorCode::EC_MIN; codei < V3ErrorCode::_ENUM_MAX; codei++) {
const V3ErrorCode code{codei};
if (code.lintError()) warnOff(code, turnOff);
}
}
void FileLine::warnStyleOff(bool turnOff) {
for (int codei = V3ErrorCode::EC_MIN; codei < V3ErrorCode::_ENUM_MAX; codei++) {
const V3ErrorCode code{codei};
if (code.styleError()) warnOff(code, turnOff);
}
}
bool FileLine::warnIsOff(V3ErrorCode code) const {
if (!msgEn().enabled(code)) return true;
if (!defaultFileLine().msgEn().enabled(code)) return true; // Global overrides local
if ((code.lintError() || code.styleError()) && !msgEn().enabled(V3ErrorCode::I_LINT)) {
return true;
}
return false;
}

View File

@ -81,9 +81,19 @@ class FileLineSingleton final {
return m_codeEn == other.m_codeEn && m_ctrlEn == other.m_ctrlEn;
}
const VErrorBitSet& getAll(Subset subset) const {
return subset == Subset::CODE ? m_codeEn : m_ctrlEn;
}
bool test(Subset subset, V3ErrorCode code) const {
return subset == Subset::CODE ? m_codeEn.test(code) : m_ctrlEn.test(code);
}
void setAll(Subset subset, const VErrorBitSet& bitset) {
if (subset == Subset::CODE) { // LCOV_EXCL_BR_LINE
m_codeEn = bitset; // LCOV_EXCL_LINE
} else {
m_ctrlEn = bitset;
}
}
void set(Subset subset, V3ErrorCode code, bool value) {
if (subset == Subset::CODE) {
m_codeEn.set(code, value);
@ -136,6 +146,8 @@ class FileLineSingleton final {
// Set code to value in bitset at interned index setIdx, return interned index of result
msgEnSetIdx_t msgEnSetBit(msgEnSetIdx_t setIdx, MsgEnBitSet::Subset subset, V3ErrorCode code,
bool value);
// Bulk-set all control codes to given bitset
msgEnSetIdx_t msgSetCtrlBitSet(msgEnSetIdx_t setIdx, const VErrorBitSet& bitset);
// Return index to intersection set
msgEnSetIdx_t msgEnAnd(msgEnSetIdx_t lhsIdx, msgEnSetIdx_t rhsIdx);
// Retrieve interned bitset at given interned index. The returned reference is not persistent.
@ -367,8 +379,8 @@ private:
public:
void warnOn(V3ErrorCode code, bool flag) { warnSet(MsgEnBitSet::Subset::CODE, code, flag); }
void warnOnCtrl(V3ErrorCode code, bool flag) {
warnSet(MsgEnBitSet::Subset::CTRL, code, flag);
void warnSetCtrlBitSet(const VErrorBitSet& bitset) {
m_msgEnIdx = singleton().msgSetCtrlBitSet(m_msgEnIdx, bitset);
}
void warnOff(V3ErrorCode code, bool turnOff) { warnOn(code, !turnOff); }
string warnOffParse(const string& msgs, bool turnOff); // Returns "" if ok
@ -392,8 +404,6 @@ public:
// <command-line> and <built-in> match what GCC outputs
static string commandLineFilename() VL_MT_SAFE { return "<command-line>"; }
static string builtInFilename() VL_MT_SAFE { return "<built-in>"; }
static void globalWarnLintOff(bool turnOff) { defaultFileLine().warnLintOff(turnOff); }
static void globalWarnStyleOff(bool turnOff) { defaultFileLine().warnStyleOff(turnOff); }
static void globalWarnOff(V3ErrorCode code, bool turnOff) {
defaultFileLine().warnOff(code, turnOff);
}

View File

@ -1859,10 +1859,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
});
DECL_OPTION("-vpi", OnOff, &m_vpi);
DECL_OPTION("-Wall", CbCall, []() {
FileLine::globalWarnLintOff(false);
FileLine::globalWarnStyleOff(false);
});
DECL_OPTION("-Wall", CbCall, []() { FileLine::globalWarnOff(V3ErrorCode::I_LINT, false); });
DECL_OPTION("-Werror-", CbPartialMatch, [this, fl](const char* optp) {
const V3ErrorCode code{optp};
if (code == V3ErrorCode::EC_ERROR) {
@ -1890,11 +1887,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
}
DECL_OPTION("-Wno-context", CbCall, [this]() { m_context = false; });
DECL_OPTION("-Wno-fatal", CbCall, []() { V3Error::warnFatal(false); });
DECL_OPTION("-Wno-lint", CbCall, []() {
FileLine::globalWarnLintOff(true);
FileLine::globalWarnStyleOff(true);
});
DECL_OPTION("-Wno-style", CbCall, []() { FileLine::globalWarnStyleOff(true); });
DECL_OPTION("-Wno-lint", CbCall, []() { FileLine::globalWarnOff(V3ErrorCode::I_LINT, true); });
DECL_OPTION("-Wno-style", CbCall,
[]() { FileLine::globalWarnOff(V3ErrorCode::I_STYLE, true); });
DECL_OPTION("-work", Set, &m_work);
DECL_OPTION("-Wpedantic", CbCall, [this]() {
m_pedantic = true;
@ -1913,8 +1908,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
V3Error::pretendError(code, false);
}
});
DECL_OPTION("-Wwarn-lint", CbCall, []() { FileLine::globalWarnLintOff(false); });
DECL_OPTION("-Wwarn-style", CbCall, []() { FileLine::globalWarnStyleOff(false); });
DECL_OPTION("-Wwarn-lint", CbCall,
[]() { FileLine::globalWarnOff(V3ErrorCode::I_LINT, false); });
DECL_OPTION("-Wwarn-style", CbCall,
[]() { FileLine::globalWarnOff(V3ErrorCode::I_STYLE, false); });
DECL_OPTION("-waiver-multiline", OnOff, &m_waiverMultiline);
DECL_OPTION("-waiver-output", Set, &m_waiverOutput);

View File

@ -723,6 +723,7 @@ static bool verilate(const string& argString) {
V3ExecGraph::selfTest();
V3PreShell::selfTest();
V3Broken::selfTest();
V3Control::selfTest();
V3ThreadPool::selfTest();
UINFO(2, "selfTest done");
}

View File

@ -8036,9 +8036,9 @@ vltItem:
| vltOffFront vltDFile
{ V3Control::addIgnore($1, false, *$2, 0, 0); }
| vltOffFront vltDFile yVLT_D_LINES yaINTNUM
{ V3Control::addIgnore($1, false, *$2, $4->toUInt(), $4->toUInt() + 1); }
{ V3Control::addIgnore($1, false, *$2, $4->toUInt(), $4->toUInt()); }
| vltOffFront vltDFile yVLT_D_LINES yaINTNUM '-' yaINTNUM
{ V3Control::addIgnore($1, false, *$2, $4->toUInt(), $6->toUInt() + 1); }
{ V3Control::addIgnore($1, false, *$2, $4->toUInt(), $6->toUInt()); }
| vltOffFront vltDFile vltDMatch
{ if (($1 == V3ErrorCode::I_COVERAGE) || ($1 == V3ErrorCode::I_TRACING)) {
$<fl>1->v3error("Argument -match only supported for lint_off");
@ -8074,9 +8074,9 @@ vltItem:
| vltOnFront vltDFile
{ V3Control::addIgnore($1, true, *$2, 0, 0); }
| vltOnFront vltDFile yVLT_D_LINES yaINTNUM
{ V3Control::addIgnore($1, true, *$2, $4->toUInt(), $4->toUInt() + 1); }
{ V3Control::addIgnore($1, true, *$2, $4->toUInt(), $4->toUInt()); }
| vltOnFront vltDFile yVLT_D_LINES yaINTNUM '-' yaINTNUM
{ V3Control::addIgnore($1, true, *$2, $4->toUInt(), $6->toUInt() + 1); }
{ V3Control::addIgnore($1, true, *$2, $4->toUInt(), $6->toUInt()); }
| vltOnFront vltDScope
{ if ($1 != V3ErrorCode::I_TRACING) {
$<fl>1->v3error("Argument -scope only supported for tracing_on/off");

View File

@ -0,0 +1,7 @@
%Warning-UNUSEDSIGNAL: t/t_vlt_warn_file2_bad.v:20:7: Signal is not driven, nor used: 'unuse_warn_var_line20'
: ... note: In instance 't'
20 | reg unuse_warn_var_line20;
| ^~~~~~~~~~~~~~~~~~~~~
... 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,16 @@
#!/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=['--Wall'], fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,23 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`ifdef verilator
`verilator_config
lint_off -rule DECLFILENAME
// Test filename matches are in directive parse order
lint_off -rule UNUSED -file "*/t_*" // Sorts before t_vlt_*
lint_off -rule UNUSED -file "*/t_vlt_warn*" // Sorts after t_vlt_*
lint_on -rule UNUSED -file "*/t_vlt_*"
lint_off -rule UNUSED -file "*/t_vlt_warn*" -lines 21-22
`verilog
`endif
module t;
reg unuse_warn_var_line20; // Unused warning - must be line 20 (on)
reg unuse_warn2_var_line21; // Unused warning - must be line 21 (off)
reg unuse_warn3_var_line22; // Unused warning - must be line 22 (off)
endmodule

View File

@ -0,0 +1,11 @@
%Warning-UNUSEDPARAM: t/t_vlt_warn_one_on_bad.v:24:14: Parameter is not used: 'unuse_warn5_var_line24'
: ... note: In instance 't'
24 | localparam unuse_warn5_var_line24 = 0;
| ^~~~~~~~~~~~~~~~~~~~~~
... For warning description see https://verilator.org/warn/UNUSEDPARAM?v=latest
... Use "/* verilator lint_off UNUSEDPARAM */" and lint_on around source to disable this message.
%Warning-UNUSEDPARAM: t/t_vlt_warn_one_on_bad.v:25:14: Parameter is not used: 'unuse_warn5_var_line25'
: ... note: In instance 't'
25 | localparam unuse_warn5_var_line25 = 0;
| ^~~~~~~~~~~~~~~~~~~~~~
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/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=['--Wall'], fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,27 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`ifdef verilator
`verilator_config
// Issue #4185
lint_off
lint_on -rule UNUSEDPARAM
`verilog
`endif
module t;
reg unuse_warn_var_line20; // Unused warning - must be line 20
reg unuse_warn2_var_line21; // Unused warning - must be line 21
reg unuse_warn3_var_line22; // Unused warning - must be line 22
reg unuse_warn4_var_line23; // Unused warning - must be line 23
localparam unuse_warn5_var_line24 = 0; // Unused warning - must be line 24 (not suppressed)
localparam unuse_warn5_var_line25 = 0; // Unused warning - must be line 25 (not suppressed)
endmodule

View File

@ -0,0 +1,11 @@
%Warning-UNUSEDSIGNAL: t/t_vlt_warn_range_bad.v:24:7: Signal is not driven, nor used: 'unuse_warn5_var_line24'
: ... note: In instance 't'
24 | reg unuse_warn5_var_line24;
| ^~~~~~~~~~~~~~~~~~~~~~
... 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.
%Warning-UNUSEDSIGNAL: t/t_vlt_warn_range_bad.v:26:7: Signal is not driven, nor used: 'unuse_warn5_var_line26'
: ... note: In instance 't'
26 | reg unuse_warn5_var_line26;
| ^~~~~~~~~~~~~~~~~~~~~~
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/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=['--Wall'], fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,28 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`ifdef verilator
`verilator_config
lint_off -rule DECLFILENAME
// Test overlapping ranges work correctly
lint_off -rule UNUSED -file "*/t_*" -lines 21-23
lint_off -rule UNUSED -file "*/t_*" -lines 20-22 // Intentional overlap with above
lint_off -rule UNUSED -file "*/t_*" -lines 25-99
lint_on -rule UNUSED -file "*/t_*" -lines 26
`verilog
`endif
module t;
reg unuse_warn_var_line20; // Unused warning - must be line 20
reg unuse_warn2_var_line21; // Unused warning - must be line 21
reg unuse_warn3_var_line22; // Unused warning - must be line 22
reg unuse_warn4_var_line23; // Unused warning - must be line 23
reg unuse_warn5_var_line24; // Unused warning - must be line 24 (not suppressed)
reg unuse_warn5_var_line25; // Unused warning - must be line 25
reg unuse_warn5_var_line26; // Unused warning - must be line 26 (turned on)
reg unuse_warn5_var_line27; // Unused warning - must be line 27
endmodule