20
20
21
21
22
22
class CompareToEmptyStringChecker (checkers .BaseChecker ):
23
- """Checks for comparisons to empty string.
24
-
25
- Most of the time you should use the fact that empty strings are false.
26
- An exception to this rule is when an empty string value is allowed in the program
27
- and has a different meaning than None!
28
- """
29
-
30
- # configuration section name
31
23
name = "compare-to-empty-string"
32
24
msgs = {
33
25
"C1901" : (
34
- "Avoid comparisons to empty string" ,
26
+ '"%s" can be simplified to "%s" as an empty string is falsey' ,
35
27
"compare-to-empty-string" ,
36
28
"Used when Pylint detects comparison to an empty string constant." ,
37
29
)
@@ -41,31 +33,45 @@ class CompareToEmptyStringChecker(checkers.BaseChecker):
41
33
42
34
@utils .only_required_for_messages ("compare-to-empty-string" )
43
35
def visit_compare (self , node : nodes .Compare ) -> None :
44
- _operators = ["!=" , "==" , "is not" , "is" ]
45
- # note: astroid.Compare has the left most operand in node.left
46
- # while the rest are a list of tuples in node.ops
47
- # the format of the tuple is ('compare operator sign', node)
48
- # here we squash everything into `ops` to make it easier for processing later
36
+ """Checks for comparisons to empty string.
37
+
38
+ Most of the time you should use the fact that empty strings are false.
39
+ An exception to this rule is when an empty string value is allowed in the program
40
+ and has a different meaning than None!
41
+ """
42
+ _operators = {"!=" , "==" , "is not" , "is" }
43
+ # note: astroid.Compare has the left most operand in node.left while the rest
44
+ # are a list of tuples in node.ops the format of the tuple is
45
+ # ('compare operator sign', node) here we squash everything into `ops`
46
+ # to make it easier for processing later
49
47
ops : list [tuple [str , nodes .NodeNG | None ]] = [("" , node .left )]
50
48
ops .extend (node .ops )
51
49
iter_ops = iter (ops )
52
50
ops = list (itertools .chain (* iter_ops )) # type: ignore[arg-type]
53
-
54
51
for ops_idx in range (len (ops ) - 2 ):
55
52
op_1 : nodes .NodeNG | None = ops [ops_idx ]
56
53
op_2 : str = ops [ops_idx + 1 ] # type: ignore[assignment]
57
54
op_3 : nodes .NodeNG | None = ops [ops_idx + 2 ]
58
55
error_detected = False
59
-
56
+ if op_1 is None or op_3 is None or op_2 not in _operators :
57
+ continue
58
+ node_name = ""
60
59
# x ?? ""
61
- if utils .is_empty_str_literal (op_1 ) and op_2 in _operators :
60
+ if utils .is_empty_str_literal (op_1 ):
62
61
error_detected = True
62
+ node_name = op_3 .as_string ()
63
63
# '' ?? X
64
- elif op_2 in _operators and utils .is_empty_str_literal (op_3 ):
64
+ elif utils .is_empty_str_literal (op_3 ):
65
65
error_detected = True
66
-
66
+ node_name = op_1 . as_string ()
67
67
if error_detected :
68
- self .add_message ("compare-to-empty-string" , node = node , confidence = HIGH )
68
+ suggestion = f"not { node_name } " if op_2 in {"==" , "is" } else node_name
69
+ self .add_message (
70
+ "compare-to-empty-string" ,
71
+ args = (node .as_string (), suggestion ),
72
+ node = node ,
73
+ confidence = HIGH ,
74
+ )
69
75
70
76
71
77
def register (linter : PyLinter ) -> None :
0 commit comments