Skip to content

Conversation

@ntBre
Copy link
Contributor

@ntBre ntBre commented Jul 30, 2025

Summary

This is the other commit I wanted to spin off from #19415, currently stacked on #19644.

This PR suppresses blank snippets for empty ranges at the very beginning of a file, and for empty ranges in non-existent files. Ruff includes empty ranges for IO errors, for example.

// The `0..0` range is used to highlight file-level diagnostics.
if message.expect_range() != TextRange::default() {
writeln!(
writer,
"{}",
MessageCodeFrame {
message,
notebook_index
}
)?;
}

The diagnostics now look like this (new snapshot test):

error[test-diagnostic]: main diagnostic message
--> example.py:1:1                             

Instead of 1

error[test-diagnostic]: main diagnostic message
--> example.py:1:1
 |
 |

Test Plan

A new ruff_db test showing the expected output format

Footnotes

  1. This doesn't correspond precisely to the example in the PR because of some details of the diagnostic builder helper methods in ruff_db, but you can see another example in the current version of the summary in Move full diagnostic rendering to ruff_db #19415.

@ntBre ntBre added internal An internal refactor or improvement diagnostics Related to reporting of diagnostics. labels Jul 30, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented Jul 30, 2025

CodSpeed Instrumentation Performance Report

Merging #19653 will not alter performance

Comparing brent/full-file-diagnostics (2cfad84) with main (2db4e5d)

Summary

✅ 42 untouched benchmarks

@github-actions
Copy link
Contributor

github-actions bot commented Jul 30, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@ntBre ntBre force-pushed the brent/hide-severity branch from 1e1bd62 to 1cdb26c Compare July 31, 2025 16:23
@ntBre ntBre force-pushed the brent/full-file-diagnostics branch from 8a027bb to f4e93b6 Compare July 31, 2025 20:34
@ntBre ntBre changed the title [WIP] Hide empty snippets for full-file diagnostics Hide empty snippets for full-file diagnostics Jul 31, 2025
@ntBre ntBre marked this pull request as ready for review July 31, 2025 20:46
Copy link
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

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

Thank you. The approach looks good to me but I'm unsure about the ruff integration


let is_file_level = snippet.annotations.iter().any(|ann| ann.is_file_level);
if is_file_level {
let header = format_header(origin, main_range, &[], is_first);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Would it make sense to add an assertion that the snippet has no body (e.g no associated message?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, some kind of assertion makes sense to me. I think we can assert that the snippet source is empty, I'll just have to rework the test to avoid using the builder, which expects a non-empty snippet. It looks like Ruff's real diagnostics that will go through this path should already have empty snippets. We could also check that the annotation labels are empty (the text after ^^^), but I think those are always empty for us right now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think RenderableSnippet::new might try to expand the snippet to the usual number of context lines, unless we also check is_file_level there. We might have to remove or update the assertion when running this in #19415, but I'll add it for now. It'll be a good reminder to revisit it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, maybe that's in line with your suggestion about applying this per-rule. I guess we shouldn't render one of these except in cases where the snippet isn't available, like an io-error!

Comment on lines 717 to 718
/// When set, rendering will not include a file snippet but will still have the file's name and
/// (optional) range.
Copy link
Member

Choose a reason for hiding this comment

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

We should mention that this also omits the annotation's message. Or we could phrase it differently and say that everything other than the file name and range are omitted.

self.tags.push(tag);
}

pub fn set_file_level(&mut self, yes: bool) {
Copy link
Member

Choose a reason for hiding this comment

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

Can we document the meaning of a file level annotation here too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea, I added a note here about Span too, referring to this section of the Span docs:

/// It consists of a `File` and an optional range into that file. When the
/// range isn't present, it semantically implies that the diagnostic refers to
/// the entire file. For example, when the file should be executable but isn't.
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
pub struct Span {

Copy link
Member

Choose a reason for hiding this comment

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

Should this get a TODO that it ought to be removed in favor of Ruff diagnostics being updated to use a None span?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah that's a good point. I added an explicit TODO in the code here and also wrote some notes from looking into the options today: #19688 (comment). In short, I thought we would handle this with a None range on the span, but I think you're right that we should omit the span entirely instead.

It might be a bit tangential to the main issue in #19688, but at least it's recorded somewhere, and I can spin it off later if needed.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah this at least gives us a possible path toward eventually merging back with upstream.

Comment on lines 79 to 81
let mut annotation = Annotation::primary(span);
// The `0..0` range is used to highlight file-level diagnostics.
if range == TextRange::default() {
annotation.set_file_level(true);
}
Copy link
Member

Choose a reason for hiding this comment

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

Would it be much more work to instead change the rules that add file level diagnostics rather than doing this "global" override?

(the rule would have to mutate the Diagnostic after reporting it with report_diagnostic).

The reason I'd prefer this is that it makes it more apparent where we rely on this behavior and it doesn't prevent other rules to use an empty range (e.g. what if we had a rule that enforces module level docstrings. That rule would probably ask you to insert the docstring at 0:0.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah that's a great idea! We actually do have rules like you're describing. I think at least one import rule suggests adding an import at the top of the file (probably a __future__ import rule). It might be easier to handle that in #19415 as I'm going through the snapshots, but I can revert this for now.

You're right about the syntax errors too, I don't think we have any file-level ones. That was just for consistency with the current version.

On second thought, we might want to hold off on this until we can cache the Annotations. I don't think set_file_level will be cached currently.

Copy link
Member

Choose a reason for hiding this comment

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

On second thought, we might want to hold off on this until we can cache the Annotations. I don't think set_file_level will be cached currently.

I'm not sure I understand what you mean by caching.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(also discussed on Discord, just recording here for future reference)

For caching, I just mean in ruff's diagnostic cache. I think we would lose the is_file_level field on the annotation when converting to a CacheMessage

I'll drop the syntax error check because that shouldn't affect any real diagnostics and open an issue to follow up on this after a caching PR.

Copy link
Member

Choose a reason for hiding this comment

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

Let's maybe add a TODO here too

Comment on lines 79 to 81
let mut annotation = Annotation::primary(span);
// The `0..0` range is used to highlight file-level diagnostics.
if range == TextRange::default() {
annotation.set_file_level(true);
}
Copy link
Member

Choose a reason for hiding this comment

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

Let's maybe add a TODO here too

self.tags.push(tag);
}

pub fn set_file_level(&mut self, yes: bool) {
Copy link
Member

Choose a reason for hiding this comment

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

Should this get a TODO that it ought to be removed in favor of Ruff diagnostics being updated to use a None span?

Base automatically changed from brent/hide-severity to main August 5, 2025 13:56
@ntBre ntBre force-pushed the brent/full-file-diagnostics branch from 01c8ac8 to 4603fc8 Compare August 5, 2025 14:24
ntBre added a commit that referenced this pull request Aug 5, 2025
Summary
--

Fixes a snapshot test failure I saw in #19653 locally and in Windows CI by
padding the hex ID to 16 digits to match the regex in `filter_result_id`.

https://github.com/astral-sh/ruff/blob/78e5fe0a51fcbb1456d54c65b4632f60c268e389/crates/ty_server/tests/e2e/pull_diagnostics.rs#L380-L384

Test Plan
--

I applied this to the branch from #19653 locally and saw that the tests now
pass. I couldn't reproduce this failure directly on `main` or this branch,
though.
ntBre added a commit that referenced this pull request Aug 5, 2025
Summary
--

Fixes a snapshot test failure I saw in #19653 locally and in Windows CI
by
padding the hex ID to 16 digits to match the regex in
`filter_result_id`.


https://github.com/astral-sh/ruff/blob/78e5fe0a51fcbb1456d54c65b4632f60c268e389/crates/ty_server/tests/e2e/pull_diagnostics.rs#L380-L384

Test Plan
--

I applied this to the branch from #19653 locally and saw that the tests
now
pass. I couldn't reproduce this failure directly on `main` or this
branch,
though.
Summary
--

This isn't really polished for review yet, but this is the other commit I wanted
to spin off from #19415.

This suppresses empty snippets for empty ranges at the very beginning of a
file, and for empty ranges in non-existent files. Ruff includes empty ranges for
IO errors, for example.

Test Plan
--

TODO add a `ruff_db` test with an example
ntBre added 8 commits August 5, 2025 10:55
We do need a single character because the diagnostic builder does some
subtraction on the range, but conceptually this can be a completely empty or
non-existent file, as can be the case in Ruff.
also construct the test span manually to avoid some non-empty snippet
assumptions in `TestEnvironment::span`
we don't currently have any file-level syntax errors, so this wasn't having any
effect
@ntBre ntBre force-pushed the brent/full-file-diagnostics branch from 4603fc8 to 2cfad84 Compare August 5, 2025 14:56
@ntBre ntBre merged commit b324ae1 into main Aug 5, 2025
35 checks passed
@ntBre ntBre deleted the brent/full-file-diagnostics branch August 5, 2025 15:20
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. internal An internal refactor or improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants