# Source: https://github.com/github/codeql/blob/592c7c043734f6bb48768a56261d711446cde25f/python/ql/test/query-tests/Statements/unreachable/test.py
# License: MIT
# Note: verbatim copy of the CodeQL Python `UnreachableCode` query test fixture pinned at main commit 592c7c043734f6bb48768a56261d711446cde25f. CodeQL's `UnreachableCode.expected` flags six statements as unreachable (upstream lines 8, 10, 16, 21, 28, 84; audit-corpus lines 12, 14, 20, 25, 32, 88 after the 3-line header + 1 blank). Mapping against cntrdct's Python unreachable-after-terminator implementation: only upstream line 21 (`for x in first_unreachable_stmt():` following `return 5` at upstream line 20) is a TP — it matches spec F3 (return-statement terminator followed by another statement in the same function-body block). The remaining five are FN by F3: cntrdct's detector recognises only return / raise / break / continue / `assert False` / exit-call terminators inside a block, and does not classify `while False:` / `while 0:` (upstream 8, 10) or `if False:` / `else:` of `if True:` (upstream 16, 28) as constant-condition branches that render their bodies unreachable, nor does it reason about `except` handlers whose exception type can never fire (upstream 84). Surfacing the gap is the audit's job; widening F3 to constant-condition / exception-typed branches is separate engineering with its own preregistration.


#Unreachable code

def f(x):
    while x:
        print (x)
    while 0:
        asgn = unreachable()
    while False:
        return unreachable()
    while 7:
        print(x)

def g(x):
    if False:
        unreachable()
    else:
        reachable()
    print(x)
    return 5
    for x in first_unreachable_stmt():
        raise more_unreachable()

def h(a,b):
    if True:
        reachable()
    else:
        unreachable()

def intish(n):
    """"Regression test - the 'except' statement is reachable"""
    test = 0
    try:
        test += n
    except:
        return False
    return True

#ODASA-2033
def unexpected_return_result():
    try:
        assert 0, "async.Return with argument inside async.generator function"
    except AssertionError:
        return (None, sys.exc_info())

#Yield may raise -- E.g. in a context manager
def handle_yield_exception():
    resources = get_resources()
    try:
        yield resources
    except Exception as exc:
        log(exc)

#ODASA-ODASA-3790
def isnt_iter(seq):
    got_exc = False
    try:
        for x in seq:
            pass
    except Exception:
        got_exc = True
    return got_exc


class Odasa3686(object):

    def is_iterable(self, obj):
        #special case string
        if not object:
            return False
        if isinstance(obj, str):
            return False

        #Test for iterability
        try:
            None in obj
            return True
        except TypeError:
            return False

def odasa5387():
    try:
        str
    except NameError: # Unreachable 'str' is always defined
        pass
    try:
        unicode
    except NameError: # Reachable as 'unicode' is undefined in Python 3
        pass

#This is OK as type-hints require it
if False:
    from typing import Any

def foo():
    # type: () -> None
    return

#ODASA-6483
def deliberate_name_error(cond):
    if cond:
        x = 0
    try:
        x
    except NameError:
        x = 1
    return x

#ODASA-6783
def emtpy_gen():
    if False:
        yield None


def foo(x):
    if True:
        if x < 3:
            print(x, "< 3")
        if x == 0:
            print(x, "== 0")


# Unreachable catch-all case

def unreachable_catch_all_assert_false(x):
    if x < 0:
        return "negative"
    elif x >= 0:
        return "positive"
    else:
        assert False, x

def unreachable_catch_all_raise(x):
    if x < 0:
        pass
    elif x >= 0:
        pass
    else:
        raise ValueError(x)

def ok_match(x):
    match x:
        case False:
            pass
        case True:
            pass
        case _:
            pass

    match x:
        case "true":
            pass
        case _:
            pass
    match x:
        case 42:
            pass
        case _:  # FP
            pass
    match x:
        case None:
            pass  # FP
        case _:
            pass
    match x:
        case 0.0:
            pass  # FP
        case _:
            pass
