Skip to content

Commit 62b81ec

Browse files
committed
[ty] No reachability analysis for implicit instance attributes
1 parent 18eaa65 commit 62b81ec

File tree

6 files changed

+15
-69
lines changed

6 files changed

+15
-69
lines changed

crates/ty_python_semantic/resources/mdtest/attributes.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -571,8 +571,8 @@ class C:
571571
if (2 + 3) < 4:
572572
self.x: str = "a"
573573

574-
# error: [unresolved-attribute]
575-
reveal_type(C().x) # revealed: Unknown
574+
# TODO: this would ideally raise a `unresolved-attribute` error
575+
reveal_type(C().x) # revealed: str
576576
```
577577

578578
```py
@@ -600,9 +600,10 @@ class C:
600600
def set_e(self, e: str) -> None:
601601
self.e = e
602602

603-
reveal_type(C(True).a) # revealed: Unknown | Literal[1]
604-
# error: [unresolved-attribute]
605-
reveal_type(C(True).b) # revealed: Unknown
603+
# TODO: this would ideally be `Unknown | Literal[1]`
604+
reveal_type(C(True).a) # revealed: Unknown | Literal[1, "a"]
605+
# TODO: this would ideally raise a `unresolved-attribute` error
606+
reveal_type(C(True).b) # revealed: Unknown | Literal[2]
606607
reveal_type(C(True).c) # revealed: Unknown | Literal[3] | str
607608
# Ideally, this would just be `Unknown | Literal[5]`, but we currently do not
608609
# attempt to analyze control flow within methods more closely. All reachable

crates/ty_python_semantic/src/semantic_index/reachability_constraints.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,8 @@ impl ReachabilityConstraints {
819819
}
820820

821821
fn analyze_single(db: &dyn Db, predicate: &Predicate) -> Truthiness {
822+
let _span = tracing::trace_span!("analyze_single", ?predicate).entered();
823+
822824
match predicate.node {
823825
PredicateNode::Expression(test_expr) => {
824826
let ty = infer_expression_type(db, test_expr);

crates/ty_python_semantic/src/semantic_index/use_def.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -598,18 +598,6 @@ impl<'db> UseDefMap<'db> {
598598
.is_always_false()
599599
}
600600

601-
pub(crate) fn is_declaration_reachable(
602-
&self,
603-
db: &dyn crate::Db,
604-
declaration: &DeclarationWithConstraint<'db>,
605-
) -> Truthiness {
606-
self.reachability_constraints.evaluate(
607-
db,
608-
&self.predicates,
609-
declaration.reachability_constraint,
610-
)
611-
}
612-
613601
pub(crate) fn is_binding_reachable(
614602
&self,
615603
db: &dyn crate::Db,

crates/ty_python_semantic/src/types.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8659,14 +8659,6 @@ impl Truthiness {
86598659
if condition { self.negate() } else { self }
86608660
}
86618661

8662-
pub(crate) fn and(self, other: Self) -> Self {
8663-
match (self, other) {
8664-
(Truthiness::AlwaysTrue, Truthiness::AlwaysTrue) => Truthiness::AlwaysTrue,
8665-
(Truthiness::AlwaysFalse, _) | (_, Truthiness::AlwaysFalse) => Truthiness::AlwaysFalse,
8666-
_ => Truthiness::Ambiguous,
8667-
}
8668-
}
8669-
86708662
pub(crate) fn or(self, other: Self) -> Self {
86718663
match (self, other) {
86728664
(Truthiness::AlwaysFalse, Truthiness::AlwaysFalse) => Truthiness::AlwaysFalse,

crates/ty_python_semantic/src/types/class.rs

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2736,13 +2736,6 @@ impl<'db> ClassLiteral<'db> {
27362736
// self.name: <annotation>
27372737
// self.name: <annotation> = …
27382738

2739-
if use_def_map(db, method_scope)
2740-
.is_declaration_reachable(db, &attribute_declaration)
2741-
.is_always_false()
2742-
{
2743-
continue;
2744-
}
2745-
27462739
let annotation = declaration_type(db, declaration);
27472740
let annotation =
27482741
Place::bound(annotation.inner).with_qualifiers(annotation.qualifiers);
@@ -2778,8 +2771,6 @@ impl<'db> ClassLiteral<'db> {
27782771
continue;
27792772
}
27802773

2781-
let method_map = use_def_map(db, method_scope);
2782-
27832774
// The attribute assignment inherits the reachability of the method which contains it
27842775
let is_method_reachable =
27852776
if let Some(method_def) = method_scope.node(db).as_function(&module) {
@@ -2799,49 +2790,15 @@ impl<'db> ClassLiteral<'db> {
27992790
continue;
28002791
}
28012792

2802-
// Storage for the implicit `DefinitionState::Undefined` binding. If present, it
2803-
// will be the first binding in the `attribute_assignments` iterator.
2804-
let mut unbound_binding = None;
2805-
28062793
for attribute_assignment in attribute_assignments {
28072794
if let DefinitionState::Undefined = attribute_assignment.binding {
2808-
// Store the implicit unbound binding here so that we can delay the
2809-
// computation of `unbound_reachability` to the point when we actually
2810-
// need it. This is an optimization for the common case where the
2811-
// `unbound` binding is the only binding of the `name` attribute,
2812-
// i.e. if there is no `self.name = …` assignment in this method.
2813-
unbound_binding = Some(attribute_assignment);
28142795
continue;
28152796
}
28162797

28172798
let DefinitionState::Defined(binding) = attribute_assignment.binding else {
28182799
continue;
28192800
};
2820-
match method_map
2821-
.is_binding_reachable(db, &attribute_assignment)
2822-
.and(is_method_reachable)
2823-
{
2824-
Truthiness::AlwaysTrue | Truthiness::Ambiguous => {
2825-
is_attribute_bound = true;
2826-
}
2827-
Truthiness::AlwaysFalse => {
2828-
continue;
2829-
}
2830-
}
2831-
2832-
// There is at least one attribute assignment that may be reachable, so if `unbound_reachability` is
2833-
// always false then this attribute is considered bound.
2834-
// TODO: this is incomplete logic since the attributes bound after termination are considered reachable.
2835-
let unbound_reachability = unbound_binding
2836-
.as_ref()
2837-
.map(|binding| method_map.is_binding_reachable(db, binding))
2838-
.unwrap_or(Truthiness::AlwaysFalse);
2839-
2840-
if unbound_reachability
2841-
.negate()
2842-
.and(is_method_reachable)
2843-
.is_always_true()
2844-
{
2801+
if !is_method_reachable.is_always_false() {
28452802
is_attribute_bound = true;
28462803
}
28472804

crates/ty_python_semantic/src/types/infer.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4608,6 +4608,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
46084608
assignment: &'db AnnotatedAssignmentDefinitionKind,
46094609
definition: Definition<'db>,
46104610
) {
4611+
let _span =
4612+
tracing::trace_span!("infer_annotated_assignment_definition", ?assignment).entered();
46114613
let annotation = assignment.annotation(self.module());
46124614
let target = assignment.target(self.module());
46134615
let value = assignment.value(self.module());
@@ -5550,6 +5552,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
55505552
}
55515553

55525554
fn infer_maybe_standalone_expression(&mut self, expression: &ast::Expr) -> Type<'db> {
5555+
let _span =
5556+
tracing::trace_span!("infer_maybe_standalone_expression", ?expression).entered();
5557+
55535558
if let Some(standalone_expression) = self.index.try_expression(expression) {
55545559
self.infer_standalone_expression_impl(expression, standalone_expression)
55555560
} else {
@@ -9416,6 +9421,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
94169421
annotation: &ast::Expr,
94179422
deferred_state: DeferredExpressionState,
94189423
) -> TypeAndQualifiers<'db> {
9424+
let _span = tracing::trace_span!("infer_annotation_expression", ?annotation).entered();
94199425
let previous_deferred_state = std::mem::replace(&mut self.deferred_state, deferred_state);
94209426
let annotation_ty = self.infer_annotation_expression_impl(annotation);
94219427
self.deferred_state = previous_deferred_state;

0 commit comments

Comments
 (0)