From 4080284e53b29930a1dfcff0b650995e6ce91f82 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 30 Dec 2025 20:31:34 -0500 Subject: [PATCH] Fix warning lint directive ordering and consistency (#4185) (#5368) (#5610) (#6876). --- Changes | 2 + docs/guide/exe_verilator.rst | 23 +- src/V3Control.cpp | 324 +++++++++++++++++++---- src/V3Control.h | 2 + src/V3Error.h | 39 ++- src/V3FileLine.cpp | 32 +-- src/V3FileLine.h | 18 +- src/V3Options.cpp | 19 +- src/Verilator.cpp | 1 + src/verilog.y | 8 +- test_regress/t/t_vlt_warn_file2_bad.out | 7 + test_regress/t/t_vlt_warn_file2_bad.py | 16 ++ test_regress/t/t_vlt_warn_file2_bad.v | 23 ++ test_regress/t/t_vlt_warn_one_on_bad.out | 11 + test_regress/t/t_vlt_warn_one_on_bad.py | 16 ++ test_regress/t/t_vlt_warn_one_on_bad.v | 27 ++ test_regress/t/t_vlt_warn_range_bad.out | 11 + test_regress/t/t_vlt_warn_range_bad.py | 16 ++ test_regress/t/t_vlt_warn_range_bad.v | 28 ++ 19 files changed, 515 insertions(+), 108 deletions(-) create mode 100644 test_regress/t/t_vlt_warn_file2_bad.out create mode 100755 test_regress/t/t_vlt_warn_file2_bad.py create mode 100644 test_regress/t/t_vlt_warn_file2_bad.v create mode 100644 test_regress/t/t_vlt_warn_one_on_bad.out create mode 100755 test_regress/t/t_vlt_warn_one_on_bad.py create mode 100644 test_regress/t/t_vlt_warn_one_on_bad.v create mode 100644 test_regress/t/t_vlt_warn_range_bad.out create mode 100755 test_regress/t/t_vlt_warn_range_bad.py create mode 100644 test_regress/t/t_vlt_warn_range_bad.v diff --git a/Changes b/Changes index eb2888c1c..f4fce3052 100644 --- a/Changes +++ b/Changes @@ -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 diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 04d37d398..b452e6df8 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -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. diff --git a/src/V3Control.cpp b/src/V3Control.cpp index 3e432be8d..c089ddeec 100644 --- a/src/V3Control.cpp +++ b/src/V3Control.cpp @@ -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& 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; // 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 controlIgnLines; + +using IgnIndices = std::vector; // 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 m_leftp; // Entry with all its rules/entries < this center + std::unique_ptr 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 m_rootp; // Root of interval tree + IgnIndices m_everyLines; // All-line disables + +private: + std::unique_ptr buildTree(const IgnIndices& points) { + if (points.empty()) return nullptr; + + int minval = std::numeric_limits::max(); + int maxval = std::numeric_limits::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(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::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> 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(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::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(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; @@ -355,16 +552,17 @@ public: // File entity class V3ControlFile final { using LineAttrMap = std::map; // Map line->bitset of attributes - using IgnLines = std::multiset; // list of {line,code,on} using Waivers = std::vector; // 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(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::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(); } diff --git a/src/V3Control.h b/src/V3Control.h index 8a7a9070f..0df5dd252 100644 --- a/src/V3Control.h +++ b/src/V3Control.h @@ -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 diff --git a/src/V3Error.h b/src/V3Error.h index 48cfab6c4..b0b8f88ee 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -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 }; // ###################################################################### diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index e518f258b..13905cb9b 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -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; } diff --git a/src/V3FileLine.h b/src/V3FileLine.h index ffb9778de..5aac13328 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -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: // and match what GCC outputs static string commandLineFilename() VL_MT_SAFE { return ""; } static string builtInFilename() VL_MT_SAFE { return ""; } - 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); } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 2f05ebf78..8df2783f6 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -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); diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 1a559bba4..47856e1da 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -723,6 +723,7 @@ static bool verilate(const string& argString) { V3ExecGraph::selfTest(); V3PreShell::selfTest(); V3Broken::selfTest(); + V3Control::selfTest(); V3ThreadPool::selfTest(); UINFO(2, "selfTest done"); } diff --git a/src/verilog.y b/src/verilog.y index 5779ac185..63526580d 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -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)) { $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) { $1->v3error("Argument -scope only supported for tracing_on/off"); diff --git a/test_regress/t/t_vlt_warn_file2_bad.out b/test_regress/t/t_vlt_warn_file2_bad.out new file mode 100644 index 000000000..6967a504f --- /dev/null +++ b/test_regress/t/t_vlt_warn_file2_bad.out @@ -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 diff --git a/test_regress/t/t_vlt_warn_file2_bad.py b/test_regress/t/t_vlt_warn_file2_bad.py new file mode 100755 index 000000000..6b6b023e4 --- /dev/null +++ b/test_regress/t/t_vlt_warn_file2_bad.py @@ -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() diff --git a/test_regress/t/t_vlt_warn_file2_bad.v b/test_regress/t/t_vlt_warn_file2_bad.v new file mode 100644 index 000000000..aa761484f --- /dev/null +++ b/test_regress/t/t_vlt_warn_file2_bad.v @@ -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 diff --git a/test_regress/t/t_vlt_warn_one_on_bad.out b/test_regress/t/t_vlt_warn_one_on_bad.out new file mode 100644 index 000000000..44442c9c5 --- /dev/null +++ b/test_regress/t/t_vlt_warn_one_on_bad.out @@ -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 diff --git a/test_regress/t/t_vlt_warn_one_on_bad.py b/test_regress/t/t_vlt_warn_one_on_bad.py new file mode 100755 index 000000000..6b6b023e4 --- /dev/null +++ b/test_regress/t/t_vlt_warn_one_on_bad.py @@ -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() diff --git a/test_regress/t/t_vlt_warn_one_on_bad.v b/test_regress/t/t_vlt_warn_one_on_bad.v new file mode 100644 index 000000000..a43270f7d --- /dev/null +++ b/test_regress/t/t_vlt_warn_one_on_bad.v @@ -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 diff --git a/test_regress/t/t_vlt_warn_range_bad.out b/test_regress/t/t_vlt_warn_range_bad.out new file mode 100644 index 000000000..03dcab696 --- /dev/null +++ b/test_regress/t/t_vlt_warn_range_bad.out @@ -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 diff --git a/test_regress/t/t_vlt_warn_range_bad.py b/test_regress/t/t_vlt_warn_range_bad.py new file mode 100755 index 000000000..6b6b023e4 --- /dev/null +++ b/test_regress/t/t_vlt_warn_range_bad.py @@ -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() diff --git a/test_regress/t/t_vlt_warn_range_bad.v b/test_regress/t/t_vlt_warn_range_bad.v new file mode 100644 index 000000000..8c045e32a --- /dev/null +++ b/test_regress/t/t_vlt_warn_range_bad.v @@ -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