From 5a6bfca0edaf4a773e56d5dc4f1536b0da48ace9 Mon Sep 17 00:00:00 2001 From: David Peter Date: Thu, 23 Oct 2025 10:34:51 +0200 Subject: [PATCH] [ty] Simplify `Self` for final types --- .../resources/mdtest/annotations/self.md | 18 ++++++++++++++++++ .../mdtest/exhaustiveness_checking.md | 19 +++++++++++++++++++ .../ty_python_semantic/src/types/generics.rs | 4 ++++ 3 files changed, 41 insertions(+) diff --git a/crates/ty_python_semantic/resources/mdtest/annotations/self.md b/crates/ty_python_semantic/resources/mdtest/annotations/self.md index 621db497c4..a37af2ef9f 100644 --- a/crates/ty_python_semantic/resources/mdtest/annotations/self.md +++ b/crates/ty_python_semantic/resources/mdtest/annotations/self.md @@ -508,4 +508,22 @@ def _(c: CallableTypeOf[C().method]): reveal_type(c) # revealed: (...) -> None ``` +## Final classes + +For final classes, we simplify `Self` to an instance of the class: + +```py +from typing import final, Self + +@final +class FinalClass: + def implicit_self(self) -> Self: + reveal_type(self) # revealed: FinalClass + return self + + def explicit_self(self: Self) -> Self: + reveal_type(self) # revealed: FinalClass + return self +``` + [self attribute]: https://typing.python.org/en/latest/spec/generics.html#use-in-attribute-annotations diff --git a/crates/ty_python_semantic/resources/mdtest/exhaustiveness_checking.md b/crates/ty_python_semantic/resources/mdtest/exhaustiveness_checking.md index 800ae7c7bb..6e64fdc172 100644 --- a/crates/ty_python_semantic/resources/mdtest/exhaustiveness_checking.md +++ b/crates/ty_python_semantic/resources/mdtest/exhaustiveness_checking.md @@ -379,3 +379,22 @@ def as_pattern_non_exhaustive(subject: int | str): # this diagnostic is correct: the inferred type of `subject` is `str` assert_never(subject) # error: [type-assertion-failure] ``` + +## Exhaustiveness checking for methods of enums + +```py +from enum import Enum + +class Answer(Enum): + YES = "yes" + NO = "no" + + def is_yes(self) -> bool: + reveal_type(self) # revealed: Answer + + match self: + case Answer.YES: + return True + case Answer.NO: + return False +``` diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index e8c6305d06..461e4b6fda 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -84,6 +84,10 @@ pub(crate) fn typing_self<'db>( typevar_binding_context: Option>, class: ClassLiteral<'db>, ) -> Option> { + if class.is_final(db) { + return Some(Type::instance(db, class.identity_specialization(db))); + } + let index = semantic_index(db, function_scope_id.file(db)); let identity = TypeVarIdentity::new(