Skip to content

Commit

Permalink
[red-knot] Remove Type::RevealType (#13567)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood authored Oct 1, 2024
1 parent 6322639 commit 2a36b47
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use ruff_db::parsed::ParsedModule;
use ruff_python_ast as ast;

use crate::ast_node_ref::AstNodeRef;
use crate::module_resolver::file_to_module;
use crate::node_key::NodeKey;
use crate::semantic_index::symbol::{FileScopeId, ScopeId, ScopedSymbolId};
use crate::Db;
Expand Down Expand Up @@ -45,6 +46,14 @@ impl<'db> Definition<'db> {
pub(crate) fn is_binding(self, db: &'db dyn Db) -> bool {
self.kind(db).category().is_binding()
}

/// Return true if this is a symbol was defined in the `typing` or `typing_extensions` modules
pub(crate) fn is_typing_definition(self, db: &'db dyn Db) -> bool {
file_to_module(db, self.file(db)).is_some_and(|module| {
module.search_path().is_standard_library()
&& matches!(&**module.name(), "typing" | "typing_extensions")
})
}
}

#[derive(Copy, Clone, Debug)]
Expand Down
52 changes: 25 additions & 27 deletions crates/red_knot_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,6 @@ pub enum Type<'db> {
Todo,
/// A specific function object
Function(FunctionType<'db>),
/// The `typing.reveal_type` function, which has special `__call__` behavior.
RevealTypeFunction(FunctionType<'db>),
/// A specific module object
Module(File),
/// A specific class object
Expand Down Expand Up @@ -344,16 +342,14 @@ impl<'db> Type<'db> {

pub const fn into_function_type(self) -> Option<FunctionType<'db>> {
match self {
Type::Function(function_type) | Type::RevealTypeFunction(function_type) => {
Some(function_type)
}
Type::Function(function_type) => Some(function_type),
_ => None,
}
}

pub fn expect_function(self) -> FunctionType<'db> {
self.into_function_type()
.expect("Expected a variant wrapping a FunctionType")
.expect("Expected a Type::Function variant")
}

pub const fn into_int_literal_type(self) -> Option<i64> {
Expand Down Expand Up @@ -396,9 +392,7 @@ 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) | Type::RevealTypeFunction(function) => {
function.is_stdlib_symbol(db, module_name, name)
}
Type::Function(function) => function.is_stdlib_symbol(db, module_name, name),
_ => false,
}
}
Expand Down Expand Up @@ -492,7 +486,7 @@ impl<'db> Type<'db> {
// TODO: attribute lookup on None type
Type::Todo
}
Type::Function(_) | Type::RevealTypeFunction(_) => {
Type::Function(_) => {
// TODO: attribute lookup on function type
Type::Todo
}
Expand Down Expand Up @@ -545,7 +539,7 @@ impl<'db> Type<'db> {
Truthiness::Ambiguous
}
Type::None => Truthiness::AlwaysFalse,
Type::Function(_) | Type::RevealTypeFunction(_) => Truthiness::AlwaysTrue,
Type::Function(_) => Truthiness::AlwaysTrue,
Type::Module(_) => Truthiness::AlwaysTrue,
Type::Class(_) => {
// TODO: lookup `__bool__` and `__len__` methods on the class's metaclass
Expand Down Expand Up @@ -592,11 +586,13 @@ 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) => CallOutcome::callable(function_type.return_type(db)),
Type::RevealTypeFunction(function_type) => CallOutcome::revealed(
function_type.return_type(db),
*arg_types.first().unwrap_or(&Type::Unknown),
),
Type::Function(function_type) => match function_type.kind(db) {
FunctionKind::Ordinary => CallOutcome::callable(function_type.return_type(db)),
FunctionKind::RevealType => CallOutcome::revealed(
function_type.return_type(db),
*arg_types.first().unwrap_or(&Type::Unknown),
),
},

// TODO annotated return type on `__new__` or metaclass `__call__`
Type::Class(class) => {
Expand Down Expand Up @@ -719,7 +715,6 @@ impl<'db> Type<'db> {
Type::BooleanLiteral(_)
| Type::BytesLiteral(_)
| Type::Function(_)
| Type::RevealTypeFunction(_)
| Type::Instance(_)
| Type::Module(_)
| Type::IntLiteral(_)
Expand All @@ -742,7 +737,7 @@ impl<'db> Type<'db> {
Type::BooleanLiteral(_) => builtins_symbol_ty(db, "bool"),
Type::BytesLiteral(_) => builtins_symbol_ty(db, "bytes"),
Type::IntLiteral(_) => builtins_symbol_ty(db, "int"),
Type::Function(_) | Type::RevealTypeFunction(_) => types_symbol_ty(db, "FunctionType"),
Type::Function(_) => types_symbol_ty(db, "FunctionType"),
Type::Module(_) => types_symbol_ty(db, "ModuleType"),
Type::Tuple(_) => builtins_symbol_ty(db, "tuple"),
Type::None => typeshed_symbol_ty(db, "NoneType"),
Expand Down Expand Up @@ -1045,6 +1040,9 @@ pub struct FunctionType<'db> {
#[return_ref]
pub name: ast::name::Name,

/// Is this a function that we special-case somehow? If so, which one?
kind: FunctionKind,

definition: Definition<'db>,

/// types of all decorators on this function
Expand All @@ -1060,15 +1058,6 @@ impl<'db> FunctionType<'db> {
})
}

/// Return true if this is a symbol with given name from `typing` or `typing_extensions`.
pub(crate) fn is_typing_symbol(self, db: &'db dyn Db, name: &str) -> bool {
name == self.name(db)
&& file_to_module(db, self.definition(db).file(db)).is_some_and(|module| {
module.search_path().is_standard_library()
&& matches!(&**module.name(), "typing" | "typing_extensions")
})
}

pub fn has_decorator(self, db: &dyn Db, decorator: Type<'_>) -> bool {
self.decorators(db).contains(&decorator)
}
Expand Down Expand Up @@ -1104,6 +1093,15 @@ impl<'db> FunctionType<'db> {
}
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Hash)]
pub enum FunctionKind {
/// Just a normal function for which we have no particular special casing
#[default]
Ordinary,
/// `builtins.reveal_type`, `typing.reveal_type` or `typing_extensions.reveal_type`
RevealType,
}

#[salsa::interned]
pub struct ClassType<'db> {
/// Name of the class at definition
Expand Down
7 changes: 2 additions & 5 deletions crates/red_knot_python_semantic/src/types/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ impl Display for DisplayType<'_> {
| Type::BytesLiteral(_)
| Type::Class(_)
| Type::Function(_)
| Type::RevealTypeFunction(_)
) {
write!(f, "Literal[{representation}]",)
} else {
Expand Down Expand Up @@ -76,9 +75,7 @@ impl Display for DisplayRepresentation<'_> {
// TODO functions and classes should display using a fully qualified name
Type::Class(class) => f.write_str(class.name(self.db)),
Type::Instance(class) => f.write_str(class.name(self.db)),
Type::Function(function) | Type::RevealTypeFunction(function) => {
f.write_str(function.name(self.db))
}
Type::Function(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),
Expand Down Expand Up @@ -197,7 +194,7 @@ impl TryFrom<Type<'_>> for LiteralTypeKind {
fn try_from(value: Type<'_>) -> Result<Self, Self::Error> {
match value {
Type::Class(_) => Ok(Self::Class),
Type::Function(_) | Type::RevealTypeFunction(_) => Ok(Self::Function),
Type::Function(_) => Ok(Self::Function),
Type::IntLiteral(_) => Ok(Self::IntLiteral),
Type::StringLiteral(_) => Ok(Self::StringLiteral),
Type::BytesLiteral(_) => Ok(Self::BytesLiteral),
Expand Down
19 changes: 12 additions & 7 deletions crates/red_knot_python_semantic/src/types/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ use crate::stdlib::builtins_module_scope;
use crate::types::diagnostic::{TypeCheckDiagnostic, TypeCheckDiagnostics};
use crate::types::{
bindings_ty, builtins_symbol_ty, declarations_ty, global_symbol_ty, symbol_ty,
typing_extensions_symbol_ty, BytesLiteralType, ClassType, FunctionType, StringLiteralType,
Truthiness, TupleType, Type, TypeArrayDisplay, UnionType,
typing_extensions_symbol_ty, BytesLiteralType, ClassType, FunctionKind, FunctionType,
StringLiteralType, Truthiness, TupleType, Type, TypeArrayDisplay, UnionType,
};
use crate::Db;

Expand Down Expand Up @@ -732,12 +732,17 @@ impl<'db> TypeInferenceBuilder<'db> {
}
}

let function_type = FunctionType::new(self.db, name.id.clone(), definition, decorator_tys);
let function_ty = if function_type.is_typing_symbol(self.db, "reveal_type") {
Type::RevealTypeFunction(function_type)
} else {
Type::Function(function_type)
let function_kind = match &**name {
"reveal_type" if definition.is_typing_definition(self.db) => FunctionKind::RevealType,
_ => FunctionKind::Ordinary,
};
let function_ty = Type::Function(FunctionType::new(
self.db,
name.id.clone(),
function_kind,
definition,
decorator_tys,
));

self.add_declaration_with_binding(function.into(), definition, function_ty, function_ty);
}
Expand Down

0 comments on commit 2a36b47

Please sign in to comment.