mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 05:20:49 -05:00
[ty] Avoid reporting overload errors for successful union variants (#22688)
## Summary
Consider `x: str | bytes` and then `x.split(" ")`. Because we have a
union, and at least one variant errors (`bytes` expects a `Buffer`, not
a `str`), we call `binding.report_diagnostics` for each variant. For the
`str` variant, it has two overloads that both match arity, but only one
actually matches the signature... So
`matching_overload_before_type_checking` is `None` (because they both
match arity), but we don't actually have an error, and we fall through
to `NO_MATCHING_OVERLOAD`.
If one variant succeeds, we should avoid reporting errors for it, even
if not _all_ variants matched.
This commit is contained in:
@@ -184,3 +184,22 @@ def _(x: T, y: int) -> T:
|
||||
# error: [invalid-argument-type]
|
||||
return x.foo(y)
|
||||
```
|
||||
|
||||
## Union with overloaded method and incompatible variant
|
||||
|
||||
When calling a method on a union type where:
|
||||
|
||||
- One variant has the method with compatible arguments (`str.split`)
|
||||
- Another variant has the method but with incompatible arguments (`bytes.split` expects `Buffer`,
|
||||
not `str`)
|
||||
- Other variants don't have the method at all (contributing `Unknown` to the callable)
|
||||
|
||||
We should only report the specific error for the incompatible variant (`invalid-argument-type` for
|
||||
`bytes.split`), not a spurious `no-matching-overload` for the compatible variant.
|
||||
|
||||
```py
|
||||
def _(x: bytes | str | int):
|
||||
# error: [invalid-argument-type]
|
||||
# error: [possibly-missing-attribute]
|
||||
x.split(" ")
|
||||
```
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
|
||||
---
|
||||
mdtest name: union_call.md - Calling a union of function types - Union with overloaded method and incompatible variant
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/union_call.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | def _(x: bytes | str | int):
|
||||
2 | # error: [invalid-argument-type]
|
||||
3 | # error: [possibly-missing-attribute]
|
||||
4 | x.split(" ")
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
warning[possibly-missing-attribute]: Attribute `split` may be missing on object of type `bytes | str | int`
|
||||
--> src/mdtest_snippet.py:4:5
|
||||
|
|
||||
2 | # error: [invalid-argument-type]
|
||||
3 | # error: [possibly-missing-attribute]
|
||||
4 | x.split(" ")
|
||||
| ^^^^^^^
|
||||
|
|
||||
info: rule `possibly-missing-attribute` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[invalid-argument-type]: Argument to bound method `split` is incorrect
|
||||
--> src/mdtest_snippet.py:4:13
|
||||
|
|
||||
2 | # error: [invalid-argument-type]
|
||||
3 | # error: [possibly-missing-attribute]
|
||||
4 | x.split(" ")
|
||||
| ^^^ Expected `Buffer | None`, found `Literal[" "]`
|
||||
|
|
||||
info: Method defined here
|
||||
--> stdlib/builtins.pyi:1761:9
|
||||
|
|
||||
1759 | """
|
||||
1760 |
|
||||
1761 | def split(self, sep: ReadableBuffer | None = None, maxsplit: SupportsIndex = -1) -> list[bytes]:
|
||||
| ^^^^^ --------------------------------- Parameter declared here
|
||||
1762 | """Return a list of the sections in the bytes, using sep as the delimiter.
|
||||
|
|
||||
info: Union variant `bound method bytes.split(sep: Buffer | None = None, maxsplit: SupportsIndex = -1) -> list[bytes]` is incompatible with this call site
|
||||
info: Attempted to call union type `(bound method bytes.split(sep: Buffer | None = None, maxsplit: SupportsIndex = -1) -> list[bytes]) | (Overload[(sep: LiteralString | None = None, maxsplit: SupportsIndex = -1) -> list[LiteralString], (sep: str | None = None, maxsplit: SupportsIndex = -1) -> list[str]])`
|
||||
info: rule `invalid-argument-type` is enabled by default
|
||||
|
||||
```
|
||||
@@ -327,6 +327,9 @@ impl<'db> Bindings<'db> {
|
||||
}
|
||||
|
||||
for binding in self {
|
||||
if binding.as_result().is_ok() {
|
||||
continue;
|
||||
}
|
||||
let union_diag = UnionDiagnostic {
|
||||
callable_type: self.callable_type(),
|
||||
binding,
|
||||
|
||||
Reference in New Issue
Block a user