mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 18:58:04 +00:00
ty_python_semantic: report all union diagnostic
This makes one very simple change: we report all call binding errors from each union variant. This does result in duplicate-seeming diagnostics. For example, when two union variants are invalid for the same reason.
This commit is contained in:
parent
90272ad85a
commit
5ea3a52c8a
6 changed files with 152 additions and 19 deletions
|
@ -100,8 +100,10 @@ def _(flag: bool) -> None:
|
|||
|
||||
reveal_type(Foo(1)) # revealed: Foo
|
||||
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["1"]`"
|
||||
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["1"]`"
|
||||
reveal_type(Foo("1")) # revealed: Foo
|
||||
# error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`"
|
||||
# error: [missing-argument] "No argument provided for required parameter `x` of function `__new__`"
|
||||
reveal_type(Foo()) # revealed: Foo
|
||||
# error: [too-many-positional-arguments] "Too many positional arguments to function `__new__`: expected 1, got 2"
|
||||
reveal_type(Foo(1, 2)) # revealed: Foo
|
||||
|
@ -231,8 +233,10 @@ def _(flag: bool) -> None:
|
|||
|
||||
reveal_type(Foo(1)) # revealed: Foo
|
||||
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["1"]`"
|
||||
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `int`, found `Literal["1"]`"
|
||||
reveal_type(Foo("1")) # revealed: Foo
|
||||
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
|
||||
# error: [missing-argument] "No argument provided for required parameter `x` of bound method `__init__`"
|
||||
reveal_type(Foo()) # revealed: Foo
|
||||
# error: [too-many-positional-arguments] "Too many positional arguments to bound method `__init__`: expected 1, got 2"
|
||||
reveal_type(Foo(1, 2)) # revealed: Foo
|
||||
|
|
|
@ -56,8 +56,8 @@ def _(flag: bool, flag2: bool):
|
|||
else:
|
||||
def f() -> int:
|
||||
return 1
|
||||
# TODO we should mention all non-callable elements of the union
|
||||
# error: [call-non-callable] "Object of type `Literal[1]` is not callable"
|
||||
# error: [call-non-callable] "Object of type `Literal["foo"]` is not callable"
|
||||
# revealed: Unknown | int
|
||||
reveal_type(f())
|
||||
```
|
||||
|
@ -125,8 +125,8 @@ def _(flag: bool):
|
|||
else:
|
||||
f = f2
|
||||
|
||||
# TODO: we should show all errors from the union, not arbitrarily pick one union element
|
||||
# error: [too-many-positional-arguments] "Too many positional arguments to function `f1`: expected 0, got 1"
|
||||
# error: [too-many-positional-arguments] "Too many positional arguments to function `f2`: expected 0, got 1"
|
||||
x = f(3)
|
||||
reveal_type(x) # revealed: Unknown
|
||||
```
|
||||
|
@ -143,8 +143,8 @@ def _(flag: bool):
|
|||
else:
|
||||
f = C()
|
||||
|
||||
# TODO: we should either show all union errors here, or prioritize the not-callable error
|
||||
# error: [too-many-positional-arguments] "Too many positional arguments to function `f1`: expected 0, got 1"
|
||||
# error: [call-non-callable] "Object of type `C` is not callable"
|
||||
x = f(3)
|
||||
reveal_type(x) # revealed: Unknown
|
||||
```
|
||||
|
|
|
@ -22,6 +22,7 @@ def _(flag: bool):
|
|||
else:
|
||||
f = f2
|
||||
# error: [too-many-positional-arguments]
|
||||
# error: [invalid-argument-type]
|
||||
x = f(3)
|
||||
```
|
||||
|
||||
|
@ -98,6 +99,12 @@ def _(n: int):
|
|||
else:
|
||||
f = PossiblyNotCallable()
|
||||
# error: [too-many-positional-arguments]
|
||||
# error: [invalid-argument-type] "Argument to this function is incorrect: Expected `str`, found `Literal[3]`"
|
||||
# error: [missing-argument]
|
||||
# error: [invalid-argument-type] "Argument to this function is incorrect: Argument type `Literal[3]` does not satisfy upper bound of type variable `T`"
|
||||
# error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
||||
# error: [no-matching-overload]
|
||||
# error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
||||
x = f(3)
|
||||
```
|
||||
|
||||
|
|
|
@ -24,18 +24,41 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/union_call.m
|
|||
10 | else:
|
||||
11 | f = f2
|
||||
12 | # error: [too-many-positional-arguments]
|
||||
13 | x = f(3)
|
||||
13 | # error: [invalid-argument-type]
|
||||
14 | x = f(3)
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:too-many-positional-arguments: Too many positional arguments to function `f1`: expected 0, got 1
|
||||
--> src/mdtest_snippet.py:13:11
|
||||
error: lint:invalid-argument-type: Argument to this function is incorrect
|
||||
--> src/mdtest_snippet.py:14:11
|
||||
|
|
||||
11 | f = f2
|
||||
12 | # error: [too-many-positional-arguments]
|
||||
13 | x = f(3)
|
||||
13 | # error: [invalid-argument-type]
|
||||
14 | x = f(3)
|
||||
| ^ Expected `str`, found `Literal[3]`
|
||||
|
|
||||
info: Function defined here
|
||||
--> src/mdtest_snippet.py:4:5
|
||||
|
|
||||
2 | return 0
|
||||
3 |
|
||||
4 | def f2(name: str) -> int:
|
||||
| ^^ --------- Parameter declared here
|
||||
5 | return 0
|
||||
|
|
||||
info: `lint:invalid-argument-type` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error: lint:too-many-positional-arguments: Too many positional arguments to function `f1`: expected 0, got 1
|
||||
--> src/mdtest_snippet.py:14:11
|
||||
|
|
||||
12 | # error: [too-many-positional-arguments]
|
||||
13 | # error: [invalid-argument-type]
|
||||
14 | x = f(3)
|
||||
| ^
|
||||
|
|
||||
info: `lint:too-many-positional-arguments` is enabled by default
|
||||
|
|
|
@ -53,18 +53,120 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/union_call.m
|
|||
39 | else:
|
||||
40 | f = PossiblyNotCallable()
|
||||
41 | # error: [too-many-positional-arguments]
|
||||
42 | x = f(3)
|
||||
42 | # error: [invalid-argument-type] "Argument to this function is incorrect: Expected `str`, found `Literal[3]`"
|
||||
43 | # error: [missing-argument]
|
||||
44 | # error: [invalid-argument-type] "Argument to this function is incorrect: Argument type `Literal[3]` does not satisfy upper bound of type variable `T`"
|
||||
45 | # error: [call-non-callable] "Object of type `Literal[5]` is not callable"
|
||||
46 | # error: [no-matching-overload]
|
||||
47 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
||||
48 | x = f(3)
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:too-many-positional-arguments: Too many positional arguments to function `f1`: expected 0, got 1
|
||||
--> src/mdtest_snippet.py:42:11
|
||||
error: lint:call-non-callable: Object of type `Literal[5]` is not callable
|
||||
--> src/mdtest_snippet.py:48:9
|
||||
|
|
||||
40 | f = PossiblyNotCallable()
|
||||
41 | # error: [too-many-positional-arguments]
|
||||
42 | x = f(3)
|
||||
46 | # error: [no-matching-overload]
|
||||
47 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
||||
48 | x = f(3)
|
||||
| ^^^^
|
||||
|
|
||||
info: `lint:call-non-callable` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error: lint:call-non-callable: Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)
|
||||
--> src/mdtest_snippet.py:48:9
|
||||
|
|
||||
46 | # error: [no-matching-overload]
|
||||
47 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
||||
48 | x = f(3)
|
||||
| ^^^^
|
||||
|
|
||||
info: `lint:call-non-callable` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error: lint:missing-argument: No argument provided for required parameter `b` of function `f3`
|
||||
--> src/mdtest_snippet.py:48:9
|
||||
|
|
||||
46 | # error: [no-matching-overload]
|
||||
47 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
||||
48 | x = f(3)
|
||||
| ^^^^
|
||||
|
|
||||
info: `lint:missing-argument` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error: lint:no-matching-overload: No overload of method wrapper `__get__` of function `f` matches arguments
|
||||
--> src/mdtest_snippet.py:48:9
|
||||
|
|
||||
46 | # error: [no-matching-overload]
|
||||
47 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
||||
48 | x = f(3)
|
||||
| ^^^^
|
||||
|
|
||||
info: `lint:no-matching-overload` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error: lint:invalid-argument-type: Argument to this function is incorrect
|
||||
--> src/mdtest_snippet.py:48:11
|
||||
|
|
||||
46 | # error: [no-matching-overload]
|
||||
47 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
||||
48 | x = f(3)
|
||||
| ^ Expected `str`, found `Literal[3]`
|
||||
|
|
||||
info: Function defined here
|
||||
--> src/mdtest_snippet.py:6:5
|
||||
|
|
||||
4 | return 0
|
||||
5 |
|
||||
6 | def f2(name: str) -> int:
|
||||
| ^^ --------- Parameter declared here
|
||||
7 | return 0
|
||||
|
|
||||
info: `lint:invalid-argument-type` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error: lint:invalid-argument-type: Argument to this function is incorrect
|
||||
--> src/mdtest_snippet.py:48:11
|
||||
|
|
||||
46 | # error: [no-matching-overload]
|
||||
47 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
||||
48 | x = f(3)
|
||||
| ^ Argument type `Literal[3]` does not satisfy upper bound of type variable `T`
|
||||
|
|
||||
info: Type variable defined here
|
||||
--> src/mdtest_snippet.py:12:8
|
||||
|
|
||||
10 | return 0
|
||||
11 |
|
||||
12 | def f4[T: str](x: T) -> int:
|
||||
| ^^^^^^
|
||||
13 | return 0
|
||||
|
|
||||
info: `lint:invalid-argument-type` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error: lint:too-many-positional-arguments: Too many positional arguments to function `f1`: expected 0, got 1
|
||||
--> src/mdtest_snippet.py:48:11
|
||||
|
|
||||
46 | # error: [no-matching-overload]
|
||||
47 | # error: [call-non-callable] "Object of type `PossiblyNotCallable` is not callable (possibly unbound `__call__` method)"
|
||||
48 | x = f(3)
|
||||
| ^
|
||||
|
|
||||
info: `lint:too-many-positional-arguments` is enabled by default
|
||||
|
|
|
@ -199,11 +199,8 @@ impl<'db> Bindings<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: We currently only report errors for the first union element. Ideally, we'd report
|
||||
// an error saying that the union type can't be called, followed by subdiagnostics
|
||||
// explaining why.
|
||||
if let Some(first) = self.into_iter().find(|b| b.as_result().is_err()) {
|
||||
first.report_diagnostics(context, node);
|
||||
for binding in self {
|
||||
binding.report_diagnostics(context, node);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue