-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[red-knot] ruff_db: make diagnostic rendering prettier #15856
Conversation
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks fantastic to me!
27869d0
to
f292f01
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So good!
| | ||
1 | | ||
2 | y = 4 / 0 | ||
| ----- Cannot divide object of type `Literal[4]` by zero |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok really dumb question, but why does annotate-snippets choose ^^^^
in some cases but ----
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question actually! It chooses a different mark based on the severity level:
ruff/crates/ruff_annotate_snippets/src/renderer/display_list.rs
Lines 716 to 723 in b0b8b06
let mark = match annotation.annotation_type { | |
DisplayAnnotationType::Error => '^', | |
DisplayAnnotationType::Warning => '-', | |
DisplayAnnotationType::Info => '-', | |
DisplayAnnotationType::Note => '-', | |
DisplayAnnotationType::Help => '-', | |
DisplayAnnotationType::None => ' ', | |
}; |
Honestly kinda meh on this myself since it's somewhat non-obvious to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes more sense once you have e.g. (multiple) "Info" and "Error" annotations in a single diagnostic. Example from my own project (thinking about it, the function parameter annotation on top here should probably also be "info", but anyway):
I kind of like how the informational parts are just underlined with ---
and the actual error is underlined with ^^^
as if to say: look, this is what you need to change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah that's a good point to consider too. I'll leave it as-is for now. This was just meant to get some decent rendering in. We can absolutely iterate on the exact format and what not.
Oh, you're gonna need to update the output-validation in our benchmark somehow... |
Yeah working on that hah. Trying |
2fc4a27
to
5da4fb6
Compare
CodSpeed Performance ReportMerging #15856 will not alter performanceComparing Summary
|
5da4fb6
to
1080155
Compare
// We disable colors here so that our diagnostics don't include | ||
// ANSI escape codes. | ||
// | ||
// NOTE: I kinda feel like this is a sub-optimal way of going about | ||
// this. It just feels bad to be reaching in and doing global | ||
// state. I feel like it would be better to have a more explicit | ||
// configuration step before emitting diagnostics, but I think that | ||
// requires more design. | ||
colored::control::set_override(false); | ||
|
||
assert_eq!(&normalized, EXPECTED_DIAGNOSTICS); | ||
for (diagnostic, &expected) in diagnostics.into_iter().zip(EXPECTED_DIAGNOSTICS) { | ||
let got = diagnostic | ||
.display(db.upcast()) | ||
.to_string() | ||
.replace('\\', "/"); | ||
assert_eq!(got, expected); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that we have the diagnostic data structures here, why should we render them using display
at all, now that that representation is so inconvienent for comparison and update? (Not that it was ever convenient.) Couldn't we just compare against, like, a tuple of the key fields instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I'll look into this. I was mostly just looking to do the most minimal thing possible, but yeah, if you're cool with switching this out to just doing a comparison of key fields (range and diagnostic id I guess) then that seems fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that seems definitely better to me -- the full diagnostic strings look like they'd be an absolute nightmare to update without help from insta
.
// we just check that the number of diagnostics reported is what we | ||
// expect, to avoid doing most costly validation. | ||
let result: Vec<_> = case.db.check().unwrap(); | ||
assert_diagnostics(&case.db, result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second thought, I think the bogus codspeed numbers are telling us why we can't make this change. It turns the "cold" benchmark into a "no-op incremental" benchmark, which, not surprisingly, is very fast indeed. That's probably why the full diagnostics assertion was included in the benchmark. I think we either need to put it back the way it was, or do it as a totally separate prior step, not part of the per-iteration setup_case
at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aye. I'll switch it back. I guess it should be fine now since I'm not using insta
any more. Although I guess the diagnostic strings are now longer, so comparisons will be longer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we go with key fields instead of the full string, it should be pretty similar to before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though doing it as a separate step (not in setup_case
) also seems pretty easy and fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah sorry I made that comment before the other one.
1080155
to
88a0707
Compare
/// the actual diagnostic output, but using `insta` inside benchmarks is | ||
/// problematic, and updating the strings otherwise when diagnostic rendering | ||
/// changes is a PITA. | ||
type KeyDiagnosticFields = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we may want .message()
string in here too, but I'm not sure how much difference it will make going forward, and we can always add it back in as a separate change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
Can you say more about why .message()
is desirable here? Do you mean it's desirable to test and validate, or do you mean it's desirable from a measurement perspective?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's desirable from a usability perspective of validating in PR review that the diagnostics removed from (or added to) this assertion list are sensibly related to the red-knot changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I think I understand now. Thank you for explaining!
This change does a simple swap of the existing renderer for one that uses our vendored copy of `annotate-snippets`. We don't change anything about the diagnostic data model, but this alone already makes diagnostics look a lot nicer!
88a0707
to
07c80e8
Compare
--> <temp_dir>/child/test.py:2:1 | ||
| | ||
2 | from utils import add | ||
| ^^^^^^^^^^^^^^^^^^^^^ Cannot resolve import `utils` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This demonstrates that we should start capturing at least some full diagnostics in mdtests because highlighting the entire range here seems incorrect (compared to the import utils
case below where it only highlights the name. I'll create an issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I agree. We need a fair bit of test coverage beyond what we have. I think a nice short-term target would be at least one test for every type of diagnostic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that we need a lot more tests that capture the full diagnostic output, but I'm not convinced this should necessarily be an mdtest feature (#15867 (comment))
This change does a simple swap of the existing renderer for one that
uses our vendored copy of
annotate-snippets
. We don't change anythingabout the diagnostic data model, but this alone already makes
diagnostics look a lot nicer!
Here's an example of what it looks like now: