Files
ruff/src/violation.rs
Martin Fischer 16e79c8db6 derive-msg-formats 4/5: Implement #[derive_message_formats]
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.)
2023-01-19 11:03:32 -05:00

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)*
};
}