summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Walls <jacobtylerwalls@gmail.com>2022-06-12 08:49:10 -0400
committerGitHub <noreply@github.com>2022-06-12 08:49:10 -0400
commit398a8292a29b5dbd7c015f510136c663a25f9596 (patch)
tree0ece6c45727c4c09dc07b5be46befd3bf3e8f160
parentd4d8c0febd897615322cc4a7a216550f065e0e3f (diff)
downloadpylint-git-398a8292a29b5dbd7c015f510136c663a25f9596.tar.gz
Warn about self-referencing named expressions lacking prior assignments (#6915)
-rw-r--r--doc/whatsnew/2/2.15/index.rst5
-rw-r--r--pylint/checkers/variables.py20
-rw-r--r--tests/functional/u/undefined/undefined_variable_py38.py8
-rw-r--r--tests/functional/u/undefined/undefined_variable_py38.txt16
4 files changed, 37 insertions, 12 deletions
diff --git a/doc/whatsnew/2/2.15/index.rst b/doc/whatsnew/2/2.15/index.rst
index cac82dd23..0f284c81c 100644
--- a/doc/whatsnew/2/2.15/index.rst
+++ b/doc/whatsnew/2/2.15/index.rst
@@ -36,6 +36,11 @@ False negatives fixed
Closes #6648
+* Emit ``used-before-assignment`` for self-referencing named expressions (``:=``) lacking
+ prior assignments.
+
+ Closes #5653
+
* Emit ``using-constant-test`` when testing the truth value of a call that only returns generators.
Closes #6909
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index d03a74ca2..9389a6982 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -1973,10 +1973,14 @@ class VariablesChecker(BaseChecker):
)
)
):
- # Expressions, with assignment expressions
- # Use only after assignment
- # b = (c := 2) and c
- maybe_before_assign = False
+ # Relation of a name to the same name in a named expression
+ # Could be used before assignment if self-referencing:
+ # (b := b)
+ # Otherwise, safe if used after assignment:
+ # (b := 2) and b
+ maybe_before_assign = any(
+ anc is defnode.value for anc in node.node_ancestors()
+ )
# Look for type checking definitions inside a type checking guard.
if isinstance(defstmt, (nodes.Import, nodes.ImportFrom)):
@@ -2076,6 +2080,14 @@ class VariablesChecker(BaseChecker):
if (
not isinstance(ref_node.parent, nodes.AnnAssign)
or ref_node.parent.value
+ ) and not (
+ # EXCEPTION: will not have a value if a self-referencing named expression
+ # var: int
+ # if (var := var * var) <-- "var" still undefined
+ isinstance(ref_node.parent, nodes.NamedExpr)
+ and any(
+ anc is ref_node.parent.value for anc in node.node_ancestors()
+ )
):
return False
parent = parent_scope.parent
diff --git a/tests/functional/u/undefined/undefined_variable_py38.py b/tests/functional/u/undefined/undefined_variable_py38.py
index 93ba1eb2b..7a6aed295 100644
--- a/tests/functional/u/undefined/undefined_variable_py38.py
+++ b/tests/functional/u/undefined/undefined_variable_py38.py
@@ -14,7 +14,13 @@ def typing_and_assignment_expression():
def typing_and_self_referencing_assignment_expression():
"""The variable gets assigned in an assignment expression that references itself"""
var: int
- if (var := var ** 2): # false negative: https://github.com/PyCQA/pylint/issues/5653
+ if (var := var ** 2): # [used-before-assignment]
+ print(var)
+
+
+def self_referencing_assignment_expression():
+ """An invalid self-referencing assignment expression"""
+ if (var := var()): # [used-before-assignment]
print(var)
diff --git a/tests/functional/u/undefined/undefined_variable_py38.txt b/tests/functional/u/undefined/undefined_variable_py38.txt
index e912cbbf9..cc438c0d7 100644
--- a/tests/functional/u/undefined/undefined_variable_py38.txt
+++ b/tests/functional/u/undefined/undefined_variable_py38.txt
@@ -1,7 +1,9 @@
-undefined-variable:42:6:42:16::Undefined variable 'no_default':UNDEFINED
-undefined-variable:50:6:50:22::Undefined variable 'again_no_default':UNDEFINED
-undefined-variable:76:6:76:19::Undefined variable 'else_assign_1':INFERENCE
-undefined-variable:99:6:99:19::Undefined variable 'else_assign_2':INFERENCE
-used-before-assignment:134:10:134:16:type_annotation_used_improperly_after_comprehension:Using variable 'my_int' before assignment:HIGH
-used-before-assignment:141:10:141:16:type_annotation_used_improperly_after_comprehension_2:Using variable 'my_int' before assignment:HIGH
-used-before-assignment:171:12:171:16:expression_in_ternary_operator_inside_container_wrong_position:Using variable 'val3' before assignment:HIGH
+used-before-assignment:17:15:17:18:typing_and_self_referencing_assignment_expression:Using variable 'var' before assignment:HIGH
+used-before-assignment:23:15:23:18:self_referencing_assignment_expression:Using variable 'var' before assignment:HIGH
+undefined-variable:48:6:48:16::Undefined variable 'no_default':UNDEFINED
+undefined-variable:56:6:56:22::Undefined variable 'again_no_default':UNDEFINED
+undefined-variable:82:6:82:19::Undefined variable 'else_assign_1':INFERENCE
+undefined-variable:105:6:105:19::Undefined variable 'else_assign_2':INFERENCE
+used-before-assignment:140:10:140:16:type_annotation_used_improperly_after_comprehension:Using variable 'my_int' before assignment:HIGH
+used-before-assignment:147:10:147:16:type_annotation_used_improperly_after_comprehension_2:Using variable 'my_int' before assignment:HIGH
+used-before-assignment:177:12:177:16:expression_in_ternary_operator_inside_container_wrong_position:Using variable 'val3' before assignment:HIGH