fix emit unused parameter #955#1447
Conversation
|
I'm a little worried about how long this would make the [no-matching-overload] error message. I agree that the line breaks make the individual signatures more readable, but this could easily turn, say, a 10-line message into a 100-line one. And I assume the syntax highlighting doesn't show up unless you do an extra step of piping to bat, so it wouldn't be there by default to help with parsing the output? I wonder if it would be better to instead implement the other suggestion in #955, hiding unrelated fields. That could make these messages more readable without increasing their length. |
rchen152
left a comment
There was a problem hiding this comment.
Marking this as "Changes requested", since I don't think we can merge it as-is. My main concern is about error message length, but looking more closely at the PR, it also puts the # [closest match] comment in an odd place, so that would need to be fixed as well.
There was a problem hiding this comment.
Pull request overview
This PR improves the readability of CLI overload error messages by formatting them as syntactically valid Python code that can be piped to syntax highlighters like bat. The changes enable better visualization of long, complex overload signatures by marking the "[closest match]" indicator as a Python comment.
Changes:
- Changed the overload closest match marker from
" [closest match]"to"# [closest match]"to make it a Python comment - Updated test expectations to reflect the new formatting that includes
defprefix, newlines for parameters, and proper Python syntax - Updated conformance test expectations across multiple test cases to match the new format
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| pyrefly/lib/alt/overload.rs | Changed closest match suffix from space-prefixed to hash-prefixed to create a valid Python comment |
| pyrefly/lib/test/overload.rs | Updated test expectation to match new multi-line format with Python syntax |
| conformance/third_party/conformance.exp | Updated multiple test expectations to reflect new overload formatting with def syntax and comment markers |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
this will be much shorter |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
i'm gonna re-trigger CI |
| def f(x: str, y: str, z: str, w: str) -> str: ... | ||
| def f(x, y, z=None, w=None): return x | ||
|
|
||
| f(1, 2) # E: (x: int, y: int, ...) -> int [missing required arguments] # !E: z: # !E: w: |
There was a problem hiding this comment.
Omitting z and w here doesn't seem like an improvement. Since the error is that they're missing, we should show them so that the user can see what arguments they need to pass in and in what order.
IMO the obvious case in which it would be useful to omit parameters would a wrong argument type error, in which the user has passed in the right number of arguments but one or more has the wrong type, so we want the error message to highlight those.
| return full_signature(); | ||
| }; | ||
|
|
||
| let mut positional_remaining = args |
There was a problem hiding this comment.
Reconstructing the argument->parameter mapping for error reporting seems inefficient and error-prone. We already return the mapping from callable_infer, so it would be nice if we could reuse it:
pyrefly/pyrefly/lib/alt/callable.rs
Line 1238 in c12e9d6
|
is this still relevant with our current error messages? |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
@rchen152 has imported this pull request. If you are a Meta employee, you can view this in D107938504. |
|
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅ |
|
Sorry for the delayed response here! I ended up taking a different approach to this issue, in #3782. The key differences are that that PR has a narrower focus (just omits irrelevant parameter types, doesn't try to do any handling of other kinds of call errors), reuses the existing signature display code, tracks parameters by name instead of index, and filters all overload signatures rather than just the closest one. I'm going to close this PR now, but thank you for all of your contributions. Apologies again for sitting on this for so long. |
Summary: No functional change. I'm planning to add more info to this map, so let's first rename it and make it more structured. Context: This stack is a ground-up re-implementation of #1447. It has a narrower focus (just omits irrelevant parameter types, doesn't try to do any handling of other kinds of call errors), reuses the existing signature display code, tracks parameters by name instead of index, and filters all overload signatures rather than just the closest one. Reviewed By: grievejia Differential Revision: D108230388 fbshipit-source-id: 3a7f63d9143cc2767566f414b7994e4eff015d5c
Summary: No functional change. This will be needed to accurately map keyword arguments to their corresponding parameters. Context: This stack is a ground-up re-implementation of #1447. It has a narrower focus (just omits irrelevant parameter types, doesn't try to do any handling of other kinds of call errors), reuses the existing signature display code, tracks parameters by name instead of index, and filters all overload signatures rather than just the closest one. Reviewed By: grievejia Differential Revision: D108230391 fbshipit-source-id: 32b3ded028ee5349cd750ca3a8880b20604e84f9
Summary: Adds parameter names to the map that callable_infer builds from arguments to matched parameters. This will be used to improve signature printing in no-matching-overload errors. No functional change. Context: This stack is a ground-up re-implementation of #1447. It has a narrower focus (just omits irrelevant parameter types, doesn't try to do any handling of other kinds of call errors), reuses the existing signature display code, tracks parameters by name instead of index, and filters all overload signatures rather than just the closest one. Reviewed By: grievejia Differential Revision: D108230387 fbshipit-source-id: b7eda2ffbbf6904e3324f96eff0d7e12c97ad951
Summary: No functional change. Restructures ParamList:fmt_with_type to first compute what it needs to write, then write everything in a block at the end. This will make the next diff easier to read. Context: This stack is a ground-up re-implementation of #1447. It has a narrower focus (just omits irrelevant parameter types, doesn't try to do any handling of other kinds of call errors), reuses the existing signature display code, tracks parameters by name instead of index, and filters all overload signatures rather than just the closest one. Reviewed By: yangdanny97 Differential Revision: D108230392 fbshipit-source-id: 980152f39bc882f57ab8670308dad64bf4be6e93
Summary: Adds a `ParamOverlay` enum to control which parameters are included when displaying a ParamsList. The enum has two variants: `All` (include all params) and `Subset(SmallSet<Name>)` (include only this set of params). Anonymous params (name=None) are always included. Params that are not included are replaced with `...`. `/` and `*` markers are always included. This will be used in the next diff to improve signature display in no-matching-overload errors. Context: This stack is a ground-up re-implementation of #1447. It has a narrower focus (just omits irrelevant parameter types, doesn't try to do any handling of other kinds of call errors), reuses the existing signature display code, tracks parameters by name instead of index, and filters all overload signatures rather than just the closest one. Reviewed By: grievejia Differential Revision: D108230385 fbshipit-source-id: 50a4425640cdd31caf8bfc644f07d3cab7fc6545
Summary: Stores required parameters that were not matched by any arguments in ArgMap. This will be used to improve no-matching-overload error messages. Context: This stack is a ground-up re-implementation of #1447. It has a narrower focus (just omits irrelevant parameter types, doesn't try to do any handling of other kinds of call errors), reuses the existing signature display code, tracks parameters by name instead of index, and filters all overload signatures rather than just the closest one. Reviewed By: yangdanny97 Differential Revision: D108250102 fbshipit-source-id: a6ef981f46cfad0b2dc1af19c50447e132d93913
Summary:
When a call to an overloaded function fails but has the right number of arguments, show only the parameters corresponding to passed arguments and required missing arguments when printing possible overloads. This makes the signatures easier to read by hiding irrelevant information.
Before:
```
ERROR No matching overload found for function `subprocess.run` called with arguments: (Literal[42], capture_output=Any) [no-matching-overload]
--> empty.py:6:15
|
6 | subprocess.run(42, capture_output=x)
| ^^^^^^^^^^^^^^^^^^^^^^
|
Possible overloads:
(args: _CMD, bufsize: int = -1, executable: PathLike[bytes] | PathLike[str] | bytes | str | None = None, stdin: _FILE = None, stdout: _FILE = None, stderr: _FILE = None, preexec_fn: (() -> object) | None = None, close_fds: bool = True, shell: bool = False, cwd: PathLike[bytes] | PathLike[str] | bytes | str | None = None, env: Mapping[bytes, StrOrBytesPath] | Mapping[str, StrOrBytesPath] | None = None, universal_newlines: Literal[True] | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, pass_fds: Collection[int] = ..., *, capture_output: bool = False, check: bool = False, encoding: str | None = None, errors: str | None = None, input: str | None = None, text: Literal[True], timeout: float | None = None, user: int | str | None = None, group: int | str | None = None, extra_groups: Iterable[int | str] | None = None, umask: int = -1, pipesize: int = -1, process_group: int | None = None) -> CompletedProcess[str]
(args: _CMD, bufsize: int = -1, executable: PathLike[bytes] | PathLike[str] | bytes | str | None = None, stdin: _FILE = None, stdout: _FILE = None, stderr: _FILE = None, preexec_fn: (() -> object) | None = None, close_fds: bool = True, shell: bool = False, cwd: PathLike[bytes] | PathLike[str] | bytes | str | None = None, env: Mapping[bytes, StrOrBytesPath] | Mapping[str, StrOrBytesPath] | None = None, universal_newlines: bool | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, pass_fds: Collection[int] = ..., *, capture_output: bool = False, check: bool = False, encoding: str, errors: str | None = None, input: str | None = None, text: bool | None = None, timeout: float | None = None, user: int | str | None = None, group: int | str | None = None, extra_groups: Iterable[int | str] | None = None, umask: int = -1, pipesize: int = -1, process_group: int | None = None) -> CompletedProcess[str]
(args: _CMD, bufsize: int = -1, executable: PathLike[bytes] | PathLike[str] | bytes | str | None = None, stdin: _FILE = None, stdout: _FILE = None, stderr: _FILE = None, preexec_fn: (() -> object) | None = None, close_fds: bool = True, shell: bool = False, cwd: PathLike[bytes] | PathLike[str] | bytes | str | None = None, env: Mapping[bytes, StrOrBytesPath] | Mapping[str, StrOrBytesPath] | None = None, universal_newlines: bool | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, pass_fds: Collection[int] = ..., *, capture_output: bool = False, check: bool = False, encoding: str | None = None, errors: str, input: str | None = None, text: bool | None = None, timeout: float | None = None, user: int | str | None = None, group: int | str | None = None, extra_groups: Iterable[int | str] | None = None, umask: int = -1, pipesize: int = -1, process_group: int | None = None) -> CompletedProcess[str]
(args: _CMD, bufsize: int = -1, executable: PathLike[bytes] | PathLike[str] | bytes | str | None = None, stdin: _FILE = None, stdout: _FILE = None, stderr: _FILE = None, preexec_fn: (() -> object) | None = None, close_fds: bool = True, shell: bool = False, cwd: PathLike[bytes] | PathLike[str] | bytes | str | None = None, env: Mapping[bytes, StrOrBytesPath] | Mapping[str, StrOrBytesPath] | None = None, *, universal_newlines: Literal[True], startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, pass_fds: Collection[int] = ..., capture_output: bool = False, check: bool = False, encoding: str | None = None, errors: str | None = None, input: str | None = None, text: Literal[True] | None = None, timeout: float | None = None, user: int | str | None = None, group: int | str | None = None, extra_groups: Iterable[int | str] | None = None, umask: int = -1, pipesize: int = -1, process_group: int | None = None) -> CompletedProcess[str]
(args: _CMD, bufsize: int = -1, executable: PathLike[bytes] | PathLike[str] | bytes | str | None = None, stdin: _FILE = None, stdout: _FILE = None, stderr: _FILE = None, preexec_fn: (() -> object) | None = None, close_fds: bool = True, shell: bool = False, cwd: PathLike[bytes] | PathLike[str] | bytes | str | None = None, env: Mapping[bytes, StrOrBytesPath] | Mapping[str, StrOrBytesPath] | None = None, universal_newlines: Literal[False] | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, pass_fds: Collection[int] = ..., *, capture_output: bool = False, check: bool = False, encoding: None = None, errors: None = None, input: Buffer | None = None, text: Literal[False] | None = None, timeout: float | None = None, user: int | str | None = None, group: int | str | None = None, extra_groups: Iterable[int | str] | None = None, umask: int = -1, pipesize: int = -1, process_group: int | None = None) -> CompletedProcess[bytes] [closest match]
(args: _CMD, bufsize: int = -1, executable: PathLike[bytes] | PathLike[str] | bytes | str | None = None, stdin: _FILE = None, stdout: _FILE = None, stderr: _FILE = None, preexec_fn: (() -> object) | None = None, close_fds: bool = True, shell: bool = False, cwd: PathLike[bytes] | PathLike[str] | bytes | str | None = None, env: Mapping[bytes, StrOrBytesPath] | Mapping[str, StrOrBytesPath] | None = None, universal_newlines: bool | None = None, startupinfo: Any = None, creationflags: int = 0, restore_signals: bool = True, start_new_session: bool = False, pass_fds: Collection[int] = ..., *, capture_output: bool = False, check: bool = False, encoding: str | None = None, errors: str | None = None, input: Buffer | str | None = None, text: bool | None = None, timeout: float | None = None, user: int | str | None = None, group: int | str | None = None, extra_groups: Iterable[int | str] | None = None, umask: int = -1, pipesize: int = -1, process_group: int | None = None) -> CompletedProcess[Any]
Argument `Literal[42]` is not assignable to parameter `args` with type `PathLike[bytes] | PathLike[str] | Sequence[StrOrBytesPath] | bytes | str` in function `subprocess.run`
```
After:
```
ERROR No matching overload found for function `subprocess.run` called with arguments: (Literal[42], capture_output=Any) [no-matching-overload]
--> empty.py:6:15
|
6 | subprocess.run(42, capture_output=x)
| ^^^^^^^^^^^^^^^^^^^^^^
|
Possible overloads:
(args: _CMD, ..., *, capture_output: bool = False, ...) -> CompletedProcess[str]
(args: _CMD, ..., *, capture_output: bool = False, ...) -> CompletedProcess[str]
(args: _CMD, ..., *, capture_output: bool = False, ...) -> CompletedProcess[str]
(args: _CMD, ..., *, ..., capture_output: bool = False, ...) -> CompletedProcess[str]
(args: _CMD, ..., *, capture_output: bool = False, ...) -> CompletedProcess[bytes] [closest match]
(args: _CMD, ..., *, capture_output: bool = False, ...) -> CompletedProcess[Any]
Argument `Literal[42]` is not assignable to parameter `args` with type `PathLike[bytes] | PathLike[str] | Sequence[StrOrBytesPath] | bytes | str` in function `subprocess.run`
```
Closes #955. There are two requests in that issue (hide unrelated fields, add line breaks). I don't think we should do the second, because it would make no-matching-overload errors very long, so this completes the actionable part.
Context:
This stack is a ground-up re-implementation of #1447. It has a narrower focus (just omits irrelevant parameter types, doesn't try to do any handling of other kinds of call errors), reuses the existing signature display code, tracks parameters by name instead of index, and filters all overload signatures rather than just the closest one.
Reviewed By: samwgoldman
Differential Revision: D108230384
fbshipit-source-id: 1a29b445d7daccf703fa64927416f2b425bd1f89
part of #955
callable_infer now records not only the expected parameter type for each call argument, but also which parameter index the argument matched.
Overload “No matching overload” diagnostics use that real argument-to-parameter mapping instead of reconstructing it from positional counts / keyword names.
If the failure is missing required args, show the full required signature so users can see what to pass, e.g. z / w.
If the failure is wrong argument type, truncate unrelated optional params with ... so the relevant parameters stand out.