mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-30 00:13:48 +00:00
refactor: use remove_argument helper in pyupgrade
This commit is contained in:
parent
d76a47d366
commit
63fc912ed8
9 changed files with 521 additions and 271 deletions
|
@ -14,3 +14,60 @@ def ok_other_scope():
|
|||
@pytest.fixture(scope="function")
|
||||
def error():
|
||||
...
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", name="my_fixture")
|
||||
def error_multiple_args():
|
||||
...
|
||||
|
||||
|
||||
@pytest.fixture(name="my_fixture", scope="function")
|
||||
def error_multiple_args():
|
||||
...
|
||||
|
||||
|
||||
@pytest.fixture(name="my_fixture", scope="function", **kwargs)
|
||||
def error_second_arg():
|
||||
...
|
||||
|
||||
|
||||
# pytest.fixture does not take positional arguments, however this
|
||||
# tests the general case as we use a helper function that should
|
||||
# work for all cases.
|
||||
@pytest.fixture("my_fixture", scope="function")
|
||||
def error_arg():
|
||||
...
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="function",
|
||||
name="my_fixture",
|
||||
)
|
||||
def error_multiple_args():
|
||||
...
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
name="my_fixture",
|
||||
scope="function",
|
||||
)
|
||||
def error_multiple_args():
|
||||
...
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
"hello",
|
||||
name,
|
||||
*args
|
||||
,
|
||||
|
||||
# another comment ,)
|
||||
|
||||
scope=\
|
||||
"function" # some comment ),
|
||||
,
|
||||
|
||||
name2=name, name3="my_fixture", **kwargs
|
||||
)
|
||||
def error_multiple_args():
|
||||
...
|
||||
|
|
|
@ -3,7 +3,9 @@ use itertools::Itertools;
|
|||
use libcst_native::{
|
||||
Codegen, CodegenState, ImportNames, ParenthesizableWhitespace, SmallStatement, Statement,
|
||||
};
|
||||
use rustpython_parser::ast::{ExcepthandlerKind, Location, Stmt, StmtKind};
|
||||
use rustpython_parser::ast::{ExcepthandlerKind, Expr, Keyword, Location, Stmt, StmtKind};
|
||||
use rustpython_parser::lexer;
|
||||
use rustpython_parser::lexer::Tok;
|
||||
|
||||
use crate::ast::helpers;
|
||||
use crate::ast::helpers::to_absolute;
|
||||
|
@ -321,6 +323,108 @@ pub fn remove_unused_imports<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Generic function te remove (keyword)arguments in function calls
|
||||
/// and class definitions. (For classes `args` should be considered `bases`)
|
||||
///
|
||||
/// Supports the removal of parentheses when this is the only (kw)arg left.
|
||||
/// For this behaviour set `remove_parentheses` to `true`.
|
||||
pub fn remove_argument(
|
||||
locator: &Locator,
|
||||
stmt_at: Location,
|
||||
expr_at: Location,
|
||||
expr_end: Location,
|
||||
args: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
remove_parentheses: bool,
|
||||
) -> Result<Fix> {
|
||||
// TODO: preserve trailing comments
|
||||
let contents = locator.slice_source_code_at(stmt_at);
|
||||
|
||||
let mut fix_start = None;
|
||||
let mut fix_end = None;
|
||||
|
||||
let n_keywords = keywords.len() + args.len();
|
||||
if n_keywords == 0 {
|
||||
bail!("No arguments or keywords to remove");
|
||||
}
|
||||
|
||||
if n_keywords == 1 {
|
||||
// Case 1: there is only one argument.
|
||||
let mut count: usize = 0;
|
||||
for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt_at).flatten() {
|
||||
if matches!(tok, Tok::Lpar) {
|
||||
if count == 0 {
|
||||
fix_start = Some(if remove_parentheses {
|
||||
start
|
||||
} else {
|
||||
Location::new(start.row(), start.column() + 1)
|
||||
})
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if matches!(tok, Tok::Rpar) {
|
||||
count -= 1;
|
||||
if count == 0 {
|
||||
fix_end = Some(if remove_parentheses {
|
||||
end
|
||||
} else {
|
||||
Location::new(end.row(), end.column() - 1)
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if args
|
||||
.iter()
|
||||
.map(|node| node.location)
|
||||
.chain(keywords.iter().map(|node| node.location))
|
||||
.any(|location| location > expr_at)
|
||||
{
|
||||
// Case 2: argument or keyword is _not_ the last node.
|
||||
let mut seen_comma = false;
|
||||
for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt_at).flatten() {
|
||||
if seen_comma {
|
||||
if matches!(tok, Tok::NonLogicalNewline) {
|
||||
// Also delete any non-logical newlines after the comma.
|
||||
continue;
|
||||
}
|
||||
fix_end = Some(if matches!(tok, Tok::Newline) {
|
||||
end
|
||||
} else {
|
||||
start
|
||||
});
|
||||
break;
|
||||
}
|
||||
if start == expr_at {
|
||||
fix_start = Some(start);
|
||||
}
|
||||
if fix_start.is_some() && matches!(tok, Tok::Comma) {
|
||||
seen_comma = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Case 3: argument or keyword is the last node, so we have to find the last
|
||||
// comma in the stmt.
|
||||
for (start, tok, _) in lexer::make_tokenizer_located(contents, stmt_at).flatten() {
|
||||
if start == expr_at {
|
||||
fix_end = Some(expr_end);
|
||||
break;
|
||||
}
|
||||
if matches!(tok, Tok::Comma) {
|
||||
fix_start = Some(start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match (fix_start, fix_end) {
|
||||
(Some(start), Some(end)) => Ok(Fix::deletion(start, end)),
|
||||
_ => {
|
||||
bail!("No fix could be constructed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
|
|
|
@ -386,11 +386,11 @@ ruff_macros::define_rule_mapping!(
|
|||
EM102 => violations::FStringInException,
|
||||
EM103 => violations::DotFormatInException,
|
||||
// flake8-pytest-style
|
||||
PT001 => violations::IncorrectFixtureParenthesesStyle,
|
||||
PT002 => violations::FixturePositionalArgs,
|
||||
PT003 => violations::ExtraneousScopeFunction,
|
||||
PT004 => violations::MissingFixtureNameUnderscore,
|
||||
PT005 => violations::IncorrectFixtureNameUnderscore,
|
||||
PT001 => rules::flake8_pytest_style::rules::IncorrectFixtureParenthesesStyle,
|
||||
PT002 => rules::flake8_pytest_style::rules::FixturePositionalArgs,
|
||||
PT003 => rules::flake8_pytest_style::rules::ExtraneousScopeFunction,
|
||||
PT004 => rules::flake8_pytest_style::rules::MissingFixtureNameUnderscore,
|
||||
PT005 => rules::flake8_pytest_style::rules::IncorrectFixtureNameUnderscore,
|
||||
PT006 => violations::ParametrizeNamesWrongType,
|
||||
PT007 => violations::ParametrizeValuesWrongType,
|
||||
PT008 => violations::PatchWithLambda,
|
||||
|
@ -403,13 +403,13 @@ ruff_macros::define_rule_mapping!(
|
|||
PT016 => violations::FailWithoutMessage,
|
||||
PT017 => violations::AssertInExcept,
|
||||
PT018 => violations::CompositeAssertion,
|
||||
PT019 => violations::FixtureParamWithoutValue,
|
||||
PT020 => violations::DeprecatedYieldFixture,
|
||||
PT021 => violations::FixtureFinalizerCallback,
|
||||
PT022 => violations::UselessYieldFixture,
|
||||
PT019 => rules::flake8_pytest_style::rules::FixtureParamWithoutValue,
|
||||
PT020 => rules::flake8_pytest_style::rules::DeprecatedYieldFixture,
|
||||
PT021 => rules::flake8_pytest_style::rules::FixtureFinalizerCallback,
|
||||
PT022 => rules::flake8_pytest_style::rules::UselessYieldFixture,
|
||||
PT023 => violations::IncorrectMarkParenthesesStyle,
|
||||
PT024 => violations::UnnecessaryAsyncioMarkOnFixture,
|
||||
PT025 => violations::ErroneousUseFixturesOnFixture,
|
||||
PT024 => rules::flake8_pytest_style::rules::UnnecessaryAsyncioMarkOnFixture,
|
||||
PT025 => rules::flake8_pytest_style::rules::ErroneousUseFixturesOnFixture,
|
||||
PT026 => violations::UseFixturesWithoutParameters,
|
||||
// flake8-pie
|
||||
PIE790 => rules::flake8_pie::rules::NoUnnecessaryPass,
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use rustpython_ast::{Arguments, Expr, ExprKind, Location, Stmt, StmtKind};
|
||||
use anyhow::Result;
|
||||
use log::error;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Arguments, Expr, ExprKind, Keyword, Location, Stmt, StmtKind};
|
||||
|
||||
use super::helpers::{
|
||||
get_mark_decorators, get_mark_name, is_abstractmethod_decorator, is_pytest_fixture,
|
||||
|
@ -8,10 +11,164 @@ use crate::ast::helpers::{collect_arg_names, collect_call_path};
|
|||
use crate::ast::types::Range;
|
||||
use crate::ast::visitor;
|
||||
use crate::ast::visitor::Visitor;
|
||||
use crate::autofix::helpers::remove_argument;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::violations;
|
||||
use crate::source_code::Locator;
|
||||
use crate::violation::{AlwaysAutofixableViolation, Violation};
|
||||
|
||||
define_violation!(
|
||||
pub struct IncorrectFixtureParenthesesStyle {
|
||||
pub expected_parens: String,
|
||||
pub actual_parens: String,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for IncorrectFixtureParenthesesStyle {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let IncorrectFixtureParenthesesStyle {
|
||||
expected_parens,
|
||||
actual_parens,
|
||||
} = self;
|
||||
format!("Use `@pytest.fixture{expected_parens}` over `@pytest.fixture{actual_parens}`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Add/remove parentheses".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct FixturePositionalArgs {
|
||||
pub function: String,
|
||||
}
|
||||
);
|
||||
impl Violation for FixturePositionalArgs {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let FixturePositionalArgs { function } = self;
|
||||
format!("Configuration for fixture `{function}` specified via positional args, use kwargs")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ExtraneousScopeFunction;
|
||||
);
|
||||
impl Violation for ExtraneousScopeFunction {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`scope='function'` is implied in `@pytest.fixture()`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct MissingFixtureNameUnderscore {
|
||||
pub function: String,
|
||||
}
|
||||
);
|
||||
impl Violation for MissingFixtureNameUnderscore {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let MissingFixtureNameUnderscore { function } = self;
|
||||
format!("Fixture `{function}` does not return anything, add leading underscore")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct IncorrectFixtureNameUnderscore {
|
||||
pub function: String,
|
||||
}
|
||||
);
|
||||
impl Violation for IncorrectFixtureNameUnderscore {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let IncorrectFixtureNameUnderscore { function } = self;
|
||||
format!("Fixture `{function}` returns a value, remove leading underscore")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct FixtureParamWithoutValue {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for FixtureParamWithoutValue {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let FixtureParamWithoutValue { name } = self;
|
||||
format!(
|
||||
"Fixture `{name}` without value is injected as parameter, use \
|
||||
`@pytest.mark.usefixtures` instead"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct DeprecatedYieldFixture;
|
||||
);
|
||||
impl Violation for DeprecatedYieldFixture {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`@pytest.yield_fixture` is deprecated, use `@pytest.fixture`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct FixtureFinalizerCallback;
|
||||
);
|
||||
impl Violation for FixtureFinalizerCallback {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Use `yield` instead of `request.addfinalizer`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct UselessYieldFixture {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for UselessYieldFixture {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UselessYieldFixture { name } = self;
|
||||
format!("No teardown in fixture `{name}`, use `return` instead of `yield`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Replace `yield` with `return`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ErroneousUseFixturesOnFixture;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for ErroneousUseFixturesOnFixture {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`pytest.mark.usefixtures` has no effect on fixtures")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Remove `pytest.mark.usefixtures`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct UnnecessaryAsyncioMarkOnFixture;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for UnnecessaryAsyncioMarkOnFixture {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`pytest.mark.asyncio` is unnecessary for fixtures")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Remove `pytest.mark.asyncio`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
/// Visitor that skips functions
|
||||
|
@ -80,7 +237,7 @@ fn pytest_fixture_parentheses(
|
|||
actual: &str,
|
||||
) {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::IncorrectFixtureParenthesesStyle {
|
||||
IncorrectFixtureParenthesesStyle {
|
||||
expected_parens: preferred.to_string(),
|
||||
actual_parens: actual.to_string(),
|
||||
},
|
||||
|
@ -92,6 +249,17 @@ fn pytest_fixture_parentheses(
|
|||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
pub fn fix_extraneous_scope_function(
|
||||
locator: &Locator,
|
||||
stmt_at: Location,
|
||||
expr_at: Location,
|
||||
expr_end: Location,
|
||||
args: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
) -> Result<Fix> {
|
||||
remove_argument(locator, stmt_at, expr_at, expr_end, args, keywords, false)
|
||||
}
|
||||
|
||||
/// PT001, PT002, PT003
|
||||
fn check_fixture_decorator(checker: &mut Checker, func_name: &str, decorator: &Expr) {
|
||||
match &decorator.node {
|
||||
|
@ -116,7 +284,7 @@ fn check_fixture_decorator(checker: &mut Checker, func_name: &str, decorator: &E
|
|||
|
||||
if checker.settings.rules.enabled(&Rule::FixturePositionalArgs) && !args.is_empty() {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::FixturePositionalArgs {
|
||||
FixturePositionalArgs {
|
||||
function: func_name.to_string(),
|
||||
},
|
||||
Range::from_located(decorator),
|
||||
|
@ -134,10 +302,26 @@ fn check_fixture_decorator(checker: &mut Checker, func_name: &str, decorator: &E
|
|||
|
||||
if let Some(scope_keyword) = scope_keyword {
|
||||
if keyword_is_literal(scope_keyword, "function") {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::ExtraneousScopeFunction,
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
ExtraneousScopeFunction,
|
||||
Range::from_located(scope_keyword),
|
||||
));
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
match fix_extraneous_scope_function(
|
||||
checker.locator,
|
||||
decorator.location,
|
||||
diagnostic.location,
|
||||
diagnostic.end_location,
|
||||
args,
|
||||
keywords,
|
||||
) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
}
|
||||
Err(e) => error!("Failed to generate fix: {e}"),
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +356,7 @@ fn check_fixture_returns(checker: &mut Checker, func: &Stmt, func_name: &str, bo
|
|||
&& func_name.starts_with('_')
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::IncorrectFixtureNameUnderscore {
|
||||
IncorrectFixtureNameUnderscore {
|
||||
function: func_name.to_string(),
|
||||
},
|
||||
Range::from_located(func),
|
||||
|
@ -186,7 +370,7 @@ fn check_fixture_returns(checker: &mut Checker, func: &Stmt, func_name: &str, bo
|
|||
&& !func_name.starts_with('_')
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingFixtureNameUnderscore {
|
||||
MissingFixtureNameUnderscore {
|
||||
function: func_name.to_string(),
|
||||
},
|
||||
Range::from_located(func),
|
||||
|
@ -199,7 +383,7 @@ fn check_fixture_returns(checker: &mut Checker, func: &Stmt, func_name: &str, bo
|
|||
if let ExprKind::Yield { .. } = value.node {
|
||||
if visitor.yield_statements.len() == 1 {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::UselessYieldFixture {
|
||||
UselessYieldFixture {
|
||||
name: func_name.to_string(),
|
||||
},
|
||||
Range::from_located(stmt),
|
||||
|
@ -228,7 +412,7 @@ fn check_test_function_args(checker: &mut Checker, args: &Arguments) {
|
|||
let name = &arg.node.arg;
|
||||
if name.starts_with('_') {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::FixtureParamWithoutValue {
|
||||
FixtureParamWithoutValue {
|
||||
name: name.to_string(),
|
||||
},
|
||||
Range::from_located(arg),
|
||||
|
@ -241,7 +425,7 @@ fn check_test_function_args(checker: &mut Checker, args: &Arguments) {
|
|||
fn check_fixture_decorator_name(checker: &mut Checker, decorator: &Expr) {
|
||||
if is_pytest_yield_fixture(decorator, checker) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::DeprecatedYieldFixture,
|
||||
DeprecatedYieldFixture,
|
||||
Range::from_located(decorator),
|
||||
));
|
||||
}
|
||||
|
@ -261,7 +445,7 @@ fn check_fixture_addfinalizer(checker: &mut Checker, args: &Arguments, body: &[S
|
|||
|
||||
if let Some(addfinalizer) = visitor.addfinalizer_call {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::FixtureFinalizerCallback,
|
||||
FixtureFinalizerCallback,
|
||||
Range::from_located(addfinalizer),
|
||||
));
|
||||
}
|
||||
|
@ -278,10 +462,8 @@ fn check_fixture_marks(checker: &mut Checker, decorators: &[Expr]) {
|
|||
.enabled(&Rule::UnnecessaryAsyncioMarkOnFixture)
|
||||
{
|
||||
if name == "asyncio" {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::UnnecessaryAsyncioMarkOnFixture,
|
||||
Range::from_located(mark),
|
||||
);
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(UnnecessaryAsyncioMarkOnFixture, Range::from_located(mark));
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let start = Location::new(mark.location.row(), 0);
|
||||
let end = Location::new(mark.end_location.unwrap().row() + 1, 0);
|
||||
|
@ -297,10 +479,8 @@ fn check_fixture_marks(checker: &mut Checker, decorators: &[Expr]) {
|
|||
.enabled(&Rule::ErroneousUseFixturesOnFixture)
|
||||
{
|
||||
if name == "usefixtures" {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::ErroneousUseFixturesOnFixture,
|
||||
Range::from_located(mark),
|
||||
);
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(ErroneousUseFixturesOnFixture, Range::from_located(mark));
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let start = Location::new(mark.location.row(), 0);
|
||||
let end = Location::new(mark.end_location.unwrap().row() + 1, 0);
|
||||
|
|
|
@ -2,7 +2,12 @@ pub use assertion::{
|
|||
assert_falsy, assert_in_exception_handler, composite_condition, unittest_assertion,
|
||||
};
|
||||
pub use fail::fail_call;
|
||||
pub use fixture::fixture;
|
||||
pub use fixture::{
|
||||
fixture, DeprecatedYieldFixture, ErroneousUseFixturesOnFixture, ExtraneousScopeFunction,
|
||||
FixtureFinalizerCallback, FixtureParamWithoutValue, FixturePositionalArgs,
|
||||
IncorrectFixtureNameUnderscore, IncorrectFixtureParenthesesStyle, MissingFixtureNameUnderscore,
|
||||
UnnecessaryAsyncioMarkOnFixture, UselessYieldFixture,
|
||||
};
|
||||
pub use imports::{import, import_from};
|
||||
pub use marks::marks;
|
||||
pub use parametrize::parametrize;
|
||||
|
|
|
@ -10,6 +10,140 @@ expression: diagnostics
|
|||
end_location:
|
||||
row: 14
|
||||
column: 32
|
||||
fix: ~
|
||||
fix:
|
||||
content:
|
||||
- ""
|
||||
location:
|
||||
row: 14
|
||||
column: 16
|
||||
end_location:
|
||||
row: 14
|
||||
column: 32
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousScopeFunction: ~
|
||||
location:
|
||||
row: 19
|
||||
column: 16
|
||||
end_location:
|
||||
row: 19
|
||||
column: 32
|
||||
fix:
|
||||
content:
|
||||
- ""
|
||||
location:
|
||||
row: 19
|
||||
column: 16
|
||||
end_location:
|
||||
row: 19
|
||||
column: 34
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousScopeFunction: ~
|
||||
location:
|
||||
row: 24
|
||||
column: 35
|
||||
end_location:
|
||||
row: 24
|
||||
column: 51
|
||||
fix:
|
||||
content:
|
||||
- ""
|
||||
location:
|
||||
row: 24
|
||||
column: 33
|
||||
end_location:
|
||||
row: 24
|
||||
column: 51
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousScopeFunction: ~
|
||||
location:
|
||||
row: 29
|
||||
column: 35
|
||||
end_location:
|
||||
row: 29
|
||||
column: 51
|
||||
fix:
|
||||
content:
|
||||
- ""
|
||||
location:
|
||||
row: 29
|
||||
column: 35
|
||||
end_location:
|
||||
row: 29
|
||||
column: 53
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousScopeFunction: ~
|
||||
location:
|
||||
row: 37
|
||||
column: 30
|
||||
end_location:
|
||||
row: 37
|
||||
column: 46
|
||||
fix:
|
||||
content:
|
||||
- ""
|
||||
location:
|
||||
row: 37
|
||||
column: 28
|
||||
end_location:
|
||||
row: 37
|
||||
column: 46
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousScopeFunction: ~
|
||||
location:
|
||||
row: 43
|
||||
column: 4
|
||||
end_location:
|
||||
row: 43
|
||||
column: 20
|
||||
fix:
|
||||
content:
|
||||
- ""
|
||||
location:
|
||||
row: 43
|
||||
column: 4
|
||||
end_location:
|
||||
row: 44
|
||||
column: 4
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousScopeFunction: ~
|
||||
location:
|
||||
row: 52
|
||||
column: 4
|
||||
end_location:
|
||||
row: 52
|
||||
column: 20
|
||||
fix:
|
||||
content:
|
||||
- ""
|
||||
location:
|
||||
row: 51
|
||||
column: 21
|
||||
end_location:
|
||||
row: 52
|
||||
column: 20
|
||||
parent: ~
|
||||
- kind:
|
||||
ExtraneousScopeFunction: ~
|
||||
location:
|
||||
row: 66
|
||||
column: 4
|
||||
end_location:
|
||||
row: 67
|
||||
column: 18
|
||||
fix:
|
||||
content:
|
||||
- ""
|
||||
location:
|
||||
row: 66
|
||||
column: 4
|
||||
end_location:
|
||||
row: 70
|
||||
column: 4
|
||||
parent: ~
|
||||
|
||||
|
|
|
@ -2,10 +2,9 @@ use libcst_native::{
|
|||
Codegen, CodegenState, Expression, ParenthesizableWhitespace, SmallStatement, Statement,
|
||||
};
|
||||
use rustpython_ast::{Expr, Keyword, Location};
|
||||
use rustpython_parser::lexer;
|
||||
use rustpython_parser::lexer::Tok;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::helpers::remove_argument;
|
||||
use crate::fix::Fix;
|
||||
use crate::source_code::{Locator, Stylist};
|
||||
|
||||
|
@ -14,93 +13,14 @@ pub fn remove_class_def_base(
|
|||
locator: &Locator,
|
||||
stmt_at: Location,
|
||||
expr_at: Location,
|
||||
expr_end: Location,
|
||||
bases: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
) -> Option<Fix> {
|
||||
let contents = locator.slice_source_code_at(stmt_at);
|
||||
|
||||
// Case 1: `object` is the only base.
|
||||
if bases.len() == 1 && keywords.is_empty() {
|
||||
let mut fix_start = None;
|
||||
let mut fix_end = None;
|
||||
let mut count: usize = 0;
|
||||
for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt_at).flatten() {
|
||||
if matches!(tok, Tok::Lpar) {
|
||||
if count == 0 {
|
||||
fix_start = Some(start);
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if matches!(tok, Tok::Rpar) {
|
||||
count -= 1;
|
||||
if count == 0 {
|
||||
fix_end = Some(end);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return match (fix_start, fix_end) {
|
||||
(Some(start), Some(end)) => Some(Fix::deletion(start, end)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if bases
|
||||
.iter()
|
||||
.map(|node| node.location)
|
||||
.chain(keywords.iter().map(|node| node.location))
|
||||
.any(|location| location > expr_at)
|
||||
{
|
||||
// Case 2: `object` is _not_ the last node.
|
||||
let mut fix_start: Option<Location> = None;
|
||||
let mut fix_end: Option<Location> = None;
|
||||
let mut seen_comma = false;
|
||||
for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt_at).flatten() {
|
||||
if seen_comma {
|
||||
if matches!(tok, Tok::NonLogicalNewline) {
|
||||
// Also delete any non-logical newlines after the comma.
|
||||
continue;
|
||||
}
|
||||
if matches!(tok, Tok::Newline) {
|
||||
fix_end = Some(end);
|
||||
} else {
|
||||
fix_end = Some(start);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if start == expr_at {
|
||||
fix_start = Some(start);
|
||||
}
|
||||
if fix_start.is_some() && matches!(tok, Tok::Comma) {
|
||||
seen_comma = true;
|
||||
}
|
||||
}
|
||||
|
||||
match (fix_start, fix_end) {
|
||||
(Some(start), Some(end)) => Some(Fix::deletion(start, end)),
|
||||
_ => None,
|
||||
}
|
||||
if let Ok(fix) = remove_argument(locator, stmt_at, expr_at, expr_end, bases, keywords, true) {
|
||||
Some(fix)
|
||||
} else {
|
||||
// Case 3: `object` is the last node, so we have to find the last token that
|
||||
// isn't a comma.
|
||||
let mut fix_start: Option<Location> = None;
|
||||
let mut fix_end: Option<Location> = None;
|
||||
for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt_at).flatten() {
|
||||
if start == expr_at {
|
||||
fix_end = Some(end);
|
||||
break;
|
||||
}
|
||||
if matches!(tok, Tok::Comma) {
|
||||
fix_start = Some(start);
|
||||
}
|
||||
}
|
||||
|
||||
match (fix_start, fix_end) {
|
||||
(Some(start), Some(end)) => Some(Fix::deletion(start, end)),
|
||||
_ => None,
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ pub fn useless_object_inheritance(
|
|||
checker.locator,
|
||||
stmt.location,
|
||||
diagnostic.location,
|
||||
diagnostic.end_location,
|
||||
bases,
|
||||
keywords,
|
||||
) {
|
||||
|
|
|
@ -4684,76 +4684,6 @@ impl Violation for DotFormatInException {
|
|||
|
||||
// flake8-pytest-style
|
||||
|
||||
define_violation!(
|
||||
pub struct IncorrectFixtureParenthesesStyle {
|
||||
pub expected_parens: String,
|
||||
pub actual_parens: String,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for IncorrectFixtureParenthesesStyle {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let IncorrectFixtureParenthesesStyle {
|
||||
expected_parens,
|
||||
actual_parens,
|
||||
} = self;
|
||||
format!("Use `@pytest.fixture{expected_parens}` over `@pytest.fixture{actual_parens}`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Add/remove parentheses".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct FixturePositionalArgs {
|
||||
pub function: String,
|
||||
}
|
||||
);
|
||||
impl Violation for FixturePositionalArgs {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let FixturePositionalArgs { function } = self;
|
||||
format!("Configuration for fixture `{function}` specified via positional args, use kwargs")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ExtraneousScopeFunction;
|
||||
);
|
||||
impl Violation for ExtraneousScopeFunction {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`scope='function'` is implied in `@pytest.fixture()`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct MissingFixtureNameUnderscore {
|
||||
pub function: String,
|
||||
}
|
||||
);
|
||||
impl Violation for MissingFixtureNameUnderscore {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let MissingFixtureNameUnderscore { function } = self;
|
||||
format!("Fixture `{function}` does not return anything, add leading underscore")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct IncorrectFixtureNameUnderscore {
|
||||
pub function: String,
|
||||
}
|
||||
);
|
||||
impl Violation for IncorrectFixtureNameUnderscore {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let IncorrectFixtureNameUnderscore { function } = self;
|
||||
format!("Fixture `{function}` returns a value, remove leading underscore")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ParametrizeNamesWrongType {
|
||||
pub expected: ParametrizeNameType,
|
||||
|
@ -4905,59 +4835,6 @@ impl Violation for CompositeAssertion {
|
|||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct FixtureParamWithoutValue {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for FixtureParamWithoutValue {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let FixtureParamWithoutValue { name } = self;
|
||||
format!(
|
||||
"Fixture `{name}` without value is injected as parameter, use \
|
||||
`@pytest.mark.usefixtures` instead"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct DeprecatedYieldFixture;
|
||||
);
|
||||
impl Violation for DeprecatedYieldFixture {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`@pytest.yield_fixture` is deprecated, use `@pytest.fixture`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct FixtureFinalizerCallback;
|
||||
);
|
||||
impl Violation for FixtureFinalizerCallback {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Use `yield` instead of `request.addfinalizer`")
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct UselessYieldFixture {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for UselessYieldFixture {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UselessYieldFixture { name } = self;
|
||||
format!("No teardown in fixture `{name}`, use `return` instead of `yield`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Replace `yield` with `return`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct IncorrectMarkParenthesesStyle {
|
||||
pub mark_name: String,
|
||||
|
@ -4984,34 +4861,6 @@ impl AlwaysAutofixableViolation for IncorrectMarkParenthesesStyle {
|
|||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct UnnecessaryAsyncioMarkOnFixture;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for UnnecessaryAsyncioMarkOnFixture {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`pytest.mark.asyncio` is unnecessary for fixtures")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Remove `pytest.mark.asyncio`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct ErroneousUseFixturesOnFixture;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for ErroneousUseFixturesOnFixture {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`pytest.mark.usefixtures` has no effect on fixtures")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Remove `pytest.mark.usefixtures`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
define_violation!(
|
||||
pub struct UseFixturesWithoutParameters;
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue