Minor follow-up to https://github.com/astral-sh/ruff/pull/16161 This `not_callable` flag wasn't functional, because it could never be `false`. It was initialized to `true` and then only ever updated with `|=`, which can never make it `false`. Add a test that exercises the case where it _should_ be `false` (all of the union elements are callable) but `bindings` is also empty (all union elements have binding errors). Before this PR, the added test wrongly emits a diagnostic that the union `Literal[f1] | Literal[f2]` is not callable. And add a test where a union call results in one binding error and one not-callable error, where we currently give the wrong result (we show only the binding error), with a TODO. Also add TODO comments in a couple other tests where ideally we'd report more than just one error out of a union call. Also update the flag name to `all_errors_not_callable` to more clearly indicate the semantics of the flag.
3.2 KiB
3.2 KiB
Unions in calls
Union of return types
def _(flag: bool):
if flag:
def f() -> int:
return 1
else:
def f() -> str:
return "foo"
reveal_type(f()) # revealed: int | str
Calling with an unknown union
from nonexistent import f # error: [unresolved-import] "Cannot resolve import `nonexistent`"
def coinflip() -> bool:
return True
if coinflip():
def f() -> int:
return 1
reveal_type(f()) # revealed: Unknown | int
Non-callable elements in a union
Calling a union with a non-callable element should emit a diagnostic.
def _(flag: bool):
if flag:
f = 1
else:
def f() -> int:
return 1
x = f() # error: [call-non-callable] "Object of type `Literal[1]` is not callable"
reveal_type(x) # revealed: int | Unknown
Multiple non-callable elements in a union
Calling a union with multiple non-callable elements should mention all of them in the diagnostic.
def _(flag: bool, flag2: bool):
if flag:
f = 1
elif flag2:
f = "foo"
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"
# revealed: int | Unknown
reveal_type(f())
All non-callable union elements
Calling a union with no callable elements can emit a simpler diagnostic.
def _(flag: bool):
if flag:
f = 1
else:
f = "foo"
x = f() # error: [call-non-callable] "Object of type `Literal[1, "foo"]` is not callable"
reveal_type(x) # revealed: Unknown
Mismatching signatures
Calling a union where the arguments don't match the signature of all variants.
def f1(a: int) -> int: ...
def f2(a: str) -> str: ...
def _(flag: bool):
if flag:
f = f1
else:
f = f2
# error: [invalid-argument-type] "Object of type `Literal[3]` cannot be assigned to parameter 1 (`a`) of function `f2`; expected type `str`"
x = f(3)
reveal_type(x) # revealed: int | str
Any non-callable variant
def f1(a: int): ...
def _(flag: bool):
if flag:
f = f1
else:
f = "This is a string literal"
# error: [call-non-callable] "Object of type `Literal["This is a string literal"]` is not callable"
x = f(3)
reveal_type(x) # revealed: Unknown
Union of binding errors
def f1(): ...
def f2(): ...
def _(flag: bool):
if flag:
f = f1
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"
x = f(3)
reveal_type(x) # revealed: Unknown
One not-callable, one wrong argument
class C: ...
def f1(): ...
def _(flag: bool):
if flag:
f = f1
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"
x = f(3)
reveal_type(x) # revealed: Unknown