diff options
author | Jaehoon Hwang <jaehoonhwang@users.noreply.github.com> | 2021-10-23 05:52:23 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-23 14:52:23 +0200 |
commit | 8b60e428457fb1125f61bd97296665aef53eefb8 (patch) | |
tree | 52d4cfa002f6177870296e4bba13890ec06ae6a3 /pylint | |
parent | 772b3dcc0b0770a843653783e5c93b4256e5ec6f (diff) | |
download | pylint-git-8b60e428457fb1125f61bd97296665aef53eefb8.tar.gz |
Fix use-implicit-booleaness-not-comparison crash (#5176)
* Fix use-implicit-booleaness-not-comparison crash
`use-implicit-booleaness-not-comparison` caused a crash due to
`target_node` not being an instance of `NodeNG.Name`
This fix will allow the checker to have a default handling when it
encounters `target_node` other than `Calls`,`Name`, or `Attribute`
* Added more comprehensive test for implicit_booealness_checker
* Make implicit_booealness_checker to have default `variable_name`
* Handle `Calls`,`Name`, or `Attribute`
* Fix typing in ImplicitBooleanessChecker.base_classes_of_node
* [implicit-booleaness] Add call nodes name in warnings
* Use `BaseContainer` to check for empty tuple/list and use `as_string` for `Attribute` and `Name` nodes for message
Using `BaseContainer` for checking for empty `tuple` and `list`.
In addition, `is_base_container` checks for `FrozenSet` and `Set` as
well.
* Update test cases with cr concerns
* Use `BaseContainer` when checking for empty list or tuple
* Update `is_literal_tuple/list` to use `is_base_container`
* Use `as_string` when giving out function or variable name for message.
* Fix broken baseContainer test
* Use safe_infer for inferencing `target_instance`
* Swap opreators message
* Address CR comments; no more try/catch for infer & Add more test cases
* Add more test cases and changed few cases to cover more cases.
* Remove `try/catch` from `safe_infer` since `safe_infer` will return
`None` when it encounters exceptions.
* Comparison from infer to be more explicit; using `None` instead of
relying on `bool`
Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Diffstat (limited to 'pylint')
-rw-r--r-- | pylint/checkers/refactoring/implicit_booleaness_checker.py | 42 | ||||
-rw-r--r-- | pylint/checkers/utils.py | 8 |
2 files changed, 23 insertions, 27 deletions
diff --git a/pylint/checkers/refactoring/implicit_booleaness_checker.py b/pylint/checkers/refactoring/implicit_booleaness_checker.py index b5d0b277f..fbcb29536 100644 --- a/pylint/checkers/refactoring/implicit_booleaness_checker.py +++ b/pylint/checkers/refactoring/implicit_booleaness_checker.py @@ -145,19 +145,15 @@ class ImplicitBooleanessChecker(checkers.BaseChecker): self, node: nodes.Compare ) -> None: """Check for left side and right side of the node for empty literals""" - is_left_empty_literal = ( - utils.is_empty_list_literal(node.left) - or utils.is_empty_tuple_literal(node.left) - or utils.is_empty_dict_literal(node.left) - ) + is_left_empty_literal = utils.is_base_container( + node.left + ) or utils.is_empty_dict_literal(node.left) # Check both left hand side and right hand side for literals for operator, comparator in node.ops: - is_right_empty_literal = ( - utils.is_empty_list_literal(comparator) - or utils.is_empty_tuple_literal(comparator) - or utils.is_empty_dict_literal(comparator) - ) + is_right_empty_literal = utils.is_base_container( + comparator + ) or utils.is_empty_dict_literal(comparator) # Using Exclusive OR (XOR) to compare between two side. # If two sides are both literal, it should be different error. if is_right_empty_literal ^ is_left_empty_literal: @@ -165,14 +161,12 @@ class ImplicitBooleanessChecker(checkers.BaseChecker): target_node = node.left if is_right_empty_literal else comparator literal_node = comparator if is_right_empty_literal else node.left # Infer node to check - try: - target_instance = next(target_node.infer()) - except astroid.InferenceError: - # Probably undefined-variable, continue with check + target_instance = utils.safe_infer(target_node) + if target_instance is None: continue mother_classes = self.base_classes_of_node(target_instance) is_base_comprehension_type = any( - t in mother_classes for t in ("tuple", "list", "dict") + t in mother_classes for t in ("tuple", "list", "dict", "set") ) # Only time we bypass check is when target_node is not inherited by @@ -183,20 +177,26 @@ class ImplicitBooleanessChecker(checkers.BaseChecker): continue # No need to check for operator when visiting compare node - if operator in ["==", "!=", ">=", ">", "<=", "<"]: + if operator in ("==", "!=", ">=", ">", "<=", "<"): collection_literal = "{}" if isinstance(literal_node, nodes.List): collection_literal = "[]" if isinstance(literal_node, nodes.Tuple): collection_literal = "()" - variable_name = target_node.name + + instance_name = "x" + if isinstance(target_node, nodes.Call) and target_node.func: + instance_name = f"{target_node.func.as_string()}(...)" + elif isinstance(target_node, (nodes.Attribute, nodes.Name)): + instance_name = target_node.as_string() + original_comparison = ( - f"{variable_name} {operator} {collection_literal}" + f"{instance_name} {operator} {collection_literal}" ) suggestion = ( - f"not {variable_name}" + f"{instance_name}" if operator == "!=" - else f"{variable_name}" + else f"not {instance_name}" ) self.add_message( "use-implicit-booleaness-not-comparison", @@ -208,7 +208,7 @@ class ImplicitBooleanessChecker(checkers.BaseChecker): ) @staticmethod - def base_classes_of_node(instance: nodes.ClassDef) -> List[nodes.Name]: + def base_classes_of_node(instance: nodes.ClassDef) -> List[str]: """Return all the classes names that a ClassDef inherit from including 'object'.""" try: return [instance.name] + [x.name for x in instance.ancestors()] diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 0fee05af9..43157d0f9 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -1626,12 +1626,8 @@ def is_function_body_ellipsis(node: nodes.FunctionDef) -> bool: ) -def is_empty_list_literal(node: Optional[nodes.NodeNG]) -> bool: - return isinstance(node, nodes.List) and not node.elts - - -def is_empty_tuple_literal(node: Optional[nodes.NodeNG]) -> bool: - return isinstance(node, nodes.Tuple) and not node.elts +def is_base_container(node: Optional[nodes.NodeNG]) -> bool: + return isinstance(node, nodes.BaseContainer) and not node.elts def is_empty_dict_literal(node: Optional[nodes.NodeNG]) -> bool: |