diff --git a/ChangeLog b/ChangeLog index f77b7e8706..1559b071e3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,6 +23,10 @@ Release date: TBA Closes #5323 +* Add checker ``unnecessary-ellipsis`` + + Closes #5460 + * Fixed detection of ``arguments-differ`` when superclass static methods lacked a ``@staticmethod`` decorator. diff --git a/doc/whatsnew/2.13.rst b/doc/whatsnew/2.13.rst index 0713e343ae..7d9dd28972 100644 --- a/doc/whatsnew/2.13.rst +++ b/doc/whatsnew/2.13.rst @@ -10,6 +10,9 @@ Summary -- Release highlights New checkers ============ +* ``unnecessary-ellipsis``: Emmitted when the ellipsis constant is used unnecessarily. + + Closes #5460 Removed checkers ================ diff --git a/pylint/checkers/strings.py b/pylint/checkers/strings.py index bc573979f6..a10a9f35a9 100644 --- a/pylint/checkers/strings.py +++ b/pylint/checkers/strings.py @@ -673,6 +673,11 @@ class StringConstantChecker(BaseTokenChecker): "in Python 2 to indicate a string was Unicode, but since Python 3.0 strings " "are Unicode by default.", ), + "W1407": ( + "Unnecessary ellipsis constant", + "unnecessary-ellipsis", + "Used when the ellipsis constant is encountered and can be avoided.", + ), } options = ( ( @@ -914,6 +919,7 @@ def visit_const(self, node: nodes.Const) -> None: node.parent, nodes.JoinedStr ): self._detect_u_string_prefix(node) + self._detect_ellipsis(node) def _detect_u_string_prefix(self, node: nodes.Const): """Check whether strings include a 'u' prefix like u'String'""" @@ -924,6 +930,20 @@ def _detect_u_string_prefix(self, node: nodes.Const): col_offset=node.col_offset, ) + def _detect_ellipsis(self, node: nodes.Const) -> None: + """Check if the ellipsis constant is used unnecessarily""" + if node.value is not Ellipsis or isinstance( + node.parent, (nodes.Assign, nodes.AnnAssign) + ): + return + if len(node.parent.parent.child_sequence(node.parent)) > 1 or ( + isinstance(node.parent.parent, (nodes.ClassDef, nodes.FunctionDef)) + and (node.parent.parent.doc is not None) + ): + self.add_message( + "unnecessary-ellipsis", line=node.lineno, col_offset=node.col_offset + ) + def register(linter): """required method to auto register this checker""" diff --git a/tests/functional/c/class_members.py b/tests/functional/c/class_members.py index e43ab57baa..e741c760b3 100644 --- a/tests/functional/c/class_members.py +++ b/tests/functional/c/class_members.py @@ -3,7 +3,6 @@ class Class: attr: int - ... # `bar` definitely does not exist here, but in a complex scenario, diff --git a/tests/functional/s/statement_without_effect.py b/tests/functional/s/statement_without_effect.py index 53da873d88..31fc7250f0 100644 --- a/tests/functional/s/statement_without_effect.py +++ b/tests/functional/s/statement_without_effect.py @@ -1,5 +1,5 @@ """Test for statements without effects.""" -# pylint: disable=too-few-public-methods, useless-object-inheritance, unnecessary-comprehension, use-list-literal +# pylint: disable=too-few-public-methods, useless-object-inheritance, unnecessary-comprehension, unnecessary-ellipsis, use-list-literal # +1:[pointless-string-statement] """inline doc string should use a separated message""" diff --git a/tests/functional/t/too/too_few_public_methods_excluded.py b/tests/functional/t/too/too_few_public_methods_excluded.py index 35ba873ee5..fecf497ad1 100644 --- a/tests/functional/t/too/too_few_public_methods_excluded.py +++ b/tests/functional/t/too/too_few_public_methods_excluded.py @@ -11,4 +11,3 @@ class MyJsonEncoder(JSONEncoder): class InheritedInModule(Control): """This class inherits from a class that doesn't have enough mehods, and its parent is excluded via config, so it doesn't raise.""" - ... diff --git a/tests/functional/u/unnecessary/unnecessary_ellipsis.py b/tests/functional/u/unnecessary/unnecessary_ellipsis.py new file mode 100644 index 0000000000..6122e13662 --- /dev/null +++ b/tests/functional/u/unnecessary/unnecessary_ellipsis.py @@ -0,0 +1,54 @@ +"""Emit a warning when the ellipsis constant is used and can be avoided""" + +# pylint: disable=missing-docstring, too-few-public-methods + +try: + A = 2 +except ValueError: + A = 24 + ... # [unnecessary-ellipsis] + +B = ... +C = [..., 1, 2, 3] + +def docstring_only(): + '''In Python, stubbed functions often have a body that contains just a + single `...` constant, indicating that the function doesn't do + anything. However, a stubbed function can also have just a + docstring, and function with a docstring and no body also does + nothing. + ''' + + +# This function has no docstring, so it needs a `...` constant. +def ellipsis_only(): + ... + + +def docstring_and_ellipsis(): + '''This function doesn't do anything, but it has a docstring, so its + `...` constant is useless clutter. + + NEW CHECK: unnecessary-ellipsis + + This would check for stubs with both docstrings and `...` + constants, suggesting the removal of the useless `...` + constants + ''' + ... # [unnecessary-ellipsis] + + +class DocstringOnly: + '''The same goes for class stubs: docstring, or `...`, but not both. + ''' + + +# No problem +class EllipsisOnly: + ... + + +class DocstringAndEllipsis: + '''Whoops! Mark this one as bad too. + ''' + ... # [unnecessary-ellipsis] diff --git a/tests/functional/u/unnecessary/unnecessary_ellipsis.txt b/tests/functional/u/unnecessary/unnecessary_ellipsis.txt new file mode 100644 index 0000000000..02e7620f2f --- /dev/null +++ b/tests/functional/u/unnecessary/unnecessary_ellipsis.txt @@ -0,0 +1,3 @@ +unnecessary-ellipsis:9:4:None:None::Unnecessary ellipsis constant:UNDEFINED +unnecessary-ellipsis:38:4:None:None::Unnecessary ellipsis constant:UNDEFINED +unnecessary-ellipsis:54:4:None:None::Unnecessary ellipsis constant:UNDEFINED