[ty] Add support for @staticmethods (#18809)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / mkdocs (push) Waiting to run
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

## Summary

Add support for `@staticmethod`s. Overall, the changes are very similar
to #16305.

#18587 will be dependent on this PR for a potential fix of
https://github.com/astral-sh/ty/issues/207.

mypy_primer will look bad since the new code allows ty to check more
code.

## Test Plan

Added new markdown tests. Please comment if there's any missing tests
that I should add in, thank you.
This commit is contained in:
med1844 2025-06-20 01:38:17 -07:00 committed by GitHub
parent e180975226
commit 7982edac90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 156 additions and 8 deletions

View file

@ -284,6 +284,9 @@ impl<'db> Bindings<'db> {
}
_ => {}
}
} else if function.has_known_decorator(db, FunctionDecorators::STATICMETHOD)
{
overload.set_return_type(Type::FunctionLiteral(function));
} else if let [Some(first), _] = overload.parameter_types() {
if first.is_none(db) {
overload.set_return_type(Type::FunctionLiteral(function));
@ -319,6 +322,10 @@ impl<'db> Bindings<'db> {
_ => {}
}
} else if function
.has_known_decorator(db, FunctionDecorators::STATICMETHOD)
{
overload.set_return_type(*function_ty);
} else {
match overload.parameter_types() {
[_, Some(instance), _] if instance.is_none(db) => {

View file

@ -2119,6 +2119,7 @@ pub enum KnownClass {
Exception,
BaseExceptionGroup,
ExceptionGroup,
Staticmethod,
Classmethod,
Super,
// enum
@ -2249,6 +2250,7 @@ impl<'db> KnownClass {
// and raises a `TypeError` in Python >=3.14
// (see https://docs.python.org/3/library/constants.html#NotImplemented)
| Self::NotImplementedType
| Self::Staticmethod
| Self::Classmethod
| Self::Field
| Self::KwOnly
@ -2293,6 +2295,7 @@ impl<'db> KnownClass {
| Self::BaseExceptionGroup
| Self::Exception
| Self::ExceptionGroup
| Self::Staticmethod
| Self::Classmethod
| Self::GenericAlias
| Self::GeneratorType
@ -2355,6 +2358,7 @@ impl<'db> KnownClass {
Self::BaseExceptionGroup => "BaseExceptionGroup",
Self::Exception => "Exception",
Self::ExceptionGroup => "ExceptionGroup",
Self::Staticmethod => "staticmethod",
Self::Classmethod => "classmethod",
Self::GenericAlias => "GenericAlias",
Self::ModuleType => "ModuleType",
@ -2578,6 +2582,7 @@ impl<'db> KnownClass {
| Self::BaseExceptionGroup
| Self::Exception
| Self::ExceptionGroup
| Self::Staticmethod
| Self::Classmethod
| Self::Slice
| Self::Super
@ -2672,6 +2677,7 @@ impl<'db> KnownClass {
| Self::BaseExceptionGroup
| Self::Exception
| Self::ExceptionGroup
| Self::Staticmethod
| Self::Classmethod
| Self::GenericAlias
| Self::ModuleType
@ -2754,6 +2760,7 @@ impl<'db> KnownClass {
| Self::BaseExceptionGroup
| Self::Exception
| Self::ExceptionGroup
| Self::Staticmethod
| Self::Classmethod
| Self::TypeVar
| Self::ParamSpec
@ -2801,6 +2808,7 @@ impl<'db> KnownClass {
"BaseExceptionGroup" => Self::BaseExceptionGroup,
"Exception" => Self::Exception,
"ExceptionGroup" => Self::ExceptionGroup,
"staticmethod" => Self::Staticmethod,
"classmethod" => Self::Classmethod,
"GenericAlias" => Self::GenericAlias,
"NoneType" => Self::NoneType,
@ -2885,6 +2893,7 @@ impl<'db> KnownClass {
| Self::ExceptionGroup
| Self::EllipsisType
| Self::BaseExceptionGroup
| Self::Staticmethod
| Self::Classmethod
| Self::FunctionType
| Self::MethodType

View file

@ -103,6 +103,8 @@ bitflags! {
const ABSTRACT_METHOD = 1 << 3;
/// `@typing.final`
const FINAL = 1 << 4;
/// `@staticmethod`
const STATICMETHOD = 1 << 5;
/// `@typing.override`
const OVERRIDE = 1 << 6;
}

View file

@ -1277,8 +1277,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}
}
// TODO: Add `@staticmethod`
for (decorator, name) in [(FunctionDecorators::CLASSMETHOD, "classmethod")] {
for (decorator, name) in [
(FunctionDecorators::CLASSMETHOD, "classmethod"),
(FunctionDecorators::STATICMETHOD, "staticmethod"),
] {
let mut decorator_present = false;
let mut decorator_missing = vec![];
@ -2195,12 +2197,17 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
_ => {}
}
}
Type::ClassLiteral(class) => {
if class.is_known(self.db(), KnownClass::Classmethod) {
Type::ClassLiteral(class) => match class.known(self.db()) {
Some(KnownClass::Classmethod) => {
function_decorators |= FunctionDecorators::CLASSMETHOD;
continue;
}
}
Some(KnownClass::Staticmethod) => {
function_decorators |= FunctionDecorators::STATICMETHOD;
continue;
}
_ => {}
},
Type::DataclassTransformer(params) => {
dataclass_transformer_params = Some(params);
}