summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Walls <jacobtylerwalls@gmail.com>2023-04-29 14:32:50 -0400
committerGitHub <noreply@github.com>2023-04-29 20:32:50 +0200
commit1cbc3d818b2b279747ef8d4222262fea4b21cccb (patch)
tree44d31a92a68c3933b87f888192371961d0d5e836
parent87a4d672a8661aa61d5717fd902315d9563e33b6 (diff)
downloadpylint-git-1cbc3d818b2b279747ef8d4222262fea4b21cccb.tar.gz
Fix false negative for calling module-level function before definition (#8494)
-rw-r--r--doc/whatsnew/fragments/1144.false_negative3
-rw-r--r--pylint/checkers/variables.py12
-rw-r--r--tests/functional/u/used/used_before_assignment.py11
-rw-r--r--tests/functional/u/used/used_before_assignment.txt16
-rw-r--r--tests/functional/u/used/used_before_assignment_ternary.txt6
5 files changed, 29 insertions, 19 deletions
diff --git a/doc/whatsnew/fragments/1144.false_negative b/doc/whatsnew/fragments/1144.false_negative
new file mode 100644
index 000000000..9b30c197b
--- /dev/null
+++ b/doc/whatsnew/fragments/1144.false_negative
@@ -0,0 +1,3 @@
+Emit ``used-before-assignment`` when calling module-level functions before definition.
+
+Closes #1144
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index 08797c93c..2504c0c84 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -218,17 +218,11 @@ def _detect_global_scope(
return node.lineno < defframe.lineno # type: ignore[no-any-return]
if not isinstance(node.parent, (nodes.FunctionDef, nodes.Arguments)):
return False
- elif any(
- not isinstance(f, (nodes.ClassDef, nodes.Module)) for f in (frame, defframe)
- ):
- # Not interested in other frames, since they are already
- # not in a global scope.
- return False
break_scopes = []
- for current_scope in (scope, def_scope):
+ for current_scope in (scope or frame, def_scope):
# Look for parent scopes. If there is anything different
- # than a module or a class scope, then they frames don't
+ # than a module or a class scope, then the frames don't
# share a global scope.
parent_scope = current_scope
while parent_scope:
@@ -239,7 +233,7 @@ def _detect_global_scope(
parent_scope = parent_scope.parent.scope()
else:
break
- if break_scopes and len(set(break_scopes)) != 1:
+ if len(set(break_scopes)) > 1:
# Store different scopes than expected.
# If the stored scopes are, in fact, the very same, then it means
# that the two frames (frame and defframe) share the same scope,
diff --git a/tests/functional/u/used/used_before_assignment.py b/tests/functional/u/used/used_before_assignment.py
index 91212612d..9b7d668ca 100644
--- a/tests/functional/u/used/used_before_assignment.py
+++ b/tests/functional/u/used/used_before_assignment.py
@@ -14,6 +14,17 @@ def outer():
outer()
+class ClassWithProperty: # pylint: disable=too-few-public-methods
+ """This test depends on earlier and later defined module-level functions."""
+ prop = property(redefine_time_import) # [used-before-assignment]
+ prop_defined_earlier = property(outer)
+
+
+calculate(1.01, 2) # [used-before-assignment]
+def calculate(value1: int, value2: float) -> int:
+ return value1 + value2
+
+
# pylint: disable=unused-import, wrong-import-position, import-outside-toplevel, reimported, redefined-outer-name, global-statement
import time
def redefine_time_import():
diff --git a/tests/functional/u/used/used_before_assignment.txt b/tests/functional/u/used/used_before_assignment.txt
index 0a3c9ff2f..37b25ab49 100644
--- a/tests/functional/u/used/used_before_assignment.txt
+++ b/tests/functional/u/used/used_before_assignment.txt
@@ -1,10 +1,12 @@
used-before-assignment:5:19:5:22::Using variable 'MSG' before assignment:HIGH
used-before-assignment:7:20:7:24::Using variable 'MSG2' before assignment:HIGH
used-before-assignment:10:4:10:9:outer:Using variable 'inner' before assignment:HIGH
-used-before-assignment:20:10:20:14:redefine_time_import:Using variable 'time' before assignment:HIGH
-used-before-assignment:34:3:34:7::Using variable 'VAR2' before assignment:CONTROL_FLOW
-used-before-assignment:52:3:52:7::Using variable 'VAR4' before assignment:CONTROL_FLOW
-used-before-assignment:67:3:67:7::Using variable 'VAR6' before assignment:CONTROL_FLOW
-used-before-assignment:102:6:102:11::Using variable 'VAR10' before assignment:CONTROL_FLOW
-used-before-assignment:133:10:133:14::Using variable 'SALE' before assignment:CONTROL_FLOW
-used-before-assignment:165:10:165:18::Using variable 'ALL_DONE' before assignment:CONTROL_FLOW
+used-before-assignment:19:20:19:40:ClassWithProperty:Using variable 'redefine_time_import' before assignment:HIGH
+used-before-assignment:23:0:23:9::Using variable 'calculate' before assignment:HIGH
+used-before-assignment:31:10:31:14:redefine_time_import:Using variable 'time' before assignment:HIGH
+used-before-assignment:45:3:45:7::Using variable 'VAR2' before assignment:CONTROL_FLOW
+used-before-assignment:63:3:63:7::Using variable 'VAR4' before assignment:CONTROL_FLOW
+used-before-assignment:78:3:78:7::Using variable 'VAR6' before assignment:CONTROL_FLOW
+used-before-assignment:113:6:113:11::Using variable 'VAR10' before assignment:CONTROL_FLOW
+used-before-assignment:144:10:144:14::Using variable 'SALE' before assignment:CONTROL_FLOW
+used-before-assignment:176:10:176:18::Using variable 'ALL_DONE' before assignment:CONTROL_FLOW
diff --git a/tests/functional/u/used/used_before_assignment_ternary.txt b/tests/functional/u/used/used_before_assignment_ternary.txt
index d991970e4..bcb21df1c 100644
--- a/tests/functional/u/used/used_before_assignment_ternary.txt
+++ b/tests/functional/u/used/used_before_assignment_ternary.txt
@@ -1,3 +1,3 @@
-used-before-assignment:6:14:6:15:invalid:Using variable 'a' before assignment:HIGH
-used-before-assignment:8:14:8:15:invalid:Using variable 'b' before assignment:HIGH
-used-before-assignment:9:10:9:11:invalid:Using variable 'c' before assignment:HIGH
+used-before-assignment:6:14:6:15:invalid:Using variable 'a' before assignment:HIGH
+used-before-assignment:8:14:8:15:invalid:Using variable 'b' before assignment:HIGH
+used-before-assignment:9:10:9:11:invalid:Using variable 'c' before assignment:HIGH