Skip to content

Commit 6f0ddf6

Browse files
Merge the empty-string extensions to 'implicit_booleaness_checker'
1 parent fe6b404 commit 6f0ddf6

File tree

20 files changed

+115
-150
lines changed

20 files changed

+115
-150
lines changed

doc/data/messages/c/compare-to-empty-string/bad.py

Lines changed: 0 additions & 8 deletions
This file was deleted.

doc/data/messages/c/compare-to-empty-string/good.py

Lines changed: 0 additions & 8 deletions
This file was deleted.

doc/data/messages/c/compare-to-empty-string/pylintrc

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def important_string_manipulation(x: str, y: str) -> None:
2+
if x == "": # [use-implicit-booleaness-not-comparison-to-string]
3+
print("x is an empty string")
4+
5+
if y != "": # [use-implicit-booleaness-not-comparison-to-string]
6+
print("y is not an empty string")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Following this check blindly in weakly typed code base can create hard to debug issues. If the value
2+
can be something else that is falsey but not a string (for example ``None``, or ``0``), the code will
3+
not be equivalent.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def important_string_manipulation(x: str, y: str) -> None:
2+
if not x:
3+
print("x is an empty string")
4+
5+
if y:
6+
print("y is not an empty string")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[main]
2+
enable=use-implicit-booleaness-not-comparison-to-string

doc/user_guide/checkers/extensions.rst

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ Pylint provides the following optional plugins:
1919
- :ref:`pylint.extensions.docstyle`
2020
- :ref:`pylint.extensions.dunder`
2121
- :ref:`pylint.extensions.empty_comment`
22-
- :ref:`pylint.extensions.emptystring`
2322
- :ref:`pylint.extensions.eq_without_hash`
2423
- :ref:`pylint.extensions.for_any_all`
2524
- :ref:`pylint.extensions.magic_value`
@@ -87,20 +86,6 @@ Code Style checker Messages
8786
to. This can be changed to be an augmented assign. Disabled by default!
8887

8988

90-
.. _pylint.extensions.emptystring:
91-
92-
Compare-To-Empty-String checker
93-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
94-
95-
This checker is provided by ``pylint.extensions.emptystring``.
96-
Verbatim name of the checker is ``compare-to-empty-string``.
97-
98-
Compare-To-Empty-String checker Messages
99-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
100-
:compare-to-empty-string (C1901): *"%s" can be simplified to "%s" as an empty string is falsey*
101-
Used when Pylint detects comparison to an empty string constant.
102-
103-
10489
.. _pylint.extensions.comparison_placement:
10590

10691
Comparison-Placement checker

doc/user_guide/checkers/features.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,8 +893,10 @@ Refactoring checker Messages
893893
Emitted when a single "return" or "return None" statement is found at the end
894894
of function or method definition. This statement can safely be removed
895895
because Python will implicitly return None
896-
:use-implicit-booleaness-not-comparison-to-zero (C1804): *"%s" can be simplified to "%s" as 0 is falsey*
896+
:use-implicit-booleaness-not-comparison-to-zero (C1805): *"%s" can be simplified to "%s" as 0 is falsey*
897897
Used when Pylint detects comparison to a 0 constant.
898+
:use-implicit-booleaness-not-comparison-to-string (C1804): *"%s" can be simplified to "%s" as an empty string is falsey*
899+
Used when Pylint detects comparison to an empty string constant.
898900
:use-implicit-booleaness-not-comparison (C1803): *'%s' can be simplified to '%s' as an empty %s is falsey*
899901
Used when Pylint detects that collection literal comparison is being used to
900902
check for emptiness; Use implicit booleaness instead of a collection classes;

doc/user_guide/configuration/all-options.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ Standard Checkers
233233
234234
confidence = ["HIGH", "CONTROL_FLOW", "INFERENCE", "INFERENCE_FAILURE", "UNDEFINED"]
235235
236-
disable = ["raw-checker-failed", "bad-inline-option", "locally-disabled", "file-ignored", "suppressed-message", "useless-suppression", "deprecated-pragma", "use-implicit-booleaness-not-comparison-to-zero", "use-symbolic-message-instead", "consider-using-augmented-assign"]
236+
disable = ["raw-checker-failed", "bad-inline-option", "locally-disabled", "file-ignored", "suppressed-message", "useless-suppression", "deprecated-pragma", "use-implicit-booleaness-not-comparison-to-string", "use-implicit-booleaness-not-comparison-to-zero", "use-symbolic-message-instead", "consider-using-augmented-assign"]
237237
238238
enable = []
239239

doc/user_guide/messages/messages_overview.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,6 @@ All messages in the convention category:
390390
convention/bad-file-encoding
391391
convention/bad-mcs-classmethod-argument
392392
convention/bad-mcs-method-argument
393-
convention/compare-to-empty-string
394393
convention/consider-iterating-dictionary
395394
convention/consider-using-any-or-all
396395
convention/consider-using-dict-items
@@ -432,6 +431,7 @@ All messages in the convention category:
432431
convention/unnecessary-lambda-assignment
433432
convention/unneeded-not
434433
convention/use-implicit-booleaness-not-comparison
434+
convention/use-implicit-booleaness-not-comparison-to-string
435435
convention/use-implicit-booleaness-not-comparison-to-zero
436436
convention/use-implicit-booleaness-not-len
437437
convention/use-maxsplit-arg
@@ -449,6 +449,7 @@ All renamed messages in the convention category:
449449
:titlesonly:
450450

451451
convention/blacklisted-name
452+
convention/compare-to-empty-string
452453
convention/compare-to-zero
453454
convention/len-as-condition
454455
convention/missing-docstring
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
* The compare to empty string checker (``pylint.extensions.emptystring``) has been removed and its checks are
1+
* The compare to empty string checker (``pylint.extensions.emptystring``) and the compare to
2+
zero checker (``pylint.extensions.compare-to-zero``) have been removed and their checks are
23
now part of the implicit booleaness checker:
34

45
- ``compare-to-zero`` was renamed ``use-implicit-booleaness-not-comparison-to-zero``
5-
and it now need to be enabled explicitly.
6-
- The ``pylint.extensions.compare-to-zero`` extension no longer exists and
6+
and ``compare-to-empty-string`` was renamed ``use-implicit-booleaness-not-comparison-to-string``
7+
and they now need to be enabled explicitly.
8+
- The `pylint.extensions.emptystring`` and ``pylint.extensions.compare-to-zero`` extensions no longer exists and
79
needs to be removed from the ``load-plugins`` option.
810

9-
This permits to make the likeness explicit and will provide better performance as they share most of their
11+
This permits to make their likeness explicit and will provide better performance as they share most of their
1012
conditions to be raised.
1113

1214
Refs #6871

pylint/checkers/refactoring/implicit_booleaness_checker.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ class ImplicitBooleanessChecker(checkers.BaseChecker):
8181
"of a collection classes; empty collections are considered as false",
8282
),
8383
"C1804": (
84+
'"%s" can be simplified to "%s" as an empty string is falsey',
85+
"use-implicit-booleaness-not-comparison-to-string",
86+
"Used when Pylint detects comparison to an empty string constant.",
87+
{
88+
"default_enabled": False,
89+
"old_names": [("C1901", "compare-to-empty-string")],
90+
},
91+
),
92+
"C1805": (
8493
'"%s" can be simplified to "%s" as 0 is falsey',
8594
"use-implicit-booleaness-not-comparison-to-zero",
8695
"Used when Pylint detects comparison to a 0 constant.",
@@ -162,12 +171,16 @@ def visit_unaryop(self, node: nodes.UnaryOp) -> None:
162171
"use-implicit-booleaness-not-len", node=node, confidence=HIGH
163172
)
164173

165-
@utils.only_required_for_messages("use-implicit-booleaness-not-comparison")
174+
@utils.only_required_for_messages(
175+
"use-implicit-booleaness-not-comparison",
176+
"use-implicit-booleaness-not-comparison-to-string",
177+
"use-implicit-booleaness-not-comparison-to-zero",
178+
)
166179
def visit_compare(self, node: nodes.Compare) -> None:
167180
self._check_use_implicit_booleaness_not_comparison(node)
168181
self._check_compare_to_zero(node)
182+
self._check_compare_to_string(node)
169183

170-
@utils.only_required_for_messages("compare-to-zero")
171184
def _check_compare_to_zero(self, node: nodes.Compare) -> None:
172185
# pylint: disable=duplicate-code
173186
_operators = ["!=", "==", "is not", "is"]
@@ -209,6 +222,47 @@ def _check_compare_to_zero(self, node: nodes.Compare) -> None:
209222
confidence=HIGH,
210223
)
211224

225+
def _check_compare_to_string(self, node: nodes.Compare) -> None:
226+
"""Checks for comparisons to empty string.
227+
228+
Most of the time you should use the fact that empty strings are false.
229+
An exception to this rule is when an empty string value is allowed in the program
230+
and has a different meaning than None!
231+
"""
232+
_operators = {"!=", "==", "is not", "is"}
233+
# note: astroid.Compare has the left most operand in node.left while the rest
234+
# are a list of tuples in node.ops the format of the tuple is
235+
# ('compare operator sign', node) here we squash everything into `ops`
236+
# to make it easier for processing later
237+
ops: list[tuple[str, nodes.NodeNG | None]] = [("", node.left)]
238+
ops.extend(node.ops)
239+
iter_ops = iter(ops)
240+
ops = list(itertools.chain(*iter_ops)) # type: ignore[arg-type]
241+
for ops_idx in range(len(ops) - 2):
242+
op_1: nodes.NodeNG | None = ops[ops_idx]
243+
op_2: str = ops[ops_idx + 1] # type: ignore[assignment]
244+
op_3: nodes.NodeNG | None = ops[ops_idx + 2]
245+
error_detected = False
246+
if op_1 is None or op_3 is None or op_2 not in _operators:
247+
continue
248+
node_name = ""
249+
# x ?? ""
250+
if utils.is_empty_str_literal(op_1):
251+
error_detected = True
252+
node_name = op_3.as_string()
253+
# '' ?? X
254+
elif utils.is_empty_str_literal(op_3):
255+
error_detected = True
256+
node_name = op_1.as_string()
257+
if error_detected:
258+
suggestion = f"not {node_name}" if op_2 in {"==", "is"} else node_name
259+
self.add_message(
260+
"compare-to-empty-string",
261+
args=(node.as_string(), suggestion),
262+
node=node,
263+
confidence=HIGH,
264+
)
265+
212266
def _check_use_implicit_booleaness_not_comparison(
213267
self, node: nodes.Compare
214268
) -> None:

pylint/extensions/emptystring.py

Lines changed: 0 additions & 78 deletions
This file was deleted.

tests/functional/ext/emptystring/empty_string_comparison.py

Lines changed: 0 additions & 22 deletions
This file was deleted.

tests/functional/ext/emptystring/empty_string_comparison.rc

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/functional/ext/emptystring/empty_string_comparison.txt

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# pylint: disable=literal-comparison,missing-docstring
2+
3+
X = ''
4+
Y = 'test'
5+
6+
if X is '': # [use-implicit-booleaness-not-comparison-to-string]
7+
pass
8+
9+
if Y is not "": # [use-implicit-booleaness-not-comparison-to-string]
10+
pass
11+
12+
if X == "": # [use-implicit-booleaness-not-comparison-to-string]
13+
pass
14+
15+
if Y != '': # [use-implicit-booleaness-not-comparison-to-string]
16+
pass
17+
18+
if "" == Y: # [use-implicit-booleaness-not-comparison-to-string]
19+
pass
20+
21+
if '' != X: # [use-implicit-booleaness-not-comparison-to-string]
22+
pass
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[MAIN]
2+
enable=use-implicit-booleaness-not-comparison-to-string
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use-implicit-booleaness-not-comparison-to-string:6:3:6:10::"""X is ''"" can be simplified to ""not X"" as an empty string is falsey":HIGH
2+
use-implicit-booleaness-not-comparison-to-string:9:3:9:14::"""Y is not ''"" can be simplified to ""Y"" as an empty string is falsey":HIGH
3+
use-implicit-booleaness-not-comparison-to-string:12:3:12:10::"""X == ''"" can be simplified to ""not X"" as an empty string is falsey":HIGH
4+
use-implicit-booleaness-not-comparison-to-string:15:3:15:10::"""Y != ''"" can be simplified to ""Y"" as an empty string is falsey":HIGH
5+
use-implicit-booleaness-not-comparison-to-string:18:3:18:10::"""'' == Y"" can be simplified to ""not Y"" as an empty string is falsey":HIGH
6+
use-implicit-booleaness-not-comparison-to-string:21:3:21:10::"""'' != X"" can be simplified to ""X"" as an empty string is falsey":HIGH

0 commit comments

Comments
 (0)