From 996ef47341f4972a2e4fa534973de9fe95135bda Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sat, 20 Aug 2022 14:27:27 -0400 Subject: [PATCH 1/2] Fix `undefined-loop-variable` from walrus in comprehension test --- doc/whatsnew/fragments/7222.bugfix | 3 +++ pylint/checkers/variables.py | 16 ++++++++++++++++ .../u/undefined/undefined_loop_variable_py38.py | 8 ++++++++ .../u/undefined/undefined_loop_variable_py38.rc | 2 ++ 4 files changed, 29 insertions(+) create mode 100644 doc/whatsnew/fragments/7222.bugfix create mode 100644 tests/functional/u/undefined/undefined_loop_variable_py38.py create mode 100644 tests/functional/u/undefined/undefined_loop_variable_py38.rc diff --git a/doc/whatsnew/fragments/7222.bugfix b/doc/whatsnew/fragments/7222.bugfix new file mode 100644 index 0000000000..0d18e73da4 --- /dev/null +++ b/doc/whatsnew/fragments/7222.bugfix @@ -0,0 +1,3 @@ +Fix `undefined-loop-variable` from walrus in comprehension test. + +Closes #7222 diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index e6af22689b..27d3d7b17e 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -2249,6 +2249,22 @@ def _loopvar_name(self, node: astroid.Name) -> None: ): return + maybe_walrus = utils.get_node_first_ancestor_of_type(node, nodes.NamedExpr) + if maybe_walrus: + maybe_comprehension = utils.get_node_first_ancestor_of_type( + maybe_walrus, nodes.Comprehension + ) + if maybe_comprehension: + comprehension_scope = utils.get_node_first_ancestor_of_type( + maybe_comprehension, nodes.ComprehensionScope + ) + assert comprehension_scope is not None + if ( + comprehension_scope.parent.scope() is scope + and node.name in comprehension_scope.locals + ): + return + # For functions we can do more by inferring the length of the itered object try: inferred = next(assign.iter.infer()) diff --git a/tests/functional/u/undefined/undefined_loop_variable_py38.py b/tests/functional/u/undefined/undefined_loop_variable_py38.py new file mode 100644 index 0000000000..5778df7d29 --- /dev/null +++ b/tests/functional/u/undefined/undefined_loop_variable_py38.py @@ -0,0 +1,8 @@ +"""Tests for undefined-loop-variable with assignment expressions""" + + +def walrus_in_comprehension_test(container): + """https://github.com/PyCQA/pylint/issues/7222""" + for something in container: + print(something) + print([my_test for something in container if (my_test := something)]) diff --git a/tests/functional/u/undefined/undefined_loop_variable_py38.rc b/tests/functional/u/undefined/undefined_loop_variable_py38.rc new file mode 100644 index 0000000000..85fc502b37 --- /dev/null +++ b/tests/functional/u/undefined/undefined_loop_variable_py38.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.8 From 2c10bbe2413949b031696212a02de0c8aa53e9b0 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sat, 20 Aug 2022 15:55:49 -0400 Subject: [PATCH 2/2] Add safety --- pylint/checkers/variables.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 27d3d7b17e..3249a4b361 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -2258,8 +2258,10 @@ def _loopvar_name(self, node: astroid.Name) -> None: comprehension_scope = utils.get_node_first_ancestor_of_type( maybe_comprehension, nodes.ComprehensionScope ) - assert comprehension_scope is not None - if ( + if comprehension_scope is None: + # Should not be possible. + pass + elif ( comprehension_scope.parent.scope() is scope and node.name in comprehension_scope.locals ):