diff options
-rw-r--r-- | pylint/checkers/base.py | 2 | ||||
-rw-r--r-- | pylint/checkers/classes.py | 8 | ||||
-rw-r--r-- | pylint/checkers/format.py | 4 | ||||
-rw-r--r-- | pylint/checkers/typecheck.py | 11 | ||||
-rw-r--r-- | pylint/checkers/utils.py | 2 | ||||
-rw-r--r-- | pylint/checkers/variables.py | 51 | ||||
-rw-r--r-- | tests/functional/r/regression_02/regression_node_statement.py | 18 |
7 files changed, 64 insertions, 32 deletions
diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 0206b1202..9d61cf10f 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -606,7 +606,7 @@ class BasicErrorChecker(_BasicChecker): # PEP 448 unpacking. return - stmt = node.statement() + stmt = node.statement(future=True) if not isinstance(stmt, nodes.Assign): return diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 478197b0f..da9bf074f 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -1052,7 +1052,9 @@ a metaclass class method.", nodes_lst = [ n for n in nodes_lst - if not isinstance(n.statement(), (nodes.Delete, nodes.AugAssign)) + if not isinstance( + n.statement(future=True), (nodes.Delete, nodes.AugAssign) + ) and n.root() is current_module ] if not nodes_lst: @@ -1651,7 +1653,7 @@ a metaclass class method.", # class A: # b = property(lambda: self._b) - stmt = node.parent.statement() + stmt = node.parent.statement(future=True) if ( isinstance(stmt, nodes.Assign) and len(stmt.targets) == 1 @@ -1796,7 +1798,7 @@ a metaclass class method.", _node.frame() is frame and _node.fromlineno < lno and not astroid.are_exclusive( - _node.statement(), defstmt, excs + _node.statement(future=True), defstmt, excs ) ): self.add_message( diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index cdbacd4cf..0c0cb8752 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -602,8 +602,10 @@ class FormatChecker(BaseTokenChecker): isinstance(node.parent, nodes.TryFinally) and node in node.parent.finalbody ): prev_line = node.parent.body[0].tolineno + 1 + elif isinstance(node.parent, nodes.Module): + prev_line = 0 else: - prev_line = node.parent.statement().fromlineno + prev_line = node.parent.statement(future=True).fromlineno line = node.fromlineno assert line, node if prev_line == line and self._visited_lines.get(line) != 2: diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index c5203adf7..df01cc2fe 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -72,6 +72,7 @@ from functools import singledispatch from typing import Any, Callable, Iterator, List, Optional, Pattern, Tuple import astroid +import astroid.exceptions from astroid import bases, nodes from pylint.checkers import BaseChecker, utils @@ -615,7 +616,7 @@ def _has_parent_of_type(node, node_type, statement): def _no_context_variadic_keywords(node, scope): - statement = node.statement() + statement = node.statement(future=True) variadics = () if isinstance(scope, nodes.Lambda) and not isinstance(scope, nodes.FunctionDef): @@ -649,7 +650,7 @@ def _no_context_variadic(node, variadic_name, variadic_type, variadics): is_in_lambda_scope = not isinstance(scope, nodes.FunctionDef) and isinstance( scope, nodes.Lambda ) - statement = node.statement() + statement = node.statement(future=True) for name in statement.nodes_of_class(nodes.Name): if name.name != variadic_name: continue @@ -667,7 +668,7 @@ def _no_context_variadic(node, variadic_name, variadic_type, variadics): # so we need to go the lambda instead inferred_statement = inferred.parent.parent else: - inferred_statement = inferred.statement() + inferred_statement = inferred.statement(future=True) if not length and isinstance(inferred_statement, nodes.Lambda): is_in_starred_context = _has_parent_of_type(node, variadic_type, statement) @@ -1029,10 +1030,12 @@ accessed. Python regular expressions are accepted.", if not [ n for n in owner.getattr(node.attrname) - if not isinstance(n.statement(), nodes.AugAssign) + if not isinstance(n.statement(future=True), nodes.AugAssign) ]: missingattr.add((owner, name)) continue + except astroid.exceptions.StatementMissing: + continue except AttributeError: continue except astroid.DuplicateBasesError: diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index d71669729..1f85399a7 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -401,7 +401,7 @@ def is_defined_before(var_node: nodes.Name) -> bool: if is_defined_in_scope(var_node, varname, parent): return True # possibly multiple statements on the same line using semi colon separator - stmt = var_node.statement() + stmt = var_node.statement(future=True) _node = stmt.previous_sibling() lineno = stmt.fromlineno while _node and _node.fromlineno == lineno: diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 86b7eb669..a1cee36a0 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -1110,7 +1110,7 @@ class VariablesChecker(BaseChecker): It's important that all 'Name' nodes are visited, otherwise the 'NamesConsumers' won't be correct. """ - stmt = node.statement() + stmt = node.statement(future=True) if stmt.fromlineno is None: # name node from an astroid built from live code, skip assert not stmt.root().file.endswith(".py") @@ -1261,7 +1261,7 @@ class VariablesChecker(BaseChecker): return (VariableVisitConsumerAction.CONSUME, found_nodes) defnode = utils.assign_parent(found_nodes[0]) - defstmt = defnode.statement() + defstmt = defnode.statement(future=True) defframe = defstmt.frame() # The class reuses itself in the class scope. @@ -1515,7 +1515,10 @@ class VariablesChecker(BaseChecker): @staticmethod def _defined_in_function_definition(node, frame): in_annotation_or_default_or_decorator = False - if isinstance(frame, nodes.FunctionDef) and node.statement() is frame: + if ( + isinstance(frame, nodes.FunctionDef) + and node.statement(future=True) is frame + ): in_annotation_or_default_or_decorator = ( ( node in frame.args.annotations @@ -1563,8 +1566,8 @@ class VariablesChecker(BaseChecker): def _is_variable_violation( node: nodes.Name, defnode, - stmt, - defstmt, + stmt: nodes.Statement, + defstmt: nodes.Statement, frame, # scope of statement of node defframe, base_scope_type, @@ -1753,12 +1756,8 @@ class VariablesChecker(BaseChecker): return maybe_before_assign, annotation_return, use_outer_definition - # pylint: disable-next=fixme - # TODO: The typing of `NodeNG.statement()` in astroid is non-specific - # After this has been updated the typing of `defstmt` should reflect this - # See: https://github.com/PyCQA/astroid/pull/1217 @staticmethod - def _is_only_type_assignment(node: nodes.Name, defstmt: nodes.NodeNG) -> bool: + def _is_only_type_assignment(node: nodes.Name, defstmt: nodes.Statement) -> bool: """Check if variable only gets assigned a type and never a value""" if not isinstance(defstmt, nodes.AnnAssign) or defstmt.value: return False @@ -1866,7 +1865,7 @@ class VariablesChecker(BaseChecker): # ... name = node.name - frame = node.statement().scope() + frame = node.statement(future=True).scope() in_annotation_or_default_or_decorator = self._defined_in_function_definition( node, frame ) @@ -1890,29 +1889,36 @@ class VariablesChecker(BaseChecker): # the variable is not defined. scope = node.scope() if isinstance(scope, nodes.FunctionDef) and any( - asmt.statement().parent_of(scope) for asmt in astmts + asmt.scope().parent_of(scope) for asmt in astmts ): return - - # filter variables according their respective scope test is_statement - # and parent to avoid #74747. This is not a total fix, which would + # Filter variables according to their respective scope. Test parent + # and statement to avoid #74747. This is not a total fix, which would # introduce a mechanism similar to special attribute lookup in # modules. Also, in order to get correct inference in this case, the # scope lookup rules would need to be changed to return the initial # assignment (which does not exist in code per se) as well as any later # modifications. + # pylint: disable-next=too-many-boolean-expressions if ( not astmts - or (astmts[0].is_statement or astmts[0].parent) - and astmts[0].statement().parent_of(node) + or ( + astmts[0].parent == astmts[0].root() + and astmts[0].parent.parent_of(node) + ) + or ( + astmts[0].is_statement + or not isinstance(astmts[0].parent, nodes.Module) + and astmts[0].statement(future=True).parent_of(node) + ) ): _astmts = [] else: _astmts = astmts[:1] for i, stmt in enumerate(astmts[1:]): - if astmts[i].statement().parent_of(stmt) and not in_for_else_branch( - astmts[i].statement(), stmt - ): + if astmts[i].statement(future=True).parent_of( + stmt + ) and not in_for_else_branch(astmts[i].statement(future=True), stmt): continue _astmts.append(stmt) astmts = _astmts @@ -1922,7 +1928,7 @@ class VariablesChecker(BaseChecker): assign = astmts[0].assign_type() if not ( isinstance(assign, (nodes.For, nodes.Comprehension, nodes.GeneratorExp)) - and assign.statement() is not node.statement() + and assign.statement(future=True) is not node.statement(future=True) ): return @@ -2136,7 +2142,8 @@ class VariablesChecker(BaseChecker): maybe_for and maybe_for.parent_of(node_scope) and not utils.is_being_called(node_scope) - and not isinstance(node_scope.statement(), nodes.Return) + and node_scope.parent + and not isinstance(node_scope.statement(future=True), nodes.Return) ): self.add_message("cell-var-from-loop", node=node, args=node.name) diff --git a/tests/functional/r/regression_02/regression_node_statement.py b/tests/functional/r/regression_02/regression_node_statement.py new file mode 100644 index 000000000..bd982480b --- /dev/null +++ b/tests/functional/r/regression_02/regression_node_statement.py @@ -0,0 +1,18 @@ +"""Test to see we don't crash on this code in pandas. +See: https://github.com/pandas-dev/pandas/blob/master/pandas/core/arrays/sparse/array.py +Code written by Guido van Rossum here: https://github.com/python/typing/issues/684""" +# pylint: disable=no-member, redefined-builtin, invalid-name, missing-class-docstring + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from enum import Enum + + class ellipsis(Enum): + Ellipsis = "..." + + Ellipsis = ellipsis.Ellipsis + + +else: + ellipsis = type(Ellipsis) |