diff options
author | Jacob Walls <jacobtylerwalls@gmail.com> | 2022-06-12 08:49:10 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-12 08:49:10 -0400 |
commit | 398a8292a29b5dbd7c015f510136c663a25f9596 (patch) | |
tree | 0ece6c45727c4c09dc07b5be46befd3bf3e8f160 | |
parent | d4d8c0febd897615322cc4a7a216550f065e0e3f (diff) | |
download | pylint-git-398a8292a29b5dbd7c015f510136c663a25f9596.tar.gz |
Warn about self-referencing named expressions lacking prior assignments (#6915)
-rw-r--r-- | doc/whatsnew/2/2.15/index.rst | 5 | ||||
-rw-r--r-- | pylint/checkers/variables.py | 20 | ||||
-rw-r--r-- | tests/functional/u/undefined/undefined_variable_py38.py | 8 | ||||
-rw-r--r-- | tests/functional/u/undefined/undefined_variable_py38.txt | 16 |
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 |