Compare commits
6 Commits
tracing-in
...
zanie/rule
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1880cb45df | ||
|
|
df8a3db939 | ||
|
|
595207b853 | ||
|
|
97654a9911 | ||
|
|
eccda3d3ed | ||
|
|
daa9b4db72 |
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -783,6 +783,15 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
|
||||
|
||||
[[package]]
|
||||
name = "fern"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.22"
|
||||
@@ -2021,6 +2030,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"colored",
|
||||
"fern",
|
||||
"glob",
|
||||
"globset",
|
||||
"imperative",
|
||||
@@ -2070,8 +2080,6 @@ dependencies = [
|
||||
"test-case",
|
||||
"thiserror",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"typed-arena",
|
||||
"unicode-width",
|
||||
"unicode_names2",
|
||||
|
||||
@@ -37,6 +37,7 @@ bitflags = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive", "string"], optional = true }
|
||||
colored = { workspace = true }
|
||||
fern = { version = "0.6.1" }
|
||||
glob = { workspace = true }
|
||||
globset = { workspace = true }
|
||||
imperative = { version = "1.0.4" }
|
||||
@@ -70,8 +71,6 @@ strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unicode-width = { workspace = true }
|
||||
unicode_names2 = { version = "0.6.0", git = "https://github.com/youknowone/unicode_names2.git", rev = "4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde" }
|
||||
@@ -90,3 +89,5 @@ default = []
|
||||
schemars = ["dep:schemars"]
|
||||
# Enables the UnreachableCode rule
|
||||
unreachable-code = []
|
||||
# Enables rules for internal integration tests
|
||||
test-rules = []
|
||||
|
||||
@@ -7,8 +7,6 @@ reversed(sorted(x, reverse=True))
|
||||
reversed(sorted(x, key=lambda e: e, reverse=True))
|
||||
reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||
reversed(sorted(x, reverse=False))
|
||||
reversed(sorted(x, reverse=x))
|
||||
reversed(sorted(x, reverse=not x))
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
reversed(sorted(i for i in range(42)))
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
class Everything:
|
||||
foo = 1
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def _private(self):
|
||||
pass
|
||||
|
||||
def method1(self):
|
||||
pass
|
||||
|
||||
def method2(self):
|
||||
pass
|
||||
|
||||
def method3(self):
|
||||
pass
|
||||
|
||||
def method4(self):
|
||||
pass
|
||||
|
||||
def method5(self):
|
||||
pass
|
||||
|
||||
def method6(self):
|
||||
pass
|
||||
|
||||
def method7(self):
|
||||
pass
|
||||
|
||||
def method8(self):
|
||||
pass
|
||||
|
||||
def method9(self):
|
||||
pass
|
||||
|
||||
class Small:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def _private(self):
|
||||
pass
|
||||
|
||||
def method1(self):
|
||||
pass
|
||||
|
||||
def method2(self):
|
||||
pass
|
||||
|
||||
def method3(self):
|
||||
pass
|
||||
|
||||
def method4(self):
|
||||
pass
|
||||
|
||||
def method5(self):
|
||||
pass
|
||||
|
||||
def method6(self):
|
||||
pass
|
||||
@@ -411,13 +411,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::EqWithoutHash) {
|
||||
pylint::rules::object_without_hash_method(checker, class_def);
|
||||
}
|
||||
if checker.enabled(Rule::TooManyPublicMethods) {
|
||||
pylint::rules::too_many_public_methods(
|
||||
checker,
|
||||
class_def,
|
||||
checker.settings.pylint.max_public_methods,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::GlobalStatement) {
|
||||
pylint::rules::global_statement(checker, name);
|
||||
}
|
||||
|
||||
@@ -269,7 +269,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "W1510") => (RuleGroup::Unspecified, rules::pylint::rules::SubprocessRunWithoutCheck),
|
||||
#[allow(deprecated)]
|
||||
(Pylint, "W1641") => (RuleGroup::Nursery, rules::pylint::rules::EqWithoutHash),
|
||||
(Pylint, "R0904") => (RuleGroup::Preview, rules::pylint::rules::TooManyPublicMethods),
|
||||
(Pylint, "W2901") => (RuleGroup::Unspecified, rules::pylint::rules::RedefinedLoopName),
|
||||
#[allow(deprecated)]
|
||||
(Pylint, "W3201") => (RuleGroup::Nursery, rules::pylint::rules::BadDunderMethodName),
|
||||
@@ -866,6 +865,13 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Ruff, "017") => (RuleGroup::Nursery, rules::ruff::rules::QuadraticListSummation),
|
||||
(Ruff, "100") => (RuleGroup::Unspecified, rules::ruff::rules::UnusedNOQA),
|
||||
(Ruff, "200") => (RuleGroup::Unspecified, rules::ruff::rules::InvalidPyprojectToml),
|
||||
#[cfg(feature = "test-rules")]
|
||||
(Ruff, "900") => (RuleGroup::Unspecified, rules::ruff::rules::StableTestRule),
|
||||
#[cfg(feature = "test-rules")]
|
||||
(Ruff, "911") => (RuleGroup::Preview, rules::ruff::rules::PreviewTestRule),
|
||||
#[cfg(feature = "test-rules")]
|
||||
#[allow(deprecated)]
|
||||
(Ruff, "912") => (RuleGroup::Nursery, rules::ruff::rules::NurseryTestRule),
|
||||
|
||||
// flake8-django
|
||||
(Flake8Django, "001") => (RuleGroup::Unspecified, rules::flake8_django::rules::DjangoNullableModelStringField),
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use libcst_native::{
|
||||
Expression, Name, NameOrAttribute, ParenthesizableWhitespace, SimpleWhitespace, UnaryOperation,
|
||||
};
|
||||
use libcst_native::{Expression, NameOrAttribute, ParenthesizableWhitespace, SimpleWhitespace};
|
||||
|
||||
fn compose_call_path_inner<'a>(expr: &'a Expression, parts: &mut Vec<&'a str>) {
|
||||
match expr {
|
||||
@@ -52,41 +50,3 @@ pub(crate) fn or_space(whitespace: ParenthesizableWhitespace) -> Parenthesizable
|
||||
whitespace
|
||||
}
|
||||
}
|
||||
|
||||
/// Negate a condition, i.e., `a` => `not a` and `not a` => `a`.
|
||||
pub(crate) fn negate<'a>(expression: &Expression<'a>) -> Expression<'a> {
|
||||
if let Expression::UnaryOperation(ref expression) = expression {
|
||||
if matches!(expression.operator, libcst_native::UnaryOp::Not { .. }) {
|
||||
return *expression.expression.clone();
|
||||
}
|
||||
}
|
||||
|
||||
if let Expression::Name(ref expression) = expression {
|
||||
match expression.value {
|
||||
"True" => {
|
||||
return Expression::Name(Box::new(Name {
|
||||
value: "False",
|
||||
lpar: vec![],
|
||||
rpar: vec![],
|
||||
}));
|
||||
}
|
||||
"False" => {
|
||||
return Expression::Name(Box::new(Name {
|
||||
value: "True",
|
||||
lpar: vec![],
|
||||
rpar: vec![],
|
||||
}));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Expression::UnaryOperation(Box::new(UnaryOperation {
|
||||
operator: libcst_native::UnaryOp::Not {
|
||||
whitespace_after: space(),
|
||||
},
|
||||
expression: Box::new(expression.clone()),
|
||||
lpar: vec![],
|
||||
rpar: vec![],
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -8,16 +8,6 @@ use itertools::Itertools;
|
||||
use log::error;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::imports::ImportMap;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::lexer::LexResult;
|
||||
use ruff_python_parser::{AsMode, ParseError};
|
||||
use ruff_source_file::{Locator, SourceFileBuilder};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::autofix::{fix_file, FixResult};
|
||||
use crate::checkers::ast::check_ast;
|
||||
use crate::checkers::filesystem::check_file_path;
|
||||
@@ -35,6 +25,15 @@ use crate::rules::pycodestyle;
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_kind::SourceKind;
|
||||
use crate::{directives, fs};
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::imports::ImportMap;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::lexer::LexResult;
|
||||
use ruff_python_parser::{AsMode, ParseError};
|
||||
use ruff_source_file::{Locator, SourceFileBuilder};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
const CARGO_PKG_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
const CARGO_PKG_REPOSITORY: &str = env!("CARGO_PKG_REPOSITORY");
|
||||
@@ -213,6 +212,31 @@ pub fn check_path(
|
||||
));
|
||||
}
|
||||
|
||||
// Raise violations for internal test rules
|
||||
#[cfg(feature = "test-rules")]
|
||||
{
|
||||
if settings.rules.enabled(Rule::StableTestRule) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
crate::rules::ruff::rules::StableTestRule,
|
||||
ruff_text_size::TextRange::default(),
|
||||
));
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::PreviewTestRule) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
crate::rules::ruff::rules::PreviewTestRule,
|
||||
ruff_text_size::TextRange::default(),
|
||||
));
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::NurseryTestRule) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
crate::rules::ruff::rules::NurseryTestRule,
|
||||
ruff_text_size::TextRange::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore diagnostics based on per-file-ignores.
|
||||
if !diagnostics.is_empty() && !settings.per_file_ignores.is_empty() {
|
||||
let ignores = fs::ignores_from_path(path, &settings.per_file_ignores);
|
||||
|
||||
@@ -4,17 +4,16 @@ use std::sync::Mutex;
|
||||
|
||||
use anyhow::Result;
|
||||
use colored::Colorize;
|
||||
use fern;
|
||||
use log::Level;
|
||||
use once_cell::sync::Lazy;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use ruff_notebook::Notebook;
|
||||
use ruff_python_parser::{ParseError, ParseErrorType};
|
||||
|
||||
use ruff_source_file::{OneIndexed, SourceCode, SourceLocation};
|
||||
|
||||
use crate::fs;
|
||||
use crate::source_kind::SourceKind;
|
||||
use ruff_notebook::Notebook;
|
||||
|
||||
pub static WARNINGS: Lazy<Mutex<Vec<&'static str>>> = Lazy::new(Mutex::default);
|
||||
|
||||
@@ -91,27 +90,49 @@ pub enum LogLevel {
|
||||
|
||||
impl LogLevel {
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
const fn tracing_level(&self) -> tracing::Level {
|
||||
const fn level_filter(&self) -> log::LevelFilter {
|
||||
match self {
|
||||
LogLevel::Default => tracing::Level::INFO,
|
||||
LogLevel::Verbose => tracing::Level::DEBUG,
|
||||
LogLevel::Quiet => tracing::Level::WARN,
|
||||
LogLevel::Silent => tracing::Level::ERROR,
|
||||
LogLevel::Default => log::LevelFilter::Info,
|
||||
LogLevel::Verbose => log::LevelFilter::Debug,
|
||||
LogLevel::Quiet => log::LevelFilter::Off,
|
||||
LogLevel::Silent => log::LevelFilter::Off,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Log level priorities: 1. `RUST_LOG=`, 2. explicit CLI log level, 3. default to info
|
||||
pub fn set_up_logging(level: &LogLevel) -> Result<()> {
|
||||
let filter_layer = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
|
||||
EnvFilter::builder()
|
||||
.with_default_directive(level.tracing_level().into())
|
||||
.parse_lossy("")
|
||||
});
|
||||
tracing_subscriber::registry()
|
||||
.with(filter_layer)
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.init();
|
||||
fern::Dispatch::new()
|
||||
.format(|out, message, record| match record.level() {
|
||||
Level::Error => {
|
||||
out.finish(format_args!(
|
||||
"{}{} {}",
|
||||
"error".red().bold(),
|
||||
":".bold(),
|
||||
message
|
||||
));
|
||||
}
|
||||
Level::Warn => {
|
||||
out.finish(format_args!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
message
|
||||
));
|
||||
}
|
||||
Level::Info | Level::Debug | Level::Trace => {
|
||||
out.finish(format_args!(
|
||||
"{}[{}][{}] {}",
|
||||
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
|
||||
record.target(),
|
||||
record.level(),
|
||||
message
|
||||
));
|
||||
}
|
||||
})
|
||||
.level(level.level_filter())
|
||||
.level_for("globset", log::LevelFilter::Warn)
|
||||
.chain(std::io::stderr())
|
||||
.apply()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -7,17 +7,17 @@ use libcst_native::{
|
||||
RightCurlyBrace, RightParen, RightSquareBracket, Set, SetComp, SimpleString, SimpleWhitespace,
|
||||
TrailingWhitespace, Tuple,
|
||||
};
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use ruff_diagnostics::{Edit, Fix};
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::autofix::codemods::CodegenStylist;
|
||||
use crate::autofix::edits::pad;
|
||||
use crate::cst::helpers::{negate, space};
|
||||
use crate::cst::helpers::space;
|
||||
use crate::rules::flake8_comprehensions::rules::ObjectType;
|
||||
use crate::{
|
||||
checkers::ast::Checker,
|
||||
@@ -728,7 +728,7 @@ pub(crate) fn fix_unnecessary_call_around_sorted(
|
||||
})
|
||||
)
|
||||
}) {
|
||||
// Negate the `reverse` argument.
|
||||
// Negate the `reverse` argument
|
||||
inner_call
|
||||
.args
|
||||
.clone()
|
||||
@@ -741,9 +741,31 @@ pub(crate) fn fix_unnecessary_call_around_sorted(
|
||||
..
|
||||
})
|
||||
) {
|
||||
arg.value = negate(&arg.value);
|
||||
if let Expression::Name(ref val) = arg.value {
|
||||
if val.value == "True" {
|
||||
// TODO: even better would be to drop the argument, as False is the default
|
||||
arg.value = Expression::Name(Box::new(Name {
|
||||
value: "False",
|
||||
lpar: vec![],
|
||||
rpar: vec![],
|
||||
}));
|
||||
arg
|
||||
} else if val.value == "False" {
|
||||
arg.value = Expression::Name(Box::new(Name {
|
||||
value: "True",
|
||||
lpar: vec![],
|
||||
rpar: vec![],
|
||||
}));
|
||||
arg
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
arg
|
||||
})
|
||||
.collect_vec()
|
||||
} else {
|
||||
|
||||
@@ -103,18 +103,17 @@ C413.py:7:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||
7 |+sorted(x, key=lambda e: e, reverse=False)
|
||||
8 8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||
9 9 | reversed(sorted(x, reverse=False))
|
||||
10 10 | reversed(sorted(x, reverse=x))
|
||||
10 10 |
|
||||
|
||||
C413.py:8:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||
|
|
||||
6 | reversed(sorted(x, reverse=True))
|
||||
7 | reversed(sorted(x, key=lambda e: e, reverse=True))
|
||||
8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||
9 | reversed(sorted(x, reverse=False))
|
||||
10 | reversed(sorted(x, reverse=x))
|
||||
|
|
||||
= help: Remove unnecessary `reversed` call
|
||||
|
|
||||
6 | reversed(sorted(x, reverse=True))
|
||||
7 | reversed(sorted(x, key=lambda e: e, reverse=True))
|
||||
8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||
9 | reversed(sorted(x, reverse=False))
|
||||
|
|
||||
= help: Remove unnecessary `reversed` call
|
||||
|
||||
ℹ Suggested fix
|
||||
5 5 | reversed(sorted(x, key=lambda e: e))
|
||||
@@ -123,8 +122,8 @@ C413.py:8:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||
8 |-reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||
8 |+sorted(x, reverse=False, key=lambda e: e)
|
||||
9 9 | reversed(sorted(x, reverse=False))
|
||||
10 10 | reversed(sorted(x, reverse=x))
|
||||
11 11 | reversed(sorted(x, reverse=not x))
|
||||
10 10 |
|
||||
11 11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
|
||||
C413.py:9:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||
|
|
||||
@@ -132,8 +131,8 @@ C413.py:9:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||
8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||
9 | reversed(sorted(x, reverse=False))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||
10 | reversed(sorted(x, reverse=x))
|
||||
11 | reversed(sorted(x, reverse=not x))
|
||||
10 |
|
||||
11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
|
|
||||
= help: Remove unnecessary `reversed` call
|
||||
|
||||
@@ -143,87 +142,46 @@ C413.py:9:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||
8 8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||
9 |-reversed(sorted(x, reverse=False))
|
||||
9 |+sorted(x, reverse=True)
|
||||
10 10 | reversed(sorted(x, reverse=x))
|
||||
11 11 | reversed(sorted(x, reverse=not x))
|
||||
12 12 |
|
||||
10 10 |
|
||||
11 11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
12 12 | reversed(sorted(i for i in range(42)))
|
||||
|
||||
C413.py:10:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||
C413.py:12:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||
|
|
||||
8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||
9 | reversed(sorted(x, reverse=False))
|
||||
10 | reversed(sorted(x, reverse=x))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||
11 | reversed(sorted(x, reverse=not x))
|
||||
|
|
||||
= help: Remove unnecessary `reversed` call
|
||||
|
||||
ℹ Suggested fix
|
||||
7 7 | reversed(sorted(x, key=lambda e: e, reverse=True))
|
||||
8 8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||
9 9 | reversed(sorted(x, reverse=False))
|
||||
10 |-reversed(sorted(x, reverse=x))
|
||||
10 |+sorted(x, reverse=not x)
|
||||
11 11 | reversed(sorted(x, reverse=not x))
|
||||
12 12 |
|
||||
13 13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
|
||||
C413.py:11:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||
|
|
||||
9 | reversed(sorted(x, reverse=False))
|
||||
10 | reversed(sorted(x, reverse=x))
|
||||
11 | reversed(sorted(x, reverse=not x))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||
12 |
|
||||
13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
|
|
||||
= help: Remove unnecessary `reversed` call
|
||||
|
||||
ℹ Suggested fix
|
||||
8 8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||
9 9 | reversed(sorted(x, reverse=False))
|
||||
10 10 | reversed(sorted(x, reverse=x))
|
||||
11 |-reversed(sorted(x, reverse=not x))
|
||||
11 |+sorted(x, reverse=x)
|
||||
12 12 |
|
||||
13 13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
14 14 | reversed(sorted(i for i in range(42)))
|
||||
|
||||
C413.py:14:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||
|
|
||||
13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
14 | reversed(sorted(i for i in range(42)))
|
||||
11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
12 | reversed(sorted(i for i in range(42)))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||
15 | reversed(sorted((i for i in range(42)), reverse=True))
|
||||
13 | reversed(sorted((i for i in range(42)), reverse=True))
|
||||
|
|
||||
= help: Remove unnecessary `reversed` call
|
||||
|
||||
ℹ Suggested fix
|
||||
11 11 | reversed(sorted(x, reverse=not x))
|
||||
12 12 |
|
||||
13 13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
14 |-reversed(sorted(i for i in range(42)))
|
||||
14 |+sorted((i for i in range(42)), reverse=True)
|
||||
15 15 | reversed(sorted((i for i in range(42)), reverse=True))
|
||||
16 16 |
|
||||
17 17 |
|
||||
9 9 | reversed(sorted(x, reverse=False))
|
||||
10 10 |
|
||||
11 11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
12 |-reversed(sorted(i for i in range(42)))
|
||||
12 |+sorted((i for i in range(42)), reverse=True)
|
||||
13 13 | reversed(sorted((i for i in range(42)), reverse=True))
|
||||
14 14 |
|
||||
15 15 |
|
||||
|
||||
C413.py:15:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||
C413.py:13:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||
|
|
||||
13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
14 | reversed(sorted(i for i in range(42)))
|
||||
15 | reversed(sorted((i for i in range(42)), reverse=True))
|
||||
11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
12 | reversed(sorted(i for i in range(42)))
|
||||
13 | reversed(sorted((i for i in range(42)), reverse=True))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||
|
|
||||
= help: Remove unnecessary `reversed` call
|
||||
|
||||
ℹ Suggested fix
|
||||
12 12 |
|
||||
13 13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
14 14 | reversed(sorted(i for i in range(42)))
|
||||
15 |-reversed(sorted((i for i in range(42)), reverse=True))
|
||||
15 |+sorted((i for i in range(42)), reverse=False)
|
||||
16 16 |
|
||||
17 17 |
|
||||
18 18 | def reversed(*args, **kwargs):
|
||||
10 10 |
|
||||
11 11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||
12 12 | reversed(sorted(i for i in range(42)))
|
||||
13 |-reversed(sorted((i for i in range(42)), reverse=True))
|
||||
13 |+sorted((i for i in range(42)), reverse=False)
|
||||
14 14 |
|
||||
15 15 |
|
||||
16 16 | def reversed(*args, **kwargs):
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use anyhow::Result;
|
||||
use anyhow::{bail, Context};
|
||||
use libcst_native::{
|
||||
self, Assert, BooleanOp, CompoundStatement, Expression, ParenthesizedNode, SimpleStatementLine,
|
||||
SimpleWhitespace, SmallStatement, Statement, TrailingWhitespace,
|
||||
SimpleWhitespace, SmallStatement, Statement, TrailingWhitespace, UnaryOperation,
|
||||
};
|
||||
|
||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||
@@ -21,7 +21,7 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::autofix::codemods::CodegenStylist;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::cst::helpers::negate;
|
||||
use crate::cst::helpers::space;
|
||||
use crate::cst::matchers::match_indented_block;
|
||||
use crate::cst::matchers::match_module;
|
||||
use crate::importer::ImportRequest;
|
||||
@@ -567,6 +567,23 @@ fn is_composite_condition(test: &Expr) -> CompositionKind {
|
||||
CompositionKind::None
|
||||
}
|
||||
|
||||
/// Negate a condition, i.e., `a` => `not a` and `not a` => `a`.
|
||||
fn negate<'a>(expression: &Expression<'a>) -> Expression<'a> {
|
||||
if let Expression::UnaryOperation(ref expression) = expression {
|
||||
if matches!(expression.operator, libcst_native::UnaryOp::Not { .. }) {
|
||||
return *expression.expression.clone();
|
||||
}
|
||||
}
|
||||
Expression::UnaryOperation(Box::new(UnaryOperation {
|
||||
operator: libcst_native::UnaryOp::Not {
|
||||
whitespace_after: space(),
|
||||
},
|
||||
expression: Box::new(expression.clone()),
|
||||
lpar: vec![],
|
||||
rpar: vec![],
|
||||
}))
|
||||
}
|
||||
|
||||
/// Propagate parentheses from a parent to a child expression, if necessary.
|
||||
///
|
||||
/// For example, when splitting:
|
||||
|
||||
@@ -256,20 +256,4 @@ mod tests {
|
||||
assert_messages!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_public_methods() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("pylint/too_many_public_methods.py"),
|
||||
&Settings {
|
||||
pylint: pylint::settings::Settings {
|
||||
max_public_methods: 7,
|
||||
..pylint::settings::Settings::default()
|
||||
},
|
||||
..Settings::for_rules(vec![Rule::TooManyPublicMethods])
|
||||
},
|
||||
)?;
|
||||
assert_messages!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ pub(crate) use subprocess_run_without_check::*;
|
||||
pub(crate) use sys_exit_alias::*;
|
||||
pub(crate) use too_many_arguments::*;
|
||||
pub(crate) use too_many_branches::*;
|
||||
pub(crate) use too_many_public_methods::*;
|
||||
pub(crate) use too_many_return_statements::*;
|
||||
pub(crate) use too_many_statements::*;
|
||||
pub(crate) use type_bivariance::*;
|
||||
@@ -102,7 +101,6 @@ mod subprocess_run_without_check;
|
||||
mod sys_exit_alias;
|
||||
mod too_many_arguments;
|
||||
mod too_many_branches;
|
||||
mod too_many_public_methods;
|
||||
mod too_many_return_statements;
|
||||
mod too_many_statements;
|
||||
mod type_bivariance;
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_semantic::analyze::visibility::{self, Visibility::Public};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for classes with too many public methods
|
||||
///
|
||||
/// By default, this rule allows up to 20 statements, as configured by the
|
||||
/// [`pylint.max-public-methods`] option.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Classes with many public methods are harder to understand
|
||||
/// and maintain.
|
||||
///
|
||||
/// Instead, consider refactoring the class into separate classes.
|
||||
///
|
||||
/// ## Example
|
||||
/// Assuming that `pylint.max-public-settings` is set to 5:
|
||||
/// ```python
|
||||
/// class Linter:
|
||||
/// def __init__(self):
|
||||
/// pass
|
||||
///
|
||||
/// def pylint(self):
|
||||
/// pass
|
||||
///
|
||||
/// def pylint_settings(self):
|
||||
/// pass
|
||||
///
|
||||
/// def flake8(self):
|
||||
/// pass
|
||||
///
|
||||
/// def flake8_settings(self):
|
||||
/// pass
|
||||
///
|
||||
/// def pydocstyle(self):
|
||||
/// pass
|
||||
///
|
||||
/// def pydocstyle_settings(self):
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// class Linter:
|
||||
/// def __init__(self):
|
||||
/// self.pylint = Pylint()
|
||||
/// self.flake8 = Flake8()
|
||||
/// self.pydocstyle = Pydocstyle()
|
||||
///
|
||||
/// def lint(self):
|
||||
/// pass
|
||||
///
|
||||
///
|
||||
/// class Pylint:
|
||||
/// def lint(self):
|
||||
/// pass
|
||||
///
|
||||
/// def settings(self):
|
||||
/// pass
|
||||
///
|
||||
///
|
||||
/// class Flake8:
|
||||
/// def lint(self):
|
||||
/// pass
|
||||
///
|
||||
/// def settings(self):
|
||||
/// pass
|
||||
///
|
||||
///
|
||||
/// class Pydocstyle:
|
||||
/// def lint(self):
|
||||
/// pass
|
||||
///
|
||||
/// def settings(self):
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// ## Options
|
||||
/// - `pylint.max-public-methods`
|
||||
#[violation]
|
||||
pub struct TooManyPublicMethods {
|
||||
methods: usize,
|
||||
max_methods: usize,
|
||||
}
|
||||
|
||||
impl Violation for TooManyPublicMethods {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let TooManyPublicMethods {
|
||||
methods,
|
||||
max_methods,
|
||||
} = self;
|
||||
format!("Too many public methods ({methods} > {max_methods})")
|
||||
}
|
||||
}
|
||||
|
||||
/// R0904
|
||||
pub(crate) fn too_many_public_methods(
|
||||
checker: &mut Checker,
|
||||
class_def: &ast::StmtClassDef,
|
||||
max_methods: usize,
|
||||
) {
|
||||
let methods = class_def
|
||||
.body
|
||||
.iter()
|
||||
.filter(|stmt| {
|
||||
stmt.as_function_def_stmt()
|
||||
.is_some_and(|node| matches!(visibility::method_visibility(node), Public))
|
||||
})
|
||||
.count();
|
||||
|
||||
if methods > max_methods {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
TooManyPublicMethods {
|
||||
methods,
|
||||
max_methods,
|
||||
},
|
||||
class_def.range(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,6 @@ pub struct Settings {
|
||||
pub max_returns: usize,
|
||||
pub max_branches: usize,
|
||||
pub max_statements: usize,
|
||||
pub max_public_methods: usize,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
@@ -53,7 +52,6 @@ impl Default for Settings {
|
||||
max_returns: 6,
|
||||
max_branches: 12,
|
||||
max_statements: 50,
|
||||
max_public_methods: 20,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pylint/mod.rs
|
||||
---
|
||||
too_many_public_methods.py:1:1: PLR0904 Too many public methods (10 > 7)
|
||||
|
|
||||
1 | / class Everything:
|
||||
2 | | foo = 1
|
||||
3 | |
|
||||
4 | | def __init__(self):
|
||||
5 | | pass
|
||||
6 | |
|
||||
7 | | def _private(self):
|
||||
8 | | pass
|
||||
9 | |
|
||||
10 | | def method1(self):
|
||||
11 | | pass
|
||||
12 | |
|
||||
13 | | def method2(self):
|
||||
14 | | pass
|
||||
15 | |
|
||||
16 | | def method3(self):
|
||||
17 | | pass
|
||||
18 | |
|
||||
19 | | def method4(self):
|
||||
20 | | pass
|
||||
21 | |
|
||||
22 | | def method5(self):
|
||||
23 | | pass
|
||||
24 | |
|
||||
25 | | def method6(self):
|
||||
26 | | pass
|
||||
27 | |
|
||||
28 | | def method7(self):
|
||||
29 | | pass
|
||||
30 | |
|
||||
31 | | def method8(self):
|
||||
32 | | pass
|
||||
33 | |
|
||||
34 | | def method9(self):
|
||||
35 | | pass
|
||||
| |____________^ PLR0904
|
||||
36 |
|
||||
37 | class Small:
|
||||
|
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ pub(crate) use mutable_class_default::*;
|
||||
pub(crate) use mutable_dataclass_default::*;
|
||||
pub(crate) use pairwise_over_zipped::*;
|
||||
pub(crate) use static_key_dict_comprehension::*;
|
||||
#[cfg(feature = "test-rules")]
|
||||
pub(crate) use test_rules::*;
|
||||
pub(crate) use unnecessary_iterable_allocation_for_first_element::*;
|
||||
#[cfg(feature = "unreachable-code")]
|
||||
pub(crate) use unreachable::*;
|
||||
@@ -29,6 +31,8 @@ mod mutable_class_default;
|
||||
mod mutable_dataclass_default;
|
||||
mod pairwise_over_zipped;
|
||||
mod static_key_dict_comprehension;
|
||||
#[cfg(feature = "test-rules")]
|
||||
mod test_rules;
|
||||
mod unnecessary_iterable_allocation_for_first_element;
|
||||
#[cfg(feature = "unreachable-code")]
|
||||
pub(crate) mod unreachable;
|
||||
|
||||
83
crates/ruff/src/rules/ruff/rules/test_rules.rs
Normal file
83
crates/ruff/src/rules/ruff/rules/test_rules.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use ruff_diagnostics::{AutofixKind, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Fake rule for testing.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Tests must pass!
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// foo
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// bar
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct StableTestRule;
|
||||
|
||||
impl Violation for StableTestRule {
|
||||
const AUTOFIX: AutofixKind = AutofixKind::None;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Hey this is a stable test rule.")
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Fake rule for testing.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Tests must pass!
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// foo
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// bar
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct PreviewTestRule;
|
||||
|
||||
impl Violation for PreviewTestRule {
|
||||
const AUTOFIX: AutofixKind = AutofixKind::None;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Hey this is a preview test rule.")
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Fake rule for testing.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Tests must pass!
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// foo
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// bar
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct NurseryTestRule;
|
||||
|
||||
impl Violation for NurseryTestRule {
|
||||
const AUTOFIX: AutofixKind = AutofixKind::None;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Hey this is a nursery test rule.")
|
||||
}
|
||||
}
|
||||
@@ -64,11 +64,12 @@ shellexpand = { workspace = true }
|
||||
similar = { workspace = true }
|
||||
strum = { workspace = true, features = [] }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing = { workspace = true, features = ["log"] }
|
||||
walkdir = { version = "2.3.2" }
|
||||
wild = { version = "2" }
|
||||
|
||||
[dev-dependencies]
|
||||
ruff = { path = "../ruff", features = ["clap", "test-rules"] }
|
||||
assert_cmd = { version = "2.0.8" }
|
||||
# Avoid writing colored snapshots when running tests from the terminal
|
||||
colored = { workspace = true, features = ["no-color"]}
|
||||
|
||||
@@ -249,16 +249,16 @@ fn show_statistics() {
|
||||
|
||||
#[test]
|
||||
fn nursery_prefix() {
|
||||
// `--select E` should detect E741, but not E225, which is in the nursery.
|
||||
let args = ["--select", "E"];
|
||||
// Should only detect RUF900, but not the unstable test rules
|
||||
let args = ["--select", "RUF9"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:1: E741 Ambiguous variable name: `I`
|
||||
-:1:1: RUF900 Hey this is a stable test rule.
|
||||
Found 1 error.
|
||||
|
||||
----- stderr -----
|
||||
@@ -267,17 +267,17 @@ fn nursery_prefix() {
|
||||
|
||||
#[test]
|
||||
fn nursery_all() {
|
||||
// `--select ALL` should detect E741, but not E225, which is in the nursery.
|
||||
// Should detect RUF900, but not the unstable test rules
|
||||
let args = ["--select", "ALL"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:1: E741 Ambiguous variable name: `I`
|
||||
-:1:1: D100 Missing docstring in public module
|
||||
-:1:1: RUF900 Hey this is a stable test rule.
|
||||
Found 2 errors.
|
||||
|
||||
----- stderr -----
|
||||
@@ -288,36 +288,35 @@ fn nursery_all() {
|
||||
|
||||
#[test]
|
||||
fn nursery_direct() {
|
||||
// `--select E225` should detect E225.
|
||||
let args = ["--select", "E225"];
|
||||
let args = ["--select", "RUF912"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:2: E225 Missing whitespace around operator
|
||||
-:1:1: RUF912 Hey this is a nursery test rule.
|
||||
Found 1 error.
|
||||
|
||||
----- stderr -----
|
||||
warning: Selection of nursery rule `E225` without the `--preview` flag is deprecated.
|
||||
warning: Selection of nursery rule `RUF912` without the `--preview` flag is deprecated.
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nursery_group_selector() {
|
||||
// Only nursery rules should be detected e.g. E225 and a warning should be displayed
|
||||
// Only nursery rules should be detected e.g. RUF912
|
||||
let args = ["--select", "NURSERY"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:1: CPY001 Missing copyright notice at top of file
|
||||
-:1:2: E225 Missing whitespace around operator
|
||||
-:1:1: RUF912 Hey this is a nursery test rule.
|
||||
Found 2 errors.
|
||||
|
||||
----- stderr -----
|
||||
@@ -327,17 +326,17 @@ fn nursery_group_selector() {
|
||||
|
||||
#[test]
|
||||
fn nursery_group_selector_preview_enabled() {
|
||||
// Only nursery rules should be detected e.g. E225 and a warning should be displayed
|
||||
// A warning should be displayed due to deprecated selector usage
|
||||
let args = ["--select", "NURSERY", "--preview"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:1: CPY001 Missing copyright notice at top of file
|
||||
-:1:2: E225 Missing whitespace around operator
|
||||
-:1:1: RUF912 Hey this is a nursery test rule.
|
||||
Found 2 errors.
|
||||
|
||||
----- stderr -----
|
||||
@@ -347,18 +346,19 @@ fn nursery_group_selector_preview_enabled() {
|
||||
|
||||
#[test]
|
||||
fn preview_enabled_prefix() {
|
||||
// E741 and E225 (preview) should both be detected
|
||||
let args = ["--select", "E", "--preview"];
|
||||
// All the RUF9XX test rules should be triggered
|
||||
let args = ["--select", "RUF9", "--preview"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:1: E741 Ambiguous variable name: `I`
|
||||
-:1:2: E225 Missing whitespace around operator
|
||||
Found 2 errors.
|
||||
-:1:1: RUF900 Hey this is a stable test rule.
|
||||
-:1:1: RUF911 Hey this is a preview test rule.
|
||||
-:1:1: RUF912 Hey this is a nursery test rule.
|
||||
Found 3 errors.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
@@ -370,15 +370,16 @@ fn preview_enabled_all() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:1: E741 Ambiguous variable name: `I`
|
||||
-:1:1: D100 Missing docstring in public module
|
||||
-:1:1: CPY001 Missing copyright notice at top of file
|
||||
-:1:2: E225 Missing whitespace around operator
|
||||
Found 4 errors.
|
||||
-:1:1: RUF900 Hey this is a stable test rule.
|
||||
-:1:1: RUF911 Hey this is a preview test rule.
|
||||
-:1:1: RUF912 Hey this is a nursery test rule.
|
||||
Found 5 errors.
|
||||
|
||||
----- stderr -----
|
||||
warning: `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible. Ignoring `one-blank-line-before-class`.
|
||||
@@ -388,16 +389,16 @@ fn preview_enabled_all() {
|
||||
|
||||
#[test]
|
||||
fn preview_enabled_direct() {
|
||||
// E225 should be detected without warning
|
||||
let args = ["--select", "E225", "--preview"];
|
||||
// Should be enabled without warning
|
||||
let args = ["--select", "RUF911", "--preview"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:2: E225 Missing whitespace around operator
|
||||
-:1:1: RUF911 Hey this is a preview test rule.
|
||||
Found 1 error.
|
||||
|
||||
----- stderr -----
|
||||
@@ -406,35 +407,35 @@ fn preview_enabled_direct() {
|
||||
|
||||
#[test]
|
||||
fn preview_disabled_direct() {
|
||||
// FURB145 is preview not nursery so selecting should be empty
|
||||
let args = ["--select", "FURB145"];
|
||||
// RUF911 is preview not nursery so selecting should be empty
|
||||
let args = ["--select", "RUF911"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("a = l[:]\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
warning: Selection `FURB145` has no effect because the `--preview` flag was not included.
|
||||
warning: Selection `RUF911` has no effect because the `--preview` flag was not included.
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preview_disabled_prefix_empty() {
|
||||
// Warns that the selection is empty since all of the CPY rules are in preview
|
||||
let args = ["--select", "CPY"];
|
||||
// Warns that the selection is empty since all of the RUF91 rules are in preview
|
||||
let args = ["--select", "RUF91"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
warning: Selection `CPY` has no effect because the `--preview` flag was not included.
|
||||
warning: Selection `RUF91` has no effect because the `--preview` flag was not included.
|
||||
"###);
|
||||
}
|
||||
|
||||
@@ -445,7 +446,7 @@ fn preview_disabled_group_selector() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -462,13 +463,14 @@ fn preview_enabled_group_selector() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:1: CPY001 Missing copyright notice at top of file
|
||||
-:1:2: E225 Missing whitespace around operator
|
||||
Found 2 errors.
|
||||
-:1:1: RUF911 Hey this is a preview test rule.
|
||||
-:1:1: RUF912 Hey this is a nursery test rule.
|
||||
Found 3 errors.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
@@ -476,18 +478,19 @@ fn preview_enabled_group_selector() {
|
||||
|
||||
#[test]
|
||||
fn preview_enabled_group_ignore() {
|
||||
// `--select E --ignore PREVIEW` should detect E741 and E225, which is in preview but "E" is more specific.
|
||||
let args = ["--select", "E", "--ignore", "PREVIEW", "--preview"];
|
||||
// Should detect stable and unstable rules, RUF9 is more specific than PREVIEW so ignore has no effect
|
||||
let args = ["--select", "RUF9", "--ignore", "PREVIEW", "--preview"];
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(args)
|
||||
.pass_stdin("I=42\n"), @r###"
|
||||
.pass_stdin(""), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:1: E741 Ambiguous variable name: `I`
|
||||
-:1:2: E225 Missing whitespace around operator
|
||||
Found 2 errors.
|
||||
-:1:1: RUF900 Hey this is a stable test rule.
|
||||
-:1:1: RUF911 Hey this is a preview test rule.
|
||||
-:1:1: RUF912 Hey this is a nursery test rule.
|
||||
Found 3 errors.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
|
||||
@@ -184,7 +184,7 @@ pub(crate) fn function_visibility(function: &ast::StmtFunctionDef) -> Visibility
|
||||
}
|
||||
}
|
||||
|
||||
pub fn method_visibility(function: &ast::StmtFunctionDef) -> Visibility {
|
||||
pub(crate) fn method_visibility(function: &ast::StmtFunctionDef) -> Visibility {
|
||||
// Is this a setter or deleter?
|
||||
if function.decorator_list.iter().any(|decorator| {
|
||||
collect_call_path(&decorator.expression).is_some_and(|call_path| {
|
||||
|
||||
@@ -851,7 +851,7 @@ mod tests {
|
||||
Rule::QuadraticListSummation,
|
||||
];
|
||||
|
||||
const PREVIEW_RULES: &[Rule] = &[Rule::TooManyPublicMethods, Rule::SliceCopy];
|
||||
const PREVIEW_RULES: &[Rule] = &[Rule::SliceCopy];
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn resolve_rules(
|
||||
|
||||
@@ -2155,13 +2155,6 @@ pub struct PylintOptions {
|
||||
/// Maximum number of statements allowed for a function or method body (see:
|
||||
/// `PLR0915`).
|
||||
pub max_statements: Option<usize>,
|
||||
#[option(
|
||||
default = r"20",
|
||||
value_type = "int",
|
||||
example = r"max-public-methods = 20"
|
||||
)]
|
||||
/// Maximum number of public methods allowed for a class (see: `PLR0904`).
|
||||
pub max_public_methods: Option<usize>,
|
||||
}
|
||||
|
||||
impl PylintOptions {
|
||||
@@ -2175,9 +2168,6 @@ impl PylintOptions {
|
||||
max_returns: self.max_returns.unwrap_or(defaults.max_returns),
|
||||
max_branches: self.max_branches.unwrap_or(defaults.max_branches),
|
||||
max_statements: self.max_statements.unwrap_or(defaults.max_statements),
|
||||
max_public_methods: self
|
||||
.max_public_methods
|
||||
.unwrap_or(defaults.max_public_methods),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,9 +394,8 @@ i = 1 # noqa: E741, F841
|
||||
x = 1 # noqa
|
||||
```
|
||||
|
||||
For multi-line strings (like docstrings),
|
||||
the `noqa` directive should come at the end of the string (after the closing triple quote),
|
||||
and will apply to the entire string, like so:
|
||||
Note that, for multi-line strings, the `noqa` directive should come at the end of the string, and
|
||||
will apply to the entire string, like so:
|
||||
|
||||
```python
|
||||
"""Lorem ipsum dolor sit amet.
|
||||
|
||||
@@ -205,11 +205,6 @@ def sum_even_numbers(numbers: List[int]) -> int:
|
||||
return sum(num for num in numbers if num % 2 == 0)
|
||||
```
|
||||
|
||||
For more in-depth instructions on ignoring errors,
|
||||
please see [_Configuration_](configuration.md#error-suppression).
|
||||
|
||||
### Adding Rules
|
||||
|
||||
When enabling a new rule on an existing codebase, you may want to ignore all _existing_
|
||||
violations of that rule and instead focus on enforcing it going forward.
|
||||
|
||||
|
||||
11
ruff.schema.json
generated
11
ruff.schema.json
generated
@@ -1612,15 +1612,6 @@
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"max-public-methods": {
|
||||
"description": "Maximum number of public methods allowed for a class (see: `PLR0904`).",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"max-returns": {
|
||||
"description": "Maximum number of return statements allowed for a function or method body (see `PLR0911`)",
|
||||
"type": [
|
||||
@@ -2305,8 +2296,6 @@
|
||||
"PLR040",
|
||||
"PLR0402",
|
||||
"PLR09",
|
||||
"PLR090",
|
||||
"PLR0904",
|
||||
"PLR091",
|
||||
"PLR0911",
|
||||
"PLR0912",
|
||||
|
||||
Reference in New Issue
Block a user