mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-30 08:23:53 +00:00
[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:
parent
c6ce52c29e
commit
f335fe4d4a
6 changed files with 163 additions and 117 deletions
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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(()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue