diff --git a/ChangeLog b/ChangeLog index 6c77e9a0b7..e457271884 100644 --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,10 @@ Release date: TBA Closes #5588 +* Fixed false positive for ``global-variable-undefined`` when ``global`` is used with a class name + + Closes #3088 + * Added ``lru-cache-decorating-method`` checker with checks for the use of ``functools.lru_cache`` on class methods. This is unrecommended as it creates memory leaks by never letting the instance getting garbage collected. diff --git a/doc/whatsnew/2.13.rst b/doc/whatsnew/2.13.rst index a8507ed234..d3422ccfbd 100644 --- a/doc/whatsnew/2.13.rst +++ b/doc/whatsnew/2.13.rst @@ -94,6 +94,10 @@ Other Changes * When run in parallel mode ``pylint`` now pickles the data passed to subprocesses with the ``dill`` package. The ``dill`` package has therefore been added as a dependency. +* Fixed false positive for ``global-variable-undefined`` when ``global`` is used with a class name + + Closes #3088 + * Fixed crash on properties and inherited class methods when comparing them for equality against an empty dict. diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 7e08a9048a..a73ee2fa9d 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -1668,7 +1668,9 @@ 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""" return any( a.name == varname and a.lineno > node.lineno - for a in node.scope().nodes_of_class((nodes.AssignName, nodes.FunctionDef)) + for a in node.scope().nodes_of_class( + (nodes.AssignName, nodes.ClassDef, nodes.FunctionDef) + ) ) diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index b540ed6154..b8b8934442 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -1205,7 +1205,10 @@ def visit_global(self, node: nodes.Global) -> None: if anode.frame(future=True) is module: # module level assignment break - if isinstance(anode, nodes.FunctionDef) and anode.parent is module: + if ( + isinstance(anode, (nodes.ClassDef, nodes.FunctionDef)) + and anode.parent is module + ): # module level function assignment break else: diff --git a/tests/functional/g/globals.py b/tests/functional/g/globals.py index 56c852d8a6..f12c06800c 100644 --- a/tests/functional/g/globals.py +++ b/tests/functional/g/globals.py @@ -1,5 +1,5 @@ """Warnings about global statements and usage of global variables.""" -# pylint: disable=invalid-name, redefined-outer-name, missing-function-docstring, import-outside-toplevel +# pylint: disable=invalid-name, redefined-outer-name, missing-function-docstring, missing-class-docstring, import-outside-toplevel, too-few-public-methods from __future__ import print_function global CSTE # [global-at-module-level] @@ -9,6 +9,9 @@ def FUNC(): pass +class CLASS: + pass + def fix_contant(value): """all this is ok, but try not using global ;)""" global CONSTANT # [global-statement] @@ -78,3 +81,12 @@ def func(): global sys # [global-statement] import sys + +def override_class(): + """Overriding a class should only throw a global statement error""" + global CLASS # [global-statement] + + class CLASS(): + pass + + CLASS() diff --git a/tests/functional/g/globals.txt b/tests/functional/g/globals.txt index 4e3f6dca43..975506cc8e 100644 --- a/tests/functional/g/globals.txt +++ b/tests/functional/g/globals.txt @@ -1,13 +1,14 @@ global-at-module-level:5:0:5:11::Using the global statement at the module level:UNDEFINED undefined-variable:6:6:6:10::Undefined variable 'CSTE':UNDEFINED -global-statement:14:4:14:19:fix_contant:Using the global statement:UNDEFINED -global-variable-not-assigned:21:4:21:14:other:Using global for 'HOP' but no assignment is done:UNDEFINED -undefined-variable:22:10:22:13:other:Undefined variable 'HOP':UNDEFINED -global-variable-undefined:27:4:27:18:define_constant:Global variable 'SOMEVAR' undefined at the module level:UNDEFINED -global-statement:33:4:33:14:global_with_import:Using the global statement:UNDEFINED -global-variable-not-assigned:39:4:39:19:global_no_assign:Using global for 'CONSTANT' but no assignment is done:UNDEFINED -global-statement:45:4:45:19:global_del:Using the global statement:UNDEFINED -global-statement:52:4:52:19:global_operator_assign:Using the global statement:UNDEFINED -global-statement:59:4:59:19:global_function_assign:Using the global statement:UNDEFINED -global-statement:69:4:69:15:override_func:Using the global statement:UNDEFINED -global-statement:78:4:78:14:func:Using the global statement:UNDEFINED +global-statement:17:4:17:19:fix_contant:Using the global statement:UNDEFINED +global-variable-not-assigned:24:4:24:14:other:Using global for 'HOP' but no assignment is done:UNDEFINED +undefined-variable:25:10:25:13:other:Undefined variable 'HOP':UNDEFINED +global-variable-undefined:30:4:30:18:define_constant:Global variable 'SOMEVAR' undefined at the module level:UNDEFINED +global-statement:36:4:36:14:global_with_import:Using the global statement:UNDEFINED +global-variable-not-assigned:42:4:42:19:global_no_assign:Using global for 'CONSTANT' but no assignment is done:UNDEFINED +global-statement:48:4:48:19:global_del:Using the global statement:UNDEFINED +global-statement:55:4:55:19:global_operator_assign:Using the global statement:UNDEFINED +global-statement:62:4:62:19:global_function_assign:Using the global statement:UNDEFINED +global-statement:72:4:72:15:override_func:Using the global statement:UNDEFINED +global-statement:81:4:81:14:func:Using the global statement:UNDEFINED +global-statement:87:4:87:16:override_class:Using the global statement:UNDEFINED