From f335fe4d4aa4d88322073f58ba45de7e61a6bb71 Mon Sep 17 00:00:00 2001 From: David Peter Date: Tue, 22 Oct 2024 22:10:53 +0200 Subject: [PATCH] [red-knot] rename {Class,Module,Function} => {Class,Module,Function}Literal (#13873) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary * Rename `Type::Class` => `Type::ClassLiteral` * Rename `Type::Function` => `Type::FunctionLiteral` * Do not rename `Type::Module` * Remove `*Literal` suffixes in `display::LiteralTypeKind` variants, as per clippy suggestion * Get rid of `Type::is_class()` in favor of `is_subtype_of(…, 'type')`; modifiy `is_subtype_of` to support this. * Add new `Type::is_xyz()` methods and use them instead of matching on `Type` variants. closes #13863 ## Test Plan New `is_subtype_of_class_literals` unit test. --------- Co-authored-by: Alex Waygood --- .../src/semantic_model.rs | 7 +- crates/red_knot_python_semantic/src/types.rs | 167 +++++++++++------- .../src/types/builder.rs | 2 +- .../src/types/display.rs | 58 +++--- .../src/types/infer.rs | 42 ++--- crates/red_knot_workspace/src/lint.rs | 4 +- 6 files changed, 163 insertions(+), 117 deletions(-) diff --git a/crates/red_knot_python_semantic/src/semantic_model.rs b/crates/red_knot_python_semantic/src/semantic_model.rs index f4a66ef199c00..9cdbec789c50a 100644 --- a/crates/red_knot_python_semantic/src/semantic_model.rs +++ b/crates/red_knot_python_semantic/src/semantic_model.rs @@ -175,7 +175,6 @@ mod tests { use crate::db::tests::TestDb; use crate::program::{Program, SearchPathSettings}; use crate::python_version::PythonVersion; - use crate::types::Type; use crate::{HasTy, ProgramSettings, SemanticModel}; fn setup_db<'a>(files: impl IntoIterator) -> anyhow::Result { @@ -205,7 +204,7 @@ mod tests { let model = SemanticModel::new(&db, foo); let ty = function.ty(&model); - assert!(matches!(ty, Type::Function(_))); + assert!(ty.is_function_literal()); Ok(()) } @@ -222,7 +221,7 @@ mod tests { let model = SemanticModel::new(&db, foo); let ty = class.ty(&model); - assert!(matches!(ty, Type::Class(_))); + assert!(ty.is_class_literal()); Ok(()) } @@ -243,7 +242,7 @@ mod tests { let model = SemanticModel::new(&db, bar); let ty = alias.ty(&model); - assert!(matches!(ty, Type::Class(_))); + assert!(ty.is_class_literal()); Ok(()) } diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index 5e5f43b9df229..ec6cd935dfa76 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -255,11 +255,11 @@ pub enum Type<'db> { /// `Todo` would change the output type. Todo, /// A specific function object - Function(FunctionType<'db>), + FunctionLiteral(FunctionType<'db>), /// A specific module object - Module(File), + ModuleLiteral(File), /// A specific class object - Class(ClassType<'db>), + ClassLiteral(ClassType<'db>), /// The set of Python objects with the given class in their __class__'s method resolution order Instance(ClassType<'db>), /// The set of objects in any of the types in the union @@ -292,28 +292,32 @@ impl<'db> Type<'db> { matches!(self, Type::Never) } - pub const fn into_class_type(self) -> Option> { + pub const fn into_class_literal_type(self) -> Option> { match self { - Type::Class(class_type) => Some(class_type), + Type::ClassLiteral(class_type) => Some(class_type), _ => None, } } - pub fn expect_class(self) -> ClassType<'db> { - self.into_class_type() - .expect("Expected a Type::Class variant") + pub fn expect_class_literal(self) -> ClassType<'db> { + self.into_class_literal_type() + .expect("Expected a Type::ClassLiteral variant") } - pub const fn into_module_type(self) -> Option { + pub const fn is_class_literal(&self) -> bool { + matches!(self, Type::ClassLiteral(..)) + } + + pub const fn into_module_literal_type(self) -> Option { match self { - Type::Module(file) => Some(file), + Type::ModuleLiteral(file) => Some(file), _ => None, } } - pub fn expect_module(self) -> File { - self.into_module_type() - .expect("Expected a Type::Module variant") + pub fn expect_module_literal(self) -> File { + self.into_module_literal_type() + .expect("Expected a Type::ModuleLiteral variant") } pub const fn into_union_type(self) -> Option> { @@ -328,6 +332,10 @@ impl<'db> Type<'db> { .expect("Expected a Type::Union variant") } + pub const fn is_union(&self) -> bool { + matches!(self, Type::Union(..)) + } + pub const fn into_intersection_type(self) -> Option> { match self { Type::Intersection(intersection_type) => Some(intersection_type), @@ -340,16 +348,20 @@ impl<'db> Type<'db> { .expect("Expected a Type::Intersection variant") } - pub const fn into_function_type(self) -> Option> { + pub const fn into_function_literal_type(self) -> Option> { match self { - Type::Function(function_type) => Some(function_type), + Type::FunctionLiteral(function_type) => Some(function_type), _ => None, } } - pub fn expect_function(self) -> FunctionType<'db> { - self.into_function_type() - .expect("Expected a Type::Function variant") + pub fn expect_function_literal(self) -> FunctionType<'db> { + self.into_function_literal_type() + .expect("Expected a Type::FunctionLiteral variant") + } + + pub const fn is_function_literal(&self) -> bool { + matches!(self, Type::FunctionLiteral(..)) } pub const fn into_int_literal_type(self) -> Option { @@ -364,6 +376,14 @@ impl<'db> Type<'db> { .expect("Expected a Type::IntLiteral variant") } + pub const fn is_boolean_literal(&self) -> bool { + matches!(self, Type::BooleanLiteral(..)) + } + + pub const fn is_literal_string(&self) -> bool { + matches!(self, Type::LiteralString) + } + pub fn may_be_unbound(&self, db: &'db dyn Db) -> bool { match self { Type::Unbound => true, @@ -387,18 +407,8 @@ impl<'db> Type<'db> { pub fn is_stdlib_symbol(&self, db: &'db dyn Db, module_name: &str, name: &str) -> bool { match self { - Type::Class(class) => class.is_stdlib_symbol(db, module_name, name), - Type::Function(function) => function.is_stdlib_symbol(db, module_name, name), - _ => false, - } - } - - /// Return true if the type is a class or a union of classes. - pub fn is_class(&self, db: &'db dyn Db) -> bool { - match self { - Type::Union(union) => union.elements(db).iter().all(|ty| ty.is_class(db)), - Type::Class(_) => true, - // / TODO include type[X], once we add that type + Type::ClassLiteral(class) => class.is_stdlib_symbol(db, module_name, name), + Type::FunctionLiteral(function) => function.is_stdlib_symbol(db, module_name, name), _ => false, } } @@ -436,6 +446,11 @@ impl<'db> Type<'db> { { true } + (Type::ClassLiteral(..), Type::Instance(class)) + if class.is_known(db, KnownClass::Type) => + { + true + } (Type::Union(union), ty) => union .elements(db) .iter() @@ -517,17 +532,17 @@ impl<'db> Type<'db> { | Type::IntLiteral(..) | Type::StringLiteral(..) | Type::BytesLiteral(..) - | Type::Function(..) - | Type::Module(..) - | Type::Class(..)), + | Type::FunctionLiteral(..) + | Type::ModuleLiteral(..) + | Type::ClassLiteral(..)), right @ (Type::None | Type::BooleanLiteral(..) | Type::IntLiteral(..) | Type::StringLiteral(..) | Type::BytesLiteral(..) - | Type::Function(..) - | Type::Module(..) - | Type::Class(..)), + | Type::FunctionLiteral(..) + | Type::ModuleLiteral(..) + | Type::ClassLiteral(..)), ) => left != right, (Type::None, Type::Instance(class_type)) | (Type::Instance(class_type), Type::None) => { @@ -577,12 +592,12 @@ impl<'db> Type<'db> { (Type::BytesLiteral(..), _) | (_, Type::BytesLiteral(..)) => true, ( - Type::Function(..) | Type::Module(..) | Type::Class(..), + Type::FunctionLiteral(..) | Type::ModuleLiteral(..) | Type::ClassLiteral(..), Type::Instance(class_type), ) | ( Type::Instance(class_type), - Type::Function(..) | Type::Module(..) | Type::Class(..), + Type::FunctionLiteral(..) | Type::ModuleLiteral(..) | Type::ClassLiteral(..), ) => !class_type.is_known(db, KnownClass::Object), (Type::Instance(..), Type::Instance(..)) => { @@ -643,7 +658,7 @@ impl<'db> Type<'db> { // are both of type Literal[345], for example. false } - Type::None | Type::BooleanLiteral(_) | Type::Function(..) | Type::Class(..) | Type::Module(..) => true, + Type::None | Type::BooleanLiteral(_) | Type::FunctionLiteral(..) | Type::ClassLiteral(..) | Type::ModuleLiteral(..) => true, Type::Tuple(..) => { // The empty tuple is a singleton on CPython and PyPy, but not on other Python // implementations such as GraalPy. Its *use* as a singleton is discouraged and @@ -675,9 +690,9 @@ impl<'db> Type<'db> { pub(crate) fn is_single_valued(self, db: &'db dyn Db) -> bool { match self { Type::None - | Type::Function(..) - | Type::Module(..) - | Type::Class(..) + | Type::FunctionLiteral(..) + | Type::ModuleLiteral(..) + | Type::ClassLiteral(..) | Type::IntLiteral(..) | Type::BooleanLiteral(..) | Type::StringLiteral(..) @@ -747,12 +762,12 @@ impl<'db> Type<'db> { // TODO: attribute lookup on None type Type::Todo } - Type::Function(_) => { + Type::FunctionLiteral(_) => { // TODO: attribute lookup on function type Type::Todo } - Type::Module(file) => global_symbol_ty(db, *file, name), - Type::Class(class) => class.class_member(db, name), + Type::ModuleLiteral(file) => global_symbol_ty(db, *file, name), + Type::ClassLiteral(class) => class.class_member(db, name), Type::Instance(_) => { // TODO MRO? get_own_instance_member, get_instance_member Type::Todo @@ -800,9 +815,9 @@ impl<'db> Type<'db> { Truthiness::Ambiguous } Type::None => Truthiness::AlwaysFalse, - Type::Function(_) => Truthiness::AlwaysTrue, - Type::Module(_) => Truthiness::AlwaysTrue, - Type::Class(_) => { + Type::FunctionLiteral(_) => Truthiness::AlwaysTrue, + Type::ModuleLiteral(_) => Truthiness::AlwaysTrue, + Type::ClassLiteral(_) => { // TODO: lookup `__bool__` and `__len__` methods on the class's metaclass // More info in https://docs.python.org/3/library/stdtypes.html#truth-value-testing Truthiness::Ambiguous @@ -847,7 +862,7 @@ impl<'db> Type<'db> { fn call(self, db: &'db dyn Db, arg_types: &[Type<'db>]) -> CallOutcome<'db> { match self { // TODO validate typed call arguments vs callable signature - Type::Function(function_type) => match function_type.known(db) { + Type::FunctionLiteral(function_type) => match function_type.known(db) { None => CallOutcome::callable(function_type.return_type(db)), Some(KnownFunction::RevealType) => CallOutcome::revealed( function_type.return_type(db), @@ -856,7 +871,7 @@ impl<'db> Type<'db> { }, // TODO annotated return type on `__new__` or metaclass `__call__` - Type::Class(class) => { + Type::ClassLiteral(class) => { CallOutcome::callable(match class.known(db) { // If the class is the builtin-bool class (for example `bool(1)`), we try to // return the specific truthiness value of the input arg, `Literal[True]` for @@ -976,7 +991,7 @@ impl<'db> Type<'db> { Type::Unknown => Type::Unknown, Type::Unbound => Type::Unknown, Type::Never => Type::Never, - Type::Class(class) => Type::Instance(*class), + Type::ClassLiteral(class) => Type::Instance(*class), Type::Union(union) => union.map(db, |element| element.to_instance(db)), // TODO: we can probably do better here: --Alex Type::Intersection(_) => Type::Todo, @@ -984,9 +999,9 @@ impl<'db> Type<'db> { // since they already indicate that the object is an instance of some kind: Type::BooleanLiteral(_) | Type::BytesLiteral(_) - | Type::Function(_) + | Type::FunctionLiteral(_) | Type::Instance(_) - | Type::Module(_) + | Type::ModuleLiteral(_) | Type::IntLiteral(_) | Type::StringLiteral(_) | Type::Tuple(_) @@ -1002,17 +1017,17 @@ impl<'db> Type<'db> { match self { Type::Unbound => Type::Unbound, Type::Never => Type::Never, - Type::Instance(class) => Type::Class(*class), + Type::Instance(class) => Type::ClassLiteral(*class), Type::Union(union) => union.map(db, |ty| ty.to_meta_type(db)), Type::BooleanLiteral(_) => KnownClass::Bool.to_class(db), Type::BytesLiteral(_) => KnownClass::Bytes.to_class(db), Type::IntLiteral(_) => KnownClass::Int.to_class(db), - Type::Function(_) => KnownClass::FunctionType.to_class(db), - Type::Module(_) => KnownClass::ModuleType.to_class(db), + Type::FunctionLiteral(_) => KnownClass::FunctionType.to_class(db), + Type::ModuleLiteral(_) => KnownClass::ModuleType.to_class(db), Type::Tuple(_) => KnownClass::Tuple.to_class(db), Type::None => KnownClass::NoneType.to_class(db), // TODO not accurate if there's a custom metaclass... - Type::Class(_) => KnownClass::Type.to_class(db), + Type::ClassLiteral(_) => KnownClass::Type.to_class(db), // TODO can we do better here? `type[LiteralString]`? Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_class(db), // TODO: `type[Any]`? @@ -1642,7 +1657,7 @@ impl<'db> ClassType<'db> { // TODO: we need to iterate over the *MRO* here, not the bases (other == self) || self.bases(db).any(|base| match base { - Type::Class(base_class) => base_class == other, + Type::ClassLiteral(base_class) => base_class == other, // `is_subclass_of` is checking the subtype relation, in which gradual types do not // participate, so we should not return `True` if we find `Any/Unknown` in the // bases. @@ -1923,6 +1938,32 @@ mod tests { assert!(!from.into_type(&db).is_subtype_of(&db, to.into_type(&db))); } + #[test] + fn is_subtype_of_class_literals() { + let mut db = setup_db(); + db.write_dedented( + "/src/module.py", + " + class A: ... + class B: ... + U = A if flag else B + ", + ) + .unwrap(); + let module = ruff_db::files::system_path_to_file(&db, "/src/module.py").unwrap(); + + let type_a = super::global_symbol_ty(&db, module, "A"); + let type_u = super::global_symbol_ty(&db, module, "U"); + + assert!(type_a.is_class_literal()); + assert!(type_a.is_subtype_of(&db, Ty::BuiltinInstance("type").into_type(&db))); + assert!(type_a.is_subtype_of(&db, Ty::BuiltinInstance("object").into_type(&db))); + + assert!(type_u.is_union()); + assert!(type_u.is_subtype_of(&db, Ty::BuiltinInstance("type").into_type(&db))); + assert!(type_u.is_subtype_of(&db, Ty::BuiltinInstance("object").into_type(&db))); + } + #[test_case( Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]) @@ -2005,19 +2046,19 @@ mod tests { " class A: ... class B: ... - x = A if flag else B + U = A if flag else B ", ) .unwrap(); let module = ruff_db::files::system_path_to_file(&db, "/src/module.py").unwrap(); let type_a = super::global_symbol_ty(&db, module, "A"); - let type_x = super::global_symbol_ty(&db, module, "x"); + let type_u = super::global_symbol_ty(&db, module, "U"); - assert!(matches!(type_a, Type::Class(_))); - assert!(matches!(type_x, Type::Union(_))); + assert!(type_a.is_class_literal()); + assert!(type_u.is_union()); - assert!(!type_a.is_disjoint_from(&db, type_x)); + assert!(!type_a.is_disjoint_from(&db, type_u)); } #[test_case(Ty::None)] diff --git a/crates/red_knot_python_semantic/src/types/builder.rs b/crates/red_knot_python_semantic/src/types/builder.rs index 7b8114dbade6c..7a17fc1085834 100644 --- a/crates/red_knot_python_semantic/src/types/builder.rs +++ b/crates/red_knot_python_semantic/src/types/builder.rs @@ -231,7 +231,7 @@ impl<'db> InnerIntersectionBuilder<'db> { if let Some(&Type::BooleanLiteral(value)) = self .negative .iter() - .find(|element| matches!(element, Type::BooleanLiteral(..))) + .find(|element| element.is_boolean_literal()) { *self = Self::new(); self.positive.insert(Type::BooleanLiteral(!value)); diff --git a/crates/red_knot_python_semantic/src/types/display.rs b/crates/red_knot_python_semantic/src/types/display.rs index b362bd982f26b..395b290ae024c 100644 --- a/crates/red_knot_python_semantic/src/types/display.rs +++ b/crates/red_knot_python_semantic/src/types/display.rs @@ -34,8 +34,8 @@ impl Display for DisplayType<'_> { | Type::BooleanLiteral(_) | Type::StringLiteral(_) | Type::BytesLiteral(_) - | Type::Class(_) - | Type::Function(_) + | Type::ClassLiteral(_) + | Type::FunctionLiteral(_) ) { write!(f, "Literal[{representation}]") } else { @@ -69,13 +69,13 @@ impl Display for DisplayRepresentation<'_> { // `[Type::Todo]`'s display should be explicit that is not a valid display of // any other type Type::Todo => f.write_str("@Todo"), - Type::Module(file) => { + Type::ModuleLiteral(file) => { write!(f, "", file.path(self.db)) } // TODO functions and classes should display using a fully qualified name - Type::Class(class) => f.write_str(class.name(self.db)), + Type::ClassLiteral(class) => f.write_str(class.name(self.db)), Type::Instance(class) => f.write_str(class.name(self.db)), - Type::Function(function) => f.write_str(function.name(self.db)), + Type::FunctionLiteral(function) => f.write_str(function.name(self.db)), Type::Union(union) => union.display(self.db).fmt(f), Type::Intersection(intersection) => intersection.display(self.db).fmt(f), Type::IntLiteral(n) => n.fmt(f), @@ -119,13 +119,13 @@ impl Display for DisplayUnionType<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let elements = self.ty.elements(self.db); - // Group literal types by kind. - let mut grouped_literals = FxHashMap::default(); + // Group condensed-display types by kind. + let mut grouped_condensed_kinds = FxHashMap::default(); for element in elements { - if let Ok(literal_kind) = LiteralTypeKind::try_from(*element) { - grouped_literals - .entry(literal_kind) + if let Ok(kind) = CondensedDisplayTypeKind::try_from(*element) { + grouped_condensed_kinds + .entry(kind) .or_insert_with(Vec::new) .push(*element); } @@ -134,15 +134,15 @@ impl Display for DisplayUnionType<'_> { let mut join = f.join(" | "); for element in elements { - if let Ok(literal_kind) = LiteralTypeKind::try_from(*element) { - let Some(mut literals) = grouped_literals.remove(&literal_kind) else { + if let Ok(kind) = CondensedDisplayTypeKind::try_from(*element) { + let Some(mut condensed_kind) = grouped_condensed_kinds.remove(&kind) else { continue; }; - if literal_kind == LiteralTypeKind::IntLiteral { - literals.sort_unstable_by_key(|ty| ty.expect_int_literal()); + if kind == CondensedDisplayTypeKind::Int { + condensed_kind.sort_unstable_by_key(|ty| ty.expect_int_literal()); } join.entry(&DisplayLiteralGroup { - literals, + literals: condensed_kind, db: self.db, }); } else { @@ -152,7 +152,7 @@ impl Display for DisplayUnionType<'_> { join.finish()?; - debug_assert!(grouped_literals.is_empty()); + debug_assert!(grouped_condensed_kinds.is_empty()); Ok(()) } @@ -179,25 +179,31 @@ impl Display for DisplayLiteralGroup<'_> { } } +/// Enumeration of literal types that are displayed in a "condensed way" inside `Literal` slices. +/// +/// For example, `Literal[1] | Literal[2]` is displayed as `"Literal[1, 2]"`. +/// Not all `Literal` types are displayed using `Literal` slices +/// (e.g. it would be inappropriate to display `LiteralString` +/// as `Literal[LiteralString]`). #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -enum LiteralTypeKind { +enum CondensedDisplayTypeKind { Class, Function, - IntLiteral, - StringLiteral, - BytesLiteral, + Int, + String, + Bytes, } -impl TryFrom> for LiteralTypeKind { +impl TryFrom> for CondensedDisplayTypeKind { type Error = (); fn try_from(value: Type<'_>) -> Result { match value { - Type::Class(_) => Ok(Self::Class), - Type::Function(_) => Ok(Self::Function), - Type::IntLiteral(_) => Ok(Self::IntLiteral), - Type::StringLiteral(_) => Ok(Self::StringLiteral), - Type::BytesLiteral(_) => Ok(Self::BytesLiteral), + Type::ClassLiteral(_) => Ok(Self::Class), + Type::FunctionLiteral(_) => Ok(Self::Function), + Type::IntLiteral(_) => Ok(Self::Int), + Type::StringLiteral(_) => Ok(Self::String), + Type::BytesLiteral(_) => Ok(Self::Bytes), _ => Err(()), } } diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index ca9a26b4694d3..548f8e50a577a 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -510,12 +510,12 @@ impl<'db> TypeInferenceBuilder<'db> { assigned_ty: Type<'db>, ) { match declared_ty { - Type::Class(class) => { + Type::ClassLiteral(class) => { self.add_diagnostic(node, "invalid-assignment", format_args!( "Implicit shadowing of class `{}`; annotate to make it explicit if this is intentional", class.name(self.db))); } - Type::Function(function) => { + Type::FunctionLiteral(function) => { self.add_diagnostic(node, "invalid-assignment", format_args!( "Implicit shadowing of function `{}`; annotate to make it explicit if this is intentional", function.name(self.db))); @@ -778,7 +778,7 @@ impl<'db> TypeInferenceBuilder<'db> { } _ => None, }; - let function_ty = Type::Function(FunctionType::new( + let function_ty = Type::FunctionLiteral(FunctionType::new( self.db, name.id.clone(), function_kind, @@ -887,7 +887,7 @@ impl<'db> TypeInferenceBuilder<'db> { .as_ref() .and_then(|module| KnownClass::maybe_from_module(module, name.as_str())); - let class_ty = Type::Class(ClassType::new( + let class_ty = Type::ClassLiteral(ClassType::new( self.db, name.id.clone(), definition, @@ -1056,13 +1056,13 @@ impl<'db> TypeInferenceBuilder<'db> { // anything else is invalid and should lead to a diagnostic being reported --Alex match node_ty { Type::Any | Type::Unknown => node_ty, - Type::Class(class_ty) => Type::Instance(class_ty), + Type::ClassLiteral(class_ty) => Type::Instance(class_ty), Type::Tuple(tuple) => UnionType::from_elements( self.db, - tuple - .elements(self.db) - .iter() - .map(|ty| ty.into_class_type().map_or(Type::Todo, Type::Instance)), + tuple.elements(self.db).iter().map(|ty| { + ty.into_class_literal_type() + .map_or(Type::Todo, Type::Instance) + }), ), _ => Type::Todo, } @@ -1311,7 +1311,7 @@ impl<'db> TypeInferenceBuilder<'db> { } } _ => { - let value_ty = if matches!(value_ty, Type::LiteralString) { + let value_ty = if value_ty.is_literal_string() { Type::LiteralString } else { value_ty @@ -1773,7 +1773,7 @@ impl<'db> TypeInferenceBuilder<'db> { } fn module_ty_from_name(&self, module_name: &ModuleName) -> Option> { - resolve_module(self.db, module_name).map(|module| Type::Module(module.file())) + resolve_module(self.db, module_name).map(|module| Type::ModuleLiteral(module.file())) } fn infer_decorator(&mut self, decorator: &ast::Decorator) -> Type<'db> { @@ -3311,7 +3311,7 @@ impl<'db> TypeInferenceBuilder<'db> { // even if the target version is Python 3.8 or lower, // despite the fact that there will be no corresponding `__class_getitem__` // method in these `sys.version_info` branches. - if value_ty.is_class(self.db) { + if value_ty.is_subtype_of(self.db, KnownClass::Type.to_instance(self.db)) { let dunder_class_getitem_method = value_ty.member(self.db, "__class_getitem__"); if !dunder_class_getitem_method.is_unbound() { return dunder_class_getitem_method @@ -3331,7 +3331,7 @@ impl<'db> TypeInferenceBuilder<'db> { }); } - if matches!(value_ty, Type::Class(class) if class.is_known(self.db, KnownClass::Type)) + if matches!(value_ty, Type::ClassLiteral(class) if class.is_known(self.db, KnownClass::Type)) { return KnownClass::GenericAlias.to_instance(self.db); } @@ -3907,7 +3907,7 @@ mod tests { let mod_file = system_path_to_file(&db, "src/mod.py").expect("file to exist"); let ty = global_symbol_ty(&db, mod_file, "Sub"); - let class = ty.expect_class(); + let class = ty.expect_class_literal(); let base_names: Vec<_> = class .bases(&db) @@ -3933,9 +3933,9 @@ mod tests { let mod_file = system_path_to_file(&db, "src/mod.py").unwrap(); let ty = global_symbol_ty(&db, mod_file, "C"); - let class_id = ty.expect_class(); + let class_id = ty.expect_class_literal(); let member_ty = class_id.class_member(&db, &Name::new_static("f")); - let func = member_ty.expect_function(); + let func = member_ty.expect_function_literal(); assert_eq!(func.name(&db), "f"); Ok(()) @@ -4113,7 +4113,7 @@ mod tests { db.write_file("src/a.py", "def example() -> int: return 42")?; let mod_file = system_path_to_file(&db, "src/a.py").unwrap(); - let function = global_symbol_ty(&db, mod_file, "example").expect_function(); + let function = global_symbol_ty(&db, mod_file, "example").expect_function_literal(); let returns = function.return_type(&db); assert_eq!(returns.display(&db).to_string(), "int"); @@ -4142,14 +4142,14 @@ mod tests { let a = system_path_to_file(&db, "src/a.py").expect("file to exist"); let c_ty = global_symbol_ty(&db, a, "C"); - let c_class = c_ty.expect_class(); + let c_class = c_ty.expect_class_literal(); let mut c_bases = c_class.bases(&db); let b_ty = c_bases.next().unwrap(); - let b_class = b_ty.expect_class(); + let b_class = b_ty.expect_class_literal(); assert_eq!(b_class.name(&db), "B"); let mut b_bases = b_class.bases(&db); let a_ty = b_bases.next().unwrap(); - let a_class = a_ty.expect_class(); + let a_class = a_ty.expect_class_literal(); assert_eq!(a_class.name(&db), "A"); Ok(()) @@ -4299,7 +4299,7 @@ mod tests { let ty = global_symbol_ty(&db, file, "C"); let base = ty - .expect_class() + .expect_class_literal() .bases(&db) .next() .expect("there should be at least one base"); diff --git a/crates/red_knot_workspace/src/lint.rs b/crates/red_knot_workspace/src/lint.rs index 090e1fd5b609e..dbc7ea7289446 100644 --- a/crates/red_knot_workspace/src/lint.rs +++ b/crates/red_knot_workspace/src/lint.rs @@ -142,7 +142,7 @@ fn lint_bad_override(context: &SemanticLintContext, class: &ast::StmtClassDef) { let override_ty = semantic.global_symbol_ty(&typing, "override"); - let Type::Class(class_ty) = class.ty(semantic) else { + let Type::ClassLiteral(class_ty) = class.ty(semantic) else { return; }; @@ -151,7 +151,7 @@ fn lint_bad_override(context: &SemanticLintContext, class: &ast::StmtClassDef) { .iter() .filter_map(|stmt| stmt.as_function_def_stmt()) { - let Type::Function(ty) = function.ty(semantic) else { + let Type::FunctionLiteral(ty) = function.ty(semantic) else { return; };