Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/ty_ide/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2519,10 +2519,10 @@ def function():
// TODO: This should just be `**AB@Alias2 (<variance>)`
// https://github.com/astral-sh/ty/issues/1581
assert_snapshot!(test.hover(), @"
(**AB@Alias2) -> tuple[AB@Alias2]
(**AB@Alias2) -> tuple[Unknown]
---------------------------------------------
```python
(**AB@Alias2) -> tuple[AB@Alias2]
(**AB@Alias2) -> tuple[Unknown]
```
---------------------------------------------
info[hover]: Hovered content is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,21 @@ class B: ...
In type annotations, `ParamSpec` is only valid as the first element to `Callable`, the final element
to `Concatenate`, or as a type parameter to `Protocol` or `Generic`.

<!-- snapshot-diagnostics -->

`library.py`:

```py
from typing import ParamSpec

LibraryP = ParamSpec("LibraryP")
```

`main.py`:

```py
from typing import ParamSpec, Callable, Concatenate, Protocol, Generic
import library
from typing import Any, Final, ParamSpec, Callable, Concatenate, Protocol, Generic, Union, Optional, Annotated

P = ParamSpec("P")

Expand All @@ -167,19 +180,61 @@ class ValidGeneric(Generic[P]):
def valid(
a1: Callable[P, int],
a2: Callable[Concatenate[int, P], int],
a3: Callable["P", int],
a4: Callable[Concatenate[int, "P"], int],
a5: Callable[library.LibraryP, int],
a6: Callable["Concatenate[int, P]", int],
a7: Callable["library.LibraryP", int],
) -> None: ...
def invalid(
# TODO: error
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a1: P,
# TODO: error
# TODO: this should cause us to emit an error because a `ParamSpec` type argument
# cannot be used to specialize a non-`ParamSpec` type parameter
a2: list[P],
# TODO: error
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a3: Callable[[P], int],
# TODO: error
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a4: Callable[..., P],
# TODO: error
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a5: Callable[Concatenate[P, ...], int],
Comment thread
AlexWaygood marked this conversation as resolved.
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a6: P | int,
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a7: Union[P, int],
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a8: Optional[P],
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a9: Annotated[P, "metadata"],
# error: [invalid-type-form] "The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`"
a10: Callable["[int, str]", str],
# error: [invalid-type-form] "The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`"
a11: Callable["...", int],
) -> None: ...

# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
def invalid_return() -> P:
raise NotImplementedError

def invalid_variable_annotation(y: Any) -> None:
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
x: P = y

def invalid_with_qualifier(y: Any) -> None:
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
x: Final[P] = y

# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
def invalid_stringified_return() -> "P":
raise NotImplementedError

def invalid_stringified_annotation(
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a: "P",
) -> None: ...
def invalid_stringified_variable_annotation(y: Any) -> None:
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
x: "P" = y
```

## Validating `P.args` and `P.kwargs` usage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,11 @@ If a `ParamSpec` appears in `Concatenate`, it must be the last element.
```py
from typing import Callable, Concatenate

# TODO: Should be an error - ParamSpec not in last position
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
def invalid1[**P](c: Callable[Concatenate[P, int], bool]):
reveal_type(c) # revealed: (...) -> bool

# TODO: Should be an error - ParamSpec not in last position
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
def invalid2[**P](c: Callable[Concatenate[P, ...], bool]):
reveal_type(c) # revealed: (...) -> bool

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,25 +67,65 @@ def foo[**P = int]() -> None:

`ParamSpec` is only valid as the first element to `Callable` or the final element to `Concatenate`.

<!-- snapshot-diagnostics -->

```py
from typing import ParamSpec, Callable, Concatenate
from typing import Any, Final, ParamSpec, Callable, Concatenate, Union, Optional, Annotated

def valid[**P](
a1: Callable[P, int],
a2: Callable[Concatenate[int, P], int],
a3: Callable["P", int],
a4: Callable[Concatenate[int, "P"], int],
) -> None: ...
def invalid[**P](
# TODO: error
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a1: P,
# TODO: error
# TODO: this should cause us to emit an error because a `ParamSpec` type argument
# cannot be used to specialize a non-`ParamSpec` type parameter
a2: list[P],
# TODO: error
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a3: Callable[[P], int],
# TODO: error
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a4: Callable[..., P],
# TODO: error
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a5: Callable[Concatenate[P, ...], int],
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a6: P | int,
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a7: Union[P, int],
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a8: Optional[P],
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a9: Annotated[P, "metadata"],
) -> None: ...

# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
def invalid_return[**P]() -> P:
raise NotImplementedError

# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
type Alias[**P] = P

def invalid_variable_annotation[**P](y: Any) -> None:
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
x: P = y

def invalid_with_qualifier[**P](y: Any) -> None:
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
x: Final[P] = y

# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
def invalid_stringified_return[**P]() -> "P":
raise NotImplementedError

def invalid_stringified_annotation[**P](
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
a: "P",
) -> None: ...
def invalid_stringified_variable_annotation[**P](y: Any) -> None:
# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
x: "P" = y
```

## Validating `P.args` and `P.kwargs` usage
Expand Down
Loading
Loading