Skip to content

Commit 92c666b

Browse files
committed
Handle when scope is unexpected
1 parent 0a7d8d2 commit 92c666b

File tree

4 files changed

+62
-61
lines changed

4 files changed

+62
-61
lines changed

crates/ty_python_semantic/resources/mdtest/annotations/callable.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ def _(c: Callable[[int, Unpack[Ts]], int]):
300300
from typing import Callable
301301

302302
def _(c: Callable[[int], int]):
303-
reveal_type(c.__init__) # revealed: def __init__(self) -> None
303+
reveal_type(c.__init__) # revealed: def __init__(self: Self) -> None
304304
reveal_type(c.__class__) # revealed: type
305305
reveal_type(c.__call__) # revealed: (int, /) -> int
306306
```

crates/ty_python_semantic/resources/mdtest/annotations/self.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,5 +130,6 @@ class Container(Generic[T]):
130130
def set_value(self: Self, value: T) -> Self:
131131
return self
132132

133-
reveal_type(Shape().implicit_self()) # revealed: Shape
133+
c = Container[int]()
134+
reveal_type(c.set_value(1)) # revealed: Container[int]
134135
```

crates/ty_python_semantic/src/types/infer.rs

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,53 +2132,51 @@ impl<'db> TypeInferenceBuilder<'db> {
21322132
let ty = if let Some(default_ty) = default_ty {
21332133
UnionType::from_elements(self.db(), [Type::unknown(), default_ty])
21342134
} else {
2135-
let func =
2136-
enclosing_function_definition(self.db(), self.index, self.scope()).unwrap();
2137-
let DefinitionKind::Function(func_def) = func.kind(self.db()) else {
2138-
panic!("Expected function definition");
2139-
};
2140-
let func_type =
2141-
enclosing_function_type(self.db(), self.index, self.scope()).unwrap();
2142-
// TODO: Enum or something better
2143-
let mut first_self_or_cls = 1;
2144-
2145-
if func_type.has_known_decorator(self.db(), FunctionDecorators::CLASSMETHOD) {
2146-
first_self_or_cls = 2;
2147-
}
2148-
// if func_type
2149-
// .has_known_decorator(self.db(), FunctionDecorators::STATICMETHOD)
2150-
// {
2151-
// first_self_or_cls = 3;
2152-
// }
2153-
2154-
// If this parameter is the first parameter
2155-
if func_def
2156-
.parameters
2157-
.index(parameter.name())
2158-
.is_some_and(|index| index == 0)
2159-
{
2160-
// if the first parameter is `self`
2161-
if first_self_or_cls == 1 {
2162-
let ty = Type::KnownInstance(KnownInstanceType::TypingSelf)
2163-
.in_type_expression(self.db(), self.scope())
2164-
.unwrap();
2165-
ty
2166-
// TODO: if the first parameter is `cls`
2167-
} else if first_self_or_cls == 2 {
2168-
// type[Self]
2169-
Type::unknown()
2170-
// TODO: This is a static method
2171-
} else {
2172-
Type::unknown()
2173-
}
2174-
} else {
2175-
Type::unknown()
2176-
}
2135+
self.special_first_method_argument(parameter)
21772136
};
21782137
self.add_binding(parameter.into(), definition, ty);
21792138
}
21802139
}
21812140

2141+
// Extract this logic into a separate function
2142+
fn special_first_method_argument(&self, parameter: &ast::Parameter) -> Type<'db> {
2143+
let Some(func_type) = enclosing_function_type(self.db(), self.index, self.scope()) else {
2144+
return Type::unknown();
2145+
};
2146+
let Some(func) = enclosing_function_definition(self.db(), self.index, self.scope()) else {
2147+
return Type::unknown();
2148+
};
2149+
let DefinitionKind::Function(func_def) = func.kind(self.db()) else {
2150+
return Type::unknown();
2151+
};
2152+
2153+
let method_type =
2154+
if func_type.has_known_decorator(self.db(), FunctionDecorators::CLASSMETHOD) {
2155+
2 // cls
2156+
// } else if func_type.has_known_decorator(self.db(), FunctionDecorators::STATICMETHOD) {
2157+
// 3 // static
2158+
} else {
2159+
1 // self
2160+
};
2161+
2162+
let is_first_param = func_def
2163+
.parameters
2164+
.index(parameter.name())
2165+
.is_some_and(|index| index == 0);
2166+
2167+
if !is_first_param {
2168+
return Type::unknown();
2169+
}
2170+
2171+
match method_type {
2172+
1 => Type::KnownInstance(KnownInstanceType::TypingSelf)
2173+
.in_type_expression(self.db(), self.scope())
2174+
.unwrap_or_else(|_| Type::unknown()),
2175+
2 => Type::unknown(), // TODO: type[Self]
2176+
_ => Type::unknown(), // TODO: This is a static method
2177+
}
2178+
}
2179+
21822180
/// Set initial declared/inferred types for a `*args` variadic positional parameter.
21832181
///
21842182
/// The annotated type is implicitly wrapped in a homogeneous tuple.

crates/ty_python_semantic/src/types/signatures.rs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,29 +1044,31 @@ impl<'db> Parameters<'db> {
10441044
} else {
10451045
false
10461046
};
1047-
let classmethod = if matches!(definition.kind(db), DefinitionKind::Function(_)) {
1048-
let result = infer_definition_types(db, definition);
1049-
match result.declaration_type(definition).inner_type() {
1050-
Type::FunctionLiteral(t) => {
1051-
t.decorators(db).contains(FunctionDecorators::CLASSMETHOD)
1047+
let classmethod = if let DefinitionKind::Function(f) = definition.kind(db) {
1048+
if f.name.id() == "__new__" {
1049+
true
1050+
} else {
1051+
let result = infer_definition_types(db, definition);
1052+
match result.declaration_type(definition).inner_type() {
1053+
Type::FunctionLiteral(t) => {
1054+
t.decorators(db).contains(FunctionDecorators::CLASSMETHOD)
1055+
}
1056+
_ => false,
10521057
}
1053-
_ => false,
10541058
}
10551059
} else {
10561060
false
10571061
};
10581062
let positional_or_keyword = args.iter().enumerate().map(|(index, arg)| {
1059-
if index == 0 && function_is_method && arg.parameter.annotation().is_none() {
1060-
let implicit_annotation = if classmethod {
1061-
Type::KnownInstance(KnownInstanceType::TypingSelf)
1062-
.to_meta_type(db)
1063-
.in_type_expression(db, definition.scope(db))
1064-
.unwrap()
1065-
} else {
1066-
Type::KnownInstance(KnownInstanceType::TypingSelf)
1067-
.in_type_expression(db, definition.scope(db))
1068-
.unwrap()
1069-
};
1063+
if index == 0
1064+
&& function_is_method
1065+
&& arg.parameter.annotation().is_none()
1066+
// TODO: Handle type of cls
1067+
&& !classmethod
1068+
{
1069+
let implicit_annotation = Type::KnownInstance(KnownInstanceType::TypingSelf)
1070+
.in_type_expression(db, definition.scope(db))
1071+
.unwrap();
10701072
Parameter {
10711073
annotated_type: Some(implicit_annotation),
10721074
kind: ParameterKind::PositionalOrKeyword {

0 commit comments

Comments
 (0)