diff --git a/doc/whatsnew/fragments/8827.false_negative b/doc/whatsnew/fragments/8827.false_negative new file mode 100644 index 0000000000..42b84e712d --- /dev/null +++ b/doc/whatsnew/fragments/8827.false_negative @@ -0,0 +1,3 @@ +Extend broad-exception-raised and broad-exception-caught to except*. + +Closes #8827 diff --git a/pylint/checkers/base/basic_checker.py b/pylint/checkers/base/basic_checker.py index 8aaa274d65..0bfbcece98 100644 --- a/pylint/checkers/base/basic_checker.py +++ b/pylint/checkers/base/basic_checker.py @@ -478,7 +478,10 @@ def visit_expr(self, node: nodes.Expr) -> None: # side effects), else pointless-statement if ( isinstance(expr, (nodes.Yield, nodes.Await)) - or (isinstance(node.parent, nodes.Try) and node.parent.body == [node]) + or ( + isinstance(node.parent, (nodes.Try, nodes.TryStar)) + and node.parent.body == [node] + ) or (isinstance(expr, nodes.Const) and expr.value is Ellipsis) ): return diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py index ce3f9367f0..edc72c22ea 100644 --- a/pylint/checkers/exceptions.py +++ b/pylint/checkers/exceptions.py @@ -556,6 +556,10 @@ def visit_compare(self, node: nodes.Compare) -> None: "catching-non-exception", "duplicate-except", ) + def visit_trystar(self, node: nodes.TryStar) -> None: + """Check for empty except*.""" + self.visit_try(node) + def visit_try(self, node: nodes.Try) -> None: """Check for empty except.""" self._check_try_except_raise(node) diff --git a/tests/functional/b/broad_exception_caught.py b/tests/functional/b/broad_exception/broad_exception_caught.py similarity index 100% rename from tests/functional/b/broad_exception_caught.py rename to tests/functional/b/broad_exception/broad_exception_caught.py diff --git a/tests/functional/b/broad_exception/broad_exception_caught.rc b/tests/functional/b/broad_exception/broad_exception_caught.rc new file mode 100644 index 0000000000..5af8705b60 --- /dev/null +++ b/tests/functional/b/broad_exception/broad_exception_caught.rc @@ -0,0 +1,5 @@ +[EXCEPTIONS] +overgeneral-exceptions=builtins.BaseException, + builtins.Exception, + functional.b.broad_exception.broad_exception_caught.CustomBroadException, + broad_exception_caught.CustomBroadException diff --git a/tests/functional/b/broad_exception_caught.txt b/tests/functional/b/broad_exception/broad_exception_caught.txt similarity index 98% rename from tests/functional/b/broad_exception_caught.txt rename to tests/functional/b/broad_exception/broad_exception_caught.txt index 386423b63f..817e620172 100644 --- a/tests/functional/b/broad_exception_caught.txt +++ b/tests/functional/b/broad_exception/broad_exception_caught.txt @@ -1,3 +1,3 @@ -broad-exception-caught:14:7:14:16::Catching too general exception Exception:INFERENCE -broad-exception-caught:20:7:20:20::Catching too general exception BaseException:INFERENCE -broad-exception-caught:32:7:32:27::Catching too general exception CustomBroadException:INFERENCE +broad-exception-caught:14:7:14:16::Catching too general exception Exception:INFERENCE +broad-exception-caught:20:7:20:20::Catching too general exception BaseException:INFERENCE +broad-exception-caught:32:7:32:27::Catching too general exception CustomBroadException:INFERENCE diff --git a/tests/functional/b/broad_exception/broad_exception_caught_trystar.py b/tests/functional/b/broad_exception/broad_exception_caught_trystar.py new file mode 100644 index 0000000000..a50c1e4dda --- /dev/null +++ b/tests/functional/b/broad_exception/broad_exception_caught_trystar.py @@ -0,0 +1,39 @@ +# pylint: disable=missing-docstring +__revision__ = 0 + +class CustomBroadException(Exception): + pass + + +class CustomNarrowException(CustomBroadException): + pass + + +try: + __revision__ += 1 +except* Exception: # [broad-exception-caught] + print('error') + + +try: + __revision__ += 1 +except* BaseException: # [broad-exception-caught] + print('error') + + +try: + __revision__ += 1 +except* ValueError: + print('error') + + +try: + __revision__ += 1 +except CustomBroadException: # [broad-exception-caught] + print('error') + + +try: + __revision__ += 1 +except* CustomNarrowException: + print('error') diff --git a/tests/functional/b/broad_exception/broad_exception_caught_trystar.rc b/tests/functional/b/broad_exception/broad_exception_caught_trystar.rc new file mode 100644 index 0000000000..e47a4eb9f6 --- /dev/null +++ b/tests/functional/b/broad_exception/broad_exception_caught_trystar.rc @@ -0,0 +1,8 @@ +[EXCEPTIONS] +overgeneral-exceptions=builtins.BaseException, + builtins.Exception, + functional.b.broad_exception.broad_exception_caught_trystar.CustomBroadException, + broad_exception_caught_trystar.CustomBroadException + +[testoptions] +min_pyver=3.11 diff --git a/tests/functional/b/broad_exception/broad_exception_caught_trystar.txt b/tests/functional/b/broad_exception/broad_exception_caught_trystar.txt new file mode 100644 index 0000000000..da8725e57a --- /dev/null +++ b/tests/functional/b/broad_exception/broad_exception_caught_trystar.txt @@ -0,0 +1,3 @@ +broad-exception-caught:14:8:14:17::Catching too general exception Exception:INFERENCE +broad-exception-caught:20:8:20:21::Catching too general exception BaseException:INFERENCE +broad-exception-caught:32:7:32:27::Catching too general exception CustomBroadException:INFERENCE diff --git a/tests/functional/b/broad_exception_raised.py b/tests/functional/b/broad_exception/broad_exception_raised.py similarity index 100% rename from tests/functional/b/broad_exception_raised.py rename to tests/functional/b/broad_exception/broad_exception_raised.py diff --git a/tests/functional/b/broad_exception/broad_exception_raised.rc b/tests/functional/b/broad_exception/broad_exception_raised.rc new file mode 100644 index 0000000000..bc73576c00 --- /dev/null +++ b/tests/functional/b/broad_exception/broad_exception_raised.rc @@ -0,0 +1,5 @@ +[EXCEPTIONS] +overgeneral-exceptions=builtins.BaseException, + builtins.Exception, + functional.b.broad_exception.broad_exception_raised.CustomBroadException, + broad_exception_raised.CustomBroadException diff --git a/tests/functional/b/broad_exception_raised.txt b/tests/functional/b/broad_exception/broad_exception_raised.txt similarity index 99% rename from tests/functional/b/broad_exception_raised.txt rename to tests/functional/b/broad_exception/broad_exception_raised.txt index 1e27b23f98..705bf45cd8 100644 --- a/tests/functional/b/broad_exception_raised.txt +++ b/tests/functional/b/broad_exception/broad_exception_raised.txt @@ -1,8 +1,8 @@ -broad-exception-raised:15:4:15:41:exploding_apple:"Raising too general exception: Exception":INFERENCE -broad-exception-raised:20:8:20:34:raise_and_catch:"Raising too general exception: Exception":INFERENCE -broad-exception-caught:21:11:21:20:raise_and_catch:Catching too general exception Exception:INFERENCE -broad-exception-raised:38:8:38:35:raise_catch_raise:"Raising too general exception: Exception":INFERENCE -broad-exception-raised:46:8:46:40:raise_catch_raise_using_alias:"Raising too general exception: Exception":INFERENCE -broad-exception-raised:48:0:48:17::"Raising too general exception: Exception":INFERENCE -broad-exception-raised:49:0:49:21::"Raising too general exception: BaseException":INFERENCE -broad-exception-raised:50:0:50:28::"Raising too general exception: CustomBroadException":INFERENCE +broad-exception-raised:15:4:15:41:exploding_apple:"Raising too general exception: Exception":INFERENCE +broad-exception-raised:20:8:20:34:raise_and_catch:"Raising too general exception: Exception":INFERENCE +broad-exception-caught:21:11:21:20:raise_and_catch:Catching too general exception Exception:INFERENCE +broad-exception-raised:38:8:38:35:raise_catch_raise:"Raising too general exception: Exception":INFERENCE +broad-exception-raised:46:8:46:40:raise_catch_raise_using_alias:"Raising too general exception: Exception":INFERENCE +broad-exception-raised:48:0:48:17::"Raising too general exception: Exception":INFERENCE +broad-exception-raised:49:0:49:21::"Raising too general exception: BaseException":INFERENCE +broad-exception-raised:50:0:50:28::"Raising too general exception: CustomBroadException":INFERENCE diff --git a/tests/functional/b/broad_exception/broad_exception_raised_trystar.py b/tests/functional/b/broad_exception/broad_exception_raised_trystar.py new file mode 100644 index 0000000000..850fce4754 --- /dev/null +++ b/tests/functional/b/broad_exception/broad_exception_raised_trystar.py @@ -0,0 +1,52 @@ +# pylint: disable=missing-docstring, unreachable + +ExceptionAlias = Exception + +class CustomBroadException(Exception): + pass + + +class CustomNarrowException(CustomBroadException): + pass + + +def exploding_apple(apple): + print(f"{apple} is about to explode") + raise Exception("{apple} exploded !") # [broad-exception-raised] + + +def raise_and_catch_star(): + try: + raise Exception("Oh No!!") # [broad-exception-raised] + except* Exception as ex: # [broad-exception-caught] + print(ex) + + +def raise_catch_reraise_star(): + try: + exploding_apple("apple") + except* Exception as ex: + print(ex) + raise ex + + +def raise_catch_raise_star(): + try: + exploding_apple("apple") + except* Exception as ex: + print(ex) + raise Exception() from None # [broad-exception-raised] + + +def raise_catch_raise_using_alias_star(): + try: + exploding_apple("apple") + except* Exception as ex: + print(ex) + raise ExceptionAlias() from None # [broad-exception-raised] + +raise Exception() # [broad-exception-raised] +raise BaseException() # [broad-exception-raised] +raise CustomBroadException() # [broad-exception-raised] +raise IndexError from None +raise CustomNarrowException() from None diff --git a/tests/functional/b/broad_exception/broad_exception_raised_trystar.rc b/tests/functional/b/broad_exception/broad_exception_raised_trystar.rc new file mode 100644 index 0000000000..48fca7d55b --- /dev/null +++ b/tests/functional/b/broad_exception/broad_exception_raised_trystar.rc @@ -0,0 +1,7 @@ +[EXCEPTIONS] +overgeneral-exceptions=builtins.BaseException, + builtins.Exception, + functional.b.broad_exception.broad_exception_raised_trystar.CustomBroadException, + broad_exception_raised_trystar.CustomBroadException +[testoptions] +min_pyver=3.11 diff --git a/tests/functional/b/broad_exception/broad_exception_raised_trystar.txt b/tests/functional/b/broad_exception/broad_exception_raised_trystar.txt new file mode 100644 index 0000000000..c88b26b4be --- /dev/null +++ b/tests/functional/b/broad_exception/broad_exception_raised_trystar.txt @@ -0,0 +1,8 @@ +broad-exception-raised:15:4:15:41:exploding_apple:"Raising too general exception: Exception":INFERENCE +broad-exception-raised:20:8:20:34:raise_and_catch_star:"Raising too general exception: Exception":INFERENCE +broad-exception-caught:21:12:21:21:raise_and_catch_star:Catching too general exception Exception:INFERENCE +broad-exception-raised:38:8:38:35:raise_catch_raise_star:"Raising too general exception: Exception":INFERENCE +broad-exception-raised:46:8:46:40:raise_catch_raise_using_alias_star:"Raising too general exception: Exception":INFERENCE +broad-exception-raised:48:0:48:17::"Raising too general exception: Exception":INFERENCE +broad-exception-raised:49:0:49:21::"Raising too general exception: BaseException":INFERENCE +broad-exception-raised:50:0:50:28::"Raising too general exception: CustomBroadException":INFERENCE diff --git a/tests/functional/b/broad_exception_caught.rc b/tests/functional/b/broad_exception_caught.rc deleted file mode 100644 index e0e1a7b6c5..0000000000 --- a/tests/functional/b/broad_exception_caught.rc +++ /dev/null @@ -1,4 +0,0 @@ -[EXCEPTIONS] -overgeneral-exceptions=builtins.BaseException, - builtins.Exception, - functional.b.broad_exception_caught.CustomBroadException diff --git a/tests/functional/b/broad_exception_raised.rc b/tests/functional/b/broad_exception_raised.rc deleted file mode 100644 index 4f85d2933f..0000000000 --- a/tests/functional/b/broad_exception_raised.rc +++ /dev/null @@ -1,4 +0,0 @@ -[EXCEPTIONS] -overgeneral-exceptions=builtins.BaseException, - builtins.Exception, - functional.b.broad_exception_raised.CustomBroadException diff --git a/tests/pyreverse/functional/class_diagrams/colorized_output/colorized.puml b/tests/pyreverse/functional/class_diagrams/colorized_output/colorized.puml index 1c3d49d806..fb60c29e27 100644 --- a/tests/pyreverse/functional/class_diagrams/colorized_output/colorized.puml +++ b/tests/pyreverse/functional/class_diagrams/colorized_output/colorized.puml @@ -21,6 +21,7 @@ class "ExceptionsChecker" as pylint.checkers.exceptions.ExceptionsChecker #44BB9 visit_compare(node: nodes.Compare) -> None visit_raise(node: nodes.Raise) -> None visit_try(node: nodes.Try) -> None + visit_trystar(node: nodes.TryStar) -> None } class "StdlibChecker" as pylint.checkers.stdlib.StdlibChecker #44BB99 { msgs : dict[str, MessageDefinitionTuple] diff --git a/tests/pyreverse/functional/class_diagrams/colorized_output/custom_colors.dot b/tests/pyreverse/functional/class_diagrams/colorized_output/custom_colors.dot index 4f1b5f8b1c..bdf7ed2f0f 100644 --- a/tests/pyreverse/functional/class_diagrams/colorized_output/custom_colors.dot +++ b/tests/pyreverse/functional/class_diagrams/colorized_output/custom_colors.dot @@ -3,7 +3,7 @@ rankdir=BT charset="utf-8" "custom_colors.CheckerCollector" [color="red", fontcolor="black", label=<{CheckerCollector|checker1
checker2
checker3
|}>, shape="record", style="filled"]; "pylint.extensions.check_elif.ElseifUsedChecker" [color="#44BB88", fontcolor="black", label=<{ElseifUsedChecker|msgs : dict
name : str
|leave_module(_: nodes.Module): None
process_tokens(tokens: list[TokenInfo]): None
visit_if(node: nodes.If): None
}>, shape="record", style="filled"]; -"pylint.checkers.exceptions.ExceptionsChecker" [color="yellow", fontcolor="black", label=<{ExceptionsChecker|msgs : dict
name : str
options : tuple
|open(): None
visit_binop(node: nodes.BinOp): None
visit_compare(node: nodes.Compare): None
visit_raise(node: nodes.Raise): None
visit_try(node: nodes.Try): None
}>, shape="record", style="filled"]; +"pylint.checkers.exceptions.ExceptionsChecker" [color="yellow", fontcolor="black", label=<{ExceptionsChecker|msgs : dict
name : str
options : tuple
|open(): None
visit_binop(node: nodes.BinOp): None
visit_compare(node: nodes.Compare): None
visit_raise(node: nodes.Raise): None
visit_try(node: nodes.Try): None
visit_trystar(node: nodes.TryStar): None
}>, shape="record", style="filled"]; "pylint.checkers.stdlib.StdlibChecker" [color="yellow", fontcolor="black", label=<{StdlibChecker|msgs : dict[str, MessageDefinitionTuple]
name : str
|deprecated_arguments(method: str): tuple[tuple[int \| None, str], ...]
deprecated_attributes(): Iterable[str]
deprecated_classes(module: str): Iterable[str]
deprecated_decorators(): Iterable[str]
deprecated_methods(): set[str]
visit_boolop(node: nodes.BoolOp): None
visit_call(node: nodes.Call): None
visit_functiondef(node: nodes.FunctionDef): None
visit_if(node: nodes.If): None
visit_ifexp(node: nodes.IfExp): None
visit_unaryop(node: nodes.UnaryOp): None
}>, shape="record", style="filled"]; "pylint.checkers.exceptions.ExceptionsChecker" -> "custom_colors.CheckerCollector" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="checker1", style="solid"]; "pylint.checkers.stdlib.StdlibChecker" -> "custom_colors.CheckerCollector" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="checker3", style="solid"]; diff --git a/tests/pyreverse/functional/class_diagrams/colorized_output/custom_colors.puml b/tests/pyreverse/functional/class_diagrams/colorized_output/custom_colors.puml index a132fb0d3e..ecfccde3c8 100644 --- a/tests/pyreverse/functional/class_diagrams/colorized_output/custom_colors.puml +++ b/tests/pyreverse/functional/class_diagrams/colorized_output/custom_colors.puml @@ -21,6 +21,7 @@ class "ExceptionsChecker" as pylint.checkers.exceptions.ExceptionsChecker #yello visit_compare(node: nodes.Compare) -> None visit_raise(node: nodes.Raise) -> None visit_try(node: nodes.Try) -> None + visit_trystar(node: nodes.TryStar) -> None } class "StdlibChecker" as pylint.checkers.stdlib.StdlibChecker #yellow { msgs : dict[str, MessageDefinitionTuple]