@@ -962,13 +962,18 @@ def _get_names_from_tuple(self, node: ast.Tuple):
962
962
elif isinstance (dim , ast .Tuple ):
963
963
yield from self ._get_names_from_tuple (dim )
964
964
965
- def _get_dict_comp_loop_var_names (self , node : ast .DictComp ):
965
+ def _get_dict_comp_loop_and_named_expr_var_names (self , node : ast .DictComp ):
966
+ finder = NamedExprFinder ()
966
967
for gen in node .generators :
967
968
if isinstance (gen .target , ast .Name ):
968
969
yield gen .target .id
969
970
elif isinstance (gen .target , ast .Tuple ):
970
971
yield from self ._get_names_from_tuple (gen .target )
971
972
973
+ finder .visit (gen .ifs )
974
+
975
+ yield from finder .names .keys ()
976
+
972
977
def check_for_b035 (self , node : ast .DictComp ):
973
978
"""Check that a static key isn't used in a dict comprehension.
974
979
@@ -980,7 +985,9 @@ def check_for_b035(self, node: ast.DictComp):
980
985
B035 (node .key .lineno , node .key .col_offset , vars = (node .key .value ,))
981
986
)
982
987
elif isinstance (node .key , ast .Name ):
983
- if node .key .id not in self ._get_dict_comp_loop_var_names (node ):
988
+ if node .key .id not in self ._get_dict_comp_loop_and_named_expr_var_names (
989
+ node
990
+ ):
984
991
self .errors .append (
985
992
B035 (node .key .lineno , node .key .col_offset , vars = (node .key .id ,))
986
993
)
@@ -1539,6 +1546,30 @@ def visit(self, node):
1539
1546
return node
1540
1547
1541
1548
1549
+ @attr .s
1550
+ class NamedExprFinder (ast .NodeVisitor ):
1551
+ """Finds names defined through an ast.NamedExpr.
1552
+
1553
+ After `.visit(node)` is called, `found` is a dict with all name nodes inside,
1554
+ key is name string, value is the node (useful for location purposes).
1555
+ """
1556
+
1557
+ names : Dict [str , List [ast .Name ]] = attr .ib (default = attr .Factory (dict ))
1558
+
1559
+ def visit_NamedExpr (self , node : ast .NamedExpr ):
1560
+ self .names .setdefault (node .target .id , []).append (node .target )
1561
+ self .generic_visit (node )
1562
+
1563
+ def visit (self , node ):
1564
+ """Like super-visit but supports iteration over lists."""
1565
+ if not isinstance (node , list ):
1566
+ return super ().visit (node )
1567
+
1568
+ for elem in node :
1569
+ super ().visit (elem )
1570
+ return node
1571
+
1572
+
1542
1573
class FuntionDefDefaultsVisitor (ast .NodeVisitor ):
1543
1574
def __init__ (self , b008_extend_immutable_calls = None ):
1544
1575
self .b008_extend_immutable_calls = b008_extend_immutable_calls or set ()
0 commit comments