ruff/crates/ty_python_semantic/resources/mdtest
David Peter b01003f81d
[ty] Infer nonlocal types as unions of all reachable bindings (#18750)
## Summary

This PR includes a behavioral change to how we infer types for public
uses of symbols within a module. Where we would previously use the type
that a use at the end of the scope would see, we now consider all
reachable bindings and union the results:

```py
x = None

def f():
    reveal_type(x)  # previously `Unknown | Literal[1]`, now `Unknown | None | Literal[1]`

f()

x = 1

f()
```

This helps especially in cases where the the end of the scope is not
reachable:

```py
def outer(x: int):
    def inner():
        reveal_type(x)  # previously `Unknown`, now `int`

    raise ValueError
```

This PR also proposes to skip the boundness analysis of public uses.
This is consistent with the "all reachable bindings" strategy, because
the implicit `x = <unbound>` binding is also always reachable, and we
would have to emit "possibly-unresolved" diagnostics for every public
use otherwise. Changing this behavior allows common use-cases like the
following to type check without any errors:

```py
def outer(flag: bool):
    if flag:
        x = 1

        def inner():
            print(x)  # previously: possibly-unresolved-reference, now: no error
```

closes https://github.com/astral-sh/ty/issues/210
closes https://github.com/astral-sh/ty/issues/607
closes https://github.com/astral-sh/ty/issues/699

## Follow up

It is now possible to resolve the following TODO, but I would like to do
that as a follow-up, because it requires some changes to how we treat
implicit attribute assignments, which could result in ecosystem changes
that I'd like to see separately.


315fb0f3da/crates/ty_python_semantic/src/semantic_index/builder.rs (L1095-L1117)

## Ecosystem analysis

[**Full report**](https://shark.fish/diff-public-types.html)

* This change obviously removes a lot of `possibly-unresolved-reference`
diagnostics (7818) because we do not analyze boundness for public uses
of symbols inside modules anymore.
* As the primary goal here, this change also removes a lot of
false-positive `unresolved-reference` diagnostics (231) in scenarios
like this:
    ```py
    def _(flag: bool):
        if flag:
            x = 1
    
            def inner():
                x
    
            raise
    ```
* This change also introduces some new false positives for cases like:
    ```py
    def _():
        x = None
    
        x = "test"
    
        def inner():
x.upper() # Attribute `upper` on type `Unknown | None | Literal["test"]`
is possibly unbound
    ```
We have test cases for these situations and it's plausible that we can
improve this in a follow-up.


## Test Plan

New Markdown tests
2025-06-26 12:24:40 +02:00
..
annotations [ty] Improve disjointness inference for NominalInstanceTypes and SubclassOfTypes (#18864) 2025-06-24 20:27:37 +00:00
assignment
binary
boolean
boundness_declaredness
call [ty] eliminate is_fully_static (#18799) 2025-06-24 18:02:05 -07:00
class
comparison
comprehensions
conditional [ty] Improve disjointness inference for NominalInstanceTypes and SubclassOfTypes (#18864) 2025-06-24 20:27:37 +00:00
declaration [ty] Infer nonlocal types as unions of all reachable bindings (#18750) 2025-06-26 12:24:40 +02:00
diagnostics
directives
doc
exception
expression
function
generics [ty] eliminate is_fully_static (#18799) 2025-06-24 18:02:05 -07:00
ide_support
import
literal
loops
narrow [ty] Type narrowing in comprehensions (#18934) 2025-06-25 11:30:28 +02:00
regression
scopes [ty] Infer nonlocal types as unions of all reachable bindings (#18750) 2025-06-26 12:24:40 +02:00
shadowing
snapshots [ty] Add subdiagnostic about empty bodies in more cases (#18942) 2025-06-25 20:25:00 +01:00
stubs
subscript [ty] Fix false positives when subscripting an object inferred as having an Intersection type (#18920) 2025-06-24 18:39:02 +00:00
suppressions
type_compendium [ty] eliminate is_fully_static (#18799) 2025-06-24 18:02:05 -07:00
type_of
type_properties [ty] eliminate is_fully_static (#18799) 2025-06-24 18:02:05 -07:00
type_qualifiers
unary
with
.mdformat.toml
attributes.md [ty] Add decorator check for implicit attribute assignments (#18587) 2025-06-24 11:42:10 +02:00
cycle.md
dataclass_transform.md
dataclasses.md [ty] eliminate is_fully_static (#18799) 2025-06-24 18:02:05 -07:00
decorators.md
del.md
descriptor_protocol.md
final.md
instance_layout_conflict.md [ty] Improve disjointness inference for NominalInstanceTypes and SubclassOfTypes (#18864) 2025-06-24 20:27:37 +00:00
intersection_types.md [ty] eliminate is_fully_static (#18799) 2025-06-24 18:02:05 -07:00
invalid_syntax.md
known_constants.md
mdtest_config.md
mdtest_custom_typeshed.md
metaclass.md
mro.md
named_tuple.md
overloads.md
pep695_type_aliases.md
properties.md
protocols.md [ty] eliminate is_fully_static (#18799) 2025-06-24 18:02:05 -07:00
public_types.md [ty] Infer nonlocal types as unions of all reachable bindings (#18750) 2025-06-26 12:24:40 +02:00
statically_known_branches.md [ty] Infer nonlocal types as unions of all reachable bindings (#18750) 2025-06-26 12:24:40 +02:00
sys_platform.md
sys_version_info.md
terminal_statements.md [ty] Infer nonlocal types as unions of all reachable bindings (#18750) 2025-06-26 12:24:40 +02:00
type_api.md [ty] eliminate is_fully_static (#18799) 2025-06-24 18:02:05 -07:00
typed_dict.md
union_types.md [ty] eliminate is_fully_static (#18799) 2025-06-24 18:02:05 -07:00
unpacking.md [ty] Avoid duplicate diagnostic in unpacking (#18897) 2025-06-24 07:49:44 +05:30
unreachable.md [ty] Infer nonlocal types as unions of all reachable bindings (#18750) 2025-06-26 12:24:40 +02:00