Skip to content

Commit df1d430

Browse files
authored
[red-knot] Reduce usage of From<Type> implementations when working with Symbols (#16076)
1 parent 69d86d1 commit df1d430

File tree

3 files changed

+94
-77
lines changed

3 files changed

+94
-77
lines changed

crates/red_knot_python_semantic/src/symbol.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
types::{Type, UnionType},
2+
types::{todo_type, Type, UnionType},
33
Db,
44
};
55

@@ -33,6 +33,17 @@ pub(crate) enum Symbol<'db> {
3333
}
3434

3535
impl<'db> Symbol<'db> {
36+
/// Constructor that creates a `Symbol` with boundness [`Boundness::Bound`].
37+
pub(crate) fn bound(ty: impl Into<Type<'db>>) -> Self {
38+
Symbol::Type(ty.into(), Boundness::Bound)
39+
}
40+
41+
/// Constructor that creates a [`Symbol`] with a [`crate::types::TodoType`] type
42+
/// and boundness [`Boundness::Bound`].
43+
pub(crate) fn todo(message: &'static str) -> Self {
44+
Symbol::Type(todo_type!(message), Boundness::Bound)
45+
}
46+
3647
pub(crate) fn is_unbound(&self) -> bool {
3748
matches!(self, Symbol::Unbound)
3849
}

crates/red_knot_python_semantic/src/types.rs

Lines changed: 62 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ fn symbol<'db>(db: &'db dyn Db, scope: ScopeId<'db>, name: &str) -> Symbol<'db>
143143
}
144144
// Symbol is possibly undeclared and (possibly) bound
145145
Symbol::Type(inferred_ty, boundness) => Symbol::Type(
146-
UnionType::from_elements(db, [inferred_ty, declared_ty].iter().copied()),
146+
UnionType::from_elements(db, [inferred_ty, declared_ty]),
147147
boundness,
148148
),
149149
}
@@ -159,7 +159,7 @@ fn symbol<'db>(db: &'db dyn Db, scope: ScopeId<'db>, name: &str) -> Symbol<'db>
159159
Err((declared_ty, _)) => {
160160
// Intentionally ignore conflicting declared types; that's not our problem,
161161
// it's the problem of the module we are importing from.
162-
declared_ty.inner_type().into()
162+
Symbol::bound(declared_ty.inner_type())
163163
}
164164
}
165165

@@ -187,18 +187,15 @@ fn symbol<'db>(db: &'db dyn Db, scope: ScopeId<'db>, name: &str) -> Symbol<'db>
187187
&& file_to_module(db, scope.file(db))
188188
.is_some_and(|module| module.is_known(KnownModule::Typing))
189189
{
190-
return Symbol::Type(Type::BooleanLiteral(true), Boundness::Bound);
190+
return Symbol::bound(Type::BooleanLiteral(true));
191191
}
192192
if name == "platform"
193193
&& file_to_module(db, scope.file(db))
194194
.is_some_and(|module| module.is_known(KnownModule::Sys))
195195
{
196196
match Program::get(db).python_platform(db) {
197197
crate::PythonPlatform::Identifier(platform) => {
198-
return Symbol::Type(
199-
Type::StringLiteral(StringLiteralType::new(db, platform.as_str())),
200-
Boundness::Bound,
201-
);
198+
return Symbol::bound(Type::string_literal(db, platform.as_str()));
202199
}
203200
crate::PythonPlatform::All => {
204201
// Fall through to the looked up type
@@ -401,9 +398,16 @@ fn symbol_from_bindings<'db>(
401398
/// If we look up the declared type of `variable` in the scope of class `C`, we will get
402399
/// the type `int`, a "declaredness" of [`Boundness::PossiblyUnbound`], and the information
403400
/// that this comes with a [`TypeQualifiers::CLASS_VAR`] type qualifier.
401+
#[derive(Debug)]
404402
pub(crate) struct SymbolAndQualifiers<'db>(Symbol<'db>, TypeQualifiers);
405403

406404
impl SymbolAndQualifiers<'_> {
405+
/// Constructor that creates a [`SymbolAndQualifiers`] instance with a [`TodoType`] type
406+
/// and no qualifiers.
407+
fn todo(message: &'static str) -> Self {
408+
Self(Symbol::todo(message), TypeQualifiers::empty())
409+
}
410+
407411
fn is_class_var(&self) -> bool {
408412
self.1.contains(TypeQualifiers::CLASS_VAR)
409413
}
@@ -419,12 +423,6 @@ impl<'db> From<Symbol<'db>> for SymbolAndQualifiers<'db> {
419423
}
420424
}
421425

422-
impl<'db> From<Type<'db>> for SymbolAndQualifiers<'db> {
423-
fn from(ty: Type<'db>) -> Self {
424-
SymbolAndQualifiers(ty.into(), TypeQualifiers::empty())
425-
}
426-
}
427-
428426
/// The result of looking up a declared type from declarations; see [`symbol_from_declarations`].
429427
type SymbolFromDeclarationsResult<'db> =
430428
Result<SymbolAndQualifiers<'db>, (TypeAndQualifiers<'db>, Box<[Type<'db>]>)>;
@@ -560,6 +558,11 @@ macro_rules! todo_type {
560558
$crate::types::TodoType::Message($message),
561559
))
562560
};
561+
($message:ident) => {
562+
$crate::types::Type::Dynamic($crate::types::DynamicType::Todo(
563+
$crate::types::TodoType::Message($message),
564+
))
565+
};
563566
}
564567

565568
#[cfg(not(debug_assertions))]
@@ -570,6 +573,9 @@ macro_rules! todo_type {
570573
($message:literal) => {
571574
$crate::types::Type::Dynamic($crate::types::DynamicType::Todo(crate::types::TodoType))
572575
};
576+
($message:ident) => {
577+
$crate::types::Type::Dynamic($crate::types::DynamicType::Todo(crate::types::TodoType))
578+
};
573579
}
574580

575581
pub(crate) use todo_type;
@@ -1688,17 +1694,17 @@ impl<'db> Type<'db> {
16881694
#[must_use]
16891695
pub(crate) fn member(&self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
16901696
if name == "__class__" {
1691-
return self.to_meta_type(db).into();
1697+
return Symbol::bound(self.to_meta_type(db));
16921698
}
16931699

16941700
match self {
1695-
Type::Dynamic(_) => self.into(),
1701+
Type::Dynamic(_) => Symbol::bound(self),
16961702

1697-
Type::Never => todo_type!("attribute lookup on Never").into(),
1703+
Type::Never => Symbol::todo("attribute lookup on Never"),
16981704

16991705
Type::FunctionLiteral(_) => match name {
1700-
"__get__" => todo_type!("`__get__` method on functions").into(),
1701-
"__call__" => todo_type!("`__call__` method on functions").into(),
1706+
"__get__" => Symbol::todo("`__get__` method on functions"),
1707+
"__call__" => Symbol::todo("`__call__` method on functions"),
17021708
_ => KnownClass::FunctionType.to_instance(db).member(db, name),
17031709
},
17041710

@@ -1711,12 +1717,12 @@ impl<'db> Type<'db> {
17111717
Type::KnownInstance(known_instance) => known_instance.member(db, name),
17121718

17131719
Type::Instance(InstanceType { class }) => match (class.known(db), name) {
1714-
(Some(KnownClass::VersionInfo), "major") => {
1715-
Type::IntLiteral(Program::get(db).python_version(db).major.into()).into()
1716-
}
1717-
(Some(KnownClass::VersionInfo), "minor") => {
1718-
Type::IntLiteral(Program::get(db).python_version(db).minor.into()).into()
1719-
}
1720+
(Some(KnownClass::VersionInfo), "major") => Symbol::bound(Type::IntLiteral(
1721+
Program::get(db).python_version(db).major.into(),
1722+
)),
1723+
(Some(KnownClass::VersionInfo), "minor") => Symbol::bound(Type::IntLiteral(
1724+
Program::get(db).python_version(db).minor.into(),
1725+
)),
17201726
_ => {
17211727
let SymbolAndQualifiers(symbol, _) = class.instance_member(db, name);
17221728
symbol
@@ -1762,30 +1768,30 @@ impl<'db> Type<'db> {
17621768
Type::Intersection(_) => {
17631769
// TODO perform the get_member on each type in the intersection
17641770
// TODO return the intersection of those results
1765-
todo_type!("Attribute access on `Intersection` types").into()
1771+
Symbol::todo("Attribute access on `Intersection` types")
17661772
}
17671773

17681774
Type::IntLiteral(_) => match name {
1769-
"real" | "numerator" => self.into(),
1775+
"real" | "numerator" => Symbol::bound(self),
17701776
// TODO more attributes could probably be usefully special-cased
17711777
_ => KnownClass::Int.to_instance(db).member(db, name),
17721778
},
17731779

17741780
Type::BooleanLiteral(bool_value) => match name {
1775-
"real" | "numerator" => Type::IntLiteral(i64::from(*bool_value)).into(),
1781+
"real" | "numerator" => Symbol::bound(Type::IntLiteral(i64::from(*bool_value))),
17761782
_ => KnownClass::Bool.to_instance(db).member(db, name),
17771783
},
17781784

17791785
Type::StringLiteral(_) => {
17801786
// TODO defer to `typing.LiteralString`/`builtins.str` methods
17811787
// from typeshed's stubs
1782-
todo_type!("Attribute access on `StringLiteral` types").into()
1788+
Symbol::todo("Attribute access on `StringLiteral` types")
17831789
}
17841790

17851791
Type::LiteralString => {
17861792
// TODO defer to `typing.LiteralString`/`builtins.str` methods
17871793
// from typeshed's stubs
1788-
todo_type!("Attribute access on `LiteralString` types").into()
1794+
Symbol::todo("Attribute access on `LiteralString` types")
17891795
}
17901796

17911797
Type::BytesLiteral(_) => KnownClass::Bytes.to_instance(db).member(db, name),
@@ -1797,13 +1803,13 @@ impl<'db> Type<'db> {
17971803

17981804
Type::Tuple(_) => {
17991805
// TODO: implement tuple methods
1800-
todo_type!("Attribute access on heterogeneous tuple types").into()
1806+
Symbol::todo("Attribute access on heterogeneous tuple types")
18011807
}
18021808

18031809
Type::AlwaysTruthy | Type::AlwaysFalsy => match name {
18041810
"__bool__" => {
18051811
// TODO should be `Callable[[], Literal[True/False]]`
1806-
todo_type!("`__bool__` for `AlwaysTruthy`/`AlwaysFalsy` Type variants").into()
1812+
Symbol::todo("`__bool__` for `AlwaysTruthy`/`AlwaysFalsy` Type variants")
18071813
}
18081814
_ => Type::object(db).member(db, name),
18091815
},
@@ -2528,18 +2534,6 @@ impl<'db> From<&Type<'db>> for Type<'db> {
25282534
}
25292535
}
25302536

2531-
impl<'db> From<Type<'db>> for Symbol<'db> {
2532-
fn from(value: Type<'db>) -> Self {
2533-
Symbol::Type(value, Boundness::Bound)
2534-
}
2535-
}
2536-
2537-
impl<'db> From<&Type<'db>> for Symbol<'db> {
2538-
fn from(value: &Type<'db>) -> Self {
2539-
Self::from(*value)
2540-
}
2541-
}
2542-
25432537
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
25442538
pub enum DynamicType {
25452539
// An explicitly annotated `typing.Any`
@@ -2572,7 +2566,7 @@ impl std::fmt::Display for DynamicType {
25722566

25732567
bitflags! {
25742568
/// Type qualifiers that appear in an annotation expression.
2575-
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
2569+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
25762570
pub(crate) struct TypeQualifiers: u8 {
25772571
/// `typing.ClassVar`
25782572
const CLASS_VAR = 1 << 0;
@@ -2599,6 +2593,14 @@ impl<'db> TypeAndQualifiers<'db> {
25992593
Self { inner, qualifiers }
26002594
}
26012595

2596+
/// Constructor that creates a [`TypeAndQualifiers`] instance with type `Unknown` and no qualifiers.
2597+
pub(crate) fn unknown() -> Self {
2598+
Self {
2599+
inner: Type::unknown(),
2600+
qualifiers: TypeQualifiers::empty(),
2601+
}
2602+
}
2603+
26022604
/// Forget about type qualifiers and only return the inner type.
26032605
pub(crate) fn inner_type(&self) -> Type<'db> {
26042606
self.inner
@@ -3330,7 +3332,7 @@ impl<'db> KnownInstanceType<'db> {
33303332
(Self::TypeAliasType(alias), "__name__") => Type::string_literal(db, alias.name(db)),
33313333
_ => return self.instance_fallback(db).member(db, name),
33323334
};
3333-
ty.into()
3335+
Symbol::bound(ty)
33343336
}
33353337
}
33363338

@@ -3788,8 +3790,7 @@ impl<'db> ModuleLiteralType<'db> {
37883790
full_submodule_name.extend(&submodule_name);
37893791
if imported_submodules.contains(&full_submodule_name) {
37903792
if let Some(submodule) = resolve_module(db, &full_submodule_name) {
3791-
let submodule_ty = Type::module_literal(db, importing_file, submodule);
3792-
return Symbol::Type(submodule_ty, Boundness::Bound);
3793+
return Symbol::bound(Type::module_literal(db, importing_file, submodule));
37933794
}
37943795
}
37953796
}
@@ -4123,7 +4124,7 @@ impl<'db> Class<'db> {
41234124
pub(crate) fn class_member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
41244125
if name == "__mro__" {
41254126
let tuple_elements = self.iter_mro(db).map(Type::from);
4126-
return TupleType::from_elements(db, tuple_elements).into();
4127+
return Symbol::bound(TupleType::from_elements(db, tuple_elements));
41274128
}
41284129

41294130
for superclass in self.iter_mro(db) {
@@ -4163,7 +4164,9 @@ impl<'db> Class<'db> {
41634164
for superclass in self.iter_mro(db) {
41644165
match superclass {
41654166
ClassBase::Dynamic(_) => {
4166-
return todo_type!("instance attribute on class with dynamic base").into();
4167+
return SymbolAndQualifiers::todo(
4168+
"instance attribute on class with dynamic base",
4169+
);
41674170
}
41684171
ClassBase::Class(class) => {
41694172
if let member @ SymbolAndQualifiers(Symbol::Type(_, _), _) =
@@ -4213,7 +4216,7 @@ impl<'db> Class<'db> {
42134216
.and_then(|assignments| assignments.get(name))
42144217
else {
42154218
if inferred_type_from_class_body.is_some() {
4216-
return union_of_inferred_types.build().into();
4219+
return Symbol::bound(union_of_inferred_types.build());
42174220
}
42184221
return Symbol::Unbound;
42194222
};
@@ -4230,7 +4233,7 @@ impl<'db> Class<'db> {
42304233
let annotation_ty = infer_expression_type(db, *annotation);
42314234

42324235
// TODO: check if there are conflicting declarations
4233-
return annotation_ty.into();
4236+
return Symbol::bound(annotation_ty);
42344237
}
42354238
AttributeAssignment::Unannotated { value } => {
42364239
// We found an un-annotated attribute assignment of the form:
@@ -4270,7 +4273,7 @@ impl<'db> Class<'db> {
42704273
}
42714274
}
42724275

4273-
union_of_inferred_types.build().into()
4276+
Symbol::bound(union_of_inferred_types.build())
42744277
}
42754278

42764279
/// A helper function for `instance_member` that looks up the `name` attribute only on
@@ -4299,12 +4302,12 @@ impl<'db> Class<'db> {
42994302
// just a temporary heuristic to provide a broad categorization into properties
43004303
// and non-property methods.
43014304
if function.has_decorator(db, KnownClass::Property.to_class_literal(db)) {
4302-
todo_type!("@property").into()
4305+
SymbolAndQualifiers::todo("@property")
43034306
} else {
4304-
todo_type!("bound method").into()
4307+
SymbolAndQualifiers::todo("bound method")
43054308
}
43064309
} else {
4307-
SymbolAndQualifiers(Symbol::Type(declared_ty, Boundness::Bound), qualifiers)
4310+
SymbolAndQualifiers(Symbol::bound(declared_ty), qualifiers)
43084311
}
43094312
}
43104313
Ok(SymbolAndQualifiers(Symbol::Unbound, _)) => {
@@ -4319,7 +4322,10 @@ impl<'db> Class<'db> {
43194322
}
43204323
Err((declared_ty, _conflicting_declarations)) => {
43214324
// There are conflicting declarations for this attribute in the class body.
4322-
SymbolAndQualifiers(declared_ty.inner_type().into(), declared_ty.qualifiers())
4325+
SymbolAndQualifiers(
4326+
Symbol::bound(declared_ty.inner_type()),
4327+
declared_ty.qualifiers(),
4328+
)
43234329
}
43244330
}
43254331
} else {

0 commit comments

Comments
 (0)