[ty] No boundness analysis for implicit instance attributes (#20128)

## Summary

With this PR, we stop performing boundness analysis for implicit
instance attributes:

```py
class C:
    def __init__(self):
        if False:   
            self.x = 1

C().x  # would previously show an error, with this PR we pretend the attribute exists
```

This PR is potentially just a temporary measure until we find a better
fix. But I have already invested a lot of time trying to find the root
cause of https://github.com/astral-sh/ty/issues/758 (and [this
example](https://github.com/astral-sh/ty/issues/758#issuecomment-3206108262),
which I'm not entirely sure is related) and I still don't understand
what is going on. This PR fixes the performance problems in both of
these problems (in a rather crude way).

The impact of the proposed change on the ecosystem is small, and the
three new diagnostics are arguably true positives (previously hidden
because we considered the code unreachable, based on e.g. `assert`ions
that depended on implicit instance attributes). So this seems like a
reasonable fix for now.

Note that we still support cases like these:

```py
class D:
    if False:  # or any other expression that statically evaluates to `False`
        x: int = 1

D().x  # still an error


class E:
    if False:  # or any other expression that statically evaluates to `False`
        def f(self):
            self.x = 1

E().x  # still an error
```

closes https://github.com/astral-sh/ty/issues/758

## Test Plan

Updated tests, benchmark results
This commit is contained in:
David Peter 2025-08-28 16:25:07 +02:00 committed by GitHub
parent c2bc15bc15
commit b3c4005289
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 21 additions and 83 deletions

View file

@ -820,6 +820,8 @@ impl ReachabilityConstraints {
}
fn analyze_single(db: &dyn Db, predicate: &Predicate) -> Truthiness {
let _span = tracing::trace_span!("analyze_single", ?predicate).entered();
match predicate.node {
PredicateNode::Expression(test_expr) => {
static_expression_truthiness(db, test_expr).negate_if(!predicate.is_positive)

View file

@ -598,18 +598,6 @@ impl<'db> UseDefMap<'db> {
.is_always_false()
}
pub(crate) fn declaration_reachability(
&self,
db: &dyn crate::Db,
declaration: &DeclarationWithConstraint<'db>,
) -> Truthiness {
self.reachability_constraints.evaluate(
db,
&self.predicates,
declaration.reachability_constraint,
)
}
pub(crate) fn binding_reachability(
&self,
db: &dyn crate::Db,