mirror of https://github.com/astral-sh/ruff
224 lines
8.6 KiB
Rust
224 lines
8.6 KiB
Rust
//! Effective program settings, taking into account pyproject.toml and
|
|
//! command-line options. Structure is optimized for internal usage, as opposed
|
|
//! to external visibility or parsing.
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use anyhow::Result;
|
|
use globset::{Glob, GlobMatcher};
|
|
use once_cell::sync::Lazy;
|
|
use path_absolutize::path_dedot;
|
|
use regex::Regex;
|
|
use rustc_hash::FxHashSet;
|
|
|
|
use crate::codes::RuleCodePrefix;
|
|
use ruff_macros::CacheKey;
|
|
|
|
use crate::line_width::LineLength;
|
|
use crate::registry::{Linter, Rule, RuleSet};
|
|
use crate::rules::{
|
|
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions,
|
|
flake8_copyright, flake8_errmsg, flake8_gettext, flake8_implicit_str_concat,
|
|
flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_self,
|
|
flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, pep8_naming,
|
|
pycodestyle, pydocstyle, pyflakes, pylint, pyupgrade,
|
|
};
|
|
use crate::settings::types::{FilePatternSet, PerFileIgnore, PythonVersion};
|
|
use crate::{codes, RuleSelector};
|
|
|
|
use super::line_width::IndentWidth;
|
|
|
|
use self::fix_safety_table::FixSafetyTable;
|
|
use self::rule_table::RuleTable;
|
|
use self::types::PreviewMode;
|
|
use crate::rule_selector::PreviewOptions;
|
|
|
|
pub mod fix_safety_table;
|
|
pub mod flags;
|
|
pub mod rule_table;
|
|
pub mod types;
|
|
|
|
#[derive(Debug, CacheKey)]
|
|
pub struct LinterSettings {
|
|
pub exclude: FilePatternSet,
|
|
pub project_root: PathBuf,
|
|
|
|
pub rules: RuleTable,
|
|
pub per_file_ignores: Vec<(GlobMatcher, GlobMatcher, RuleSet)>,
|
|
pub fix_safety: FixSafetyTable,
|
|
|
|
pub target_version: PythonVersion,
|
|
pub preview: PreviewMode,
|
|
pub explicit_preview_rules: bool,
|
|
|
|
// Rule-specific settings
|
|
pub allowed_confusables: FxHashSet<char>,
|
|
pub builtins: Vec<String>,
|
|
pub dummy_variable_rgx: Regex,
|
|
pub external: Vec<String>,
|
|
pub ignore_init_module_imports: bool,
|
|
pub logger_objects: Vec<String>,
|
|
pub namespace_packages: Vec<PathBuf>,
|
|
pub src: Vec<PathBuf>,
|
|
pub tab_size: IndentWidth,
|
|
pub line_length: LineLength,
|
|
pub task_tags: Vec<String>,
|
|
pub typing_modules: Vec<String>,
|
|
|
|
// Plugins
|
|
pub flake8_annotations: flake8_annotations::settings::Settings,
|
|
pub flake8_bandit: flake8_bandit::settings::Settings,
|
|
pub flake8_bugbear: flake8_bugbear::settings::Settings,
|
|
pub flake8_builtins: flake8_builtins::settings::Settings,
|
|
pub flake8_comprehensions: flake8_comprehensions::settings::Settings,
|
|
pub flake8_copyright: flake8_copyright::settings::Settings,
|
|
pub flake8_errmsg: flake8_errmsg::settings::Settings,
|
|
pub flake8_gettext: flake8_gettext::settings::Settings,
|
|
pub flake8_implicit_str_concat: flake8_implicit_str_concat::settings::Settings,
|
|
pub flake8_import_conventions: flake8_import_conventions::settings::Settings,
|
|
pub flake8_pytest_style: flake8_pytest_style::settings::Settings,
|
|
pub flake8_quotes: flake8_quotes::settings::Settings,
|
|
pub flake8_self: flake8_self::settings::Settings,
|
|
pub flake8_tidy_imports: flake8_tidy_imports::settings::Settings,
|
|
pub flake8_type_checking: flake8_type_checking::settings::Settings,
|
|
pub flake8_unused_arguments: flake8_unused_arguments::settings::Settings,
|
|
pub isort: isort::settings::Settings,
|
|
pub mccabe: mccabe::settings::Settings,
|
|
pub pep8_naming: pep8_naming::settings::Settings,
|
|
pub pycodestyle: pycodestyle::settings::Settings,
|
|
pub pydocstyle: pydocstyle::settings::Settings,
|
|
pub pyflakes: pyflakes::settings::Settings,
|
|
pub pylint: pylint::settings::Settings,
|
|
pub pyupgrade: pyupgrade::settings::Settings,
|
|
}
|
|
|
|
pub const DEFAULT_SELECTORS: &[RuleSelector] = &[
|
|
RuleSelector::Linter(Linter::Pyflakes),
|
|
// Only include pycodestyle rules that do not overlap with the formatter
|
|
RuleSelector::Prefix {
|
|
prefix: RuleCodePrefix::Pycodestyle(codes::Pycodestyle::E4),
|
|
redirected_from: None,
|
|
},
|
|
RuleSelector::Prefix {
|
|
prefix: RuleCodePrefix::Pycodestyle(codes::Pycodestyle::E7),
|
|
redirected_from: None,
|
|
},
|
|
RuleSelector::Prefix {
|
|
prefix: RuleCodePrefix::Pycodestyle(codes::Pycodestyle::E9),
|
|
redirected_from: None,
|
|
},
|
|
];
|
|
|
|
pub const TASK_TAGS: &[&str] = &["TODO", "FIXME", "XXX"];
|
|
|
|
pub static DUMMY_VARIABLE_RGX: Lazy<Regex> =
|
|
Lazy::new(|| Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap());
|
|
|
|
impl LinterSettings {
|
|
pub fn for_rule(rule_code: Rule) -> Self {
|
|
Self {
|
|
rules: RuleTable::from_iter([rule_code]),
|
|
target_version: PythonVersion::latest(),
|
|
..Self::default()
|
|
}
|
|
}
|
|
|
|
pub fn for_rules(rules: impl IntoIterator<Item = Rule>) -> Self {
|
|
Self {
|
|
rules: RuleTable::from_iter(rules),
|
|
target_version: PythonVersion::latest(),
|
|
..Self::default()
|
|
}
|
|
}
|
|
|
|
pub fn new(project_root: &Path) -> Self {
|
|
Self {
|
|
exclude: FilePatternSet::default(),
|
|
target_version: PythonVersion::default(),
|
|
project_root: project_root.to_path_buf(),
|
|
rules: DEFAULT_SELECTORS
|
|
.iter()
|
|
.flat_map(|selector| selector.rules(&PreviewOptions::default()))
|
|
.collect(),
|
|
allowed_confusables: FxHashSet::from_iter([]),
|
|
|
|
// Needs duplicating
|
|
builtins: vec![],
|
|
dummy_variable_rgx: DUMMY_VARIABLE_RGX.clone(),
|
|
|
|
external: vec![],
|
|
ignore_init_module_imports: false,
|
|
logger_objects: vec![],
|
|
namespace_packages: vec![],
|
|
|
|
per_file_ignores: vec![],
|
|
fix_safety: FixSafetyTable::default(),
|
|
|
|
src: vec![path_dedot::CWD.clone()],
|
|
// Needs duplicating
|
|
tab_size: IndentWidth::default(),
|
|
line_length: LineLength::default(),
|
|
|
|
task_tags: TASK_TAGS.iter().map(ToString::to_string).collect(),
|
|
typing_modules: vec![],
|
|
flake8_annotations: flake8_annotations::settings::Settings::default(),
|
|
flake8_bandit: flake8_bandit::settings::Settings::default(),
|
|
flake8_bugbear: flake8_bugbear::settings::Settings::default(),
|
|
flake8_builtins: flake8_builtins::settings::Settings::default(),
|
|
flake8_comprehensions: flake8_comprehensions::settings::Settings::default(),
|
|
flake8_copyright: flake8_copyright::settings::Settings::default(),
|
|
flake8_errmsg: flake8_errmsg::settings::Settings::default(),
|
|
flake8_implicit_str_concat: flake8_implicit_str_concat::settings::Settings::default(),
|
|
flake8_import_conventions: flake8_import_conventions::settings::Settings::default(),
|
|
flake8_pytest_style: flake8_pytest_style::settings::Settings::default(),
|
|
flake8_quotes: flake8_quotes::settings::Settings::default(),
|
|
flake8_gettext: flake8_gettext::settings::Settings::default(),
|
|
flake8_self: flake8_self::settings::Settings::default(),
|
|
flake8_tidy_imports: flake8_tidy_imports::settings::Settings::default(),
|
|
flake8_type_checking: flake8_type_checking::settings::Settings::default(),
|
|
flake8_unused_arguments: flake8_unused_arguments::settings::Settings::default(),
|
|
isort: isort::settings::Settings::default(),
|
|
mccabe: mccabe::settings::Settings::default(),
|
|
pep8_naming: pep8_naming::settings::Settings::default(),
|
|
pycodestyle: pycodestyle::settings::Settings::default(),
|
|
pydocstyle: pydocstyle::settings::Settings::default(),
|
|
pyflakes: pyflakes::settings::Settings::default(),
|
|
pylint: pylint::settings::Settings::default(),
|
|
pyupgrade: pyupgrade::settings::Settings::default(),
|
|
preview: PreviewMode::default(),
|
|
explicit_preview_rules: false,
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
pub fn with_target_version(mut self, target_version: PythonVersion) -> Self {
|
|
self.target_version = target_version;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Default for LinterSettings {
|
|
fn default() -> Self {
|
|
Self::new(path_dedot::CWD.as_path())
|
|
}
|
|
}
|
|
|
|
/// Given a list of patterns, create a `GlobSet`.
|
|
pub fn resolve_per_file_ignores(
|
|
per_file_ignores: Vec<PerFileIgnore>,
|
|
) -> Result<Vec<(GlobMatcher, GlobMatcher, RuleSet)>> {
|
|
per_file_ignores
|
|
.into_iter()
|
|
.map(|per_file_ignore| {
|
|
// Construct absolute path matcher.
|
|
let absolute =
|
|
Glob::new(&per_file_ignore.absolute.to_string_lossy())?.compile_matcher();
|
|
|
|
// Construct basename matcher.
|
|
let basename = Glob::new(&per_file_ignore.basename)?.compile_matcher();
|
|
|
|
Ok((absolute, basename, per_file_ignore.rules))
|
|
})
|
|
.collect()
|
|
}
|