def _index_name_nodes( index: str, for_node: astroid.For ) -> List[Union[astroid.AssignName, astroid.Name]]: """Return a list of <index> AssignName and Name nodes contained in the body of <for_node>.""" return [ name_node for name_node in for_node.nodes_of_class((astroid.AssignName, astroid.Name)) if name_node.name == index and name_node != for_node.target ]
def visit_for(self, node: astroid.For) -> None: old_curr = self._current_block old_curr.add_statement(node.iter) node.cfg_block = old_curr # Handle "test" block test_block = self._current_cfg.create_block() test_block.add_statement(node.target) self._current_cfg.link_or_merge(old_curr, test_block) after_for_block = self._current_cfg.create_block() # step into for self._control_boundaries.append((node, { astroid.Break.__name__: after_for_block, astroid.Continue.__name__: test_block })) # Handle "body" branch body_block = self._current_cfg.create_block(test_block) self._current_block = body_block for child in node.body: child.accept(self) end_body = self._current_block self._current_cfg.link_or_merge(end_body, test_block) # step out of for self._control_boundaries.pop() # Handle "else" branch else_block = self._current_cfg.create_block(test_block) self._current_block = else_block for child in node.orelse: child.accept(self) end_else = self._current_block self._current_cfg.link_or_merge(end_else, after_for_block) self._current_block = after_for_block
def _index_name_nodes(index: str, for_node: astroid.For) -> List[Union[astroid.AssignName, astroid.Name]]: """Return a list of <index> AssignName and Name nodes contained in the body of <for_node>.""" return [name_node for name_node in for_node.nodes_of_class((astroid.AssignName, astroid.Name)) if name_node.name == index and name_node != for_node.target]
def _check_consider_using_enumerate(self, node: astroid.For) -> None: """Emit a convention whenever range and len are used for indexing.""" # Verify that we have a `range([start], len(...), [stop])` call and # that the object which is iterated is used as a subscript in the # body of the for. # Is it a proper range call? if not isinstance(node.iter, astroid.Call): return if not self._is_builtin(node.iter.func, "range"): return if not node.iter.args: return is_constant_zero = (isinstance(node.iter.args[0], astroid.Const) and node.iter.args[0].value == 0) if len(node.iter.args) == 2 and not is_constant_zero: return if len(node.iter.args) > 2: return # Is it a proper len call? if not isinstance(node.iter.args[-1], astroid.Call): return second_func = node.iter.args[-1].func if not self._is_builtin(second_func, "len"): return len_args = node.iter.args[-1].args if not len_args or len(len_args) != 1: return iterating_object = len_args[0] if isinstance(iterating_object, astroid.Name): expected_subscript_val_type = astroid.Name elif isinstance(iterating_object, astroid.Attribute): expected_subscript_val_type = astroid.Attribute else: return # If we're defining __iter__ on self, enumerate won't work scope = node.scope() if (isinstance(iterating_object, astroid.Name) and iterating_object.name == "self" and scope.name == "__iter__"): return # Verify that the body of the for loop uses a subscript # with the object that was iterated. This uses some heuristics # in order to make sure that the same object is used in the # for body. for child in node.body: for subscript in child.nodes_of_class(astroid.Subscript): subscript = cast(astroid.Subscript, subscript) if not isinstance(subscript.value, expected_subscript_val_type): continue value = subscript.slice if not isinstance(value, astroid.Name): continue if subscript.value.scope() != node.scope(): # Ignore this subscript if it's not in the same # scope. This means that in the body of the for # loop, another scope was created, where the same # name for the iterating object was used. continue if value.name == node.target.name and ( isinstance(subscript.value, astroid.Name) and iterating_object.name == subscript.value.name or isinstance(subscript.value, astroid.Attribute) and iterating_object.attrname == subscript.value.attrname): self.add_message("consider-using-enumerate", node=node) return