Skip to content

Commit

Permalink
Add explicit-preview-rules to toggle explicit selection of preview …
Browse files Browse the repository at this point in the history
…rules
  • Loading branch information
zanieb committed Sep 14, 2023
1 parent b9bb6bf commit 91283e5
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 23 deletions.
4 changes: 2 additions & 2 deletions crates/flake8_to_ruff/src/plugin.rs
Expand Up @@ -4,7 +4,7 @@ use std::str::FromStr;

use anyhow::anyhow;
use ruff::registry::Linter;
use ruff::settings::types::PreviewMode;
use ruff::rule_selector::PreviewOptions;
use ruff::RuleSelector;

#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
Expand Down Expand Up @@ -332,7 +332,7 @@ pub(crate) fn infer_plugins_from_codes(selectors: &HashSet<RuleSelector>) -> Vec
.filter(|plugin| {
for selector in selectors {
if selector
.rules(PreviewMode::Disabled)
.rules(&PreviewOptions::default())
.any(|rule| Linter::from(plugin).rules().any(|r| r == rule))
{
return true;
Expand Down
17 changes: 13 additions & 4 deletions crates/ruff/src/rule_selector.rs
Expand Up @@ -198,16 +198,19 @@ impl RuleSelector {
}
}

/// Returns rules matching the selector, taking into account whether preview mode is enabled.
pub fn rules(&self, preview: PreviewMode) -> impl Iterator<Item = Rule> + '_ {
/// Returns rules matching the selector, taking into account preview options enabled.
pub fn rules<'a, 'b>(&'a self, preview: &'b PreviewOptions) -> impl Iterator<Item = Rule> + 'a {
let preview_enabled = preview.mode.is_enabled();
let preview_require_explicit = preview.require_explicit;
#[allow(deprecated)]
self.all_rules().filter(move |rule| {
// Always include rules that are not in preview or the nursery
!(rule.is_preview() || rule.is_nursery())
// Backwards compatibility allows selection of nursery rules by exact code or dedicated group
|| ((matches!(self, RuleSelector::Rule { .. }) || matches!(self, RuleSelector::Nursery { .. })) && rule.is_nursery())
// Enabling preview includes all preview or nursery rules
|| preview.is_enabled()
// Enabling preview includes all preview or nursery rules unless explicit selection
// is turned on
|| (preview_enabled && (matches!(self, RuleSelector::Rule { .. }) || !preview_require_explicit))
})
}
}
Expand All @@ -232,6 +235,12 @@ impl Iterator for RuleSelectorIter {
}
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PreviewOptions {
pub mode: PreviewMode,
pub require_explicit: bool,
}

#[cfg(feature = "schemars")]
mod schema {
use itertools::Itertools;
Expand Down
5 changes: 3 additions & 2 deletions crates/ruff/src/settings/defaults.rs
Expand Up @@ -9,7 +9,7 @@ use super::Settings;
use crate::codes::{self, RuleCodePrefix};
use crate::line_width::{LineLength, TabSize};
use crate::registry::Linter;
use crate::rule_selector::RuleSelector;
use crate::rule_selector::{PreviewOptions, RuleSelector};
use crate::rules::{
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions,
flake8_copyright, flake8_errmsg, flake8_gettext, flake8_implicit_str_concat,
Expand Down Expand Up @@ -75,7 +75,7 @@ impl Default for Settings {
Self {
rules: PREFIXES
.iter()
.flat_map(|selector| selector.rules(PreviewMode::default()))
.flat_map(|selector| selector.rules(&PreviewOptions::default()))
.collect(),
allowed_confusables: FxHashSet::from_iter([]),
builtins: vec![],
Expand All @@ -91,6 +91,7 @@ impl Default for Settings {
logger_objects: vec![],
namespace_packages: vec![],
preview: PreviewMode::default(),
explicit_preview_rules: false,
per_file_ignores: vec![],
project_root: path_dedot::CWD.clone(),
respect_gitignore: true,
Expand Down
1 change: 1 addition & 0 deletions crates/ruff/src/settings/mod.rs
Expand Up @@ -57,6 +57,7 @@ pub struct Settings {

pub target_version: PythonVersion,
pub preview: PreviewMode,
pub explicit_preview_rules: bool,

// Resolver settings
pub exclude: FilePatternSet,
Expand Down
33 changes: 21 additions & 12 deletions crates/ruff_workspace/src/configuration.rs
Expand Up @@ -20,7 +20,7 @@ use regex::Regex;
use ruff::line_width::{LineLength, TabSize};
use ruff::registry::RuleNamespace;
use ruff::registry::{Rule, RuleSet, INCOMPATIBLE_CODES};
use ruff::rule_selector::Specificity;
use ruff::rule_selector::{PreviewOptions, Specificity};
use ruff::settings::rule_table::RuleTable;
use ruff::settings::types::{
FilePattern, FilePatternSet, PerFileIgnore, PreviewMode, PythonVersion, SerializationFormat,
Expand Down Expand Up @@ -69,6 +69,7 @@ pub struct Configuration {
pub logger_objects: Option<Vec<String>>,
pub namespace_packages: Option<Vec<PathBuf>>,
pub preview: Option<PreviewMode>,
pub explicit_preview_rules: Option<bool>,
pub required_version: Option<Version>,
pub respect_gitignore: Option<bool>,
pub show_fixes: Option<bool>,
Expand Down Expand Up @@ -177,6 +178,7 @@ impl Configuration {
}),
logger_objects: self.logger_objects.unwrap_or_default(),
preview: self.preview.unwrap_or_default(),
explicit_preview_rules: self.explicit_preview_rules.unwrap_or_default(),
typing_modules: self.typing_modules.unwrap_or_default(),
// Plugins
flake8_annotations: self
Expand Down Expand Up @@ -391,6 +393,7 @@ impl Configuration {
.map(|namespace_package| resolve_src(&namespace_package, project_root))
.transpose()?,
preview: options.preview.map(PreviewMode::from),
explicit_preview_rules: options.explicit_preview_rules,
per_file_ignores: options.per_file_ignores.map(|per_file_ignores| {
per_file_ignores
.into_iter()
Expand Down Expand Up @@ -440,16 +443,19 @@ impl Configuration {
}

pub fn as_rule_table(&self) -> RuleTable {
let preview = self.preview.unwrap_or_default();
let preview = PreviewOptions {
mode: self.preview.unwrap_or_default(),
require_explicit: self.explicit_preview_rules.unwrap_or_default(),
};

// The select_set keeps track of which rules have been selected.
let mut select_set: RuleSet = defaults::PREFIXES
.iter()
.flat_map(|selector| selector.rules(preview))
.flat_map(|selector| selector.rules(&preview))
.collect();

// The fixable set keeps track of which rules are fixable.
let mut fixable_set: RuleSet = RuleSelector::All.rules(preview).collect();
let mut fixable_set: RuleSet = RuleSelector::All.rules(&preview).collect();

// Ignores normally only subtract from the current set of selected
// rules. By that logic the ignore in `select = [], ignore = ["E501"]`
Expand Down Expand Up @@ -488,7 +494,7 @@ impl Configuration {
.chain(selection.extend_select.iter())
.filter(|s| s.specificity() == spec)
{
for rule in selector.rules(preview) {
for rule in selector.rules(&preview) {
select_map_updates.insert(rule, true);
}
}
Expand All @@ -498,7 +504,7 @@ impl Configuration {
.chain(carriedover_ignores.into_iter().flatten())
.filter(|s| s.specificity() == spec)
{
for rule in selector.rules(preview) {
for rule in selector.rules(&preview) {
select_map_updates.insert(rule, false);
}
}
Expand All @@ -510,7 +516,7 @@ impl Configuration {
.chain(selection.extend_fixable.iter())
.filter(|s| s.specificity() == spec)
{
for rule in selector.rules(preview) {
for rule in selector.rules(&preview) {
fixable_map_updates.insert(rule, true);
}
}
Expand All @@ -520,7 +526,7 @@ impl Configuration {
.chain(carriedover_unfixables.into_iter().flatten())
.filter(|s| s.specificity() == spec)
{
for rule in selector.rules(preview) {
for rule in selector.rules(&preview) {
fixable_map_updates.insert(rule, false);
}
}
Expand Down Expand Up @@ -587,24 +593,24 @@ impl Configuration {
{
#[allow(deprecated)]
if matches!(selector, RuleSelector::Nursery) {
let suggestion = if preview.is_disabled() {
let suggestion = if preview.mode.is_disabled() {
" Use the `--preview` flag instead."
} else {
// We have no suggested alternative since there is intentionally no "PREVIEW" selector
""
};
warn_user_once!("The `NURSERY` selector has been deprecated.{suggestion}");
}
};

if preview.is_disabled() {
if preview.mode.is_disabled() {
if let RuleSelector::Rule { prefix, .. } = selector {
if prefix.rules().any(|rule| rule.is_nursery()) {
deprecated_nursery_selectors.insert(selector);
}
}

// Check if the selector is empty because preview mode is disabled
if selector.rules(PreviewMode::Disabled).next().is_none() {
if selector.rules(&PreviewOptions::default()).next().is_none() {
ignored_preview_selectors.insert(selector);
}
}
Expand Down Expand Up @@ -722,6 +728,9 @@ impl Configuration {
src: self.src.or(config.src),
target_version: self.target_version.or(config.target_version),
preview: self.preview.or(config.preview),
explicit_preview_rules: self
.explicit_preview_rules
.or(config.explicit_preview_rules),
task_tags: self.task_tags.or(config.task_tags),
typing_modules: self.typing_modules.or(config.typing_modules),
// Plugins
Expand Down
18 changes: 15 additions & 3 deletions crates/ruff_workspace/src/options.rs
Expand Up @@ -494,15 +494,27 @@ pub struct Options {
/// use unstable rules and fixes.
pub preview: Option<bool>,
#[option(
default = r#"["TODO", "FIXME", "XXX"]"#,
value_type = "list[str]",
example = r#"task-tags = ["HACK"]"#
default = "false",
value_type = "bool",
example = r#"
# Require explicit selection of preview rules
explicit-preview-rules = true
"#
)]
/// Whether to require exact codes to select preview rules. When enabled,
/// preview rules will be be selected by prefixes — the full code of each
/// preview rule will be required to enable the rule.
pub explicit_preview_rules: Option<bool>,
/// A list of task tags to recognize (e.g., "TODO", "FIXME", "XXX").
///
/// Comments starting with these tags will be ignored by commented-out code
/// detection (`ERA`), and skipped by line-length rules (`E501`) if
/// `ignore-overlong-task-comments` is set to `true`.
#[option(
default = r#"["TODO", "FIXME", "XXX"]"#,
value_type = "list[str]",
example = r#"task-tags = ["HACK"]"#
)]
pub task_tags: Option<Vec<String>>,
#[option(
default = r#"[]"#,
Expand Down
7 changes: 7 additions & 0 deletions ruff.schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 91283e5

Please sign in to comment.