mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 22:54:42 +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",
|
||||
"regex",
|
||||
"result-like",
|
||||
"ruff_cache",
|
||||
"ruff_macros",
|
||||
"ruff_python",
|
||||
"ruff_rustpython",
|
||||
|
@ -2005,6 +2006,17 @@ dependencies = [
|
|||
"wasm-bindgen-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cache"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"globset",
|
||||
"itertools",
|
||||
"regex",
|
||||
"ruff_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.253"
|
||||
|
@ -2033,6 +2045,7 @@ dependencies = [
|
|||
"rayon",
|
||||
"regex",
|
||||
"ruff",
|
||||
"ruff_cache",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
|
@ -19,6 +19,7 @@ doctest = false
|
|||
ruff_macros = { path = "../ruff_macros" }
|
||||
ruff_python = { path = "../ruff_python" }
|
||||
ruff_rustpython = { path = "../ruff_rustpython" }
|
||||
ruff_cache = { path = "../ruff_cache" }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
bisection = { version = "0.1.0" }
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use globset::GlobMatcher;
|
||||
use log::debug;
|
||||
use path_absolutize::{path_dedot, Absolutize};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::hashable::{HashableGlobMatcher, HashableHashSet};
|
||||
|
||||
/// Extract the absolute path and basename (as strings) from a Path.
|
||||
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.
|
||||
pub(crate) fn ignores_from_path<'a>(
|
||||
path: &Path,
|
||||
pattern_code_pairs: &'a [(
|
||||
HashableGlobMatcher,
|
||||
HashableGlobMatcher,
|
||||
HashableHashSet<Rule>,
|
||||
)],
|
||||
pattern_code_pairs: &'a [(GlobMatcher, GlobMatcher, FxHashSet<Rule>)],
|
||||
) -> FxHashSet<&'a Rule> {
|
||||
let (file_path, file_basename) = extract_path_names(path).expect("Unable to parse filename");
|
||||
pattern_code_pairs
|
||||
|
@ -39,8 +34,8 @@ pub(crate) fn ignores_from_path<'a>(
|
|||
debug!(
|
||||
"Adding per-file ignores for {:?} due to basename match on {:?}: {:?}",
|
||||
path,
|
||||
basename.deref().glob().regex(),
|
||||
&**codes
|
||||
basename.glob().regex(),
|
||||
codes
|
||||
);
|
||||
return Some(codes.iter());
|
||||
}
|
||||
|
@ -48,8 +43,8 @@ pub(crate) fn ignores_from_path<'a>(
|
|||
debug!(
|
||||
"Adding per-file ignores for {:?} due to absolute match on {:?}: {:?}",
|
||||
path,
|
||||
absolute.deref().glob().regex(),
|
||||
&**codes
|
||||
absolute.glob().regex(),
|
||||
codes
|
||||
);
|
||||
return Some(codes.iter());
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ pub use violation::{AutofixKind, Availability as AutofixAvailability};
|
|||
|
||||
mod ast;
|
||||
mod autofix;
|
||||
pub mod cache;
|
||||
mod checkers;
|
||||
mod codes;
|
||||
mod cst;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Settings for the `flake-annotations` plugin.
|
||||
|
||||
use ruff_macros::CacheKey;
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -60,7 +61,7 @@ pub struct Options {
|
|||
pub ignore_fully_untyped: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Hash)]
|
||||
#[derive(Debug, Default, CacheKey)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Settings {
|
||||
pub mypy_init_return: bool,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `flake8-bandit` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -45,7 +45,7 @@ pub struct Options {
|
|||
pub check_typed_exception: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub hardcoded_tmp_directory: Vec<String>,
|
||||
pub check_typed_exception: bool,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `flake8-bugbear` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -26,7 +26,7 @@ pub struct Options {
|
|||
pub extend_immutable_calls: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Hash)]
|
||||
#[derive(Debug, Default, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub extend_immutable_calls: Vec<String>,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `flake8-builtins` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -22,7 +22,7 @@ pub struct Options {
|
|||
pub builtins_ignorelist: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Hash)]
|
||||
#[derive(Debug, Default, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub builtins_ignorelist: Vec<String>,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `flake8-comprehensions` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -22,7 +22,7 @@ pub struct Options {
|
|||
pub allow_dict_calls_with_keyword_arguments: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Hash)]
|
||||
#[derive(Debug, Default, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub allow_dict_calls_with_keyword_arguments: bool,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `flake8-errmsg` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -18,7 +18,7 @@ pub struct Options {
|
|||
pub max_string_length: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Hash)]
|
||||
#[derive(Debug, Default, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub max_string_length: usize,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `flake8-implicit-str-concat` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -32,7 +32,7 @@ pub struct Options {
|
|||
pub allow_multiline: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub allow_multiline: bool,
|
||||
}
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
//! Settings for import conventions.
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use rustc_hash::FxHashMap;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::settings::hashable::HashableHashMap;
|
||||
|
||||
const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[
|
||||
("altair", "alt"),
|
||||
("matplotlib", "mpl"),
|
||||
|
@ -64,9 +60,9 @@ pub struct Options {
|
|||
pub extend_aliases: Option<FxHashMap<String, String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub aliases: HashableHashMap<String, String>,
|
||||
pub 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 {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
aliases: default_aliases().into(),
|
||||
aliases: default_aliases(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +94,7 @@ impl Default for Settings {
|
|||
impl From<Options> for Settings {
|
||||
fn from(options: Options) -> 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 {
|
||||
fn from(settings: Settings) -> Self {
|
||||
Self {
|
||||
aliases: Some(settings.aliases.into()),
|
||||
aliases: Some(settings.aliases),
|
||||
extend_aliases: None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `flake8-pytest-style` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -113,7 +113,7 @@ pub struct Options {
|
|||
pub mark_parentheses: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub fixture_parentheses: bool,
|
||||
pub parametrize_names_type: types::ParametrizeNameType,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use ruff_macros::CacheKey;
|
||||
use schemars::JsonSchema;
|
||||
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 {
|
||||
#[serde(rename = "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 {
|
||||
#[serde(rename = "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 {
|
||||
#[serde(rename = "tuple")]
|
||||
Tuple,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
//! Settings for the `flake8-quotes` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
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")]
|
||||
pub enum Quote {
|
||||
/// Use single quotes.
|
||||
|
@ -71,7 +71,7 @@ pub struct Options {
|
|||
pub avoid_escape: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub inline_quotes: Quote,
|
||||
pub multiline_quotes: Quote,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `flake8-self` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -28,7 +28,7 @@ pub struct Options {
|
|||
pub ignore_names: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct Settings {
|
||||
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 rustpython_parser::ast::{Alias, Expr, Located};
|
||||
use schemars::JsonSchema;
|
||||
|
@ -7,12 +7,11 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::ast::types::{CallPath, Range};
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::settings::hashable::HashableHashMap;
|
||||
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")]
|
||||
pub struct ApiBan {
|
||||
/// The message to display when the API is used.
|
||||
|
@ -147,8 +146,7 @@ mod tests {
|
|||
msg: "Use typing_extensions.TypedDict instead.".to_string(),
|
||||
},
|
||||
),
|
||||
])
|
||||
.into(),
|
||||
]),
|
||||
..Default::default()
|
||||
},
|
||||
..Settings::for_rules(vec![Rule::BannedApi])
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
//! Rules from [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/).
|
||||
use ruff_macros::CacheKey;
|
||||
|
||||
pub mod options;
|
||||
|
||||
pub mod banned_api;
|
||||
pub mod relative_imports;
|
||||
|
||||
#[derive(Debug, Hash, Default)]
|
||||
#[derive(Debug, CacheKey, Default)]
|
||||
pub struct Settings {
|
||||
pub ban_relative_imports: relative_imports::Settings,
|
||||
pub banned_api: banned_api::Settings,
|
||||
|
|
|
@ -48,7 +48,7 @@ impl From<Options> for Settings {
|
|||
fn from(options: Options) -> Self {
|
||||
Self {
|
||||
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 {
|
||||
Self {
|
||||
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 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 crate::ast::helpers::{create_stmt, from_relative_import, unparse_stmt};
|
||||
|
@ -15,7 +15,7 @@ use crate::violation::{AutofixKind, Availability, Violation};
|
|||
|
||||
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")]
|
||||
pub enum Strictness {
|
||||
/// Ban imports that extend into the parent module or beyond.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `flake8-type-checking` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -36,7 +36,7 @@ pub struct Options {
|
|||
pub exempt_modules: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub strict: bool,
|
||||
pub exempt_modules: Vec<String>,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `flake8-unused-arguments` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -22,7 +22,7 @@ pub struct Options {
|
|||
pub ignore_variadic_names: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Hash)]
|
||||
#[derive(Debug, Default, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub ignore_variadic_names: bool,
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::fs;
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use log::debug;
|
||||
use ruff_macros::CacheKey;
|
||||
use ruff_python::sys::KNOWN_STANDARD_LIBRARY;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -12,7 +13,17 @@ use super::types::{ImportBlock, Importable};
|
|||
use crate::settings::types::PythonVersion;
|
||||
|
||||
#[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")]
|
||||
pub enum ImportType {
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
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")]
|
||||
pub enum RelativeImportsOrder {
|
||||
/// Place "closer" imports (fewer `.` characters, most local) before
|
||||
|
@ -265,7 +265,7 @@ pub struct Options {
|
|||
pub forced_separate: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Settings {
|
||||
pub required_imports: BTreeSet<String>,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `mccabe` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -25,7 +25,7 @@ pub struct Options {
|
|||
pub max_complexity: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub max_complexity: usize,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `pep8-naming` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -69,7 +69,7 @@ pub struct Options {
|
|||
pub staticmethod_decorators: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub ignore_names: Vec<String>,
|
||||
pub classmethod_decorators: Vec<String>,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `pycodestyle` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -32,7 +32,7 @@ pub struct Options {
|
|||
pub ignore_overlong_task_comments: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Hash)]
|
||||
#[derive(Debug, Default, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub max_doc_length: Option<usize>,
|
||||
pub ignore_overlong_task_comments: bool,
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
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")]
|
||||
pub enum Convention {
|
||||
/// Use Google-style docstrings.
|
||||
|
@ -112,7 +112,7 @@ pub struct Options {
|
|||
pub property_decorators: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Hash)]
|
||||
#[derive(Debug, Default, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub convention: Option<Convention>,
|
||||
pub ignore_decorators: BTreeSet<String>,
|
||||
|
|
|
@ -129,7 +129,7 @@ mod tests {
|
|||
let diagnostics = test_path(
|
||||
Path::new("pyflakes/F841_0.py"),
|
||||
&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)
|
||||
},
|
||||
)?;
|
||||
|
|
|
@ -99,7 +99,7 @@ mod tests {
|
|||
let diagnostics = test_path(
|
||||
Path::new("pylint/too_many_arguments_params.py"),
|
||||
&Settings {
|
||||
dummy_variable_rgx: Regex::new(r"skip_.*").unwrap().into(),
|
||||
dummy_variable_rgx: Regex::new(r"skip_.*").unwrap(),
|
||||
..Settings::for_rules(vec![Rule::TooManyArguments])
|
||||
},
|
||||
)?;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use regex::Regex;
|
||||
use std::{fmt, iter};
|
||||
|
||||
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::checkers::ast::Checker;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::settings::hashable::HashableRegex;
|
||||
use crate::violation::Violation;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
|
||||
|
@ -142,7 +142,7 @@ struct ExprWithInnerBindingKind<'a> {
|
|||
}
|
||||
|
||||
struct InnerForWithAssignTargetsVisitor<'a> {
|
||||
dummy_variable_rgx: &'a HashableRegex,
|
||||
dummy_variable_rgx: &'a Regex,
|
||||
assignment_targets: Vec<ExprWithInnerBindingKind<'a>>,
|
||||
}
|
||||
|
||||
|
@ -213,7 +213,7 @@ where
|
|||
|
||||
fn assignment_targets_from_expr<'a, U>(
|
||||
expr: &'a Expr<U>,
|
||||
dummy_variable_rgx: &'a HashableRegex,
|
||||
dummy_variable_rgx: &'a Regex,
|
||||
) -> 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
|
||||
// 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>(
|
||||
items: &'a [Withitem<U>],
|
||||
dummy_variable_rgx: &'a HashableRegex,
|
||||
dummy_variable_rgx: &'a Regex,
|
||||
) -> impl Iterator<Item = &'a Expr<U>> + 'a {
|
||||
items
|
||||
.iter()
|
||||
|
@ -280,7 +280,7 @@ fn assignment_targets_from_with_items<'a, U>(
|
|||
|
||||
fn assignment_targets_from_assign_targets<'a, U>(
|
||||
targets: &'a [Expr<U>],
|
||||
dummy_variable_rgx: &'a HashableRegex,
|
||||
dummy_variable_rgx: &'a Regex,
|
||||
) -> impl Iterator<Item = &'a Expr<U>> + 'a {
|
||||
targets
|
||||
.iter()
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
//! Settings for the `pylint` plugin.
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use rustpython_parser::ast::Constant;
|
||||
use schemars::JsonSchema;
|
||||
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")]
|
||||
pub enum ConstantType {
|
||||
Bytes,
|
||||
|
@ -72,7 +70,7 @@ pub struct Options {
|
|||
pub max_statements: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub allow_magic_value_types: Vec<ConstantType>,
|
||||
pub max_args: usize,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Settings for the `pyupgrade` plugin.
|
||||
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use ruff_macros::{CacheKey, ConfigurationOptions};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -31,7 +31,7 @@ pub struct Options {
|
|||
pub keep_runtime_typing: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Hash)]
|
||||
#[derive(Debug, Default, CacheKey)]
|
||||
pub struct Settings {
|
||||
pub keep_runtime_typing: bool,
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ mod tests {
|
|||
let diagnostics = test_path(
|
||||
Path::new("ruff/confusables.py"),
|
||||
&settings::Settings {
|
||||
allowed_confusables: FxHashSet::from_iter(['−', 'ρ', '∗']).into(),
|
||||
allowed_confusables: FxHashSet::from_iter(['−', 'ρ', '∗']),
|
||||
..settings::Settings::for_rules(vec![
|
||||
Rule::AmbiguousUnicodeCharacterString,
|
||||
Rule::AmbiguousUnicodeCharacterDocstring,
|
||||
|
|
|
@ -2,8 +2,8 @@ use once_cell::sync::Lazy;
|
|||
use path_absolutize::path_dedot;
|
||||
use regex::Regex;
|
||||
use rustc_hash::FxHashSet;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::hashable::{HashableGlobSet, HashableHashSet};
|
||||
use super::types::{FilePattern, PythonVersion};
|
||||
use super::Settings;
|
||||
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,
|
||||
isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, pyupgrade,
|
||||
};
|
||||
use crate::settings::types::FilePatternSet;
|
||||
|
||||
pub const PREFIXES: &[RuleSelector] = &[
|
||||
prefix_to_selector(RuleCodePrefix::Pycodestyle(codes::Pycodestyle::E)),
|
||||
|
@ -59,12 +60,12 @@ impl Default for Settings {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
rules: PREFIXES.iter().flat_map(IntoIterator::into_iter).into(),
|
||||
allowed_confusables: FxHashSet::from_iter([]).into(),
|
||||
allowed_confusables: FxHashSet::from_iter([]),
|
||||
builtins: vec![],
|
||||
dummy_variable_rgx: DUMMY_VARIABLE_RGX.clone().into(),
|
||||
exclude: HashableGlobSet::new(EXCLUDE.clone()).unwrap(),
|
||||
extend_exclude: HashableGlobSet::empty(),
|
||||
external: HashableHashSet::default(),
|
||||
dummy_variable_rgx: DUMMY_VARIABLE_RGX.clone(),
|
||||
exclude: FilePatternSet::try_from_vec(EXCLUDE.clone()).unwrap(),
|
||||
extend_exclude: FilePatternSet::default(),
|
||||
external: HashSet::default(),
|
||||
force_exclude: false,
|
||||
ignore_init_module_imports: false,
|
||||
line_length: LINE_LENGTH,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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 {
|
||||
Enabled,
|
||||
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 anyhow::{anyhow, Result};
|
||||
use globset::Glob;
|
||||
use globset::{Glob, GlobMatcher};
|
||||
use regex::Regex;
|
||||
use ruff_cache::cache_dir;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use self::hashable::{HashableGlobMatcher, HashableGlobSet, HashableHashSet, HashableRegex};
|
||||
use self::rule_table::RuleTable;
|
||||
use crate::cache::cache_dir;
|
||||
use crate::registry::{Rule, RuleNamespace, INCOMPATIBLE_CODES};
|
||||
use crate::rule_selector::{RuleSelector, Specificity};
|
||||
use crate::rules::{
|
||||
|
@ -21,13 +21,13 @@ use crate::rules::{
|
|||
isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, pyupgrade,
|
||||
};
|
||||
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 ruff_macros::CacheKey;
|
||||
|
||||
pub mod configuration;
|
||||
pub mod defaults;
|
||||
pub mod flags;
|
||||
pub mod hashable;
|
||||
pub mod options;
|
||||
pub mod options_base;
|
||||
pub mod pyproject;
|
||||
|
@ -74,31 +74,27 @@ pub struct CliSettings {
|
|||
pub update_check: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Settings {
|
||||
pub rules: RuleTable,
|
||||
pub per_file_ignores: Vec<(
|
||||
HashableGlobMatcher,
|
||||
HashableGlobMatcher,
|
||||
HashableHashSet<Rule>,
|
||||
)>,
|
||||
pub per_file_ignores: Vec<(GlobMatcher, GlobMatcher, FxHashSet<Rule>)>,
|
||||
|
||||
pub show_source: bool,
|
||||
pub target_version: PythonVersion,
|
||||
|
||||
// Resolver settings
|
||||
pub exclude: HashableGlobSet,
|
||||
pub extend_exclude: HashableGlobSet,
|
||||
pub exclude: FilePatternSet,
|
||||
pub extend_exclude: FilePatternSet,
|
||||
pub force_exclude: bool,
|
||||
pub respect_gitignore: bool,
|
||||
pub project_root: PathBuf,
|
||||
|
||||
// Rule-specific settings
|
||||
pub allowed_confusables: HashableHashSet<char>,
|
||||
pub allowed_confusables: FxHashSet<char>,
|
||||
pub builtins: Vec<String>,
|
||||
pub dummy_variable_rgx: HashableRegex,
|
||||
pub external: HashableHashSet<String>,
|
||||
pub dummy_variable_rgx: Regex,
|
||||
pub external: FxHashSet<String>,
|
||||
pub ignore_init_module_imports: bool,
|
||||
pub line_length: usize,
|
||||
pub namespace_packages: Vec<PathBuf>,
|
||||
|
@ -146,18 +142,16 @@ impl Settings {
|
|||
allowed_confusables: config
|
||||
.allowed_confusables
|
||||
.map(FxHashSet::from_iter)
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
.unwrap_or_default(),
|
||||
builtins: config.builtins.unwrap_or_default(),
|
||||
dummy_variable_rgx: config
|
||||
.dummy_variable_rgx
|
||||
.unwrap_or_else(|| defaults::DUMMY_VARIABLE_RGX.clone())
|
||||
.into(),
|
||||
exclude: HashableGlobSet::new(
|
||||
.unwrap_or_else(|| defaults::DUMMY_VARIABLE_RGX.clone()),
|
||||
exclude: FilePatternSet::try_from_vec(
|
||||
config.exclude.unwrap_or_else(|| defaults::EXCLUDE.clone()),
|
||||
)?,
|
||||
extend_exclude: HashableGlobSet::new(config.extend_exclude)?,
|
||||
external: FxHashSet::from_iter(config.external.unwrap_or_default()).into(),
|
||||
extend_exclude: FilePatternSet::try_from_vec(config.extend_exclude)?,
|
||||
external: FxHashSet::from_iter(config.external.unwrap_or_default()),
|
||||
|
||||
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`.
|
||||
pub fn resolve_per_file_ignores(
|
||||
per_file_ignores: Vec<PerFileIgnore>,
|
||||
) -> Result<
|
||||
Vec<(
|
||||
HashableGlobMatcher,
|
||||
HashableGlobMatcher,
|
||||
HashableHashSet<Rule>,
|
||||
)>,
|
||||
> {
|
||||
) -> Result<Vec<(GlobMatcher, GlobMatcher, FxHashSet<Rule>)>> {
|
||||
per_file_ignores
|
||||
.into_iter()
|
||||
.map(|per_file_ignore| {
|
||||
|
@ -431,7 +419,7 @@ pub fn resolve_per_file_ignores(
|
|||
// Construct basename 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()
|
||||
}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
use std::collections::hash_map;
|
||||
use std::collections::{hash_map, HashMap};
|
||||
|
||||
use ruff_macros::CacheKey;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use super::hashable::HashableHashMap;
|
||||
use crate::registry::Rule;
|
||||
|
||||
/// A table to keep track of which rules are enabled
|
||||
/// and Whether they should be autofixed.
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, CacheKey)]
|
||||
pub struct RuleTable {
|
||||
/// Maps rule codes to a boolean indicating if the rule should be autofixed.
|
||||
enabled: HashableHashMap<Rule, bool>,
|
||||
enabled: FxHashMap<Rule, bool>,
|
||||
}
|
||||
|
||||
impl RuleTable {
|
||||
/// Creates a new empty rule table.
|
||||
pub fn empty() -> 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 {
|
||||
enabled.insert(code, true);
|
||||
}
|
||||
Self {
|
||||
enabled: enabled.into(),
|
||||
}
|
||||
Self { enabled }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
use std::hash::Hash;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
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 schemars::JsonSchema;
|
||||
use serde::{de, Deserialize, Deserializer, Serialize};
|
||||
|
||||
use super::hashable::HashableHashSet;
|
||||
use crate::registry::Rule;
|
||||
use crate::rule_selector::RuleSelector;
|
||||
use crate::{fs, warn_user_once};
|
||||
|
||||
#[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")]
|
||||
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 {
|
||||
Builtin(&'static str),
|
||||
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)]
|
||||
pub struct PerFileIgnore {
|
||||
pub(crate) basename: String,
|
||||
pub(crate) absolute: PathBuf,
|
||||
pub(crate) rules: HashableHashSet<Rule>,
|
||||
pub(crate) rules: FxHashSet<Rule>,
|
||||
}
|
||||
|
||||
impl PerFileIgnore {
|
||||
|
@ -116,7 +157,7 @@ impl PerFileIgnore {
|
|||
Self {
|
||||
basename: pattern,
|
||||
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};
|
||||
|
||||
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]
|
||||
ruff = { path = "../ruff" }
|
||||
ruff_cache = { path = "../ruff_cache" }
|
||||
|
||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::fs;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::hash::Hasher;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
|
@ -10,6 +9,7 @@ use log::error;
|
|||
use path_absolutize::Absolutize;
|
||||
use ruff::message::Message;
|
||||
use ruff::settings::{flags, AllSettings, Settings};
|
||||
use ruff_cache::{CacheKey, CacheKeyHasher};
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
@ -37,18 +37,18 @@ fn cache_key<P: AsRef<Path>>(
|
|||
settings: &Settings,
|
||||
autofix: flags::Autofix,
|
||||
) -> u64 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
CARGO_PKG_VERSION.hash(&mut hasher);
|
||||
path.as_ref().absolutize().unwrap().hash(&mut hasher);
|
||||
let mut hasher = CacheKeyHasher::new();
|
||||
CARGO_PKG_VERSION.cache_key(&mut hasher);
|
||||
path.as_ref().absolutize().unwrap().cache_key(&mut hasher);
|
||||
package
|
||||
.as_ref()
|
||||
.map(|path| path.as_ref().absolutize().unwrap())
|
||||
.hash(&mut hasher);
|
||||
FileTime::from_last_modification_time(metadata).hash(&mut hasher);
|
||||
.cache_key(&mut hasher);
|
||||
FileTime::from_last_modification_time(metadata).cache_key(&mut hasher);
|
||||
#[cfg(unix)]
|
||||
metadata.permissions().mode().hash(&mut hasher);
|
||||
settings.hash(&mut hasher);
|
||||
autofix.hash(&mut hasher);
|
||||
metadata.permissions().mode().cache_key(&mut hasher);
|
||||
settings.cache_key(&mut hasher);
|
||||
autofix.cache_key(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@ use colored::Colorize;
|
|||
use path_absolutize::path_dedot;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use ruff::cache::CACHE_DIR_NAME;
|
||||
use ruff::fs;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff_cache::CACHE_DIR_NAME;
|
||||
|
||||
/// Clear any caches in the current directory or any subdirectories.
|
||||
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"] }
|
||||
textwrap = { version = "0.16.0" }
|
||||
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.
|
||||
|
||||
use crate::cache_key::derive_cache_key;
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput, ItemFn};
|
||||
|
||||
mod cache_key;
|
||||
mod config;
|
||||
mod define_violation;
|
||||
mod derive_message_formats;
|
||||
|
@ -20,6 +22,16 @@ pub fn derive_config(input: proc_macro::TokenStream) -> proc_macro::TokenStream
|
|||
.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]
|
||||
pub fn register_rules(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let mapping = parse_macro_input!(item as register_rules::Input);
|
||||
|
|
|
@ -56,6 +56,7 @@ pub fn register_rules(input: &Input) -> proc_macro2::TokenStream {
|
|||
Hash,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
::ruff_macros::CacheKey,
|
||||
AsRefStr,
|
||||
::strum_macros::IntoStaticStr,
|
||||
)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue