diff --git a/ChangeLog b/ChangeLog index 0cd167d4c5..79f49e6931 100644 --- a/ChangeLog +++ b/ChangeLog @@ -34,6 +34,10 @@ Release date: TBA * Fix bug where specifically enabling just ``await-outside-async`` was not possible. +* Added new message called ``duplicate-value`` which identifies duplicate values inside sets. + + Closes #5880 + .. Insert your changelog randomly, it will reduce merge conflicts (Ie. not necessarily at the end) diff --git a/doc/data/messages/d/duplicate-value/bad.py b/doc/data/messages/d/duplicate-value/bad.py new file mode 100644 index 0000000000..bf6f8ad005 --- /dev/null +++ b/doc/data/messages/d/duplicate-value/bad.py @@ -0,0 +1 @@ +incorrect_set = {'value1', 23, 5, 'value1'} # [duplicate-value] diff --git a/doc/data/messages/d/duplicate-value/good.py b/doc/data/messages/d/duplicate-value/good.py new file mode 100644 index 0000000000..c30cd78213 --- /dev/null +++ b/doc/data/messages/d/duplicate-value/good.py @@ -0,0 +1 @@ +correct_set = {'value1', 23, 5} diff --git a/doc/whatsnew/2.14.rst b/doc/whatsnew/2.14.rst index ecb7388def..d81d64662c 100644 --- a/doc/whatsnew/2.14.rst +++ b/doc/whatsnew/2.14.rst @@ -27,6 +27,10 @@ New checkers Closes #4525 +* Added new message called ``duplicate-value`` which identifies duplicate values inside sets. + + Closes #5880 + Removed checkers ================ diff --git a/pylint/checkers/base/basic_checker.py b/pylint/checkers/base/basic_checker.py index 1e5a2a15bb..55ce39d900 100644 --- a/pylint/checkers/base/basic_checker.py +++ b/pylint/checkers/base/basic_checker.py @@ -15,7 +15,7 @@ from pylint import interfaces from pylint import utils as lint_utils from pylint.checkers import BaseChecker, utils -from pylint.interfaces import IAstroidChecker +from pylint.interfaces import HIGH, IAstroidChecker from pylint.reporters.ureports import nodes as reporter_nodes from pylint.utils import LinterStats from pylint.utils.utils import get_global_option @@ -246,6 +246,11 @@ class BasicChecker(_BasicChecker): "Used when an assert statement has a string literal as its first argument, which will " "cause the assert to always pass.", ), + "W0130": ( + "Duplicate value %r in set", + "duplicate-value", + "This message is emitted when a set contains the same value two or more times.", + ), } reports = (("RP0101", "Statistics by type", report_by_type_stats),) @@ -637,6 +642,21 @@ def visit_dict(self, node: nodes.Dict) -> None: self.add_message("duplicate-key", node=node, args=key) keys.add(key) + @utils.check_messages("duplicate-value") + def visit_set(self, node: nodes.Set) -> None: + """Check duplicate value in set.""" + values = set() + for v in node.elts: + if isinstance(v, nodes.Const): + value = v.value + else: + continue + if value in values: + self.add_message( + "duplicate-value", node=node, args=value, confidence=HIGH + ) + values.add(value) + def visit_tryfinally(self, node: nodes.TryFinally) -> None: """Update try...finally flag.""" self._tryfinallys.append(node) diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index 70c56a621a..0bea56f8a0 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -673,10 +673,8 @@ def is_line_length_check_activated(pylint_pattern_match_object) -> bool: def specific_splitlines(lines: str) -> List[str]: """Split lines according to universal newlines except those in a specific sets.""" unsplit_ends = { - "\v", - "\x0b", - "\f", - "\x0c", + "\x0b", # synonym of \v + "\x0c", # synonym of \f "\x1c", "\x1d", "\x1e", diff --git a/tests/functional/d/duplicate_value.py b/tests/functional/d/duplicate_value.py new file mode 100644 index 0000000000..291353c862 --- /dev/null +++ b/tests/functional/d/duplicate_value.py @@ -0,0 +1,18 @@ +# pylint: disable = invalid-name, line-too-long +"""Simple test sets for checking duplicate values""" + +set1 = {1, 2, 3, 4} +set2 = {1, 1, 2} # [duplicate-value] +set3 = {1, 2, 2} # [duplicate-value] + +set4 = {'one', 'two', 'three'} +set5 = {'one', 'two', 'one'} # [duplicate-value] +set6 = {'one', 'two', 'two'} # [duplicate-value] + +wrong_set = {12, 23, True, 6, True, 0, 12} # [duplicate-value, duplicate-value] +correct_set = {12, 13, 23, 24, 89} + +wrong_set_mixed = {1, 2, 'value', 1} # [duplicate-value] +wrong_set = {'arg1', 'arg2', False, 'arg1', True} # [duplicate-value] + +another_wrong_set = {2, 3, 'arg1', True, 'arg1', False, True} # [duplicate-value, duplicate-value] diff --git a/tests/functional/d/duplicate_value.txt b/tests/functional/d/duplicate_value.txt new file mode 100644 index 0000000000..25c154086e --- /dev/null +++ b/tests/functional/d/duplicate_value.txt @@ -0,0 +1,10 @@ +duplicate-value:5:7:5:16::Duplicate value 1 in set:HIGH +duplicate-value:6:7:6:16::Duplicate value 2 in set:HIGH +duplicate-value:9:7:9:28::Duplicate value 'one' in set:HIGH +duplicate-value:10:7:10:28::Duplicate value 'two' in set:HIGH +duplicate-value:12:12:12:42::Duplicate value 12 in set:HIGH +duplicate-value:12:12:12:42::Duplicate value True in set:HIGH +duplicate-value:15:18:15:36::Duplicate value 1 in set:HIGH +duplicate-value:16:12:16:49::Duplicate value 'arg1' in set:HIGH +duplicate-value:18:20:18:61::Duplicate value 'arg1' in set:HIGH +duplicate-value:18:20:18:61::Duplicate value True in set:HIGH