[red-knot] rename {Class,Module,Function} => {Class,Module,Function}Literal (#13873)

## 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 <Alex.Waygood@Gmail.com>
This commit is contained in:
David Peter 2024-10-22 22:10:53 +02:00 committed by GitHub
parent c6ce52c29e
commit f335fe4d4a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 163 additions and 117 deletions

View file

@ -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<Item = (&'a str, &'a str)>) -> anyhow::Result<TestDb> {
@ -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(())
}

View file

@ -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<ClassType<'db>> {
pub const fn into_class_literal_type(self) -> Option<ClassType<'db>> {
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<File> {
pub const fn is_class_literal(&self) -> bool {
matches!(self, Type::ClassLiteral(..))
}
pub const fn into_module_literal_type(self) -> Option<File> {
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<UnionType<'db>> {
@ -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<IntersectionType<'db>> {
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<FunctionType<'db>> {
pub const fn into_function_literal_type(self) -> Option<FunctionType<'db>> {
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<i64> {
@ -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)]

View file

@ -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));

View file

@ -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, "<module '{:?}'>", 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<Type<'_>> for LiteralTypeKind {
impl TryFrom<Type<'_>> for CondensedDisplayTypeKind {
type Error = ();
fn try_from(value: Type<'_>) -> Result<Self, Self::Error> {
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(()),
}
}

View file

@ -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<Type<'db>> {
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");

View file

@ -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;
};