This rule raises an issue when an `except` statement has had all its exceptions caught by a previous `except` clause.

== Why is this an issue?

Exceptions handlers (`except`) are evaluated in the order they are written. Once a match is found, the evaluation stops.

In some contexts, an except block is dead code as it will never catch any exception:

* If there is a handler for a base class followed by a handler for class derived from that base class, the second handler will never trigger: The handler for the base class will match the derived class, and will be the only executed handler.
* When multiple `except` statements try to catch the same exception class, only the first one will be executed.
* In Python 3, `BaseException` is the parent of every exception class. When a `BaseException` is caught by an `except` clause, none of the subsequent `except` statement will catch anything. This is true as well for the bare except statement (`except:`).


== How to fix it

When using multiple `except` statements, make sure to:

* Order the `except` blocks from the most specialzed exception to the most generic, i.e when wanting to catch a `FloatingPointError` and an `ArithemticError`, as `FloatingPointError` is a subclass of `ArithmeticError`, the first `except` statement should be `FloatingPointError`.

* Catch the same exception only once.

* Catch a `BaseException` only once with either an `except BaseException:` statement or a bare `except:` statement, as the two statements are equivalent.

=== Code examples

==== Noncompliant code example

[source,python,diff-id=1,diff-type=noncompliant]
----
def foo():
    try:
        raise FloatingPointError()
    except (ArithmeticError, RuntimeError) as e: 
        print(e)
    except FloatingPointError as e: # Noncompliant: FloatingPointError is a subclass of ArithmeticError.
        print("Never executed")
    except OverflowError as e: # Noncompliant: OverflowError is a subclass of ArithmeticError.
        print("Never executed")

    try:
        raise TypeError()
    except TypeError as e: 
        print(e)
    except TypeError as e: # Noncompliant: duplicate except.
        print("Never executed")

    try:
        raise ValueError()
    except BaseException as e:  
        print(e)
    except: # Noncompliant: this is equivalent to "except BaseException" block.
        print("Never executed")
----

==== Compliant solution

[source,python,diff-id=1,diff-type=compliant]
----
def foo():
    try:
        raise FloatingPointError()
    except FloatingPointError as e:
        print("Executed")
    except OverflowError as e:
        print("Executed")
    except (ArithmeticError, RuntimeError) as e: 
        print(e)

    try:
        raise TypeError()
    except TypeError as e: 
        print(e)

    try:
        raise ValueError()
    except BaseException as e:  
        print(e)
----

*Note*: __It is generally not recommended to try catching `BaseException`, as it is the base class for all built-in exceptions in Python, including system-exiting exceptions like ``++SystemExit++`` or ``++KeyboardInterrupt++``, which are typically not meant to be caught. See https://www.python.org/dev/peps/pep-0352/#exception-hierarchy-changes[PEP 352] for more information.__


== Resources

=== Documentation 

* https://docs.python.org/3/reference/compound_stmts.html#the-try-statement[The `try` statement]
* https://docs.python.org/3/library/exceptions.html#exception-hierarchy[Exception hierarchy]

ifdef::env-github,rspecator-view[]

'''
== Implementation Specification
(visible only on this page)

=== Highlighting

Primary:

* All exceptions which will never match because they are subclasses or duplicate of the first exception caught.
* The "except:" statement if "BaseException" is caught before.

Secondary:

* The exception class which shadows other except blocks.
message should be: 'Exceptions will be caught here.'


=== Message

* When a parent except block is caught before derived classes
* Catch this exception only once; it is already handled by a previous except clause.
* When the same exception class is caught multiple times:
* Catch this exception only once; it is already handled by a previous except clause.
* When an "BaseException" is caught in the same try-except as a bare "except:"
* Merge this bare "except:" with the "BaseException" catch


'''
== Comments And Links
(visible only on this page)

include::../comments-and-links.adoc[]

endif::env-github,rspecator-view[]
