mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 21:40:51 -05:00
The idea is nice and simple we replace:
fn placeholder() -> Self;
with
fn message_formats() -> &'static [&'static str];
So e.g. if a Violation implementation defines:
fn message(&self) -> String {
format!("Local variable `{name}` is assigned to but never used")
}
it would also have to define:
fn message_formats() -> &'static [&'static str] {
&["Local variable `{name}` is assigned to but never used"]
}
Since we however obviously do not want to duplicate all of our format
strings we simply introduce a new procedural macro attribute
#[derive_message_formats] that can be added to the message method
declaration in order to automatically derive the message_formats
implementation.
This commit implements the macro. The following and final commit
updates violations.rs to use the macro. (The changes have been separated
because the next commit is autogenerated via a Python script.)
83 lines
2.5 KiB
Rust
83 lines
2.5 KiB
Rust
use std::fmt::Debug;
|
|
|
|
use serde::de::DeserializeOwned;
|
|
use serde::Serialize;
|
|
|
|
pub trait Violation: Debug + PartialEq + Eq + Serialize + DeserializeOwned {
|
|
/// `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;
|
|
|
|
/// 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];
|
|
}
|
|
|
|
pub struct AutofixKind {
|
|
pub available: Availability,
|
|
}
|
|
|
|
pub enum Availability {
|
|
Sometimes,
|
|
Always,
|
|
}
|
|
|
|
impl AutofixKind {
|
|
pub const fn new(available: Availability) -> Self {
|
|
Self { available }
|
|
}
|
|
}
|
|
|
|
/// 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 + Serialize + DeserializeOwned
|
|
{
|
|
/// The message used to describe the violation.
|
|
fn message(&self) -> String;
|
|
|
|
/// 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 autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
|
|
Some(Self::autofix_title)
|
|
}
|
|
|
|
fn message_formats() -> &'static [&'static str] {
|
|
<Self as AlwaysAutofixableViolation>::message_formats()
|
|
}
|
|
}
|
|
|
|
/// This macro just exists so that you don't have to add the `#[derive]`
|
|
/// attribute every time you define a new violation. And so that new traits can
|
|
/// be easily derived everywhere by just changing a single line.
|
|
#[macro_export]
|
|
macro_rules! define_violation {
|
|
($($struct:tt)*) => {
|
|
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
$($struct)*
|
|
};
|
|
}
|