diff --git a/doc/whatsnew/fragments/10559.new_check b/doc/whatsnew/fragments/10559.new_check index 7f463df68c..e06b219f8f 100644 --- a/doc/whatsnew/fragments/10559.new_check +++ b/doc/whatsnew/fragments/10559.new_check @@ -1,6 +1,6 @@ -Add new checks for invalid uses of class patterns in ``match``. -* ``invalid-match-args-definition`` is emitted if ``__match_args__`` isn't a tuple of strings. -* ``too-many-positional-sub-patterns`` if there are more positional sub-patterns than specified in ``__match_args__``. -* ``multiple-class-sub-patterns`` if there are multiple sub-patterns for the same attribute. +Add new checks for invalid uses of class patterns in :keyword:`match`. +* :ref:`invalid-match-args-definition` is emitted if :py:data:`object.__match_args__` isn't a tuple of strings. +* :ref:`too-many-positional-sub-patterns` if there are more positional sub-patterns than specified in :py:data:`object.__match_args__`. +* :ref:`multiple-class-sub-patterns` if there are multiple sub-patterns for the same attribute. Refs #10559 diff --git a/pylint/checkers/base/basic_checker.py b/pylint/checkers/base/basic_checker.py index 3afdecaeb7..602861cd64 100644 --- a/pylint/checkers/base/basic_checker.py +++ b/pylint/checkers/base/basic_checker.py @@ -717,7 +717,7 @@ def visit_assert(self, node: nodes.Assert) -> None: match node.test: case nodes.Tuple(elts=elts) if len(elts) > 0: self.add_message("assert-on-tuple", node=node, confidence=HIGH) - case nodes.Const(value=str(val)): + case nodes.Const(value=str() as val): when = "never" if val else "always" self.add_message("assert-on-string-literal", node=node, args=(when,)) diff --git a/pylint/checkers/base/comparison_checker.py b/pylint/checkers/base/comparison_checker.py index 2bde5475b4..ca4b404ec1 100644 --- a/pylint/checkers/base/comparison_checker.py +++ b/pylint/checkers/base/comparison_checker.py @@ -146,7 +146,7 @@ def _check_nan_comparison( def _is_float_nan(node: nodes.NodeNG) -> bool: try: match node: - case nodes.Call(args=[nodes.Const(value=str(value))]) if ( + case nodes.Call(args=[nodes.Const(value=str() as value)]) if ( value.lower() == "nan" ): return node.inferred()[0].pytype() == "builtins.float" # type: ignore[no-any-return] diff --git a/pylint/checkers/base/name_checker/checker.py b/pylint/checkers/base/name_checker/checker.py index aba283044c..8685d6a6a2 100644 --- a/pylint/checkers/base/name_checker/checker.py +++ b/pylint/checkers/base/name_checker/checker.py @@ -17,6 +17,7 @@ from typing import TYPE_CHECKING import astroid +import astroid.bases from astroid import nodes from astroid.typing import InferenceResult diff --git a/pylint/checkers/classes/class_checker.py b/pylint/checkers/classes/class_checker.py index 1cb0ee4990..8cb45fc39b 100644 --- a/pylint/checkers/classes/class_checker.py +++ b/pylint/checkers/classes/class_checker.py @@ -14,6 +14,7 @@ from typing import TYPE_CHECKING, Any, NamedTuple, TypeAlias import astroid +import astroid.objects from astroid import bases, nodes, util from astroid.nodes import LocalsDictNodeNG from astroid.typing import SuccessfulInferenceResult @@ -1641,7 +1642,7 @@ def _check_slots_elt( match inferred: case util.UninferableBase(): continue - case nodes.Const(value=str(value)) if value: + case nodes.Const(value=str() as value) if value: pass case _: self.add_message( diff --git a/pylint/checkers/refactoring/refactoring_checker.py b/pylint/checkers/refactoring/refactoring_checker.py index 37ce125c69..a6b234adbd 100644 --- a/pylint/checkers/refactoring/refactoring_checker.py +++ b/pylint/checkers/refactoring/refactoring_checker.py @@ -14,6 +14,7 @@ from typing import TYPE_CHECKING, Any, NamedTuple, TypeAlias, cast import astroid +import astroid.objects from astroid import bases, nodes from astroid.util import UninferableBase @@ -1125,7 +1126,6 @@ def _check_consider_using_generator(self, node: nodes.Call) -> None: inside_comp = f"({inside_comp})" inside_comp += ", " inside_comp += ", ".join(kw.as_string() for kw in node.keywords) - call_name = node.func.name if call_name in {"any", "all"}: self.add_message( "use-a-generator", @@ -2043,7 +2043,7 @@ def _is_node_return_ended(self, node: nodes.NodeNG) -> bool: return any( self._is_node_return_ended(_child) for _child in all_but_handler ) and all(self._is_node_return_ended(_child) for _child in handlers) - case nodes.Assert(test=nodes.Const(value=value)) if not value: + case nodes.Assert(test=nodes.Const(value=False | 0)): # consider assert False as a return node return True # recurses on the children of the node diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 428a702488..fd8de5e3ce 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -1755,10 +1755,9 @@ def _check_invalid_sequence_index(self, subscript: nodes.Subscript) -> None: if index_type is None or isinstance(index_type, util.UninferableBase): return None match index_type: - case nodes.Const(): + case nodes.Const(value=int()): # Constants must be of type int - if isinstance(index_type.value, int): - return None + return None case astroid.Instance(): # Instance values must be int, slice, or have an __index__ method if index_type.pytype() in {"builtins.int", "builtins.slice"}: diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 88c5bce0f6..8976a97521 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -1704,8 +1704,7 @@ def is_test_condition( parent: nodes.NodeNG | None = None, ) -> bool: """Returns true if the given node is being tested for truthiness.""" - parent = parent or node.parent - match parent: + match parent := parent or node.parent: case nodes.While() | nodes.If() | nodes.IfExp() | nodes.Assert(): return node is parent.test or parent.test.parent_of(node) case nodes.Comprehension(): diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 5fc6e07ea5..d7850aea55 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -1038,7 +1038,7 @@ def _check_loop_finishes_via_except( """ if not other_node_try_except.orelse: return False - closest_loop: None | (nodes.For | nodes.While) = ( + closest_loop: nodes.For | nodes.While | None = ( utils.get_node_first_ancestor_of_type(node, (nodes.For, nodes.While)) ) if closest_loop is None: @@ -3034,7 +3034,7 @@ def _store_type_annotation_node(self, type_annotation: nodes.NodeNG) -> None: return match type_annotation.value: - case nodes.Attribute(expr=nodes.Name(name=n)) if n == TYPING_MODULE: + case nodes.Attribute(expr=nodes.Name(name=name)) if name == TYPING_MODULE: self._type_annotation_names.append(TYPING_MODULE) return diff --git a/pylint/extensions/_check_docs_utils.py b/pylint/extensions/_check_docs_utils.py index 5664e70474..31ed92f0ec 100644 --- a/pylint/extensions/_check_docs_utils.py +++ b/pylint/extensions/_check_docs_utils.py @@ -41,9 +41,7 @@ def get_setters_property_name(node: nodes.FunctionDef) -> str | None: decorators = node.decorators.nodes if node.decorators else [] for decorator in decorators: match decorator: - case nodes.Attribute(attrname=attrname, expr=nodes.Name(name=name)) if ( - attrname == "setter" - ): + case nodes.Attribute(attrname="setter", expr=nodes.Name(name=name)): return name # type: ignore[no-any-return] return None diff --git a/pylint/extensions/no_self_use.py b/pylint/extensions/no_self_use.py index 1ed3dc82a4..06dd6d12d4 100644 --- a/pylint/extensions/no_self_use.py +++ b/pylint/extensions/no_self_use.py @@ -101,9 +101,8 @@ def leave_functiondef(self, node: nodes.FunctionDef) -> None: def _has_bare_super_call(fundef_node: nodes.FunctionDef) -> bool: for call in fundef_node.nodes_of_class(nodes.Call): - func = call.func - match func: - case nodes.Name(name="super") if not call.args: + match call: + case nodes.Call(func=nodes.Name(name="super"), args=[]): return True return False diff --git a/pylint/extensions/private_import.py b/pylint/extensions/private_import.py index c9d5dd3334..7bcecda9dc 100644 --- a/pylint/extensions/private_import.py +++ b/pylint/extensions/private_import.py @@ -196,9 +196,9 @@ def _populate_type_annotations_annotation( or a Subscript e.g. `Optional[type]` or an Attribute, e.g. `pylint.lint.linter`. """ match node: - case nodes.Name() if node.name not in all_used_type_annotations: - all_used_type_annotations[node.name] = True - return node.name # type: ignore[no-any-return] + case nodes.Name(name=name) if name not in all_used_type_annotations: + all_used_type_annotations[name] = True + return name # type: ignore[no-any-return] case nodes.Subscript(): # e.g. Optional[List[str]] # slice is the next nested type self._populate_type_annotations_annotation( diff --git a/pylint/extensions/typing.py b/pylint/extensions/typing.py index e5ba3f75e2..cfd50317ad 100644 --- a/pylint/extensions/typing.py +++ b/pylint/extensions/typing.py @@ -4,7 +4,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, NamedTuple +from typing import TYPE_CHECKING, NamedTuple, TypeGuard import astroid.bases from astroid import nodes @@ -299,7 +299,7 @@ def visit_subscript(self, node: nodes.Subscript) -> None: @staticmethod def _is_deprecated_union_annotation( annotation: nodes.NodeNG, union_name: str - ) -> bool: + ) -> TypeGuard[nodes.Subscript]: match annotation: case nodes.Subscript(value=nodes.Name(name=name)): return name == union_name # type: ignore[no-any-return] diff --git a/tests/functional/ext/code_style/cs_consider_using_assignment_expr.py b/tests/functional/ext/code_style/cs_consider_using_assignment_expr.py index 4f9a837003..62f5ba7a9b 100644 --- a/tests/functional/ext/code_style/cs_consider_using_assignment_expr.py +++ b/tests/functional/ext/code_style/cs_consider_using_assignment_expr.py @@ -31,7 +31,7 @@ def func_a(): if a6 is None: ... -# Previous unrelate note should not match +# Previous unrelated note should not match print("") if a7: ...