diff --git a/crates/ty_ide/src/inlay_hints.rs b/crates/ty_ide/src/inlay_hints.rs index aea0e73361..d7bf669c64 100644 --- a/crates/ty_ide/src/inlay_hints.rs +++ b/crates/ty_ide/src/inlay_hints.rs @@ -6018,7 +6018,7 @@ Source with applied edits: def foo(x: int, *y: bool, z: str | int | list[str]): ... - a[: def foo(x: int, *y: bool, *, z: str | int | list[str]) -> Unknown] = foo + a[: def foo(x: int, *y: bool, z: str | int | list[str]) -> Unknown] = foo --------------------------------------------- info[inlay-hint-location]: Inlay Hint Target --> stdlib/builtins.pyi:LL:7 @@ -6028,7 +6028,7 @@ Source with applied edits: info: Source --> main2.py:LL:16 | - LL | a[: def foo(x: int, *y: bool, *, z: str | int | list[str]) -> Unknown] = foo + LL | a[: def foo(x: int, *y: bool, z: str | int | list[str]) -> Unknown] = foo | ^^^ info[inlay-hint-location]: Inlay Hint Target @@ -6039,7 +6039,7 @@ Source with applied edits: info: Source --> main2.py:LL:25 | - LL | a[: def foo(x: int, *y: bool, *, z: str | int | list[str]) -> Unknown] = foo + LL | a[: def foo(x: int, *y: bool, z: str | int | list[str]) -> Unknown] = foo | ^^^^ info[inlay-hint-location]: Inlay Hint Target @@ -6048,10 +6048,10 @@ Source with applied edits: LL | class str(Sequence[str]): | ^^^ info: Source - --> main2.py:LL:37 + --> main2.py:LL:34 | - LL | a[: def foo(x: int, *y: bool, *, z: str | int | list[str]) -> Unknown] = foo - | ^^^ + LL | a[: def foo(x: int, *y: bool, z: str | int | list[str]) -> Unknown] = foo + | ^^^ info[inlay-hint-location]: Inlay Hint Target --> stdlib/builtins.pyi:LL:7 @@ -6059,10 +6059,10 @@ Source with applied edits: LL | class int: | ^^^ info: Source - --> main2.py:LL:43 + --> main2.py:LL:40 | - LL | a[: def foo(x: int, *y: bool, *, z: str | int | list[str]) -> Unknown] = foo - | ^^^ + LL | a[: def foo(x: int, *y: bool, z: str | int | list[str]) -> Unknown] = foo + | ^^^ info[inlay-hint-location]: Inlay Hint Target --> stdlib/builtins.pyi:LL:7 @@ -6070,10 +6070,10 @@ Source with applied edits: LL | class list(MutableSequence[_T]): | ^^^^ info: Source - --> main2.py:LL:49 + --> main2.py:LL:46 | - LL | a[: def foo(x: int, *y: bool, *, z: str | int | list[str]) -> Unknown] = foo - | ^^^^ + LL | a[: def foo(x: int, *y: bool, z: str | int | list[str]) -> Unknown] = foo + | ^^^^ info[inlay-hint-location]: Inlay Hint Target --> stdlib/builtins.pyi:LL:7 @@ -6081,10 +6081,10 @@ Source with applied edits: LL | class str(Sequence[str]): | ^^^ info: Source - --> main2.py:LL:54 + --> main2.py:LL:51 | - LL | a[: def foo(x: int, *y: bool, *, z: str | int | list[str]) -> Unknown] = foo - | ^^^ + LL | a[: def foo(x: int, *y: bool, z: str | int | list[str]) -> Unknown] = foo + | ^^^ info[inlay-hint-location]: Inlay Hint Target --> stdlib/ty_extensions/_internal.pyi:LL:1 @@ -6092,10 +6092,10 @@ Source with applied edits: LL | Unknown: _SpecialForm | ^^^^^^^ info: Source - --> main2.py:LL:63 + --> main2.py:LL:60 | - LL | a[: def foo(x: int, *y: bool, *, z: str | int | list[str]) -> Unknown] = foo - | ^^^^^^^ + LL | a[: def foo(x: int, *y: bool, z: str | int | list[str]) -> Unknown] = foo + | ^^^^^^^ "); } diff --git a/crates/ty_python_semantic/resources/mdtest/bidirectional.md b/crates/ty_python_semantic/resources/mdtest/bidirectional.md index 2f220cecff..9582f08984 100644 --- a/crates/ty_python_semantic/resources/mdtest/bidirectional.md +++ b/crates/ty_python_semantic/resources/mdtest/bidirectional.md @@ -1804,7 +1804,7 @@ reveal_type(f8) # revealed: (int, /) -> None # An optional keyword-only parameter does not prevent `*args` from accepting the positional # suffix in a `Callable` annotation. f9: Callable[[*tuple[int, ...], int], None] = lambda *args, x=1: None -reveal_type(f9) # revealed: (*args, *, x=1) -> None +reveal_type(f9) # revealed: (*args, x=1) -> None f10: Callable[[str, int, str], tuple[str, int, str]] = lambda x, y, z: reveal_type((x, y, z)) # revealed: tuple[str, int, str] reveal_type(f10) # revealed: (x: str, y: int, z: str) -> tuple[str, int, str] diff --git a/crates/ty_python_semantic/resources/mdtest/call/functools_partial.md b/crates/ty_python_semantic/resources/mdtest/call/functools_partial.md index 7886deaae0..82d6963ffa 100644 --- a/crates/ty_python_semantic/resources/mdtest/call/functools_partial.md +++ b/crates/ty_python_semantic/resources/mdtest/call/functools_partial.md @@ -410,7 +410,7 @@ def collect[*Ts](*values: *Ts) -> tuple[*Ts]: return values bound = partial(collect) -reveal_type(bound) # revealed: partial[[*Ts](*values: Ts) -> tuple[*Ts]] +reveal_type(bound) # revealed: partial[[*Ts](*values: *Ts) -> tuple[*Ts]] reveal_type(bound()) # revealed: tuple[()] reveal_type(bound("x", 1)) # revealed: tuple[Literal["x"], Literal[1]] ``` @@ -432,7 +432,7 @@ def collect[*Ts](prefix: int, *values: *Ts) -> tuple[*Ts]: return values bound = partial(collect, 1) -reveal_type(bound) # revealed: partial[[*Ts](*values: Ts) -> tuple[*Ts]] +reveal_type(bound) # revealed: partial[[*Ts](*values: *Ts) -> tuple[*Ts]] reveal_type(bound()) # revealed: tuple[()] reveal_type(bound("x", 2)) # revealed: tuple[Literal["x"], Literal[2]] reveal_type(collect(1)) # revealed: tuple[()] @@ -455,7 +455,7 @@ def collect[T, *Ts](prefix: T, *values: *Ts) -> tuple[T, *Ts]: return (prefix, *values) bound = partial(collect, 1) -reveal_type(bound) # revealed: partial[[*Ts](*values: Ts) -> tuple[Literal[1], *Ts]] +reveal_type(bound) # revealed: partial[[*Ts](*values: *Ts) -> tuple[Literal[1], *Ts]] reveal_type(bound()) # revealed: tuple[Literal[1]] reveal_type(bound("x", True)) # revealed: tuple[Literal[1], Literal["x"], Literal[True]] ``` diff --git a/crates/ty_python_semantic/resources/mdtest/expression/lambda.md b/crates/ty_python_semantic/resources/mdtest/expression/lambda.md index 08862d222f..c8af9a4b41 100644 --- a/crates/ty_python_semantic/resources/mdtest/expression/lambda.md +++ b/crates/ty_python_semantic/resources/mdtest/expression/lambda.md @@ -55,7 +55,7 @@ reveal_type(lambda **kwargs: kwargs) # revealed: (**kwargs) -> dict[str, Unknow Mixing all of them together: ```py -# revealed: (a, b, /, c=True, *args, *, d="default", e=5, **kwargs) -> None +# revealed: (a, b, /, c=True, *args, d="default", e=5, **kwargs) -> None reveal_type(lambda a, b, /, c=True, *args, d="default", e=5, **kwargs: None) ``` diff --git a/crates/ty_python_semantic/resources/mdtest/type_display/callable.md b/crates/ty_python_semantic/resources/mdtest/type_display/callable.md index 2d350bdb43..19561ebcbf 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_display/callable.md +++ b/crates/ty_python_semantic/resources/mdtest/type_display/callable.md @@ -77,6 +77,29 @@ def _(x: object): reveal_type(c) # revealed: C[Top[(...)]] ``` +## Unpacked variadic signatures + +Display an unpacked variadic as one parameter, including its fixed prefix and required suffix. + +```toml +[environment] +python-version = "3.12" +``` + +```py +def mixed( + prefix: bytes, + /, + label: str, + *args: *tuple[bool, *tuple[int, ...], bytes, str], + flag: bool = False, + **kwargs: bytes, +) -> None: ... + +# revealed: def mixed(prefix: bytes, /, label: str, *args: *tuple[bool, *tuple[int, ...], bytes, str], flag: bool = False, **kwargs: bytes) -> None +reveal_type(mixed) +``` + ## Type aliases are not expanded unless necessary ```toml diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index 9e7592265f..1bc3774bb7 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -2610,6 +2610,7 @@ impl<'db> FmtDetailed<'db> for DisplayParameters<'_, 'db> { .fmt_detailed(&mut f.with_detail(TypeDetail::Parameter(param_name)))?; after_synthetic_unpack |= is_synthetic_unpack; + star_added |= parameter.is_variadic(); first = false; } @@ -2744,6 +2745,9 @@ impl<'db> FmtDetailed<'db> for DisplayParameter<'_, 'db> { if self.param.should_annotation_be_displayed() { let annotated_type = self.param.annotated_type(); f.write_str(": ")?; + if self.param.is_variadic() && self.param.has_starred_annotation() { + f.write_char('*')?; + } annotated_type .display_with(db, self.env, self.settings.clone()) .fmt_detailed(f)?; @@ -4090,7 +4094,7 @@ mod tests { ], Some(KnownClass::Bytes.to_instance(db, &env)) ), - @"(a, b: int, c=1, d: int = 2, /, e=3, f: int = 4, *args: object, *, g=5, h: int = 6, **kwargs: str) -> bytes" + @"(a, b: int, c=1, d: int = 2, /, e=3, f: int = 4, *args: object, g=5, h: int = 6, **kwargs: str) -> bytes" ); } @@ -4256,7 +4260,6 @@ mod tests { e=3, f: int = 4, *args: object, - *, g=5, h: int = 6, **kwargs: str