mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 07:04:37 +00:00
refactor: Introduce CacheKey
trait (#3323)
This PR introduces a new `CacheKey` trait for types that can be used as a cache key. I'm not entirely sure if this is worth the "overhead", but I was surprised to find `HashableHashSet` and got scared when I looked at the time complexity of the `hash` function. These implementations must be extremely slow in hashed collections. I then searched for usages and quickly realized that only the cache uses these `Hash` implementations, where performance is less sensitive. This PR introduces a new `CacheKey` trait to communicate the difference between a hash and computing a key for the cache. The new trait can be implemented for types that don't implement `Hash` for performance reasons, and we can define additional constraints on the implementation: For example, we'll want to enforce portability when we add remote caching support. Using a different trait further allows us not to implement it for types without stable identities (e.g. pointers) or use other implementations than the standard hash function.
This commit is contained in:
parent
d1288dc2b1
commit
cdbe2ee496
53 changed files with 842 additions and 331 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -1982,6 +1982,7 @@ dependencies = [
|
||||||
"path-absolutize",
|
"path-absolutize",
|
||||||
"regex",
|
"regex",
|
||||||
"result-like",
|
"result-like",
|
||||||
|
"ruff_cache",
|
||||||
"ruff_macros",
|
"ruff_macros",
|
||||||
"ruff_python",
|
"ruff_python",
|
||||||
"ruff_rustpython",
|
"ruff_rustpython",
|
||||||
|
@ -2005,6 +2006,17 @@ dependencies = [
|
||||||
"wasm-bindgen-test",
|
"wasm-bindgen-test",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ruff_cache"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"filetime",
|
||||||
|
"globset",
|
||||||
|
"itertools",
|
||||||
|
"regex",
|
||||||
|
"ruff_macros",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff_cli"
|
name = "ruff_cli"
|
||||||
version = "0.0.253"
|
version = "0.0.253"
|
||||||
|
@ -2033,6 +2045,7 @@ dependencies = [
|
||||||
"rayon",
|
"rayon",
|
||||||
"regex",
|
"regex",
|
||||||
"ruff",
|
"ruff",
|
||||||
|
"ruff_cache",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
@ -19,6 +19,7 @@ doctest = false
|
||||||
ruff_macros = { path = "../ruff_macros" }
|
ruff_macros = { path = "../ruff_macros" }
|
||||||
ruff_python = { path = "../ruff_python" }
|
ruff_python = { path = "../ruff_python" }
|
||||||
ruff_rustpython = { path = "../ruff_rustpython" }
|
ruff_rustpython = { path = "../ruff_rustpython" }
|
||||||
|
ruff_cache = { path = "../ruff_cache" }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
bisection = { version = "0.1.0" }
|
bisection = { version = "0.1.0" }
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
use std::ops::Deref;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use globset::GlobMatcher;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use path_absolutize::{path_dedot, Absolutize};
|
use path_absolutize::{path_dedot, Absolutize};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
use crate::settings::hashable::{HashableGlobMatcher, HashableHashSet};
|
|
||||||
|
|
||||||
/// Extract the absolute path and basename (as strings) from a Path.
|
/// Extract the absolute path and basename (as strings) from a Path.
|
||||||
pub fn extract_path_names(path: &Path) -> Result<(&str, &str)> {
|
pub fn extract_path_names(path: &Path) -> Result<(&str, &str)> {
|
||||||
|
@ -25,11 +24,7 @@ pub fn extract_path_names(path: &Path) -> Result<(&str, &str)> {
|
||||||
/// Create a set with codes matching the pattern/code pairs.
|
/// Create a set with codes matching the pattern/code pairs.
|
||||||
pub(crate) fn ignores_from_path<'a>(
|
pub(crate) fn ignores_from_path<'a>(
|
||||||
path: &Path,
|
path: &Path,
|
||||||
pattern_code_pairs: &'a [(
|
pattern_code_pairs: &'a [(GlobMatcher, GlobMatcher, FxHashSet<Rule>)],
|
||||||
HashableGlobMatcher,
|
|
||||||
HashableGlobMatcher,
|
|
||||||
HashableHashSet<Rule>,
|
|
||||||
)],
|
|
||||||
) -> FxHashSet<&'a Rule> {
|
) -> FxHashSet<&'a Rule> {
|
||||||
let (file_path, file_basename) = extract_path_names(path).expect("Unable to parse filename");
|
let (file_path, file_basename) = extract_path_names(path).expect("Unable to parse filename");
|
||||||
pattern_code_pairs
|
pattern_code_pairs
|
||||||
|
@ -39,8 +34,8 @@ pub(crate) fn ignores_from_path<'a>(
|
||||||
debug!(
|
debug!(
|
||||||
"Adding per-file ignores for {:?} due to basename match on {:?}: {:?}",
|
"Adding per-file ignores for {:?} due to basename match on {:?}: {:?}",
|
||||||
path,
|
path,
|
||||||
basename.deref().glob().regex(),
|
basename.glob().regex(),
|
||||||
&**codes
|
codes
|
||||||
);
|
);
|
||||||
return Some(codes.iter());
|
return Some(codes.iter());
|
||||||
}
|
}
|
||||||
|
@ -48,8 +43,8 @@ pub(crate) fn ignores_from_path<'a>(
|
||||||
debug!(
|
debug!(
|
||||||
"Adding per-file ignores for {:?} due to absolute match on {:?}: {:?}",
|
"Adding per-file ignores for {:?} due to absolute match on {:?}: {:?}",
|
||||||
path,
|
path,
|
||||||
absolute.deref().glob().regex(),
|
absolute.glob().regex(),
|
||||||
&**codes
|
codes
|
||||||
);
|
);
|
||||||
return Some(codes.iter());
|
return Some(codes.iter());
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ pub use violation::{AutofixKind, Availability as AutofixAvailability};
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
mod autofix;
|
mod autofix;
|
||||||
pub mod cache;
|
|
||||||
mod checkers;
|
mod checkers;
|
||||||
mod codes;
|
mod codes;
|
||||||
mod cst;
|
mod cst;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! Settings for the `flake-annotations` plugin.
|
//! Settings for the `flake-annotations` plugin.
|
||||||
|
|
||||||
|
use ruff_macros::CacheKey;
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::ConfigurationOptions;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -60,7 +61,7 @@ pub struct Options {
|
||||||
pub ignore_fully_untyped: Option<bool>,
|
pub ignore_fully_untyped: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Hash)]
|
#[derive(Debug, Default, CacheKey)]
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub mypy_init_return: bool,
|
pub mypy_init_return: bool,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `flake8-bandit` plugin.
|
//! Settings for the `flake8-bandit` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ pub struct Options {
|
||||||
pub check_typed_exception: Option<bool>,
|
pub check_typed_exception: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub hardcoded_tmp_directory: Vec<String>,
|
pub hardcoded_tmp_directory: Vec<String>,
|
||||||
pub check_typed_exception: bool,
|
pub check_typed_exception: bool,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `flake8-bugbear` plugin.
|
//! Settings for the `flake8-bugbear` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ pub struct Options {
|
||||||
pub extend_immutable_calls: Option<Vec<String>>,
|
pub extend_immutable_calls: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Hash)]
|
#[derive(Debug, Default, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub extend_immutable_calls: Vec<String>,
|
pub extend_immutable_calls: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `flake8-builtins` plugin.
|
//! Settings for the `flake8-builtins` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ pub struct Options {
|
||||||
pub builtins_ignorelist: Option<Vec<String>>,
|
pub builtins_ignorelist: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Hash)]
|
#[derive(Debug, Default, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub builtins_ignorelist: Vec<String>,
|
pub builtins_ignorelist: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `flake8-comprehensions` plugin.
|
//! Settings for the `flake8-comprehensions` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ pub struct Options {
|
||||||
pub allow_dict_calls_with_keyword_arguments: Option<bool>,
|
pub allow_dict_calls_with_keyword_arguments: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Hash)]
|
#[derive(Debug, Default, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub allow_dict_calls_with_keyword_arguments: bool,
|
pub allow_dict_calls_with_keyword_arguments: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `flake8-errmsg` plugin.
|
//! Settings for the `flake8-errmsg` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ pub struct Options {
|
||||||
pub max_string_length: Option<usize>,
|
pub max_string_length: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Hash)]
|
#[derive(Debug, Default, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub max_string_length: usize,
|
pub max_string_length: usize,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `flake8-implicit-str-concat` plugin.
|
//! Settings for the `flake8-implicit-str-concat` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ pub struct Options {
|
||||||
pub allow_multiline: Option<bool>,
|
pub allow_multiline: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub allow_multiline: bool,
|
pub allow_multiline: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
//! Settings for import conventions.
|
//! Settings for import conventions.
|
||||||
|
|
||||||
use std::hash::Hash;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::settings::hashable::HashableHashMap;
|
|
||||||
|
|
||||||
const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[
|
const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[
|
||||||
("altair", "alt"),
|
("altair", "alt"),
|
||||||
("matplotlib", "mpl"),
|
("matplotlib", "mpl"),
|
||||||
|
@ -64,9 +60,9 @@ pub struct Options {
|
||||||
pub extend_aliases: Option<FxHashMap<String, String>>,
|
pub extend_aliases: Option<FxHashMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub aliases: HashableHashMap<String, String>,
|
pub aliases: FxHashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_aliases() -> FxHashMap<String, String> {
|
fn default_aliases() -> FxHashMap<String, String> {
|
||||||
|
@ -90,7 +86,7 @@ fn resolve_aliases(options: Options) -> FxHashMap<String, String> {
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
aliases: default_aliases().into(),
|
aliases: default_aliases(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,7 +94,7 @@ impl Default for Settings {
|
||||||
impl From<Options> for Settings {
|
impl From<Options> for Settings {
|
||||||
fn from(options: Options) -> Self {
|
fn from(options: Options) -> Self {
|
||||||
Self {
|
Self {
|
||||||
aliases: resolve_aliases(options).into(),
|
aliases: resolve_aliases(options),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +102,7 @@ impl From<Options> for Settings {
|
||||||
impl From<Settings> for Options {
|
impl From<Settings> for Options {
|
||||||
fn from(settings: Settings) -> Self {
|
fn from(settings: Settings) -> Self {
|
||||||
Self {
|
Self {
|
||||||
aliases: Some(settings.aliases.into()),
|
aliases: Some(settings.aliases),
|
||||||
extend_aliases: None,
|
extend_aliases: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `flake8-pytest-style` plugin.
|
//! Settings for the `flake8-pytest-style` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ pub struct Options {
|
||||||
pub mark_parentheses: Option<bool>,
|
pub mark_parentheses: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub fixture_parentheses: bool,
|
pub fixture_parentheses: bool,
|
||||||
pub parametrize_names_type: types::ParametrizeNameType,
|
pub parametrize_names_type: types::ParametrizeNameType,
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
use ruff_macros::CacheKey;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Copy, Debug, CacheKey, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
pub enum ParametrizeNameType {
|
pub enum ParametrizeNameType {
|
||||||
#[serde(rename = "csv")]
|
#[serde(rename = "csv")]
|
||||||
Csv,
|
Csv,
|
||||||
|
@ -29,7 +30,7 @@ impl Display for ParametrizeNameType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Copy, Debug, CacheKey, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
pub enum ParametrizeValuesType {
|
pub enum ParametrizeValuesType {
|
||||||
#[serde(rename = "tuple")]
|
#[serde(rename = "tuple")]
|
||||||
Tuple,
|
Tuple,
|
||||||
|
@ -52,7 +53,7 @@ impl Display for ParametrizeValuesType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Copy, Debug, CacheKey, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
pub enum ParametrizeValuesRowType {
|
pub enum ParametrizeValuesRowType {
|
||||||
#[serde(rename = "tuple")]
|
#[serde(rename = "tuple")]
|
||||||
Tuple,
|
Tuple,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
//! Settings for the `flake8-quotes` plugin.
|
//! Settings for the `flake8-quotes` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, CacheKey, JsonSchema)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub enum Quote {
|
pub enum Quote {
|
||||||
/// Use single quotes.
|
/// Use single quotes.
|
||||||
|
@ -71,7 +71,7 @@ pub struct Options {
|
||||||
pub avoid_escape: Option<bool>,
|
pub avoid_escape: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub inline_quotes: Quote,
|
pub inline_quotes: Quote,
|
||||||
pub multiline_quotes: Quote,
|
pub multiline_quotes: Quote,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `flake8-self` plugin.
|
//! Settings for the `flake8-self` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ pub struct Options {
|
||||||
pub ignore_names: Option<Vec<String>>,
|
pub ignore_names: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub ignore_names: Vec<String>,
|
pub ignore_names: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_macros::{define_violation, derive_message_formats};
|
use ruff_macros::{define_violation, derive_message_formats, CacheKey};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use rustpython_parser::ast::{Alias, Expr, Located};
|
use rustpython_parser::ast::{Alias, Expr, Located};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
|
@ -7,12 +7,11 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::ast::types::{CallPath, Range};
|
use crate::ast::types::{CallPath, Range};
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::registry::Diagnostic;
|
use crate::registry::Diagnostic;
|
||||||
use crate::settings::hashable::HashableHashMap;
|
|
||||||
use crate::violation::Violation;
|
use crate::violation::Violation;
|
||||||
|
|
||||||
pub type Settings = HashableHashMap<String, ApiBan>;
|
pub type Settings = FxHashMap<String, ApiBan>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, CacheKey, JsonSchema)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub struct ApiBan {
|
pub struct ApiBan {
|
||||||
/// The message to display when the API is used.
|
/// The message to display when the API is used.
|
||||||
|
@ -147,8 +146,7 @@ mod tests {
|
||||||
msg: "Use typing_extensions.TypedDict instead.".to_string(),
|
msg: "Use typing_extensions.TypedDict instead.".to_string(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
])
|
]),
|
||||||
.into(),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
..Settings::for_rules(vec![Rule::BannedApi])
|
..Settings::for_rules(vec![Rule::BannedApi])
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
//! Rules from [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/).
|
//! Rules from [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/).
|
||||||
|
use ruff_macros::CacheKey;
|
||||||
|
|
||||||
pub mod options;
|
pub mod options;
|
||||||
|
|
||||||
pub mod banned_api;
|
pub mod banned_api;
|
||||||
pub mod relative_imports;
|
pub mod relative_imports;
|
||||||
|
|
||||||
#[derive(Debug, Hash, Default)]
|
#[derive(Debug, CacheKey, Default)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub ban_relative_imports: relative_imports::Settings,
|
pub ban_relative_imports: relative_imports::Settings,
|
||||||
pub banned_api: banned_api::Settings,
|
pub banned_api: banned_api::Settings,
|
||||||
|
|
|
@ -48,7 +48,7 @@ impl From<Options> for Settings {
|
||||||
fn from(options: Options) -> Self {
|
fn from(options: Options) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ban_relative_imports: options.ban_relative_imports.unwrap_or(Strictness::Parents),
|
ban_relative_imports: options.ban_relative_imports.unwrap_or(Strictness::Parents),
|
||||||
banned_api: options.banned_api.unwrap_or_default().into(),
|
banned_api: options.banned_api.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ impl From<Settings> for Options {
|
||||||
fn from(settings: Settings) -> Self {
|
fn from(settings: Settings) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ban_relative_imports: Some(settings.ban_relative_imports),
|
ban_relative_imports: Some(settings.ban_relative_imports),
|
||||||
banned_api: Some(settings.banned_api.into()),
|
banned_api: Some(settings.banned_api),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use rustpython_parser::ast::{Stmt, StmtKind};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use ruff_macros::{define_violation, derive_message_formats};
|
use ruff_macros::{define_violation, derive_message_formats, CacheKey};
|
||||||
use ruff_python::identifiers::is_module_name;
|
use ruff_python::identifiers::is_module_name;
|
||||||
|
|
||||||
use crate::ast::helpers::{create_stmt, from_relative_import, unparse_stmt};
|
use crate::ast::helpers::{create_stmt, from_relative_import, unparse_stmt};
|
||||||
|
@ -15,7 +15,7 @@ use crate::violation::{AutofixKind, Availability, Violation};
|
||||||
|
|
||||||
pub type Settings = Strictness;
|
pub type Settings = Strictness;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, CacheKey, JsonSchema, Default)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub enum Strictness {
|
pub enum Strictness {
|
||||||
/// Ban imports that extend into the parent module or beyond.
|
/// Ban imports that extend into the parent module or beyond.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `flake8-type-checking` plugin.
|
//! Settings for the `flake8-type-checking` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ pub struct Options {
|
||||||
pub exempt_modules: Option<Vec<String>>,
|
pub exempt_modules: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub strict: bool,
|
pub strict: bool,
|
||||||
pub exempt_modules: Vec<String>,
|
pub exempt_modules: Vec<String>,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `flake8-unused-arguments` plugin.
|
//! Settings for the `flake8-unused-arguments` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ pub struct Options {
|
||||||
pub ignore_variadic_names: Option<bool>,
|
pub ignore_variadic_names: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Hash)]
|
#[derive(Debug, Default, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub ignore_variadic_names: bool,
|
pub ignore_variadic_names: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
use ruff_macros::CacheKey;
|
||||||
use ruff_python::sys::KNOWN_STANDARD_LIBRARY;
|
use ruff_python::sys::KNOWN_STANDARD_LIBRARY;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -12,7 +13,17 @@ use super::types::{ImportBlock, Importable};
|
||||||
use crate::settings::types::PythonVersion;
|
use crate::settings::types::PythonVersion;
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema, Hash, EnumIter,
|
Debug,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Clone,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
JsonSchema,
|
||||||
|
CacheKey,
|
||||||
|
EnumIter,
|
||||||
)]
|
)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub enum ImportType {
|
pub enum ImportType {
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::categorize::ImportType;
|
use super::categorize::ImportType;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, CacheKey, JsonSchema)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub enum RelativeImportsOrder {
|
pub enum RelativeImportsOrder {
|
||||||
/// Place "closer" imports (fewer `.` characters, most local) before
|
/// Place "closer" imports (fewer `.` characters, most local) before
|
||||||
|
@ -265,7 +265,7 @@ pub struct Options {
|
||||||
pub forced_separate: Option<Vec<String>>,
|
pub forced_separate: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub required_imports: BTreeSet<String>,
|
pub required_imports: BTreeSet<String>,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `mccabe` plugin.
|
//! Settings for the `mccabe` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ pub struct Options {
|
||||||
pub max_complexity: Option<usize>,
|
pub max_complexity: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub max_complexity: usize,
|
pub max_complexity: usize,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `pep8-naming` plugin.
|
//! Settings for the `pep8-naming` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ pub struct Options {
|
||||||
pub staticmethod_decorators: Option<Vec<String>>,
|
pub staticmethod_decorators: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub ignore_names: Vec<String>,
|
pub ignore_names: Vec<String>,
|
||||||
pub classmethod_decorators: Vec<String>,
|
pub classmethod_decorators: Vec<String>,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `pycodestyle` plugin.
|
//! Settings for the `pycodestyle` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ pub struct Options {
|
||||||
pub ignore_overlong_task_comments: Option<bool>,
|
pub ignore_overlong_task_comments: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Hash)]
|
#[derive(Debug, Default, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub max_doc_length: Option<usize>,
|
pub max_doc_length: Option<usize>,
|
||||||
pub ignore_overlong_task_comments: bool,
|
pub ignore_overlong_task_comments: bool,
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, CacheKey)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub enum Convention {
|
pub enum Convention {
|
||||||
/// Use Google-style docstrings.
|
/// Use Google-style docstrings.
|
||||||
|
@ -112,7 +112,7 @@ pub struct Options {
|
||||||
pub property_decorators: Option<Vec<String>>,
|
pub property_decorators: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Hash)]
|
#[derive(Debug, Default, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub convention: Option<Convention>,
|
pub convention: Option<Convention>,
|
||||||
pub ignore_decorators: BTreeSet<String>,
|
pub ignore_decorators: BTreeSet<String>,
|
||||||
|
|
|
@ -129,7 +129,7 @@ mod tests {
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
Path::new("pyflakes/F841_0.py"),
|
Path::new("pyflakes/F841_0.py"),
|
||||||
&settings::Settings {
|
&settings::Settings {
|
||||||
dummy_variable_rgx: Regex::new(r"^z$").unwrap().into(),
|
dummy_variable_rgx: Regex::new(r"^z$").unwrap(),
|
||||||
..settings::Settings::for_rule(Rule::UnusedVariable)
|
..settings::Settings::for_rule(Rule::UnusedVariable)
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -99,7 +99,7 @@ mod tests {
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
Path::new("pylint/too_many_arguments_params.py"),
|
Path::new("pylint/too_many_arguments_params.py"),
|
||||||
&Settings {
|
&Settings {
|
||||||
dummy_variable_rgx: Regex::new(r"skip_.*").unwrap().into(),
|
dummy_variable_rgx: Regex::new(r"skip_.*").unwrap(),
|
||||||
..Settings::for_rules(vec![Rule::TooManyArguments])
|
..Settings::for_rules(vec![Rule::TooManyArguments])
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use regex::Regex;
|
||||||
use std::{fmt, iter};
|
use std::{fmt, iter};
|
||||||
|
|
||||||
use rustpython_parser::ast::{Expr, ExprContext, ExprKind, Stmt, StmtKind, Withitem};
|
use rustpython_parser::ast::{Expr, ExprContext, ExprKind, Stmt, StmtKind, Withitem};
|
||||||
|
@ -12,7 +13,6 @@ use crate::ast::visitor;
|
||||||
use crate::ast::visitor::Visitor;
|
use crate::ast::visitor::Visitor;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::registry::Diagnostic;
|
use crate::registry::Diagnostic;
|
||||||
use crate::settings::hashable::HashableRegex;
|
|
||||||
use crate::violation::Violation;
|
use crate::violation::Violation;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
|
||||||
|
@ -142,7 +142,7 @@ struct ExprWithInnerBindingKind<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InnerForWithAssignTargetsVisitor<'a> {
|
struct InnerForWithAssignTargetsVisitor<'a> {
|
||||||
dummy_variable_rgx: &'a HashableRegex,
|
dummy_variable_rgx: &'a Regex,
|
||||||
assignment_targets: Vec<ExprWithInnerBindingKind<'a>>,
|
assignment_targets: Vec<ExprWithInnerBindingKind<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ where
|
||||||
|
|
||||||
fn assignment_targets_from_expr<'a, U>(
|
fn assignment_targets_from_expr<'a, U>(
|
||||||
expr: &'a Expr<U>,
|
expr: &'a Expr<U>,
|
||||||
dummy_variable_rgx: &'a HashableRegex,
|
dummy_variable_rgx: &'a Regex,
|
||||||
) -> Box<dyn Iterator<Item = &'a Expr<U>> + 'a> {
|
) -> Box<dyn Iterator<Item = &'a Expr<U>> + 'a> {
|
||||||
// The Box is necessary to ensure the match arms have the same return type - we can't use
|
// The Box is necessary to ensure the match arms have the same return type - we can't use
|
||||||
// a cast to "impl Iterator", since at the time of writing that is only allowed for
|
// a cast to "impl Iterator", since at the time of writing that is only allowed for
|
||||||
|
@ -266,7 +266,7 @@ fn assignment_targets_from_expr<'a, U>(
|
||||||
|
|
||||||
fn assignment_targets_from_with_items<'a, U>(
|
fn assignment_targets_from_with_items<'a, U>(
|
||||||
items: &'a [Withitem<U>],
|
items: &'a [Withitem<U>],
|
||||||
dummy_variable_rgx: &'a HashableRegex,
|
dummy_variable_rgx: &'a Regex,
|
||||||
) -> impl Iterator<Item = &'a Expr<U>> + 'a {
|
) -> impl Iterator<Item = &'a Expr<U>> + 'a {
|
||||||
items
|
items
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -280,7 +280,7 @@ fn assignment_targets_from_with_items<'a, U>(
|
||||||
|
|
||||||
fn assignment_targets_from_assign_targets<'a, U>(
|
fn assignment_targets_from_assign_targets<'a, U>(
|
||||||
targets: &'a [Expr<U>],
|
targets: &'a [Expr<U>],
|
||||||
dummy_variable_rgx: &'a HashableRegex,
|
dummy_variable_rgx: &'a Regex,
|
||||||
) -> impl Iterator<Item = &'a Expr<U>> + 'a {
|
) -> impl Iterator<Item = &'a Expr<U>> + 'a {
|
||||||
targets
|
targets
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
//! Settings for the `pylint` plugin.
|
//! Settings for the `pylint` plugin.
|
||||||
|
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use rustpython_parser::ast::Constant;
|
use rustpython_parser::ast::Constant;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, CacheKey, JsonSchema)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub enum ConstantType {
|
pub enum ConstantType {
|
||||||
Bytes,
|
Bytes,
|
||||||
|
@ -72,7 +70,7 @@ pub struct Options {
|
||||||
pub max_statements: Option<usize>,
|
pub max_statements: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub allow_magic_value_types: Vec<ConstantType>,
|
pub allow_magic_value_types: Vec<ConstantType>,
|
||||||
pub max_args: usize,
|
pub max_args: usize,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Settings for the `pyupgrade` plugin.
|
//! Settings for the `pyupgrade` plugin.
|
||||||
|
|
||||||
use ruff_macros::ConfigurationOptions;
|
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ pub struct Options {
|
||||||
pub keep_runtime_typing: Option<bool>,
|
pub keep_runtime_typing: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Hash)]
|
#[derive(Debug, Default, CacheKey)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub keep_runtime_typing: bool,
|
pub keep_runtime_typing: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ mod tests {
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
Path::new("ruff/confusables.py"),
|
Path::new("ruff/confusables.py"),
|
||||||
&settings::Settings {
|
&settings::Settings {
|
||||||
allowed_confusables: FxHashSet::from_iter(['−', 'ρ', '∗']).into(),
|
allowed_confusables: FxHashSet::from_iter(['−', 'ρ', '∗']),
|
||||||
..settings::Settings::for_rules(vec![
|
..settings::Settings::for_rules(vec![
|
||||||
Rule::AmbiguousUnicodeCharacterString,
|
Rule::AmbiguousUnicodeCharacterString,
|
||||||
Rule::AmbiguousUnicodeCharacterDocstring,
|
Rule::AmbiguousUnicodeCharacterDocstring,
|
||||||
|
|
|
@ -2,8 +2,8 @@ use once_cell::sync::Lazy;
|
||||||
use path_absolutize::path_dedot;
|
use path_absolutize::path_dedot;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use super::hashable::{HashableGlobSet, HashableHashSet};
|
|
||||||
use super::types::{FilePattern, PythonVersion};
|
use super::types::{FilePattern, PythonVersion};
|
||||||
use super::Settings;
|
use super::Settings;
|
||||||
use crate::codes::{self, RuleCodePrefix};
|
use crate::codes::{self, RuleCodePrefix};
|
||||||
|
@ -15,6 +15,7 @@ use crate::rules::{
|
||||||
flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments,
|
flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments,
|
||||||
isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, pyupgrade,
|
isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, pyupgrade,
|
||||||
};
|
};
|
||||||
|
use crate::settings::types::FilePatternSet;
|
||||||
|
|
||||||
pub const PREFIXES: &[RuleSelector] = &[
|
pub const PREFIXES: &[RuleSelector] = &[
|
||||||
prefix_to_selector(RuleCodePrefix::Pycodestyle(codes::Pycodestyle::E)),
|
prefix_to_selector(RuleCodePrefix::Pycodestyle(codes::Pycodestyle::E)),
|
||||||
|
@ -59,12 +60,12 @@ impl Default for Settings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
rules: PREFIXES.iter().flat_map(IntoIterator::into_iter).into(),
|
rules: PREFIXES.iter().flat_map(IntoIterator::into_iter).into(),
|
||||||
allowed_confusables: FxHashSet::from_iter([]).into(),
|
allowed_confusables: FxHashSet::from_iter([]),
|
||||||
builtins: vec![],
|
builtins: vec![],
|
||||||
dummy_variable_rgx: DUMMY_VARIABLE_RGX.clone().into(),
|
dummy_variable_rgx: DUMMY_VARIABLE_RGX.clone(),
|
||||||
exclude: HashableGlobSet::new(EXCLUDE.clone()).unwrap(),
|
exclude: FilePatternSet::try_from_vec(EXCLUDE.clone()).unwrap(),
|
||||||
extend_exclude: HashableGlobSet::empty(),
|
extend_exclude: FilePatternSet::default(),
|
||||||
external: HashableHashSet::default(),
|
external: HashSet::default(),
|
||||||
force_exclude: false,
|
force_exclude: false,
|
||||||
ignore_init_module_imports: false,
|
ignore_init_module_imports: false,
|
||||||
line_length: LINE_LENGTH,
|
line_length: LINE_LENGTH,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::fix;
|
use crate::fix;
|
||||||
|
use ruff_macros::CacheKey;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Hash, result_like::BoolLike)]
|
#[derive(Debug, Copy, Clone, CacheKey, result_like::BoolLike)]
|
||||||
pub enum Autofix {
|
pub enum Autofix {
|
||||||
Enabled,
|
Enabled,
|
||||||
Disabled,
|
Disabled,
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
use derivative::Derivative;
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use globset::{GlobMatcher, GlobSet};
|
|
||||||
use itertools::Itertools;
|
|
||||||
use regex::Regex;
|
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
|
||||||
|
|
||||||
use super::types::FilePattern;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct HashableRegex(Regex);
|
|
||||||
|
|
||||||
impl Hash for HashableRegex {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.0.as_str().hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Regex> for HashableRegex {
|
|
||||||
fn from(regex: Regex) -> Self {
|
|
||||||
Self(regex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for HashableRegex {
|
|
||||||
type Target = Regex;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct HashableGlobMatcher(GlobMatcher);
|
|
||||||
|
|
||||||
impl From<GlobMatcher> for HashableGlobMatcher {
|
|
||||||
fn from(matcher: GlobMatcher) -> Self {
|
|
||||||
Self(matcher)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for HashableGlobMatcher {
|
|
||||||
type Target = GlobMatcher;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for HashableGlobMatcher {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.0.glob().hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Derivative)]
|
|
||||||
#[derivative(Debug)]
|
|
||||||
pub struct HashableGlobSet {
|
|
||||||
patterns: Vec<FilePattern>,
|
|
||||||
#[derivative(Debug = "ignore")]
|
|
||||||
globset: GlobSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HashableGlobSet {
|
|
||||||
pub fn new(patterns: Vec<FilePattern>) -> anyhow::Result<Self> {
|
|
||||||
let mut builder = globset::GlobSetBuilder::new();
|
|
||||||
for pattern in &patterns {
|
|
||||||
pattern.clone().add_to(&mut builder)?;
|
|
||||||
}
|
|
||||||
let globset = builder.build()?;
|
|
||||||
Ok(HashableGlobSet { patterns, globset })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty() -> Self {
|
|
||||||
Self {
|
|
||||||
patterns: Vec::new(),
|
|
||||||
globset: GlobSet::empty(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for HashableGlobSet {
|
|
||||||
type Target = GlobSet;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.globset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for HashableGlobSet {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
for pattern in self.patterns.iter().sorted() {
|
|
||||||
pattern.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct HashableHashSet<T>(FxHashSet<T>);
|
|
||||||
|
|
||||||
impl<T: Hash + Ord> Hash for HashableHashSet<T> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
for v in self.0.iter().sorted() {
|
|
||||||
v.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for HashableHashSet<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(FxHashSet::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<FxHashSet<T>> for HashableHashSet<T> {
|
|
||||||
fn from(set: FxHashSet<T>) -> Self {
|
|
||||||
Self(set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<HashableHashSet<T>> for FxHashSet<T> {
|
|
||||||
fn from(set: HashableHashSet<T>) -> Self {
|
|
||||||
set.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Deref for HashableHashSet<T> {
|
|
||||||
type Target = FxHashSet<T>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct HashableHashMap<K, V>(FxHashMap<K, V>);
|
|
||||||
|
|
||||||
impl<K: Hash + Ord, V: Hash> Hash for HashableHashMap<K, V> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
for key in self.0.keys().sorted() {
|
|
||||||
key.hash(state);
|
|
||||||
self.0[key].hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> Default for HashableHashMap<K, V> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(FxHashMap::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> From<FxHashMap<K, V>> for HashableHashMap<K, V> {
|
|
||||||
fn from(map: FxHashMap<K, V>) -> Self {
|
|
||||||
Self(map)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> From<HashableHashMap<K, V>> for FxHashMap<K, V> {
|
|
||||||
fn from(map: HashableHashMap<K, V>) -> Self {
|
|
||||||
map.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> Deref for HashableHashMap<K, V> {
|
|
||||||
type Target = FxHashMap<K, V>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V> DerefMut for HashableHashMap<K, V> {
|
|
||||||
fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,13 +5,13 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use globset::Glob;
|
use globset::{Glob, GlobMatcher};
|
||||||
|
use regex::Regex;
|
||||||
|
use ruff_cache::cache_dir;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use self::hashable::{HashableGlobMatcher, HashableGlobSet, HashableHashSet, HashableRegex};
|
|
||||||
use self::rule_table::RuleTable;
|
use self::rule_table::RuleTable;
|
||||||
use crate::cache::cache_dir;
|
|
||||||
use crate::registry::{Rule, RuleNamespace, INCOMPATIBLE_CODES};
|
use crate::registry::{Rule, RuleNamespace, INCOMPATIBLE_CODES};
|
||||||
use crate::rule_selector::{RuleSelector, Specificity};
|
use crate::rule_selector::{RuleSelector, Specificity};
|
||||||
use crate::rules::{
|
use crate::rules::{
|
||||||
|
@ -21,13 +21,13 @@ use crate::rules::{
|
||||||
isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, pyupgrade,
|
isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, pyupgrade,
|
||||||
};
|
};
|
||||||
use crate::settings::configuration::Configuration;
|
use crate::settings::configuration::Configuration;
|
||||||
use crate::settings::types::{PerFileIgnore, PythonVersion, SerializationFormat};
|
use crate::settings::types::{FilePatternSet, PerFileIgnore, PythonVersion, SerializationFormat};
|
||||||
use crate::warn_user_once;
|
use crate::warn_user_once;
|
||||||
|
use ruff_macros::CacheKey;
|
||||||
|
|
||||||
pub mod configuration;
|
pub mod configuration;
|
||||||
pub mod defaults;
|
pub mod defaults;
|
||||||
pub mod flags;
|
pub mod flags;
|
||||||
pub mod hashable;
|
|
||||||
pub mod options;
|
pub mod options;
|
||||||
pub mod options_base;
|
pub mod options_base;
|
||||||
pub mod pyproject;
|
pub mod pyproject;
|
||||||
|
@ -74,31 +74,27 @@ pub struct CliSettings {
|
||||||
pub update_check: bool,
|
pub update_check: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub rules: RuleTable,
|
pub rules: RuleTable,
|
||||||
pub per_file_ignores: Vec<(
|
pub per_file_ignores: Vec<(GlobMatcher, GlobMatcher, FxHashSet<Rule>)>,
|
||||||
HashableGlobMatcher,
|
|
||||||
HashableGlobMatcher,
|
|
||||||
HashableHashSet<Rule>,
|
|
||||||
)>,
|
|
||||||
|
|
||||||
pub show_source: bool,
|
pub show_source: bool,
|
||||||
pub target_version: PythonVersion,
|
pub target_version: PythonVersion,
|
||||||
|
|
||||||
// Resolver settings
|
// Resolver settings
|
||||||
pub exclude: HashableGlobSet,
|
pub exclude: FilePatternSet,
|
||||||
pub extend_exclude: HashableGlobSet,
|
pub extend_exclude: FilePatternSet,
|
||||||
pub force_exclude: bool,
|
pub force_exclude: bool,
|
||||||
pub respect_gitignore: bool,
|
pub respect_gitignore: bool,
|
||||||
pub project_root: PathBuf,
|
pub project_root: PathBuf,
|
||||||
|
|
||||||
// Rule-specific settings
|
// Rule-specific settings
|
||||||
pub allowed_confusables: HashableHashSet<char>,
|
pub allowed_confusables: FxHashSet<char>,
|
||||||
pub builtins: Vec<String>,
|
pub builtins: Vec<String>,
|
||||||
pub dummy_variable_rgx: HashableRegex,
|
pub dummy_variable_rgx: Regex,
|
||||||
pub external: HashableHashSet<String>,
|
pub external: FxHashSet<String>,
|
||||||
pub ignore_init_module_imports: bool,
|
pub ignore_init_module_imports: bool,
|
||||||
pub line_length: usize,
|
pub line_length: usize,
|
||||||
pub namespace_packages: Vec<PathBuf>,
|
pub namespace_packages: Vec<PathBuf>,
|
||||||
|
@ -146,18 +142,16 @@ impl Settings {
|
||||||
allowed_confusables: config
|
allowed_confusables: config
|
||||||
.allowed_confusables
|
.allowed_confusables
|
||||||
.map(FxHashSet::from_iter)
|
.map(FxHashSet::from_iter)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default(),
|
||||||
.into(),
|
|
||||||
builtins: config.builtins.unwrap_or_default(),
|
builtins: config.builtins.unwrap_or_default(),
|
||||||
dummy_variable_rgx: config
|
dummy_variable_rgx: config
|
||||||
.dummy_variable_rgx
|
.dummy_variable_rgx
|
||||||
.unwrap_or_else(|| defaults::DUMMY_VARIABLE_RGX.clone())
|
.unwrap_or_else(|| defaults::DUMMY_VARIABLE_RGX.clone()),
|
||||||
.into(),
|
exclude: FilePatternSet::try_from_vec(
|
||||||
exclude: HashableGlobSet::new(
|
|
||||||
config.exclude.unwrap_or_else(|| defaults::EXCLUDE.clone()),
|
config.exclude.unwrap_or_else(|| defaults::EXCLUDE.clone()),
|
||||||
)?,
|
)?,
|
||||||
extend_exclude: HashableGlobSet::new(config.extend_exclude)?,
|
extend_exclude: FilePatternSet::try_from_vec(config.extend_exclude)?,
|
||||||
external: FxHashSet::from_iter(config.external.unwrap_or_default()).into(),
|
external: FxHashSet::from_iter(config.external.unwrap_or_default()),
|
||||||
|
|
||||||
force_exclude: config.force_exclude.unwrap_or(false),
|
force_exclude: config.force_exclude.unwrap_or(false),
|
||||||
|
|
||||||
|
@ -414,13 +408,7 @@ impl From<&Configuration> for RuleTable {
|
||||||
/// Given a list of patterns, create a `GlobSet`.
|
/// Given a list of patterns, create a `GlobSet`.
|
||||||
pub fn resolve_per_file_ignores(
|
pub fn resolve_per_file_ignores(
|
||||||
per_file_ignores: Vec<PerFileIgnore>,
|
per_file_ignores: Vec<PerFileIgnore>,
|
||||||
) -> Result<
|
) -> Result<Vec<(GlobMatcher, GlobMatcher, FxHashSet<Rule>)>> {
|
||||||
Vec<(
|
|
||||||
HashableGlobMatcher,
|
|
||||||
HashableGlobMatcher,
|
|
||||||
HashableHashSet<Rule>,
|
|
||||||
)>,
|
|
||||||
> {
|
|
||||||
per_file_ignores
|
per_file_ignores
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|per_file_ignore| {
|
.map(|per_file_ignore| {
|
||||||
|
@ -431,7 +419,7 @@ pub fn resolve_per_file_ignores(
|
||||||
// Construct basename matcher.
|
// Construct basename matcher.
|
||||||
let basename = Glob::new(&per_file_ignore.basename)?.compile_matcher();
|
let basename = Glob::new(&per_file_ignore.basename)?.compile_matcher();
|
||||||
|
|
||||||
Ok((absolute.into(), basename.into(), per_file_ignore.rules))
|
Ok((absolute, basename, per_file_ignore.rules))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
use std::collections::hash_map;
|
use std::collections::{hash_map, HashMap};
|
||||||
|
|
||||||
|
use ruff_macros::CacheKey;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use super::hashable::HashableHashMap;
|
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
|
|
||||||
/// A table to keep track of which rules are enabled
|
/// A table to keep track of which rules are enabled
|
||||||
/// and Whether they should be autofixed.
|
/// and Whether they should be autofixed.
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, CacheKey)]
|
||||||
pub struct RuleTable {
|
pub struct RuleTable {
|
||||||
/// Maps rule codes to a boolean indicating if the rule should be autofixed.
|
/// Maps rule codes to a boolean indicating if the rule should be autofixed.
|
||||||
enabled: HashableHashMap<Rule, bool>,
|
enabled: FxHashMap<Rule, bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RuleTable {
|
impl RuleTable {
|
||||||
/// Creates a new empty rule table.
|
/// Creates a new empty rule table.
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
enabled: HashableHashMap::default(),
|
enabled: HashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,8 +53,6 @@ impl<I: IntoIterator<Item = Rule>> From<I> for RuleTable {
|
||||||
for code in codes {
|
for code in codes {
|
||||||
enabled.insert(code, true);
|
enabled.insert(code, true);
|
||||||
}
|
}
|
||||||
Self {
|
Self { enabled }
|
||||||
enabled: enabled.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
use std::hash::Hash;
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
use globset::{Glob, GlobSetBuilder};
|
use globset::{Glob, GlobSet, GlobSetBuilder};
|
||||||
|
use ruff_cache::{CacheKey, CacheKeyHasher};
|
||||||
|
use ruff_macros::CacheKey;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize};
|
use serde::{de, Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
use super::hashable::HashableHashSet;
|
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
use crate::rule_selector::RuleSelector;
|
use crate::rule_selector::RuleSelector;
|
||||||
use crate::{fs, warn_user_once};
|
use crate::{fs, warn_user_once};
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema,
|
Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, JsonSchema, CacheKey,
|
||||||
)]
|
)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum PythonVersion {
|
pub enum PythonVersion {
|
||||||
|
@ -61,7 +62,7 @@ impl PythonVersion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
|
#[derive(Debug, Clone, CacheKey, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
pub enum FilePattern {
|
pub enum FilePattern {
|
||||||
Builtin(&'static str),
|
Builtin(&'static str),
|
||||||
User(String, PathBuf),
|
User(String, PathBuf),
|
||||||
|
@ -97,11 +98,51 @@ impl FromStr for FilePattern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct FilePatternSet {
|
||||||
|
set: GlobSet,
|
||||||
|
cache_key: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilePatternSet {
|
||||||
|
pub fn try_from_vec(patterns: Vec<FilePattern>) -> Result<Self, anyhow::Error> {
|
||||||
|
let mut builder = GlobSetBuilder::new();
|
||||||
|
let mut hasher = CacheKeyHasher::new();
|
||||||
|
|
||||||
|
for pattern in patterns {
|
||||||
|
pattern.cache_key(&mut hasher);
|
||||||
|
pattern.add_to(&mut builder)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let set = builder.build()?;
|
||||||
|
|
||||||
|
Ok(FilePatternSet {
|
||||||
|
set,
|
||||||
|
cache_key: hasher.finish(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for FilePatternSet {
|
||||||
|
type Target = GlobSet;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for FilePatternSet {
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_usize(self.set.len());
|
||||||
|
state.write_u64(self.cache_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PerFileIgnore {
|
pub struct PerFileIgnore {
|
||||||
pub(crate) basename: String,
|
pub(crate) basename: String,
|
||||||
pub(crate) absolute: PathBuf,
|
pub(crate) absolute: PathBuf,
|
||||||
pub(crate) rules: HashableHashSet<Rule>,
|
pub(crate) rules: FxHashSet<Rule>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PerFileIgnore {
|
impl PerFileIgnore {
|
||||||
|
@ -116,7 +157,7 @@ impl PerFileIgnore {
|
||||||
Self {
|
Self {
|
||||||
basename: pattern,
|
basename: pattern,
|
||||||
absolute,
|
absolute,
|
||||||
rules: rules.into(),
|
rules,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
crates/ruff_cache/Cargo.toml
Normal file
15
crates/ruff_cache/Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "ruff_cache"
|
||||||
|
version = "0.0.0"
|
||||||
|
publish = false
|
||||||
|
edition = { workspace = true }
|
||||||
|
rust-version = { workspace = true }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
itertools = { workspace = true }
|
||||||
|
globset = { version = "0.4.9" }
|
||||||
|
regex = { workspace = true }
|
||||||
|
filetime = { version = "0.2.17" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
ruff_macros = { path = "../ruff_macros" }
|
376
crates/ruff_cache/src/cache_key.rs
Normal file
376
crates/ruff_cache/src/cache_key.rs
Normal file
|
@ -0,0 +1,376 @@
|
||||||
|
use itertools::Itertools;
|
||||||
|
use regex::Regex;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct CacheKeyHasher {
|
||||||
|
inner: DefaultHasher,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKeyHasher {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: DefaultHasher::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for CacheKeyHasher {
|
||||||
|
type Target = DefaultHasher;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for CacheKeyHasher {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type that be used as part of a cache key.
|
||||||
|
///
|
||||||
|
/// A cache looks up artefacts by a cache key. Many cache keys are composed of sub-keys. For example,
|
||||||
|
/// caching the lint results of a file depend at least on the file content, the user settings, and linter version.
|
||||||
|
/// Types implementing the [`CacheKey`] trait can be used as part of a cache key by which artefacts are queried.
|
||||||
|
///
|
||||||
|
/// ## Implementing `CacheKey`
|
||||||
|
///
|
||||||
|
/// You can derive [`CacheKey`] with `#[derive(CacheKey)]` if all fields implement [`CacheKey`]. The resulting
|
||||||
|
/// cache key will be the combination of the values from calling `cache_key` on each field.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use ruff_macros::CacheKey;
|
||||||
|
///
|
||||||
|
/// #[derive(CacheKey)]
|
||||||
|
/// struct Test {
|
||||||
|
/// name: String,
|
||||||
|
/// version: u32,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// If you need more control over computing the cache key, you can of course implement the [`CacheKey]` yourself:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ruff_cache::{CacheKey, CacheKeyHasher};
|
||||||
|
///
|
||||||
|
/// struct Test {
|
||||||
|
/// name: String,
|
||||||
|
/// version: u32,
|
||||||
|
/// other: String
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl CacheKey for Test {
|
||||||
|
/// fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
/// self.name.cache_key(state);
|
||||||
|
/// self.version.cache_key(state);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Portability
|
||||||
|
///
|
||||||
|
/// Ideally, the cache key is portable across platforms but this is not yet a strict requirement.
|
||||||
|
///
|
||||||
|
/// ## Using [`Hash`]
|
||||||
|
///
|
||||||
|
/// You can defer to the [`Hash`] implementation for non-composite types.
|
||||||
|
/// Be aware, that the [`Hash`] implementation may not be portable.
|
||||||
|
///
|
||||||
|
/// ## Why a new trait rather than reusing [`Hash`]?
|
||||||
|
/// The main reason is that hashes and cache keys have different constraints:
|
||||||
|
///
|
||||||
|
/// * Cache keys are less performance sensitive: Hashes must be super fast to compute for performant hashed-collections. That's
|
||||||
|
/// why some standard types don't implement [`Hash`] where it would be safe to to implement [`CacheKey`], e.g. `HashSet`
|
||||||
|
/// * Cache keys must be deterministic where hash keys do not have this constraint. That's why pointers don't implement [`CacheKey`] but they implement [`Hash`].
|
||||||
|
/// * Ideally, cache keys are portable
|
||||||
|
///
|
||||||
|
/// [`Hash`](std::hash::Hash)
|
||||||
|
pub trait CacheKey {
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher);
|
||||||
|
|
||||||
|
fn cache_key_slice(data: &[Self], state: &mut CacheKeyHasher)
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
for piece in data {
|
||||||
|
piece.cache_key(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for bool {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_u8(u8::from(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for char {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_u32(*self as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for usize {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_usize(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for u128 {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_u128(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for u64 {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_u64(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for u32 {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_u32(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for u16 {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_u16(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for u8 {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_u8(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for isize {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_isize(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for i128 {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_i128(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for i64 {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_i64(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for i32 {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_i32(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for i16 {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_i16(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for i8 {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_i8(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_cache_key_tuple {
|
||||||
|
() => (
|
||||||
|
impl CacheKey for () {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, _state: &mut CacheKeyHasher) {}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
( $($name:ident)+) => (
|
||||||
|
impl<$($name: CacheKey),+> CacheKey for ($($name,)+) where last_type!($($name,)+): ?Sized {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
let ($(ref $name,)+) = *self;
|
||||||
|
$($name.cache_key(state);)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! last_type {
|
||||||
|
($a:ident,) => { $a };
|
||||||
|
($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) };
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_cache_key_tuple! {}
|
||||||
|
impl_cache_key_tuple! { T }
|
||||||
|
impl_cache_key_tuple! { T B }
|
||||||
|
impl_cache_key_tuple! { T B C }
|
||||||
|
impl_cache_key_tuple! { T B C D }
|
||||||
|
impl_cache_key_tuple! { T B C D E }
|
||||||
|
impl_cache_key_tuple! { T B C D E F }
|
||||||
|
impl_cache_key_tuple! { T B C D E F G }
|
||||||
|
impl_cache_key_tuple! { T B C D E F G H }
|
||||||
|
impl_cache_key_tuple! { T B C D E F G H I }
|
||||||
|
impl_cache_key_tuple! { T B C D E F G H I J }
|
||||||
|
impl_cache_key_tuple! { T B C D E F G H I J K }
|
||||||
|
impl_cache_key_tuple! { T B C D E F G H I J K L }
|
||||||
|
|
||||||
|
impl CacheKey for str {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
self.hash(&mut **state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for String {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
self.hash(&mut **state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: CacheKey> CacheKey for Option<T> {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
match self {
|
||||||
|
None => state.write_usize(0),
|
||||||
|
Some(value) => {
|
||||||
|
state.write_usize(1);
|
||||||
|
value.cache_key(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: CacheKey> CacheKey for [T] {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_usize(self.len());
|
||||||
|
CacheKey::cache_key_slice(self, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + CacheKey> CacheKey for &T {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
(**self).cache_key(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + CacheKey> CacheKey for &mut T {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
(**self).cache_key(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> CacheKey for Vec<T>
|
||||||
|
where
|
||||||
|
T: CacheKey,
|
||||||
|
{
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_usize(self.len());
|
||||||
|
CacheKey::cache_key_slice(self, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V, S> CacheKey for HashMap<K, V, S>
|
||||||
|
where
|
||||||
|
K: CacheKey + Ord,
|
||||||
|
V: CacheKey,
|
||||||
|
{
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_usize(self.len());
|
||||||
|
for (key, value) in self
|
||||||
|
.iter()
|
||||||
|
.sorted_by(|(left, _), (right, _)| left.cmp(right))
|
||||||
|
{
|
||||||
|
key.cache_key(state);
|
||||||
|
value.cache_key(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: CacheKey + Ord, S> CacheKey for HashSet<V, S> {
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_usize(self.len());
|
||||||
|
for value in self.iter().sorted() {
|
||||||
|
value.cache_key(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: CacheKey> CacheKey for BTreeSet<V> {
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_usize(self.len());
|
||||||
|
for item in self {
|
||||||
|
item.cache_key(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: CacheKey + Ord, V: CacheKey> CacheKey for BTreeMap<K, V> {
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
state.write_usize(self.len());
|
||||||
|
|
||||||
|
for (key, value) in self {
|
||||||
|
key.cache_key(state);
|
||||||
|
value.cache_key(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for Path {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
self.hash(&mut **state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for PathBuf {
|
||||||
|
#[inline]
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
self.hash(&mut **state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: ?Sized> CacheKey for Cow<'_, V>
|
||||||
|
where
|
||||||
|
V: CacheKey + ToOwned,
|
||||||
|
{
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
(**self).cache_key(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for Regex {
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
self.as_str().cache_key(state);
|
||||||
|
}
|
||||||
|
}
|
9
crates/ruff_cache/src/filetime.rs
Normal file
9
crates/ruff_cache/src/filetime.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use crate::{CacheKey, CacheKeyHasher};
|
||||||
|
use filetime::FileTime;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
impl CacheKey for FileTime {
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
self.hash(&mut **state);
|
||||||
|
}
|
||||||
|
}
|
14
crates/ruff_cache/src/globset.rs
Normal file
14
crates/ruff_cache/src/globset.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use crate::{CacheKey, CacheKeyHasher};
|
||||||
|
use globset::{Glob, GlobMatcher};
|
||||||
|
|
||||||
|
impl CacheKey for GlobMatcher {
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
self.glob().cache_key(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CacheKey for Glob {
|
||||||
|
fn cache_key(&self, state: &mut CacheKeyHasher) {
|
||||||
|
self.glob().cache_key(state);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,9 @@
|
||||||
|
mod cache_key;
|
||||||
|
pub mod filetime;
|
||||||
|
pub mod globset;
|
||||||
|
|
||||||
|
pub use cache_key::{CacheKey, CacheKeyHasher};
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub const CACHE_DIR_NAME: &str = ".ruff_cache";
|
pub const CACHE_DIR_NAME: &str = ".ruff_cache";
|
108
crates/ruff_cache/tests/cache_key.rs
Normal file
108
crates/ruff_cache/tests/cache_key.rs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
use ruff_cache::{CacheKey, CacheKeyHasher};
|
||||||
|
use ruff_macros::CacheKey;
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
#[derive(CacheKey, Hash)]
|
||||||
|
struct UnitStruct;
|
||||||
|
|
||||||
|
#[derive(CacheKey, Hash)]
|
||||||
|
struct NamedFieldsStruct {
|
||||||
|
a: String,
|
||||||
|
b: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(CacheKey, Hash)]
|
||||||
|
struct UnnamedFieldsStruct(String, String);
|
||||||
|
|
||||||
|
#[derive(CacheKey, Hash)]
|
||||||
|
enum Enum {
|
||||||
|
Unit,
|
||||||
|
UnnamedFields(String, String),
|
||||||
|
NamedFields { a: String, b: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unit_struct_cache_key() {
|
||||||
|
let mut key = CacheKeyHasher::new();
|
||||||
|
|
||||||
|
UnitStruct.cache_key(&mut key);
|
||||||
|
|
||||||
|
let mut hash = DefaultHasher::new();
|
||||||
|
UnitStruct.hash(&mut hash);
|
||||||
|
|
||||||
|
assert_eq!(hash.finish(), key.finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn named_field_struct() {
|
||||||
|
let mut key = CacheKeyHasher::new();
|
||||||
|
|
||||||
|
let named_fields = NamedFieldsStruct {
|
||||||
|
a: "Hello".into(),
|
||||||
|
b: "World".into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
named_fields.cache_key(&mut key);
|
||||||
|
|
||||||
|
let mut hash = DefaultHasher::new();
|
||||||
|
named_fields.hash(&mut hash);
|
||||||
|
|
||||||
|
assert_eq!(hash.finish(), key.finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unnamed_field_struct() {
|
||||||
|
let mut key = CacheKeyHasher::new();
|
||||||
|
|
||||||
|
let unnamed_fields = UnnamedFieldsStruct("Hello".into(), "World".into());
|
||||||
|
|
||||||
|
unnamed_fields.cache_key(&mut key);
|
||||||
|
|
||||||
|
let mut hash = DefaultHasher::new();
|
||||||
|
unnamed_fields.hash(&mut hash);
|
||||||
|
|
||||||
|
assert_eq!(hash.finish(), key.finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enum_unit_variant() {
|
||||||
|
let mut key = CacheKeyHasher::new();
|
||||||
|
|
||||||
|
let variant = Enum::Unit;
|
||||||
|
variant.cache_key(&mut key);
|
||||||
|
|
||||||
|
let mut hash = DefaultHasher::new();
|
||||||
|
variant.hash(&mut hash);
|
||||||
|
|
||||||
|
assert_eq!(hash.finish(), key.finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enum_named_fields_variant() {
|
||||||
|
let mut key = CacheKeyHasher::new();
|
||||||
|
|
||||||
|
let variant = Enum::NamedFields {
|
||||||
|
a: "Hello".to_string(),
|
||||||
|
b: "World".to_string(),
|
||||||
|
};
|
||||||
|
variant.cache_key(&mut key);
|
||||||
|
|
||||||
|
let mut hash = DefaultHasher::new();
|
||||||
|
variant.hash(&mut hash);
|
||||||
|
|
||||||
|
assert_eq!(hash.finish(), key.finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enum_unnamed_fields_variant() {
|
||||||
|
let mut key = CacheKeyHasher::new();
|
||||||
|
|
||||||
|
let variant = Enum::UnnamedFields("Hello".to_string(), "World".to_string());
|
||||||
|
variant.cache_key(&mut key);
|
||||||
|
|
||||||
|
let mut hash = DefaultHasher::new();
|
||||||
|
variant.hash(&mut hash);
|
||||||
|
|
||||||
|
assert_eq!(hash.finish(), key.finish());
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ doc = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ruff = { path = "../ruff" }
|
ruff = { path = "../ruff" }
|
||||||
|
ruff_cache = { path = "../ruff_cache" }
|
||||||
|
|
||||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::collections::hash_map::DefaultHasher;
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::Hasher;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
@ -10,6 +9,7 @@ use log::error;
|
||||||
use path_absolutize::Absolutize;
|
use path_absolutize::Absolutize;
|
||||||
use ruff::message::Message;
|
use ruff::message::Message;
|
||||||
use ruff::settings::{flags, AllSettings, Settings};
|
use ruff::settings::{flags, AllSettings, Settings};
|
||||||
|
use ruff_cache::{CacheKey, CacheKeyHasher};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
@ -37,18 +37,18 @@ fn cache_key<P: AsRef<Path>>(
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
autofix: flags::Autofix,
|
autofix: flags::Autofix,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut hasher = CacheKeyHasher::new();
|
||||||
CARGO_PKG_VERSION.hash(&mut hasher);
|
CARGO_PKG_VERSION.cache_key(&mut hasher);
|
||||||
path.as_ref().absolutize().unwrap().hash(&mut hasher);
|
path.as_ref().absolutize().unwrap().cache_key(&mut hasher);
|
||||||
package
|
package
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|path| path.as_ref().absolutize().unwrap())
|
.map(|path| path.as_ref().absolutize().unwrap())
|
||||||
.hash(&mut hasher);
|
.cache_key(&mut hasher);
|
||||||
FileTime::from_last_modification_time(metadata).hash(&mut hasher);
|
FileTime::from_last_modification_time(metadata).cache_key(&mut hasher);
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
metadata.permissions().mode().hash(&mut hasher);
|
metadata.permissions().mode().cache_key(&mut hasher);
|
||||||
settings.hash(&mut hasher);
|
settings.cache_key(&mut hasher);
|
||||||
autofix.hash(&mut hasher);
|
autofix.cache_key(&mut hasher);
|
||||||
hasher.finish()
|
hasher.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,9 @@ use colored::Colorize;
|
||||||
use path_absolutize::path_dedot;
|
use path_absolutize::path_dedot;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use ruff::cache::CACHE_DIR_NAME;
|
|
||||||
use ruff::fs;
|
use ruff::fs;
|
||||||
use ruff::logging::LogLevel;
|
use ruff::logging::LogLevel;
|
||||||
|
use ruff_cache::CACHE_DIR_NAME;
|
||||||
|
|
||||||
/// Clear any caches in the current directory or any subdirectories.
|
/// Clear any caches in the current directory or any subdirectories.
|
||||||
pub fn clean(level: LogLevel) -> Result<()> {
|
pub fn clean(level: LogLevel) -> Result<()> {
|
||||||
|
|
|
@ -15,3 +15,4 @@ quote = { version = "1.0.21" }
|
||||||
syn = { version = "1.0.103", features = ["derive", "parsing", "extra-traits"] }
|
syn = { version = "1.0.103", features = ["derive", "parsing", "extra-traits"] }
|
||||||
textwrap = { version = "0.16.0" }
|
textwrap = { version = "0.16.0" }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
|
|
||||||
|
|
103
crates/ruff_macros/src/cache_key.rs
Normal file
103
crates/ruff_macros/src/cache_key.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
use syn::spanned::Spanned;
|
||||||
|
use syn::{Data, DeriveInput, Error, Fields};
|
||||||
|
|
||||||
|
pub fn derive_cache_key(item: &DeriveInput) -> syn::Result<TokenStream> {
|
||||||
|
let fields = match &item.data {
|
||||||
|
Data::Enum(item_enum) => {
|
||||||
|
let arms = item_enum.variants.iter().enumerate().map(|(i, variant)| {
|
||||||
|
let variant_name = &variant.ident;
|
||||||
|
|
||||||
|
match &variant.fields {
|
||||||
|
Fields::Named(fields) => {
|
||||||
|
let field_names: Vec<_> = fields
|
||||||
|
.named
|
||||||
|
.iter()
|
||||||
|
.map(|field| field.ident.clone().unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let fields_code = field_names
|
||||||
|
.iter()
|
||||||
|
.map(|field| quote!(#field.cache_key(key);));
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
Self::#variant_name{#(#field_names),*} => {
|
||||||
|
key.write_usize(#i);
|
||||||
|
#(#fields_code)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Fields::Unnamed(fields) => {
|
||||||
|
let field_names: Vec<_> = fields
|
||||||
|
.unnamed
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, _)| format_ident!("field_{i}"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let fields_code = field_names
|
||||||
|
.iter()
|
||||||
|
.map(|field| quote!(#field.cache_key(key);));
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
Self::#variant_name(#(#field_names),*) => {
|
||||||
|
key.write_usize(#i);
|
||||||
|
#(#fields_code)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Fields::Unit => {
|
||||||
|
quote! {
|
||||||
|
Self::#variant_name => {
|
||||||
|
key.write_usize(#i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
match self {
|
||||||
|
#(#arms)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::Struct(item_struct) => {
|
||||||
|
let fields = item_struct.fields.iter().enumerate().map(|(i, field)| {
|
||||||
|
let field_attr = match &field.ident {
|
||||||
|
Some(ident) => quote!(self.#ident),
|
||||||
|
None => {
|
||||||
|
let index = syn::Index::from(i);
|
||||||
|
quote!(self.#index)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote!(#field_attr.cache_key(key);)
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {#(#fields)*}
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::Union(_) => {
|
||||||
|
return Err(Error::new(
|
||||||
|
item.span(),
|
||||||
|
"CacheKey does not support unions. Only structs and enums are supported",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = &item.ident;
|
||||||
|
let (impl_generics, ty_generics, where_clause) = &item.generics.split_for_impl();
|
||||||
|
|
||||||
|
Ok(quote!(
|
||||||
|
impl #impl_generics ruff_cache::CacheKey for #name #ty_generics #where_clause {
|
||||||
|
fn cache_key(&self, key: &mut ruff_cache::CacheKeyHasher) {
|
||||||
|
use std::hash::Hasher;
|
||||||
|
use ruff_cache::CacheKey;
|
||||||
|
#fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
//! This crate implements internal macros for the `ruff` library.
|
//! This crate implements internal macros for the `ruff` library.
|
||||||
|
|
||||||
|
use crate::cache_key::derive_cache_key;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use syn::{parse_macro_input, DeriveInput, ItemFn};
|
use syn::{parse_macro_input, DeriveInput, ItemFn};
|
||||||
|
|
||||||
|
mod cache_key;
|
||||||
mod config;
|
mod config;
|
||||||
mod define_violation;
|
mod define_violation;
|
||||||
mod derive_message_formats;
|
mod derive_message_formats;
|
||||||
|
@ -20,6 +22,16 @@ pub fn derive_config(input: proc_macro::TokenStream) -> proc_macro::TokenStream
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(CacheKey)]
|
||||||
|
pub fn cache_key(input: TokenStream) -> TokenStream {
|
||||||
|
let item = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
let result = derive_cache_key(&item);
|
||||||
|
let stream = result.unwrap_or_else(|err| err.to_compile_error());
|
||||||
|
|
||||||
|
TokenStream::from(stream)
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn register_rules(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn register_rules(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let mapping = parse_macro_input!(item as register_rules::Input);
|
let mapping = parse_macro_input!(item as register_rules::Input);
|
||||||
|
|
|
@ -56,6 +56,7 @@ pub fn register_rules(input: &Input) -> proc_macro2::TokenStream {
|
||||||
Hash,
|
Hash,
|
||||||
PartialOrd,
|
PartialOrd,
|
||||||
Ord,
|
Ord,
|
||||||
|
::ruff_macros::CacheKey,
|
||||||
AsRefStr,
|
AsRefStr,
|
||||||
::strum_macros::IntoStaticStr,
|
::strum_macros::IntoStaticStr,
|
||||||
)]
|
)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue