Account for selector specificity when merging extend_unsafe_fixes and override extend_safe_fixes (#8444)

## Summary

Prior to this change `extend_unsafe_fixes` took precedence over
`extend_safe_fixes` selectors, so any conflicts were resolved in favour
of `extend_unsafe_fixes`. Thanks to that ruff were conservatively
assuming that if configs conlict the fix corresponding to selected rule
will be treated as unsafe.

After this change we take into account Specificity of the selectors. For
conflicts between selectors of the same Specificity we will treat the
corresponding fixes as unsafe. But if the conflicting selectors are of
different specificity the more specific one will win.

## Test Plan

Tests were added for the `FixSafetyTable` struct. The
`check_extend_unsafe_fixes_conflict_with_extend_safe_fixes_by_specificity`
integration test was added to test conflicting rules of different
specificity.

Fixes #8404

---------

Co-authored-by: Zanie <contact@zanie.dev>
This commit is contained in:
Lukasz Piatkowski 2023-11-07 17:33:40 +01:00 committed by GitHub
parent 7873ca38e5
commit 03303a9edd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 251 additions and 41 deletions

View file

@ -10,6 +10,7 @@ use std::path::{Path, PathBuf};
use anyhow::{anyhow, Result};
use glob::{glob, GlobError, Paths, PatternError};
use regex::Regex;
use ruff_linter::settings::fix_safety_table::FixSafetyTable;
use rustc_hash::{FxHashMap, FxHashSet};
use shellexpand;
use shellexpand::LookupError;
@ -239,26 +240,14 @@ impl Configuration {
.collect(),
)?,
extend_safe_fixes: lint
.extend_safe_fixes
.iter()
.flat_map(|selector| {
selector.rules(&PreviewOptions {
mode: lint_preview,
require_explicit: false,
})
})
.collect(),
extend_unsafe_fixes: lint
.extend_unsafe_fixes
.iter()
.flat_map(|selector| {
selector.rules(&PreviewOptions {
mode: lint_preview,
require_explicit: false,
})
})
.collect(),
fix_safety: FixSafetyTable::from_rule_selectors(
&lint.extend_safe_fixes,
&lint.extend_unsafe_fixes,
&PreviewOptions {
mode: lint_preview,
require_explicit: false,
},
),
src: self.src.unwrap_or_else(|| vec![project_root.to_path_buf()]),
explicit_preview_rules: lint.explicit_preview_rules.unwrap_or_default(),