Update mdtests

This commit is contained in:
Glyphack 2025-09-05 00:02:39 +02:00
parent 178f48fc0b
commit 0b19caedec
6 changed files with 64 additions and 28 deletions

View File

@ -253,4 +253,23 @@ reveal_type(D().instance_method)
reveal_type(D.class_method) reveal_type(D.class_method)
``` ```
## Test
```py
from ty_extensions import generic_context
from typing import Generic, TypeVar
T = TypeVar("T")
U = TypeVar("U")
class C(Generic[T]):
def method(self, u: int) -> int:
return u
def generic_method(self, t: T, u: U) -> U:
return u
reveal_type(generic_context(C.method)) # revealed: tuple[Self@method]
```
[self attribute]: https://typing.python.org/en/latest/spec/generics.html#use-in-attribute-annotations [self attribute]: https://typing.python.org/en/latest/spec/generics.html#use-in-attribute-annotations

View File

@ -69,9 +69,7 @@ reveal_type(bound_method(1)) # revealed: str
When we call the function object itself, we need to pass the `instance` explicitly: When we call the function object itself, we need to pass the `instance` explicitly:
```py ```py
# error: [invalid-argument-type] C.f(1) # error: [missing-argument]
# error: [missing-argument]
C.f(1)
reveal_type(C.f(C(), 1)) # revealed: str reveal_type(C.f(C(), 1)) # revealed: str
``` ```

View File

@ -562,17 +562,17 @@ class C(Generic[T]):
return u return u
reveal_type(generic_context(C)) # revealed: tuple[T@C] reveal_type(generic_context(C)) # revealed: tuple[T@C]
reveal_type(generic_context(C.method)) # revealed: None reveal_type(generic_context(C.method)) # revealed: tuple[Self@method]
reveal_type(generic_context(C.generic_method)) # revealed: tuple[U@generic_method] reveal_type(generic_context(C.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
reveal_type(generic_context(C[int])) # revealed: None reveal_type(generic_context(C[int])) # revealed: None
reveal_type(generic_context(C[int].method)) # revealed: None reveal_type(generic_context(C[int].method)) # revealed: tuple[Self@method]
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U@generic_method] reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
c: C[int] = C[int]() c: C[int] = C[int]()
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"] reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
reveal_type(generic_context(c)) # revealed: None reveal_type(generic_context(c)) # revealed: None
reveal_type(generic_context(c.method)) # revealed: None reveal_type(generic_context(c.method)) # revealed: tuple[Self@method]
reveal_type(generic_context(c.generic_method)) # revealed: tuple[U@generic_method] reveal_type(generic_context(c.generic_method)) # revealed: tuple[Self@generic_method, U@generic_method]
``` ```
## Specializations propagate ## Specializations propagate

View File

@ -395,6 +395,7 @@ class C[T]:
def __init__[S](self, x: T, y: S) -> None: ... def __init__[S](self, x: T, y: S) -> None: ...
# TODO: need to handle self in __init__ call.
reveal_type(C(1, 1)) # revealed: C[int] reveal_type(C(1, 1)) # revealed: C[int]
reveal_type(C(1, "string")) # revealed: C[int] reveal_type(C(1, "string")) # revealed: C[int]
reveal_type(C(1, True)) # revealed: C[int] reveal_type(C(1, True)) # revealed: C[int]
@ -511,16 +512,16 @@ class C[T]:
def cannot_shadow_class_typevar[T](self, t: T): ... def cannot_shadow_class_typevar[T](self, t: T): ...
reveal_type(generic_context(C)) # revealed: tuple[T@C] reveal_type(generic_context(C)) # revealed: tuple[T@C]
reveal_type(generic_context(C.method)) # revealed: None reveal_type(generic_context(C.method)) # revealed: tuple[Self@method]
reveal_type(generic_context(C.generic_method)) # revealed: tuple[U@generic_method] reveal_type(generic_context(C.generic_method)) # revealed: tuple[U@generic_method]
reveal_type(generic_context(C[int])) # revealed: None reveal_type(generic_context(C[int])) # revealed: None
reveal_type(generic_context(C[int].method)) # revealed: None reveal_type(generic_context(C[int].method)) # revealed: tuple[Self@method]
reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U@generic_method] reveal_type(generic_context(C[int].generic_method)) # revealed: tuple[U@generic_method]
c: C[int] = C[int]() c: C[int] = C[int]()
reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"] reveal_type(c.generic_method(1, "string")) # revealed: Literal["string"]
reveal_type(generic_context(c)) # revealed: None reveal_type(generic_context(c)) # revealed: None
reveal_type(generic_context(c.method)) # revealed: None reveal_type(generic_context(c.method)) # revealed: tuple[Self@method]
reveal_type(generic_context(c.generic_method)) # revealed: tuple[U@generic_method] reveal_type(generic_context(c.generic_method)) # revealed: tuple[U@generic_method]
``` ```

View File

@ -154,7 +154,7 @@ from ty_extensions import generic_context
legacy.m("string", None) # error: [invalid-argument-type] legacy.m("string", None) # error: [invalid-argument-type]
reveal_type(legacy.m) # revealed: bound method Legacy[int].m[S](x: int, y: S@m) -> S@m reveal_type(legacy.m) # revealed: bound method Legacy[int].m[S](x: int, y: S@m) -> S@m
reveal_type(generic_context(Legacy)) # revealed: tuple[T@Legacy] reveal_type(generic_context(Legacy)) # revealed: tuple[T@Legacy]
reveal_type(generic_context(legacy.m)) # revealed: tuple[S@m] reveal_type(generic_context(legacy.m)) # revealed: tuple[Self@m, S@m]
``` ```
With PEP 695 syntax, it is clearer that the method uses a separate typevar: With PEP 695 syntax, it is clearer that the method uses a separate typevar:
@ -165,6 +165,10 @@ class C[T]:
return y return y
c: C[int] = C() c: C[int] = C()
reveal_type(c) # revealed: C[Unknown]
# reveal_type(c) # revealed: C[Unknown]
# TODO: Next line fails. The reason is the reveal type above
# error: 13 [invalid-argument-type] "Argument to bound method `m` is incorrect: Expected `Self@m`, found `C[Unknown]`"
reveal_type(c.m(1, "string")) # revealed: Literal["string"] reveal_type(c.m(1, "string")) # revealed: Literal["string"]
``` ```

View File

@ -13,42 +13,41 @@
use std::{collections::HashMap, slice::Iter}; use std::{collections::HashMap, slice::Iter};
use itertools::EitherOrBoth; use itertools::EitherOrBoth;
use ruff_db::parsed::parsed_module;
use smallvec::{SmallVec, smallvec_inline}; use smallvec::{SmallVec, smallvec_inline};
use super::TypeVarVariance;
use super::{ use super::{
DynamicType, Type, definition_expression_type, infer_definition_types, semantic_index, DynamicType, Type, TypeVarVariance, definition_expression_type, infer_definition_types,
semantic_index,
}; };
use crate::semantic_index::definition::Definition; use crate::semantic_index::definition::{Definition, DefinitionKind};
use crate::types::constraints::{ConstraintSet, Constraints, IteratorConstraintsExtension}; use crate::types::constraints::{ConstraintSet, Constraints, IteratorConstraintsExtension};
use crate::types::function::FunctionType; use crate::types::function::FunctionType;
use crate::types::generics::GenericContext; use crate::types::generics::{GenericContext, walk_generic_context};
use crate::types::generics::walk_generic_context;
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, FindLegacyTypeVarsVisitor,
HasRelationToVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind, NormalizedVisitor, HasRelationToVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind, NormalizedVisitor,
SpecialFormType, TypeMapping, TypeRelation, VarianceInferable, todo_type, SpecialFormType, TypeMapping, TypeRelation, VarianceInferable, todo_type,
}; };
use crate::{Db, FxOrderSet}; use crate::{Db, FxOrderSet};
use ruff_db::parsed::parsed_module;
use ruff_python_ast::{self as ast, name::Name}; use ruff_python_ast::{self as ast, name::Name};
fn infer_method_type<'db>( fn infer_method_type<'db>(
db: &'db dyn Db, db: &'db dyn Db,
definition: Definition<'db>, definition: Definition<'db>,
) -> Option<FunctionType<'db>> { ) -> Option<FunctionType<'db>> {
let scope_id = definition.scope(db); let class_scope_id = definition.scope(db);
let file = scope_id.file(db); let file = class_scope_id.file(db);
let index = semantic_index(db, file); let index = semantic_index(db, file);
let module = parsed_module(db, file).load(db); let module = parsed_module(db, file).load(db);
let method_scope = index.scope(scope_id.file_scope_id(db)); let DefinitionKind::Function(func_def) = definition.kind(db) else {
let method = method_scope.node().as_function(&module)?; return None;
let parent_scope_id = method_scope.parent()?; };
let parent_scope = index.scope(parent_scope_id); let class_scope = index.scope(class_scope_id.file_scope_id(db));
parent_scope.node().as_class(&module)?; class_scope.node().as_class(&module)?;
let method_definition = index.expect_single_definition(method); let method_definition = index.expect_single_definition(func_def.node(&module));
let func_type = infer_definition_types(db, method_definition) let func_type = infer_definition_types(db, method_definition)
.declaration_type(method_definition) .declaration_type(method_definition)
.inner_type() .inner_type()
@ -384,6 +383,7 @@ impl<'db> Signature<'db> {
) -> Self { ) -> Self {
let parameters = let parameters =
Parameters::from_parameters(db, definition, function_node.parameters.as_ref()); Parameters::from_parameters(db, definition, function_node.parameters.as_ref());
let return_ty = function_node.returns.as_ref().map(|returns| { let return_ty = function_node.returns.as_ref().map(|returns| {
let plain_return_ty = definition_expression_type(db, definition, returns.as_ref()) let plain_return_ty = definition_expression_type(db, definition, returns.as_ref())
.apply_type_mapping( .apply_type_mapping(
@ -1194,17 +1194,30 @@ impl<'db> Parameters<'db> {
}, },
) )
}); });
let method_type = infer_method_type(db, definition); let method_type = infer_method_type(db, definition);
let is_method = method_type.is_some();
let is_classmethod = method_type.is_some_and(|f| f.is_classmethod(db)); let is_classmethod = method_type.is_some_and(|f| f.is_classmethod(db));
let is_staticmethod = method_type.is_some_and(|f| f.is_staticmethod(db)); let is_staticmethod = method_type.is_some_and(|f| f.is_staticmethod(db));
let positional_or_keyword = args.iter().map(|arg| { let positional_or_keyword = args.iter().map(|arg| {
// TODO(https://github.com/astral-sh/ty/issues/159): Also set the type for `cls` argument // TODO(https://github.com/astral-sh/ty/issues/159): Also set the type for `cls` argument
if !is_staticmethod // eprintln!(
// "arg {}: pos: {:?}, is_static: {}, is_class: {}, anno: {:?}",
// arg.name().id.as_str(),
// parameters.index(arg.name().id()),
// is_staticmethod,
// is_classmethod,
// arg.parameter.annotation()
// );
// dbg!(is_method);
if is_method
&& !is_staticmethod
&& !is_classmethod && !is_classmethod
&& arg.parameter.annotation().is_none() && arg.parameter.annotation().is_none()
&& parameters.index(arg.name().id()) == Some(0) && parameters.index(arg.name().id()) == Some(0)
{ {
eprintln!("arg {} is self", arg.name().id.as_str());
let implicit_annotation = Type::SpecialForm(SpecialFormType::TypingSelf) let implicit_annotation = Type::SpecialForm(SpecialFormType::TypingSelf)
.in_type_expression(db, definition.scope(db), Some(definition)) .in_type_expression(db, definition.scope(db), Some(definition))
.ok(); .ok();
@ -1218,6 +1231,7 @@ impl<'db> Parameters<'db> {
form: ParameterForm::Value, form: ParameterForm::Value,
} }
} else { } else {
eprintln!("arg {} is not self", arg.name().id.as_str());
Parameter::from_node_and_kind( Parameter::from_node_and_kind(
db, db,
definition, definition,