summaryrefslogtreecommitdiff
path: root/pylint
diff options
context:
space:
mode:
authorDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2023-02-26 21:47:30 +0100
committerGitHub <noreply@github.com>2023-02-26 21:47:30 +0100
commite7ad3e64d36e450a9133c64e804c921d0fd0f204 (patch)
tree5db8215be4d052ccd7be2d2a5cbafe7ccdb89be5 /pylint
parent52a2a04a59526ce8344d4d2b7f86bb978177e047 (diff)
downloadpylint-git-e7ad3e64d36e450a9133c64e804c921d0fd0f204.tar.gz
Add ``invalid-name`` check for ``TypeAlias`` names (#7116)
Diffstat (limited to 'pylint')
-rw-r--r--pylint/checkers/bad_chained_comparison.py3
-rw-r--r--pylint/checkers/base/name_checker/checker.py71
-rw-r--r--pylint/checkers/base/name_checker/naming_style.py1
-rw-r--r--pylint/constants.py1
-rw-r--r--pylint/utils/linterstats.py7
5 files changed, 61 insertions, 22 deletions
diff --git a/pylint/checkers/bad_chained_comparison.py b/pylint/checkers/bad_chained_comparison.py
index fa75c52b2..8c3aeb9cc 100644
--- a/pylint/checkers/bad_chained_comparison.py
+++ b/pylint/checkers/bad_chained_comparison.py
@@ -24,7 +24,7 @@ class BadChainedComparisonChecker(BaseChecker):
name = "bad-chained-comparison"
msgs = {
- "W3501": (
+ "W3601": (
"Suspicious %s-part chained comparison using semantically incompatible operators (%s)",
"bad-chained-comparison",
"Used when there is a chained comparison where one expression is part "
@@ -35,7 +35,6 @@ class BadChainedComparisonChecker(BaseChecker):
}
def _has_diff_semantic_groups(self, operators: list[str]) -> bool:
-
# Check if comparison operators are in the same semantic group
for semantic_group in (COMPARISON_OP, IDENTITY_OP, MEMBERSHIP_OP):
if operators[0] in semantic_group:
diff --git a/pylint/checkers/base/name_checker/checker.py b/pylint/checkers/base/name_checker/checker.py
index cdbf4a7b5..f1fd8d379 100644
--- a/pylint/checkers/base/name_checker/checker.py
+++ b/pylint/checkers/base/name_checker/checker.py
@@ -40,7 +40,8 @@ _BadNamesTuple = Tuple[nodes.NodeNG, str, str, interfaces.Confidence]
DEFAULT_PATTERNS = {
"typevar": re.compile(
r"^_{0,2}(?!T[A-Z])(?:[A-Z]+|(?:[A-Z]+[a-z]+)+T?(?<!Type))(?:_co(?:ntra)?)?$"
- )
+ ),
+ "typealias": re.compile(r"^_{0,2}(?!T[A-Z]|Type)[A-Z]+[a-z]+(?:[A-Z][a-z]+)*$"),
}
BUILTIN_PROPERTY = "builtins.property"
@@ -400,7 +401,9 @@ class NameChecker(_BasicChecker):
"typevar-double-variance",
"typevar-name-mismatch",
)
- def visit_assignname(self, node: nodes.AssignName) -> None:
+ def visit_assignname( # pylint: disable=too-many-branches
+ self, node: nodes.AssignName
+ ) -> None:
"""Check module level assigned names."""
frame = node.frame(future=True)
assign_type = node.assign_type()
@@ -415,25 +418,40 @@ class NameChecker(_BasicChecker):
if isinstance(assign_type, nodes.Assign):
inferred_assign_type = utils.safe_infer(assign_type.value)
- # Check TypeVar's assigned alone or in tuple assignment
- if isinstance(node.parent, nodes.Assign) and self._assigns_typevar(
- assign_type.value
- ):
- self._check_name("typevar", assign_type.targets[0].name, node)
- elif (
+ # Check TypeVar's and TypeAliases assigned alone or in tuple assignment
+ if isinstance(node.parent, nodes.Assign):
+ if self._assigns_typevar(assign_type.value):
+ self._check_name("typevar", assign_type.targets[0].name, node)
+ return
+ if self._assigns_typealias(assign_type.value):
+ self._check_name("typealias", assign_type.targets[0].name, node)
+ return
+
+ if (
isinstance(node.parent, nodes.Tuple)
and isinstance(assign_type.value, nodes.Tuple)
# protect against unbalanced tuple unpacking
and node.parent.elts.index(node) < len(assign_type.value.elts)
- and self._assigns_typevar(
- assign_type.value.elts[node.parent.elts.index(node)]
- )
):
- self._check_name(
- "typevar",
- assign_type.targets[0].elts[node.parent.elts.index(node)].name,
- node,
- )
+ assigner = assign_type.value.elts[node.parent.elts.index(node)]
+ if self._assigns_typevar(assigner):
+ self._check_name(
+ "typevar",
+ assign_type.targets[0]
+ .elts[node.parent.elts.index(node)]
+ .name,
+ node,
+ )
+ return
+ if self._assigns_typealias(assigner):
+ self._check_name(
+ "typealias",
+ assign_type.targets[0]
+ .elts[node.parent.elts.index(node)]
+ .name,
+ node,
+ )
+ return
# Check classes (TypeVar's are classes so they need to be excluded first)
elif isinstance(inferred_assign_type, nodes.ClassDef):
@@ -450,10 +468,11 @@ class NameChecker(_BasicChecker):
)
# Check names defined in AnnAssign nodes
- elif isinstance(
- assign_type, nodes.AnnAssign
- ) and utils.is_assign_name_annotated_with(node, "Final"):
- self._check_name("const", node.name, node)
+ elif isinstance(assign_type, nodes.AnnAssign):
+ if utils.is_assign_name_annotated_with(node, "Final"):
+ self._check_name("const", node.name, node)
+ elif self._assigns_typealias(assign_type.annotation):
+ self._check_name("typealias", node.name, node)
# Check names defined in function scopes
elif isinstance(frame, nodes.FunctionDef):
@@ -577,6 +596,18 @@ class NameChecker(_BasicChecker):
return True
return False
+ @staticmethod
+ def _assigns_typealias(node: nodes.NodeNG | None) -> bool:
+ """Check if a node is assigning a TypeAlias."""
+ inferred = utils.safe_infer(node)
+ if isinstance(inferred, nodes.ClassDef):
+ if inferred.qname() == ".Union":
+ return True
+ elif isinstance(inferred, nodes.FunctionDef):
+ if inferred.qname() == "typing.TypeAlias":
+ return True
+ return False
+
def _check_typevar(self, name: str, node: nodes.AssignName) -> None:
"""Check for TypeVar lint violations."""
if isinstance(node.parent, nodes.Assign):
diff --git a/pylint/checkers/base/name_checker/naming_style.py b/pylint/checkers/base/name_checker/naming_style.py
index bfe6f77d3..8ccec4289 100644
--- a/pylint/checkers/base/name_checker/naming_style.py
+++ b/pylint/checkers/base/name_checker/naming_style.py
@@ -137,6 +137,7 @@ DEFAULT_NAMING_STYLES = {
KNOWN_NAME_TYPES = {
*KNOWN_NAME_TYPES_WITH_STYLE,
"typevar",
+ "typealias",
}
diff --git a/pylint/constants.py b/pylint/constants.py
index 92a9990d1..ad7fc2256 100644
--- a/pylint/constants.py
+++ b/pylint/constants.py
@@ -80,6 +80,7 @@ HUMAN_READABLE_TYPES = {
"class_const": "class constant",
"inlinevar": "inline iteration",
"typevar": "type variable",
+ "typealias": "type alias",
}
# ignore some messages when emitting useless-suppression:
diff --git a/pylint/utils/linterstats.py b/pylint/utils/linterstats.py
index 11338850c..f9a6c6c3c 100644
--- a/pylint/utils/linterstats.py
+++ b/pylint/utils/linterstats.py
@@ -30,6 +30,7 @@ class BadNames(TypedDict):
module: int
variable: int
typevar: int
+ typealias: int
class CodeTypeCount(TypedDict):
@@ -107,6 +108,7 @@ class LinterStats:
module=0,
variable=0,
typevar=0,
+ typealias=0,
)
self.by_module: dict[str, ModuleStats] = by_module or {}
self.by_msg: dict[str, int] = by_msg or {}
@@ -182,6 +184,7 @@ class LinterStats:
"module",
"variable",
"typevar",
+ "typealias",
],
) -> int:
"""Get a bad names node count."""
@@ -204,6 +207,7 @@ class LinterStats:
"module",
"variable",
"typevar",
+ "typealias",
}:
raise ValueError("Node type not part of the bad_names stat")
@@ -221,6 +225,7 @@ class LinterStats:
"module",
"variable",
"typevar",
+ "typealias",
],
node_name,
)
@@ -244,6 +249,7 @@ class LinterStats:
module=0,
variable=0,
typevar=0,
+ typealias=0,
)
def get_code_count(
@@ -336,6 +342,7 @@ def merge_stats(stats: list[LinterStats]) -> LinterStats:
merged.bad_names["module"] += stat.bad_names["module"]
merged.bad_names["variable"] += stat.bad_names["variable"]
merged.bad_names["typevar"] += stat.bad_names["typevar"]
+ merged.bad_names["typealias"] += stat.bad_names["typealias"]
for mod_key, mod_value in stat.by_module.items():
merged.by_module[mod_key] = mod_value