mirror of https://github.com/astral-sh/ruff
start pulling out enum
This commit is contained in:
parent
454ad15aee
commit
0a4dec0323
|
|
@ -5863,8 +5863,91 @@ impl<'db> IntoIterator for &'db FunctionSignature<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A callable type that represents a single Python function.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, salsa::Update)]
|
||||||
|
pub enum FunctionType<'db> {
|
||||||
|
/// A function literal in the Python AST
|
||||||
|
FunctionLiteral(FunctionLiteral<'db>),
|
||||||
|
|
||||||
|
/// A function that has a specialization applied to its signature.
|
||||||
|
///
|
||||||
|
/// (This does not necessarily mean that the function itself is generic — the methods of a
|
||||||
|
/// generic class, for instance, will have the class's specialization applied so that we
|
||||||
|
/// correctly substitute any class typevars that appear in the signature.)
|
||||||
|
Specialized(SpecializedFunction<'db>),
|
||||||
|
|
||||||
|
/// A function that we treat as generic because it inherits a containing generic context.
|
||||||
|
///
|
||||||
|
/// This is currently only used for the `__new__` and `__init__` methods of a generic class.
|
||||||
|
/// That lets us pretend those methods are generic, so that we can infer a class specialization
|
||||||
|
/// from the arguments to its constructor.
|
||||||
|
InheritedGenericContext(FunctionWithInheritedGenericContext<'db>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> FunctionType<'db> {
|
||||||
|
fn function_literal(self, db: &'db dyn Db) -> FunctionLiteral<'db> {
|
||||||
|
match self {
|
||||||
|
FunctionType::FunctionLiteral(literal) => literal,
|
||||||
|
FunctionType::InheritedGenericContext(inherited) => inherited.function(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn has_known_decorator(self, db: &dyn Db, decorator: FunctionDecorators) -> bool {
|
||||||
|
self.function_literal(db).decorators(db).contains(decorator)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the `FunctionType` into a [`Type::Callable`].
|
||||||
|
pub(crate) fn into_callable_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||||
|
Type::Callable(CallableType::from_overloads(
|
||||||
|
db,
|
||||||
|
self.signature(db).iter().cloned(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the [`FileRange`] of the function's name.
|
||||||
|
pub fn focus_range(self, db: &dyn Db) -> FileRange {
|
||||||
|
let body_scope = self.function_literal(db).body_scope(db);
|
||||||
|
FileRange::new(
|
||||||
|
body_scope.file(db),
|
||||||
|
body_scope.node(db).expect_function().name.range,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn full_range(self, db: &dyn Db) -> FileRange {
|
||||||
|
let body_scope = self.function_literal(db).body_scope(db);
|
||||||
|
FileRange::new(
|
||||||
|
body_scope.file(db),
|
||||||
|
body_scope.node(db).expect_function().range,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn definition(self, db: &'db dyn Db) -> Definition<'db> {
|
||||||
|
let body_scope = self.function_literal(db).body_scope(db);
|
||||||
|
let index = semantic_index(db, body_scope.file(db));
|
||||||
|
index.expect_single_definition(body_scope.node(db).expect_function())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Typed externally-visible signature for this function.
|
||||||
|
///
|
||||||
|
/// This is the signature as seen by external callers, possibly modified by decorators and/or
|
||||||
|
/// overloaded.
|
||||||
|
///
|
||||||
|
/// ## Why is this a salsa query?
|
||||||
|
///
|
||||||
|
/// This is a salsa query to short-circuit the invalidation
|
||||||
|
/// when the function's AST node changes.
|
||||||
|
///
|
||||||
|
/// Were this not a salsa query, then the calling query
|
||||||
|
/// would depend on the function's AST and rerun for every change in that file.
|
||||||
|
pub(crate) fn signature(self, db: &'db dyn Db) -> FunctionSignature<'db> {
|
||||||
|
match self {
|
||||||
|
FunctionType::FunctionLiteral(literal) => literal.signature(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[salsa::interned(debug)]
|
#[salsa::interned(debug)]
|
||||||
pub struct FunctionType<'db> {
|
pub struct FunctionLiteral<'db> {
|
||||||
/// Name of the function at definition.
|
/// Name of the function at definition.
|
||||||
#[return_ref]
|
#[return_ref]
|
||||||
pub name: ast::name::Name,
|
pub name: ast::name::Name,
|
||||||
|
|
@ -5888,40 +5971,11 @@ pub struct FunctionType<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[salsa::tracked]
|
#[salsa::tracked]
|
||||||
impl<'db> FunctionType<'db> {
|
impl<'db> FunctionLiteral<'db> {
|
||||||
pub(crate) fn has_known_decorator(self, db: &dyn Db, decorator: FunctionDecorators) -> bool {
|
fn has_known_decorator(self, db: &dyn Db, decorator: FunctionDecorators) -> bool {
|
||||||
self.decorators(db).contains(decorator)
|
self.decorators(db).contains(decorator)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the `FunctionType` into a [`Type::Callable`].
|
|
||||||
pub(crate) fn into_callable_type(self, db: &'db dyn Db) -> Type<'db> {
|
|
||||||
Type::Callable(CallableType::from_overloads(
|
|
||||||
db,
|
|
||||||
self.signature(db).iter().cloned(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the [`FileRange`] of the function's name.
|
|
||||||
pub fn focus_range(self, db: &dyn Db) -> FileRange {
|
|
||||||
FileRange::new(
|
|
||||||
self.body_scope(db).file(db),
|
|
||||||
self.body_scope(db).node(db).expect_function().name.range,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn full_range(self, db: &dyn Db) -> FileRange {
|
|
||||||
FileRange::new(
|
|
||||||
self.body_scope(db).file(db),
|
|
||||||
self.body_scope(db).node(db).expect_function().range,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn definition(self, db: &'db dyn Db) -> Definition<'db> {
|
|
||||||
let body_scope = self.body_scope(db);
|
|
||||||
let index = semantic_index(db, body_scope.file(db));
|
|
||||||
index.expect_single_definition(body_scope.node(db).expect_function())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Typed externally-visible signature for this function.
|
/// Typed externally-visible signature for this function.
|
||||||
///
|
///
|
||||||
/// This is the signature as seen by external callers, possibly modified by decorators and/or
|
/// This is the signature as seen by external callers, possibly modified by decorators and/or
|
||||||
|
|
@ -5934,8 +5988,8 @@ impl<'db> FunctionType<'db> {
|
||||||
///
|
///
|
||||||
/// Were this not a salsa query, then the calling query
|
/// Were this not a salsa query, then the calling query
|
||||||
/// would depend on the function's AST and rerun for every change in that file.
|
/// would depend on the function's AST and rerun for every change in that file.
|
||||||
#[salsa::tracked(return_ref)]
|
#[salsa::tracked]
|
||||||
pub(crate) fn signature(self, db: &'db dyn Db) -> FunctionSignature<'db> {
|
fn signature(self, db: &'db dyn Db) -> FunctionSignature<'db> {
|
||||||
let mut internal_signature = self.internal_signature(db);
|
let mut internal_signature = self.internal_signature(db);
|
||||||
|
|
||||||
if let Some(specialization) = self.specialization(db) {
|
if let Some(specialization) = self.specialization(db) {
|
||||||
|
|
@ -6007,16 +6061,16 @@ impl<'db> FunctionType<'db> {
|
||||||
self.known(db) == Some(known_function)
|
self.known(db) == Some(known_function)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_generic_context(self, db: &'db dyn Db, generic_context: GenericContext<'db>) -> Self {
|
fn with_generic_context(
|
||||||
Self::new(
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
generic_context: GenericContext<'db>,
|
||||||
|
) -> FunctionType<'db> {
|
||||||
|
FunctionType::InheritedGenericContext(FunctionWithInheritedGenericContext::new(
|
||||||
db,
|
db,
|
||||||
self.name(db).clone(),
|
self,
|
||||||
self.known(db),
|
generic_context,
|
||||||
self.body_scope(db),
|
))
|
||||||
self.decorators(db),
|
|
||||||
Some(generic_context),
|
|
||||||
self.specialization(db),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_specialization(self, db: &'db dyn Db, specialization: Specialization<'db>) -> Self {
|
fn apply_specialization(self, db: &'db dyn Db, specialization: Specialization<'db>) -> Self {
|
||||||
|
|
@ -6036,6 +6090,12 @@ impl<'db> FunctionType<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[salsa::interned(debug)]
|
||||||
|
pub struct FunctionWithInheritedGenericContext<'db> {
|
||||||
|
function: FunctionLiteral<'db>,
|
||||||
|
generic_context: GenericContext<'db>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Non-exhaustive enumeration of known functions (e.g. `builtins.reveal_type`, ...) that might
|
/// Non-exhaustive enumeration of known functions (e.g. `builtins.reveal_type`, ...) that might
|
||||||
/// have special behavior.
|
/// have special behavior.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, strum_macros::EnumString)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, strum_macros::EnumString)]
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,8 @@ impl<'db> Bindings<'db> {
|
||||||
|
|
||||||
match binding_type {
|
match binding_type {
|
||||||
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(function)) => {
|
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(function)) => {
|
||||||
if function.has_known_decorator(db, FunctionDecorators::CLASSMETHOD) {
|
let function_literal = function.function_literal();
|
||||||
|
if function_literal.has_known_decorator(db, FunctionDecorators::CLASSMETHOD) {
|
||||||
match overload.parameter_types() {
|
match overload.parameter_types() {
|
||||||
[_, Some(owner)] => {
|
[_, Some(owner)] => {
|
||||||
overload.set_return_type(Type::BoundMethod(BoundMethodType::new(
|
overload.set_return_type(Type::BoundMethod(BoundMethodType::new(
|
||||||
|
|
@ -250,7 +251,9 @@ impl<'db> Bindings<'db> {
|
||||||
if let [Some(function_ty @ Type::FunctionLiteral(function)), ..] =
|
if let [Some(function_ty @ Type::FunctionLiteral(function)), ..] =
|
||||||
overload.parameter_types()
|
overload.parameter_types()
|
||||||
{
|
{
|
||||||
if function.has_known_decorator(db, FunctionDecorators::CLASSMETHOD) {
|
let function_literal = function.function_literal();
|
||||||
|
if function_literal.has_known_decorator(db, FunctionDecorators::CLASSMETHOD)
|
||||||
|
{
|
||||||
match overload.parameter_types() {
|
match overload.parameter_types() {
|
||||||
[_, _, Some(owner)] => {
|
[_, _, Some(owner)] => {
|
||||||
overload.set_return_type(Type::BoundMethod(
|
overload.set_return_type(Type::BoundMethod(
|
||||||
|
|
@ -298,7 +301,7 @@ impl<'db> Bindings<'db> {
|
||||||
if property.getter(db).is_some_and(|getter| {
|
if property.getter(db).is_some_and(|getter| {
|
||||||
getter
|
getter
|
||||||
.into_function_literal()
|
.into_function_literal()
|
||||||
.is_some_and(|f| f.name(db) == "__name__")
|
.is_some_and(|f| f.function_literal().name(db) == "__name__")
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
overload.set_return_type(Type::string_literal(db, type_alias.name(db)));
|
overload.set_return_type(Type::string_literal(db, type_alias.name(db)));
|
||||||
|
|
@ -307,7 +310,7 @@ impl<'db> Bindings<'db> {
|
||||||
if property.getter(db).is_some_and(|getter| {
|
if property.getter(db).is_some_and(|getter| {
|
||||||
getter
|
getter
|
||||||
.into_function_literal()
|
.into_function_literal()
|
||||||
.is_some_and(|f| f.name(db) == "__name__")
|
.is_some_and(|f| f.function_literal().name(db) == "__name__")
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
overload.set_return_type(Type::string_literal(db, type_var.name(db)));
|
overload.set_return_type(Type::string_literal(db, type_var.name(db)));
|
||||||
|
|
@ -416,7 +419,12 @@ impl<'db> Bindings<'db> {
|
||||||
Type::BoundMethod(bound_method)
|
Type::BoundMethod(bound_method)
|
||||||
if bound_method.self_instance(db).is_property_instance() =>
|
if bound_method.self_instance(db).is_property_instance() =>
|
||||||
{
|
{
|
||||||
match bound_method.function(db).name(db).as_str() {
|
match bound_method
|
||||||
|
.function(db)
|
||||||
|
.function_literal()
|
||||||
|
.name(db)
|
||||||
|
.as_str()
|
||||||
|
{
|
||||||
"setter" => {
|
"setter" => {
|
||||||
if let [Some(_), Some(setter)] = overload.parameter_types() {
|
if let [Some(_), Some(setter)] = overload.parameter_types() {
|
||||||
let mut ty_property = bound_method.self_instance(db);
|
let mut ty_property = bound_method.self_instance(db);
|
||||||
|
|
@ -456,7 +464,10 @@ impl<'db> Bindings<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::FunctionLiteral(function_type) => match function_type.known(db) {
|
Type::FunctionLiteral(function_type) => match function_type
|
||||||
|
.function_literal()
|
||||||
|
.known(db)
|
||||||
|
{
|
||||||
Some(KnownFunction::IsEquivalentTo) => {
|
Some(KnownFunction::IsEquivalentTo) => {
|
||||||
if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() {
|
if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() {
|
||||||
overload.set_return_type(Type::BooleanLiteral(
|
overload.set_return_type(Type::BooleanLiteral(
|
||||||
|
|
@ -1166,7 +1177,7 @@ impl<'db> CallableDescription<'db> {
|
||||||
match callable_type {
|
match callable_type {
|
||||||
Type::FunctionLiteral(function) => Some(CallableDescription {
|
Type::FunctionLiteral(function) => Some(CallableDescription {
|
||||||
kind: "function",
|
kind: "function",
|
||||||
name: function.name(db),
|
name: function.function_literal().name(db),
|
||||||
}),
|
}),
|
||||||
Type::ClassLiteral(class_type) => Some(CallableDescription {
|
Type::ClassLiteral(class_type) => Some(CallableDescription {
|
||||||
kind: "class",
|
kind: "class",
|
||||||
|
|
@ -1174,12 +1185,12 @@ impl<'db> CallableDescription<'db> {
|
||||||
}),
|
}),
|
||||||
Type::BoundMethod(bound_method) => Some(CallableDescription {
|
Type::BoundMethod(bound_method) => Some(CallableDescription {
|
||||||
kind: "bound method",
|
kind: "bound method",
|
||||||
name: bound_method.function(db).name(db),
|
name: bound_method.function(db).function_literal().name(db),
|
||||||
}),
|
}),
|
||||||
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(function)) => {
|
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(function)) => {
|
||||||
Some(CallableDescription {
|
Some(CallableDescription {
|
||||||
kind: "method wrapper `__get__` of function",
|
kind: "method wrapper `__get__` of function",
|
||||||
name: function.name(db),
|
name: function.function_literal().name(db),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Type::MethodWrapper(MethodWrapperKind::PropertyDunderGet(_)) => {
|
Type::MethodWrapper(MethodWrapperKind::PropertyDunderGet(_)) => {
|
||||||
|
|
@ -1304,7 +1315,7 @@ impl<'db> BindingError<'db> {
|
||||||
) -> Option<(Span, Span)> {
|
) -> Option<(Span, Span)> {
|
||||||
match callable_ty {
|
match callable_ty {
|
||||||
Type::FunctionLiteral(function) => {
|
Type::FunctionLiteral(function) => {
|
||||||
let function_scope = function.body_scope(db);
|
let function_scope = function.function_literal().body_scope(db);
|
||||||
let span = Span::from(function_scope.file(db));
|
let span = Span::from(function_scope.file(db));
|
||||||
let node = function_scope.node(db);
|
let node = function_scope.node(db);
|
||||||
if let Some(func_def) = node.as_function() {
|
if let Some(func_def) = node.as_function() {
|
||||||
|
|
|
||||||
|
|
@ -610,7 +610,11 @@ impl<'db> ClassLiteralType<'db> {
|
||||||
self.decorators(db)
|
self.decorators(db)
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|deco| deco.into_function_literal())
|
.filter_map(|deco| deco.into_function_literal())
|
||||||
.any(|decorator| decorator.is_known(db, KnownFunction::Final))
|
.any(|decorator| {
|
||||||
|
decorator
|
||||||
|
.function_literal()
|
||||||
|
.is_known(db, KnownFunction::Final)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to resolve the [method resolution order] ("MRO") for this class.
|
/// Attempt to resolve the [method resolution order] ("MRO") for this class.
|
||||||
|
|
@ -951,7 +955,9 @@ impl<'db> ClassLiteralType<'db> {
|
||||||
Some(_),
|
Some(_),
|
||||||
"__new__" | "__init__",
|
"__new__" | "__init__",
|
||||||
) => Type::FunctionLiteral(
|
) => Type::FunctionLiteral(
|
||||||
function.with_generic_context(db, origin.generic_context(db)),
|
function
|
||||||
|
.function_literal()
|
||||||
|
.with_generic_context(db, origin.generic_context(db)),
|
||||||
),
|
),
|
||||||
_ => ty,
|
_ => ty,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,9 @@ impl<'db> InferContext<'db> {
|
||||||
|
|
||||||
// Iterate over all functions and test if any is decorated with `@no_type_check`.
|
// Iterate over all functions and test if any is decorated with `@no_type_check`.
|
||||||
function_scope_tys.any(|function_ty| {
|
function_scope_tys.any(|function_ty| {
|
||||||
function_ty.has_known_decorator(self.db, FunctionDecorators::NO_TYPE_CHECK)
|
function_ty
|
||||||
|
.function_literal()
|
||||||
|
.has_known_decorator(self.db, FunctionDecorators::NO_TYPE_CHECK)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
InNoTypeCheck::Yes => true,
|
InNoTypeCheck::Yes => true,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue