From eb300be351fd7a9f7a8e7340fb44e21865b87d61 Mon Sep 17 00:00:00 2001 From: George Date: Tue, 9 May 2023 18:34:25 +0100 Subject: [PATCH] Add B033: Duplicate items in sets (#373) * Add B033: Duplicate items in sets * Simplify by checking lengths --------- Co-authored-by: Cooper Lees --- README.rst | 3 +++ bugbear.py | 19 +++++++++++++++++++ tests/b033.py | 17 +++++++++++++++++ tests/test_bugbear.py | 16 ++++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 tests/b033.py diff --git a/README.rst b/README.rst index 224fcaf..a299937 100644 --- a/README.rst +++ b/README.rst @@ -186,6 +186,8 @@ second usage. Save the result to a list if the result is needed multiple times. **B032**: Possible unintentional type annotation (using ``:``). Did you mean to assign (using ``=``)? +**B033**: Sets should not contain duplicate items. Duplicate items will be replaced with a single item at runtime. + Opinionated warnings ~~~~~~~~~~~~~~~~~~~~ @@ -337,6 +339,7 @@ Unreleased * flake8-bugbear is now >= 3.8.1 project like flake8>=6.0.0 * This has allowed some more modern AST usage cleanup and less CI running etc. * B030: Fix crash on certain unusual except handlers (e.g. ``except a[0].b:``) +* Add B033: Check for duplicate items in sets. 23.3.12 ~~~~~~~~ diff --git a/bugbear.py b/bugbear.py index c108931..d14e21d 100644 --- a/bugbear.py +++ b/bugbear.py @@ -527,6 +527,10 @@ def visit_Import(self, node): self.check_for_b005(node) self.generic_visit(node) + def visit_Set(self, node): + self.check_for_b033(node) + self.generic_visit(node) + def check_for_b005(self, node): if isinstance(node, ast.Import): for name in node.names: @@ -1346,6 +1350,14 @@ def check_for_b032(self, node): ): self.errors.append(B032(node.lineno, node.col_offset)) + def check_for_b033(self, node): + constants = [ + item.value + for item in filter(lambda x: isinstance(x, ast.Constant), node.elts) + ] + if len(constants) != len(set(constants)): + self.errors.append(B033(node.lineno, node.col_offset)) + def compose_call_path(node): if isinstance(node, ast.Attribute): @@ -1743,6 +1755,13 @@ def visit_Lambda(self, node): ) ) +B033 = Error( + message=( + "B033 Sets should not contain duplicate items. Duplicate items will be replaced" + " with a single item at runtime." + ) +) + # Warnings disabled by default. B901 = Error( message=( diff --git a/tests/b033.py b/tests/b033.py new file mode 100644 index 0000000..4738344 --- /dev/null +++ b/tests/b033.py @@ -0,0 +1,17 @@ +""" +Should emit: +B033 - on lines 6-12 +""" + +test = {1, 2, 3, 3, 5} +test = {"a", "b", "c", "c", "e"} +test = {True, False, True} +test = {None, True, None} +test = {3, 3.0} +test = {1, True} +test = {0, False} + +test = {1, 2, 3, 3.5, 5} +test = {"a", "b", "c", "d", "e"} +test = {True, False} +test = {None} diff --git a/tests/test_bugbear.py b/tests/test_bugbear.py index 045f88d..73e7b19 100644 --- a/tests/test_bugbear.py +++ b/tests/test_bugbear.py @@ -44,6 +44,7 @@ B030, B031, B032, + B033, B901, B902, B903, @@ -487,6 +488,21 @@ def test_b032(self): ) self.assertEqual(errors, expected) + def test_b033(self): + filename = Path(__file__).absolute().parent / "b033.py" + bbc = BugBearChecker(filename=str(filename)) + errors = list(bbc.run()) + expected = self.errors( + B033(6, 7), + B033(7, 7), + B033(8, 7), + B033(9, 7), + B033(10, 7), + B033(11, 7), + B033(12, 7), + ) + self.assertEqual(errors, expected) + def test_b908(self): filename = Path(__file__).absolute().parent / "b908.py" bbc = BugBearChecker(filename=str(filename))