Skip to content
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

Merged
merged 1 commit into from
Jan 31, 2025

Conversation

BurntSushi
Copy link
Member

@BurntSushi BurntSushi commented Jan 31, 2025

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!

Here's an example of what it looks like now:

diag-example

@BurntSushi BurntSushi changed the title ruff_db: make diagnostic rendering prettier [red-knot] ruff_db: make diagnostic rendering prettier Jan 31, 2025
@AlexWaygood AlexWaygood added the red-knot Multi-file analysis & type inference label Jan 31, 2025
Copy link
Contributor

github-actions bot commented Jan 31, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

Formatter (stable)

✅ ecosystem check detected no format changes.

Formatter (preview)

✅ ecosystem check detected no format changes.

Copy link
Member

@AlexWaygood AlexWaygood left a 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!

@BurntSushi BurntSushi force-pushed the ag/red-knot-prototype-diagnostics branch from 27869d0 to f292f01 Compare January 31, 2025 18:04
Copy link
Contributor

@carljm carljm left a 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
Copy link
Contributor

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?

Copy link
Member Author

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:

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.

Copy link
Contributor

@sharkdp sharkdp Jan 31, 2025

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):

image

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.

Copy link
Member Author

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.

@carljm
Copy link
Contributor

carljm commented Jan 31, 2025

Oh, you're gonna need to update the output-validation in our benchmark somehow...

@BurntSushi
Copy link
Member Author

Yeah working on that hah. Trying insta.

@BurntSushi BurntSushi force-pushed the ag/red-knot-prototype-diagnostics branch 4 times, most recently from 2fc4a27 to 5da4fb6 Compare January 31, 2025 19:42
Copy link

codspeed-hq bot commented Jan 31, 2025

CodSpeed Performance Report

Merging #15856 will not alter performance

Comparing ag/red-knot-prototype-diagnostics (07c80e8) with main (fab86de)

Summary

✅ 32 untouched benchmarks

@BurntSushi BurntSushi force-pushed the ag/red-knot-prototype-diagnostics branch from 5da4fb6 to 1080155 Compare January 31, 2025 19:50
Comment on lines 239 to 254
// 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);
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

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);
Copy link
Contributor

@carljm carljm Jan 31, 2025

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.

Copy link
Member Author

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.

Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Member Author

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.

@BurntSushi BurntSushi force-pushed the ag/red-knot-prototype-diagnostics branch from 1080155 to 88a0707 Compare January 31, 2025 21:03
@BurntSushi BurntSushi requested a review from carljm January 31, 2025 21:10
/// 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 = (
Copy link
Contributor

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.

Copy link
Member Author

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?

Copy link
Contributor

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.

Copy link
Member Author

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!
@BurntSushi BurntSushi force-pushed the ag/red-knot-prototype-diagnostics branch from 88a0707 to 07c80e8 Compare January 31, 2025 21:21
@BurntSushi BurntSushi merged commit b58f2c3 into main Jan 31, 2025
21 checks passed
@BurntSushi BurntSushi deleted the ag/red-knot-prototype-diagnostics branch January 31, 2025 21:37
--> <temp_dir>/child/test.py:2:1
|
2 | from utils import add
| ^^^^^^^^^^^^^^^^^^^^^ Cannot resolve import `utils`
Copy link
Member

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

Copy link
Member Author

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.

Copy link
Member

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))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
red-knot Multi-file analysis & type inference
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants