## Summary
This PR is a collaboration with @AlexWaygood from our pairing session
last Friday.
The main goal here is removing `ruff_linter::message::OldDiagnostic` in
favor of
using `ruff_db::diagnostic::Diagnostic` directly. This involved a few
major steps:
- Transferring the fields
- Transferring the methods and trait implementations, where possible
- Converting some constructor methods to free functions
- Moving the `SecondaryCode` struct
- Updating the method names
I'm hoping that some of the methods, especially those in the
`expect_ruff_*`
family, won't be necessary long-term, but I avoided trying to replace
them
entirely for now to keep the already-large diff a bit smaller.
### Related refactors
Alex and I noticed a few refactoring opportunities while looking at the
code,
specifically the very similar implementations for
`create_parse_diagnostic`,
`create_unsupported_syntax_diagnostic`, and
`create_semantic_syntax_diagnostic`.
We combined these into a single generic function, which I then copied
into
`ruff_linter::message` with some small changes and a TODO to combine
them in the
future.
I also deleted the `DisplayParseErrorType` and `TruncateAtNewline` types
for
reporting parse errors. These were added in #4124, I believe to work
around the
error messages from LALRPOP. Removing these didn't affect any tests, so
I think
they were unnecessary now that we fully control the error messages from
the
parser.
On a more minor note, I factored out some calls to the
`OldDiagnostic::filename`
(now `Diagnostic::expect_ruff_filename`) function to avoid repeatedly
allocating
`String`s in some places.
### Snapshot changes
The `show_statistics_syntax_errors` integration test changed because the
`OldDiagnostic::name` method used `syntax-error` instead of
`invalid-syntax`
like in ty. I think this (`--statistics`) is one of the only places we
actually
use this name for syntax errors, so I hope this is okay. An alternative
is to
use `syntax-error` in ty too.
The other snapshot changes are from removing this code, as discussed on
[Discord](https://discord.com/channels/1039017663004942429/1228460843033821285/1388252408848847069):
34052a1185/crates/ruff_linter/src/message/mod.rs (L128-L135)
I think both of these are technically breaking changes, but they only
affect
syntax errors and are very narrow in scope, while also pretty
substantially
simplifying the refactor, so I hope they're okay to include in a patch
release.
## Test plan
Existing tests, with the adjustments mentioned above
---------
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
144 lines
4.0 KiB
Rust
144 lines
4.0 KiB
Rust
use std::cmp::Ordering;
|
|
|
|
#[cfg(feature = "serde")]
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
|
|
|
/// A text edit to be applied to a source file. Inserts, deletes, or replaces
|
|
/// content at a given location.
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, get_size2::GetSize)]
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
pub struct Edit {
|
|
/// The start location of the edit.
|
|
range: TextRange,
|
|
/// The replacement content to insert between the start and end locations.
|
|
content: Option<Box<str>>,
|
|
}
|
|
|
|
impl Edit {
|
|
/// Creates an edit that deletes the content in the `start` to `end` range.
|
|
#[inline]
|
|
pub const fn deletion(start: TextSize, end: TextSize) -> Self {
|
|
Self::range_deletion(TextRange::new(start, end))
|
|
}
|
|
|
|
/// Creates an edit that deletes the content in `range`.
|
|
pub const fn range_deletion(range: TextRange) -> Self {
|
|
Self {
|
|
content: None,
|
|
range,
|
|
}
|
|
}
|
|
|
|
/// Creates an edit that replaces the content in the `start` to `end` range with `content`.
|
|
#[inline]
|
|
pub fn replacement(content: String, start: TextSize, end: TextSize) -> Self {
|
|
Self::range_replacement(content, TextRange::new(start, end))
|
|
}
|
|
|
|
/// Creates an edit that replaces the content in `range` with `content`.
|
|
pub fn range_replacement(content: String, range: TextRange) -> Self {
|
|
debug_assert!(!content.is_empty(), "Prefer `Fix::deletion`");
|
|
|
|
Self {
|
|
content: Some(Box::from(content)),
|
|
range,
|
|
}
|
|
}
|
|
|
|
/// Creates an edit that inserts `content` at the [`TextSize`] `at`.
|
|
pub fn insertion(content: String, at: TextSize) -> Self {
|
|
debug_assert!(!content.is_empty(), "Insert content is empty");
|
|
|
|
Self {
|
|
content: Some(Box::from(content)),
|
|
range: TextRange::new(at, at),
|
|
}
|
|
}
|
|
|
|
/// Returns the new content for an insertion or deletion.
|
|
pub fn content(&self) -> Option<&str> {
|
|
self.content.as_deref()
|
|
}
|
|
|
|
pub fn into_content(self) -> Option<Box<str>> {
|
|
self.content
|
|
}
|
|
|
|
fn kind(&self) -> EditOperationKind {
|
|
if self.content.is_none() {
|
|
EditOperationKind::Deletion
|
|
} else if self.range.is_empty() {
|
|
EditOperationKind::Insertion
|
|
} else {
|
|
EditOperationKind::Replacement
|
|
}
|
|
}
|
|
|
|
/// Returns `true` if this edit deletes content from the source document.
|
|
#[inline]
|
|
pub fn is_deletion(&self) -> bool {
|
|
self.kind().is_deletion()
|
|
}
|
|
|
|
/// Returns `true` if this edit inserts new content into the source document.
|
|
#[inline]
|
|
pub fn is_insertion(&self) -> bool {
|
|
self.kind().is_insertion()
|
|
}
|
|
|
|
/// Returns `true` if this edit replaces some existing content with new content.
|
|
#[inline]
|
|
pub fn is_replacement(&self) -> bool {
|
|
self.kind().is_replacement()
|
|
}
|
|
}
|
|
|
|
impl Ord for Edit {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
self.start()
|
|
.cmp(&other.start())
|
|
.then_with(|| self.end().cmp(&other.end()))
|
|
.then_with(|| self.content.cmp(&other.content))
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for Edit {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Ranged for Edit {
|
|
fn range(&self) -> TextRange {
|
|
self.range
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
|
enum EditOperationKind {
|
|
/// Edit that inserts new content into the source document.
|
|
Insertion,
|
|
|
|
/// Edit that deletes content from the source document.
|
|
Deletion,
|
|
|
|
/// Edit that replaces content from the source document.
|
|
Replacement,
|
|
}
|
|
|
|
impl EditOperationKind {
|
|
pub(crate) const fn is_insertion(self) -> bool {
|
|
matches!(self, EditOperationKind::Insertion)
|
|
}
|
|
|
|
pub(crate) const fn is_deletion(self) -> bool {
|
|
matches!(self, EditOperationKind::Deletion)
|
|
}
|
|
|
|
pub(crate) const fn is_replacement(self) -> bool {
|
|
matches!(self, EditOperationKind::Replacement)
|
|
}
|
|
}
|