[ty] Improve diagnostics for syntax errors in forward annotations#25158
[ty] Improve diagnostics for syntax errors in forward annotations#25158AlexWaygood wants to merge 4 commits into
Conversation
Typing conformance resultsNo changes detected ✅Current numbersThe percentage of diagnostics emitted that were expected errors held steady at 89.36%. The percentage of expected errors that received a diagnostic held steady at 85.49%. The number of fully passing files held steady at 88/134. |
Memory usage reportMemory usage unchanged ✅ |
|
d8cb69f to
b48c5b0
Compare
|
| [options] | ||
| exclude-newer = "2026-05-06T17:15:42Z" | ||
|
|
There was a problem hiding this comment.
this was added automatically by uv run crates/ty_python_semantic/mdtest.py. I assume it's a change in newer versions of uv?
| // Suppress diagnostics in unreachable code. This checks both whether | ||
| // the scope itself is unreachable and whether the specific statement or | ||
| // expression containing this diagnostic is unreachable. | ||
| if !ctx.is_range_reachable(range) { |
There was a problem hiding this comment.
Does is_range_reachable work with ranges that point into string annotations?
There was a problem hiding this comment.
I don't see why it wouldn't: the range inside a string annotation is still relative to the start of the file and is_range_reachable just uses contains_range:
71a8c76 to
2b4be22
Compare
0cf71d5 to
078b217
Compare
6852d23 to
d445f2c
Compare
d445f2c to
c57a5d8
Compare
|
Okay, I've removed mdtest's custom assertion-matching mechanism. The relevant APIs in the mdtest crate now accept closures that determine whether an assertion should match a given diagnostic, which allows us to hook into ty's error-suppression logic rather than handrolling something totally different in mdtest. I think this should hopefully be extensible for the ongoing project to use mdtests in Ruff too |
c57a5d8 to
8462ff8
Compare
Summary
Fixes astral-sh/ty#1627.
Here's an example diagnostic with the current release of ty:
On this branch, this diagnostic becomes:
The exact span of the node that creates the syntax error is now retained and highlighted in the diagnostic.
Implementation
Propagating the range of the node inside the string annotation into the diagnostic is trivial. However, naively implementing that quickly revealed that this would make diagnostics like this unsuppressable:
The primary range of the diagnostic is now specifically the
yield from range(42)part of the string rather than the string node as a whole. But I cannot add aty: ignorecomment that is either on or above theyield from range(42)part of the string -- the "comment" there would just become part of the string. In order to ensure that these diagnostics are suppressible (and testable in mdtest), therefore, it was necessary to add a way to attach a custom primary range to a diagnostic separate to that diagnostic's suppression range. This is something we've talked about doing lots of times before, so I hope it isn't too controversial.This in itself was also fairly trivial, but I soon realised that in order for mdtest to be able to recognise
# errorassertions correctly, we would need to retain the custom suppression ranges for diagnostics iff thetestingfeature was activated. This ended up feeling slightly icky (lots of#[cfg(feature = "testing")]attributes everywhere), but I'm not sure I see another way.The second commit of this PR improves the consistency of our parser error messages in general when it comes to capitalization.
Test Plan
Mdtests extended and updated