From 5d8f2f8cd6ae783014d3848a16802cf65fc855a6 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 10 Apr 2026 01:43:08 -0700 Subject: [PATCH] feat: expand use-implicit-booleaness-not-len to catch len comparisons to 0 --- .../implicit_booleaness_checker.py | 34 +++++++++++ .../u/use/use_implicit_booleaness_not_len.py | 37 ++++++++---- .../u/use/use_implicit_booleaness_not_len.txt | 56 ++++++++++++------- 3 files changed, 95 insertions(+), 32 deletions(-) diff --git a/pylint/checkers/refactoring/implicit_booleaness_checker.py b/pylint/checkers/refactoring/implicit_booleaness_checker.py index f3a3c6c964..7270995b61 100644 --- a/pylint/checkers/refactoring/implicit_booleaness_checker.py +++ b/pylint/checkers/refactoring/implicit_booleaness_checker.py @@ -173,11 +173,14 @@ def visit_unaryop(self, node: nodes.UnaryOp) -> None: ) @utils.only_required_for_messages( + "use-implicit-booleaness-not-len", "use-implicit-booleaness-not-comparison", "use-implicit-booleaness-not-comparison-to-string", "use-implicit-booleaness-not-comparison-to-zero", ) def visit_compare(self, node: nodes.Compare) -> None: + if self.linter.is_message_enabled("use-implicit-booleaness-not-len"): + self._check_len_comparison_to_zero(node) if self.linter.is_message_enabled("use-implicit-booleaness-not-comparison"): self._check_use_implicit_booleaness_not_comparison(node) if self.linter.is_message_enabled( @@ -187,6 +190,37 @@ def visit_compare(self, node: nodes.Compare) -> None: ): self._check_compare_to_str_or_zero(node) + def _check_len_comparison_to_zero(self, node: nodes.Compare) -> None: + """Check for len(x) comparisons against literal zero.""" + if len(node.ops) != 1: + return + + operator, right = node.ops[0] + left = node.left + + if ( + utils.is_call_of_name(left, "len") + and _is_constant_zero(right) + and operator in ("==", "!=", ">", ">=", "<", "<=") + ): + self.add_message( + "use-implicit-booleaness-not-len", + node=node, + confidence=HIGH, + ) + return + + if ( + _is_constant_zero(left) + and utils.is_call_of_name(right, "len") + and operator in ("==", "!=", ">", ">=", "<", "<=") + ): + self.add_message( + "use-implicit-booleaness-not-len", + node=node, + confidence=HIGH, + ) + def _check_compare_to_str_or_zero(self, node: nodes.Compare) -> None: # Skip check for chained comparisons if len(node.ops) != 1: diff --git a/tests/functional/u/use/use_implicit_booleaness_not_len.py b/tests/functional/u/use/use_implicit_booleaness_not_len.py index 4a1063e757..110256b55c 100644 --- a/tests/functional/u/use/use_implicit_booleaness_not_len.py +++ b/tests/functional/u/use/use_implicit_booleaness_not_len.py @@ -14,22 +14,37 @@ if True or len('TEST'): # [use-implicit-booleaness-not-len] pass -if len('TEST') == 0: # Should be fine +if len('TEST') == 0: # [use-implicit-booleaness-not-len] pass -if len('TEST') < 1: # Should be fine +if len('TEST') < 1: # Should be fine (comparing to 1, not 0) pass -if len('TEST') <= 0: # Should be fine +if len('TEST') <= 0: # [use-implicit-booleaness-not-len] pass -if 1 > len('TEST'): # Should be fine +if 1 > len('TEST'): # Should be fine (comparing to 1, not 0) pass -if 0 >= len('TEST'): # Should be fine +if 0 >= len('TEST'): # [use-implicit-booleaness-not-len] pass -if z and len('TEST') == 0: # Should be fine +if len('TEST') > 0: # [use-implicit-booleaness-not-len] + pass + +if len('TEST') != 0: # [use-implicit-booleaness-not-len] + pass + +if 0 < len('TEST'): # [use-implicit-booleaness-not-len] + pass + +if 0 == len('TEST'): # [use-implicit-booleaness-not-len] + pass + +if 0 != len('TEST'): # [use-implicit-booleaness-not-len] + pass + +if z and len('TEST') == 0: # [use-implicit-booleaness-not-len] pass if 0 == len('TEST') < 10: # Should be fine @@ -73,16 +88,16 @@ while not len('TEST') and z: # [use-implicit-booleaness-not-len] pass -assert len('TEST') > 0 # Should be fine +assert len('TEST') > 0 # [use-implicit-booleaness-not-len] -x = 1 if len('TEST') != 0 else 2 # Should be fine +x = 1 if len('TEST') != 0 else 2 # [use-implicit-booleaness-not-len] f_o_o = len('TEST') or 42 # Should be fine a = x and len(x) # Should be fine def some_func(): - return len('TEST') > 0 # Should be fine + return len('TEST') > 0 # [use-implicit-booleaness-not-len] def github_issue_1325(): l = [1, 2, 3] @@ -132,7 +147,7 @@ class ChildClassWithoutBool(ClassWithoutBool): # pylint: disable=import-outside-toplevel import numpy numpy_array = numpy.array([0]) - if len(numpy_array) > 0: + if len(numpy_array) > 0: # [use-implicit-booleaness-not-len] print('numpy_array') if len(numpy_array): print('numpy_array') @@ -143,7 +158,7 @@ class ChildClassWithoutBool(ClassWithoutBool): pandas_df = pd.DataFrame() if len(pandas_df): print("this works, but pylint tells me not to use len() without comparison") - if len(pandas_df) > 0: + if len(pandas_df) > 0: # [use-implicit-booleaness-not-len] print("this works and pylint likes it, but it's not the solution intended by PEP-8") if pandas_df: print("this does not work (truth value of dataframe is ambiguous)") diff --git a/tests/functional/u/use/use_implicit_booleaness_not_len.txt b/tests/functional/u/use/use_implicit_booleaness_not_len.txt index 78078751cd..136971fa2b 100644 --- a/tests/functional/u/use/use_implicit_booleaness_not_len.txt +++ b/tests/functional/u/use/use_implicit_booleaness_not_len.txt @@ -2,24 +2,38 @@ use-implicit-booleaness-not-len:4:3:4:14::Do not use `len(SEQUENCE)` without com use-implicit-booleaness-not-len:7:3:7:18::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH use-implicit-booleaness-not-len:11:9:11:34::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE use-implicit-booleaness-not-len:14:11:14:22::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -comparison-of-constants:39:3:39:28::"Comparison between constants: ""0 < 1"" has a constant value":HIGH -use-implicit-booleaness-not-len:56:5:56:16::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -use-implicit-booleaness-not-len:61:5:61:20::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH -use-implicit-booleaness-not-len:64:6:64:17::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -use-implicit-booleaness-not-len:67:6:67:21::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH -use-implicit-booleaness-not-len:70:12:70:23::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -use-implicit-booleaness-not-len:73:6:73:21::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH -use-implicit-booleaness-not-len:96:11:96:20:github_issue_1331_v2:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -use-implicit-booleaness-not-len:99:11:99:20:github_issue_1331_v3:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -use-implicit-booleaness-not-len:102:17:102:26:github_issue_1331_v4:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -use-implicit-booleaness-not-len:104:9:104:15::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -use-implicit-booleaness-not-len:105:9:105:20::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -use-implicit-booleaness-not-len:124:11:124:34:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -use-implicit-booleaness-not-len:125:11:125:39:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -use-implicit-booleaness-not-len:126:11:126:24:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -use-implicit-booleaness-not-len:127:11:127:35:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH -use-implicit-booleaness-not-len:129:11:129:41:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH -use-implicit-booleaness-not-len:130:11:130:43:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -use-implicit-booleaness-not-len:171:11:171:42:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE -undefined-variable:183:11:183:24:github_issue_4215:Undefined variable 'undefined_var':UNDEFINED -undefined-variable:185:11:185:25:github_issue_4215:Undefined variable 'undefined_var2':UNDEFINED +use-implicit-booleaness-not-len:17:3:17:19::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:23:3:23:19::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:29:3:29:19::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:32:3:32:18::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:35:3:35:19::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:38:3:38:18::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:41:3:41:19::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:44:3:44:19::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:47:9:47:25::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +comparison-of-constants:54:3:54:28::"Comparison between constants: ""0 < 1"" has a constant value":HIGH +use-implicit-booleaness-not-len:71:5:71:16::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +use-implicit-booleaness-not-len:76:5:76:20::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:79:6:79:17::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +use-implicit-booleaness-not-len:82:6:82:21::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:85:12:85:23::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +use-implicit-booleaness-not-len:88:6:88:21::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:91:7:91:22::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:93:9:93:25::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:100:11:100:26:some_func:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:111:11:111:20:github_issue_1331_v2:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +use-implicit-booleaness-not-len:114:11:114:20:github_issue_1331_v3:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +use-implicit-booleaness-not-len:117:17:117:26:github_issue_1331_v4:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +use-implicit-booleaness-not-len:119:9:119:15::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +use-implicit-booleaness-not-len:120:9:120:20::Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +use-implicit-booleaness-not-len:139:11:139:34:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +use-implicit-booleaness-not-len:140:11:140:39:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +use-implicit-booleaness-not-len:141:11:141:24:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +use-implicit-booleaness-not-len:142:11:142:35:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:144:11:144:41:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:145:11:145:43:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +use-implicit-booleaness-not-len:150:7:150:27:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:161:7:161:25:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:HIGH +use-implicit-booleaness-not-len:186:11:186:42:github_issue_1879:Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty:INFERENCE +undefined-variable:198:11:198:24:github_issue_4215:Undefined variable 'undefined_var':UNDEFINED +undefined-variable:200:11:200:25:github_issue_4215:Undefined variable 'undefined_var2':UNDEFINED