Skip to content

[ty] Disambiguate duplicate-looking overloaded callables in union display#23907

Merged
charliermarsh merged 2 commits intomainfrom
charlie/projection
Mar 12, 2026
Merged

[ty] Disambiguate duplicate-looking overloaded callables in union display#23907
charliermarsh merged 2 commits intomainfrom
charlie/projection

Conversation

@charliermarsh
Copy link
Member

Summary

Adjust type display so unions that contain distinct overloaded callables with identical single-line renderings include callable names for any ambiguous elements... This avoids outputs like A | A when the union actually contains two different function values (like str.upper | str.lower).

Closes astral-sh/ty#2919.

@astral-sh-bot astral-sh-bot bot added the ty Multi-file analysis & type inference label Mar 12, 2026
@charliermarsh charliermarsh added the diagnostics Related to reporting of diagnostics. label Mar 12, 2026
@astral-sh-bot
Copy link

astral-sh-bot bot commented Mar 12, 2026

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 85.29%. The percentage of expected errors that received a diagnostic held steady at 78.13%. The number of fully passing files held steady at 64/132.

@astral-sh-bot
Copy link

astral-sh-bot bot commented Mar 12, 2026

mypy_primer results

Changes were detected when running on open source projects
prefect (https://github.com/PrefectHQ/prefect)
- src/integrations/prefect-docker/tests/test_containers.py:26:31: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-docker/tests/test_containers.py:41:26: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-docker/tests/test_containers.py:54:31: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-docker/tests/test_containers.py:67:31: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-docker/tests/test_containers.py:80:31: error[invalid-await] `object` is not awaitable
+ src/integrations/prefect-docker/tests/test_containers.py:27:47: error[invalid-argument-type] Argument is incorrect: Expected `str`, found `str | bool | dict[str, int] | None`
+ src/integrations/prefect-docker/tests/test_containers.py:27:47: error[invalid-argument-type] Argument is incorrect: Expected `str | list[str] | None`, found `str | bool | dict[str, int] | None`
+ src/integrations/prefect-docker/tests/test_containers.py:27:47: error[invalid-argument-type] Argument is incorrect: Expected `str | None`, found `str | bool | dict[str, int] | None`
+ src/integrations/prefect-docker/tests/test_containers.py:27:47: error[invalid-argument-type] Argument is incorrect: Expected `bool | None`, found `str | bool | dict[str, int] | None`
+ src/integrations/prefect-docker/tests/test_containers.py:27:47: error[invalid-argument-type] Argument is incorrect: Expected `str | list[str] | None`, found `str | bool | dict[str, int] | None`
+ src/integrations/prefect-docker/tests/test_containers.py:27:47: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, str] | list[str] | None`, found `str | bool | dict[str, int] | None`
+ src/integrations/prefect-docker/tests/test_containers.py:27:47: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `str | bool | dict[str, int] | None`
+ src/integrations/prefect-docker/tests/test_containers.py:42:47: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `str`
+ src/integrations/prefect-docker/tests/test_containers.py:55:47: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `str`
+ src/integrations/prefect-docker/tests/test_containers.py:68:47: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `str`
+ src/integrations/prefect-docker/tests/test_containers.py:81:47: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `str`
- src/integrations/prefect-docker/tests/test_images.py:16:23: error[invalid-await] `object` is not awaitable
+ src/integrations/prefect-docker/tests/test_images.py:16:44: error[invalid-argument-type] Argument is incorrect: Expected `str`, found `str | bool`
+ src/integrations/prefect-docker/tests/test_images.py:16:44: error[invalid-argument-type] Argument is incorrect: Expected `str | None`, found `str | bool`
+ src/integrations/prefect-docker/tests/test_images.py:16:44: error[invalid-argument-type] Argument is incorrect: Expected `str | None`, found `str | bool`
+ src/integrations/prefect-docker/tests/test_images.py:16:44: error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `str | bool`
+ src/integrations/prefect-docker/tests/test_images.py:16:44: error[invalid-argument-type] Argument is incorrect: Expected `DockerHost | None`, found `str | bool`
+ src/integrations/prefect-docker/tests/test_images.py:16:44: error[invalid-argument-type] Argument is incorrect: Expected `DockerRegistryCredentials | None`, found `str | bool`
+ src/integrations/prefect-docker/tests/test_images.py:16:44: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `str | bool`
- src/integrations/prefect-docker/tests/test_images.py:28:27: error[invalid-await] `object` is not awaitable
+ src/integrations/prefect-docker/tests/test_images.py:29:47: error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `str`
+ src/integrations/prefect-docker/tests/test_images.py:29:47: error[invalid-argument-type] Argument is incorrect: Expected `DockerRegistryCredentials | None`, found `str`
+ src/integrations/prefect-docker/tests/test_images.py:29:47: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `str`
+ src/integrations/prefect-docker/tests/test_images.py:31:16: error[unresolved-attribute] Attribute `id` is not defined on `list[Unknown]` in union `Unknown | list[Unknown]`
- src/integrations/prefect-docker/tests/test_images.py:48:27: error[invalid-await] `object` is not awaitable
+ src/integrations/prefect-docker/tests/test_images.py:51:17: error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `str`
- src/integrations/prefect-docker/tests/test_images.py:63:28: error[invalid-await] `object` is not awaitable
+ src/integrations/prefect-docker/tests/test_images.py:51:17: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `str`
+ src/integrations/prefect-docker/tests/test_images.py:53:16: error[unresolved-attribute] Attribute `id` is not defined on `list[Unknown]` in union `Unknown | list[Unknown]`
+ src/integrations/prefect-docker/tests/test_images.py:64:47: error[invalid-argument-type] Argument is incorrect: Expected `str`, found `str | bool`
+ src/integrations/prefect-docker/tests/test_images.py:64:47: error[invalid-argument-type] Argument is incorrect: Expected `str | None`, found `str | bool`
+ src/integrations/prefect-docker/tests/test_images.py:64:47: error[invalid-argument-type] Argument is incorrect: Expected `str | None`, found `str | bool`
+ src/integrations/prefect-docker/tests/test_images.py:64:47: error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `str | bool`
+ src/integrations/prefect-docker/tests/test_images.py:64:47: error[invalid-argument-type] Argument is incorrect: Expected `DockerRegistryCredentials | None`, found `str | bool`
+ src/integrations/prefect-docker/tests/test_images.py:64:47: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `str | bool`
- src/integrations/prefect-kubernetes/prefect_kubernetes/jobs.py:425:33: error[invalid-await] `object` is not awaitable
+ src/integrations/prefect-kubernetes/prefect_kubernetes/jobs.py:428:17: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `str`
- src/integrations/prefect-kubernetes/tests/test_custom_objects.py:16:15: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_custom_objects.py:24:15: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_custom_objects.py:33:15: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_custom_objects.py:46:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_custom_objects.py:98:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_custom_objects.py:144:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_custom_objects.py:190:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_custom_objects.py:236:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_custom_objects.py:274:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_custom_objects.py:332:11: error[invalid-await] `object` is not awaitable
+ src/integrations/prefect-kubernetes/tests/test_custom_objects.py:20:13: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `None`
+ src/integrations/prefect-kubernetes/tests/test_custom_objects.py:29:13: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `None`
+ src/integrations/prefect-kubernetes/tests/test_custom_objects.py:38:13: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `None`
+ src/integrations/prefect-kubernetes/tests/test_custom_objects.py:57:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_custom_objects.py:103:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_custom_objects.py:149:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_custom_objects.py:195:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_custom_objects.py:240:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_custom_objects.py:286:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_custom_objects.py:344:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
- src/integrations/prefect-kubernetes/tests/test_deployments.py:16:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_deployments.py:34:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_deployments.py:68:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_deployments.py:88:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_deployments.py:110:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_deployments.py:137:11: error[invalid-await] `object` is not awaitable
+ src/integrations/prefect-kubernetes/tests/test_deployments.py:18:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_deployments.py:38:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_deployments.py:70:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_deployments.py:92:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_deployments.py:113:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_deployments.py:141:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
- src/integrations/prefect-kubernetes/tests/test_jobs.py:34:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_jobs.py:50:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_jobs.py:66:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_jobs.py:84:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_jobs.py:104:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_jobs.py:127:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_jobs.py:156:11: error[invalid-await] `object` is not awaitable
+ src/integrations/prefect-kubernetes/tests/test_jobs.py:36:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_jobs.py:52:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_jobs.py:68:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_jobs.py:87:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_jobs.py:107:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_jobs.py:131:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_jobs.py:159:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
- src/integrations/prefect-kubernetes/tests/test_pods.py:27:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_pods.py:42:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_pods.py:76:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_pods.py:92:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_pods.py:112:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_pods.py:133:11: error[invalid-await] `object` is not awaitable
- src/integrations/prefect-kubernetes/tests/test_pods.py:163:11: error[invalid-await] `object` is not awaitable
+ src/integrations/prefect-kubernetes/tests/test_pods.py:29:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_pods.py:46:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_pods.py:78:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_pods.py:96:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_pods.py:115:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_pods.py:137:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
+ src/integrations/prefect-kubernetes/tests/test_pods.py:167:9: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Any]`, found `Literal["test"]`
- src/prefect/cache_policies.py:311:25: error[unresolved-attribute] Attribute `__code__` is not defined on `((...) -> Any) & ((*args: object, **kwargs: object) -> object)` in union `Unknown | (((...) -> Any) & ((*args: object, **kwargs: object) -> object))`
+ src/prefect/cache_policies.py:311:25: error[unresolved-attribute] Attribute `__code__` is not defined on `(...) -> Any` in union `Unknown | ((...) -> Any)`
+ src/prefect/task_engine.py:1642:28: error[invalid-await] `Unknown | R@AsyncTaskRunEngine | Coroutine[Any, Any, R@AsyncTaskRunEngine]` is not awaitable
- src/prefect/tasks.py:185:9: error[unresolved-attribute] Attribute `__code__` is not defined on `((...) -> Any) & ((*args: object, **kwargs: object) -> object)` in union `Unknown | (((...) -> Any) & ((*args: object, **kwargs: object) -> object))`
+ src/prefect/tasks.py:185:9: error[unresolved-attribute] Attribute `__code__` is not defined on `(...) -> Any` in union `Unknown | ((...) -> Any)`
- src/prefect/tasks.py:795:13: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `(() -> str) | TaskRunNameCallbackWithParameters | str | None`, found `((() -> str) & ~<class 'NotSet'>) | TaskRunNameCallbackWithParameters | str | ... omitted 4 union elements`
+ src/prefect/tasks.py:795:13: error[invalid-argument-type] Argument to bound method `__init__` is incorrect: Expected `(() -> str) | TaskRunNameCallbackWithParameters | str | None`, found `(() -> str) | TaskRunNameCallbackWithParameters | str | ... omitted 3 union elements`
- src/prefect/utilities/_engine.py:64:13: error[invalid-argument-type] Argument to bound method `is_callback_with_parameters` is incorrect: Expected `(...) -> str`, found `(Unknown & Top[(...) -> object]) | (str & Top[(...) -> object]) | ((() -> str) & ((*args: object, **kwargs: object) -> object)) | (TaskRunNameCallbackWithParameters & ((*args: object, **kwargs: object) -> object))`
+ src/prefect/utilities/_engine.py:64:13: error[invalid-argument-type] Argument to bound method `is_callback_with_parameters` is incorrect: Expected `(...) -> str`, found `(Unknown & Top[(...) -> object]) | (str & Top[(...) -> object]) | (() -> str) | TaskRunNameCallbackWithParameters`
+ src/prefect/utilities/_engine.py:66:48: error[unknown-argument] Argument `parameters` does not match any known parameter
- src/prefect/utilities/_engine.py:85:12: error[invalid-return-type] Return type does not match returned value: expected `str`, found `object`
+ src/prefect/utilities/_engine.py:69:29: error[missing-argument] No argument provided for required parameter `parameters` of bound method `__call__`
- Found 5865 diagnostics
+ Found 5889 diagnostics

@astral-sh-bot
Copy link

astral-sh-bot bot commented Mar 12, 2026

Memory usage report

Memory usage unchanged ✅

@charliermarsh charliermarsh marked this pull request as ready for review March 12, 2026 01:15
@carljm carljm removed their request for review March 12, 2026 04:33
Comment on lines +2418 to +2429
fn duplicate_ambiguous_labels(element_labels: &[Option<String>]) -> FxHashSet<String> {
let mut counts = FxHashMap::default();

for label in element_labels.iter().flatten() {
*counts.entry(label.clone()).or_insert(0usize) += 1;
}

counts
.into_iter()
.filter_map(|(label, count)| (count > 1).then_some(label))
.collect()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can return an FxHashSet<&str> here and reduce the number of necessary clones:

diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs
index 2ed4ea0cad..1f5ac7b757 100644
--- a/crates/ty_python_semantic/src/types/display.rs
+++ b/crates/ty_python_semantic/src/types/display.rs
@@ -2415,11 +2415,11 @@ impl<'db> FmtDetailed<'db> for DisplayUnionType<'_, 'db> {
             element.display_with(db, settings.singleline()).to_string()
         }
 
-        fn duplicate_ambiguous_labels(element_labels: &[Option<String>]) -> FxHashSet<String> {
-            let mut counts = FxHashMap::default();
+        fn duplicate_ambiguous_labels(element_labels: &[Option<String>]) -> FxHashSet<&str> {
+            let mut counts: FxHashMap<&str, usize> = FxHashMap::default();
 
             for label in element_labels.iter().flatten() {
-                *counts.entry(label.clone()).or_insert(0usize) += 1;
+                *counts.entry(&**label).or_default() += 1;
             }
 
             counts
@@ -2491,7 +2491,7 @@ impl<'db> FmtDetailed<'db> for DisplayUnionType<'_, 'db> {
             } else {
                 displayed_entries += 1;
                 let settings = if label
-                    .as_ref()
+                    .as_deref()
                     .is_some_and(|label| duplicate_ambiguous_labels.contains(label))
                 {
                     self.settings.singleline().force_signature_name()

@AlexWaygood
Copy link
Member

Thanks!

@charliermarsh charliermarsh enabled auto-merge (squash) March 12, 2026 12:39
@charliermarsh charliermarsh disabled auto-merge March 12, 2026 12:44
…le.md

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
@charliermarsh charliermarsh enabled auto-merge (squash) March 12, 2026 12:49
@charliermarsh charliermarsh merged commit a25a4df into main Mar 12, 2026
50 checks passed
@charliermarsh charliermarsh deleted the charlie/projection branch March 12, 2026 12:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

diagnostics Related to reporting of diagnostics. ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Display of union containing two overloaded functions can inaccurately imply that the union contains duplicate elements

3 participants