[ty] Add named fields for Place enum (#22172)

## Summary

Mechanical refactor to migrate this enum to named fields. No functional
changes.

See:
https://github.com/astral-sh/ruff/pull/22093#discussion_r2636050127.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Charlie Marsh
2026-01-06 12:24:51 -05:00
committed by GitHub
parent b59f6eb5e9
commit 01de8bef3e
15 changed files with 791 additions and 453 deletions

View File

@@ -88,6 +88,45 @@ impl Widening {
}
}
/// A defined place with its type, origin, definedness, and widening information.
#[derive(Debug, Clone, Copy, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub(crate) struct DefinedPlace<'db> {
pub(crate) ty: Type<'db>,
pub(crate) origin: TypeOrigin,
pub(crate) definedness: Definedness,
pub(crate) widening: Widening,
}
impl<'db> DefinedPlace<'db> {
pub(crate) fn new(ty: Type<'db>) -> Self {
Self {
ty,
origin: TypeOrigin::Inferred,
definedness: Definedness::AlwaysDefined,
widening: Widening::None,
}
}
pub(crate) fn with_origin(mut self, origin: TypeOrigin) -> Self {
self.origin = origin;
self
}
pub(crate) fn with_definedness(mut self, definedness: Definedness) -> Self {
self.definedness = definedness;
self
}
pub(crate) fn with_widening(mut self, widening: Widening) -> Self {
self.widening = widening;
self
}
pub(crate) const fn is_definitely_defined(&self) -> bool {
matches!(self.definedness, Definedness::AlwaysDefined)
}
}
/// The result of a place lookup, which can either be a (possibly undefined) type
/// or a completely undefined place.
///
@@ -110,50 +149,35 @@ impl Widening {
///
/// If we look up places in this scope, we would get the following results:
/// ```rs
/// bound: Place::Defined(Literal[1], TypeOrigin::Inferred, Definedness::AlwaysDefined, _),
/// declared: Place::Defined(int, TypeOrigin::Declared, Definedness::AlwaysDefined, _),
/// possibly_unbound: Place::Defined(Literal[2], TypeOrigin::Inferred, Definedness::PossiblyUndefined, _),
/// possibly_undeclared: Place::Defined(int, TypeOrigin::Declared, Definedness::PossiblyUndefined, _),
/// bound_or_declared: Place::Defined(Literal[1], TypeOrigin::Inferred, Definedness::PossiblyUndefined, _),
/// bound: Place::Defined(DefinedPlace { ty: Literal[1], origin: TypeOrigin::Inferred, definedness: Definedness::AlwaysDefined, .. }),
/// declared: Place::Defined(DefinedPlace { ty: int, origin: TypeOrigin::Declared, definedness: Definedness::AlwaysDefined, .. }),
/// possibly_unbound: Place::Defined(DefinedPlace { ty: Literal[2], origin: TypeOrigin::Inferred, definedness: Definedness::PossiblyUndefined, .. }),
/// possibly_undeclared: Place::Defined(DefinedPlace { ty: int, origin: TypeOrigin::Declared, definedness: Definedness::PossiblyUndefined, .. }),
/// bound_or_declared: Place::Defined(DefinedPlace { ty: Literal[1], origin: TypeOrigin::Inferred, definedness: Definedness::PossiblyUndefined, .. }),
/// non_existent: Place::Undefined,
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub(crate) enum Place<'db> {
Defined(Type<'db>, TypeOrigin, Definedness, Widening),
Defined(DefinedPlace<'db>),
Undefined,
}
impl<'db> Place<'db> {
/// Constructor that creates a [`Place`] with type origin [`TypeOrigin::Inferred`] and definedness [`Definedness::AlwaysDefined`].
pub(crate) fn bound(ty: impl Into<Type<'db>>) -> Self {
Place::Defined(
ty.into(),
TypeOrigin::Inferred,
Definedness::AlwaysDefined,
Widening::None,
)
Place::Defined(DefinedPlace::new(ty.into()))
}
/// Constructor that creates a [`Place`] with type origin [`TypeOrigin::Declared`] and definedness [`Definedness::AlwaysDefined`].
pub(crate) fn declared(ty: impl Into<Type<'db>>) -> Self {
Place::Defined(
ty.into(),
TypeOrigin::Declared,
Definedness::AlwaysDefined,
Widening::None,
)
Place::Defined(DefinedPlace::new(ty.into()).with_origin(TypeOrigin::Declared))
}
/// Constructor that creates a [`Place`] with a [`crate::types::TodoType`] type
/// and definedness [`Definedness::AlwaysDefined`].
#[allow(unused_variables)] // Only unused in release builds
pub(crate) fn todo(message: &'static str) -> Self {
Place::Defined(
todo_type!(message),
TypeOrigin::Inferred,
Definedness::AlwaysDefined,
Widening::None,
)
Place::Defined(DefinedPlace::new(todo_type!(message)))
}
pub(crate) fn is_undefined(&self) -> bool {
@@ -166,7 +190,7 @@ impl<'db> Place<'db> {
/// if there is at least one control-flow path where the place is defined, return the type.
pub(crate) fn ignore_possibly_undefined(&self) -> Option<Type<'db>> {
match self {
Place::Defined(ty, _, _, _) => Some(*ty),
Place::Defined(defined) => Some(defined.ty),
Place::Undefined => None,
}
}
@@ -177,7 +201,7 @@ impl<'db> Place<'db> {
/// is applied lazily when converting to `LookupResult`.
pub(crate) fn unwidened_type(&self) -> Option<Type<'db>> {
match self {
Place::Defined(ty, _, _, _) => Some(*ty),
Place::Defined(defined) => Some(defined.ty),
Place::Undefined => None,
}
}
@@ -192,20 +216,19 @@ impl<'db> Place<'db> {
#[must_use]
pub(crate) fn map_type(self, f: impl FnOnce(Type<'db>) -> Type<'db>) -> Place<'db> {
match self {
Place::Defined(ty, origin, definedness, widening) => {
Place::Defined(f(ty), origin, definedness, widening)
}
Place::Defined(defined) => Place::Defined(DefinedPlace {
ty: f(defined.ty),
..defined
}),
Place::Undefined => Place::Undefined,
}
}
/// Set the widening mode for this place.
#[must_use]
pub(crate) fn with_widening(self, widening: Widening) -> Place<'db> {
pub(crate) fn with_widening(self, new_widening: Widening) -> Place<'db> {
match self {
Place::Defined(ty, origin, definedness, _) => {
Place::Defined(ty, origin, definedness, widening)
}
Place::Defined(defined) => Place::Defined(defined.with_widening(new_widening)),
Place::Undefined => Place::Undefined,
}
}
@@ -223,24 +246,32 @@ impl<'db> Place<'db> {
/// This is used to resolve (potential) descriptor attributes.
pub(crate) fn try_call_dunder_get(self, db: &'db dyn Db, owner: Type<'db>) -> Place<'db> {
match self {
Place::Defined(Type::Union(union), origin, definedness, widening) => union
.map_with_boundness(db, |elem| {
Place::Defined(*elem, origin, definedness, widening)
.try_call_dunder_get(db, owner)
}),
Place::Defined(
place @ DefinedPlace {
ty: Type::Union(union),
..
},
) => union.map_with_boundness(db, |elem| {
Place::Defined(DefinedPlace { ty: *elem, ..place }).try_call_dunder_get(db, owner)
}),
Place::Defined(Type::Intersection(intersection), origin, definedness, widening) => {
intersection.map_with_boundness(db, |elem| {
Place::Defined(*elem, origin, definedness, widening)
.try_call_dunder_get(db, owner)
})
}
Place::Defined(
place @ DefinedPlace {
ty: Type::Intersection(intersection),
..
},
) => intersection.map_with_boundness(db, |elem| {
Place::Defined(DefinedPlace { ty: *elem, ..place }).try_call_dunder_get(db, owner)
}),
Place::Defined(self_ty, origin, definedness, widening) => {
Place::Defined(defined) => {
if let Some((dunder_get_return_ty, _)) =
self_ty.try_call_dunder_get(db, Type::none(db), owner)
defined.ty.try_call_dunder_get(db, Type::none(db), owner)
{
Place::Defined(dunder_get_return_ty, origin, definedness, widening)
Place::Defined(DefinedPlace {
ty: dunder_get_return_ty,
..defined
})
} else {
self
}
@@ -251,7 +282,13 @@ impl<'db> Place<'db> {
}
pub(crate) const fn is_definitely_bound(&self) -> bool {
matches!(self, Place::Defined(_, _, Definedness::AlwaysDefined, _))
matches!(
self,
Place::Defined(DefinedPlace {
definedness: Definedness::AlwaysDefined,
..
})
)
}
}
@@ -262,10 +299,8 @@ impl<'db> From<LookupResult<'db>> for PlaceAndQualifiers<'db> {
.with_qualifiers(type_and_qualifiers.qualifiers()),
Err(LookupError::Undefined(qualifiers)) => Place::Undefined.with_qualifiers(qualifiers),
Err(LookupError::PossiblyUndefined(type_and_qualifiers)) => Place::Defined(
type_and_qualifiers.inner_type(),
TypeOrigin::Inferred,
Definedness::PossiblyUndefined,
Widening::None,
DefinedPlace::new(type_and_qualifiers.inner_type())
.with_definedness(Definedness::PossiblyUndefined),
)
.with_qualifiers(type_and_qualifiers.qualifiers()),
}
@@ -717,20 +752,17 @@ impl<'db> PlaceAndQualifiers<'db> {
pub(crate) fn into_lookup_result(self, db: &'db dyn Db) -> LookupResult<'db> {
match self {
PlaceAndQualifiers {
place: Place::Defined(ty, origin, Definedness::AlwaysDefined, widening),
place: Place::Defined(place),
qualifiers,
} => {
let ty = widening.apply_if_needed(db, ty);
Ok(TypeAndQualifiers::new(ty, origin, qualifiers))
}
PlaceAndQualifiers {
place: Place::Defined(ty, origin, Definedness::PossiblyUndefined, widening),
qualifiers,
} => {
let ty = widening.apply_if_needed(db, ty);
Err(LookupError::PossiblyUndefined(TypeAndQualifiers::new(
ty, origin, qualifiers,
)))
let ty = place.widening.apply_if_needed(db, place.ty);
let type_and_qualifiers = TypeAndQualifiers::new(ty, place.origin, qualifiers);
match place.definedness {
Definedness::AlwaysDefined => Ok(type_and_qualifiers),
Definedness::PossiblyUndefined => {
Err(LookupError::PossiblyUndefined(type_and_qualifiers))
}
}
}
PlaceAndQualifiers {
place: Place::Undefined,
@@ -783,15 +815,10 @@ impl<'db> PlaceAndQualifiers<'db> {
let place = match (previous_place.place, self.place) {
// In fixed-point iteration of type inference, the member type must be monotonically widened and not "oscillate".
// Here, monotonicity is guaranteed by pre-unioning the type of the previous iteration into the current result.
(
Place::Defined(prev_ty, _, _, _),
Place::Defined(ty, origin, definedness, widening),
) => Place::Defined(
ty.cycle_normalized(db, prev_ty, cycle),
origin,
definedness,
widening,
),
(Place::Defined(prev), Place::Defined(current)) => Place::Defined(DefinedPlace {
ty: current.ty.cycle_normalized(db, prev.ty, cycle),
..current
}),
// If a `Place` in the current cycle is `Defined` but `Undefined` in the previous cycle,
// that means that its definedness depends on the truthiness of the previous cycle value.
// In this case, the definedness of the current cycle `Place` is set to `PossiblyUndefined`.
@@ -799,26 +826,22 @@ impl<'db> PlaceAndQualifiers<'db> {
// so convergence is guaranteed without resorting to this handling.
// However, the handling described above may reduce the exactness of reachability analysis,
// so it may be better to remove it. In that case, this branch is necessary.
(Place::Undefined, Place::Defined(ty, origin, _definedness, widening)) => {
Place::Defined(
ty.recursive_type_normalized(db, cycle),
origin,
Definedness::PossiblyUndefined,
widening,
)
}
(Place::Undefined, Place::Defined(current)) => Place::Defined(DefinedPlace {
ty: current.ty.recursive_type_normalized(db, cycle),
definedness: Definedness::PossiblyUndefined,
..current
}),
// If a `Place` that was `Defined(Divergent)` in the previous cycle is actually found to be unreachable in the current cycle,
// it is set to `Undefined` (because the cycle initial value does not include meaningful reachability information).
(Place::Defined(ty, origin, _definedness, widening), Place::Undefined) => {
if cycle.head_ids().any(|id| ty == Type::divergent(id)) {
(Place::Defined(prev), Place::Undefined) => {
if cycle.head_ids().any(|id| prev.ty == Type::divergent(id)) {
Place::Undefined
} else {
Place::Defined(
ty.recursive_type_normalized(db, cycle),
origin,
Definedness::PossiblyUndefined,
widening,
)
Place::Defined(DefinedPlace {
ty: prev.ty.recursive_type_normalized(db, cycle),
definedness: Definedness::PossiblyUndefined,
..prev
})
}
}
(Place::Undefined, Place::Undefined) => Place::Undefined,
@@ -900,32 +923,56 @@ pub(crate) fn place_by_id<'db>(
// Handle bare `ClassVar` annotations by falling back to the union of `Unknown` and the
// inferred type.
PlaceAndQualifiers {
place: Place::Defined(Type::Dynamic(DynamicType::Unknown), origin, definedness, _),
place:
Place::Defined(DefinedPlace {
ty: Type::Dynamic(DynamicType::Unknown),
origin,
definedness,
..
}),
qualifiers,
} if qualifiers.contains(TypeQualifiers::CLASS_VAR) => {
let bindings = all_considered_bindings();
match place_from_bindings_impl(db, bindings, requires_explicit_reexport).place {
Place::Defined(inferred, origin, boundness, _) => Place::Defined(
UnionType::from_elements(db, [Type::unknown(), inferred]),
Place::Defined(DefinedPlace {
ty: inferred,
origin,
boundness,
Widening::None,
)
definedness: boundness,
..
}) => Place::Defined(DefinedPlace {
ty: UnionType::from_elements(db, [Type::unknown(), inferred]),
origin,
definedness: boundness,
widening: Widening::None,
})
.with_qualifiers(qualifiers),
Place::Undefined => Place::Defined(DefinedPlace {
ty: Type::unknown(),
origin,
definedness,
widening: Widening::None,
})
.with_qualifiers(qualifiers),
Place::Undefined => {
Place::Defined(Type::unknown(), origin, definedness, Widening::None)
.with_qualifiers(qualifiers)
}
}
}
// Place is declared, trust the declared type
place_and_quals @ PlaceAndQualifiers {
place: Place::Defined(_, _, Definedness::AlwaysDefined, _),
place:
Place::Defined(DefinedPlace {
definedness: Definedness::AlwaysDefined,
..
}),
qualifiers: _,
} => place_and_quals,
// Place is possibly declared
PlaceAndQualifiers {
place: Place::Defined(declared_ty, origin, Definedness::PossiblyUndefined, _),
place:
Place::Defined(DefinedPlace {
ty: declared_ty,
origin,
definedness: Definedness::PossiblyUndefined,
..
}),
qualifiers,
} => {
let bindings = all_considered_bindings();
@@ -938,24 +985,29 @@ pub(crate) fn place_by_id<'db>(
// TODO: We probably don't want to report `AlwaysDefined` here. This requires a bit of
// design work though as we might want a different behavior for stubs and for
// normal modules.
Place::Defined(
declared_ty,
Place::Defined(DefinedPlace {
ty: declared_ty,
origin,
Definedness::AlwaysDefined,
Widening::None,
)
definedness: Definedness::AlwaysDefined,
widening: Widening::None,
})
}
// Place is possibly undeclared and (possibly) bound
Place::Defined(inferred_ty, origin, boundness, _) => Place::Defined(
UnionType::from_elements(db, [inferred_ty, declared_ty]),
Place::Defined(DefinedPlace {
ty: inferred_ty,
origin,
if boundness_analysis == BoundnessAnalysis::AssumeBound {
definedness: boundness,
..
}) => Place::Defined(DefinedPlace {
ty: UnionType::from_elements(db, [inferred_ty, declared_ty]),
origin,
definedness: if boundness_analysis == BoundnessAnalysis::AssumeBound {
Definedness::AlwaysDefined
} else {
boundness
},
Widening::None,
),
widening: Widening::None,
}),
};
PlaceAndQualifiers { place, qualifiers }
@@ -971,10 +1023,11 @@ pub(crate) fn place_by_id<'db>(
place_from_bindings_impl(db, bindings, requires_explicit_reexport).place;
if boundness_analysis == BoundnessAnalysis::AssumeBound {
if let Place::Defined(ty, origin, Definedness::PossiblyUndefined, widening) =
inferred
{
inferred = Place::Defined(ty, origin, Definedness::AlwaysDefined, widening);
if let Place::Defined(defined) = inferred {
if defined.definedness == Definedness::PossiblyUndefined {
inferred =
Place::Defined(defined.with_definedness(Definedness::AlwaysDefined));
}
}
}
@@ -1263,14 +1316,11 @@ fn place_from_bindings_impl<'db>(
match deleted_reachability {
Truthiness::AlwaysFalse => {
Place::Defined(ty, TypeOrigin::Inferred, boundness, Widening::None)
Place::Defined(DefinedPlace::new(ty).with_definedness(boundness))
}
Truthiness::AlwaysTrue => Place::Undefined,
Truthiness::Ambiguous => Place::Defined(
ty,
TypeOrigin::Inferred,
Definedness::PossiblyUndefined,
Widening::None,
DefinedPlace::new(ty).with_definedness(Definedness::PossiblyUndefined),
),
}
} else {
@@ -1501,10 +1551,9 @@ fn place_from_declarations_impl<'db>(
};
let place_and_quals = Place::Defined(
declared.inner_type(),
TypeOrigin::Declared,
boundness,
Widening::None,
DefinedPlace::new(declared.inner_type())
.with_origin(TypeOrigin::Declared)
.with_definedness(boundness),
)
.with_qualifiers(declared.qualifiers());
@@ -1554,13 +1603,13 @@ mod implicit_globals {
use crate::Program;
use crate::db::Db;
use crate::place::{Definedness, PlaceAndQualifiers, TypeOrigin};
use crate::place::{Definedness, PlaceAndQualifiers};
use crate::semantic_index::symbol::Symbol;
use crate::semantic_index::{place_table, use_def_map};
use crate::types::{KnownClass, MemberLookupPolicy, Parameter, Parameters, Signature, Type};
use ruff_python_ast::PythonVersion;
use super::{Place, Widening, place_from_declarations};
use super::{DefinedPlace, Place, place_from_declarations};
pub(crate) fn module_type_implicit_global_declaration<'db>(
db: &'db dyn Db,
@@ -1618,14 +1667,16 @@ mod implicit_globals {
// Created lazily by the warnings machinery; may be absent.
// Model as possibly-unbound to avoid false negatives.
"__warningregistry__" => Place::Defined(
KnownClass::Dict
.to_specialized_instance(db, [Type::any(), KnownClass::Int.to_instance(db)]),
TypeOrigin::Inferred,
Definedness::PossiblyUndefined,
Widening::None,
)
.into(),
"__warningregistry__" => {
Place::Defined(
DefinedPlace::new(KnownClass::Dict.to_specialized_instance(
db,
[Type::any(), KnownClass::Int.to_instance(db)],
))
.with_definedness(Definedness::PossiblyUndefined),
)
.into()
}
// Marked as possibly-unbound as it is only present in the module namespace
// if at least one global symbol is annotated in the module.
@@ -1642,10 +1693,8 @@ mod implicit_globals {
)),
);
Place::Defined(
Type::function_like_callable(db, signature),
TypeOrigin::Inferred,
Definedness::PossiblyUndefined,
Widening::None,
DefinedPlace::new(Type::function_like_callable(db, signature))
.with_definedness(Definedness::PossiblyUndefined),
)
.into()
}
@@ -1847,21 +1896,41 @@ mod tests {
let unbound = || Place::Undefined.with_qualifiers(TypeQualifiers::empty());
let possibly_unbound_ty1 = || {
Place::Defined(ty1, Inferred, PossiblyUndefined, Widening::None)
.with_qualifiers(TypeQualifiers::empty())
Place::Defined(DefinedPlace {
ty: ty1,
origin: Inferred,
definedness: PossiblyUndefined,
widening: Widening::None,
})
.with_qualifiers(TypeQualifiers::empty())
};
let possibly_unbound_ty2 = || {
Place::Defined(ty2, Inferred, PossiblyUndefined, Widening::None)
.with_qualifiers(TypeQualifiers::empty())
Place::Defined(DefinedPlace {
ty: ty2,
origin: Inferred,
definedness: PossiblyUndefined,
widening: Widening::None,
})
.with_qualifiers(TypeQualifiers::empty())
};
let bound_ty1 = || {
Place::Defined(ty1, Inferred, AlwaysDefined, Widening::None)
.with_qualifiers(TypeQualifiers::empty())
Place::Defined(DefinedPlace {
ty: ty1,
origin: Inferred,
definedness: AlwaysDefined,
widening: Widening::None,
})
.with_qualifiers(TypeQualifiers::empty())
};
let bound_ty2 = || {
Place::Defined(ty2, Inferred, AlwaysDefined, Widening::None)
.with_qualifiers(TypeQualifiers::empty())
Place::Defined(DefinedPlace {
ty: ty2,
origin: Inferred,
definedness: AlwaysDefined,
widening: Widening::None,
})
.with_qualifiers(TypeQualifiers::empty())
};
// Start from an unbound symbol
@@ -1879,22 +1948,22 @@ mod tests {
);
assert_eq!(
possibly_unbound_ty1().or_fall_back_to(&db, possibly_unbound_ty2),
Place::Defined(
UnionType::from_elements(&db, [ty1, ty2]),
Inferred,
PossiblyUndefined,
Widening::None
)
Place::Defined(DefinedPlace {
ty: UnionType::from_elements(&db, [ty1, ty2]),
origin: Inferred,
definedness: PossiblyUndefined,
widening: Widening::None
})
.into()
);
assert_eq!(
possibly_unbound_ty1().or_fall_back_to(&db, bound_ty2),
Place::Defined(
UnionType::from_elements(&db, [ty1, ty2]),
Inferred,
AlwaysDefined,
Widening::None
)
Place::Defined(DefinedPlace {
ty: UnionType::from_elements(&db, [ty1, ty2]),
origin: Inferred,
definedness: AlwaysDefined,
widening: Widening::None
})
.into()
);
@@ -1911,7 +1980,11 @@ mod tests {
fn assert_bound_string_symbol<'db>(db: &'db dyn Db, symbol: Place<'db>) {
assert!(matches!(
symbol,
Place::Defined(Type::NominalInstance(_), _, Definedness::AlwaysDefined, _)
Place::Defined(DefinedPlace {
ty: Type::NominalInstance(_),
definedness: Definedness::AlwaysDefined,
..
})
));
assert_eq!(symbol.expect_type(), KnownClass::Str.to_instance(db));
}

View File

@@ -953,18 +953,14 @@ impl ReachabilityConstraints {
)
.place
{
crate::place::Place::Defined(
_,
_,
crate::place::Definedness::AlwaysDefined,
_,
) => Truthiness::AlwaysTrue,
crate::place::Place::Defined(
_,
_,
crate::place::Definedness::PossiblyUndefined,
_,
) => Truthiness::Ambiguous,
crate::place::Place::Defined(crate::place::DefinedPlace {
definedness: crate::place::Definedness::AlwaysDefined,
..
}) => Truthiness::AlwaysTrue,
crate::place::Place::Defined(crate::place::DefinedPlace {
definedness: crate::place::Definedness::PossiblyUndefined,
..
}) => Truthiness::Ambiguous,
crate::place::Place::Undefined => Truthiness::AlwaysFalse,
}
}

View File

@@ -37,8 +37,8 @@ pub(crate) use self::signatures::{CallableSignature, Signature};
pub(crate) use self::subclass_of::{SubclassOfInner, SubclassOfType};
pub use crate::diagnostic::add_inferred_python_version_hint_to_diagnostic;
use crate::place::{
Definedness, Place, PlaceAndQualifiers, TypeOrigin, Widening, builtins_module_scope,
imported_symbol, known_module_symbol,
DefinedPlace, Definedness, Place, PlaceAndQualifiers, TypeOrigin, Widening,
builtins_module_scope, imported_symbol, known_module_symbol,
};
use crate::semantic_index::definition::{Definition, DefinitionKind};
use crate::semantic_index::place::ScopedPlaceId;
@@ -1849,8 +1849,10 @@ impl<'db> Type<'db> {
)
.place;
if let Place::Defined(ty, _, Definedness::AlwaysDefined, _) = call_symbol {
ty.try_upcast_to_callable(db)
if let Place::Defined(place) = call_symbol
&& place.is_definitely_defined()
{
place.ty.try_upcast_to_callable(db)
} else {
None
}
@@ -2540,10 +2542,9 @@ impl<'db> Type<'db> {
fn static_member(&self, db: &'db dyn Db, name: &str) -> Place<'db> {
if let Type::ModuleLiteral(module) = self {
module.static_member(db, name).place
} else if let place @ Place::Defined(_, _, _, _) = self.class_member(db, name.into()).place
{
} else if let place @ Place::Defined(_) = self.class_member(db, name.into()).place {
place
} else if let Some(place @ Place::Defined(_, _, _, _)) =
} else if let Some(place @ Place::Defined(_)) =
self.find_name_in_mro(db, name).map(|inner| inner.place)
{
place
@@ -2604,7 +2605,12 @@ impl<'db> Type<'db> {
let descr_get = self.class_member(db, "__get__".into()).place;
if let Place::Defined(descr_get, _, descr_get_boundness, _) = descr_get {
if let Place::Defined(DefinedPlace {
ty: descr_get,
definedness: descr_get_boundness,
..
}) = descr_get
{
let return_ty = descr_get
.try_call(db, &CallArguments::positional([self, instance, owner]))
.map(|bindings| {
@@ -2649,23 +2655,34 @@ impl<'db> Type<'db> {
//
// The same is true for `Never`.
PlaceAndQualifiers {
place: Place::Defined(Type::Dynamic(_) | Type::Never, _, _, _),
place:
Place::Defined(DefinedPlace {
ty: Type::Dynamic(_) | Type::Never,
..
}),
qualifiers: _,
} => (attribute, AttributeKind::DataDescriptor),
PlaceAndQualifiers {
place: Place::Defined(Type::Union(union), origin, boundness, widening),
place:
Place::Defined(DefinedPlace {
ty: Type::Union(union),
origin,
definedness: boundness,
widening,
}),
qualifiers,
} => (
union
.map_with_boundness(db, |elem| {
Place::Defined(
elem.try_call_dunder_get(db, instance, owner)
Place::Defined(DefinedPlace {
ty: elem
.try_call_dunder_get(db, instance, owner)
.map_or(*elem, |(ty, _)| ty),
origin,
boundness,
definedness: boundness,
widening,
)
})
})
.with_qualifiers(qualifiers),
// TODO: avoid the duplication here:
@@ -2680,18 +2697,25 @@ impl<'db> Type<'db> {
),
PlaceAndQualifiers {
place: Place::Defined(Type::Intersection(intersection), origin, boundness, widening),
place:
Place::Defined(DefinedPlace {
ty: Type::Intersection(intersection),
origin,
definedness: boundness,
widening,
}),
qualifiers,
} => (
intersection
.map_with_boundness(db, |elem| {
Place::Defined(
elem.try_call_dunder_get(db, instance, owner)
Place::Defined(DefinedPlace {
ty: elem
.try_call_dunder_get(db, instance, owner)
.map_or(*elem, |(ty, _)| ty),
origin,
boundness,
definedness: boundness,
widening,
)
})
})
.with_qualifiers(qualifiers),
// TODO: Discover data descriptors in intersections.
@@ -2699,14 +2723,26 @@ impl<'db> Type<'db> {
),
PlaceAndQualifiers {
place: Place::Defined(attribute_ty, origin, boundness, widening),
place:
Place::Defined(DefinedPlace {
ty: attribute_ty,
origin,
definedness: boundness,
widening,
}),
qualifiers: _,
} => {
if let Some((return_ty, attribute_kind)) =
attribute_ty.try_call_dunder_get(db, instance, owner)
{
(
Place::Defined(return_ty, origin, boundness, widening).into(),
Place::Defined(DefinedPlace {
ty: return_ty,
origin,
definedness: boundness,
widening,
})
.into(),
attribute_kind,
)
} else {
@@ -2799,14 +2835,17 @@ impl<'db> Type<'db> {
match (meta_attr, meta_attr_kind, fallback) {
// The fallback type is unbound, so we can just return `meta_attr` unconditionally,
// no matter if it's data descriptor, a non-data descriptor, or a normal attribute.
(meta_attr @ Place::Defined(_, _, _, _), _, Place::Undefined) => {
(meta_attr @ Place::Defined(_), _, Place::Undefined) => {
meta_attr.with_qualifiers(meta_attr_qualifiers)
}
// `meta_attr` is the return type of a data descriptor and definitely bound, so we
// return it.
(
meta_attr @ Place::Defined(_, _, Definedness::AlwaysDefined, _),
meta_attr @ Place::Defined(DefinedPlace {
definedness: Definedness::AlwaysDefined,
..
}),
AttributeKind::DataDescriptor,
_,
) => meta_attr.with_qualifiers(meta_attr_qualifiers),
@@ -2815,15 +2854,25 @@ impl<'db> Type<'db> {
// meta-type is possibly-unbound. This means that we "fall through" to the next
// stage of the descriptor protocol and union with the fallback type.
(
Place::Defined(meta_attr_ty, meta_origin, Definedness::PossiblyUndefined, _),
Place::Defined(DefinedPlace {
ty: meta_attr_ty,
origin: meta_origin,
definedness: Definedness::PossiblyUndefined,
..
}),
AttributeKind::DataDescriptor,
Place::Defined(fallback_ty, fallback_origin, fallback_boundness, fallback_widening),
) => Place::Defined(
UnionType::from_elements(db, [meta_attr_ty, fallback_ty]),
meta_origin.merge(fallback_origin),
fallback_boundness,
fallback_widening,
)
Place::Defined(DefinedPlace {
ty: fallback_ty,
origin: fallback_origin,
definedness: fallback_boundness,
widening: fallback_widening,
}),
) => Place::Defined(DefinedPlace {
ty: UnionType::from_elements(db, [meta_attr_ty, fallback_ty]),
origin: meta_origin.merge(fallback_origin),
definedness: fallback_boundness,
widening: fallback_widening,
})
.with_qualifiers(meta_attr_qualifiers.union(fallback_qualifiers)),
// `meta_attr` is *not* a data descriptor. This means that the `fallback` type has
@@ -2835,9 +2884,12 @@ impl<'db> Type<'db> {
// would require us to statically infer if an instance attribute is always set, which
// is something we currently don't attempt to do.
(
Place::Defined(_, _, _, _),
Place::Defined(_),
AttributeKind::NormalOrNonDataDescriptor,
fallback @ Place::Defined(_, _, Definedness::AlwaysDefined, _),
fallback @ Place::Defined(DefinedPlace {
definedness: Definedness::AlwaysDefined,
..
}),
) if policy == InstanceFallbackShadowsNonDataDescriptor::Yes => {
fallback.with_qualifiers(fallback_qualifiers)
}
@@ -2846,15 +2898,25 @@ impl<'db> Type<'db> {
// unbound or the policy argument is `No`. In both cases, the `fallback` type does
// not completely shadow the non-data descriptor, so we build a union of the two.
(
Place::Defined(meta_attr_ty, meta_origin, meta_attr_boundness, _),
Place::Defined(DefinedPlace {
ty: meta_attr_ty,
origin: meta_origin,
definedness: meta_attr_boundness,
..
}),
AttributeKind::NormalOrNonDataDescriptor,
Place::Defined(fallback_ty, fallback_origin, fallback_boundness, fallback_widening),
) => Place::Defined(
UnionType::from_elements(db, [meta_attr_ty, fallback_ty]),
meta_origin.merge(fallback_origin),
meta_attr_boundness.max(fallback_boundness),
fallback_widening,
)
Place::Defined(DefinedPlace {
ty: fallback_ty,
origin: fallback_origin,
definedness: fallback_boundness,
widening: fallback_widening,
}),
) => Place::Defined(DefinedPlace {
ty: UnionType::from_elements(db, [meta_attr_ty, fallback_ty]),
origin: meta_origin.merge(fallback_origin),
definedness: meta_attr_boundness.max(fallback_boundness),
widening: fallback_widening,
})
.with_qualifiers(meta_attr_qualifiers.union(fallback_qualifiers)),
// If the attribute is not found on the meta-type, we simply return the fallback.
@@ -3218,11 +3280,19 @@ impl<'db> Type<'db> {
match result {
member @ PlaceAndQualifiers {
place: Place::Defined(_, _, Definedness::AlwaysDefined, _),
place:
Place::Defined(DefinedPlace {
definedness: Definedness::AlwaysDefined,
..
}),
qualifiers: _,
} => member,
member @ PlaceAndQualifiers {
place: Place::Defined(_, _, Definedness::PossiblyUndefined, _),
place:
Place::Defined(DefinedPlace {
definedness: Definedness::PossiblyUndefined,
..
}),
qualifiers: _,
} => member
.or_fall_back_to(db, custom_getattribute_result)
@@ -4361,7 +4431,11 @@ impl<'db> Type<'db> {
)
.place
{
Place::Defined(dunder_callable, _, boundness, _) => {
Place::Defined(DefinedPlace {
ty: dunder_callable,
definedness: boundness,
..
}) => {
let mut bindings = dunder_callable.bindings(db);
bindings.replace_callable_type(dunder_callable, self);
if boundness == Definedness::PossiblyUndefined {
@@ -4514,7 +4588,11 @@ impl<'db> Type<'db> {
)
.place
{
Place::Defined(dunder_callable, _, boundness, _) => {
Place::Defined(DefinedPlace {
ty: dunder_callable,
definedness: boundness,
..
}) => {
let bindings = dunder_callable
.bindings(db)
.match_parameters(db, argument_types)
@@ -5091,18 +5169,26 @@ impl<'db> Type<'db> {
new_method.as_ref().map(|method| &method.place),
&init_method.place,
) {
(Some(Place::Defined(new_method, ..)), Place::Undefined) => Some(
(Some(Place::Defined(DefinedPlace { ty: new_method, .. })), Place::Undefined) => Some(
new_method
.bindings(db)
.map(|binding| binding.with_bound_type(self_type)),
),
(Some(Place::Undefined) | None, Place::Defined(init_method, ..)) => {
Some(init_method.bindings(db))
}
(
Some(Place::Undefined) | None,
Place::Defined(DefinedPlace {
ty: init_method, ..
}),
) => Some(init_method.bindings(db)),
(Some(Place::Defined(new_method, ..)), Place::Defined(init_method, ..)) => {
let callable = UnionType::from_elements(db, [new_method, init_method]);
(
Some(Place::Defined(DefinedPlace { ty: new_method, .. })),
Place::Defined(DefinedPlace {
ty: init_method, ..
}),
) => {
let callable = UnionType::from_elements(db, [*new_method, *init_method]);
let new_method_bindings = new_method
.bindings(db)
@@ -5121,7 +5207,11 @@ impl<'db> Type<'db> {
let new_call_outcome = new_method.and_then(|new_method| {
match new_method.place.try_call_dunder_get(db, self_type) {
Place::Defined(new_method, _, boundness, _) => {
Place::Defined(DefinedPlace {
ty: new_method,
definedness: boundness,
..
}) => {
let argument_types = argument_types.with_self(Some(self_type));
let result = new_method
.bindings(db)
@@ -5149,7 +5239,11 @@ impl<'db> Type<'db> {
.place
{
Place::Undefined => Err(CallDunderError::MethodNotAvailable),
Place::Defined(dunder_callable, _, boundness, _) => {
Place::Defined(DefinedPlace {
ty: dunder_callable,
definedness: boundness,
..
}) => {
let bindings = dunder_callable
.bindings(db)
.with_constructor_instance_type(init_ty);
@@ -8624,7 +8718,12 @@ impl<'db> TypeVarConstraints<'db> {
Place::Undefined => {
possibly_unbound = true;
}
Place::Defined(ty_member, member_origin, member_boundness, _) => {
Place::Defined(DefinedPlace {
ty: ty_member,
origin: member_origin,
definedness: member_boundness,
..
}) => {
origin = origin.merge(member_origin);
if member_boundness == Definedness::PossiblyUndefined {
possibly_unbound = true;
@@ -8639,16 +8738,16 @@ impl<'db> TypeVarConstraints<'db> {
place: if all_unbound {
Place::Undefined
} else {
Place::Defined(
builder.build(),
Place::Defined(DefinedPlace {
ty: builder.build(),
origin,
if possibly_unbound {
definedness: if possibly_unbound {
Definedness::PossiblyUndefined
} else {
Definedness::AlwaysDefined
},
Widening::None,
)
widening: Widening::None,
})
},
qualifiers,
}
@@ -11227,15 +11326,17 @@ impl<'db> ModuleLiteralType<'db> {
// if it exists. First, we need to look up the `__getattr__` function in the module's scope.
if let Some(file) = self.module(db).file(db) {
let getattr_symbol = imported_symbol(db, file, "__getattr__", None);
if let Place::Defined(getattr_type, origin, boundness, widening) = getattr_symbol.place
{
if let Place::Defined(place) = getattr_symbol.place {
// If we found a __getattr__ function, try to call it with the name argument
if let Ok(outcome) = getattr_type.try_call(
if let Ok(outcome) = place.ty.try_call(
db,
&CallArguments::positional([Type::string_literal(db, name)]),
) {
return PlaceAndQualifiers {
place: Place::Defined(outcome.return_type(db), origin, boundness, widening),
place: Place::Defined(DefinedPlace {
ty: outcome.return_type(db),
..place
}),
qualifiers: TypeQualifiers::FROM_MODULE_GETATTR,
};
}
@@ -11772,7 +11873,12 @@ impl<'db> UnionType<'db> {
Place::Undefined => {
possibly_unbound = true;
}
Place::Defined(ty_member, member_origin, member_boundness, _) => {
Place::Defined(DefinedPlace {
ty: ty_member,
origin: member_origin,
definedness: member_boundness,
..
}) => {
origin = origin.merge(member_origin);
if member_boundness == Definedness::PossiblyUndefined {
possibly_unbound = true;
@@ -11787,18 +11893,18 @@ impl<'db> UnionType<'db> {
if all_unbound {
Place::Undefined
} else {
Place::Defined(
builder
Place::Defined(DefinedPlace {
ty: builder
.recursively_defined(self.recursively_defined(db))
.build(),
origin,
if possibly_unbound {
definedness: if possibly_unbound {
Definedness::PossiblyUndefined
} else {
Definedness::AlwaysDefined
},
Widening::None,
)
widening: Widening::None,
})
}
}
@@ -11823,7 +11929,12 @@ impl<'db> UnionType<'db> {
Place::Undefined => {
possibly_unbound = true;
}
Place::Defined(ty_member, member_origin, member_boundness, _) => {
Place::Defined(DefinedPlace {
ty: ty_member,
origin: member_origin,
definedness: member_boundness,
..
}) => {
origin = origin.merge(member_origin);
if member_boundness == Definedness::PossiblyUndefined {
possibly_unbound = true;
@@ -11838,18 +11949,18 @@ impl<'db> UnionType<'db> {
place: if all_unbound {
Place::Undefined
} else {
Place::Defined(
builder
Place::Defined(DefinedPlace {
ty: builder
.recursively_defined(self.recursively_defined(db))
.build(),
origin,
if possibly_unbound {
definedness: if possibly_unbound {
Definedness::PossiblyUndefined
} else {
Definedness::AlwaysDefined
},
Widening::None,
)
widening: Widening::None,
})
},
qualifiers,
}
@@ -12407,7 +12518,12 @@ impl<'db> IntersectionType<'db> {
let ty_member = transform_fn(&ty);
match ty_member {
Place::Undefined => {}
Place::Defined(ty_member, member_origin, member_boundness, _) => {
Place::Defined(DefinedPlace {
ty: ty_member,
origin: member_origin,
definedness: member_boundness,
..
}) => {
origin = origin.merge(member_origin);
all_unbound = false;
if member_boundness == Definedness::AlwaysDefined {
@@ -12422,16 +12538,16 @@ impl<'db> IntersectionType<'db> {
if all_unbound {
Place::Undefined
} else {
Place::Defined(
builder.build(),
Place::Defined(DefinedPlace {
ty: builder.build(),
origin,
if any_definitely_bound {
definedness: if any_definitely_bound {
Definedness::AlwaysDefined
} else {
Definedness::PossiblyUndefined
},
Widening::None,
)
widening: Widening::None,
})
}
}
@@ -12454,7 +12570,12 @@ impl<'db> IntersectionType<'db> {
qualifiers |= new_qualifiers;
match member {
Place::Undefined => {}
Place::Defined(ty_member, member_origin, member_boundness, _) => {
Place::Defined(DefinedPlace {
ty: ty_member,
origin: member_origin,
definedness: member_boundness,
..
}) => {
origin = origin.merge(member_origin);
all_unbound = false;
if member_boundness == Definedness::AlwaysDefined {
@@ -12470,16 +12591,16 @@ impl<'db> IntersectionType<'db> {
place: if all_unbound {
Place::Undefined
} else {
Place::Defined(
builder.build(),
Place::Defined(DefinedPlace {
ty: builder.build(),
origin,
if any_definitely_bound {
definedness: if any_definitely_bound {
Definedness::AlwaysDefined
} else {
Definedness::PossiblyUndefined
},
Widening::None,
)
widening: Widening::None,
})
},
qualifiers,
}

View File

@@ -23,7 +23,7 @@ use smallvec::{SmallVec, smallvec, smallvec_inline};
use super::{Argument, CallArguments, CallError, CallErrorKind, InferContext, Signature, Type};
use crate::db::Db;
use crate::dunder_all::dunder_all_names;
use crate::place::{Definedness, Place, known_module_symbol};
use crate::place::{DefinedPlace, Definedness, Place, known_module_symbol};
use crate::types::call::arguments::{Expansion, is_expandable_type};
use crate::types::constraints::ConstraintSet;
use crate::types::diagnostic::{
@@ -1022,7 +1022,11 @@ impl<'db> Bindings<'db> {
// TODO: we could emit a diagnostic here (if default is not set)
overload.set_return_type(
match instance_ty.static_member(db, attr_name.value(db)) {
Place::Defined(ty, _, Definedness::AlwaysDefined, _) => {
Place::Defined(DefinedPlace {
ty,
definedness: Definedness::AlwaysDefined,
..
}) => {
if ty.is_dynamic() {
// Here, we attempt to model the fact that an attribute lookup on
// a dynamic type could fail
@@ -1032,9 +1036,11 @@ impl<'db> Bindings<'db> {
ty
}
}
Place::Defined(ty, _, Definedness::PossiblyUndefined, _) => {
union_with_default(ty)
}
Place::Defined(DefinedPlace {
ty,
definedness: Definedness::PossiblyUndefined,
..
}) => union_with_default(ty),
Place::Undefined => default,
},
);
@@ -2829,7 +2835,11 @@ impl<'a, 'db> ArgumentMatcher<'a, 'db> {
)
.place
{
Place::Defined(getitem_method, _, Definedness::AlwaysDefined, _) => getitem_method
Place::Defined(DefinedPlace {
ty: getitem_method,
definedness: Definedness::AlwaysDefined,
..
}) => getitem_method
.try_call(db, &CallArguments::positional([Type::unknown()]))
.ok()
.map_or_else(Type::unknown, |bindings| bindings.return_type(db)),
@@ -3436,7 +3446,11 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
)
.place
{
Place::Defined(keys_method, _, Definedness::AlwaysDefined, _) => keys_method
Place::Defined(DefinedPlace {
ty: keys_method,
definedness: Definedness::AlwaysDefined,
..
}) => keys_method
.try_call(self.db, &CallArguments::none())
.ok()
.and_then(|bindings| {
@@ -3482,14 +3496,14 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
)
.place
{
Place::Defined(keys_method, _, Definedness::AlwaysDefined, _) => {
keys_method
.try_call(self.db, &CallArguments::positional([Type::unknown()]))
.ok()
.map_or_else(Type::unknown, |bindings| {
bindings.return_type(self.db)
})
}
Place::Defined(DefinedPlace {
ty: getitem_method,
definedness: Definedness::AlwaysDefined,
..
}) => getitem_method
.try_call(self.db, &CallArguments::positional([Type::unknown()]))
.ok()
.map_or_else(Type::unknown, |bindings| bindings.return_type(self.db)),
_ => Type::unknown(),
},
)

View File

@@ -8,7 +8,7 @@ use super::{
SubclassOfType, Truthiness, Type, TypeQualifiers, class_base::ClassBase,
function::FunctionType,
};
use crate::place::TypeOrigin;
use crate::place::{DefinedPlace, TypeOrigin};
use crate::semantic_index::definition::{Definition, DefinitionState};
use crate::semantic_index::scope::{NodeWithScopeKind, Scope, ScopeKind};
use crate::semantic_index::symbol::Symbol;
@@ -1186,8 +1186,10 @@ impl<'db> ClassType<'db> {
)
.place;
if let Place::Defined(Type::BoundMethod(metaclass_dunder_call_function), _, _, _) =
metaclass_dunder_call_function_symbol
if let Place::Defined(DefinedPlace {
ty: Type::BoundMethod(metaclass_dunder_call_function),
..
}) = metaclass_dunder_call_function_symbol
{
// TODO: this intentionally diverges from step 1 in
// https://typing.python.org/en/latest/spec/constructors.html#converting-a-constructor-to-callable
@@ -1249,7 +1251,7 @@ impl<'db> ClassType<'db> {
// If the class defines an `__init__` method, then we synthesize a callable type with the
// same parameters as the `__init__` method after it is bound, and with the return type of
// the concrete type of `Self`.
let synthesized_dunder_init_callable = if let Place::Defined(ty, _, _, _) =
let synthesized_dunder_init_callable = if let Place::Defined(DefinedPlace { ty, .. }) =
dunder_init_function_symbol
{
let signature = match ty {
@@ -1324,8 +1326,10 @@ impl<'db> ClassType<'db> {
)
.place;
if let Place::Defined(Type::FunctionLiteral(mut new_function), _, _, _) =
new_function_symbol
if let Place::Defined(DefinedPlace {
ty: Type::FunctionLiteral(mut new_function),
..
}) = new_function_symbol
{
if let Some(class_generic_context) = class_generic_context {
new_function =
@@ -2264,7 +2268,7 @@ impl<'db> ClassLiteral<'db> {
(
PlaceAndQualifiers {
place: Place::Defined(ty, _, _, _),
place: Place::Defined(DefinedPlace { ty, .. }),
qualifiers,
},
Some(dynamic_type),
@@ -2513,8 +2517,11 @@ impl<'db> ClassLiteral<'db> {
}
let dunder_set = field_ty.class_member(db, "__set__".into());
if let Place::Defined(dunder_set, _, Definedness::AlwaysDefined, _) =
dunder_set.place
if let Place::Defined(DefinedPlace {
ty: dunder_set,
definedness: Definedness::AlwaysDefined,
..
}) = dunder_set.place
{
// The descriptor handling below is guarded by this not-dynamic check, because
// dynamic types like `Any` are valid (data) descriptors: since they have all
@@ -3426,7 +3433,13 @@ impl<'db> ClassLiteral<'db> {
}
ClassBase::Class(class) => {
if let member @ PlaceAndQualifiers {
place: Place::Defined(ty, origin, boundness, _),
place:
Place::Defined(DefinedPlace {
ty,
origin,
definedness: boundness,
..
}),
qualifiers,
} = class.own_instance_member(db, name).inner
{
@@ -3478,12 +3491,12 @@ impl<'db> ClassLiteral<'db> {
Definedness::PossiblyUndefined
};
Place::Defined(
union.build(),
TypeOrigin::Inferred,
boundness,
Widening::None,
)
Place::Defined(DefinedPlace {
ty: union.build(),
origin: TypeOrigin::Inferred,
definedness: boundness,
widening: Widening::None,
})
.with_qualifiers(union_qualifiers)
}
}
@@ -3848,7 +3861,12 @@ impl<'db> ClassLiteral<'db> {
match declared_and_qualifiers {
PlaceAndQualifiers {
place: mut declared @ Place::Defined(declared_ty, _, declaredness, _),
place:
mut declared @ Place::Defined(DefinedPlace {
ty: declared_ty,
definedness: declaredness,
..
}),
qualifiers,
} => {
// For the purpose of finding instance attributes, ignore `ClassVar`
@@ -3889,12 +3907,15 @@ impl<'db> ClassLiteral<'db> {
}
} else {
Member {
inner: Place::Defined(
UnionType::from_elements(db, [declared_ty, implicit_ty]),
TypeOrigin::Declared,
declaredness,
Widening::None,
)
inner: Place::Defined(DefinedPlace {
ty: UnionType::from_elements(
db,
[declared_ty, implicit_ty],
),
origin: TypeOrigin::Declared,
definedness: declaredness,
widening: Widening::None,
})
.with_qualifiers(qualifiers),
}
}
@@ -3930,12 +3951,15 @@ impl<'db> ClassLiteral<'db> {
.ignore_possibly_undefined()
{
Member {
inner: Place::Defined(
UnionType::from_elements(db, [declared_ty, implicit_ty]),
TypeOrigin::Declared,
declaredness,
Widening::None,
)
inner: Place::Defined(DefinedPlace {
ty: UnionType::from_elements(
db,
[declared_ty, implicit_ty],
),
origin: TypeOrigin::Declared,
definedness: declaredness,
widening: Widening::None,
})
.with_qualifiers(qualifiers),
}
} else {
@@ -5256,16 +5280,17 @@ impl KnownClass {
) -> Result<ClassLiteral<'_>, KnownClassLookupError<'_>> {
let symbol = known_module_symbol(db, self.canonical_module(db), self.name(db)).place;
match symbol {
Place::Defined(Type::ClassLiteral(class_literal), _, Definedness::AlwaysDefined, _) => {
Ok(class_literal)
}
Place::Defined(
Type::ClassLiteral(class_literal),
_,
Definedness::PossiblyUndefined,
_,
) => Err(KnownClassLookupError::ClassPossiblyUnbound { class_literal }),
Place::Defined(found_type, _, _, _) => {
Place::Defined(DefinedPlace {
ty: Type::ClassLiteral(class_literal),
definedness: Definedness::AlwaysDefined,
..
}) => Ok(class_literal),
Place::Defined(DefinedPlace {
ty: Type::ClassLiteral(class_literal),
definedness: Definedness::PossiblyUndefined,
..
}) => Err(KnownClassLookupError::ClassPossiblyUnbound { class_literal }),
Place::Defined(DefinedPlace { ty: found_type, .. }) => {
Err(KnownClassLookupError::SymbolNotAClass { found_type })
}
Place::Undefined => Err(KnownClassLookupError::ClassNotFound),
@@ -6144,7 +6169,11 @@ enum SlotsKind {
impl SlotsKind {
fn from(db: &dyn Db, base: ClassLiteral) -> Self {
let Place::Defined(slots_ty, _, bound, _) = base
let Place::Defined(DefinedPlace {
ty: slots_ty,
definedness: bound,
..
}) = base
.own_class_member(db, base.inherited_generic_context(db), None, "__slots__")
.inner
.place

View File

@@ -8,7 +8,7 @@ use super::{
use crate::diagnostic::did_you_mean;
use crate::diagnostic::format_enumeration;
use crate::lint::{Level, LintRegistryBuilder, LintStatus};
use crate::place::Place;
use crate::place::{DefinedPlace, Place};
use crate::semantic_index::definition::{Definition, DefinitionKind};
use crate::semantic_index::place::{PlaceTable, ScopedPlaceId};
use crate::semantic_index::{global_scope, place_table, use_def_map};
@@ -4058,10 +4058,14 @@ pub(super) fn report_invalid_method_override<'db>(
.place
};
if let Place::Defined(Type::FunctionLiteral(subclass_function), _, _, _) =
class_member(subclass)
&& let Place::Defined(Type::FunctionLiteral(superclass_function), _, _, _) =
class_member(superclass)
if let Place::Defined(DefinedPlace {
ty: Type::FunctionLiteral(subclass_function),
..
}) = class_member(subclass)
&& let Place::Defined(DefinedPlace {
ty: Type::FunctionLiteral(superclass_function),
..
}) = class_member(superclass)
&& let Ok(superclass_function_kind) =
MethodDecorator::try_from_fn_type(db, superclass_function)
&& let Ok(subclass_function_kind) = MethodDecorator::try_from_fn_type(db, subclass_function)

View File

@@ -14,7 +14,7 @@ use ruff_text_size::{TextLen, TextRange, TextSize};
use rustc_hash::{FxHashMap, FxHashSet};
use crate::Db;
use crate::place::Place;
use crate::place::{DefinedPlace, Place};
use crate::semantic_index::definition::Definition;
use crate::types::class::{ClassLiteral, ClassType, GenericAlias};
use crate::types::function::{FunctionType, OverloadLiteral};
@@ -887,7 +887,7 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
f.with_type(KnownClass::MethodWrapperType.to_class_literal(self.db))
.write_str("method-wrapper")?;
f.write_str(" '")?;
if let Place::Defined(member_ty, _, _, _) =
if let Place::Defined(DefinedPlace { ty: member_ty, .. }) =
class_ty.member(self.db, member_name).place
{
f.with_type(member_ty).write_str(member_name)?;

View File

@@ -3,7 +3,9 @@ use rustc_hash::FxHashMap;
use crate::{
Db, FxIndexMap,
place::{Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
place::{
DefinedPlace, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations,
},
semantic_index::{place_table, use_def_map},
types::{
ClassBase, ClassLiteral, DynamicType, EnumLiteralType, KnownClass, MemberLookupPolicy,
@@ -76,9 +78,10 @@ pub(crate) fn enum_metadata<'db>(
let ignore_place = place_from_bindings(db, ignore_bindings).place;
match ignore_place {
Place::Defined(Type::StringLiteral(ignored_names), _, _, _) => {
Some(ignored_names.value(db).split_ascii_whitespace().collect())
}
Place::Defined(DefinedPlace {
ty: Type::StringLiteral(ignored_names),
..
}) => Some(ignored_names.value(db).split_ascii_whitespace().collect()),
// TODO: support the list-variant of `_ignore_`.
_ => None,
}
@@ -113,7 +116,7 @@ pub(crate) fn enum_metadata<'db>(
Place::Undefined => {
return None;
}
Place::Defined(ty, _, _, _) => {
Place::Defined(DefinedPlace { ty, .. }) => {
let special_case = match ty {
Type::Callable(_) | Type::FunctionLiteral(_) => {
// Some types are specifically disallowed for enum members.
@@ -193,9 +196,13 @@ pub(crate) fn enum_metadata<'db>(
.place;
match dunder_get {
Place::Undefined | Place::Defined(Type::Dynamic(_), _, _, _) => ty,
Place::Undefined
| Place::Defined(DefinedPlace {
ty: Type::Dynamic(_),
..
}) => ty,
Place::Defined(_, _, _, _) => {
Place::Defined(_) => {
// Descriptors are not considered members.
return None;
}
@@ -230,7 +237,11 @@ pub(crate) fn enum_metadata<'db>(
match declared {
PlaceAndQualifiers {
place: Place::Defined(Type::Dynamic(DynamicType::Unknown), _, _, _),
place:
Place::Defined(DefinedPlace {
ty: Type::Dynamic(DynamicType::Unknown),
..
}),
qualifiers,
} if qualifiers.contains(TypeQualifiers::FINAL) => {}
PlaceAndQualifiers {
@@ -240,7 +251,11 @@ pub(crate) fn enum_metadata<'db>(
// Undeclared attributes are considered members
}
PlaceAndQualifiers {
place: Place::Defined(Type::NominalInstance(instance), _, _, _),
place:
Place::Defined(DefinedPlace {
ty: Type::NominalInstance(instance),
..
}),
..
} if instance.has_known_class(db, KnownClass::Member) => {
// If the attribute is specifically declared with `enum.member`, it is considered a member

View File

@@ -59,7 +59,7 @@ use ruff_python_ast::{self as ast, ParameterWithDefault};
use ruff_text_size::Ranged;
use ty_module_resolver::{KnownModule, ModuleName, file_to_module, resolve_module};
use crate::place::{Definedness, Place, place_from_bindings};
use crate::place::{DefinedPlace, Definedness, Place, place_from_bindings};
use crate::semantic_index::ast_ids::HasScopedUseId;
use crate::semantic_index::definition::Definition;
use crate::semantic_index::scope::ScopeId;
@@ -376,8 +376,11 @@ impl<'db> OverloadLiteral<'db> {
.name
.scoped_use_id(db, scope);
let Place::Defined(Type::FunctionLiteral(previous_type), _, Definedness::AlwaysDefined, _) =
place_from_bindings(db, use_def.bindings_at_use(use_id)).place
let Place::Defined(DefinedPlace {
ty: Type::FunctionLiteral(previous_type),
definedness: Definedness::AlwaysDefined,
..
}) = place_from_bindings(db, use_def.bindings_at_use(use_id)).place
else {
return None;
};

View File

@@ -27,10 +27,11 @@ use super::{
use crate::diagnostic::format_enumeration;
use crate::node_key::NodeKey;
use crate::place::{
ConsideredDefinitions, Definedness, LookupError, Place, PlaceAndQualifiers, TypeOrigin,
builtins_module_scope, builtins_symbol, class_body_implicit_symbol, explicit_global_symbol,
global_symbol, module_type_implicit_global_declaration, module_type_implicit_global_symbol,
place, place_from_bindings, place_from_declarations, typing_extensions_symbol,
ConsideredDefinitions, DefinedPlace, Definedness, LookupError, Place, PlaceAndQualifiers,
TypeOrigin, builtins_module_scope, builtins_symbol, class_body_implicit_symbol,
explicit_global_symbol, global_symbol, module_type_implicit_global_declaration,
module_type_implicit_global_symbol, place, place_from_bindings, place_from_declarations,
typing_extensions_symbol,
};
use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey;
use crate::semantic_index::ast_ids::{HasScopedUseId, ScopedUseId};
@@ -1187,12 +1188,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let mut public_functions = FxIndexSet::default();
for place in overloaded_function_places {
if let Place::Defined(
Type::FunctionLiteral(function),
_,
Definedness::AlwaysDefined,
_,
) = place_from_bindings(
if let Place::Defined(DefinedPlace {
ty: Type::FunctionLiteral(function),
definedness: Definedness::AlwaysDefined,
..
}) = place_from_bindings(
self.db(),
use_def.end_of_scope_symbol_bindings(place.as_symbol().unwrap()),
)
@@ -1725,8 +1725,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
if let AnyNodeRef::ExprAttribute(ast::ExprAttribute { value, attr, .. }) = node {
let value_type =
self.infer_maybe_standalone_expression(value, TypeContext::default());
if let Place::Defined(ty, _, Definedness::AlwaysDefined, _) =
value_type.member(db, attr).place
if let Place::Defined(DefinedPlace {
ty,
definedness: Definedness::AlwaysDefined,
..
}) = value_type.member(db, attr).place
{
// TODO: also consider qualifiers on the attribute
Some(ty)
@@ -4831,7 +4834,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK,
) {
PlaceAndQualifiers {
place: Place::Defined(attr_ty, _, _, _),
place: Place::Defined(DefinedPlace { ty: attr_ty, .. }),
qualifiers: _,
} => attr_ty.is_callable_type(),
_ => false,
@@ -4883,50 +4886,61 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
false
}
PlaceAndQualifiers {
place: Place::Defined(meta_attr_ty, _, meta_attr_boundness, _),
place:
Place::Defined(DefinedPlace {
ty: meta_attr_ty,
definedness: meta_attr_boundness,
..
}),
qualifiers,
} => {
if invalid_assignment_to_final(self, qualifiers) {
return false;
}
let assignable_to_meta_attr =
if let Place::Defined(meta_dunder_set, _, _, _) =
meta_attr_ty.class_member(db, "__set__".into()).place
{
// TODO: We could use the annotated parameter type of `__set__` as
// type context here.
let dunder_set_result = meta_dunder_set.try_call(
db,
&CallArguments::positional([meta_attr_ty, object_ty, value_ty]),
);
let assignable_to_meta_attr = if let Place::Defined(DefinedPlace {
ty: meta_dunder_set,
..
}) =
meta_attr_ty.class_member(db, "__set__".into()).place
{
// TODO: We could use the annotated parameter type of `__set__` as
// type context here.
let dunder_set_result = meta_dunder_set.try_call(
db,
&CallArguments::positional([meta_attr_ty, object_ty, value_ty]),
);
if emit_diagnostics {
if let Err(dunder_set_failure) = dunder_set_result.as_ref() {
report_bad_dunder_set_call(
&self.context,
dunder_set_failure,
attribute,
object_ty,
target,
);
}
if emit_diagnostics {
if let Err(dunder_set_failure) = dunder_set_result.as_ref() {
report_bad_dunder_set_call(
&self.context,
dunder_set_failure,
attribute,
object_ty,
target,
);
}
}
dunder_set_result.is_ok()
} else {
let value_ty =
infer_value_ty(self, TypeContext::new(Some(meta_attr_ty)));
dunder_set_result.is_ok()
} else {
let value_ty =
infer_value_ty(self, TypeContext::new(Some(meta_attr_ty)));
ensure_assignable_to(self, value_ty, meta_attr_ty)
};
ensure_assignable_to(self, value_ty, meta_attr_ty)
};
let assignable_to_instance_attribute = if meta_attr_boundness
== Definedness::PossiblyUndefined
{
let (assignable, boundness) = if let PlaceAndQualifiers {
place:
Place::Defined(instance_attr_ty, _, instance_attr_boundness, _),
Place::Defined(DefinedPlace {
ty: instance_attr_ty,
definedness: instance_attr_boundness,
..
}),
qualifiers,
} =
object_ty.instance_member(db, attribute)
@@ -4967,7 +4981,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
..
} => {
if let PlaceAndQualifiers {
place: Place::Defined(instance_attr_ty, _, instance_attr_boundness, _),
place:
Place::Defined(DefinedPlace {
ty: instance_attr_ty,
definedness: instance_attr_boundness,
..
}),
qualifiers,
} = object_ty.instance_member(db, attribute)
{
@@ -5032,7 +5051,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
Type::ClassLiteral(..) | Type::GenericAlias(..) | Type::SubclassOf(..) => {
match object_ty.class_member(db, attribute.into()) {
PlaceAndQualifiers {
place: Place::Defined(meta_attr_ty, _, meta_attr_boundness, _),
place:
Place::Defined(DefinedPlace {
ty: meta_attr_ty,
definedness: meta_attr_boundness,
..
}),
qualifiers,
} => {
// We may have to perform multi-inference if the meta attribute is possibly unbound.
@@ -5043,55 +5067,59 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
return false;
}
let assignable_to_meta_attr =
if let Place::Defined(meta_dunder_set, _, _, _) =
meta_attr_ty.class_member(db, "__set__".into()).place
{
// TODO: We could use the annotated parameter type of `__set__` as
// type context here.
let dunder_set_result = meta_dunder_set.try_call(
db,
&CallArguments::positional([meta_attr_ty, object_ty, value_ty]),
);
let assignable_to_meta_attr = if let Place::Defined(DefinedPlace {
ty: meta_dunder_set,
..
}) =
meta_attr_ty.class_member(db, "__set__".into()).place
{
// TODO: We could use the annotated parameter type of `__set__` as
// type context here.
let dunder_set_result = meta_dunder_set.try_call(
db,
&CallArguments::positional([meta_attr_ty, object_ty, value_ty]),
);
if emit_diagnostics {
if let Err(dunder_set_failure) = dunder_set_result.as_ref() {
report_bad_dunder_set_call(
&self.context,
dunder_set_failure,
attribute,
object_ty,
target,
);
}
if emit_diagnostics {
if let Err(dunder_set_failure) = dunder_set_result.as_ref() {
report_bad_dunder_set_call(
&self.context,
dunder_set_failure,
attribute,
object_ty,
target,
);
}
}
dunder_set_result.is_ok()
} else {
let value_ty =
infer_value_ty(self, TypeContext::new(Some(meta_attr_ty)));
ensure_assignable_to(self, value_ty, meta_attr_ty)
};
dunder_set_result.is_ok()
} else {
let value_ty =
infer_value_ty(self, TypeContext::new(Some(meta_attr_ty)));
ensure_assignable_to(self, value_ty, meta_attr_ty)
};
let assignable_to_class_attr = if meta_attr_boundness
== Definedness::PossiblyUndefined
{
let (assignable, boundness) =
if let Place::Defined(class_attr_ty, _, class_attr_boundness, _) =
object_ty
.find_name_in_mro(db, attribute)
.expect("called on Type::ClassLiteral or Type::SubclassOf")
.place
{
let value_ty =
infer_value_ty(self, TypeContext::new(Some(class_attr_ty)));
(
ensure_assignable_to(self, value_ty, class_attr_ty),
class_attr_boundness,
)
} else {
(true, Definedness::PossiblyUndefined)
};
let (assignable, boundness) = if let Place::Defined(DefinedPlace {
ty: class_attr_ty,
definedness: class_attr_boundness,
..
}) = object_ty
.find_name_in_mro(db, attribute)
.expect("called on Type::ClassLiteral or Type::SubclassOf")
.place
{
let value_ty =
infer_value_ty(self, TypeContext::new(Some(class_attr_ty)));
(
ensure_assignable_to(self, value_ty, class_attr_ty),
class_attr_boundness,
)
} else {
(true, Definedness::PossiblyUndefined)
};
if boundness == Definedness::PossiblyUndefined {
report_possibly_missing_attribute(
@@ -5114,7 +5142,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
..
} => {
if let PlaceAndQualifiers {
place: Place::Defined(class_attr_ty, _, class_attr_boundness, _),
place:
Place::Defined(DefinedPlace {
ty: class_attr_ty,
definedness: class_attr_boundness,
..
}),
qualifiers,
} = object_ty
.find_name_in_mro(db, attribute)
@@ -5188,7 +5221,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
} else {
module.static_member(db, attribute)
};
if let Place::Defined(attr_ty, _, _, _) = sym.place {
if let Place::Defined(DefinedPlace { ty: attr_ty, .. }) = sym.place {
let value_ty = infer_value_ty(self, TypeContext::new(Some(attr_ty)));
let assignable = value_ty.is_assignable_to(db, attr_ty);
@@ -6791,7 +6824,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// First try loading the requested attribute from the module.
if !import_is_self_referential {
if let PlaceAndQualifiers {
place: Place::Defined(ty, _, boundness, _),
place:
Place::Defined(DefinedPlace {
ty,
definedness: boundness,
..
}),
qualifiers,
} = module_ty.member(self.db(), name)
{
@@ -9495,7 +9533,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}
}
let (parent_place, _use_id) = self.infer_local_place_load(parent_expr, expr_ref);
if let Place::Defined(_, _, _, _) = parent_place {
if let Place::Defined(_) = parent_place {
return Place::Undefined.into();
}
}
@@ -9638,7 +9676,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
});
// We could have `Place::Undefined` here, despite the checks above, for example if
// this scope contains a `del` statement but no binding or declaration.
if let Place::Defined(type_, _, boundness, _) = local_place_and_qualifiers.place
if let Place::Defined(DefinedPlace {
ty: type_,
definedness: boundness,
..
}) = local_place_and_qualifiers.place
{
nonlocal_union_builder.add_in_place(type_);
// `ConsideredDefinitions::AllReachable` never returns PossiblyUnbound
@@ -9893,7 +9935,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
ast::ExprRef::Attribute(attribute),
);
constraint_keys.extend(keys);
if let Place::Defined(ty, _, Definedness::AlwaysDefined, _) = resolved.place {
if let Place::Defined(DefinedPlace {
ty,
definedness: Definedness::AlwaysDefined,
..
}) = resolved.place
{
assigned_type = Some(ty);
}
}
@@ -11549,7 +11596,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let contains_dunder = right.class_member(db, "__contains__".into()).place;
let compare_result_opt = match contains_dunder {
Place::Defined(contains_dunder, _, Definedness::AlwaysDefined, _) => {
Place::Defined(DefinedPlace {
ty: contains_dunder,
definedness: Definedness::AlwaysDefined,
..
}) => {
// If `__contains__` is available, it is used directly for the membership test.
contains_dunder
.try_call(db, &CallArguments::positional([right, left]))
@@ -11786,7 +11837,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
ast::ExprRef::Subscript(subscript),
);
constraint_keys.extend(keys);
if let Place::Defined(ty, _, Definedness::AlwaysDefined, _) = place.place {
if let Place::Defined(DefinedPlace {
ty,
definedness: Definedness::AlwaysDefined,
..
}) = place.place
{
// Even if we can obtain the subscript type based on the assignments, we still perform default type inference
// (to store the expression type and to report errors).
let slice_ty = self.infer_expression(slice, TypeContext::default());
@@ -12818,7 +12874,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
match dunder_class_getitem_method {
Place::Undefined => {}
Place::Defined(ty, _, boundness, _) => {
Place::Defined(DefinedPlace {
ty,
definedness: boundness,
..
}) => {
if boundness == Definedness::PossiblyUndefined {
if let Some(builder) =
context.report_lint(&POSSIBLY_MISSING_IMPLICIT_CALL, value_node)

View File

@@ -13,7 +13,8 @@ use rustc_hash::FxHashSet;
use crate::{
Db, NameKind,
place::{
Place, PlaceWithDefinition, imported_symbol, place_from_bindings, place_from_declarations,
DefinedPlace, Place, PlaceWithDefinition, imported_symbol, place_from_bindings,
place_from_declarations,
},
semantic_index::{
attribute_scopes, definition::Definition, global_scope, place_table, scope::ScopeId,
@@ -325,7 +326,7 @@ impl<'db> AllMembers<'db> {
for (symbol_id, _) in use_def_map.all_end_of_scope_symbol_declarations() {
let symbol_name = place_table.symbol(symbol_id).name();
let Place::Defined(ty, _, _, _) =
let Place::Defined(DefinedPlace { ty, .. }) =
imported_symbol(db, file, symbol_name, None).place
else {
continue;
@@ -494,7 +495,11 @@ impl<'db> AllMembers<'db> {
Some(CodeGeneratorKind::TypedDict) => {}
Some(CodeGeneratorKind::DataclassLike(_)) => {
for attr in SYNTHETIC_DATACLASS_ATTRIBUTES {
if let Place::Defined(synthetic_member, _, _, _) = ty.member(db, attr).place {
if let Place::Defined(DefinedPlace {
ty: synthetic_member,
..
}) = ty.member(db, attr).place
{
self.members.insert(Member {
name: Name::from(*attr),
ty: synthetic_member,

View File

@@ -1,7 +1,7 @@
use crate::Db;
use crate::place::{
ConsideredDefinitions, Place, PlaceAndQualifiers, RequiresExplicitReExport, place_by_id,
place_from_bindings,
ConsideredDefinitions, DefinedPlace, Place, PlaceAndQualifiers, RequiresExplicitReExport,
place_by_id, place_from_bindings,
};
use crate::semantic_index::{place_table, scope::ScopeId, use_def_map};
use crate::types::Type;
@@ -68,7 +68,7 @@ pub(super) fn class_member<'db>(db: &'db dyn Db, scope: ScopeId<'db>, name: &str
}
if let PlaceAndQualifiers {
place: Place::Defined(ty, _, _, _),
place: Place::Defined(DefinedPlace { ty, .. }),
qualifiers,
} = place_and_quals
{
@@ -82,9 +82,8 @@ pub(super) fn class_member<'db>(db: &'db dyn Db, scope: ScopeId<'db>, name: &str
Member {
inner: match inferred {
Place::Undefined => Place::Undefined.with_qualifiers(qualifiers),
Place::Defined(_, origin, boundness, widening) => {
Place::Defined(ty, origin, boundness, widening)
.with_qualifiers(qualifiers)
Place::Defined(place) => {
Place::Defined(DefinedPlace { ty, ..place }).with_qualifiers(qualifiers)
}
},
}

View File

@@ -10,7 +10,7 @@ use rustc_hash::FxHashSet;
use crate::{
Db,
lint::LintId,
place::Place,
place::{DefinedPlace, Place},
semantic_index::{
definition::DefinitionKind, place::ScopedPlaceId, place_table, scope::ScopeId,
symbol::ScopedSymbolId, use_def_map,
@@ -110,8 +110,10 @@ fn check_class_declaration<'db>(
first_reachable_definition,
} = member;
let Place::Defined(type_on_subclass_instance, _, _, _) =
Type::instance(db, class).member(db, &member.name).place
let Place::Defined(DefinedPlace {
ty: type_on_subclass_instance,
..
}) = Type::instance(db, class).member(db, &member.name).place
else {
return;
};
@@ -190,7 +192,10 @@ fn check_class_declaration<'db>(
.unwrap_or_default();
}
let Place::Defined(superclass_type, _, _, _) = Type::instance(db, superclass)
let Place::Defined(DefinedPlace {
ty: superclass_type,
..
}) = Type::instance(db, superclass)
.member(db, &member.name)
.place
else {

View File

@@ -10,7 +10,10 @@ use crate::types::relation::{HasRelationToVisitor, IsDisjointVisitor, TypeRelati
use crate::types::{CallableTypeKind, TypeContext};
use crate::{
Db, FxOrderSet,
place::{Definedness, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
place::{
DefinedPlace, Definedness, Place, PlaceAndQualifiers, place_from_bindings,
place_from_declarations,
},
semantic_index::{definition::Definition, place::ScopedPlaceId, place_table, use_def_map},
types::{
ApplyTypeMappingVisitor, BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral,
@@ -738,7 +741,11 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
let attribute_type = if self.name == "__call__" {
other
} else {
let Place::Defined(attribute_type, _, Definedness::AlwaysDefined, _) = other
let Place::Defined(DefinedPlace {
ty: attribute_type,
definedness: Definedness::AlwaysDefined,
..
}) = other
.invoke_descriptor_protocol(
db,
self.name,
@@ -783,11 +790,17 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
// TODO: consider the types of the attribute on `other` for property members
ProtocolMemberKind::Property(_) => ConstraintSet::from(matches!(
other.member(db, self.name).place,
Place::Defined(_, _, Definedness::AlwaysDefined, _)
Place::Defined(DefinedPlace {
definedness: Definedness::AlwaysDefined,
..
})
)),
ProtocolMemberKind::Other(member_type) => {
let Place::Defined(attribute_type, _, Definedness::AlwaysDefined, _) =
other.member(db, self.name).place
let Place::Defined(DefinedPlace {
ty: attribute_type,
definedness: Definedness::AlwaysDefined,
..
}) = other.member(db, self.name).place
else {
return ConstraintSet::from(false);
};

View File

@@ -1,6 +1,6 @@
use ruff_python_ast::name::Name;
use crate::place::Place;
use crate::place::{DefinedPlace, Place};
use crate::types::constraints::{IteratorConstraintsExtension, OptionConstraintsExtension};
use crate::types::enums::is_single_member_enum;
use crate::types::{
@@ -1940,14 +1940,15 @@ impl<'db> Type<'db> {
disjointness_visitor.visit((self, other), || {
protocol.interface(db).members(db).when_any(db, |member| {
match other.member(db, member.name()).place {
Place::Defined(attribute_type, _, _, _) => member
.has_disjoint_type_from(
db,
attribute_type,
inferable,
disjointness_visitor,
relation_visitor,
),
Place::Defined(DefinedPlace {
ty: attribute_type, ..
}) => member.has_disjoint_type_from(
db,
attribute_type,
inferable,
disjointness_visitor,
relation_visitor,
),
Place::Undefined => ConstraintSet::from(false),
}
})