mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 05:20:49 -05:00
[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:
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user