[ty] Enable even more goto-definition on inlay hints (#21950)

## Summary

Working on py-fuzzer recently (AKA, a Python project!) reminded me how
cool our "inlay hint goto-definition feature" is. So this PR adds a
bunch more of that!

I also made a couple of other minor changes to type display. For
example, in the playground, this snippet:

```py
def f(): ...
reveal_type(f.__get__)
```

currently leads to this diagnostic:

```
Revealed type: `<method-wrapper `__get__` of `f`>` (revealed-type) [Ln 2, Col 13]
```

But the fact that we have backticks both around the type display and
inside the type display isn't _great_ there. This PR changes it to

```
Revealed type: `<method-wrapper '__get__' of function 'f'>` (revealed-type) [Ln 2, Col 13]
```

which avoids the nested-backticks issue in diagnostics, and is more
similar to our display for various other `Type` variants such as
class-literal types (`<class 'Foo'>`, etc., not ``<class `Foo`>``).

## Test Plan

inlay snapshots added; mdtests updated
This commit is contained in:
Alex Waygood 2025-12-12 17:57:38 +00:00 committed by GitHub
parent dec4154c8a
commit a722df6a73
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 713 additions and 116 deletions

View File

@ -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[: <module 'foo'>] = 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[: <module 'foo'>] = 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[: <module 'foo'>] = 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[: <special form 'Literal["a", "b", "c"]'>] = 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[: <special form 'Literal["a", "b", "c"]'>] = 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[: <special form 'Literal["a", "b", "c"]'>] = 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[: <special form 'Literal["a", "b", "c"]'>] = 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[: <special form 'Literal["a", "b", "c"]'>] = 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[: <wrapper-descriptor '__get__' of 'function' objects>] = 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[: <wrapper-descriptor '__get__' of 'function' objects>] = 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[: <wrapper-descriptor '__get__' of 'function' objects>] = 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[: <method-wrapper '__call__' of function 'f'>] = 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[: <method-wrapper '__call__' of function 'f'>] = 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[: <method-wrapper '__call__' of function 'f'>] = 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[: <method-wrapper '__call__' of function 'f'>] = 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[: <method-wrapper '__call__' of function 'f'>] = 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 pseudo-class 'N'>] = NewType([name=]'N', [tp=]str)
Y[: <NewType pseudo-class 'N'>] = 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 pseudo-class 'N'>] = NewType([name=]'N', [tp=]str)
| ^^^^^^^
5 |
6 | Y[: <NewType pseudo-class 'N'>] = 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 pseudo-class 'N'>] = NewType([name=]'N', [tp=]str)
| ^
5 |
6 | Y[: <NewType pseudo-class 'N'>] = 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 pseudo-class 'N'>] = NewType([name=]'N', [tp=]str)
| ^^^^
5 |
6 | Y[: <NewType pseudo-class 'N'>] = 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 pseudo-class 'N'>] = NewType([name=]'N', [tp=]str)
| ^^
5 |
6 | Y[: <NewType pseudo-class 'N'>] = 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 pseudo-class 'N'>] = NewType([name=]'N', [tp=]str)
5 |
6 | Y[: <NewType pseudo-class 'N'>] = 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 pseudo-class 'N'>] = NewType([name=]'N', [tp=]str)
5 |
6 | Y[: <NewType pseudo-class 'N'>] = 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[: <special form 'typing.Protocol[T]'>] = 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[: <special form 'typing.Protocol[T]'>] = 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[: <special form 'typing.Protocol[T]'>] = 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[: <special form 'typing.Protocol[T]'>] = 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[: <special form 'typing.Protocol[T]'>] = 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,

View File

@ -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: <method-wrapper `__get__` of `f`>
reveal_type(f.__call__) # revealed: <method-wrapper `__call__` of `f`>
reveal_type(f.__get__) # revealed: <method-wrapper '__get__' of function 'f'>
reveal_type(f.__call__) # revealed: <method-wrapper '__call__' of function 'f'>
static_assert(is_subtype_of(TypeOf[f.__get__], types.MethodWrapperType))
static_assert(is_subtype_of(TypeOf[f.__call__], types.MethodWrapperType))
```

View File

@ -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: <method-wrapper `__get__` of `f`>
# revealed: <method-wrapper '__get__' of function 'f'>
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: <method-wrapper `__get__` of `f`>
reveal_type(method_wrapper) # revealed: <method-wrapper '__get__' of function 'f'>
# 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: <method-wrapper `__get__` of `f`>
# revealed: <method-wrapper '__get__' of function 'f'>
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: <wrapper-descriptor `__get__` of `function` objects>
# revealed: <wrapper-descriptor '__get__' of 'function' objects>
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: <method-wrapper `__get__` of `f`>
# revealed: <method-wrapper '__get__' of function 'f'>
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: <method-wrapper `__call__` of `f`>
# revealed: <method-wrapper '__call__' of function 'f'>
reveal_type(f.__call__)
static_assert(is_assignable_to(TypeOf[f.__call__], Callable))
# revealed: <wrapper-descriptor `__get__` of `property` objects>
# revealed: <wrapper-descriptor '__get__' of 'property' objects>
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: <method-wrapper `__get__` of `property` object>
# revealed: <method-wrapper '__get__' of property 'my_property'>
reveal_type(MyClass.my_property.__get__)
static_assert(is_assignable_to(TypeOf[MyClass.my_property.__get__], Callable))
# revealed: <wrapper-descriptor `__set__` of `property` objects>
# revealed: <wrapper-descriptor '__set__' of 'property' objects>
reveal_type(property.__set__)
static_assert(is_assignable_to(TypeOf[property.__set__], Callable))
# revealed: <method-wrapper `__set__` of `property` object>
# revealed: <method-wrapper '__set__' of property 'my_property'>
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: <method-wrapper `startswith` of `str` object>
# revealed: <method-wrapper 'startswith' of string 'foo'>
reveal_type("foo".startswith)
static_assert(is_assignable_to(TypeOf["foo".startswith], Callable))

View File

@ -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: <method-wrapper `__get__` of `f`>
reveal_type(f.__get__) # revealed: <method-wrapper '__get__' of function 'f'>
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: <wrapper-descriptor `__get__` of `function` objects>
reveal_type(wrapper_descriptor) # revealed: <wrapper-descriptor '__get__' of 'function' objects>
reveal_type(wrapper_descriptor(f, None, type(f))) # revealed: def f(x: object) -> str
static_assert(is_subtype_of(TypeOf[wrapper_descriptor], types.WrapperDescriptorType))

View File

@ -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: <method-wrapper `__get__` of `f`>
reveal_type(getattr_static(C[int], "f").__get__) # revealed: <method-wrapper '__get__' of function 'f'>
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]))

View File

@ -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: <wrapper-descriptor `__get__` of `property` objects>
reveal_type(type(attr_property).__set__) # revealed: <wrapper-descriptor `__set__` of `property` objects>
reveal_type(type(attr_property).__get__) # revealed: <wrapper-descriptor '__get__' of 'property' objects>
reveal_type(type(attr_property).__set__) # revealed: <wrapper-descriptor '__set__' of 'property' objects>
```
When we access `c.attr`, the `__get__` method of the `property` class is called, passing the

View File

@ -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,
},

View File

@ -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.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,
"<method-wrapper `__get__` of `{function}`>",
function = function.name(self.db),
)
}
KnownBoundMethodType::FunctionTypeDunderCall(function) => {
write!(
f,
"<method-wrapper `__call__` of `{function}`>",
function = function.name(self.db),
)
}
KnownBoundMethodType::PropertyDunderGet(_) => {
f.write_str("<method-wrapper `__get__` of `property` object>")
}
KnownBoundMethodType::PropertyDunderSet(_) => {
f.write_str("<method-wrapper `__set__` of `property` object>")
}
KnownBoundMethodType::StrStartswith(_) => {
f.write_str("<method-wrapper `startswith` of `str` object>")
}
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, "<wrapper-descriptor `{method}` of `{object}` objects>")
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("<TypedDict with items ")?;
f.write_char('<')?;
f.with_type(Type::SpecialForm(SpecialFormType::TypedDict))
.write_str("TypedDict")?;
f.write_str(" with items ")?;
let items = synthesized.items(self.db);
for (i, name) in items.keys().enumerate() {
let is_last = i == items.len() - 1;
@ -1318,10 +1384,13 @@ impl<'db> 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("<special form '")?;
f.with_type(ty).write_str("typing.Protocol")?;
f.write_str(&generic_context.display(self.db).to_string())?;
f.with_type(Type::SpecialForm(SpecialFormType::Protocol))
.write_str("typing.Protocol")?;
generic_context.display(self.db).fmt_detailed(f)?;
f.write_str("'>")
}
KnownInstanceType::SubscriptedGeneric(generic_context) => {
f.set_invalid_syntax();
f.write_str("<special form '")?;
f.with_type(ty).write_str("typing.Generic")?;
f.write_str(&generic_context.display(self.db).to_string())?;
f.with_type(Type::SpecialForm(SpecialFormType::Generic))
.write_str("typing.Generic")?;
generic_context.display(self.db).fmt_detailed(f)?;
f.write_str("'>")
}
KnownInstanceType::TypeAliasType(alias) => {
@ -2278,15 +2353,9 @@ impl<'db> FmtDetailed<'db> for DisplayKnownInstanceRepr<'db> {
f.set_invalid_syntax();
f.write_str("<type alias '")?;
f.with_type(ty).write_str(alias.name(self.db))?;
f.write_str(
&specialization
.display_short(
self.db,
TupleSpecialization::No,
DisplaySettings::default(),
)
.to_string(),
)?;
specialization
.display_short(self.db, TupleSpecialization::No, DisplaySettings::default())
.fmt_detailed(f)?;
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,
"<special form '{}'>",
inner.inner(self.db).display(self.db)
)
f.write_str("<special form '")?;
inner.inner(self.db).display(self.db).fmt_detailed(f)?;
f.write_str("'>")
}
KnownInstanceType::Annotated(inner) => {
f.set_invalid_syntax();
f.write_str("<special form '")?;
f.with_type(ty).write_str("typing.Annotated")?;
write!(
f,
"[{}, <metadata>]'>",
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(", <metadata>]'>")
}
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("<special form '")?;
write!(
f.with_type(ty),
"type[{}]",
inner.inner(self.db).display(self.db)
)?;
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("<NewType pseudo-class '")?;
f.write_char('<')?;
f.with_type(KnownClass::NewType.to_class_literal(self.db))
.write_str("NewType")?;
f.write_str(" pseudo-class '")?;
f.with_type(ty).write_str(declaration.name(self.db))?;
f.write_str("'>")
}