Skip to main content

ruff_diagnostics/
fix.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4use ruff_text_size::{Ranged, TextSize};
5
6use crate::edit::Edit;
7
8/// Indicates if a fix can be applied.
9#[derive(
10    Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, is_macro::Is, get_size2::GetSize,
11)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
14pub enum Applicability {
15    /// The fix is unsafe and should only be displayed for manual application by the user.
16    ///
17    /// The fix is likely to be incorrect or the resulting code may have invalid syntax.
18    DisplayOnly,
19
20    /// The fix is unsafe and should only be applied with user opt-in.
21    ///
22    /// The fix may be what the user intended, but it is uncertain. The resulting code will have
23    /// valid syntax, but may lead to a change in runtime behavior, the removal of user comments,
24    /// or both.
25    Unsafe,
26
27    /// The fix is safe and can always be applied.
28    ///
29    /// The fix is definitely what the user intended, or maintains the exact meaning of the code.
30    /// User comments are preserved, unless the fix removes an entire statement or expression.
31    Safe,
32}
33
34/// Indicates the level of isolation required to apply a fix.
35#[derive(Default, Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, get_size2::GetSize)]
36#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
37pub enum IsolationLevel {
38    /// The fix should be applied as long as no other fixes in the same group have been applied.
39    Group(u32),
40    /// The fix should be applied as long as it does not overlap with any other fixes.
41    #[default]
42    NonOverlapping,
43}
44
45/// A collection of [`Edit`] elements to be applied to a source file.
46#[derive(Debug, PartialEq, Eq, Clone, Hash, get_size2::GetSize)]
47#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48pub struct Fix {
49    /// The [`Edit`] elements to be applied, sorted by [`Edit::start`] in ascending order.
50    edits: Vec<Edit>,
51    /// The [`Applicability`] of the fix.
52    applicability: Applicability,
53    /// The [`IsolationLevel`] of the fix.
54    isolation_level: IsolationLevel,
55}
56
57impl Fix {
58    /// Create a new [`Fix`] that is [safe](Applicability::Safe) to apply from an [`Edit`] element.
59    pub fn safe_edit(edit: Edit) -> Self {
60        Self {
61            edits: vec![edit],
62            applicability: Applicability::Safe,
63            isolation_level: IsolationLevel::default(),
64        }
65    }
66
67    /// Create a new [`Fix`] that is [safe](Applicability::Safe) to apply from multiple [`Edit`] elements.
68    pub fn safe_edits(edit: Edit, rest: impl IntoIterator<Item = Edit>) -> Self {
69        let mut edits: Vec<Edit> = std::iter::once(edit).chain(rest).collect();
70        edits.sort_by_key(|edit| (edit.start(), edit.end()));
71        Self {
72            edits,
73            applicability: Applicability::Safe,
74            isolation_level: IsolationLevel::default(),
75        }
76    }
77
78    /// Create a new [`Fix`] that is [unsafe](Applicability::Unsafe) to apply from an [`Edit`] element.
79    pub fn unsafe_edit(edit: Edit) -> Self {
80        Self {
81            edits: vec![edit],
82            applicability: Applicability::Unsafe,
83            isolation_level: IsolationLevel::default(),
84        }
85    }
86
87    /// Create a new [`Fix`] that is [unsafe](Applicability::Unsafe) to apply from multiple [`Edit`] elements.
88    pub fn unsafe_edits(edit: Edit, rest: impl IntoIterator<Item = Edit>) -> Self {
89        let mut edits: Vec<Edit> = std::iter::once(edit).chain(rest).collect();
90        edits.sort_by_key(|edit| (edit.start(), edit.end()));
91        Self {
92            edits,
93            applicability: Applicability::Unsafe,
94            isolation_level: IsolationLevel::default(),
95        }
96    }
97
98    /// Create a new [`Fix`] that should only [display](Applicability::DisplayOnly) and not apply from an [`Edit`] element .
99    pub fn display_only_edit(edit: Edit) -> Self {
100        Self {
101            edits: vec![edit],
102            applicability: Applicability::DisplayOnly,
103            isolation_level: IsolationLevel::default(),
104        }
105    }
106
107    /// Create a new [`Fix`] with the specified [`Applicability`] to apply an [`Edit`] element.
108    pub fn applicable_edit(edit: Edit, applicability: Applicability) -> Self {
109        Self {
110            edits: vec![edit],
111            applicability,
112            isolation_level: IsolationLevel::default(),
113        }
114    }
115
116    /// Create a new [`Fix`] with the specified [`Applicability`] to apply multiple [`Edit`] elements.
117    pub fn applicable_edits(
118        edit: Edit,
119        rest: impl IntoIterator<Item = Edit>,
120        applicability: Applicability,
121    ) -> Self {
122        let mut edits: Vec<Edit> = std::iter::once(edit).chain(rest).collect();
123        edits.sort_by_key(|edit| (edit.start(), edit.end()));
124        Self {
125            edits,
126            applicability,
127            isolation_level: IsolationLevel::default(),
128        }
129    }
130
131    /// Return the [`TextSize`] of the first [`Edit`] in the [`Fix`].
132    pub fn min_start(&self) -> Option<TextSize> {
133        self.edits.first().map(Edit::start)
134    }
135
136    /// Return a slice of the [`Edit`] elements in the [`Fix`], sorted by [`Edit::start`] in ascending order.
137    pub fn edits(&self) -> &[Edit] {
138        &self.edits
139    }
140
141    pub fn into_edits(self) -> Vec<Edit> {
142        self.edits
143    }
144
145    /// Return the [`Applicability`] of the [`Fix`].
146    pub fn applicability(&self) -> Applicability {
147        self.applicability
148    }
149
150    /// Return the [`IsolationLevel`] of the [`Fix`].
151    pub fn isolation(&self) -> IsolationLevel {
152        self.isolation_level
153    }
154
155    /// Create a new [`Fix`] with the given [`IsolationLevel`].
156    #[must_use]
157    pub fn isolate(mut self, isolation: IsolationLevel) -> Self {
158        self.isolation_level = isolation;
159        self
160    }
161
162    /// Return [`true`] if this [`Fix`] should be applied with at a given [`Applicability`].
163    pub fn applies(&self, applicability: Applicability) -> bool {
164        self.applicability >= applicability
165    }
166
167    /// Create a new [`Fix`] with the given [`Applicability`].
168    #[must_use]
169    pub fn with_applicability(mut self, applicability: Applicability) -> Self {
170        self.applicability = applicability;
171        self
172    }
173}