Skip to content

Commit

Permalink
Make global-variable-not-assigned check local scope
Browse files Browse the repository at this point in the history
This checker now checks whether the names after the global keyword
are reassigned in the local scope.
This closes pylint-dev#1375
  • Loading branch information
DanielNoord committed Sep 10, 2021
1 parent e777697 commit 42bac44
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 1 deletion.
5 changes: 5 additions & 0 deletions ChangeLog
Expand Up @@ -77,6 +77,11 @@ Release date: TBA

Closes #4900

* The ``global-variable-not-assigned`` checker now catches global variables that are never reassigned in a
local scope

Closes #1375


What's New in Pylint 2.10.3?
============================
Expand Down
5 changes: 5 additions & 0 deletions doc/whatsnew/2.11.rst
Expand Up @@ -79,3 +79,8 @@ Other Changes
* Fix a bug where pylint complained if the cache's parent directory does not exist

Closes #4900

* The ``global-variable-not-assigned`` checker now catches global variables that are never reassigned in a
local scope

Closes #1375
8 changes: 8 additions & 0 deletions pylint/checkers/utils.py
Expand Up @@ -1558,3 +1558,11 @@ def is_node_in_guarded_import_block(node: nodes.NodeNG) -> bool:
return isinstance(node.parent, nodes.If) and (
node.parent.is_sys_guard() or node.parent.is_typing_guard()
)


def is_reassigned_after_current(node: nodes.NodeNG, varname: str) -> bool:
"""Check if the given variable name is reassigned in the same scope after the current node"""
for assign_node in node.scope().nodes_of_class(nodes.AssignName):
if assign_node.name == varname and node.lineno < assign_node.lineno:
return True
return False
5 changes: 4 additions & 1 deletion pylint/checkers/variables.py
Expand Up @@ -931,7 +931,10 @@ def visit_global(self, node: nodes.Global) -> None:
not_defined_locally_by_import = not any(
isinstance(local, nodes.Import) for local in locals_.get(name, ())
)
if not assign_nodes and not_defined_locally_by_import:
if (
not utils.is_reassigned_after_current(node, name)
and not_defined_locally_by_import
):
self.add_message("global-variable-not-assigned", args=name, node=node)
default_message = False
continue
Expand Down
13 changes: 13 additions & 0 deletions tests/functional/g/globals.py
Expand Up @@ -30,3 +30,16 @@ def global_with_import():
"""should only warn for global-statement"""
global sys # [global-statement]
import sys # pylint: disable=import-outside-toplevel


def global_no_assign():
"""Not assigning anything to the global makes 'global' superfluous"""
global CONSTANT # [global-variable-not-assigned]
print(CONSTANT)


def global_operator_assign():
"""Operator assigns should only throw a global statement error"""
global CONSTANT # [global-statement]
print(CONSTANT)
CONSTANT += 1
2 changes: 2 additions & 0 deletions tests/functional/g/globals.txt
Expand Up @@ -5,3 +5,5 @@ global-variable-not-assigned:18:4:other:Using global for 'HOP' but no assignment
undefined-variable:19:10:other:Undefined variable 'HOP'
global-variable-undefined:24:4:define_constant:Global variable 'SOMEVAR' undefined at the module level
global-statement:31:4:global_with_import:Using the global statement
global-variable-not-assigned:37:4:global_no_assign:Using global for 'CONSTANT' but no assignment is done:HIGH
global-statement:43:4:global_operator_assign:Using the global statement:HIGH

0 comments on commit 42bac44

Please sign in to comment.