refactor: use remove_argument helper in pyupgrade

This commit is contained in:
Simon Brugman 2023-01-27 18:49:40 +01:00 committed by Charlie Marsh
parent d76a47d366
commit 63fc912ed8
9 changed files with 521 additions and 271 deletions

View file

@ -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():
...

View file

@ -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;

View file

@ -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,

View file

@ -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);

View file

@ -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;

View file

@ -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: ~

View file

@ -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
}
}

View file

@ -53,6 +53,7 @@ pub fn useless_object_inheritance(
checker.locator,
stmt.location,
diagnostic.location,
diagnostic.end_location,
bases,
keywords,
) {

View file

@ -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;
);