summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Mueller <30130371+cdce8p@users.noreply.github.com>2021-04-24 20:43:29 +0200
committerGitHub <noreply@github.com>2021-04-24 20:43:29 +0200
commitfce898e283107ff5a4f3dbf11c8927bef1a7333a (patch)
tree6619901970d4939589b694da7a4449c7d7736ca0
parentc91f7f9fc4247b1071c841131caed9a7b4d9c6d4 (diff)
downloadpylint-git-fce898e283107ff5a4f3dbf11c8927bef1a7333a.tar.gz
Add new extension TypingChecker (#4382)
-rw-r--r--ChangeLog5
-rw-r--r--doc/whatsnew/2.8.rst6
-rw-r--r--pylint/config/option.py12
-rw-r--r--pylint/extensions/typing.py315
-rw-r--r--pylint/utils/utils.py4
-rw-r--r--tests/functional/t/typing/typing_consider_using_alias.py61
-rw-r--r--tests/functional/t/typing/typing_consider_using_alias.rc9
-rw-r--r--tests/functional/t/typing/typing_consider_using_alias.txt17
-rw-r--r--tests/functional/t/typing/typing_consider_using_alias_without_future.py58
-rw-r--r--tests/functional/t/typing/typing_consider_using_alias_without_future.rc9
-rw-r--r--tests/functional/t/typing/typing_consider_using_alias_without_future.txt17
-rw-r--r--tests/functional/t/typing/typing_consider_using_union.py47
-rw-r--r--tests/functional/t/typing/typing_consider_using_union.rc9
-rw-r--r--tests/functional/t/typing/typing_consider_using_union.txt10
-rw-r--r--tests/functional/t/typing/typing_consider_using_union_py310.py45
-rw-r--r--tests/functional/t/typing/typing_consider_using_union_py310.rc8
-rw-r--r--tests/functional/t/typing/typing_consider_using_union_py310.txt18
-rw-r--r--tests/functional/t/typing/typing_consider_using_union_without_future.py45
-rw-r--r--tests/functional/t/typing/typing_consider_using_union_without_future.rc9
-rw-r--r--tests/functional/t/typing/typing_consider_using_union_without_future.txt10
-rw-r--r--tests/functional/t/typing/typing_deprecated_alias.py58
-rw-r--r--tests/functional/t/typing/typing_deprecated_alias.rc8
-rw-r--r--tests/functional/t/typing/typing_deprecated_alias.txt25
23 files changed, 804 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 613039c37..becd9d4db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -57,6 +57,11 @@ Release date: Undefined
* Update ``astroid`` to 2.5.4
+* Add new extension ``TypingChecker``. This optional checker can detect the use of deprecated typing aliases
+ and can suggest the use of the alternative union syntax where possible.
+ (For example, 'typing.Dict' can be replaced by 'dict', and 'typing.Unions' by '|', etc.)
+ Make sure to check the config options if you plan on using it!
+
What's New in Pylint 2.7.5?
===========================
diff --git a/doc/whatsnew/2.8.rst b/doc/whatsnew/2.8.rst
index 277bc8226..240cb1aaa 100644
--- a/doc/whatsnew/2.8.rst
+++ b/doc/whatsnew/2.8.rst
@@ -23,6 +23,12 @@ New checkers
* Add ``consider-using-min-max-builtin`` check for if statement which could be replaced by Python builtin min or max.
+* Add new extension ``TypingChecker``. This optional checker can detect the use of deprecated typing aliases
+ and can suggest the use of the alternative union syntax where possible.
+ (For example, 'typing.Dict' can be replaced by 'dict', and 'typing.Unions' by '|', etc.)
+ Make sure to check the config options if you plan on using it!
+
+
Other Changes
=============
diff --git a/pylint/config/option.py b/pylint/config/option.py
index 034c2094d..5994333ec 100644
--- a/pylint/config/option.py
+++ b/pylint/config/option.py
@@ -63,6 +63,15 @@ def _multiple_choices_validating_option(opt, name, value):
return _multiple_choice_validator(opt.choices, name, value)
+def _py_version_validator(_, name, value):
+ if not isinstance(value, tuple):
+ try:
+ value = tuple(int(val) for val in value.split("."))
+ except (ValueError, AttributeError):
+ raise optparse.OptionValueError(f"Invalid format for {name}") from None
+ return value
+
+
VALIDATORS = {
"string": utils._unquote,
"int": int,
@@ -76,6 +85,7 @@ VALIDATORS = {
opt["choices"], name, value
),
"non_empty_string": _non_empty_string_validator,
+ "py_version": _py_version_validator,
}
@@ -114,6 +124,7 @@ class Option(optparse.Option):
"yn",
"multiple_choice",
"non_empty_string",
+ "py_version",
)
ATTRS = optparse.Option.ATTRS + ["hide", "level"]
TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
@@ -123,6 +134,7 @@ class Option(optparse.Option):
TYPE_CHECKER["yn"] = _yn_validator
TYPE_CHECKER["multiple_choice"] = _multiple_choices_validating_option
TYPE_CHECKER["non_empty_string"] = _non_empty_string_validator
+ TYPE_CHECKER["py_version"] = _py_version_validator
def __init__(self, *opts, **attrs):
optparse.Option.__init__(self, *opts, **attrs)
diff --git a/pylint/extensions/typing.py b/pylint/extensions/typing.py
new file mode 100644
index 000000000..e4b72484b
--- /dev/null
+++ b/pylint/extensions/typing.py
@@ -0,0 +1,315 @@
+from functools import lru_cache
+from typing import Dict, List, NamedTuple, Set, Union
+
+import astroid
+import astroid.bases
+import astroid.node_classes
+
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import (
+ check_messages,
+ is_node_in_type_annotation_context,
+ safe_infer,
+)
+from pylint.interfaces import IAstroidChecker
+from pylint.lint import PyLinter
+
+
+class TypingAlias(NamedTuple):
+ name: str
+ name_collision: bool
+
+
+DEPRECATED_TYPING_ALIASES: Dict[str, TypingAlias] = {
+ "typing.Tuple": TypingAlias("tuple", False),
+ "typing.List": TypingAlias("list", False),
+ "typing.Dict": TypingAlias("dict", False),
+ "typing.Set": TypingAlias("set", False),
+ "typing.FrozenSet": TypingAlias("frozenset", False),
+ "typing.Type": TypingAlias("type", False),
+ "typing.Deque": TypingAlias("collections.deque", True),
+ "typing.DefaultDict": TypingAlias("collections.defaultdict", True),
+ "typing.OrderedDict": TypingAlias("collections.OrderedDict", True),
+ "typing.Counter": TypingAlias("collections.Counter", True),
+ "typing.ChainMap": TypingAlias("collections.ChainMap", True),
+ "typing.Awaitable": TypingAlias("collections.abc.Awaitable", True),
+ "typing.Coroutine": TypingAlias("collections.abc.Coroutine", True),
+ "typing.AsyncIterable": TypingAlias("collections.abc.AsyncIterable", True),
+ "typing.AsyncIterator": TypingAlias("collections.abc.AsyncIterator", True),
+ "typing.AsyncGenerator": TypingAlias("collections.abc.AsyncGenerator", True),
+ "typing.Iterable": TypingAlias("collections.abc.Iterable", True),
+ "typing.Iterator": TypingAlias("collections.abc.Iterator", True),
+ "typing.Generator": TypingAlias("collections.abc.Generator", True),
+ "typing.Reversible": TypingAlias("collections.abc.Reversible", True),
+ "typing.Container": TypingAlias("collections.abc.Container", True),
+ "typing.Collection": TypingAlias("collections.abc.Collection", True),
+ "typing.Callable": TypingAlias("collections.abc.Callable", True),
+ "typing.AbstractSet": TypingAlias("collections.abc.Set", False),
+ "typing.MutableSet": TypingAlias("collections.abc.MutableSet", True),
+ "typing.Mapping": TypingAlias("collections.abc.Mapping", True),
+ "typing.MutableMapping": TypingAlias("collections.abc.MutableMapping", True),
+ "typing.Sequence": TypingAlias("collections.abc.Sequence", True),
+ "typing.MutableSequence": TypingAlias("collections.abc.MutableSequence", True),
+ "typing.ByteString": TypingAlias("collections.abc.ByteString", True),
+ "typing.MappingView": TypingAlias("collections.abc.MappingView", True),
+ "typing.KeysView": TypingAlias("collections.abc.KeysView", True),
+ "typing.ItemsView": TypingAlias("collections.abc.ItemsView", True),
+ "typing.ValuesView": TypingAlias("collections.abc.ValuesView", True),
+ "typing.ContextManager": TypingAlias("contextlib.AbstractContextManager", False),
+ "typing.AsyncContextManager": TypingAlias(
+ "contextlib.AbstractAsyncContextManager", False
+ ),
+ "typing.Pattern": TypingAlias("re.Pattern", True),
+ "typing.Match": TypingAlias("re.Match", True),
+ "typing.Hashable": TypingAlias("collections.abc.Hashable", True),
+ "typing.Sized": TypingAlias("collections.abc.Sized", True),
+}
+
+ALIAS_NAMES = frozenset(key.split(".")[1] for key in DEPRECATED_TYPING_ALIASES)
+UNION_NAMES = ("Optional", "Union")
+
+
+class DeprecatedTypingAliasMsg(NamedTuple):
+ node: Union[astroid.Name, astroid.Attribute]
+ qname: str
+ alias: str
+ parent_subscript: bool
+
+
+class TypingChecker(BaseChecker):
+ """Find issue specifically related to type annotations."""
+
+ __implements__ = (IAstroidChecker,)
+
+ name = "typing"
+ priority = -1
+ msgs = {
+ "W6001": (
+ "'%s' is deprecated, use '%s' instead",
+ "deprecated-typing-alias",
+ "Emitted when a deprecated typing alias is used.",
+ ),
+ "R6002": (
+ "'%s' will be deprecated with PY39, consider using '%s' instead%s",
+ "consider-using-alias",
+ "Only emitted if 'runtime-typing=no' and a deprecated "
+ "typing alias is used in a type annotation context in "
+ "Python 3.7 or 3.8.",
+ ),
+ "R6003": (
+ "Consider using alternative Union syntax instead of '%s'%s",
+ "consider-alternative-union-syntax",
+ "Emitted when 'typing.Union' or 'typing.Optional' is used "
+ "instead of the alternative Union syntax 'int | None'.",
+ ),
+ }
+ options = (
+ (
+ "py-version",
+ {
+ "default": (3, 7),
+ "type": "py_version",
+ "metavar": "<py_version>",
+ "help": (
+ "Min Python version to use for typing related checks, "
+ "e.g. ``3.7``. This should be equal to the min supported Python "
+ "version of the project."
+ ),
+ },
+ ),
+ (
+ "runtime-typing",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": (
+ "Set to ``no`` if the app / libary does NOT need to "
+ "support runtime introspection of type "
+ "annotations. Only applies to Python version "
+ "3.7 - 3.9"
+ ),
+ },
+ ),
+ )
+
+ def __init__(self, linter: PyLinter) -> None:
+ """Initialize checker instance."""
+ super().__init__(linter=linter)
+ self._alias_name_collisions: Set[str] = set()
+ self._consider_using_alias_msgs: List[DeprecatedTypingAliasMsg] = []
+
+ @lru_cache()
+ def _py37_plus(self) -> bool:
+ return self.config.py_version >= (3, 7)
+
+ @lru_cache()
+ def _py39_plus(self) -> bool:
+ return self.config.py_version >= (3, 9)
+
+ @lru_cache()
+ def _py310_plus(self) -> bool:
+ return self.config.py_version >= (3, 10)
+
+ @lru_cache()
+ def _should_check_typing_alias(self) -> bool:
+ """The use of type aliases (PEP 585) requires Python 3.9
+ or Python 3.7+ with postponed evaluation.
+ """
+ return (
+ self._py39_plus()
+ or self._py37_plus()
+ and self.config.runtime_typing is False
+ )
+
+ @lru_cache()
+ def _should_check_alternative_union_syntax(self) -> bool:
+ """The use of alternative union syntax (PEP 604) requires Python 3.10
+ or Python 3.7+ with postponed evaluation.
+ """
+ return (
+ self._py310_plus()
+ or self._py37_plus()
+ and self.config.runtime_typing is False
+ )
+
+ def _msg_postponed_eval_hint(self, node) -> str:
+ """Message hint if postponed evaluation isn't enabled."""
+ if self._py310_plus() or "annotations" in node.root().future_imports:
+ return ""
+ return ". Add 'from __future__ import annotations' as well"
+
+ @check_messages(
+ "deprecated-typing-alias",
+ "consider-using-alias",
+ "consider-alternative-union-syntax",
+ )
+ def visit_name(self, node: astroid.Name) -> None:
+ if self._should_check_typing_alias() and node.name in ALIAS_NAMES:
+ self._check_for_typing_alias(node)
+ if self._should_check_alternative_union_syntax() and node.name in UNION_NAMES:
+ self._check_for_alternative_union_syntax(node, node.name)
+
+ @check_messages(
+ "deprecated-typing-alias",
+ "consider-using-alias",
+ "consider-alternative-union-syntax",
+ )
+ def visit_attribute(self, node: astroid.Attribute):
+ if self._should_check_typing_alias() and node.attrname in ALIAS_NAMES:
+ self._check_for_typing_alias(node)
+ if (
+ self._should_check_alternative_union_syntax()
+ and node.attrname in UNION_NAMES
+ ):
+ self._check_for_alternative_union_syntax(node, node.attrname)
+
+ def _check_for_alternative_union_syntax(
+ self,
+ node: Union[astroid.Name, astroid.Attribute],
+ name: str,
+ ) -> None:
+ """Check if alternative union syntax could be used.
+
+ Requires
+ - Python 3.10
+ - OR: Python 3.7+ with postponed evaluation in
+ a type annotation context
+ """
+ inferred = safe_infer(node)
+ if not (
+ isinstance(inferred, astroid.FunctionDef)
+ and inferred.qname()
+ in (
+ "typing.Optional",
+ "typing.Union",
+ )
+ or isinstance(inferred, astroid.bases.Instance)
+ and inferred.qname() == "typing._SpecialForm"
+ ):
+ return
+ if not (self._py310_plus() or is_node_in_type_annotation_context(node)):
+ return
+ self.add_message(
+ "consider-alternative-union-syntax",
+ node=node,
+ args=(name, self._msg_postponed_eval_hint(node)),
+ )
+
+ def _check_for_typing_alias(
+ self,
+ node: Union[astroid.Name, astroid.Attribute],
+ ) -> None:
+ """Check if typing alias is depecated or could be replaced.
+
+ Requires
+ - Python 3.9
+ - OR: Python 3.7+ with postponed evaluation in
+ a type annotation context
+
+ For Python 3.7+: Only emitt message if change doesn't create
+ any name collisions, only ever used in a type annotation
+ context, and can safely be replaced.
+ """
+ inferred = safe_infer(node)
+ if not isinstance(inferred, astroid.ClassDef):
+ return
+ alias = DEPRECATED_TYPING_ALIASES.get(inferred.qname(), None)
+ if alias is None:
+ return
+
+ if self._py39_plus():
+ self.add_message(
+ "deprecated-typing-alias",
+ node=node,
+ args=(inferred.qname(), alias.name),
+ )
+ return
+
+ # For PY37+, check for type annotation context first
+ if not is_node_in_type_annotation_context(node) and isinstance(
+ node.parent, astroid.Subscript
+ ):
+ if alias.name_collision is True:
+ self._alias_name_collisions.add(inferred.qname())
+ return
+ self._consider_using_alias_msgs.append(
+ DeprecatedTypingAliasMsg(
+ node,
+ inferred.qname(),
+ alias.name,
+ isinstance(node.parent, astroid.Subscript),
+ )
+ )
+
+ @check_messages("consider-using-alias")
+ def leave_module(self, node: astroid.Module) -> None:
+ """After parsing of module is complete, add messages for
+ 'consider-using-alias' check. Make sure results are safe
+ to recommend / collision free.
+ """
+ if self._py37_plus() and not self._py39_plus():
+ msg_future_import = self._msg_postponed_eval_hint(node)
+ while True:
+ try:
+ msg = self._consider_using_alias_msgs.pop(0)
+ except IndexError:
+ break
+ if msg.qname in self._alias_name_collisions:
+ continue
+ self.add_message(
+ "consider-using-alias",
+ node=msg.node,
+ args=(
+ msg.qname,
+ msg.alias,
+ msg_future_import if msg.parent_subscript else "",
+ ),
+ )
+ # Clear all module cache variables
+ self._alias_name_collisions.clear()
+ self._consider_using_alias_msgs.clear()
+
+
+def register(linter: PyLinter) -> None:
+ linter.register_checker(TypingChecker(linter))
diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py
index 4d4aecc1d..709ef4afc 100644
--- a/pylint/utils/utils.py
+++ b/pylint/utils/utils.py
@@ -245,7 +245,9 @@ def _comment(string):
def _format_option_value(optdict, value):
"""return the user input's value from a 'compiled' value"""
- if isinstance(value, (list, tuple)):
+ if optdict.get("type", None) == "py_version":
+ value = ".".join(str(item) for item in value)
+ elif isinstance(value, (list, tuple)):
value = ",".join(_format_option_value(optdict, item) for item in value)
elif isinstance(value, dict):
value = ",".join(f"{k}:{v}" for k, v in value.items())
diff --git a/tests/functional/t/typing/typing_consider_using_alias.py b/tests/functional/t/typing/typing_consider_using_alias.py
new file mode 100644
index 000000000..7549dd2e4
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_alias.py
@@ -0,0 +1,61 @@
+"""Test pylint.extension.typing - consider-using-alias
+
+'py-version' needs to be set to '3.7' or '3.8' and 'runtime-typing=no'.
+With 'from __future__ import annotations' present.
+"""
+# pylint: disable=missing-docstring,invalid-name,unused-argument,line-too-long
+from __future__ import annotations
+
+import collections
+import collections.abc
+import typing
+from collections.abc import Awaitable
+from dataclasses import dataclass
+from typing import Dict, List, Set, Union, TypedDict
+
+var1: typing.Dict[str, int] # [consider-using-alias]
+var2: List[int] # [consider-using-alias]
+var3: collections.abc.Iterable[int]
+var4: typing.OrderedDict[str, int] # [consider-using-alias]
+var5: typing.Awaitable[None] # [consider-using-alias]
+var6: typing.Iterable[int] # [consider-using-alias]
+var7: typing.Hashable # [consider-using-alias]
+var8: typing.ContextManager[str] # [consider-using-alias]
+var9: typing.Pattern[str] # [consider-using-alias]
+var10: typing.re.Match[str] # [consider-using-alias]
+var11: list[int]
+var12: collections.abc
+var13: Awaitable[None]
+var14: collections.defaultdict[str, str]
+
+Alias1 = Set[int]
+Alias2 = Dict[int, List[int]]
+Alias3 = Union[int, typing.List[str]]
+Alias4 = List # [consider-using-alias]
+
+def func1(arg1: List[int], /, *args: List[int], arg2: set[int], **kwargs: Dict[str, int]) -> typing.Tuple[int]:
+ # -1:[consider-using-alias,consider-using-alias,consider-using-alias,consider-using-alias]
+ pass
+
+def func2(arg1: list[int]) -> tuple[int, int]:
+ pass
+
+class CustomIntList(typing.List[int]):
+ pass
+
+cast_variable = [1, 2, 3]
+cast_variable = typing.cast(List[int], cast_variable)
+
+(lambda x: 2)(List[int])
+
+class CustomNamedTuple(typing.NamedTuple):
+ my_var: List[int] # [consider-using-alias]
+
+CustomTypedDict1 = TypedDict("CustomTypedDict1", my_var=List[int])
+
+class CustomTypedDict2(TypedDict):
+ my_var: List[int] # [consider-using-alias]
+
+@dataclass
+class CustomDataClass:
+ my_var: List[int] # [consider-using-alias]
diff --git a/tests/functional/t/typing/typing_consider_using_alias.rc b/tests/functional/t/typing/typing_consider_using_alias.rc
new file mode 100644
index 000000000..e1f43ca61
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_alias.rc
@@ -0,0 +1,9 @@
+[master]
+load-plugins=pylint.extensions.typing
+
+[testoptions]
+min_pyver=3.8
+
+[typing]
+py-version=3.7
+runtime-typing=no
diff --git a/tests/functional/t/typing/typing_consider_using_alias.txt b/tests/functional/t/typing/typing_consider_using_alias.txt
new file mode 100644
index 000000000..2ae1378f7
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_alias.txt
@@ -0,0 +1,17 @@
+consider-using-alias:16:6::'typing.Dict' will be deprecated with PY39, consider using 'dict' instead
+consider-using-alias:17:6::'typing.List' will be deprecated with PY39, consider using 'list' instead
+consider-using-alias:19:6::'typing.OrderedDict' will be deprecated with PY39, consider using 'collections.OrderedDict' instead
+consider-using-alias:20:6::'typing.Awaitable' will be deprecated with PY39, consider using 'collections.abc.Awaitable' instead
+consider-using-alias:21:6::'typing.Iterable' will be deprecated with PY39, consider using 'collections.abc.Iterable' instead
+consider-using-alias:22:6::'typing.Hashable' will be deprecated with PY39, consider using 'collections.abc.Hashable' instead
+consider-using-alias:23:6::'typing.ContextManager' will be deprecated with PY39, consider using 'contextlib.AbstractContextManager' instead
+consider-using-alias:24:6::'typing.Pattern' will be deprecated with PY39, consider using 're.Pattern' instead
+consider-using-alias:25:7::'typing.Match' will be deprecated with PY39, consider using 're.Match' instead
+consider-using-alias:34:9::'typing.List' will be deprecated with PY39, consider using 'list' instead
+consider-using-alias:36:74:func1:'typing.Dict' will be deprecated with PY39, consider using 'dict' instead
+consider-using-alias:36:16:func1:'typing.List' will be deprecated with PY39, consider using 'list' instead
+consider-using-alias:36:37:func1:'typing.List' will be deprecated with PY39, consider using 'list' instead
+consider-using-alias:36:93:func1:'typing.Tuple' will be deprecated with PY39, consider using 'tuple' instead
+consider-using-alias:52:12:CustomNamedTuple:'typing.List' will be deprecated with PY39, consider using 'list' instead
+consider-using-alias:57:12:CustomTypedDict2:'typing.List' will be deprecated with PY39, consider using 'list' instead
+consider-using-alias:61:12:CustomDataClass:'typing.List' will be deprecated with PY39, consider using 'list' instead
diff --git a/tests/functional/t/typing/typing_consider_using_alias_without_future.py b/tests/functional/t/typing/typing_consider_using_alias_without_future.py
new file mode 100644
index 000000000..44f55b8b4
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_alias_without_future.py
@@ -0,0 +1,58 @@
+"""Test pylint.extension.typing - consider-using-alias
+
+'py-version' needs to be set to '3.7' or '3.8' and 'runtime-typing=no'.
+"""
+# pylint: disable=missing-docstring,invalid-name,unused-argument,line-too-long,unsubscriptable-object
+import collections
+import collections.abc
+import typing
+from collections.abc import Awaitable
+from dataclasses import dataclass
+from typing import Dict, List, Set, Union, TypedDict
+
+var1: typing.Dict[str, int] # [consider-using-alias]
+var2: List[int] # [consider-using-alias]
+var3: collections.abc.Iterable[int]
+var4: typing.OrderedDict[str, int] # [consider-using-alias]
+var5: typing.Awaitable[None] # [consider-using-alias]
+var6: typing.Iterable[int] # [consider-using-alias]
+var7: typing.Hashable # [consider-using-alias]
+var8: typing.ContextManager[str] # [consider-using-alias]
+var9: typing.Pattern[str] # [consider-using-alias]
+var10: typing.re.Match[str] # [consider-using-alias]
+var11: list[int]
+var12: collections.abc
+var13: Awaitable[None]
+var14: collections.defaultdict[str, str]
+
+Alias1 = Set[int]
+Alias2 = Dict[int, List[int]]
+Alias3 = Union[int, typing.List[str]]
+Alias4 = List # [consider-using-alias]
+
+def func1(arg1: List[int], /, *args: List[int], arg2: set[int], **kwargs: Dict[str, int]) -> typing.Tuple[int]:
+ # -1:[consider-using-alias,consider-using-alias,consider-using-alias,consider-using-alias]
+ pass
+
+def func2(arg1: list[int]) -> tuple[int, int]:
+ pass
+
+class CustomIntList(typing.List[int]):
+ pass
+
+cast_variable = [1, 2, 3]
+cast_variable = typing.cast(List[int], cast_variable)
+
+(lambda x: 2)(List[int])
+
+class CustomNamedTuple(typing.NamedTuple):
+ my_var: List[int] # [consider-using-alias]
+
+CustomTypedDict1 = TypedDict("CustomTypedDict1", my_var=List[int])
+
+class CustomTypedDict2(TypedDict):
+ my_var: List[int] # [consider-using-alias]
+
+@dataclass
+class CustomDataClass:
+ my_var: List[int] # [consider-using-alias]
diff --git a/tests/functional/t/typing/typing_consider_using_alias_without_future.rc b/tests/functional/t/typing/typing_consider_using_alias_without_future.rc
new file mode 100644
index 000000000..e1f43ca61
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_alias_without_future.rc
@@ -0,0 +1,9 @@
+[master]
+load-plugins=pylint.extensions.typing
+
+[testoptions]
+min_pyver=3.8
+
+[typing]
+py-version=3.7
+runtime-typing=no
diff --git a/tests/functional/t/typing/typing_consider_using_alias_without_future.txt b/tests/functional/t/typing/typing_consider_using_alias_without_future.txt
new file mode 100644
index 000000000..c7105b965
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_alias_without_future.txt
@@ -0,0 +1,17 @@
+consider-using-alias:13:6::'typing.Dict' will be deprecated with PY39, consider using 'dict' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:14:6::'typing.List' will be deprecated with PY39, consider using 'list' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:16:6::'typing.OrderedDict' will be deprecated with PY39, consider using 'collections.OrderedDict' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:17:6::'typing.Awaitable' will be deprecated with PY39, consider using 'collections.abc.Awaitable' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:18:6::'typing.Iterable' will be deprecated with PY39, consider using 'collections.abc.Iterable' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:19:6::'typing.Hashable' will be deprecated with PY39, consider using 'collections.abc.Hashable' instead
+consider-using-alias:20:6::'typing.ContextManager' will be deprecated with PY39, consider using 'contextlib.AbstractContextManager' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:21:6::'typing.Pattern' will be deprecated with PY39, consider using 're.Pattern' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:22:7::'typing.Match' will be deprecated with PY39, consider using 're.Match' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:31:9::'typing.List' will be deprecated with PY39, consider using 'list' instead
+consider-using-alias:33:74:func1:'typing.Dict' will be deprecated with PY39, consider using 'dict' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:33:16:func1:'typing.List' will be deprecated with PY39, consider using 'list' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:33:37:func1:'typing.List' will be deprecated with PY39, consider using 'list' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:33:93:func1:'typing.Tuple' will be deprecated with PY39, consider using 'tuple' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:49:12:CustomNamedTuple:'typing.List' will be deprecated with PY39, consider using 'list' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:54:12:CustomTypedDict2:'typing.List' will be deprecated with PY39, consider using 'list' instead. Add 'from __future__ import annotations' as well
+consider-using-alias:58:12:CustomDataClass:'typing.List' will be deprecated with PY39, consider using 'list' instead. Add 'from __future__ import annotations' as well
diff --git a/tests/functional/t/typing/typing_consider_using_union.py b/tests/functional/t/typing/typing_consider_using_union.py
new file mode 100644
index 000000000..2f27b40da
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_union.py
@@ -0,0 +1,47 @@
+"""Test pylint.extension.typing - consider-alternative-union-syntax
+
+'py-version' needs to be set to >= '3.7' and 'runtime-typing=no'.
+With 'from __future__ import annotations' present.
+"""
+# pylint: disable=missing-docstring,invalid-name,unused-argument,line-too-long
+# pylint: disable=consider-using-alias
+from __future__ import annotations
+from dataclasses import dataclass
+import typing
+from typing import Dict, List, Optional, Union, TypedDict
+
+var1: Union[int, str] # [consider-alternative-union-syntax]
+var2: List[Union[int, None]] # [consider-alternative-union-syntax]
+var3: Dict[str, typing.Union[int, str]] # [consider-alternative-union-syntax]
+var4: Optional[int] # [consider-alternative-union-syntax]
+
+Alias1 = Union[int, str]
+Alias2 = List[Union[int, None]]
+Alias3 = Dict[str, typing.Union[int, str]]
+Alias4 = Optional[int]
+
+def func1(
+ arg1: Optional[int], # [consider-alternative-union-syntax]
+ **kwargs: Dict[str, Union[int, str]] # [consider-alternative-union-syntax]
+) -> Union[str, None]: # [consider-alternative-union-syntax]
+ pass
+
+class Custom1(List[Union[str, int]]):
+ pass
+
+cast_variable = [1, 2, 3]
+cast_variable = typing.cast(Union[List[int], None], cast_variable)
+
+(lambda x: 2)(Optional[int])
+
+class CustomNamedTuple(typing.NamedTuple):
+ my_var: Union[int, str] # [consider-alternative-union-syntax]
+
+CustomTypedDict1 = TypedDict("CustomTypedDict1", my_var=Optional[int])
+
+class CustomTypedDict2(TypedDict):
+ my_var: Dict[str, List[Union[str, int]]] # [consider-alternative-union-syntax]
+
+@dataclass
+class CustomDataClass:
+ my_var: Optional[int] # [consider-alternative-union-syntax]
diff --git a/tests/functional/t/typing/typing_consider_using_union.rc b/tests/functional/t/typing/typing_consider_using_union.rc
new file mode 100644
index 000000000..e1f43ca61
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_union.rc
@@ -0,0 +1,9 @@
+[master]
+load-plugins=pylint.extensions.typing
+
+[testoptions]
+min_pyver=3.8
+
+[typing]
+py-version=3.7
+runtime-typing=no
diff --git a/tests/functional/t/typing/typing_consider_using_union.txt b/tests/functional/t/typing/typing_consider_using_union.txt
new file mode 100644
index 000000000..330a7165f
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_union.txt
@@ -0,0 +1,10 @@
+consider-alternative-union-syntax:13:6::Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:14:11::Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:15:16::Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:16:6::Consider using alternative Union syntax instead of 'Optional'
+consider-alternative-union-syntax:24:10:func1:Consider using alternative Union syntax instead of 'Optional'
+consider-alternative-union-syntax:25:24:func1:Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:26:5:func1:Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:38:12:CustomNamedTuple:Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:43:27:CustomTypedDict2:Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:47:12:CustomDataClass:Consider using alternative Union syntax instead of 'Optional'
diff --git a/tests/functional/t/typing/typing_consider_using_union_py310.py b/tests/functional/t/typing/typing_consider_using_union_py310.py
new file mode 100644
index 000000000..1018c3670
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_union_py310.py
@@ -0,0 +1,45 @@
+"""Test pylint.extension.typing - consider-alternative-union-syntax
+
+'py-version' needs to be set to >= '3.10'.
+"""
+# pylint: disable=missing-docstring,invalid-name,unused-argument,line-too-long
+# pylint: disable=deprecated-typing-alias
+from dataclasses import dataclass
+import typing
+from typing import Dict, List, Optional, Union, TypedDict
+
+var1: Union[int, str] # [consider-alternative-union-syntax]
+var2: List[Union[int, None]] # [consider-alternative-union-syntax]
+var3: Dict[str, typing.Union[int, str]] # [consider-alternative-union-syntax]
+var4: Optional[int] # [consider-alternative-union-syntax]
+
+Alias1 = Union[int, str] # [consider-alternative-union-syntax]
+Alias2 = List[Union[int, None]] # [consider-alternative-union-syntax]
+Alias3 = Dict[str, typing.Union[int, str]] # [consider-alternative-union-syntax]
+Alias4 = Optional[int] # [consider-alternative-union-syntax]
+
+def func1(
+ arg1: Optional[int], # [consider-alternative-union-syntax]
+ **kwargs: Dict[str, Union[int, str]] # [consider-alternative-union-syntax]
+) -> Union[str, None]: # [consider-alternative-union-syntax]
+ pass
+
+class Custom1(List[Union[str, int]]): # [consider-alternative-union-syntax]
+ pass
+
+cast_variable = [1, 2, 3]
+cast_variable = typing.cast(Union[List[int], None], cast_variable) # [consider-alternative-union-syntax]
+
+(lambda x: 2)(Optional[int]) # [consider-alternative-union-syntax]
+
+class CustomNamedTuple(typing.NamedTuple):
+ my_var: Union[int, str] # [consider-alternative-union-syntax]
+
+CustomTypedDict1 = TypedDict("CustomTypedDict1", my_var=Optional[int]) # [consider-alternative-union-syntax]
+
+class CustomTypedDict2(TypedDict):
+ my_var: Dict[str, List[Union[str, int]]] # [consider-alternative-union-syntax]
+
+@dataclass
+class CustomDataClass:
+ my_var: Optional[int] # [consider-alternative-union-syntax]
diff --git a/tests/functional/t/typing/typing_consider_using_union_py310.rc b/tests/functional/t/typing/typing_consider_using_union_py310.rc
new file mode 100644
index 000000000..7400f16d6
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_union_py310.rc
@@ -0,0 +1,8 @@
+[master]
+load-plugins=pylint.extensions.typing
+
+[testoptions]
+min_pyver=3.8
+
+[typing]
+py-version=3.10
diff --git a/tests/functional/t/typing/typing_consider_using_union_py310.txt b/tests/functional/t/typing/typing_consider_using_union_py310.txt
new file mode 100644
index 000000000..4402eb218
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_union_py310.txt
@@ -0,0 +1,18 @@
+consider-alternative-union-syntax:11:6::Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:12:11::Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:13:16::Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:14:6::Consider using alternative Union syntax instead of 'Optional'
+consider-alternative-union-syntax:16:9::Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:17:14::Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:18:19::Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:19:9::Consider using alternative Union syntax instead of 'Optional'
+consider-alternative-union-syntax:22:10:func1:Consider using alternative Union syntax instead of 'Optional'
+consider-alternative-union-syntax:23:24:func1:Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:24:5:func1:Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:27:19:Custom1:Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:31:28::Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:33:14::Consider using alternative Union syntax instead of 'Optional'
+consider-alternative-union-syntax:36:12:CustomNamedTuple:Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:38:56::Consider using alternative Union syntax instead of 'Optional'
+consider-alternative-union-syntax:41:27:CustomTypedDict2:Consider using alternative Union syntax instead of 'Union'
+consider-alternative-union-syntax:45:12:CustomDataClass:Consider using alternative Union syntax instead of 'Optional'
diff --git a/tests/functional/t/typing/typing_consider_using_union_without_future.py b/tests/functional/t/typing/typing_consider_using_union_without_future.py
new file mode 100644
index 000000000..f459d047b
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_union_without_future.py
@@ -0,0 +1,45 @@
+"""Test pylint.extension.typing - consider-alternative-union-syntax
+
+'py-version' needs to be set to >= '3.7' and 'runtime-typing=no'.
+"""
+# pylint: disable=missing-docstring,invalid-name,unused-argument,line-too-long
+# pylint: disable=consider-using-alias
+from dataclasses import dataclass
+import typing
+from typing import Dict, List, Optional, Union, TypedDict
+
+var1: Union[int, str] # [consider-alternative-union-syntax]
+var2: List[Union[int, None]] # [consider-alternative-union-syntax]
+var3: Dict[str, typing.Union[int, str]] # [consider-alternative-union-syntax]
+var4: Optional[int] # [consider-alternative-union-syntax]
+
+Alias1 = Union[int, str]
+Alias2 = List[Union[int, None]]
+Alias3 = Dict[str, typing.Union[int, str]]
+Alias4 = Optional[int]
+
+def func1(
+ arg1: Optional[int], # [consider-alternative-union-syntax]
+ **kwargs: Dict[str, Union[int, str]] # [consider-alternative-union-syntax]
+) -> Union[str, None]: # [consider-alternative-union-syntax]
+ pass
+
+class Custom1(List[Union[str, int]]):
+ pass
+
+cast_variable = [1, 2, 3]
+cast_variable = typing.cast(Union[List[int], None], cast_variable)
+
+(lambda x: 2)(Optional[int])
+
+class CustomNamedTuple(typing.NamedTuple):
+ my_var: Union[int, str] # [consider-alternative-union-syntax]
+
+CustomTypedDict1 = TypedDict("CustomTypedDict1", my_var=Optional[int])
+
+class CustomTypedDict2(TypedDict):
+ my_var: Dict[str, List[Union[str, int]]] # [consider-alternative-union-syntax]
+
+@dataclass
+class CustomDataClass:
+ my_var: Optional[int] # [consider-alternative-union-syntax]
diff --git a/tests/functional/t/typing/typing_consider_using_union_without_future.rc b/tests/functional/t/typing/typing_consider_using_union_without_future.rc
new file mode 100644
index 000000000..e1f43ca61
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_union_without_future.rc
@@ -0,0 +1,9 @@
+[master]
+load-plugins=pylint.extensions.typing
+
+[testoptions]
+min_pyver=3.8
+
+[typing]
+py-version=3.7
+runtime-typing=no
diff --git a/tests/functional/t/typing/typing_consider_using_union_without_future.txt b/tests/functional/t/typing/typing_consider_using_union_without_future.txt
new file mode 100644
index 000000000..c43cbc3ce
--- /dev/null
+++ b/tests/functional/t/typing/typing_consider_using_union_without_future.txt
@@ -0,0 +1,10 @@
+consider-alternative-union-syntax:11:6::Consider using alternative Union syntax instead of 'Union'. Add 'from __future__ import annotations' as well
+consider-alternative-union-syntax:12:11::Consider using alternative Union syntax instead of 'Union'. Add 'from __future__ import annotations' as well
+consider-alternative-union-syntax:13:16::Consider using alternative Union syntax instead of 'Union'. Add 'from __future__ import annotations' as well
+consider-alternative-union-syntax:14:6::Consider using alternative Union syntax instead of 'Optional'. Add 'from __future__ import annotations' as well
+consider-alternative-union-syntax:22:10:func1:Consider using alternative Union syntax instead of 'Optional'. Add 'from __future__ import annotations' as well
+consider-alternative-union-syntax:23:24:func1:Consider using alternative Union syntax instead of 'Union'. Add 'from __future__ import annotations' as well
+consider-alternative-union-syntax:24:5:func1:Consider using alternative Union syntax instead of 'Union'. Add 'from __future__ import annotations' as well
+consider-alternative-union-syntax:36:12:CustomNamedTuple:Consider using alternative Union syntax instead of 'Union'. Add 'from __future__ import annotations' as well
+consider-alternative-union-syntax:41:27:CustomTypedDict2:Consider using alternative Union syntax instead of 'Union'. Add 'from __future__ import annotations' as well
+consider-alternative-union-syntax:45:12:CustomDataClass:Consider using alternative Union syntax instead of 'Optional'. Add 'from __future__ import annotations' as well
diff --git a/tests/functional/t/typing/typing_deprecated_alias.py b/tests/functional/t/typing/typing_deprecated_alias.py
new file mode 100644
index 000000000..3cd139cdb
--- /dev/null
+++ b/tests/functional/t/typing/typing_deprecated_alias.py
@@ -0,0 +1,58 @@
+"""Test pylint.extension.typing - deprecated-typing-alias
+
+'py-version' needs to be set to >= '3.9'.
+"""
+# pylint: disable=missing-docstring,invalid-name,unused-argument,line-too-long,unsubscriptable-object
+import collections
+import collections.abc
+import typing
+from collections.abc import Awaitable
+from dataclasses import dataclass
+from typing import Dict, List, Set, Union, TypedDict
+
+var1: typing.Dict[str, int] # [deprecated-typing-alias]
+var2: List[int] # [deprecated-typing-alias]
+var3: collections.abc.Iterable[int]
+var4: typing.OrderedDict[str, int] # [deprecated-typing-alias]
+var5: typing.Awaitable[None] # [deprecated-typing-alias]
+var6: typing.Iterable[int] # [deprecated-typing-alias]
+var7: typing.Hashable # [deprecated-typing-alias]
+var8: typing.ContextManager[str] # [deprecated-typing-alias]
+var9: typing.Pattern[str] # [deprecated-typing-alias]
+var10: typing.re.Match[str] # [deprecated-typing-alias]
+var11: list[int]
+var12: collections.abc
+var13: Awaitable[None]
+var14: collections.defaultdict[str, str]
+
+Alias1 = Set[int] # [deprecated-typing-alias]
+Alias2 = Dict[int, List[int]] # [deprecated-typing-alias,deprecated-typing-alias]
+Alias3 = Union[int, typing.List[str]] # [deprecated-typing-alias]
+Alias4 = List # [deprecated-typing-alias]
+
+def func1(arg1: List[int], /, *args: List[int], arg2: set[int], **kwargs: Dict[str, int]) -> typing.Tuple[int]:
+ # -1:[deprecated-typing-alias,deprecated-typing-alias,deprecated-typing-alias,deprecated-typing-alias]
+ pass
+
+def func2(arg1: list[int]) -> tuple[int, int]:
+ pass
+
+class CustomIntList(typing.List[int]): # [deprecated-typing-alias]
+ pass
+
+cast_variable = [1, 2, 3]
+cast_variable = typing.cast(List[int], cast_variable) # [deprecated-typing-alias]
+
+(lambda x: 2)(List[int]) # [deprecated-typing-alias]
+
+class CustomNamedTuple(typing.NamedTuple):
+ my_var: List[int] # [deprecated-typing-alias]
+
+CustomTypedDict1 = TypedDict("CustomTypedDict1", my_var=List[int]) # [deprecated-typing-alias]
+
+class CustomTypedDict2(TypedDict):
+ my_var: List[int] # [deprecated-typing-alias]
+
+@dataclass
+class CustomDataClass:
+ my_var: List[int] # [deprecated-typing-alias]
diff --git a/tests/functional/t/typing/typing_deprecated_alias.rc b/tests/functional/t/typing/typing_deprecated_alias.rc
new file mode 100644
index 000000000..2d21d503c
--- /dev/null
+++ b/tests/functional/t/typing/typing_deprecated_alias.rc
@@ -0,0 +1,8 @@
+[master]
+load-plugins=pylint.extensions.typing
+
+[testoptions]
+min_pyver=3.8
+
+[typing]
+py-version=3.9
diff --git a/tests/functional/t/typing/typing_deprecated_alias.txt b/tests/functional/t/typing/typing_deprecated_alias.txt
new file mode 100644
index 000000000..fa74fc161
--- /dev/null
+++ b/tests/functional/t/typing/typing_deprecated_alias.txt
@@ -0,0 +1,25 @@
+deprecated-typing-alias:13:6::'typing.Dict' is deprecated, use 'dict' instead
+deprecated-typing-alias:14:6::'typing.List' is deprecated, use 'list' instead
+deprecated-typing-alias:16:6::'typing.OrderedDict' is deprecated, use 'collections.OrderedDict' instead
+deprecated-typing-alias:17:6::'typing.Awaitable' is deprecated, use 'collections.abc.Awaitable' instead
+deprecated-typing-alias:18:6::'typing.Iterable' is deprecated, use 'collections.abc.Iterable' instead
+deprecated-typing-alias:19:6::'typing.Hashable' is deprecated, use 'collections.abc.Hashable' instead
+deprecated-typing-alias:20:6::'typing.ContextManager' is deprecated, use 'contextlib.AbstractContextManager' instead
+deprecated-typing-alias:21:6::'typing.Pattern' is deprecated, use 're.Pattern' instead
+deprecated-typing-alias:22:7::'typing.Match' is deprecated, use 're.Match' instead
+deprecated-typing-alias:28:9::'typing.Set' is deprecated, use 'set' instead
+deprecated-typing-alias:29:9::'typing.Dict' is deprecated, use 'dict' instead
+deprecated-typing-alias:29:19::'typing.List' is deprecated, use 'list' instead
+deprecated-typing-alias:30:20::'typing.List' is deprecated, use 'list' instead
+deprecated-typing-alias:31:9::'typing.List' is deprecated, use 'list' instead
+deprecated-typing-alias:33:74:func1:'typing.Dict' is deprecated, use 'dict' instead
+deprecated-typing-alias:33:16:func1:'typing.List' is deprecated, use 'list' instead
+deprecated-typing-alias:33:37:func1:'typing.List' is deprecated, use 'list' instead
+deprecated-typing-alias:33:93:func1:'typing.Tuple' is deprecated, use 'tuple' instead
+deprecated-typing-alias:40:20:CustomIntList:'typing.List' is deprecated, use 'list' instead
+deprecated-typing-alias:44:28::'typing.List' is deprecated, use 'list' instead
+deprecated-typing-alias:46:14::'typing.List' is deprecated, use 'list' instead
+deprecated-typing-alias:49:12:CustomNamedTuple:'typing.List' is deprecated, use 'list' instead
+deprecated-typing-alias:51:56::'typing.List' is deprecated, use 'list' instead
+deprecated-typing-alias:54:12:CustomTypedDict2:'typing.List' is deprecated, use 'list' instead
+deprecated-typing-alias:58:12:CustomDataClass:'typing.List' is deprecated, use 'list' instead