summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Byrne <31762852+mbyrnepr2@users.noreply.github.com>2021-12-13 22:48:33 +0100
committerGitHub <noreply@github.com>2021-12-13 22:48:33 +0100
commit78eed6af303e0e9854e43528d17d2ada96f11c09 (patch)
tree5cbc1e2781d32702c1d7aadb16fc80d15d2934b8
parent35254c29eb3a0f1c7847cfcb3451501a4180373d (diff)
downloadpylint-git-78eed6af303e0e9854e43528d17d2ada96f11c09.tar.gz
New checker - Detect use of unnecessary ellipsis (#5470)
Closes #5460
-rw-r--r--ChangeLog4
-rw-r--r--doc/whatsnew/2.13.rst3
-rw-r--r--pylint/checkers/ellipsis_checker.py50
-rw-r--r--tests/functional/c/class_members.py1
-rw-r--r--tests/functional/s/statement_without_effect.py2
-rw-r--r--tests/functional/t/too/too_few_public_methods_excluded.py1
-rw-r--r--tests/functional/u/unnecessary/unnecessary_ellipsis.py99
-rw-r--r--tests/functional/u/unnecessary/unnecessary_ellipsis.txt4
8 files changed, 161 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index ae0c1d6e2..8e99896db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -53,6 +53,10 @@ Release date: TBA
Closes #5323
+* Add checker ``unnecessary-ellipsis``: Emitted when the ellipsis constant is used unnecessarily.
+
+ Closes #5460
+
* Fixed incorrect classification of Numpy-style docstring as Google-style docstring for
docstrings with property setter documentation.
Docstring classification is now based on the highest amount of matched sections instead
diff --git a/doc/whatsnew/2.13.rst b/doc/whatsnew/2.13.rst
index 8555108c1..3f4584fde 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/ellipsis_checker.py b/pylint/checkers/ellipsis_checker.py
new file mode 100644
index 000000000..ea7a260ec
--- /dev/null
+++ b/pylint/checkers/ellipsis_checker.py
@@ -0,0 +1,50 @@
+"""Ellipsis checker for Python code
+"""
+from astroid import nodes
+
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import check_messages
+from pylint.interfaces import IAstroidChecker
+from pylint.lint import PyLinter
+
+
+class EllipsisChecker(BaseChecker):
+ __implements__ = (IAstroidChecker,)
+ name = "unnecessary_ellipsis"
+ msgs = {
+ "W2301": (
+ "Unnecessary ellipsis constant",
+ "unnecessary-ellipsis",
+ "Used when the ellipsis constant is encountered and can be avoided. "
+ "A line of code consisting of an ellipsis is unnecessary if "
+ "there is a docstring on the preceding line or if there is a "
+ "statement in the same scope.",
+ )
+ }
+
+ @check_messages("unnecessary-ellipsis")
+ def visit_const(self, node: nodes.Const) -> None:
+ """Check if the ellipsis constant is used unnecessarily.
+ Emit a warning when:
+ - A line consisting of an ellipsis is preceded by a docstring.
+ - A statement exists in the same scope as the ellipsis.
+ For example: A function consisting of an ellipsis followed by a
+ return statement on the next line.
+ """
+ if (
+ node.pytype() == "builtins.Ellipsis"
+ and not isinstance(node.parent, (nodes.Assign, nodes.AnnAssign, nodes.Call))
+ and (
+ 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", node=node)
+
+
+def register(linter: PyLinter) -> None:
+ """required method to auto register this checker"""
+ linter.register_checker(EllipsisChecker(linter))
diff --git a/tests/functional/c/class_members.py b/tests/functional/c/class_members.py
index e43ab57ba..e741c760b 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 53da873d8..31fc7250f 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 35ba873ee..fecf497ad 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 000000000..b5a61e349
--- /dev/null
+++ b/tests/functional/u/unnecessary/unnecessary_ellipsis.py
@@ -0,0 +1,99 @@
+"""Emit a warning when the ellipsis constant is used and can be avoided"""
+
+# pylint: disable=missing-docstring, too-few-public-methods
+
+from typing import List, overload, Union
+
+# Ellipsis and preceding statement
+try:
+ A = 2
+except ValueError:
+ A = 24
+ ... # [unnecessary-ellipsis]
+
+def ellipsis_and_subsequent_statement():
+ ... # [unnecessary-ellipsis]
+ return 0
+
+# The parent of ellipsis is an assignment
+B = ...
+C = [..., 1, 2, 3]
+
+# The parent of ellipsis is a call
+if "X" is type(...):
+ ...
+
+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]
+
+
+# Function overloading
+@overload
+def summarize(data: int) -> float: ...
+
+
+@overload
+def summarize(data: str) -> str: ...
+
+
+def summarize(data):
+ if isinstance(data, str):
+ ...
+ return float(data)
+
+
+
+# Method overloading
+class MyIntegerList(List[int]):
+ @overload
+ def __getitem__(self, index: int) -> int: ...
+
+ @overload
+ def __getitem__(self, index: slice) -> List[int]: ...
+
+ def __getitem__(self, index: Union[int, slice]) -> Union[int, List[int]]:
+ if isinstance(index, int):
+ ...
+ elif isinstance(index, slice):
+ ...
+ else:
+ raise TypeError(...)
diff --git a/tests/functional/u/unnecessary/unnecessary_ellipsis.txt b/tests/functional/u/unnecessary/unnecessary_ellipsis.txt
new file mode 100644
index 000000000..7b7b90589
--- /dev/null
+++ b/tests/functional/u/unnecessary/unnecessary_ellipsis.txt
@@ -0,0 +1,4 @@
+unnecessary-ellipsis:12:4:12:7::Unnecessary ellipsis constant:UNDEFINED
+unnecessary-ellipsis:15:4:15:7:ellipsis_and_subsequent_statement:Unnecessary ellipsis constant:UNDEFINED
+unnecessary-ellipsis:50:4:50:7:docstring_and_ellipsis:Unnecessary ellipsis constant:UNDEFINED
+unnecessary-ellipsis:66:4:66:7:DocstringAndEllipsis:Unnecessary ellipsis constant:UNDEFINED