Skip to content

Commit 02ae8e1

Browse files
authored
Apply fix availability and applicability when adding to DiagnosticGuard and remove NoqaCode::rule (#18834)
## Summary This PR removes the last two places we were using `NoqaCode::rule` in `linter.rs` (see #18391 (comment) and #18391 (comment)) by checking whether fixes are actually desired before adding them to a `DiagnosticGuard`. I implemented this by storing a `Violation`'s `Rule` on the `DiagnosticGuard` so that we could check if it was enabled in the embedded `LinterSettings` when trying to set a fix. All of the corresponding `set_fix` methods on `OldDiagnostic` were now unused (except in tests where I just set `.fix` directly), so I moved these to the guard instead of keeping both sets. The very last place where we were using `NoqaCode::rule` was in the cache. I just reverted this to parsing the `Rule` from the name. I had forgotten to update the comment there anyway. Hopefully this doesn't cause too much of a perf hit. In terms of binary size, we're back down almost to where `main` was two days ago (#18391 (comment)): ``` 41,559,344 bytes for main 2 days ago 41,669,840 bytes for #18391 41,653,760 bytes for main now (after #18391 merged) 41,602,224 bytes for this branch ``` Only 43 kb up, but that shouldn't all be me this time :) ## Test Plan Existing tests and benchmarks on this PR
1 parent 0edbd6c commit 02ae8e1

File tree

11 files changed

+73
-102
lines changed

11 files changed

+73
-102
lines changed

crates/ruff/src/cache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ impl LintCacheData {
442442
// Parse the kebab-case rule name into a `Rule`. This will fail for syntax errors, so
443443
// this also serves to filter them out, but we shouldn't be caching files with syntax
444444
// errors anyway.
445-
.filter_map(|msg| Some((msg.noqa_code().and_then(|code| code.rule())?, msg)))
445+
.filter_map(|msg| Some((msg.name().parse().ok()?, msg)))
446446
.map(|(rule, msg)| {
447447
// Make sure that all message use the same source file.
448448
assert_eq!(

crates/ruff_linter/src/checkers/ast/mod.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use itertools::Itertools;
2828
use log::debug;
2929
use rustc_hash::{FxHashMap, FxHashSet};
3030

31-
use ruff_diagnostics::IsolationLevel;
31+
use ruff_diagnostics::{Applicability, Fix, IsolationLevel};
3232
use ruff_notebook::{CellOffsets, NotebookIndex};
3333
use ruff_python_ast::helpers::{collect_import_from_member, is_docstring_stmt, to_module_path};
3434
use ruff_python_ast::identifier::Identifier;
@@ -3154,6 +3154,7 @@ impl<'a> LintContext<'a> {
31543154
DiagnosticGuard {
31553155
context: self,
31563156
diagnostic: Some(OldDiagnostic::new(kind, range, &self.source_file)),
3157+
rule: T::rule(),
31573158
}
31583159
}
31593160

@@ -3167,10 +3168,12 @@ impl<'a> LintContext<'a> {
31673168
kind: T,
31683169
range: TextRange,
31693170
) -> Option<DiagnosticGuard<'chk, 'a>> {
3170-
if self.is_rule_enabled(T::rule()) {
3171+
let rule = T::rule();
3172+
if self.is_rule_enabled(rule) {
31713173
Some(DiagnosticGuard {
31723174
context: self,
31733175
diagnostic: Some(OldDiagnostic::new(kind, range, &self.source_file)),
3176+
rule,
31743177
})
31753178
} else {
31763179
None
@@ -3222,6 +3225,7 @@ pub(crate) struct DiagnosticGuard<'a, 'b> {
32223225
///
32233226
/// This is always `Some` until the `Drop` (or `defuse`) call.
32243227
diagnostic: Option<OldDiagnostic>,
3228+
rule: Rule,
32253229
}
32263230

32273231
impl DiagnosticGuard<'_, '_> {
@@ -3234,6 +3238,50 @@ impl DiagnosticGuard<'_, '_> {
32343238
}
32353239
}
32363240

3241+
impl DiagnosticGuard<'_, '_> {
3242+
fn resolve_applicability(&self, fix: &Fix) -> Applicability {
3243+
self.context
3244+
.settings
3245+
.fix_safety
3246+
.resolve_applicability(self.rule, fix.applicability())
3247+
}
3248+
3249+
/// Set the [`Fix`] used to fix the diagnostic.
3250+
#[inline]
3251+
pub(crate) fn set_fix(&mut self, fix: Fix) {
3252+
if !self.context.rules.should_fix(self.rule) {
3253+
self.fix = None;
3254+
return;
3255+
}
3256+
let applicability = self.resolve_applicability(&fix);
3257+
self.fix = Some(fix.with_applicability(applicability));
3258+
}
3259+
3260+
/// Set the [`Fix`] used to fix the diagnostic, if the provided function returns `Ok`.
3261+
/// Otherwise, log the error.
3262+
#[inline]
3263+
pub(crate) fn try_set_fix(&mut self, func: impl FnOnce() -> anyhow::Result<Fix>) {
3264+
match func() {
3265+
Ok(fix) => self.set_fix(fix),
3266+
Err(err) => log::debug!("Failed to create fix for {}: {}", self.name(), err),
3267+
}
3268+
}
3269+
3270+
/// Set the [`Fix`] used to fix the diagnostic, if the provided function returns `Ok`.
3271+
/// Otherwise, log the error.
3272+
#[inline]
3273+
pub(crate) fn try_set_optional_fix(
3274+
&mut self,
3275+
func: impl FnOnce() -> anyhow::Result<Option<Fix>>,
3276+
) {
3277+
match func() {
3278+
Ok(None) => {}
3279+
Ok(Some(fix)) => self.set_fix(fix),
3280+
Err(err) => log::debug!("Failed to create fix for {}: {}", self.name(), err),
3281+
}
3282+
}
3283+
}
3284+
32373285
impl std::ops::Deref for DiagnosticGuard<'_, '_> {
32383286
type Target = OldDiagnostic;
32393287

crates/ruff_linter/src/fix/edits.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -749,15 +749,16 @@ x = 1 \
749749
let diag = {
750750
use crate::rules::pycodestyle::rules::MissingNewlineAtEndOfFile;
751751
let mut iter = edits.into_iter();
752-
OldDiagnostic::new(
752+
let mut diagnostic = OldDiagnostic::new(
753753
MissingNewlineAtEndOfFile, // The choice of rule here is arbitrary.
754754
TextRange::default(),
755755
&SourceFileBuilder::new("<filename>", "<code>").finish(),
756-
)
757-
.with_fix(Fix::safe_edits(
756+
);
757+
diagnostic.fix = Some(Fix::safe_edits(
758758
iter.next().ok_or(anyhow!("expected edits nonempty"))?,
759759
iter,
760-
))
760+
));
761+
diagnostic
761762
};
762763
assert_eq!(apply_fixes([diag].iter(), &locator).code, expect);
763764
Ok(())

crates/ruff_linter/src/fix/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,13 @@ mod tests {
186186
edit.into_iter()
187187
.map(|edit| {
188188
// The choice of rule here is arbitrary.
189-
let diagnostic = OldDiagnostic::new(
189+
let mut diagnostic = OldDiagnostic::new(
190190
MissingNewlineAtEndOfFile,
191191
edit.range(),
192192
&SourceFileBuilder::new(filename, source).finish(),
193193
);
194-
diagnostic.with_fix(Fix::safe_edit(edit))
194+
diagnostic.fix = Some(Fix::safe_edit(edit));
195+
diagnostic
195196
})
196197
.collect()
197198
}

crates/ruff_linter/src/linter.rs

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -378,32 +378,7 @@ pub fn check_path(
378378

379379
let (mut diagnostics, source_file) = context.into_parts();
380380

381-
if parsed.has_valid_syntax() {
382-
// Remove fixes for any rules marked as unfixable.
383-
for diagnostic in &mut diagnostics {
384-
if diagnostic
385-
.noqa_code()
386-
.and_then(|code| code.rule())
387-
.is_none_or(|rule| !settings.rules.should_fix(rule))
388-
{
389-
diagnostic.fix = None;
390-
}
391-
}
392-
393-
// Update fix applicability to account for overrides
394-
if !settings.fix_safety.is_empty() {
395-
for diagnostic in &mut diagnostics {
396-
if let Some(fix) = diagnostic.fix.take() {
397-
if let Some(rule) = diagnostic.noqa_code().and_then(|code| code.rule()) {
398-
let fixed_applicability = settings
399-
.fix_safety
400-
.resolve_applicability(rule, fix.applicability());
401-
diagnostic.set_fix(fix.with_applicability(fixed_applicability));
402-
}
403-
}
404-
}
405-
}
406-
} else {
381+
if !parsed.has_valid_syntax() {
407382
// Avoid fixing in case the source code contains syntax errors.
408383
for diagnostic in &mut diagnostics {
409384
diagnostic.fix = None;

crates/ruff_linter/src/message/mod.rs

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -186,41 +186,6 @@ impl OldDiagnostic {
186186
)
187187
}
188188

189-
/// Consumes `self` and returns a new `Diagnostic` with the given `fix`.
190-
#[inline]
191-
#[must_use]
192-
pub fn with_fix(mut self, fix: Fix) -> Self {
193-
self.set_fix(fix);
194-
self
195-
}
196-
197-
/// Set the [`Fix`] used to fix the diagnostic.
198-
#[inline]
199-
pub fn set_fix(&mut self, fix: Fix) {
200-
self.fix = Some(fix);
201-
}
202-
203-
/// Set the [`Fix`] used to fix the diagnostic, if the provided function returns `Ok`.
204-
/// Otherwise, log the error.
205-
#[inline]
206-
pub fn try_set_fix(&mut self, func: impl FnOnce() -> anyhow::Result<Fix>) {
207-
match func() {
208-
Ok(fix) => self.fix = Some(fix),
209-
Err(err) => log::debug!("Failed to create fix for {}: {}", self.name(), err),
210-
}
211-
}
212-
213-
/// Set the [`Fix`] used to fix the diagnostic, if the provided function returns `Ok`.
214-
/// Otherwise, log the error.
215-
#[inline]
216-
pub fn try_set_optional_fix(&mut self, func: impl FnOnce() -> anyhow::Result<Option<Fix>>) {
217-
match func() {
218-
Ok(None) => {}
219-
Ok(Some(fix)) => self.fix = Some(fix),
220-
Err(err) => log::debug!("Failed to create fix for {}: {}", self.name(), err),
221-
}
222-
}
223-
224189
/// Consumes `self` and returns a new `Diagnostic` with the given parent node.
225190
#[inline]
226191
#[must_use]

crates/ruff_linter/src/registry.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,12 @@ pub enum Linter {
215215
}
216216

217217
pub trait RuleNamespace: Sized {
218+
/// Returns the prefix that every single code that ruff uses to identify
219+
/// rules from this linter starts with. In the case that multiple
220+
/// `#[prefix]`es are configured for the variant in the `Linter` enum
221+
/// definition this is the empty string.
222+
fn common_prefix(&self) -> &'static str;
223+
218224
/// Attempts to parse the given rule code. If the prefix is recognized
219225
/// returns the respective variant along with the code with the common
220226
/// prefix stripped.

crates/ruff_linter/src/rule_selector.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ mod schema {
265265
use strum::IntoEnumIterator;
266266

267267
use crate::RuleSelector;
268+
use crate::registry::RuleNamespace;
268269
use crate::rule_selector::{Linter, RuleCodePrefix};
269270

270271
impl JsonSchema for RuleSelector {

crates/ruff_macros/src/map_codes.rs

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,9 @@ fn generate_rule_to_code(linter_to_rules: &BTreeMap<Ident, BTreeMap<String, Rule
254254
}
255255

256256
let mut rule_noqa_code_match_arms = quote!();
257-
let mut noqa_code_rule_match_arms = quote!();
258257
let mut rule_group_match_arms = quote!();
259-
let mut noqa_code_consts = quote!();
260258

261-
for (i, (rule, codes)) in rule_to_codes.into_iter().enumerate() {
259+
for (rule, codes) in rule_to_codes {
262260
let rule_name = rule.segments.last().unwrap();
263261
assert_eq!(
264262
codes.len(),
@@ -294,14 +292,6 @@ See also https://github.com/astral-sh/ruff/issues/2186.
294292
#(#attrs)* Rule::#rule_name => NoqaCode(crate::registry::Linter::#linter.common_prefix(), #code),
295293
});
296294

297-
let const_ident = quote::format_ident!("NOQA_PREFIX_{}", i);
298-
noqa_code_consts.extend(quote! {
299-
const #const_ident: &str = crate::registry::Linter::#linter.common_prefix();
300-
});
301-
noqa_code_rule_match_arms.extend(quote! {
302-
#(#attrs)* NoqaCode(#const_ident, #code) => Some(Rule::#rule_name),
303-
});
304-
305295
rule_group_match_arms.extend(quote! {
306296
#(#attrs)* Rule::#rule_name => #group,
307297
});
@@ -350,16 +340,6 @@ See also https://github.com/astral-sh/ruff/issues/2186.
350340
}
351341
}
352342
}
353-
354-
impl NoqaCode {
355-
pub fn rule(&self) -> Option<Rule> {
356-
#noqa_code_consts
357-
match self {
358-
#noqa_code_rule_match_arms
359-
_ => None
360-
}
361-
}
362-
}
363343
};
364344
rule_to_code
365345
}

crates/ruff_macros/src/rule_namespace.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenS
118118
None
119119
}
120120

121+
fn common_prefix(&self) -> &'static str {
122+
match self { #common_prefix_match_arms }
123+
}
124+
121125
fn name(&self) -> &'static str {
122126
match self { #name_match_arms }
123127
}
@@ -126,16 +130,6 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenS
126130
match self { #url_match_arms }
127131
}
128132
}
129-
130-
impl #ident {
131-
/// Returns the prefix that every single code that ruff uses to identify
132-
/// rules from this linter starts with. In the case that multiple
133-
/// `#[prefix]`es are configured for the variant in the `Linter` enum
134-
/// definition this is the empty string.
135-
pub const fn common_prefix(&self) -> &'static str {
136-
match self { #common_prefix_match_arms }
137-
}
138-
}
139133
})
140134
}
141135

0 commit comments

Comments
 (0)