Introduce a ruff_diagnostics crate (#3409)

## Summary

This PR moves `Diagnostic`, `DiagnosticKind`, and `Fix` into their own crate, which will enable us to further split up Ruff, since sub-linter crates (which need to implement functions that return `Diagnostic`) can now depend on `ruff_diagnostics` rather than Ruff.
This commit is contained in:
Charlie Marsh 2023-03-09 15:48:57 -05:00 committed by GitHub
parent 08ec11a31e
commit 024caca233
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
349 changed files with 758 additions and 1003 deletions

View file

@ -0,0 +1,52 @@
use rustpython_parser::ast::Location;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use ruff_python_ast::types::Range;
use crate::fix::Fix;
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DiagnosticKind {
/// The identifier of the diagnostic, used to align the diagnostic with a rule.
pub name: String,
/// The message body to display to the user, to explain the diagnostic.
pub body: String,
/// The message to display to the user, to explain the suggested fix.
pub suggestion: Option<String>,
/// Whether the diagnostic is automatically fixable.
pub fixable: bool,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Diagnostic {
pub kind: DiagnosticKind,
pub location: Location,
pub end_location: Location,
pub fix: Option<Fix>,
pub parent: Option<Location>,
}
impl Diagnostic {
pub fn new<K: Into<DiagnosticKind>>(kind: K, range: Range) -> Self {
Self {
kind: kind.into(),
location: range.location,
end_location: range.end_location,
fix: None,
parent: None,
}
}
pub fn amend(&mut self, fix: Fix) -> &mut Self {
self.fix = Some(fix);
self
}
pub fn parent(&mut self, parent: Location) -> &mut Self {
self.parent = Some(parent);
self
}
}

View file

@ -0,0 +1,41 @@
use rustpython_parser::ast::Location;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Fix {
pub content: String,
pub location: Location,
pub end_location: Location,
}
impl Fix {
pub const fn deletion(start: Location, end: Location) -> Self {
Self {
content: String::new(),
location: start,
end_location: end,
}
}
pub fn replacement(content: String, start: Location, end: Location) -> Self {
debug_assert!(!content.is_empty(), "Prefer `Fix::deletion`");
Self {
content,
location: start,
end_location: end,
}
}
pub fn insertion(content: String, at: Location) -> Self {
debug_assert!(!content.is_empty(), "Insert content is empty");
Self {
content,
location: at,
end_location: at,
}
}
}

View file

@ -0,0 +1,7 @@
pub use diagnostic::{Diagnostic, DiagnosticKind};
pub use fix::Fix;
pub use violation::{AlwaysAutofixableViolation, AutofixKind, Availability, Violation};
mod diagnostic;
mod fix;
mod violation;

View file

@ -0,0 +1,80 @@
use std::fmt::Debug;
pub enum Availability {
Sometimes,
Always,
}
pub struct AutofixKind {
pub available: Availability,
}
impl AutofixKind {
pub const fn new(available: Availability) -> Self {
Self { available }
}
}
pub trait Violation: Debug + PartialEq + Eq {
/// `None` in the case an autofix is never available or otherwise Some
/// [`AutofixKind`] describing the available autofix.
const AUTOFIX: Option<AutofixKind> = None;
/// The message used to describe the violation.
fn message(&self) -> String;
/// The explanation used in documentation and elsewhere.
fn explanation() -> Option<&'static str> {
None
}
/// If autofix is (potentially) available for this violation returns another
/// function that in turn can be used to obtain a string describing the
/// autofix.
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
None
}
/// Returns the format strings used by [`message`](Violation::message).
fn message_formats() -> &'static [&'static str];
}
/// This trait exists just to make implementing the [`Violation`] trait more
/// convenient for violations that can always be autofixed.
pub trait AlwaysAutofixableViolation: Debug + PartialEq + Eq {
/// The message used to describe the violation.
fn message(&self) -> String;
/// The explanation used in documentation and elsewhere.
fn explanation() -> Option<&'static str> {
None
}
/// The title displayed for the available autofix.
fn autofix_title(&self) -> String;
/// Returns the format strings used by
/// [`message`](AlwaysAutofixableViolation::message).
fn message_formats() -> &'static [&'static str];
}
/// A blanket implementation.
impl<VA: AlwaysAutofixableViolation> Violation for VA {
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Always));
fn message(&self) -> String {
<Self as AlwaysAutofixableViolation>::message(self)
}
fn explanation() -> Option<&'static str> {
<Self as AlwaysAutofixableViolation>::explanation()
}
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
Some(Self::autofix_title)
}
fn message_formats() -> &'static [&'static str] {
<Self as AlwaysAutofixableViolation>::message_formats()
}
}