diff options
author | Nick Drozd <nicholasdrozd@gmail.com> | 2023-02-14 16:39:57 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-14 22:39:57 +0100 |
commit | e64f0437388298c7f8514a6755c3c27a0d9a35f2 (patch) | |
tree | 115bd308fcad5e5665b37b32455393c529fed738 | |
parent | b8f2b5498656b5fe5b94b6c9da90f76677478701 (diff) | |
download | pylint-git-e64f0437388298c7f8514a6755c3c27a0d9a35f2.tar.gz |
Fix invalid type false positive (#8206)
-rw-r--r-- | doc/whatsnew/fragments/8205.false_positive | 3 | ||||
-rw-r--r-- | pylint/checkers/typecheck.py | 15 | ||||
-rw-r--r-- | pylint/constants.py | 1 | ||||
-rw-r--r-- | tests/functional/d/dataclass/dataclass_typecheck.txt | 2 | ||||
-rw-r--r-- | tests/functional/ext/typing/redundant_typehint_argument.py | 2 | ||||
-rw-r--r-- | tests/functional/ext/typing/redundant_typehint_argument_py310.py | 2 | ||||
-rw-r--r-- | tests/functional/i/isinstance_second_argument.txt | 10 | ||||
-rw-r--r-- | tests/functional/i/isinstance_second_argument_py310.py | 27 | ||||
-rw-r--r-- | tests/functional/i/isinstance_second_argument_py310.rc | 2 | ||||
-rw-r--r-- | tests/functional/i/isinstance_second_argument_py310.txt | 3 |
10 files changed, 58 insertions, 9 deletions
diff --git a/doc/whatsnew/fragments/8205.false_positive b/doc/whatsnew/fragments/8205.false_positive new file mode 100644 index 000000000..6bf596592 --- /dev/null +++ b/doc/whatsnew/fragments/8205.false_positive @@ -0,0 +1,3 @@ +Fix false positive for isinstance-second-argument-not-valid-type with union types. + +Closes #8205 diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index bfd415923..8448a0f13 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -48,6 +48,7 @@ from pylint.checkers.utils import ( supports_membership_test, supports_setitem, ) +from pylint.constants import PY310_PLUS from pylint.interfaces import HIGH, INFERENCE from pylint.typing import MessageDefinitionTuple @@ -796,6 +797,10 @@ def _is_c_extension(module_node: InferenceResult) -> bool: def _is_invalid_isinstance_type(arg: nodes.NodeNG) -> bool: # Return True if we are sure that arg is not a type + if PY310_PLUS and isinstance(arg, nodes.BinOp) and arg.op == "|": + return _is_invalid_isinstance_type(arg.left) or _is_invalid_isinstance_type( + arg.right + ) inferred = utils.safe_infer(arg) if not inferred: # Cannot infer it so skip it. @@ -806,6 +811,10 @@ def _is_invalid_isinstance_type(arg: nodes.NodeNG) -> bool: return False if isinstance(inferred, astroid.Instance) and inferred.qname() == BUILTIN_TUPLE: return False + if PY310_PLUS and isinstance(inferred, bases.UnionType): + return _is_invalid_isinstance_type( + inferred.left + ) or _is_invalid_isinstance_type(inferred.right) return True @@ -1398,7 +1407,11 @@ accessed. Python regular expressions are accepted.", second_arg = node.args[1] if _is_invalid_isinstance_type(second_arg): - self.add_message("isinstance-second-argument-not-valid-type", node=node) + self.add_message( + "isinstance-second-argument-not-valid-type", + node=node, + confidence=INFERENCE, + ) # pylint: disable = too-many-branches, too-many-locals, too-many-statements def visit_call(self, node: nodes.Call) -> None: diff --git a/pylint/constants.py b/pylint/constants.py index 3bbda93d0..92a9990d1 100644 --- a/pylint/constants.py +++ b/pylint/constants.py @@ -18,6 +18,7 @@ from pylint.typing import MessageTypesFullName PY38_PLUS = sys.version_info[:2] >= (3, 8) PY39_PLUS = sys.version_info[:2] >= (3, 9) +PY310_PLUS = sys.version_info[:2] >= (3, 10) IS_PYPY = platform.python_implementation() == "PyPy" diff --git a/tests/functional/d/dataclass/dataclass_typecheck.txt b/tests/functional/d/dataclass/dataclass_typecheck.txt index 5afac5849..1873dc122 100644 --- a/tests/functional/d/dataclass/dataclass_typecheck.txt +++ b/tests/functional/d/dataclass/dataclass_typecheck.txt @@ -9,4 +9,4 @@ unsupported-delete-operation:72:4:72:13::'obj.attr1' does not support item delet not-context-manager:97:0:98:8::Context manager 'str' doesn't implement __enter__ and __exit__.:UNDEFINED invalid-metaclass:105:0:105:11:Test2:Invalid metaclass 'Instance of builtins.int' used:UNDEFINED unhashable-member:111:0:111:2::'obj.attr5' is unhashable and can't be used as a key in a dict:INFERENCE -isinstance-second-argument-not-valid-type:121:6:121:30::Second argument of isinstance is not a type:UNDEFINED +isinstance-second-argument-not-valid-type:121:6:121:30::Second argument of isinstance is not a type:INFERENCE diff --git a/tests/functional/ext/typing/redundant_typehint_argument.py b/tests/functional/ext/typing/redundant_typehint_argument.py index e4814ed5f..fe2cc4a2e 100644 --- a/tests/functional/ext/typing/redundant_typehint_argument.py +++ b/tests/functional/ext/typing/redundant_typehint_argument.py @@ -1,4 +1,4 @@ -""""Checks for redundant Union typehints in assignments""" +"""Checks for redundant Union typehints in assignments""" # pylint: disable=deprecated-typing-alias,consider-alternative-union-syntax,consider-using-alias,invalid-name,unused-argument,missing-function-docstring from __future__ import annotations diff --git a/tests/functional/ext/typing/redundant_typehint_argument_py310.py b/tests/functional/ext/typing/redundant_typehint_argument_py310.py index 599f88283..72a4cc836 100644 --- a/tests/functional/ext/typing/redundant_typehint_argument_py310.py +++ b/tests/functional/ext/typing/redundant_typehint_argument_py310.py @@ -1,4 +1,4 @@ -""""Checks for redundant Union typehints in assignments""" +"""Checks for redundant Union typehints in assignments""" # pylint: disable=deprecated-typing-alias,consider-alternative-union-syntax,consider-using-alias,invalid-name,unused-argument,missing-function-docstring from __future__ import annotations diff --git a/tests/functional/i/isinstance_second_argument.txt b/tests/functional/i/isinstance_second_argument.txt index 20c13499e..22d8b196a 100644 --- a/tests/functional/i/isinstance_second_argument.txt +++ b/tests/functional/i/isinstance_second_argument.txt @@ -1,5 +1,5 @@ -isinstance-second-argument-not-valid-type:27:0:27:23::Second argument of isinstance is not a type:UNDEFINED -isinstance-second-argument-not-valid-type:28:0:28:19::Second argument of isinstance is not a type:UNDEFINED -isinstance-second-argument-not-valid-type:29:0:29:34::Second argument of isinstance is not a type:UNDEFINED -isinstance-second-argument-not-valid-type:30:0:30:54::Second argument of isinstance is not a type:UNDEFINED -isinstance-second-argument-not-valid-type:31:0:31:18::Second argument of isinstance is not a type:UNDEFINED +isinstance-second-argument-not-valid-type:27:0:27:23::Second argument of isinstance is not a type:INFERENCE +isinstance-second-argument-not-valid-type:28:0:28:19::Second argument of isinstance is not a type:INFERENCE +isinstance-second-argument-not-valid-type:29:0:29:34::Second argument of isinstance is not a type:INFERENCE +isinstance-second-argument-not-valid-type:30:0:30:54::Second argument of isinstance is not a type:INFERENCE +isinstance-second-argument-not-valid-type:31:0:31:18::Second argument of isinstance is not a type:INFERENCE diff --git a/tests/functional/i/isinstance_second_argument_py310.py b/tests/functional/i/isinstance_second_argument_py310.py new file mode 100644 index 000000000..8a0c17af5 --- /dev/null +++ b/tests/functional/i/isinstance_second_argument_py310.py @@ -0,0 +1,27 @@ +'''Tests for invalid isinstance with compound types''' + +# True negatives +isinstance(0, int | str) +isinstance(0, int | int | int) +isinstance(0, int | str | list | float) +isinstance(0, (int | str) | (list | float)) + +IntOrStr = int | str +isinstance(0, IntOrStr) +ListOrDict = list | dict +isinstance(0, (float | ListOrDict) | IntOrStr) + +# True positives +isinstance(0, int | 5) # [isinstance-second-argument-not-valid-type] +isinstance(0, str | 5 | int) # [isinstance-second-argument-not-valid-type] +INT = 5 +isinstance(0, INT | int) # [isinstance-second-argument-not-valid-type] + + +# FALSE NEGATIVES + +# Parameterized generics will raise type errors at runtime. +# Warnings should be raised, but aren't (yet). +isinstance(0, list[int]) +ListOfInts = list[int] +isinstance(0, ListOfInts) diff --git a/tests/functional/i/isinstance_second_argument_py310.rc b/tests/functional/i/isinstance_second_argument_py310.rc new file mode 100644 index 000000000..68a8c8ef1 --- /dev/null +++ b/tests/functional/i/isinstance_second_argument_py310.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.10 diff --git a/tests/functional/i/isinstance_second_argument_py310.txt b/tests/functional/i/isinstance_second_argument_py310.txt new file mode 100644 index 000000000..776bf3c2e --- /dev/null +++ b/tests/functional/i/isinstance_second_argument_py310.txt @@ -0,0 +1,3 @@ +isinstance-second-argument-not-valid-type:15:0:15:22::Second argument of isinstance is not a type:INFERENCE +isinstance-second-argument-not-valid-type:16:0:16:28::Second argument of isinstance is not a type:INFERENCE +isinstance-second-argument-not-valid-type:18:0:18:24::Second argument of isinstance is not a type:INFERENCE |