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]