Skip to content

Conversation

@AlexWaygood
Copy link
Member

@AlexWaygood AlexWaygood added ty Multi-file analysis & type inference diagnostics Related to reporting of diagnostics. labels Oct 23, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Oct 23, 2025

Diagnostic diff on typing conformance tests

No changes detected when running ty on typing conformance tests ✅

@github-actions
Copy link
Contributor

github-actions bot commented Oct 23, 2025

mypy_primer results

No ecosystem changes detected ✅
No memory usage changes detected ✅

Copy link
Member

Choose a reason for hiding this comment

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

The secondary info message won't be shown with --output-format=concise or in the main diagnostic shown in editors (unless they supported related information which many dont).

Given that this is the most important bit, I'm inclined to make it part of the primary annotation message, although that's somewhat tricky

Copy link
Member Author

@AlexWaygood AlexWaygood Oct 23, 2025

Choose a reason for hiding this comment

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

I think we may have had this conversation before... I think we may need to rethink our diagnostic model here. It's often not feasible to encapsulate all relevant information in a single line, and mypy, for example, will often display secondary diagnostics even if you do not specify --pretty. (Mypy uses a concise output format by default; --pretty gives you annotations as part of diagnostics, similar to our output format.) E.g. https://mypy-play.net/?mypy=latest&python=3.12&gist=8322703266ffc5e8e2ccb9538e2487c3

The approach I'm taking here is also exactly what we already do in similar diagnostics, so I'm inclined to say that if we want to change this approach, we should do so for all our diagnostics together:

error[invalid-argument-type]: Argument to function `f2` is incorrect
--> src/mdtest_snippet.py:14:11
|
12 | # error: [too-many-positional-arguments]
13 | # error: [invalid-argument-type]
14 | x = f(3)
| ^ Expected `str`, found `Literal[3]`
|
info: Function defined here
--> src/mdtest_snippet.py:4:5
|
2 | return 0
3 |
4 | def f2(name: str) -> int:
| ^^ --------- Parameter declared here
5 | return 0
|
info: Union variant `def f2(name: str) -> int` is incompatible with this call site
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int)`
info: rule `invalid-argument-type` is enabled by default

Copy link
Member

Choose a reason for hiding this comment

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

It's probably worth changing it everywhere because using sub diagnostics with info won't show with --output-format=concise or in the editor. The reason I opened this issue was because I didn't had enough information in the editor to fix the diagnostic.

I'd be inclined to change the message to:

Expected `Sized`, element `Foo` of `str | Foo` is not assignable to `Sized`

Copy link
Member Author

Choose a reason for hiding this comment

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

using sub diagnostics with info won't show with --output-format=concise

yes, that's the thing I'm suggesting we might want to rethink when I said "we may need to rethink our diagnostic model here". I don't think that's a great outcome. I think it's going to end up with worse diagnostics than mypy for CLI users. It's often silly to try to cram all relevant information onto one line, and for things like overloads it's arguably impossible.

or in the editor

Whenever I need more information on a diagnostic rust-analyzer is showing me in the "Problems" tab in VSCode, I always click on the "see full diagnostic" option, and then a new tab opens up with all subdiagnostics displayed as well as the main diagnostic. Is it not possible for us to implement something similar for users of the ty server?

Copy link
Member Author

Choose a reason for hiding this comment

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

And it seems to me that I'm already being shown several subdiagnostics on-hover here in VSCode, for example, even without clicking on the "Click for full compiler diagnostic" option?

image

Copy link
Member Author

Choose a reason for hiding this comment

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

Though even for on-hover, we may want to avoid displaying all union elements in situations where the union is 60 elements long...?

Copy link
Member Author

Choose a reason for hiding this comment

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

In general I think we infer unions more than any other Python type checker. This is not only because we treat intersections as first-class types, but also because we have concepts such as "class-literal types" and "function-literal types". To my knowledge, no other Python type checker treats these concepts as first-class types, and treating them as such seems to mean that huge unions are more common for us than for other type checkers

Copy link
Member

Choose a reason for hiding this comment

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

Screenshot 2025-10-23 at 14 32 24

Typescript does truncate very long unions

Screenshot 2025-10-23 at 14 34 04

https://www.typescriptlang.org/play/?#code/PTAEDEFcDsGMBcCWB7ap4AsCG9SoDYCeoWssApgA7wDOoN8ATotAOY0BQAZjAimpWbR4AZSYtWACgBuWfJHIAueuLYBKZdOSIAJqADeHUKFioayfOQB0+ZFNnzyagNwcAvhw4hQAFQyI6AHdEfHxQckZGZEZQACNyWCxIGnJ0DFSsaFRCAFtkZNAYfhNUeCwWOgAWACZuXiRUdHIGSSNQRHhyHOU24wAfUAAiLEpKS0He0AHB2My5ieN+odh0yMIFxemdHHINpcHyfB0I+LW9qaGuRFZz6dZGEd3J6YxUckJjwNuhgGtEYO+g0seWggJymVYyEB0ASZSET0WF0G0QhCM2Q0oIywhCwgIAjpAWBRAQ8aJRTox1s8hgwHoEKVTEdMymwIiw0ftAjtGDlDqhASNmKZ4IDYvhSD8GaLHFLqYNEsI5PlKBykaZTNBICK5bAHtBZUyhjoHpDoFxGITtYbBpDkCkDeibQ8VebLYDWJAsLJAQArCWujqA-CIXlBwgrciq6bg6DbIRgyD4B37CyIaRRjHkUgYQEqrCMXMRGghkG59kjMYZwZjSA5XPIXn3OadXMWmEk8rF0Fyhj5gNWx3wBs4KHUmrGbwAOVrp3aaEwqSKqAAhBwNKAtLoDG1BCxRKopB0ui5QN4AKKRaLKTU5WcBUBZXBYGjF1jQLBi1JDlRCVirjxeGAZ7pmgeSMKkpg5JWAAeyiZNkeQFEuaDBJgeCxD6sLoIQKqcDwcANAIUQUC+ADCDYwQAkp0OStMYR7dNS+jtDo14zhEzgPlgvLKLSEicaQSDpsosTIBYWZoB4hrMbobG3hx6AdJYvEHpxlCQGKATpKxcRiZYmSgFJ6IyTpN6nJx4rxPgKm-px0gBIgn4iXpEmGUxLFyeZoDHDQuqINQ-A2fx4Tvp+OmieJBlGUsJmeQpZSsEFbCcVwWbwJA4HhS5UXubJD7sYwnGJJ0kKUklrACYwKxpuQWWRZJuWmQVdlyAo5WcSklgILVzn1W50kefl8mFSUwjkMI7UkKMUTpnV+kNQNeVmfF5DQfAk3pswVw9bpfXRRcsVDV5iQBagk3+KwGDBpdnRza5+0DIdy0jekWA6BIk27uCZW7fN-XGYNz0dRpr3vWwk0pBqcaEL1f0PQYgPNfQtbfTDP7BatmKxjtEVw41cUjY+Sjo8loCUCwMJ3Tli1NcNRUNryE0kxVoBcOKrCsDj2ULQDS1I7yL5YJzk3gW9sP3fjR0KTQGmYQgENwpl4vU7ztNeUO5OwJNTDjWDiW-RLNME5xPzvIE0Q6XxpNMBKXN7ZLQNkxgpLE1bLMyxzzS3crPMxYjdOgASyCdJNXBetER5U77B3+15NDQP5KrrcznERT832m1H-1+3zAerRQjDUBD2BKwbKs52rCmYiazoYJNxpYFw3tl9Hj2x9L42dHAruqSUUGWM3uOG6rxugObjCW73gjICRKRZ-DT1I50PKTSwxzQXbeNG1LI3HFcCeESL5D2eQgSb8PFej5j4rvofKcbnIug7PPDtI7A4pbYgxWBffGpXDy59y4x1zl5CoTBIB8DOvfVaCQtSANbgjEBCl3rgT4MJe+IZKyM0HtzbOwDK4jQ9LoQ47JQ4WFsGfF+29HYWmUtAs00QKBUJHjvNSqZYBozdgJUYwZ4F4Lbkgka08mEZR7rZbyM9aydz4QvduQiohDlMNZaBvZNI0G0j7fhiCCEdRZHGSe4i3rIGoDI1+AcyQJEQFcb+UCuHeXIPvUx1CkbgQJIgcC2CIY4ACNtZhl9WEmGYMvH+djeTJyHkAgROjRrvTvnYng+ArihCcSwx2GpaTlCZnY8CFjup+PwaPYMOQOjeNsb3CUWRAiWB0JzfJUTR6mGOKPBwT8cF7QnGAAA5I08gnT2h0Gequdcm49CGA6b4Sk6BkAkDIM0OgWAyZRBVIweAxBMA4HCNBAItA4halANgOgRIKLeM-NhXCbR0m4B6QANVamIiQoAAC87QaJWB6ZxU8QFLyMH-EAA

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

That's true. But no other type checker seems to truncate the union type :)

I'm fine merging this as is but I really think our approach to truncating unions is problematic

@AlexWaygood AlexWaygood enabled auto-merge (squash) October 23, 2025 13:11
@AlexWaygood AlexWaygood merged commit dab3d4e into main Oct 23, 2025
36 checks passed
@AlexWaygood AlexWaygood deleted the alex/union-diag branch October 23, 2025 13:16
)),
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I assumed we would fix this somewhere else, e.g. when actually checking assignability in the first place. This code here only affects invalid-argument-type, but we should do the same thing in other situations, like for actual assignments to variables. Consider:

from typing import Sized

class Foo: ...

def g(
    b: list[str]
    | str
    | dict[str, str]
    | tuple[str, ...]
    | bytes
    | frozenset[str]
    | set[str]
    | Foo,
):
    y: Sized = b  # still only shows the truncated union

Copy link
Member Author

Choose a reason for hiding this comment

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

A more general solution would probably be #19580 (so that we always propagate up the reason why an assignability/subtyping relation did not hold). I do think we need something like that eventually, for better diagnostics in cases where Protocol subtyping doesn't hold (our diagnostics are currently terrible for protocols with lots of members — you can't tell which specific member of the protocol wasn't satisfied). But that would be a big change, and I'm not yet sure how it would fit in with Doug's work on the new constraint solver.

We already do ad-hoc special-casing of union types like this for several other diagnostics. I agree it's not great but I think it's the best we can do for now, and it's better than nothing in the short term.

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.

Improve invalid-argument diagnostic for union types

4 participants