mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:10 +00:00
[red-knot] Special case @abstractmethod
for function type (#17591)
## Summary This is required because otherwise the inferred type is not going to be `Type::FunctionLiteral` but a todo type because we don't recognize `TypeVar` yet: ```py _FuncT = TypeVar("_FuncT", bound=Callable[..., Any]) def abstractmethod(funcobj: _FuncT) -> _FuncT: ... ``` This is mainly required to raise diagnostic when only some (and not all) `@overload`-ed functions are decorated with `@abstractmethod`.
This commit is contained in:
parent
e897f37911
commit
1796ca97d5
3 changed files with 41 additions and 18 deletions
|
@ -5876,10 +5876,12 @@ bitflags! {
|
|||
pub struct FunctionDecorators: u8 {
|
||||
/// `@classmethod`
|
||||
const CLASSMETHOD = 1 << 0;
|
||||
/// `@no_type_check`
|
||||
/// `@typing.no_type_check`
|
||||
const NO_TYPE_CHECK = 1 << 1;
|
||||
/// `@overload`
|
||||
/// `@typing.overload`
|
||||
const OVERLOAD = 1 << 2;
|
||||
/// `@abc.abstractmethod`
|
||||
const ABSTRACT_METHOD = 1 << 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -585,6 +585,14 @@ impl<'db> Bindings<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
Some(KnownFunction::AbstractMethod) => {
|
||||
// TODO: This can be removed once we understand legacy generics because the
|
||||
// typeshed definition for `abc.abstractmethod` is an identity function.
|
||||
if let [Some(ty)] = overload.parameter_types() {
|
||||
overload.set_return_type(*ty);
|
||||
}
|
||||
}
|
||||
|
||||
Some(KnownFunction::GetattrStatic) => {
|
||||
let [Some(instance_ty), Some(attr_name), default] =
|
||||
overload.parameter_types()
|
||||
|
|
|
@ -1482,24 +1482,37 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
for decorator in decorator_list {
|
||||
let decorator_ty = self.infer_decorator(decorator);
|
||||
|
||||
if let Type::FunctionLiteral(function) = decorator_ty {
|
||||
if function.is_known(self.db(), KnownFunction::NoTypeCheck) {
|
||||
// If the function is decorated with the `no_type_check` decorator,
|
||||
// we need to suppress any errors that come after the decorators.
|
||||
self.context.set_in_no_type_check(InNoTypeCheck::Yes);
|
||||
function_decorators |= FunctionDecorators::NO_TYPE_CHECK;
|
||||
continue;
|
||||
} else if function.is_known(self.db(), KnownFunction::Overload) {
|
||||
function_decorators |= FunctionDecorators::OVERLOAD;
|
||||
continue;
|
||||
match decorator_ty {
|
||||
Type::FunctionLiteral(function) => {
|
||||
match function.known(self.db()) {
|
||||
Some(KnownFunction::NoTypeCheck) => {
|
||||
// If the function is decorated with the `no_type_check` decorator,
|
||||
// we need to suppress any errors that come after the decorators.
|
||||
self.context.set_in_no_type_check(InNoTypeCheck::Yes);
|
||||
function_decorators |= FunctionDecorators::NO_TYPE_CHECK;
|
||||
continue;
|
||||
}
|
||||
Some(KnownFunction::Overload) => {
|
||||
function_decorators |= FunctionDecorators::OVERLOAD;
|
||||
continue;
|
||||
}
|
||||
Some(KnownFunction::AbstractMethod) => {
|
||||
function_decorators |= FunctionDecorators::ABSTRACT_METHOD;
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
} else if let Type::ClassLiteral(class) = decorator_ty {
|
||||
if class.is_known(self.db(), KnownClass::Classmethod) {
|
||||
function_decorators |= FunctionDecorators::CLASSMETHOD;
|
||||
continue;
|
||||
Type::ClassLiteral(class) => {
|
||||
if class.is_known(self.db(), KnownClass::Classmethod) {
|
||||
function_decorators |= FunctionDecorators::CLASSMETHOD;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if let Type::DataclassTransformer(params) = decorator_ty {
|
||||
dataclass_transformer_params = Some(params);
|
||||
Type::DataclassTransformer(params) => {
|
||||
dataclass_transformer_params = Some(params);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
decorator_types_and_nodes.push((decorator_ty, decorator));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue