[ty] Fix overload filtering to prefer more "precise" match#21859
[ty] Fix overload filtering to prefer more "precise" match#21859dhruvmanila merged 4 commits intomainfrom
Conversation
Diagnostic diff on typing conformance testsNo changes detected when running ty on typing conformance tests ✅ |
|
85aa937 to
ee8b013
Compare
ee8b013 to
a04f52f
Compare
| tracing::debug!( | ||
| target: "ty_python_semantic::types::call::bind", | ||
| matching_overload_index = ?self.matching_overload_index(), | ||
| signature = %self.signature_type.display(db), | ||
| "after step 1", | ||
| ); |
There was a problem hiding this comment.
The signature is important because we want to know which message is this for but this will be too verbose for functions containing multiple overloads. Maybe, it might be better to add some information about the call site instead like which call expression this is for.
| // The following loop is trying to construct a tuple of argument types that correspond to | ||
| // the participating parameter indexes. Considering the following example: | ||
| // | ||
| // ```python | ||
| // @overload | ||
| // def f(x: Literal[1], y: Literal[2]) -> tuple[int, int]: ... | ||
| // @overload | ||
| // def f(*args: Any) -> tuple[Any, ...]: ... | ||
| // | ||
| // f(1, 2) | ||
| // ``` | ||
| // | ||
| // Here, only the first parameter participates in the filtering process because only one | ||
| // overload has the second parameter. So, while going through the argument types, the | ||
| // second argument needs to be skipped but for the second overload both arguments map to | ||
| // the first parameter and that parameter is considered for the filtering process. This | ||
| // flag is to handle that special case of many-to-one mapping from arguments to parameters. | ||
| let mut variadic_parameter_handled = false; | ||
|
|
||
| for (argument_index, argument_type) in arguments.iter_types().enumerate() { | ||
| if variadic_parameter_handled { | ||
| continue; | ||
| } | ||
| for overload_index in matching_overload_indexes { | ||
| let overload = &self.overloads[*overload_index]; | ||
| for (parameter_index, variadic_argument_type) in | ||
| overload.argument_matches[argument_index].iter() | ||
| { | ||
| if overload.signature.parameters()[parameter_index].is_variadic() { | ||
| variadic_parameter_handled = true; | ||
| } |
There was a problem hiding this comment.
This is the diff that fixes the issue. I don't really like the change that much, I tried to see if this could be simplified and I have some ideas but might require some more time so I'll punt that on for post-beta stuff to do.
There was a problem hiding this comment.
Is there a risk of skipping keyword-variadic parameters here? If so, would it be useful to add a variation of the test case where the def f(*args: Any) -> tuple[Any, ...]: ... overload also takes keyword arguments (and we would make sure that this overload is still selected if keyword arguments are also passed in)?
There was a problem hiding this comment.
Yeah, this doesn't work well when keyword arguments are involved.
There was a problem hiding this comment.
Going to track the follow-up in astral-sh/ty#1825
| // The following loop is trying to construct a tuple of argument types that correspond to | ||
| // the participating parameter indexes. Considering the following example: | ||
| // | ||
| // ```python | ||
| // @overload | ||
| // def f(x: Literal[1], y: Literal[2]) -> tuple[int, int]: ... | ||
| // @overload | ||
| // def f(*args: Any) -> tuple[Any, ...]: ... | ||
| // | ||
| // f(1, 2) | ||
| // ``` | ||
| // | ||
| // Here, only the first parameter participates in the filtering process because only one | ||
| // overload has the second parameter. So, while going through the argument types, the | ||
| // second argument needs to be skipped but for the second overload both arguments map to | ||
| // the first parameter and that parameter is considered for the filtering process. This | ||
| // flag is to handle that special case of many-to-one mapping from arguments to parameters. | ||
| let mut variadic_parameter_handled = false; | ||
|
|
||
| for (argument_index, argument_type) in arguments.iter_types().enumerate() { | ||
| if variadic_parameter_handled { | ||
| continue; | ||
| } | ||
| for overload_index in matching_overload_indexes { | ||
| let overload = &self.overloads[*overload_index]; | ||
| for (parameter_index, variadic_argument_type) in | ||
| overload.argument_matches[argument_index].iter() | ||
| { | ||
| if overload.signature.parameters()[parameter_index].is_variadic() { | ||
| variadic_parameter_handled = true; | ||
| } |
There was a problem hiding this comment.
Is there a risk of skipping keyword-variadic parameters here? If so, would it be useful to add a variation of the test case where the def f(*args: Any) -> tuple[Any, ...]: ... overload also takes keyword arguments (and we would make sure that this overload is still selected if keyword arguments are also passed in)?
…return * dcreager/die-die-intersections: (29 commits) simpler bounds [`pylint`] Detect subclasses of builtin exceptions (`PLW0133`) (#21382) Fix stack overflow with recursive generic protocols (depth limit) (#21858) New diagnostics for unused range suppressions (#21783) [ty] Use default settings in completion tests [ty] Infer type variables within generic unions (#21862) [ty] Fix overload filtering to prefer more "precise" match (#21859) [ty] Stabilize auto-import [ty] Fix reveal-type E2E test (#21865) [ty] Use concise message for LSP clients not supporting related diagnostic information (#21850) Include more details in Tokens 'offset is inside token' panic message (#21860) apply range suppressions to filter diagnostics (#21623) [ty] followup: add-import action for `reveal_type` too (#21668) [ty] Enrich function argument auto-complete suggestions with annotated types [ty] Add autocomplete suggestions for function arguments [`flake8-bugbear`] Accept immutable slice default arguments (`B008`) (#21823) [`pydocstyle`] Suppress `D417` for parameters with `Unpack` annotations (#21816) [ty] Remove legacy `concise_message` fallback behavior (#21847) [ty] Make Python-version subdiagnostics less verbose (#21849) [ty] Supress inlay hints when assigning a trivial initializer call (#21848) ...
…return * dcreager/die-die-intersections: (31 commits) clippy reword comment simpler bounds [`pylint`] Detect subclasses of builtin exceptions (`PLW0133`) (#21382) Fix stack overflow with recursive generic protocols (depth limit) (#21858) New diagnostics for unused range suppressions (#21783) [ty] Use default settings in completion tests [ty] Infer type variables within generic unions (#21862) [ty] Fix overload filtering to prefer more "precise" match (#21859) [ty] Stabilize auto-import [ty] Fix reveal-type E2E test (#21865) [ty] Use concise message for LSP clients not supporting related diagnostic information (#21850) Include more details in Tokens 'offset is inside token' panic message (#21860) apply range suppressions to filter diagnostics (#21623) [ty] followup: add-import action for `reveal_type` too (#21668) [ty] Enrich function argument auto-complete suggestions with annotated types [ty] Add autocomplete suggestions for function arguments [`flake8-bugbear`] Accept immutable slice default arguments (`B008`) (#21823) [`pydocstyle`] Suppress `D417` for parameters with `Unpack` annotations (#21816) [ty] Remove legacy `concise_message` fallback behavior (#21847) ...
…return * dcreager/die-die-intersections: (31 commits) clippy reword comment simpler bounds [`pylint`] Detect subclasses of builtin exceptions (`PLW0133`) (#21382) Fix stack overflow with recursive generic protocols (depth limit) (#21858) New diagnostics for unused range suppressions (#21783) [ty] Use default settings in completion tests [ty] Infer type variables within generic unions (#21862) [ty] Fix overload filtering to prefer more "precise" match (#21859) [ty] Stabilize auto-import [ty] Fix reveal-type E2E test (#21865) [ty] Use concise message for LSP clients not supporting related diagnostic information (#21850) Include more details in Tokens 'offset is inside token' panic message (#21860) apply range suppressions to filter diagnostics (#21623) [ty] followup: add-import action for `reveal_type` too (#21668) [ty] Enrich function argument auto-complete suggestions with annotated types [ty] Add autocomplete suggestions for function arguments [`flake8-bugbear`] Accept immutable slice default arguments (`B008`) (#21823) [`pydocstyle`] Suppress `D417` for parameters with `Unpack` annotations (#21816) [ty] Remove legacy `concise_message` fallback behavior (#21847) ...
* origin/main: (33 commits) [ty] Simplify union lower bounds and intersection upper bounds in constraint sets (#21871) [ty] Collapse `never` paths in constraint set BDDs (#21880) Fix leading comment formatting for lambdas with multiple parameters (#21879) [ty] Type inference for `@asynccontextmanager` (#21876) Fix comment placement in lambda parameters (#21868) [`pylint`] Detect subclasses of builtin exceptions (`PLW0133`) (#21382) Fix stack overflow with recursive generic protocols (depth limit) (#21858) New diagnostics for unused range suppressions (#21783) [ty] Use default settings in completion tests [ty] Infer type variables within generic unions (#21862) [ty] Fix overload filtering to prefer more "precise" match (#21859) [ty] Stabilize auto-import [ty] Fix reveal-type E2E test (#21865) [ty] Use concise message for LSP clients not supporting related diagnostic information (#21850) Include more details in Tokens 'offset is inside token' panic message (#21860) apply range suppressions to filter diagnostics (#21623) [ty] followup: add-import action for `reveal_type` too (#21668) [ty] Enrich function argument auto-complete suggestions with annotated types [ty] Add autocomplete suggestions for function arguments [`flake8-bugbear`] Accept immutable slice default arguments (`B008`) (#21823) ...
Summary
fixes: astral-sh/ty#1809
I took this chance to add some debug level tracing logs for overload call evaluation similar to Doug's implementation in
constraints.rs.Test Plan
sqlalchemy.selectin pyx which results in the correct overload being matched