Adapt is-macro for a few enums (#3182)

This commit is contained in:
Jeong YunWon
2023-02-24 13:06:56 +09:00
committed by GitHub
parent 0f37a98d91
commit da98fab4ae
16 changed files with 110 additions and 125 deletions

20
Cargo.lock generated
View File

@@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "Inflector"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
[[package]]
name = "adler"
version = "1.0.2"
@@ -1023,6 +1029,19 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "is-macro"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7d079e129b77477a49c5c4f1cfe9ce6c2c909ef52520693e8e811a714c7b20"
dependencies = [
"Inflector",
"pmutil",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "is-terminal"
version = "0.4.4"
@@ -1971,6 +1990,7 @@ dependencies = [
"ignore",
"imperative",
"insta",
"is-macro",
"itertools",
"js-sys",
"libcst",

View File

@@ -33,6 +33,7 @@ glob = { version = "0.3.0" }
globset = { version = "0.4.9" }
ignore = { version = "0.4.18" }
imperative = { version = "1.0.3" }
is-macro = { workspace = true }
itertools = { workspace = true }
libcst = { workspace = true }
log = { version = "0.4.17" }

View File

@@ -124,7 +124,7 @@ impl<'a> Scope<'a> {
// StarImportation
// FutureImportation
#[derive(Clone, Debug)]
#[derive(Clone, Debug, is_macro::Is)]
pub enum BindingKind<'a> {
Annotation,
Argument,

View File

@@ -229,9 +229,8 @@ impl<'a> Checker<'a> {
/// Return `true` if `member` is bound as a builtin.
pub fn is_builtin(&self, member: &str) -> bool {
self.find_binding(member).map_or(false, |binding| {
matches!(binding.kind, BindingKind::Builtin)
})
self.find_binding(member)
.map_or(false, |binding| binding.kind.is_builtin())
}
pub fn resolve_call_path<'b>(&'a self, value: &'b Expr) -> Option<CallPath<'a>>
@@ -1928,9 +1927,7 @@ where
if self.scopes[GLOBAL_SCOPE_INDEX]
.bindings
.get(name)
.map_or(true, |index| {
matches!(self.bindings[*index].kind, BindingKind::Annotation)
})
.map_or(true, |index| self.bindings[*index].kind.is_annotation())
{
let index = self.bindings.len();
self.bindings.push(Binding {
@@ -1992,9 +1989,7 @@ where
if self.scopes[GLOBAL_SCOPE_INDEX]
.bindings
.get(name)
.map_or(true, |index| {
matches!(self.bindings[*index].kind, BindingKind::Annotation)
})
.map_or(true, |index| self.bindings[*index].kind.is_annotation())
{
let index = self.bindings.len();
self.bindings.push(Binding {
@@ -4136,7 +4131,7 @@ impl<'a> Checker<'a> {
let existing_binding_index = self.scopes[*scope_index].bindings.get(&name).unwrap();
let existing = &self.bindings[*existing_binding_index];
let in_current_scope = stack_index == 0;
if !matches!(existing.kind, BindingKind::Builtin)
if !existing.kind.is_builtin()
&& existing.source.as_ref().map_or(true, |left| {
binding.source.as_ref().map_or(true, |right| {
!branch_detection::different_forks(
@@ -4156,7 +4151,7 @@ impl<'a> Checker<'a> {
| BindingKind::StarImportation(..)
| BindingKind::FutureImportation
);
if matches!(binding.kind, BindingKind::LoopVar) && existing_is_import {
if binding.kind.is_loop_var() && existing_is_import {
if self.settings.rules.enabled(&Rule::ImportShadowedByLoopVar) {
self.diagnostics.push(Diagnostic::new(
pyflakes::rules::ImportShadowedByLoopVar {
@@ -4170,7 +4165,7 @@ impl<'a> Checker<'a> {
if !existing.used()
&& binding.redefines(existing)
&& (!self.settings.dummy_variable_rgx.is_match(name) || existing_is_import)
&& !(matches!(existing.kind, BindingKind::FunctionDefinition)
&& !(existing.kind.is_function_definition()
&& visibility::is_overload(
self,
cast::decorator_list(existing.source.as_ref().unwrap()),
@@ -4205,36 +4200,29 @@ impl<'a> Checker<'a> {
let scope = self.current_scope();
let binding = if let Some(index) = scope.bindings.get(&name) {
if matches!(self.bindings[*index].kind, BindingKind::Builtin) {
// Avoid overriding builtins.
binding
} else if matches!(self.bindings[*index].kind, BindingKind::Global) {
// If the original binding was a global, and the new binding conflicts within
// the current scope, then the new binding is also a global.
Binding {
runtime_usage: self.bindings[*index].runtime_usage,
synthetic_usage: self.bindings[*index].synthetic_usage,
typing_usage: self.bindings[*index].typing_usage,
kind: BindingKind::Global,
..binding
let existing = &self.bindings[*index];
match &existing.kind {
BindingKind::Builtin => {
// Avoid overriding builtins.
binding
}
} else if matches!(self.bindings[*index].kind, BindingKind::Nonlocal) {
// If the original binding was a nonlocal, and the new binding conflicts within
// the current scope, then the new binding is also a nonlocal.
Binding {
runtime_usage: self.bindings[*index].runtime_usage,
synthetic_usage: self.bindings[*index].synthetic_usage,
typing_usage: self.bindings[*index].typing_usage,
kind: BindingKind::Nonlocal,
..binding
kind @ (BindingKind::Global | BindingKind::Nonlocal) => {
// If the original binding was a global or nonlocal, and the new binding conflicts within
// the current scope, then the new binding is also as the same.
Binding {
runtime_usage: existing.runtime_usage,
synthetic_usage: existing.synthetic_usage,
typing_usage: existing.typing_usage,
kind: kind.clone(),
..binding
}
}
} else {
Binding {
runtime_usage: self.bindings[*index].runtime_usage,
synthetic_usage: self.bindings[*index].synthetic_usage,
typing_usage: self.bindings[*index].typing_usage,
_ => Binding {
runtime_usage: existing.runtime_usage,
synthetic_usage: existing.synthetic_usage,
typing_usage: existing.typing_usage,
..binding
}
},
}
} else {
binding
@@ -4243,7 +4231,7 @@ impl<'a> Checker<'a> {
// Don't treat annotations as assignments if there is an existing value
// in scope.
let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found"))];
if !(matches!(binding.kind, BindingKind::Annotation) && scope.bindings.contains_key(name)) {
if !(binding.kind.is_annotation() && scope.bindings.contains_key(name)) {
if let Some(rebound_index) = scope.bindings.insert(name, binding_index) {
scope
.rebounds
@@ -4282,7 +4270,7 @@ impl<'a> Checker<'a> {
let context = self.execution_context();
self.bindings[*index].mark_used(scope_id, Range::from_located(expr), context);
if matches!(self.bindings[*index].kind, BindingKind::Annotation)
if self.bindings[*index].kind.is_annotation()
&& !self.in_deferred_string_type_definition
&& !self.in_deferred_type_definition
{
@@ -4430,9 +4418,7 @@ impl<'a> Checker<'a> {
.current_scope()
.bindings
.get(id)
.map_or(false, |index| {
matches!(self.bindings[*index].kind, BindingKind::Global)
})
.map_or(false, |index| self.bindings[*index].kind.is_global())
{
pep8_naming::rules::non_lowercase_variable_in_function(self, expr, parent, id);
}
@@ -4935,7 +4921,7 @@ impl<'a> Checker<'a> {
{
for (name, index) in &scope.bindings {
let binding = &self.bindings[*index];
if matches!(binding.kind, BindingKind::Global) {
if binding.kind.is_global() {
if let Some(stmt) = &binding.source {
if matches!(stmt.node, StmtKind::Global { .. }) {
diagnostics.push(Diagnostic::new(

View File

@@ -6,7 +6,6 @@ use rustpython_parser::ast::Location;
use rustpython_parser::lexer::LexResult;
use rustpython_parser::Tok;
use crate::registry::LintSource;
use crate::settings::Settings;
bitflags! {
@@ -21,7 +20,7 @@ impl Flags {
if settings
.rules
.iter_enabled()
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::Imports))
.any(|rule_code| rule_code.lint_source().is_imports())
{
Self::NOQA | Self::ISORT
} else {

View File

@@ -20,7 +20,7 @@ use crate::directives::Directives;
use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens};
use crate::message::{Message, Source};
use crate::noqa::{add_noqa, rule_is_ignored};
use crate::registry::{Diagnostic, LintSource, Rule};
use crate::registry::{Diagnostic, Rule};
use crate::rules::pycodestyle;
use crate::settings::{flags, Settings};
use crate::source_code::{Indexer, Locator, Stylist};
@@ -81,7 +81,7 @@ pub fn check_path(
if settings
.rules
.iter_enabled()
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::Tokens))
.any(|rule_code| rule_code.lint_source().is_tokens())
{
diagnostics.extend(check_tokens(locator, &tokens, settings, autofix));
}
@@ -90,7 +90,7 @@ pub fn check_path(
if settings
.rules
.iter_enabled()
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::Filesystem))
.any(|rule_code| rule_code.lint_source().is_filesystem())
{
diagnostics.extend(check_file_path(path, package, settings));
}
@@ -99,7 +99,7 @@ pub fn check_path(
if settings
.rules
.iter_enabled()
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::LogicalLines))
.any(|rule_code| rule_code.lint_source().is_logical_lines())
{
diagnostics.extend(check_logical_lines(&tokens, locator, stylist, settings));
}
@@ -108,12 +108,12 @@ pub fn check_path(
let use_ast = settings
.rules
.iter_enabled()
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::Ast));
.any(|rule_code| rule_code.lint_source().is_ast());
let use_imports = !directives.isort.skip_file
&& settings
.rules
.iter_enabled()
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::Imports));
.any(|rule_code| rule_code.lint_source().is_imports());
if use_ast || use_imports || use_doc_lines {
match ruff_rustpython::parse_program_tokens(tokens, &path.to_string_lossy()) {
Ok(python_ast) => {
@@ -177,7 +177,7 @@ pub fn check_path(
if settings
.rules
.iter_enabled()
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::PhysicalLines))
.any(|rule_code| rule_code.lint_source().is_physical_lines())
{
diagnostics.extend(check_physical_lines(
path,
@@ -203,7 +203,7 @@ pub fn check_path(
|| settings
.rules
.iter_enabled()
.any(|rule_code| matches!(rule_code.lint_source(), LintSource::NoQa))
.any(|rule_code| rule_code.lint_source().is_noqa())
{
check_noqa(
&mut diagnostics,

View File

@@ -759,6 +759,7 @@ impl Linter {
}
}
#[derive(is_macro::Is)]
pub enum LintSource {
Ast,
Io,
@@ -766,7 +767,7 @@ pub enum LintSource {
LogicalLines,
Tokens,
Imports,
NoQa,
Noqa,
Filesystem,
}
@@ -775,7 +776,7 @@ impl Rule {
/// physical lines).
pub const fn lint_source(&self) -> &'static LintSource {
match self {
Rule::UnusedNOQA => &LintSource::NoQa,
Rule::UnusedNOQA => &LintSource::Noqa,
Rule::BlanketNOQA
| Rule::BlanketTypeIgnore
| Rule::DocLineTooLong

View File

@@ -19,7 +19,7 @@ use crate::settings::{pyproject, AllSettings, Settings};
/// The strategy used to discover the relevant `pyproject.toml` file for each
/// Python file.
#[derive(Debug)]
#[derive(Debug, is_macro::Is)]
pub enum PyprojectDiscovery {
/// Use a fixed `pyproject.toml` file for all Python files (i.e., one
/// provided on the command-line).
@@ -30,7 +30,7 @@ pub enum PyprojectDiscovery {
}
impl PyprojectDiscovery {
fn top_level_settings(&self) -> &AllSettings {
pub fn top_level_settings(&self) -> &AllSettings {
match self {
PyprojectDiscovery::Fixed(settings) => settings,
PyprojectDiscovery::Hierarchical(settings) => settings,
@@ -82,13 +82,7 @@ impl Resolver {
.settings
.iter()
.rev()
.find_map(|(root, settings)| {
if path.starts_with(root) {
Some(settings)
} else {
None
}
})
.find_map(|(root, settings)| path.starts_with(root).then_some(settings))
.unwrap_or(default),
}
}
@@ -228,7 +222,7 @@ pub fn python_files_in_path(
// Search for `pyproject.toml` files in all parent directories.
let mut resolver = Resolver::default();
let mut seen = FxHashSet::default();
if matches!(pyproject_strategy, PyprojectDiscovery::Hierarchical(..)) {
if pyproject_strategy.is_hierarchical() {
for path in &paths {
for ancestor in path.ancestors() {
if seen.insert(ancestor) {
@@ -277,7 +271,7 @@ pub fn python_files_in_path(
Box::new(|result| {
// Search for the `pyproject.toml` file in this directory, before we visit any
// of its contents.
if matches!(pyproject_strategy, PyprojectDiscovery::Hierarchical(..)) {
if pyproject_strategy.is_hierarchical() {
if let Ok(entry) = &result {
if entry
.file_type()
@@ -372,7 +366,7 @@ pub fn python_file_at_path(
// Search for `pyproject.toml` files in all parent directories.
let mut resolver = Resolver::default();
if matches!(pyproject_strategy, PyprojectDiscovery::Hierarchical(..)) {
if pyproject_strategy.is_hierarchical() {
for ancestor in path.ancestors() {
if let Some(pyproject) = settings_toml(ancestor)? {
let (root, settings) =

View File

@@ -23,7 +23,7 @@ use rustc_hash::FxHashMap;
use rustpython_parser::ast::{Expr, ExprKind, Stmt};
use serde::{Deserialize, Serialize};
use crate::ast::types::{BindingKind, Range, RefEquality};
use crate::ast::types::{Range, RefEquality};
use crate::ast::visitor::Visitor;
use crate::ast::{helpers, visitor};
use crate::checkers::ast::Checker;
@@ -185,7 +185,7 @@ pub fn unused_loop_control_variable(
.and_then(|source| (source == &RefEquality(stmt)).then_some(binding))
});
if let Some(binding) = binding {
if matches!(binding.kind, BindingKind::LoopVar) {
if binding.kind.is_loop_var() {
if !binding.used() {
diagnostic.amend(Fix::replacement(
rename,

View File

@@ -3,7 +3,7 @@ use rustpython_parser::ast::{Expr, ExprKind};
use ruff_macros::{define_violation, derive_message_formats};
use crate::ast::helpers::collect_call_path;
use crate::ast::types::{BindingKind, Range, ScopeKind};
use crate::ast::types::{Range, ScopeKind};
use crate::checkers::ast::Checker;
use crate::registry::Diagnostic;
use crate::violation::Violation;
@@ -101,7 +101,7 @@ pub fn private_member_access(checker: &mut Checker, expr: &Expr) {
.map_or(false, |binding| {
// TODO(charlie): Could the name ever be bound to a _different_
// class here?
matches!(binding.kind, BindingKind::ClassDefinition)
binding.kind.is_class_definition()
})
} else {
false

View File

@@ -9,7 +9,7 @@ use super::helpers;
use super::types::Argumentable;
use crate::ast::function_type;
use crate::ast::function_type::FunctionType;
use crate::ast::types::{Binding, BindingKind, FunctionDef, Lambda, Scope, ScopeKind};
use crate::ast::types::{Binding, FunctionDef, Lambda, Scope, ScopeKind};
use crate::checkers::ast::Checker;
use crate::registry::Diagnostic;
use crate::violation::Violation;
@@ -89,8 +89,7 @@ fn function(
dummy_variable_rgx: &Regex,
ignore_variadic_names: bool,
) -> Vec<Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = vec![];
for arg in args
let args = args
.posonlyargs
.iter()
.chain(args.args.iter())
@@ -104,24 +103,8 @@ fn function(
iter::once::<Option<&Arg>>(args.kwarg.as_deref())
.flatten()
.skip(usize::from(ignore_variadic_names)),
)
{
if let Some(binding) = values
.get(&arg.node.arg.as_str())
.map(|index| &bindings[*index])
{
if !binding.used()
&& matches!(binding.kind, BindingKind::Argument)
&& !dummy_variable_rgx.is_match(arg.node.arg.as_str())
{
diagnostics.push(Diagnostic::new(
argumentable.check_for(arg.node.arg.to_string()),
binding.range,
));
}
}
}
diagnostics
);
call(argumentable, args, values, bindings, dummy_variable_rgx)
}
/// Check a method for unused arguments.
@@ -133,8 +116,7 @@ fn method(
dummy_variable_rgx: &Regex,
ignore_variadic_names: bool,
) -> Vec<Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = vec![];
for arg in args
let args = args
.posonlyargs
.iter()
.chain(args.args.iter())
@@ -149,14 +131,25 @@ fn method(
iter::once::<Option<&Arg>>(args.kwarg.as_deref())
.flatten()
.skip(usize::from(ignore_variadic_names)),
)
{
);
call(argumentable, args, values, bindings, dummy_variable_rgx)
}
fn call<'a>(
argumentable: &Argumentable,
args: impl Iterator<Item = &'a Arg>,
values: &FxHashMap<&str, usize>,
bindings: &[Binding],
dummy_variable_rgx: &Regex,
) -> Vec<Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = vec![];
for arg in args {
if let Some(binding) = values
.get(&arg.node.arg.as_str())
.map(|index| &bindings[*index])
{
if !binding.used()
&& matches!(binding.kind, BindingKind::Argument)
&& binding.kind.is_argument()
&& !dummy_variable_rgx.is_match(arg.node.arg.as_str())
{
diagnostics.push(Diagnostic::new(

View File

@@ -1,6 +1,5 @@
use ruff_macros::{define_violation, derive_message_formats};
use crate::ast::types::BindingKind;
use crate::checkers::ast::Checker;
use crate::registry::Diagnostic;
use crate::violation::Violation;
@@ -27,7 +26,7 @@ pub fn unused_annotation(checker: &mut Checker, scope: usize) {
.map(|(name, index)| (name, &checker.bindings[*index]))
{
if !binding.used()
&& matches!(binding.kind, BindingKind::Annotation)
&& binding.kind.is_annotation()
&& !checker.settings.dummy_variable_rgx.is_match(name)
{
checker.diagnostics.push(Diagnostic::new(

View File

@@ -5,7 +5,7 @@ use rustpython_parser::ast::{ExprKind, Located, Stmt, StmtKind};
use rustpython_parser::{lexer, Mode, Tok};
use crate::ast::helpers::contains_effect;
use crate::ast::types::{BindingKind, Range, RefEquality, ScopeKind};
use crate::ast::types::{Range, RefEquality, ScopeKind};
use crate::autofix::helpers::delete_stmt;
use crate::checkers::ast::Checker;
use crate::fix::Fix;
@@ -330,7 +330,7 @@ pub fn unused_variable(checker: &mut Checker, scope: usize) {
.map(|(name, index)| (name, &checker.bindings[*index]))
{
if !binding.used()
&& matches!(binding.kind, BindingKind::Assignment)
&& binding.kind.is_assignment()
&& !checker.settings.dummy_variable_rgx.is_match(name)
&& name != &"__tracebackhide__"
&& name != &"__traceback_info__"

View File

@@ -41,23 +41,22 @@ pub fn run(
// Initialize the cache.
if cache.into() {
fn init_cache(path: &std::path::Path) {
if let Err(e) = cache::init(path) {
error!(
"Failed to initialize cache at {}: {e:?}",
path.to_string_lossy()
);
}
}
match &pyproject_strategy {
PyprojectDiscovery::Fixed(settings) => {
if let Err(e) = cache::init(&settings.cli.cache_dir) {
error!(
"Failed to initialize cache at {}: {e:?}",
settings.cli.cache_dir.to_string_lossy()
);
}
init_cache(&settings.cli.cache_dir);
}
PyprojectDiscovery::Hierarchical(default) => {
for settings in std::iter::once(default).chain(resolver.iter()) {
if let Err(e) = cache::init(&settings.cli.cache_dir) {
error!(
"Failed to initialize cache at {}: {e:?}",
settings.cli.cache_dir.to_string_lossy()
);
}
init_cache(&settings.cli.cache_dir);
}
}
}

View File

@@ -28,10 +28,7 @@ pub fn run_stdin(
return Ok(Diagnostics::default());
}
}
let settings = match pyproject_strategy {
PyprojectDiscovery::Fixed(settings) => settings,
PyprojectDiscovery::Hierarchical(settings) => settings,
};
let settings = pyproject_strategy.top_level_settings();
let package_root = filename
.and_then(Path::parent)
.and_then(|path| packaging::detect_package_root(path, &settings.lib.namespace_packages));

View File

@@ -9,7 +9,6 @@ use colored::Colorize;
use notify::{recommended_watcher, RecursiveMode, Watcher};
use ::ruff::logging::{set_up_logging, LogLevel};
use ::ruff::resolver::PyprojectDiscovery;
use ::ruff::settings::types::SerializationFormat;
use ::ruff::settings::CliSettings;
use ::ruff::{fix, fs, warn_user_once};
@@ -151,10 +150,7 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
show_fixes,
update_check,
..
} = match &pyproject_strategy {
PyprojectDiscovery::Fixed(settings) => settings.cli.clone(),
PyprojectDiscovery::Hierarchical(settings) => settings.cli.clone(),
};
} = pyproject_strategy.top_level_settings().cli.clone();
// Autofix rules are as follows:
// - If `--fix` or `--fix-only` is set, always apply fixes to the filesystem (or