Skip to content

Convert Message::SyntaxError to use Diagnostic internally#17784

Merged
ntBre merged 34 commits intomainfrom
brent/diagnostics-file-enum
May 8, 2025
Merged

Convert Message::SyntaxError to use Diagnostic internally#17784
ntBre merged 34 commits intomainfrom
brent/diagnostics-file-enum

Conversation

@ntBre
Copy link
Contributor

@ntBre ntBre commented May 1, 2025

Summary

This PR is a first step toward integration of the new Diagnostic type into ruff. There are two main changes:

  • A new UnifiedFile enum wrapping File for red-knot and a SourceFile for ruff
  • ruff's Message::SyntaxError variant is now a Diagnostic instead of a SyntaxErrorMessage

The second of these changes was mostly just a proof of concept for the first, and it went pretty smoothly. Converting DiagnosticMessages will be most of the work in replacing Message entirely.

Test Plan

Existing tests, which show no changes.

@github-actions
Copy link
Contributor

github-actions bot commented May 1, 2025

mypy_primer results

No ecosystem changes detected ✅

@ntBre ntBre force-pushed the brent/diagnostics-file-enum branch from 4c2da7c to 4fce96a Compare May 1, 2025 23:56
@github-actions
Copy link
Contributor

github-actions bot commented May 2, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

Formatter (stable)

ℹ️ ecosystem check encountered format errors. (no format changes; 1 project error)

mesonbuild/meson-python (error)

warning: Detected debug build without --no-cache.
error: Failed to read tests/packages/symlinks/baz.py: No such file or directory (os error 2)
error: Failed to read tests/packages/symlinks/qux.py: No such file or directory (os error 2)

Formatter (preview)

ℹ️ ecosystem check encountered format errors. (no format changes; 1 project error)

mesonbuild/meson-python (error)

ruff format --preview

warning: Detected debug build without --no-cache.
error: Failed to read tests/packages/symlinks/baz.py: No such file or directory (os error 2)
error: Failed to read tests/packages/symlinks/qux.py: No such file or directory (os error 2)

UnifiedFile::RedKnot(file) => resolver.path(*file),
UnifiedFile::Ruff(file) => file.name(),
}
}
Copy link
Member

Choose a reason for hiding this comment

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

FWIW, I think the main intent with FileResolver is that it would become a trait, and this would be how we'd handle Ruff using its own interning for files. i.e., Red Knot would pass its own Salsa-backed resolver, but Ruff would use its non-Salsa thing.

I think if you aren't doing the interning, then yeah, this API is probably fine and perhaps even slightly over-engineered. If you do still plan to do interning---and I think that will let you definitively make UnifiedFile and therefore Span implement Copy---then I think this is a good stepping stone toward that.

Copy link
Member

Choose a reason for hiding this comment

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

I find having both path and file_path on UnifiedFile confusing because the only difference is whether they take a db or FileResolver, but that's not apparent from the method signature.

One option is to make FileResolver a trait as @BurntSushi suggests and implement it for Db (and Upcast). We would then need to implement a similar struct for Ruff.

Or we remove file_path (and all other methods that take db) and callers should instead call expect_ty and the path on the returned file. The renderer could call resolver.path(file) directly and we move this match into resolver.path. I think I sort of prefer this.

Copy link
Member

Choose a reason for hiding this comment

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

I don't feel too strongly here primarily because path() is an internal API, so I think the blast radius of confusion is somewhat more limited.

I'm happy with either of @MichaReiser's suggestions though. I kinda like just jumping straight to the trait approach, which was the original intent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this close to what y'all had in mind? 1faa5e0

I feel like I may have gone astray here in the details, but FileResolver is now a trait, the code compiles, and I got rid of file_path, at least.

The docs on FileResolver are probably slightly outdated now, but they still sounded relevant enough to me to leave alone since we're not really fully decoupled from salsa yet.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah that looks about right to me! Thanks!

I'd probably go for dyn Trait here though since I don't think the virtual call is going to matter perf-wise, and it will hopefully save from duplicating the rendering code.

@AlexWaygood AlexWaygood added the diagnostics Related to reporting of diagnostics. label May 3, 2025
@ntBre ntBre force-pushed the brent/diagnostics-file-enum branch from 4fce96a to f11b040 Compare May 6, 2025 17:45
@ntBre
Copy link
Contributor Author

ntBre commented May 6, 2025

Okay, I think this is ready for review. The two main changes since y'all last had a look are:

  • Removing the macros
    I added a somewhat overly-specific Diagnostic::expect_ruff_span method that just unwraps a primary_span call but with a ruff-centric message. I think this should eventually go away when we actually use Diagnostic for output, so hopefully it's okay temporarily. Otherwise I just inlined the macros. They were mostly just to consolidate expect messages.
  • Removing the repeated Notebook parsing
    I added a comment to this effect in the code, but Ruff already does its own notebook handling when Messages are emitted. Thanks @MichaReiser for the suggestion there!

@ntBre ntBre marked this pull request as ready for review May 6, 2025 17:57
@ntBre
Copy link
Contributor Author

ntBre commented May 7, 2025

I went ahead and pushed the minor changes, working on the SourceText and FileResolver stuff next.

Copy link
Member

@BurntSushi BurntSushi left a comment

Choose a reason for hiding this comment

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

Looking pretty good!

}

impl Message {
pub fn syntax_error(message: String, range: TextRange, file: SourceFile) -> Message {
Copy link
Member

Choose a reason for hiding this comment

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

It might be a little nicer to accept a impl std::fmt::Display here (which is what most of the Diagnostic APIs accept, at least, in spirit). Then you could avoid the various err.to_string() calls and just pass err.

self.primary_annotation().map(|ann| ann.tags.as_slice())
}

pub fn expect_span(&self) -> Span {
Copy link
Member

Choose a reason for hiding this comment

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

Can you add a comment documenting this? In particular, it would be good to state the panic conditions and the higher level motivation for why you might want to use this. (e.g., "When working with diagnostics in Ruff, the context requires that every diagnostic have a primary span.")

I think I'd also rename this to expect_primary_span() so that it's consistent with primary_span().

pub enum UnifiedFile {
Ty(File),
Ruff(SourceFile),
}
Copy link
Member

Choose a reason for hiding this comment

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

Can you add docs to this type? This might be a nice place to put some prose about why this exists and the high level strategy for how we're trying to share the diagnostics system between both ty and ruff.

Copy link
Member

Choose a reason for hiding this comment

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

Nice, much better!

UnifiedFile::RedKnot(file) => resolver.path(*file),
UnifiedFile::Ruff(file) => file.name(),
}
}
Copy link
Member

Choose a reason for hiding this comment

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

I don't feel too strongly here primarily because path() is an internal API, so I think the blast radius of confusion is somewhat more limited.

I'm happy with either of @MichaReiser's suggestions though. I kinda like just jumping straight to the trait approach, which was the original intent.

#[derive(Clone, Eq, PartialEq)]
pub struct SourceText {
inner: Arc<SourceTextInner>,
pub(crate) inner: Arc<SourceTextInner>,
Copy link
Member

Choose a reason for hiding this comment

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

I think using just SourceCode might be a little tricky here, because you need somewhere to anchor its lifetimes to.

I do agree that reusing SourceText here is a little wonky.

Maybe the way to go here is another enum:

enum DiagnosticSource {
    Ty(SourceText),
    Ruff(SourceFile),
)

And then you should be able to define a method to return a SourceCode I think.

@MichaReiser
Copy link
Member

MichaReiser commented May 8, 2025

@ntBre can you ping me when this is ready for re-review (it is already)?

@ntBre
Copy link
Contributor Author

ntBre commented May 8, 2025

It should be ready for another review!

@ntBre ntBre requested review from BurntSushi and MichaReiser May 8, 2025 12:43
Copy link
Member

@BurntSushi BurntSushi left a comment

Choose a reason for hiding this comment

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

LGTM!

pub enum UnifiedFile {
Ty(File),
Ruff(SourceFile),
}
Copy link
Member

Choose a reason for hiding this comment

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

Nice, much better!

enum DiagnosticSource {
Ty(Input),
Ruff(SourceFile),
}
Copy link
Member

Choose a reason for hiding this comment

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

Nice docs. And I like this type. Much clearer.

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.

This is excellent. Thank you

@ntBre
Copy link
Contributor Author

ntBre commented May 8, 2025

Thank you both!

@ntBre ntBre merged commit 981bd70 into main May 8, 2025
35 checks passed
@ntBre ntBre deleted the brent/diagnostics-file-enum branch May 8, 2025 16:45
dcreager added a commit that referenced this pull request May 8, 2025
* main:
  [ty] Respect the gradual guarantee when reporting errors in resolving MROs (#17962)
  Convert `Message::SyntaxError` to use `Diagnostic` internally (#17784)
  [ty] Support extending `__all__` with a literal tuple or set as well as a literal list (#17948)
  [ty] Make `unused-ignore-comment` disabled by default for now (#17955)
  [ty] Change default severity for `unbound-reference` to `error` (#17936)
  [ty] Ignore `possibly-unresolved-reference` by default (#17934)
  [ty] Default to latest supported python version (#17938)
  [ty] Generate and add rules table (#17953)
  Update the schemastore script to match changes in ty (#17952)
  [ty] Implement `global` handling and `load-before-global-declaration` syntax error (#17637)
dcreager added a commit that referenced this pull request May 8, 2025
* dcreager/default-typevars:
  clean up the diff
  remove trait
  track in type again
  clippy
  Better expansion of default typevars
  specialize_partial
  enum for TypeMapping
  [ty] Respect the gradual guarantee when reporting errors in resolving MROs (#17962)
  Specialize trait
  Convert `Message::SyntaxError` to use `Diagnostic` internally (#17784)
  [ty] Support extending `__all__` with a literal tuple or set as well as a literal list (#17948)
  [ty] Make `unused-ignore-comment` disabled by default for now (#17955)
  [ty] Change default severity for `unbound-reference` to `error` (#17936)
  [ty] Ignore `possibly-unresolved-reference` by default (#17934)
  [ty] Default to latest supported python version (#17938)
  [ty] Generate and add rules table (#17953)
  Update the schemastore script to match changes in ty (#17952)
  [ty] Implement `global` handling and `load-before-global-declaration` syntax error (#17637)
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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants