diff --git a/.gitignore b/.gitignore deleted file mode 100644 index d807609565..0000000000 --- a/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -# Do not add entries specific to your dev environment or development -# preferences in this file. You can use the global .gitignore for that: -# git config --global core.excludesFile '~/.gitignore' -/log -*.py[cod] -/build -/doc/_build -/dist/ -/pylint.egg-info/ -.tox -*.sw[a-z] -# Can't use | operator in .gitignore, see -# https://unix.stackexchange.com/a/31806/189111 -doc/user_guide/messages/convention/ -doc/user_guide/messages/error/ -doc/user_guide/messages/fatal/ -doc/user_guide/messages/information/ -doc/user_guide/messages/refactor/ -doc/user_guide/messages/warning/ -tests/.pylint_primer_tests/ -pyve -build-stamp -.coverage -.coverage.* -.cache/ -.eggs/ -.pytest_cache/ -.mypy_cache/ -.benchmarks/ diff --git a/doc/whatsnew/fragments/10351.feature b/doc/whatsnew/fragments/10351.feature new file mode 100644 index 0000000000..6e2a4fcf54 --- /dev/null +++ b/doc/whatsnew/fragments/10351.feature @@ -0,0 +1,5 @@ +Enhanced W0236 (invalid-overridden-method) to detect return type mismatches in overridden methods. + +Before this fix, Pylint did not catch return type mismatches when a method in a subclass overrode a method from a base class with a different return type. + +Closes #10351 diff --git a/pylint/checkers/classes/class_checker.py b/pylint/checkers/classes/class_checker.py index b493a1ba73..67005d51f7 100644 --- a/pylint/checkers/classes/class_checker.py +++ b/pylint/checkers/classes/class_checker.py @@ -1465,6 +1465,9 @@ def _check_invalid_overridden_method( function_node: nodes.FunctionDef, parent_function_node: nodes.FunctionDef, ) -> None: + if _is_trivial_super_delegation(function_node): + return + parent_is_property = decorated_with_property( parent_function_node ) or is_property_setter_or_deleter(parent_function_node) @@ -1510,6 +1513,76 @@ def _check_invalid_overridden_method( node=function_node, ) + if function_node.returns is None or parent_function_node.returns is None: + return + + inferred_return = safe_infer(function_node.returns) + inferred_parent_return = safe_infer(parent_function_node.returns) + + # Skip type checking if we couldn't infer one or both return types + if inferred_return is None or inferred_parent_return is None: + return + + # Handle special cases where return type differences are allowed + # Parent returns Any - allow any return type in child + if ( + isinstance(inferred_parent_return, nodes.Name) + and inferred_parent_return.name == "Any" + ): + return + + # Parent returns None - allow any return type in child. + if ( + isinstance(inferred_parent_return, nodes.Const) + and inferred_parent_return.value is None + ): + return + + # Validation: Skip if parent has no explicit return type annotation + if parent_function_node.returns is None: + return + + # Validation: Check if child return type is a subtype of parent return type + try: + if hasattr(utils, "is_subtype") and utils.is_subtype( + inferred_return, inferred_parent_return + ): + return + except astroid.InferenceError: + pass + + # Validation: Compare qualified names for class types + if hasattr(inferred_return, "qname") and hasattr( + inferred_parent_return, "qname" + ): + if inferred_return.qname() == inferred_parent_return.qname(): + return + + # Validation: Same name for builtin types + if hasattr(inferred_return, "name") and hasattr(inferred_parent_return, "name"): + if inferred_return.name == inferred_parent_return.name: + return + + return_name = getattr(inferred_return, "name", str(inferred_return)) + parent_return_name = getattr( + inferred_parent_return, "name", str(inferred_parent_return) + ) + + # Only report if types are definitely different + if ( + return_name != parent_return_name + or inferred_return != inferred_parent_return + ): + self.add_message( + "invalid-overridden-method", + args=( + function_node.name, + parent_return_name, + return_name, + ), + node=function_node, + ) + def _check_functools_or_not(self, decorator: nodes.Attribute) -> bool: if decorator.attrname != "cached_property": return False