summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2019-09-25 09:40:18 +0200
committerClaudiu Popa <pcmanticore@gmail.com>2019-09-25 09:43:58 +0200
commit3159b1744caee14068b4d875bb5adaa5ffbebd44 (patch)
treed2ea9bc96da4e9bb6b431fb687bc9760701020d1
parent2fa5d435df32ff363596de6e0285c13264e4ba30 (diff)
downloadpylint-git-3159b1744caee14068b4d875bb5adaa5ffbebd44.tar.gz
Exempt type checking definitions defined in both clauses of a type checking guard
Close #3127
-rw-r--r--pylint/checkers/variables.py24
-rw-r--r--tests/functional/u/undefined_variable.py16
-rw-r--r--tests/functional/u/undefined_variable.txt54
3 files changed, 61 insertions, 33 deletions
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index e4be053cd..dcbf4ec0c 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -1338,12 +1338,28 @@ class VariablesChecker(BaseChecker):
# Single statement function, with the statement on the
# same line as the function definition
maybee0601 = False
+
+ # Look for type checking definitions inside a type checking guard.
if isinstance(defstmt, (astroid.Import, astroid.ImportFrom)):
defstmt_parent = defstmt.parent
- if isinstance(
- defstmt_parent, astroid.If
- ) and not defstmt_parent.parent_of(node):
- if defstmt_parent.test.as_string() in TYPING_TYPE_CHECKS_GUARDS:
+
+ if (
+ isinstance(defstmt_parent, astroid.If)
+ and defstmt_parent.test.as_string() in TYPING_TYPE_CHECKS_GUARDS
+ ):
+ # Exempt those definitions that are used inside the type checking
+ # guard or that are defined in both type checking guard branches.
+ used_in_branch = defstmt_parent.parent_of(node)
+ defined_in_or_else = False
+
+ for definition in defstmt_parent.orelse:
+ if isinstance(definition, astroid.Assign):
+ defined_in_or_else = any(
+ target.name == name for target in definition.targets
+ )
+ break
+
+ if not used_in_branch and not defined_in_or_else:
maybee0601 = True
return maybee0601, annotation_return, use_outer_definition
diff --git a/tests/functional/u/undefined_variable.py b/tests/functional/u/undefined_variable.py
index 71df92960..909898f16 100644
--- a/tests/functional/u/undefined_variable.py
+++ b/tests/functional/u/undefined_variable.py
@@ -1,6 +1,10 @@
# pylint: disable=missing-docstring, multiple-statements, useless-object-inheritance,import-outside-toplevel
# pylint: disable=too-few-public-methods, no-init, no-self-use,bare-except,broad-except, import-error
from __future__ import print_function
+
+# pylint: disable=wrong-import-position
+from typing import TYPE_CHECKING
+
DEFINED = 1
if DEFINED != 1:
@@ -247,8 +251,6 @@ def nonlocal_in_ifexp():
plt.show(block=True)
-# pylint: disable=wrong-import-position
-from typing import TYPE_CHECKING
if TYPE_CHECKING:
from datetime import datetime
@@ -264,3 +266,13 @@ if TYPE_CHECKING:
from typing_extensions import Literal
AllowedValues = Literal['hello', 'world']
+
+
+if TYPE_CHECKING:
+ from collections import Counter
+else:
+ Counter = object
+
+
+def tick(counter: Counter, name: str) -> None:
+ counter[name] += 1
diff --git a/tests/functional/u/undefined_variable.txt b/tests/functional/u/undefined_variable.txt
index 5f9ca9747..4a0e83aec 100644
--- a/tests/functional/u/undefined_variable.txt
+++ b/tests/functional/u/undefined_variable.txt
@@ -1,27 +1,27 @@
-undefined-variable:7::Undefined variable 'unknown'
-undefined-variable:13:in_method:Undefined variable 'nomoreknown'
-undefined-variable:16::Undefined variable '__revision__'
-undefined-variable:18::Undefined variable '__revision__'
-undefined-variable:22:bad_default:Undefined variable 'unknown2'
-undefined-variable:25:bad_default:Undefined variable 'xxxx'
-undefined-variable:26:bad_default:Undefined variable 'augvar'
-undefined-variable:27:bad_default:Undefined variable 'vardel'
-undefined-variable:29:<lambda>:Undefined variable 'doesnotexist'
-undefined-variable:30:<lambda>:Undefined variable 'z'
-used-before-assignment:38::Using variable 'POUETT' before assignment
-used-before-assignment:51::Using variable 'PLOUF' before assignment
-used-before-assignment:60:if_branch_test:Using variable 'xxx' before assignment
-used-before-assignment:86:test_arguments:Using variable 'TestClass' before assignment
-used-before-assignment:90:TestClass:Using variable 'Ancestor' before assignment
-used-before-assignment:93:TestClass.MissingAncestor:Using variable 'Ancestor1' before assignment
-used-before-assignment:100:TestClass.test1.UsingBeforeDefinition:Using variable 'Empty' before assignment
-undefined-variable:114:Self:Undefined variable 'Self'
-undefined-variable:130::Undefined variable 'BAT'
-used-before-assignment:141:KeywordArgument.test1:Using variable 'enabled' before assignment
-undefined-variable:144:KeywordArgument.test2:Undefined variable 'disabled'
-undefined-variable:149:KeywordArgument.<lambda>:Undefined variable 'arg'
-undefined-variable:161::Undefined variable 'unicode_2'
-undefined-variable:171::Undefined variable 'unicode_4'
-undefined-variable:226:LambdaClass4.<lambda>:Undefined variable 'LambdaClass4'
-undefined-variable:234:LambdaClass5.<lambda>:Undefined variable 'LambdaClass5'
-used-before-assignment:257:func_should_fail:Using variable 'datetime' before assignment
+undefined-variable:11::Undefined variable 'unknown'
+undefined-variable:17:in_method:Undefined variable 'nomoreknown'
+undefined-variable:20::Undefined variable '__revision__'
+undefined-variable:22::Undefined variable '__revision__'
+undefined-variable:26:bad_default:Undefined variable 'unknown2'
+undefined-variable:29:bad_default:Undefined variable 'xxxx'
+undefined-variable:30:bad_default:Undefined variable 'augvar'
+undefined-variable:31:bad_default:Undefined variable 'vardel'
+undefined-variable:33:<lambda>:Undefined variable 'doesnotexist'
+undefined-variable:34:<lambda>:Undefined variable 'z'
+used-before-assignment:42::Using variable 'POUETT' before assignment
+used-before-assignment:55::Using variable 'PLOUF' before assignment
+used-before-assignment:64:if_branch_test:Using variable 'xxx' before assignment
+used-before-assignment:90:test_arguments:Using variable 'TestClass' before assignment
+used-before-assignment:94:TestClass:Using variable 'Ancestor' before assignment
+used-before-assignment:97:TestClass.MissingAncestor:Using variable 'Ancestor1' before assignment
+used-before-assignment:104:TestClass.test1.UsingBeforeDefinition:Using variable 'Empty' before assignment
+undefined-variable:118:Self:Undefined variable 'Self'
+undefined-variable:134::Undefined variable 'BAT'
+used-before-assignment:145:KeywordArgument.test1:Using variable 'enabled' before assignment
+undefined-variable:148:KeywordArgument.test2:Undefined variable 'disabled'
+undefined-variable:153:KeywordArgument.<lambda>:Undefined variable 'arg'
+undefined-variable:165::Undefined variable 'unicode_2'
+undefined-variable:175::Undefined variable 'unicode_4'
+undefined-variable:230:LambdaClass4.<lambda>:Undefined variable 'LambdaClass4'
+undefined-variable:238:LambdaClass5.<lambda>:Undefined variable 'LambdaClass5'
+used-before-assignment:259:func_should_fail:Using variable 'datetime' before assignment