diff --git a/crates/ty_ide/src/inlay_hints.rs b/crates/ty_ide/src/inlay_hints.rs index d0742f58ec..4f9858e61e 100644 --- a/crates/ty_ide/src/inlay_hints.rs +++ b/crates/ty_ide/src/inlay_hints.rs @@ -6017,9 +6017,9 @@ mod tests { fn test_function_signature_inlay_hint() { let mut test = inlay_hint_test( " - def foo(x: int, *y: bool, z: str | int | list[str]): ... + def foo(x: int, *y: bool, z: str | int | list[str]): ... - a = foo", + a = foo", ); assert_snapshot!(test.inlay_hints(), @r#" @@ -6158,18 +6158,35 @@ mod tests { fn test_module_inlay_hint() { let mut test = inlay_hint_test( " - import foo + import foo - a = foo", + a = foo", ); test.with_extra_file("foo.py", "'''Foo module'''"); - assert_snapshot!(test.inlay_hints(), @r" + assert_snapshot!(test.inlay_hints(), @r#" import foo a[: ] = foo --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/types.pyi:423:7 + | + 422 | @disjoint_base + 423 | class ModuleType: + | ^^^^^^^^^^ + 424 | """Create a module object. + | + info: Source + --> main2.py:4:6 + | + 2 | import foo + 3 | + 4 | a[: ] = foo + | ^^^^^^ + | + info[inlay-hint-location]: Inlay Hint Target --> foo.py:1:1 | @@ -6177,32 +6194,531 @@ mod tests { | ^^^^^^^^^^^^^^^^ | info: Source - --> main2.py:4:5 + --> main2.py:4:14 | 2 | import foo 3 | 4 | a[: ] = foo - | ^^^^^^^^^^^^^^ + | ^^^ | - "); + "#); } #[test] fn test_literal_type_alias_inlay_hint() { let mut test = inlay_hint_test( " - from typing import Literal + from typing import Literal - a = Literal['a', 'b', 'c']", + a = Literal['a', 'b', 'c']", ); assert_snapshot!(test.inlay_hints(), @r#" from typing import Literal a[: ] = Literal['a', 'b', 'c'] + --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:351:1 + | + 349 | Final: _SpecialForm + 350 | + 351 | Literal: _SpecialForm + | ^^^^^^^ + 352 | TypedDict: _SpecialForm + | + info: Source + --> main2.py:4:20 + | + 2 | from typing import Literal + 3 | + 4 | a[: ] = Literal['a', 'b', 'c'] + | ^^^^^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/builtins.pyi:915:7 + | + 914 | @disjoint_base + 915 | class str(Sequence[str]): + | ^^^ + 916 | """str(object='') -> str + 917 | str(bytes_or_buffer[, encoding[, errors]]) -> str + | + info: Source + --> main2.py:4:28 + | + 2 | from typing import Literal + 3 | + 4 | a[: ] = Literal['a', 'b', 'c'] + | ^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/builtins.pyi:915:7 + | + 914 | @disjoint_base + 915 | class str(Sequence[str]): + | ^^^ + 916 | """str(object='') -> str + 917 | str(bytes_or_buffer[, encoding[, errors]]) -> str + | + info: Source + --> main2.py:4:33 + | + 2 | from typing import Literal + 3 | + 4 | a[: ] = Literal['a', 'b', 'c'] + | ^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/builtins.pyi:915:7 + | + 914 | @disjoint_base + 915 | class str(Sequence[str]): + | ^^^ + 916 | """str(object='') -> str + 917 | str(bytes_or_buffer[, encoding[, errors]]) -> str + | + info: Source + --> main2.py:4:38 + | + 2 | from typing import Literal + 3 | + 4 | a[: ] = Literal['a', 'b', 'c'] + | ^^^ + | "#); } + #[test] + fn test_wrapper_descriptor_inlay_hint() { + let mut test = inlay_hint_test( + " + from types import FunctionType + + a = FunctionType.__get__", + ); + + assert_snapshot!(test.inlay_hints(), @r#" + from types import FunctionType + + a[: ] = FunctionType.__get__ + --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/types.pyi:670:7 + | + 669 | @final + 670 | class WrapperDescriptorType: + | ^^^^^^^^^^^^^^^^^^^^^ + 671 | @property + 672 | def __name__(self) -> str: ... + | + info: Source + --> main2.py:4:6 + | + 2 | from types import FunctionType + 3 | + 4 | a[: ] = FunctionType.__get__ + | ^^^^^^^^^^^^^^^^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/types.pyi:77:7 + | + 75 | # Make sure this class definition stays roughly in line with `builtins.function` + 76 | @final + 77 | class FunctionType: + | ^^^^^^^^^^^^ + 78 | """Create a function object. + | + info: Source + --> main2.py:4:39 + | + 2 | from types import FunctionType + 3 | + 4 | a[: ] = FunctionType.__get__ + | ^^^^^^^^ + | + "#); + } + + #[test] + fn test_method_wrapper_inlay_hint() { + let mut test = inlay_hint_test( + " + def f(): ... + + a = f.__call__", + ); + + assert_snapshot!(test.inlay_hints(), @r#" + def f(): ... + + a[: ] = f.__call__ + --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/types.pyi:684:7 + | + 683 | @final + 684 | class MethodWrapperType: + | ^^^^^^^^^^^^^^^^^ + 685 | @property + 686 | def __self__(self) -> object: ... + | + info: Source + --> main2.py:4:6 + | + 2 | def f(): ... + 3 | + 4 | a[: ] = f.__call__ + | ^^^^^^^^^^^^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/types.pyi:134:9 + | + 132 | ) -> Self: ... + 133 | + 134 | def __call__(self, *args: Any, **kwargs: Any) -> Any: + | ^^^^^^^^ + 135 | """Call self as a function.""" + | + info: Source + --> main2.py:4:22 + | + 2 | def f(): ... + 3 | + 4 | a[: ] = f.__call__ + | ^^^^^^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/types.pyi:77:7 + | + 75 | # Make sure this class definition stays roughly in line with `builtins.function` + 76 | @final + 77 | class FunctionType: + | ^^^^^^^^^^^^ + 78 | """Create a function object. + | + info: Source + --> main2.py:4:35 + | + 2 | def f(): ... + 3 | + 4 | a[: ] = f.__call__ + | ^^^^^^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> main.py:2:5 + | + 2 | def f(): ... + | ^ + 3 | + 4 | a = f.__call__ + | + info: Source + --> main2.py:4:45 + | + 2 | def f(): ... + 3 | + 4 | a[: ] = f.__call__ + | ^ + | + "#); + } + + #[test] + fn test_newtype_inlay_hint() { + let mut test = inlay_hint_test( + " + from typing import NewType + + N = NewType('N', str) + + Y = N", + ); + + assert_snapshot!(test.inlay_hints(), @r#" + from typing import NewType + + N[: ] = NewType([name=]'N', [tp=]str) + + Y[: ] = N + --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:615:11 + | + 613 | TypeGuard: _SpecialForm + 614 | + 615 | class NewType: + | ^^^^^^^ + 616 | """NewType creates simple unique types with almost zero runtime overhead. + | + info: Source + --> main2.py:4:6 + | + 2 | from typing import NewType + 3 | + 4 | N[: ] = NewType([name=]'N', [tp=]str) + | ^^^^^^^ + 5 | + 6 | Y[: ] = N + | + + info[inlay-hint-location]: Inlay Hint Target + --> main.py:4:1 + | + 2 | from typing import NewType + 3 | + 4 | N = NewType('N', str) + | ^ + 5 | + 6 | Y = N + | + info: Source + --> main2.py:4:28 + | + 2 | from typing import NewType + 3 | + 4 | N[: ] = NewType([name=]'N', [tp=]str) + | ^ + 5 | + 6 | Y[: ] = N + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:637:28 + | + 635 | """ + 636 | + 637 | def __init__(self, name: str, tp: Any) -> None: ... # AnnotationForm + | ^^^^ + 638 | if sys.version_info >= (3, 11): + 639 | @staticmethod + | + info: Source + --> main2.py:4:44 + | + 2 | from typing import NewType + 3 | + 4 | N[: ] = NewType([name=]'N', [tp=]str) + | ^^^^ + 5 | + 6 | Y[: ] = N + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:637:39 + | + 635 | """ + 636 | + 637 | def __init__(self, name: str, tp: Any) -> None: ... # AnnotationForm + | ^^ + 638 | if sys.version_info >= (3, 11): + 639 | @staticmethod + | + info: Source + --> main2.py:4:56 + | + 2 | from typing import NewType + 3 | + 4 | N[: ] = NewType([name=]'N', [tp=]str) + | ^^ + 5 | + 6 | Y[: ] = N + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:615:11 + | + 613 | TypeGuard: _SpecialForm + 614 | + 615 | class NewType: + | ^^^^^^^ + 616 | """NewType creates simple unique types with almost zero runtime overhead. + | + info: Source + --> main2.py:6:6 + | + 4 | N[: ] = NewType([name=]'N', [tp=]str) + 5 | + 6 | Y[: ] = N + | ^^^^^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> main.py:4:1 + | + 2 | from typing import NewType + 3 | + 4 | N = NewType('N', str) + | ^ + 5 | + 6 | Y = N + | + info: Source + --> main2.py:6:28 + | + 4 | N[: ] = NewType([name=]'N', [tp=]str) + 5 | + 6 | Y[: ] = N + | ^ + | + "#); + } + + #[test] + fn test_meta_typevar_inlay_hint() { + let mut test = inlay_hint_test( + " + def f[T](x: type[T]): + y = x", + ); + + assert_snapshot!(test.inlay_hints(), @r#" + def f[T](x: type[T]): + y[: type[T@f]] = x + --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/builtins.pyi:247:7 + | + 246 | @disjoint_base + 247 | class type: + | ^^^^ + 248 | """type(object) -> the object's type + 249 | type(name, bases, dict, **kwds) -> a new type + | + info: Source + --> main2.py:3:9 + | + 2 | def f[T](x: type[T]): + 3 | y[: type[T@f]] = x + | ^^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> main.py:2:7 + | + 2 | def f[T](x: type[T]): + | ^ + 3 | y = x + | + info: Source + --> main2.py:3:14 + | + 2 | def f[T](x: type[T]): + 3 | y[: type[T@f]] = x + | ^^^ + | + + --------------------------------------------- + info[inlay-hint-edit]: File after edits + info: Source + + def f[T](x: type[T]): + y: type[T@f] = x + "#); + } + + #[test] + fn test_subscripted_protocol_inlay_hint() { + let mut test = inlay_hint_test( + " + from typing import Protocol, TypeVar + T = TypeVar('T') + Strange = Protocol[T]", + ); + + assert_snapshot!(test.inlay_hints(), @r" + from typing import Protocol, TypeVar + T[: typing.TypeVar] = TypeVar([name=]'T') + Strange[: ] = Protocol[T] + --------------------------------------------- + info[inlay-hint-location]: Inlay Hint Target + --> main.py:3:1 + | + 2 | from typing import Protocol, TypeVar + 3 | T = TypeVar('T') + | ^ + 4 | Strange = Protocol[T] + | + info: Source + --> main2.py:3:5 + | + 2 | from typing import Protocol, TypeVar + 3 | T[: typing.TypeVar] = TypeVar([name=]'T') + | ^^^^^^^^^^^^^^ + 4 | Strange[: ] = Protocol[T] + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:276:13 + | + 274 | def __new__( + 275 | cls, + 276 | name: str, + | ^^^^ + 277 | *constraints: Any, # AnnotationForm + 278 | bound: Any | None = None, # AnnotationForm + | + info: Source + --> main2.py:3:32 + | + 2 | from typing import Protocol, TypeVar + 3 | T[: typing.TypeVar] = TypeVar([name=]'T') + | ^^^^ + 4 | Strange[: ] = Protocol[T] + | + + info[inlay-hint-location]: Inlay Hint Target + --> stdlib/typing.pyi:341:1 + | + 340 | Union: _SpecialForm + 341 | Protocol: _SpecialForm + | ^^^^^^^^ + 342 | Callable: _SpecialForm + 343 | Type: _SpecialForm + | + info: Source + --> main2.py:4:26 + | + 2 | from typing import Protocol, TypeVar + 3 | T[: typing.TypeVar] = TypeVar([name=]'T') + 4 | Strange[: ] = Protocol[T] + | ^^^^^^^^^^^^^^^ + | + + info[inlay-hint-location]: Inlay Hint Target + --> main.py:3:1 + | + 2 | from typing import Protocol, TypeVar + 3 | T = TypeVar('T') + | ^ + 4 | Strange = Protocol[T] + | + info: Source + --> main2.py:4:42 + | + 2 | from typing import Protocol, TypeVar + 3 | T[: typing.TypeVar] = TypeVar([name=]'T') + 4 | Strange[: ] = Protocol[T] + | ^ + | + + --------------------------------------------- + info[inlay-hint-edit]: File after edits + info: Source + + from typing import Protocol, TypeVar + T: typing.TypeVar = TypeVar('T') + Strange = Protocol[T] + "); + } + struct InlayHintLocationDiagnostic { source: FileRange, target: FileRange, diff --git a/crates/ty_python_semantic/resources/mdtest/attributes.md b/crates/ty_python_semantic/resources/mdtest/attributes.md index f2eb886223..3c28431d58 100644 --- a/crates/ty_python_semantic/resources/mdtest/attributes.md +++ b/crates/ty_python_semantic/resources/mdtest/attributes.md @@ -2162,8 +2162,8 @@ Some attributes are special-cased, however: import types from ty_extensions import static_assert, TypeOf, is_subtype_of -reveal_type(f.__get__) # revealed: -reveal_type(f.__call__) # revealed: +reveal_type(f.__get__) # revealed: +reveal_type(f.__call__) # revealed: static_assert(is_subtype_of(TypeOf[f.__get__], types.MethodWrapperType)) static_assert(is_subtype_of(TypeOf[f.__call__], types.MethodWrapperType)) ``` diff --git a/crates/ty_python_semantic/resources/mdtest/call/methods.md b/crates/ty_python_semantic/resources/mdtest/call/methods.md index 0536ded1e6..a2c7d043b4 100644 --- a/crates/ty_python_semantic/resources/mdtest/call/methods.md +++ b/crates/ty_python_semantic/resources/mdtest/call/methods.md @@ -34,7 +34,8 @@ from inspect import getattr_static reveal_type(getattr_static(C, "f")) # revealed: def f(self, x: int) -> str -reveal_type(getattr_static(C, "f").__get__) # revealed: +# revealed: +reveal_type(getattr_static(C, "f").__get__) reveal_type(getattr_static(C, "f").__get__(None, C)) # revealed: def f(self, x: int) -> str reveal_type(getattr_static(C, "f").__get__(C(), C)) # revealed: bound method C.f(x: int) -> str @@ -258,7 +259,7 @@ class C: method_wrapper = getattr_static(C, "f").__get__ -reveal_type(method_wrapper) # revealed: +reveal_type(method_wrapper) # revealed: # All of these are fine: method_wrapper(C(), C) @@ -414,7 +415,8 @@ class C: def f(cls): ... reveal_type(getattr_static(C, "f")) # revealed: def f(cls) -> Unknown -reveal_type(getattr_static(C, "f").__get__) # revealed: +# revealed: +reveal_type(getattr_static(C, "f").__get__) ``` But we correctly model how the `classmethod` descriptor works: @@ -632,7 +634,7 @@ class MyClass: static_assert(is_assignable_to(types.FunctionType, Callable)) -# revealed: +# revealed: reveal_type(types.FunctionType.__get__) static_assert(is_assignable_to(TypeOf[types.FunctionType.__get__], Callable)) @@ -640,7 +642,7 @@ static_assert(is_assignable_to(TypeOf[types.FunctionType.__get__], Callable)) reveal_type(f) static_assert(is_assignable_to(TypeOf[f], Callable)) -# revealed: +# revealed: reveal_type(f.__get__) static_assert(is_assignable_to(TypeOf[f.__get__], Callable)) @@ -648,11 +650,11 @@ static_assert(is_assignable_to(TypeOf[f.__get__], Callable)) reveal_type(types.FunctionType.__call__) static_assert(is_assignable_to(TypeOf[types.FunctionType.__call__], Callable)) -# revealed: +# revealed: reveal_type(f.__call__) static_assert(is_assignable_to(TypeOf[f.__call__], Callable)) -# revealed: +# revealed: reveal_type(property.__get__) static_assert(is_assignable_to(TypeOf[property.__get__], Callable)) @@ -661,15 +663,15 @@ reveal_type(MyClass.my_property) static_assert(is_assignable_to(TypeOf[property], Callable)) static_assert(not is_assignable_to(TypeOf[MyClass.my_property], Callable)) -# revealed: +# revealed: reveal_type(MyClass.my_property.__get__) static_assert(is_assignable_to(TypeOf[MyClass.my_property.__get__], Callable)) -# revealed: +# revealed: reveal_type(property.__set__) static_assert(is_assignable_to(TypeOf[property.__set__], Callable)) -# revealed: +# revealed: reveal_type(MyClass.my_property.__set__) static_assert(is_assignable_to(TypeOf[MyClass.my_property.__set__], Callable)) @@ -677,7 +679,7 @@ static_assert(is_assignable_to(TypeOf[MyClass.my_property.__set__], Callable)) reveal_type(str.startswith) static_assert(is_assignable_to(TypeOf[str.startswith], Callable)) -# revealed: +# revealed: reveal_type("foo".startswith) static_assert(is_assignable_to(TypeOf["foo".startswith], Callable)) diff --git a/crates/ty_python_semantic/resources/mdtest/descriptor_protocol.md b/crates/ty_python_semantic/resources/mdtest/descriptor_protocol.md index 3b90696928..03110eac70 100644 --- a/crates/ty_python_semantic/resources/mdtest/descriptor_protocol.md +++ b/crates/ty_python_semantic/resources/mdtest/descriptor_protocol.md @@ -596,14 +596,14 @@ def f(x: object) -> str: return "a" reveal_type(f) # revealed: def f(x: object) -> str -reveal_type(f.__get__) # revealed: +reveal_type(f.__get__) # revealed: static_assert(is_subtype_of(TypeOf[f.__get__], types.MethodWrapperType)) reveal_type(f.__get__(None, type(f))) # revealed: def f(x: object) -> str reveal_type(f.__get__(None, type(f))(1)) # revealed: str wrapper_descriptor = getattr_static(f, "__get__") -reveal_type(wrapper_descriptor) # revealed: +reveal_type(wrapper_descriptor) # revealed: reveal_type(wrapper_descriptor(f, None, type(f))) # revealed: def f(x: object) -> str static_assert(is_subtype_of(TypeOf[wrapper_descriptor], types.WrapperDescriptorType)) diff --git a/crates/ty_python_semantic/resources/mdtest/generics/scoping.md b/crates/ty_python_semantic/resources/mdtest/generics/scoping.md index 31c04e0b37..bcc807e609 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/scoping.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/scoping.md @@ -102,7 +102,7 @@ class C[T]: return "a" reveal_type(getattr_static(C[int], "f")) # revealed: def f(self, x: int) -> str -reveal_type(getattr_static(C[int], "f").__get__) # revealed: +reveal_type(getattr_static(C[int], "f").__get__) # revealed: reveal_type(getattr_static(C[int], "f").__get__(None, C[int])) # revealed: def f(self, x: int) -> str # revealed: bound method C[int].f(x: int) -> str reveal_type(getattr_static(C[int], "f").__get__(C[int](), C[int])) diff --git a/crates/ty_python_semantic/resources/mdtest/properties.md b/crates/ty_python_semantic/resources/mdtest/properties.md index 6f1e2215ef..f0f88ae050 100644 --- a/crates/ty_python_semantic/resources/mdtest/properties.md +++ b/crates/ty_python_semantic/resources/mdtest/properties.md @@ -271,8 +271,8 @@ method, which means that it is a *data* descriptor (if there is no setter, `__se available but yields an `AttributeError` at runtime). ```py -reveal_type(type(attr_property).__get__) # revealed: -reveal_type(type(attr_property).__set__) # revealed: +reveal_type(type(attr_property).__get__) # revealed: +reveal_type(type(attr_property).__set__) # revealed: ``` When we access `c.attr`, the `__get__` method of the `property` class is called, passing the diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 1bbcc038cc..81916db6da 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -8314,6 +8314,7 @@ impl<'db> Type<'db> { KnownInstanceType::TypeAliasType(type_alias) => { type_alias.definition(db).map(TypeDefinition::TypeAlias) } + KnownInstanceType::NewType(newtype) => Some(TypeDefinition::NewType(newtype.definition(db))), _ => None, }, diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index 39e5a1caee..f6bee388f5 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -14,6 +14,7 @@ use ruff_text_size::{TextLen, TextRange, TextSize}; use rustc_hash::{FxHashMap, FxHashSet}; use crate::Db; +use crate::place::Place; use crate::types::class::{ClassLiteral, ClassType, GenericAlias}; use crate::types::function::{FunctionType, OverloadLiteral}; use crate::types::generics::{GenericContext, Specialization}; @@ -642,11 +643,13 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> { Type::PropertyInstance(_) => f.with_type(self.ty).write_str("property"), Type::ModuleLiteral(module) => { f.set_invalid_syntax(); - write!( - f.with_type(self.ty), - "", - module.module(self.db).name(self.db) - ) + f.write_char('<')?; + f.with_type(KnownClass::ModuleType.to_class_literal(self.db)) + .write_str("module")?; + f.write_str(" '")?; + f.with_type(self.ty) + .write_str(module.module(self.db).name(self.db))?; + f.write_str("'>") } Type::ClassLiteral(class) => { f.set_invalid_syntax(); @@ -692,11 +695,17 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> { write!(f.with_type(Type::Dynamic(dynamic)), "{dynamic}")?; f.write_char(']') } - SubclassOfInner::TypeVar(bound_typevar) => write!( - f, - "type[{}]", - bound_typevar.identity(self.db).display(self.db) - ), + SubclassOfInner::TypeVar(bound_typevar) => { + f.with_type(KnownClass::Type.to_class_literal(self.db)) + .write_str("type")?; + f.write_char('[')?; + write!( + f.with_type(Type::TypeVar(bound_typevar)), + "{}", + bound_typevar.identity(self.db).display(self.db) + )?; + f.write_char(']') + } }, Type::SpecialForm(special_form) => { f.set_invalid_syntax(); @@ -763,61 +772,115 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> { } Type::KnownBoundMethod(method_type) => { f.set_invalid_syntax(); - match method_type { - KnownBoundMethodType::FunctionTypeDunderGet(function) => { - write!( - f, - "", - function = function.name(self.db), - ) - } - KnownBoundMethodType::FunctionTypeDunderCall(function) => { - write!( - f, - "", - function = function.name(self.db), - ) - } - KnownBoundMethodType::PropertyDunderGet(_) => { - f.write_str("") - } - KnownBoundMethodType::PropertyDunderSet(_) => { - f.write_str("") - } - KnownBoundMethodType::StrStartswith(_) => { - f.write_str("") - } + let (cls, member_name, cls_name, ty, ty_name) = match method_type { + KnownBoundMethodType::FunctionTypeDunderGet(function) => ( + KnownClass::FunctionType, + "__get__", + "function", + Type::FunctionLiteral(function), + Some(&**function.name(self.db)), + ), + KnownBoundMethodType::FunctionTypeDunderCall(function) => ( + KnownClass::FunctionType, + "__call__", + "function", + Type::FunctionLiteral(function), + Some(&**function.name(self.db)), + ), + KnownBoundMethodType::PropertyDunderGet(property) => ( + KnownClass::Property, + "__get__", + "property", + Type::PropertyInstance(property), + property + .getter(self.db) + .and_then(Type::as_function_literal) + .map(|getter| &**getter.name(self.db)), + ), + KnownBoundMethodType::PropertyDunderSet(property) => ( + KnownClass::Property, + "__set__", + "property", + Type::PropertyInstance(property), + property + .getter(self.db) + .and_then(Type::as_function_literal) + .map(|getter| &**getter.name(self.db)), + ), + KnownBoundMethodType::StrStartswith(literal) => ( + KnownClass::Property, + "startswith", + "string", + Type::StringLiteral(literal), + Some(literal.value(self.db)), + ), KnownBoundMethodType::ConstraintSetRange => { - f.write_str("bound method `ConstraintSet.range`") + return f.write_str("bound method `ConstraintSet.range`"); } KnownBoundMethodType::ConstraintSetAlways => { - f.write_str("bound method `ConstraintSet.always`") + return f.write_str("bound method `ConstraintSet.always`"); } KnownBoundMethodType::ConstraintSetNever => { - f.write_str("bound method `ConstraintSet.never`") + return f.write_str("bound method `ConstraintSet.never`"); } KnownBoundMethodType::ConstraintSetImpliesSubtypeOf(_) => { - f.write_str("bound method `ConstraintSet.implies_subtype_of`") + return f.write_str("bound method `ConstraintSet.implies_subtype_of`"); } KnownBoundMethodType::ConstraintSetSatisfies(_) => { - f.write_str("bound method `ConstraintSet.satisfies`") + return f.write_str("bound method `ConstraintSet.satisfies`"); } KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(_) => { - f.write_str("bound method `ConstraintSet.satisfied_by_all_typevars`") + return f + .write_str("bound method `ConstraintSet.satisfied_by_all_typevars`"); } KnownBoundMethodType::GenericContextSpecializeConstrained(_) => { - f.write_str("bound method `GenericContext.specialize_constrained`") + return f.write_str("bound method `GenericContext.specialize_constrained`"); } + }; + + let class_ty = cls.to_class_literal(self.db); + f.write_char('<')?; + f.with_type(KnownClass::MethodWrapperType.to_class_literal(self.db)) + .write_str("method-wrapper")?; + f.write_str(" '")?; + if let Place::Defined(member_ty, _, _) = class_ty.member(self.db, member_name).place + { + f.with_type(member_ty).write_str(member_name)?; + } else { + f.write_str(member_name)?; + } + f.write_str("' of ")?; + f.with_type(class_ty).write_str(cls_name)?; + if let Some(name) = ty_name { + f.write_str(" '")?; + f.with_type(ty).write_str(name)?; + f.write_str("'>") + } else { + f.write_str("' object>") } } Type::WrapperDescriptor(kind) => { f.set_invalid_syntax(); - let (method, object) = match kind { - WrapperDescriptorKind::FunctionTypeDunderGet => ("__get__", "function"), - WrapperDescriptorKind::PropertyDunderGet => ("__get__", "property"), - WrapperDescriptorKind::PropertyDunderSet => ("__set__", "property"), + let (method, object, cls) = match kind { + WrapperDescriptorKind::FunctionTypeDunderGet => { + ("__get__", "function", KnownClass::FunctionType) + } + WrapperDescriptorKind::PropertyDunderGet => { + ("__get__", "property", KnownClass::Property) + } + WrapperDescriptorKind::PropertyDunderSet => { + ("__set__", "property", KnownClass::Property) + } }; - write!(f, "") + f.write_char('<')?; + f.with_type(KnownClass::WrapperDescriptorType.to_class_literal(self.db)) + .write_str("wrapper-descriptor")?; + f.write_str(" '")?; + f.write_str(method)?; + f.write_str("' of '")?; + f.with_type(cls.to_class_literal(self.db)) + .write_str(object)?; + f.write_str("' objects>") } Type::DataclassDecorator(_) => { f.set_invalid_syntax(); @@ -907,7 +970,10 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> { .fmt_detailed(f), Type::TypedDict(TypedDictType::Synthesized(synthesized)) => { f.set_invalid_syntax(); - f.write_str(" DisplayGenericContext<'_, 'db> { f.set_invalid_syntax(); let typevar = bound_typevar.typevar(self.db); if typevar.is_paramspec(self.db) { - write!(f, "**{}", typevar.name(self.db))?; - } else { - f.write_str(typevar.name(self.db))?; + f.write_str("**")?; } + write!( + f.with_type(Type::TypeVar(*bound_typevar)), + "{}", + typevar.name(self.db) + )?; } f.write_char(']') } @@ -1334,7 +1403,11 @@ impl<'db> DisplayGenericContext<'_, 'db> { f.write_str(", ")?; } f.set_invalid_syntax(); - write!(f, "{}", bound_typevar.identity(self.db).display(self.db))?; + write!( + f.with_type(Type::TypeVar(bound_typevar)), + "{}", + bound_typevar.identity(self.db).display(self.db) + )?; } f.write_char(']') } @@ -2262,15 +2335,17 @@ impl<'db> FmtDetailed<'db> for DisplayKnownInstanceRepr<'db> { KnownInstanceType::SubscriptedProtocol(generic_context) => { f.set_invalid_syntax(); f.write_str("") } KnownInstanceType::SubscriptedGeneric(generic_context) => { f.set_invalid_syntax(); f.write_str("") } KnownInstanceType::TypeAliasType(alias) => { @@ -2278,15 +2353,9 @@ impl<'db> FmtDetailed<'db> for DisplayKnownInstanceRepr<'db> { f.set_invalid_syntax(); f.write_str("") } else { f.with_type(ty).write_str("typing.TypeAliasType") @@ -2306,7 +2375,9 @@ impl<'db> FmtDetailed<'db> for DisplayKnownInstanceRepr<'db> { KnownInstanceType::Field(field) => { f.with_type(ty).write_str("dataclasses.Field")?; if let Some(default_ty) = field.default_type(self.db) { - write!(f, "[{}]", default_ty.display(self.db))?; + f.write_char('[')?; + write!(f.with_type(default_ty), "{}", default_ty.display(self.db))?; + f.write_char(']')?; } Ok(()) } @@ -2325,51 +2396,58 @@ impl<'db> FmtDetailed<'db> for DisplayKnownInstanceRepr<'db> { KnownInstanceType::UnionType(union) => { f.set_invalid_syntax(); f.write_char('<')?; - f.with_type(ty).write_str("types.UnionType")?; + f.with_type(KnownClass::UnionType.to_class_literal(self.db)) + .write_str("types.UnionType")?; f.write_str(" special form")?; if let Ok(ty) = union.union_type(self.db) { - write!(f, " '{}'", ty.display(self.db))?; + f.write_str(" '")?; + ty.display(self.db).fmt_detailed(f)?; + f.write_char('\'')?; } f.write_char('>') } KnownInstanceType::Literal(inner) => { f.set_invalid_syntax(); - write!( - f, - "", - inner.inner(self.db).display(self.db) - ) + f.write_str("") } KnownInstanceType::Annotated(inner) => { f.set_invalid_syntax(); f.write_str("]'>", - inner.inner(self.db).display(self.db) - ) + f.with_type(Type::SpecialForm(SpecialFormType::Annotated)) + .write_str("typing.Annotated")?; + f.write_char('[')?; + inner.inner(self.db).display(self.db).fmt_detailed(f)?; + f.write_str(", ]'>") } KnownInstanceType::Callable(callable) => { f.set_invalid_syntax(); f.write_char('<')?; - f.with_type(ty).write_str("typing.Callable")?; - write!(f, " special form '{}'>", callable.display(self.db)) + f.with_type(Type::SpecialForm(SpecialFormType::Callable)) + .write_str("typing.Callable")?; + f.write_str(" special form '")?; + callable.display(self.db).fmt_detailed(f)?; + f.write_str("'>") } KnownInstanceType::TypeGenericAlias(inner) => { f.set_invalid_syntax(); f.write_str("") + f.with_type(KnownClass::Type.to_class_literal(self.db)) + .write_str("type")?; + f.write_char('[')?; + inner.inner(self.db).display(self.db).fmt_detailed(f)?; + f.write_str("]'>") } - KnownInstanceType::LiteralStringAlias(_) => f.write_str("str"), + KnownInstanceType::LiteralStringAlias(_) => f + .with_type(KnownClass::Str.to_class_literal(self.db)) + .write_str("str"), KnownInstanceType::NewType(declaration) => { f.set_invalid_syntax(); - f.write_str("") }