Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ It is therefore recommended to use a stacklevel of 2 or greater to provide more
**B031**: Using the generator returned from `itertools.groupby()` more than once will do nothing on the
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 ``=``)?

Opinionated warnings
~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -333,6 +335,7 @@ Future
* B016: Warn when raising f-strings.
* Add B028: Check for an explicit stacklevel keyword argument on the warn method from the warnings module.
* Add B029: Check when trying to use ``except`` with an empty tuple i.e. ``except: ()``.
* Add B032: Check for possible unintentional type annotations instead of assignments.

23.1.20
~~~~~~~~~
Expand Down
26 changes: 26 additions & 0 deletions bugbear.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,10 @@ def visit_JoinedStr(self, node):
self.check_for_b907(node)
self.generic_visit(node)

def visit_AnnAssign(self, node):
self.check_for_b032(node)
self.generic_visit(node)

def check_for_b005(self, node):
if node.func.attr not in B005.methods:
return # method name doesn't match
Expand Down Expand Up @@ -1235,6 +1239,21 @@ def check_for_b028(self, node):
):
self.errors.append(B028(node.lineno, node.col_offset))

def check_for_b032(self, node):
if (
node.value is None
and hasattr(node.target, "value")
and isinstance(node.target.value, ast.Name)
and (
isinstance(node.target, ast.Subscript)
or (
isinstance(node.target, ast.Attribute)
and node.target.value.id != "self"
)
)
):
self.errors.append(B032(node.lineno, node.col_offset))


def compose_call_path(node):
if isinstance(node, ast.Attribute):
Expand Down Expand Up @@ -1625,6 +1644,13 @@ def visit_Lambda(self, node):
)
)

B032 = Error(
message=(
"B032 Possible unintentional type annotation (using `:`). Did you mean to"
" assign (using `=`)?"
)
)

# Warnings disabled by default.
B901 = Error(
message=(
Expand Down
29 changes: 29 additions & 0 deletions tests/b032.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
Should emit:
B032 - on lines 9, 10, 12, 13, 16-19
"""

# Flag these
dct = {"a": 1}

dct["b"]: 2
dct.b: 2

dct["b"]: "test"
dct.b: "test"

test = "test"
dct["b"]: test
dct["b"]: test.lower()
dct.b: test
dct.b: test.lower()

# Do not flag below
typed_dct: dict[str, int] = {"a": 1}
typed_dct["b"] = 2
typed_dct.b = 2


class TestClass:
def test_self(self):
self.test: int
17 changes: 17 additions & 0 deletions tests/test_bugbear.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
B029,
B030,
B031,
B032,
B901,
B902,
B903,
Expand Down Expand Up @@ -471,6 +472,22 @@ def test_b031(self):
)
self.assertEqual(errors, expected)

def test_b032(self):
filename = Path(__file__).absolute().parent / "b032.py"
bbc = BugBearChecker(filename=str(filename))
errors = list(bbc.run())
expected = self.errors(
B032(9, 0),
B032(10, 0),
B032(12, 0),
B032(13, 0),
B032(16, 0),
B032(17, 0),
B032(18, 0),
B032(19, 0),
)
self.assertEqual(errors, expected)

@unittest.skipIf(sys.version_info < (3, 8), "not implemented for <3.8")
def test_b907(self):
filename = Path(__file__).absolute().parent / "b907.py"
Expand Down