I was looking at improving some error messages in pyanalyze and thought it’d be useful to get some community feedback. I am not looking to standardize these error messages (different type checkers can make different choices and that’s fine), but I’ll take the feedback here into account for pyanalyze, and perhaps other type checkers will do the same.
I will write down a few basic programs with type checking errors, outputs from some type checkers, and a few of my thoughts, and I’d be interested to hear any input from others.
Incompatible return type
def f() -> str:
return False
- pyanalyze (as of #735; before it was much worse):
Incompatible return type: expected str, got Literal[False] (code: incompatible_return_value) - pyright:
Expression of type "Literal[False]" cannot be assigned to return type "str" - mypy:
Incompatible return value type (got "bool", expected "str") [return-value] - pyre:
Incompatible return type [7]: Expected `str` but got `bool`.
Thoughts:
- pyright’s message feels too verbose and buries the fact that this is about a return type
- pyanalyze and pyright say “Literal[False]” instead of “bool”. I think this is mostly driven by how their type inference works (definitely true for pyanalyze).
- mypy puts the actual type first, pyre the expected type. I chose to follow pyre in the change I just made, but I’m not sure which is better.
Incompatible argument type
def f(x: str) -> str:
return ""
f(1)
- pyanalyze:
Incompatible argument type for x: expected str but got Literal[1] (code: incompatible_argument) - pyright:
Argument of type "Literal[1]" cannot be assigned to parameter "x" of type "str" in function "f" - mypy:
Argument 1 to "f" has incompatible type "int"; expected "str" [arg-type] - pyre:
Incompatible parameter type [6]: In call `f`, for 1st positional argument, expected `str` but got `int`.
Thoughts:
- I feel the “assigned” language in the pyright message is a little confusing, since this isn’t an assignment, though I understand the technical background of that message.
- pyanalyze and pyright choose to mention the name of the parameter, mypy and pyre mention the position of the argument. Either is probably defensible.
Incompatible local variable type
def f(x: str) -> None:
y: int = x
- pyanalyze:
Incompatible assignment: expected int, got str (code: incompatible_assignment) - pyright:
Expression of type "str" cannot be assigned to declared type "int" - mypy:
Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] - pyre:
Incompatible variable type [9]: y is declared to have type `int` but is used as type `str`.
Thoughts:
- pyanalyze wins on terseness here. That’s not necessarily a good thing, but I think the error is still clear
- “used as” in the Pyre message feels wrong, we’re not “using” y.