From 73a8b5c26107e269d928083778bad8a5596fe322 Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Fri, 22 Aug 2025 19:10:48 +0100 Subject: [PATCH 01/14] Issue#1479: Modified architecture_025 to accept regular expressions as names. --- vsg/rules/__init__.py | 2 +- vsg/rules/architecture/rule_025.py | 8 ++++--- ...of.py => does_token_value_match_one_of.py} | 23 ++++++++++++++++--- 3 files changed, 26 insertions(+), 7 deletions(-) rename vsg/rules/{is_token_value_one_of.py => does_token_value_match_one_of.py} (56%) diff --git a/vsg/rules/__init__.py b/vsg/rules/__init__.py index cc061100f..9e53363e9 100644 --- a/vsg/rules/__init__.py +++ b/vsg/rules/__init__.py @@ -32,7 +32,7 @@ from .insert_token_next_to_token_if_it_does_not_exist_between_tokens_using_value_from_token import ( insert_token_next_to_token_if_it_does_not_exist_between_tokens_using_value_from_token, ) -from .is_token_value_one_of import is_token_value_one_of +from .does_token_value_match_one_of import does_token_value_match_one_of from .align_tokens_in_region_between_tokens import align_tokens_in_region_between_tokens from .align_tokens_in_region_between_tokens_unless_between_tokens import align_tokens_in_region_between_tokens_unless_between_tokens from .align_tokens_in_region_between_tokens_when_between_tokens_unless_between_tokens import ( diff --git a/vsg/rules/architecture/rule_025.py b/vsg/rules/architecture/rule_025.py index f7364af57..8e83e7b4a 100644 --- a/vsg/rules/architecture/rule_025.py +++ b/vsg/rules/architecture/rule_025.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -from vsg.rules import is_token_value_one_of +from vsg.rules import does_token_value_match_one_of from vsg.token import architecture_body as token -class rule_025(is_token_value_one_of): +class rule_025(does_token_value_match_one_of): """ This rule checks for valid names for the architecture. Typical architecture names are: RTL, EMPTY, and BEHAVE. This rule allows the user to restrict what can be used for an architecture name. + Note that regular expressions are accepted in the **names** field. .. NOTE:: This rule is disabled by default. You can enable and configure the names using the following configuration. @@ -24,6 +25,7 @@ class rule_025(is_token_value_one_of): - rtl - empty - behave + - my_pattern.* **Violation** @@ -40,4 +42,4 @@ def __init__(self): super().__init__(token.identifier) def _get_solution(self, iLineNumber): - return "Architecture identifier must be from this list: " + ", ".join(self.names) + return "Architecture identifier must match a name from this list: " + ", ".join(self.names) diff --git a/vsg/rules/is_token_value_one_of.py b/vsg/rules/does_token_value_match_one_of.py similarity index 56% rename from vsg/rules/is_token_value_one_of.py rename to vsg/rules/does_token_value_match_one_of.py index 14d607996..df34aec4d 100644 --- a/vsg/rules/is_token_value_one_of.py +++ b/vsg/rules/does_token_value_match_one_of.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- +import re from vsg import violation from vsg.rule_group import naming -class is_token_value_one_of(naming.Rule): +class does_token_value_match_one_of(naming.Rule): """ - Checks if a token value is in a list of provided values. + Checks if a token value matches one of provided regex patterns. """ def __init__(self, token): @@ -18,11 +19,14 @@ def __init__(self, token): self.configuration.append("names") self.token = token self.configuration_documentation_link = None + self.regexp_names = [] def _get_tokens_of_interest(self, oFile): return oFile.get_tokens_matching([self.token]) def _analyze(self, lToi): + self.generate_regexp_names() + self.solution = self._get_solution(None) lower_names = [] for sName in self.names: @@ -30,5 +34,18 @@ def _analyze(self, lToi): for oToi in lToi: lTokens = oToi.get_tokens() - if not lTokens[0].get_lower_value() in lower_names: + sToken = lTokens[0].get_lower_value() + if not self.name_found(sToken) : self.add_violation(violation.New(oToi.get_line_number(), oToi, self.solution)) + + def generate_regexp_names(self): + for name in self.names: + regexp = re.compile(name, re.IGNORECASE) + self.regexp_names.append(regexp) + + def name_found(self, sToken): + for regexp in self.regexp_names: + if regexp.fullmatch(sToken) is not None: + return True + return False + From 22570474525e06a2f3ab694505234796577cef9c Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Fri, 22 Aug 2025 19:33:59 +0100 Subject: [PATCH 02/14] Issue#1479: Corrected error by resetting regexp names. --- vsg/rules/does_token_value_match_one_of.py | 1 + 1 file changed, 1 insertion(+) diff --git a/vsg/rules/does_token_value_match_one_of.py b/vsg/rules/does_token_value_match_one_of.py index df34aec4d..826d0f21d 100644 --- a/vsg/rules/does_token_value_match_one_of.py +++ b/vsg/rules/does_token_value_match_one_of.py @@ -39,6 +39,7 @@ def _analyze(self, lToi): self.add_violation(violation.New(oToi.get_line_number(), oToi, self.solution)) def generate_regexp_names(self): + self.regexp_names= [] for name in self.names: regexp = re.compile(name, re.IGNORECASE) self.regexp_names.append(regexp) From 0f1f0c9dabb9d147943297bbd13de8159156c4a5 Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Fri, 22 Aug 2025 19:34:20 +0100 Subject: [PATCH 03/14] Issue#1479: Modified test for architecture_025 to test regular expressions as names. --- tests/architecture/test_rule_025.py | 30 +++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/architecture/test_rule_025.py b/tests/architecture/test_rule_025.py index b1b4ae72f..d40eed925 100644 --- a/tests/architecture/test_rule_025.py +++ b/tests/architecture/test_rule_025.py @@ -28,7 +28,7 @@ def test_rule_025(self): lExpected = [3, 10, 17, 24] oRule.analyze(self.oFile) self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) - self.assertEqual("Architecture identifier must be from this list: ", oRule._get_solution(None)) + self.assertEqual("Architecture identifier must match a name from this list: ", oRule._get_solution(None)) oRule.violations = [] oRule.names = [] @@ -36,46 +36,60 @@ def test_rule_025(self): lExpected = [10, 17, 24] oRule.analyze(self.oFile) self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) - self.assertEqual("Architecture identifier must be from this list: rtl", oRule._get_solution(None)) + self.assertEqual("Architecture identifier must match a name from this list: rtl", oRule._get_solution(None)) oRule.violations = [] oRule.names = ["ENTITY1"] lExpected = [3, 17, 24] oRule.analyze(self.oFile) self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) - self.assertEqual("Architecture identifier must be from this list: ENTITY1", oRule._get_solution(None)) + self.assertEqual("Architecture identifier must match a name from this list: ENTITY1", oRule._get_solution(None)) oRule.violations = [] oRule.names = ["BLUE"] lExpected = [3, 10, 24] oRule.analyze(self.oFile) self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) - self.assertEqual("Architecture identifier must be from this list: BLUE", oRule._get_solution(None)) + self.assertEqual("Architecture identifier must match a name from this list: BLUE", oRule._get_solution(None)) oRule.violations = [] oRule.names = ["CDC"] lExpected = [3, 10, 17] oRule.analyze(self.oFile) self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) - self.assertEqual("Architecture identifier must be from this list: CDC", oRule._get_solution(None)) + self.assertEqual("Architecture identifier must match a name from this list: CDC", oRule._get_solution(None)) oRule.violations = [] oRule.names = ["rtl", "CDC"] lExpected = [10, 17] oRule.analyze(self.oFile) self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) - self.assertEqual("Architecture identifier must be from this list: rtl, CDC", oRule._get_solution(None)) + self.assertEqual("Architecture identifier must match a name from this list: rtl, CDC", oRule._get_solution(None)) oRule.violations = [] oRule.names = ["rtl", "cdc", "blue"] lExpected = [10] oRule.analyze(self.oFile) self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) - self.assertEqual("Architecture identifier must be from this list: rtl, cdc, blue", oRule._get_solution(None)) + self.assertEqual("Architecture identifier must match a name from this list: rtl, cdc, blue", oRule._get_solution(None)) oRule.violations = [] oRule.names = ["rtl", "cdc", "blue", "entity1"] lExpected = [] oRule.analyze(self.oFile) self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) - self.assertEqual("Architecture identifier must be from this list: rtl, cdc, blue, entity1", oRule._get_solution(None)) + self.assertEqual("Architecture identifier must match a name from this list: rtl, cdc, blue, entity1", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = [".ntit\w\d"] + lExpected = [3, 17, 24] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Architecture identifier must match a name from this list: .ntit\w\d", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["\w\w\w", "b...", ".ntit\w\d"] + lExpected = [] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Architecture identifier must match a name from this list: \w\w\w, b..., .ntit\w\d", oRule._get_solution(None)) From 829a67969f9755d3c80ae6fb59dfabb0047e95c4 Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Fri, 22 Aug 2025 19:34:35 +0100 Subject: [PATCH 04/14] Issue#1479: Modified docs for architecture_025. --- docs/architecture_rules.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/architecture_rules.rst b/docs/architecture_rules.rst index 4d2864e65..cd39c4f2c 100644 --- a/docs/architecture_rules.rst +++ b/docs/architecture_rules.rst @@ -516,6 +516,7 @@ architecture_025 This rule checks for valid names for the architecture. Typical architecture names are: RTL, EMPTY, and BEHAVE. This rule allows the user to restrict what can be used for an architecture name. +Note that regular expressions are accepted in the **names** field. .. NOTE:: This rule is disabled by default. You can enable and configure the names using the following configuration. @@ -531,6 +532,7 @@ This rule allows the user to restrict what can be used for an architecture name. - rtl - empty - behave + - my_pattern.* **Violation** From 6e942898a49b015371aaa1eb0a68602dc80f8580 Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Fri, 22 Aug 2025 19:37:32 +0100 Subject: [PATCH 05/14] Issue#1479: Formatting. --- vsg/rules/does_token_value_match_one_of.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/vsg/rules/does_token_value_match_one_of.py b/vsg/rules/does_token_value_match_one_of.py index 826d0f21d..4b3a610e9 100644 --- a/vsg/rules/does_token_value_match_one_of.py +++ b/vsg/rules/does_token_value_match_one_of.py @@ -35,11 +35,11 @@ def _analyze(self, lToi): for oToi in lToi: lTokens = oToi.get_tokens() sToken = lTokens[0].get_lower_value() - if not self.name_found(sToken) : + if not self.name_found(sToken): self.add_violation(violation.New(oToi.get_line_number(), oToi, self.solution)) def generate_regexp_names(self): - self.regexp_names= [] + self.regexp_names = [] for name in self.names: regexp = re.compile(name, re.IGNORECASE) self.regexp_names.append(regexp) @@ -49,4 +49,3 @@ def name_found(self, sToken): if regexp.fullmatch(sToken) is not None: return True return False - From 727d91923205ef3fbd97120c8c2233ea2147caec Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Sat, 30 Aug 2025 22:01:53 +0100 Subject: [PATCH 06/14] Issue#1479: Code improvement. --- vsg/rules/does_token_value_match_one_of.py | 27 +++++++--------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/vsg/rules/does_token_value_match_one_of.py b/vsg/rules/does_token_value_match_one_of.py index 4b3a610e9..deec50e55 100644 --- a/vsg/rules/does_token_value_match_one_of.py +++ b/vsg/rules/does_token_value_match_one_of.py @@ -19,33 +19,22 @@ def __init__(self, token): self.configuration.append("names") self.token = token self.configuration_documentation_link = None - self.regexp_names = [] def _get_tokens_of_interest(self, oFile): return oFile.get_tokens_matching([self.token]) def _analyze(self, lToi): - self.generate_regexp_names() - self.solution = self._get_solution(None) - lower_names = [] - for sName in self.names: - lower_names.append(sName.lower()) + lRegexNames = [re.compile(name, re.IGNORECASE) for name in self.names] for oToi in lToi: lTokens = oToi.get_tokens() - sToken = lTokens[0].get_lower_value() - if not self.name_found(sToken): + if self._check_for_violation(lTokens[0].get_lower_value(), lRegexNames): self.add_violation(violation.New(oToi.get_line_number(), oToi, self.solution)) - def generate_regexp_names(self): - self.regexp_names = [] - for name in self.names: - regexp = re.compile(name, re.IGNORECASE) - self.regexp_names.append(regexp) - - def name_found(self, sToken): - for regexp in self.regexp_names: - if regexp.fullmatch(sToken) is not None: - return True - return False + def _check_for_violation(self, sToken, lRegexNames): + for regex in lRegexNames: + if regex.fullmatch(sToken) is not None: + return False + return True + From 085564afa1d5b3f9ddf7f03e367ead5dc8c2ea73 Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Mon, 1 Sep 2025 19:56:51 +0100 Subject: [PATCH 07/14] Issue#1504: Added does_token_value_match_none_of class. --- vsg/rules/__init__.py | 1 + vsg/rules/does_token_value_match_none_of.py | 15 +++++++++++++++ vsg/rules/does_token_value_match_one_of.py | 8 +++++--- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 vsg/rules/does_token_value_match_none_of.py diff --git a/vsg/rules/__init__.py b/vsg/rules/__init__.py index 9e53363e9..aad0a093e 100644 --- a/vsg/rules/__init__.py +++ b/vsg/rules/__init__.py @@ -32,6 +32,7 @@ from .insert_token_next_to_token_if_it_does_not_exist_between_tokens_using_value_from_token import ( insert_token_next_to_token_if_it_does_not_exist_between_tokens_using_value_from_token, ) +from .does_token_value_match_none_of import does_token_value_match_none_of from .does_token_value_match_one_of import does_token_value_match_one_of from .align_tokens_in_region_between_tokens import align_tokens_in_region_between_tokens from .align_tokens_in_region_between_tokens_unless_between_tokens import align_tokens_in_region_between_tokens_unless_between_tokens diff --git a/vsg/rules/does_token_value_match_none_of.py b/vsg/rules/does_token_value_match_none_of.py new file mode 100644 index 000000000..42b55cb12 --- /dev/null +++ b/vsg/rules/does_token_value_match_none_of.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from vsg.rules import does_token_value_match_one_of as Rule + + +class does_token_value_match_none_of(Rule.does_token_value_match_one_of): + """ + Checks if a token value matches none of provided regex patterns. + """ + + def __init__(self, oToken): + super().__init__(oToken) + + def _check_for_violation(self, sToken, lRegexNames): + return not self._token_matches_at_least_one(sToken, lRegexNames) diff --git a/vsg/rules/does_token_value_match_one_of.py b/vsg/rules/does_token_value_match_one_of.py index deec50e55..6d377e0a8 100644 --- a/vsg/rules/does_token_value_match_one_of.py +++ b/vsg/rules/does_token_value_match_one_of.py @@ -11,13 +11,13 @@ class does_token_value_match_one_of(naming.Rule): Checks if a token value matches one of provided regex patterns. """ - def __init__(self, token): + def __init__(self, oToken): super().__init__() self.names = [] self.fixable = False self.disable = True self.configuration.append("names") - self.token = token + self.token = oToken self.configuration_documentation_link = None def _get_tokens_of_interest(self, oFile): @@ -33,8 +33,10 @@ def _analyze(self, lToi): self.add_violation(violation.New(oToi.get_line_number(), oToi, self.solution)) def _check_for_violation(self, sToken, lRegexNames): + return self._token_matches_at_least_one(sToken, lRegexNames) + + def _token_matches_at_least_one(self, sToken, lRegexNames): for regex in lRegexNames: if regex.fullmatch(sToken) is not None: return False return True - From b46557bc97ccde31bdd74eb654f2bc303c143625 Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Mon, 1 Sep 2025 20:17:10 +0100 Subject: [PATCH 08/14] Issue#1504: Added rules to restrict library and package names. --- vsg/rules/library/__init__.py | 1 + vsg/rules/library/rule_012.py | 24 ++++++++++++++++++++++++ vsg/rules/use_clause/__init__.py | 1 + vsg/rules/use_clause/rule_001.py | 26 ++++++++++++++++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 vsg/rules/library/rule_012.py create mode 100644 vsg/rules/use_clause/rule_001.py diff --git a/vsg/rules/library/__init__.py b/vsg/rules/library/__init__.py index 243c5bc98..ad8b67189 100644 --- a/vsg/rules/library/__init__.py +++ b/vsg/rules/library/__init__.py @@ -11,5 +11,6 @@ from .rule_009 import rule_009 from .rule_010 import rule_010 from .rule_011 import rule_011 +from .rule_012 import rule_012 from .rule_500 import rule_500 from .rule_600 import rule_600 diff --git a/vsg/rules/library/rule_012.py b/vsg/rules/library/rule_012.py new file mode 100644 index 000000000..961de524a --- /dev/null +++ b/vsg/rules/library/rule_012.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from vsg.rules import does_token_value_match_none_of +from vsg.token import logical_name_list + + +class rule_012(does_token_value_match_none_of): + """ + This rule checks for libraries that have been restricted by the user. + + .. NOTE:: This rule is disabled by default. + + **Violation** + + .. code-block:: vhdl + + library bad_lib; + """ + + def __init__(self): + super().__init__(logical_name_list.logical_name) + + def _get_solution(self, iLineNumber): + return "Library name is on list of restricted names: " + ", ".join(self.names) diff --git a/vsg/rules/use_clause/__init__.py b/vsg/rules/use_clause/__init__.py index 48b48eb1d..13a39efd6 100644 --- a/vsg/rules/use_clause/__init__.py +++ b/vsg/rules/use_clause/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from .rule_001 import rule_001 from .rule_500 import rule_500 from .rule_501 import rule_501 from .rule_502 import rule_502 diff --git a/vsg/rules/use_clause/rule_001.py b/vsg/rules/use_clause/rule_001.py new file mode 100644 index 000000000..fa0988338 --- /dev/null +++ b/vsg/rules/use_clause/rule_001.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +from vsg.rules import does_token_value_match_none_of +from vsg.token import use_clause + + +class rule_001(does_token_value_match_none_of): + """ + This rule checks for packages that have been restricted by the user. + + .. NOTE:: This rule is configured to restrict the std_logic_arith package by default. + + **Violation** + + .. code-block:: vhdl + + use ieee.std_logic_arith.all; + """ + + def __init__(self): + super().__init__(use_clause.package_name) + self.disable = False + self.names = ["std_logic_arith"] + + def _get_solution(self, iLineNumber): + return "Package name is on list of restricted names: " + ", ".join(self.names) From e0787f39a5227ee9cb6fb5b8c782c12c0ecb3e25 Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Mon, 1 Sep 2025 20:23:30 +0100 Subject: [PATCH 09/14] Issue#1504: Added tests for rules to restrict library and package names. --- tests/library/rule_012_test_input.vhd | 5 ++ tests/library/test_rule_012.py | 82 +++++++++++++++++++++++ tests/use_clause/rule_001_test_input.vhd | 10 +++ tests/use_clause/test_rule_001.py | 84 ++++++++++++++++++++++++ 4 files changed, 181 insertions(+) create mode 100644 tests/library/rule_012_test_input.vhd create mode 100644 tests/library/test_rule_012.py create mode 100644 tests/use_clause/rule_001_test_input.vhd create mode 100644 tests/use_clause/test_rule_001.py diff --git a/tests/library/rule_012_test_input.vhd b/tests/library/rule_012_test_input.vhd new file mode 100644 index 000000000..171b49429 --- /dev/null +++ b/tests/library/rule_012_test_input.vhd @@ -0,0 +1,5 @@ + +library ieee; +library work; +library std; +library bad_lib; diff --git a/tests/library/test_rule_012.py b/tests/library/test_rule_012.py new file mode 100644 index 000000000..3cbc220eb --- /dev/null +++ b/tests/library/test_rule_012.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- + +import os +import unittest + +from tests import utils +from vsg import vhdlFile +from vsg.rules import library + +sTestDir = os.path.dirname(__file__) + +lFile, eError = vhdlFile.utils.read_vhdlfile(os.path.join(sTestDir, "rule_012_test_input.vhd")) + + +class test_rule(unittest.TestCase): + def setUp(self): + self.oFile = vhdlFile.vhdlFile(lFile) + self.assertIsNone(eError) + + def test_rule_012(self): + oRule = library.rule_012() + self.assertTrue(oRule) + self.assertEqual(oRule.name, "library") + self.assertEqual(oRule.identifier, "012") + self.assertFalse(oRule.fixable) + self.assertTrue(oRule.disable) + self.assertEqual(oRule.groups, ["naming"]) + + lExpected = [] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Library name is on list of restricted names: ", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = [] + oRule.names.append("ieee") + lExpected = [2] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Library name is on list of restricted names: ieee", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["work"] + lExpected = [3] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Library name is on list of restricted names: work", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["std"] + lExpected = [4] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Library name is on list of restricted names: std", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["bad_lib"] + lExpected = [5] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Library name is on list of restricted names: bad_lib", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["ieee", "bad_lib"] + lExpected = [2, 5] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Library name is on list of restricted names: ieee, bad_lib", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["ieee", "work", "std"] + lExpected = [2, 3, 4] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Library name is on list of restricted names: ieee, work, std", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["ieee", "work", "std", "bad_lib"] + lExpected = [2, 3, 4, 5] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Library name is on list of restricted names: ieee, work, std, bad_lib", oRule._get_solution(None)) diff --git a/tests/use_clause/rule_001_test_input.vhd b/tests/use_clause/rule_001_test_input.vhd new file mode 100644 index 000000000..54937ba90 --- /dev/null +++ b/tests/use_clause/rule_001_test_input.vhd @@ -0,0 +1,10 @@ + +library ieee; + use ieee.std_logic_arith.all; + use ieee.std_logic_1164.std_logic; + +library std; + use std.env.all; + +library bad_lib; + use bad_lib.bad_pkg.bad_obj; diff --git a/tests/use_clause/test_rule_001.py b/tests/use_clause/test_rule_001.py new file mode 100644 index 000000000..e27c2bcb3 --- /dev/null +++ b/tests/use_clause/test_rule_001.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- + +import os +import unittest + +from tests import utils +from vsg import vhdlFile +from vsg.rules import use_clause + +sTestDir = os.path.dirname(__file__) + +lFile, eError = vhdlFile.utils.read_vhdlfile(os.path.join(sTestDir, "rule_001_test_input.vhd")) + + +class test_rule(unittest.TestCase): + def setUp(self): + self.oFile = vhdlFile.vhdlFile(lFile) + self.assertIsNone(eError) + + def test_rule_001(self): + oRule = use_clause.rule_001() + self.assertTrue(oRule) + self.assertEqual(oRule.names, ["std_logic_arith"]) + self.assertEqual(oRule.name, "use_clause") + self.assertEqual(oRule.identifier, "001") + self.assertFalse(oRule.fixable) + self.assertFalse(oRule.disable) + self.assertEqual(oRule.groups, ["naming"]) + + lExpected = [] + oRule.names = [] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Package name is on list of restricted names: ", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = [] + oRule.names.append("std_logic_arith") + lExpected = [3] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Package name is on list of restricted names: std_logic_arith", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["std_logic_1164"] + lExpected = [4] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Package name is on list of restricted names: std_logic_1164", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["env"] + lExpected = [7] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Package name is on list of restricted names: env", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["bad_pkg"] + lExpected = [10] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Package name is on list of restricted names: bad_pkg", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["std_logic_arith", "bad_pkg"] + lExpected = [3, 10] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Package name is on list of restricted names: std_logic_arith, bad_pkg", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["std_logic_arith", "std_logic_1164", "env"] + lExpected = [3, 4, 7] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Package name is on list of restricted names: std_logic_arith, std_logic_1164, env", oRule._get_solution(None)) + + oRule.violations = [] + oRule.names = ["std_logic_arith", "std_logic_1164", "env", "bad_pkg"] + lExpected = [3, 4, 7, 10] + oRule.analyze(self.oFile) + self.assertEqual(lExpected, utils.extract_violation_lines_from_violation_object(oRule.violations)) + self.assertEqual("Package name is on list of restricted names: std_logic_arith, std_logic_1164, env, bad_pkg", oRule._get_solution(None)) From fc481f6acf39e8078f70f9c1ed4c86bae045a18d Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Mon, 1 Sep 2025 20:28:38 +0100 Subject: [PATCH 10/14] Issue#1504: Added docs for rules to restrict library and package names. --- docs/configuring_disabled_rules.rst | 3 +-- docs/library_rules.rst | 15 +++++++++++++++ docs/rule_groups/naming_rule_group.rst | 4 ++-- docs/unfixable_rules.rst | 4 ++-- docs/use_clause_rules.rst | 15 +++++++++++++++ 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/docs/configuring_disabled_rules.rst b/docs/configuring_disabled_rules.rst index e68500cfd..3d15b973a 100644 --- a/docs/configuring_disabled_rules.rst +++ b/docs/configuring_disabled_rules.rst @@ -59,10 +59,9 @@ Rules Disabled by Default * `generic_map_601 `_ * `instantiation_600 `_ * `instantiation_601 `_ - * `interface_incomplete_type_declaration_600 <../interface_incomplete_type_declaration_rules.html#interface-incomplete-type-declaration-600>`_ * `interface_incomplete_type_declaration_601 <../interface_incomplete_type_declaration_rules.html#interface-incomplete-type-declaration-601>`_ - +* `library_012 `_ * `loop_statement_006 `_ * `loop_statement_007 `_ * `loop_statement_600 `_ diff --git a/docs/library_rules.rst b/docs/library_rules.rst index b450f3b81..cec4a413f 100644 --- a/docs/library_rules.rst +++ b/docs/library_rules.rst @@ -275,6 +275,21 @@ This rule checks the **use** keyword is on its own line. context c1 is library ieee; use ieee.std_logic_1164.all; end context c1; +library_012 +########### + +|phase_7| |disabled| |error| |unfixable| |naming| + +This rule checks for libraries that have been restricted by the user. + +.. NOTE:: This rule is disabled by default. + +**Violation** + +.. code-block:: vhdl + + library bad_lib; + library_500 ########### diff --git a/docs/rule_groups/naming_rule_group.rst b/docs/rule_groups/naming_rule_group.rst index 246ecd632..3d486c056 100644 --- a/docs/rule_groups/naming_rule_group.rst +++ b/docs/rule_groups/naming_rule_group.rst @@ -24,10 +24,9 @@ Rules Enforcing Naming Rule Group * `generic_map_601 <../generic_map_rules.html#generic-map-601>`_ * `instantiation_600 <../instantiation_rules.html#instantiation-600>`_ * `instantiation_601 <../instantiation_rules.html#instantiation-601>`_ - * `interface_incomplete_type_declaration_600 <../interface_incomplete_type_declaration_rules.html#interface-incomplete-type-declaration-600>`_ * `interface_incomplete_type_declaration_601 <../interface_incomplete_type_declaration_rules.html#interface-incomplete-type-declaration-601>`_ - +* `library_012 <../library_rules.html#library-012>`_ * `loop_statement_600 <../loop_statement_rules.html#loop-statement-600>`_ * `loop_statement_601 <../loop_statement_rules.html#loop-statement-601>`_ * `loop_statement_602 <../loop_statement_rules.html#loop-statement-602>`_ @@ -58,5 +57,6 @@ Rules Enforcing Naming Rule Group * `subtype_600 <../subtype_rules.html#subtype-600>`_ * `type_015 <../type_rules.html#type-015>`_ * `type_600 <../type_rules.html#type-600>`_ +* `use_clause_001 <../use_clause_rules.html#use-clause-001>`_ * `variable_012 <../variable_rules.html#variable-012>`_ * `variable_600 <../variable_rules.html#variable-600>`_ diff --git a/docs/unfixable_rules.rst b/docs/unfixable_rules.rst index 16390e1fc..037d2b1fb 100644 --- a/docs/unfixable_rules.rst +++ b/docs/unfixable_rules.rst @@ -55,6 +55,8 @@ With multiple options available, the user is required to make the decision. * `architecture_025 `_ * `instantiation_036 `_ +* `library_012 <../library_rules.html#library-012>`_ +* `use_clause_001 <../use_clause_rules.html#use-clause-001>`_ Lengths ------- @@ -104,10 +106,8 @@ With multiple options available, the user is required to make the decision. * `generic_map_601 `_ * `instantiation_600 `_ * `instantiation_601 `_ - * `interface_incomplete_type_declaration_600 <../interface_incomplete_type_declaration_rules.html#interface-incomplete-type-declaration-600>`_ * `interface_incomplete_type_declaration_601 <../interface_incomplete_type_declaration_rules.html#interface-incomplete-type-declaration-601>`_ - * `loop_statement_600 `_ * `loop_statement_601 `_ * `loop_statement_602 `_ diff --git a/docs/use_clause_rules.rst b/docs/use_clause_rules.rst index 30eebb2ce..10327241a 100644 --- a/docs/use_clause_rules.rst +++ b/docs/use_clause_rules.rst @@ -3,6 +3,21 @@ Use Clause Rules ---------------- +use_clause_001 +############## + +|phase_7| |error| |unfixable| |naming| + +This rule checks for packages that have been restricted by the user. + +.. NOTE:: This rule is configured to restrict the std_logic_arith package by default. + +**Violation** + +.. code-block:: vhdl + + use ieee.std_logic_arith.all; + use_clause_500 ############## From 2b1edaea933186509cdbc0df47fbe693847c334f Mon Sep 17 00:00:00 2001 From: Jeremiah Leary Date: Wed, 3 Sep 2025 23:06:28 -0500 Subject: [PATCH 11/14] Adding documentation for rule configuration. --- docs/configuring.rst | 1 + ...ary_and_package_name_restriction_rules.rst | 67 +++++++++++++++++++ docs/library_rules.rst | 2 + docs/links.rst | 3 + docs/use_clause_rules.rst | 2 + vsg/rules/library/rule_012.py | 2 + vsg/rules/use_clause/rule_001.py | 2 + 7 files changed, 79 insertions(+) create mode 100644 docs/configuring_library_and_package_name_restriction_rules.rst diff --git a/docs/configuring.rst b/docs/configuring.rst index 631c4905b..5ace76411 100644 --- a/docs/configuring.rst +++ b/docs/configuring.rst @@ -19,6 +19,7 @@ Configuring configuring_indent_rules.rst configuring_keyword_alignment_rules.rst configuring_length_rules.rst + configuring_library_and_package_name_restriction_rules.rst configuring_move_token_rules.rst configuring_multiline_assert_rule.rst configuring_multiline_constraint_rules.rst diff --git a/docs/configuring_library_and_package_name_restriction_rules.rst b/docs/configuring_library_and_package_name_restriction_rules.rst new file mode 100644 index 000000000..9c71d439d --- /dev/null +++ b/docs/configuring_library_and_package_name_restriction_rules.rst @@ -0,0 +1,67 @@ + +.. _configuring-library-and-package-name-restriction-rules: + +Configuring Library and Package Name Restriction Rules +------------------------------------------------------ + +There are rules which will check for invalid package names in library and use clauses. +These rules are disabled by default and must be enabled before they will perform any checks. + +There is one option for these rules: + +.. |names| replace:: + :code:`names` + +.. |default| replace:: + :code:`std_logic_arith` + +.. |values__names| replace:: + List of strings + +.. |action__names| replace:: + Search for libraries and packages with the user defined names + ++--------------------------------------+-----------------+-----------+------------------------------------------------+ +| Option | Values | Default | Description | ++======================================+=================+===========+================================================+ +| |names| | |values__names| | |default| | * |action__names| | ++--------------------------------------+-----------------+-----------+------------------------------------------------+ + +This is an example of how to configure the option. + +.. code-block:: yaml + + rule : + library_012: + names: + - "work" + - "std_logic_arith" + +.. NOTE:: All examples below are using the rule **port_map_004**. + +Example: |names| set to list ["std_logic_arith"] +################################################ + +The following code would fail with this option: + +.. code-Block:: vhdl + + library ieee; + use ieee.std_logic_arith.all; + +Example: |names| set to ["work", "my_package"] +############################################## + +The following code would fail three times with this option: + +.. code-block:: vhdl + + library work; + use work.my_package.all; + + +Rules Enforcing Valid Names +########################### + +* `library_012 `_ +* `use_clause_001 `_ diff --git a/docs/library_rules.rst b/docs/library_rules.rst index cec4a413f..ccdb29f3b 100644 --- a/docs/library_rules.rst +++ b/docs/library_rules.rst @@ -282,6 +282,8 @@ library_012 This rule checks for libraries that have been restricted by the user. +|configuring_library_and_package_name_restriction_rules_link| + .. NOTE:: This rule is disabled by default. **Violation** diff --git a/docs/links.rst b/docs/links.rst index 73dea7f22..a69ca489a 100644 --- a/docs/links.rst +++ b/docs/links.rst @@ -100,3 +100,6 @@ .. |configuring_comment_indenting_link| replace:: Refer to :ref:`configuring-comment-indenting` for configuration options. + +.. |configuring_library_and_package_name_restriction_rules_link| replace:: + Refer to :ref:`configuring-library-and-package-name-restriction-rules` for configuration options. diff --git a/docs/use_clause_rules.rst b/docs/use_clause_rules.rst index 10327241a..74e8299a3 100644 --- a/docs/use_clause_rules.rst +++ b/docs/use_clause_rules.rst @@ -10,6 +10,8 @@ use_clause_001 This rule checks for packages that have been restricted by the user. +|configuring_library_and_package_name_restriction_rules_link| + .. NOTE:: This rule is configured to restrict the std_logic_arith package by default. **Violation** diff --git a/vsg/rules/library/rule_012.py b/vsg/rules/library/rule_012.py index 961de524a..880121d3f 100644 --- a/vsg/rules/library/rule_012.py +++ b/vsg/rules/library/rule_012.py @@ -8,6 +8,8 @@ class rule_012(does_token_value_match_none_of): """ This rule checks for libraries that have been restricted by the user. + |configuring_library_and_package_name_restriction_rules_link| + .. NOTE:: This rule is disabled by default. **Violation** diff --git a/vsg/rules/use_clause/rule_001.py b/vsg/rules/use_clause/rule_001.py index fa0988338..db2b2ae4c 100644 --- a/vsg/rules/use_clause/rule_001.py +++ b/vsg/rules/use_clause/rule_001.py @@ -8,6 +8,8 @@ class rule_001(does_token_value_match_none_of): """ This rule checks for packages that have been restricted by the user. + |configuring_library_and_package_name_restriction_rules_link| + .. NOTE:: This rule is configured to restrict the std_logic_arith package by default. **Violation** From 5cff66181b19dac5ec211625f8912369ea12a062 Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Thu, 4 Sep 2025 08:10:23 +0100 Subject: [PATCH 12/14] Issue#1504: Updated use_clause_001 to be disabled by default to match documentation. --- .../configuring_library_and_package_name_restriction_rules.rst | 2 +- tests/use_clause/test_rule_001.py | 2 +- vsg/rules/use_clause/rule_001.py | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/configuring_library_and_package_name_restriction_rules.rst b/docs/configuring_library_and_package_name_restriction_rules.rst index 9c71d439d..dcb1b1c3f 100644 --- a/docs/configuring_library_and_package_name_restriction_rules.rst +++ b/docs/configuring_library_and_package_name_restriction_rules.rst @@ -37,7 +37,7 @@ This is an example of how to configure the option. - "work" - "std_logic_arith" -.. NOTE:: All examples below are using the rule **port_map_004**. +.. NOTE:: All examples below are using the rule **use_clause_001**. Example: |names| set to list ["std_logic_arith"] ################################################ diff --git a/tests/use_clause/test_rule_001.py b/tests/use_clause/test_rule_001.py index e27c2bcb3..db246709a 100644 --- a/tests/use_clause/test_rule_001.py +++ b/tests/use_clause/test_rule_001.py @@ -24,7 +24,7 @@ def test_rule_001(self): self.assertEqual(oRule.name, "use_clause") self.assertEqual(oRule.identifier, "001") self.assertFalse(oRule.fixable) - self.assertFalse(oRule.disable) + self.assertTrue(oRule.disable) self.assertEqual(oRule.groups, ["naming"]) lExpected = [] diff --git a/vsg/rules/use_clause/rule_001.py b/vsg/rules/use_clause/rule_001.py index db2b2ae4c..d966227cc 100644 --- a/vsg/rules/use_clause/rule_001.py +++ b/vsg/rules/use_clause/rule_001.py @@ -10,6 +10,8 @@ class rule_001(does_token_value_match_none_of): |configuring_library_and_package_name_restriction_rules_link| + .. NOTE:: This rule is disabled by default. + .. NOTE:: This rule is configured to restrict the std_logic_arith package by default. **Violation** @@ -21,7 +23,6 @@ class rule_001(does_token_value_match_none_of): def __init__(self): super().__init__(use_clause.package_name) - self.disable = False self.names = ["std_logic_arith"] def _get_solution(self, iLineNumber): From 48f9b262a404efbf1928fa9a426f33393054d6b7 Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Thu, 4 Sep 2025 08:10:41 +0100 Subject: [PATCH 13/14] Issue#1504: Updated docs for use_clause_001 to be disabled by default to match documentation. --- docs/configuring_disabled_rules.rst | 1 + docs/use_clause_rules.rst | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/configuring_disabled_rules.rst b/docs/configuring_disabled_rules.rst index 3d15b973a..b13647e83 100644 --- a/docs/configuring_disabled_rules.rst +++ b/docs/configuring_disabled_rules.rst @@ -101,6 +101,7 @@ Rules Disabled by Default * `type_100 `_ * `type_200 `_ * `type_600 `_ +* `use_clause_001 `_ * `variable_012 `_ * `variable_100 `_ * `variable_600 `_ diff --git a/docs/use_clause_rules.rst b/docs/use_clause_rules.rst index 74e8299a3..03d1fece8 100644 --- a/docs/use_clause_rules.rst +++ b/docs/use_clause_rules.rst @@ -6,12 +6,14 @@ Use Clause Rules use_clause_001 ############## -|phase_7| |error| |unfixable| |naming| +|phase_7| |disabled| |error| |unfixable| |naming| This rule checks for packages that have been restricted by the user. |configuring_library_and_package_name_restriction_rules_link| +.. NOTE:: This rule is disabled by default. + .. NOTE:: This rule is configured to restrict the std_logic_arith package by default. **Violation** From 607093a96b702ce4cb0f93dfa357ba4c9424aed1 Mon Sep 17 00:00:00 2001 From: JHertz5 Date: Thu, 4 Sep 2025 08:22:47 +0100 Subject: [PATCH 14/14] Issue#1504: Updated docs for new rule type documentation. --- ...figuring_library_and_package_name_restriction_rules.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/configuring_library_and_package_name_restriction_rules.rst b/docs/configuring_library_and_package_name_restriction_rules.rst index dcb1b1c3f..487cd89e9 100644 --- a/docs/configuring_library_and_package_name_restriction_rules.rst +++ b/docs/configuring_library_and_package_name_restriction_rules.rst @@ -13,7 +13,7 @@ There is one option for these rules: :code:`names` .. |default| replace:: - :code:`std_logic_arith` + :code:`std_logic_arith` for **use_clause_001**. None for **library_012** .. |values__names| replace:: List of strings @@ -49,13 +49,16 @@ The following code would fail with this option: library ieee; use ieee.std_logic_arith.all; -Example: |names| set to ["work", "my_package"] +Example: |names| set to ["std_logic_arith", "my_package"] ############################################## The following code would fail three times with this option: .. code-block:: vhdl + library ieee; + use ieee.std_logic_arith.all; + library work; use work.my_package.all;