Skip to content

Commit 0c3d16a

Browse files
committed
Better tests, address review comments
1 parent 634d59c commit 0c3d16a

File tree

2 files changed

+54
-27
lines changed
  • crates/ty_python_semantic

2 files changed

+54
-27
lines changed

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

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ In instance methods, the first parameter (regardless of its name) is assumed to
5656

5757
```toml
5858
[environment]
59-
python-version = "3.11"
59+
python-version = "3.12"
6060
```
6161

6262
```py
@@ -68,11 +68,26 @@ class A:
6868

6969
return self
7070

71-
def a_method(self) -> int:
72-
def first_arg_is_not_self(a: int) -> int:
71+
def implicit_self_generic[T](self, x: T) -> T:
72+
reveal_type(self) # revealed: Self@implicit_self_generic
73+
74+
return x
75+
76+
def method_a(self) -> None:
77+
def first_param_is_not_self(a: int):
7378
reveal_type(a) # revealed: int
74-
return a
75-
return first_arg_is_not_self(1)
79+
reveal_type(self) # revealed: Self@method_a
80+
81+
def first_param_is_not_self_unannotated(a):
82+
reveal_type(a) # revealed: Unknown
83+
reveal_type(self) # revealed: Self@method_a
84+
85+
def first_param_is_also_not_self(self) -> None:
86+
reveal_type(self) # revealed: Unknown
87+
88+
def first_param_is_explicit_self(this: Self) -> None:
89+
reveal_type(this) # revealed: Self@method_a
90+
reveal_type(self) # revealed: Self@method_a
7691

7792
@classmethod
7893
def a_classmethod(cls) -> Self:

crates/ty_python_semantic/src/types/infer/builder.rs

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{iter, mem};
33
use itertools::{Either, Itertools};
44
use ruff_db::diagnostic::{Annotation, DiagnosticId, Severity};
55
use ruff_db::files::File;
6-
use ruff_db::parsed::{ParsedModuleRef, parsed_module};
6+
use ruff_db::parsed::ParsedModuleRef;
77
use ruff_python_ast::visitor::{Visitor, walk_expr};
88
use ruff_python_ast::{self as ast, AnyNodeRef, ExprContext, PythonVersion};
99
use ruff_python_stdlib::builtins::version_builtin_was_added;
@@ -83,7 +83,7 @@ use crate::types::generics::{
8383
GenericContext, InferableTypeVars, LegacyGenericBase, SpecializationBuilder, bind_typevar,
8484
enclosing_generic_contexts, typing_self,
8585
};
86-
use crate::types::infer::{nearest_enclosing_class, nearest_enclosing_function};
86+
use crate::types::infer::nearest_enclosing_function;
8787
use crate::types::instance::SliceLiteral;
8888
use crate::types::mro::MroErrorKind;
8989
use crate::types::signatures::Signature;
@@ -2543,45 +2543,57 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
25432543
parameter: &ast::Parameter,
25442544
) -> Option<Type<'db>> {
25452545
let db = self.db();
2546-
let scope_id = self.scope();
2547-
let file = scope_id.file(db);
2548-
let function_scope = scope_id.scope(db);
2549-
let method = function_scope.node().as_function()?;
2546+
let file = self.file();
25502547

2551-
let parent_scope_id = function_scope.parent()?;
2552-
let parent_scope = self.index.scope(parent_scope_id);
2553-
parent_scope.node().as_class()?;
2548+
let function_scope_id = self.scope();
2549+
let function_scope = function_scope_id.scope(db);
2550+
let function = function_scope.node().as_function()?;
25542551

2555-
let method_definition = self.index.expect_single_definition(method);
2552+
let parent_file_scope_id = function_scope.parent()?;
2553+
let mut parent_scope_id = parent_file_scope_id.to_scope_id(db, file);
2554+
2555+
// Skip type parameter scopes, if the method itself is generic.
2556+
if parent_scope_id.is_annotation(db) {
2557+
let parent_scope = parent_scope_id.scope(db);
2558+
parent_scope_id = parent_scope.parent()?.to_scope_id(db, file);
2559+
}
2560+
2561+
// Return early if this is not a method inside a class.
2562+
let class = parent_scope_id.scope(db).node().as_class()?;
2563+
2564+
let method_definition = self.index.expect_single_definition(function);
25562565
let DefinitionKind::Function(function_definition) = method_definition.kind(db) else {
25572566
return None;
25582567
};
25592568

2560-
let func_type = infer_definition_types(db, method_definition)
2561-
.declaration_type(method_definition)
2562-
.inner_type()
2563-
.as_function_literal()?;
2564-
2565-
let module = parsed_module(db, file).load(db);
25662569
if function_definition
2567-
.node(&module)
2570+
.node(self.module())
25682571
.parameters
25692572
.index(parameter.name())
2570-
.is_some_and(|index| index != 0)
2573+
.is_none_or(|index| index != 0)
25712574
{
25722575
return None;
25732576
}
25742577

2575-
if func_type.is_classmethod(db) {
2578+
let method = infer_definition_types(db, method_definition)
2579+
.declaration_type(method_definition)
2580+
.inner_type()
2581+
.as_function_literal()?;
2582+
2583+
if method.is_classmethod(db) {
25762584
// TODO: set the type for `cls` argument
25772585
return None;
2578-
} else if func_type.is_staticmethod(db) {
2586+
} else if method.is_staticmethod(db) {
25792587
return None;
25802588
}
25812589

2582-
let class = nearest_enclosing_class(db, self.index, scope_id).unwrap();
2590+
let class_definition = self.index.expect_single_definition(class);
2591+
let class_literal = infer_definition_types(db, class_definition)
2592+
.declaration_type(class_definition)
2593+
.inner_type()
2594+
.as_class_literal()?;
25832595

2584-
typing_self(db, self.scope(), Some(method_definition), class)
2596+
typing_self(db, self.scope(), Some(method_definition), class_literal)
25852597
}
25862598

25872599
/// Set initial declared/inferred types for a `*args` variadic positional parameter.

0 commit comments

Comments
 (0)