mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-24 05:25:17 +00:00
Misc. stylistic changes from flipping through rules late at night (#5757)
## Summary This is really bad PR hygiene, but a mix of: using `Locator`-based fixes in a few places (in lieu of `Generator`-based fixes), using match syntax to avoid `.len() == 1` checks, using common helpers in more places, etc. ## Test Plan `cargo test`
This commit is contained in:
parent
875e04e369
commit
5a4516b812
22 changed files with 127 additions and 169 deletions
|
@ -59,7 +59,6 @@ field18: typing.Union[
|
||||||
],
|
],
|
||||||
] # Error, newline and comment will not be emitted in message
|
] # Error, newline and comment will not be emitted in message
|
||||||
|
|
||||||
|
|
||||||
# Should emit in cases with `typing.Union` instead of `|`
|
# Should emit in cases with `typing.Union` instead of `|`
|
||||||
field19: typing.Union[int, int] # Error
|
field19: typing.Union[int, int] # Error
|
||||||
|
|
||||||
|
@ -71,3 +70,7 @@ field21: typing.Union[int, int | str] # Error
|
||||||
|
|
||||||
# Should emit only once in cases with multiple nested `typing.Union`
|
# Should emit only once in cases with multiple nested `typing.Union`
|
||||||
field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
||||||
|
|
||||||
|
# Should emit in cases with newlines
|
||||||
|
field23: set[ # foo
|
||||||
|
int] | set[int]
|
||||||
|
|
|
@ -51,11 +51,9 @@ pub(crate) fn variable_name_task_id(
|
||||||
value: &Expr,
|
value: &Expr,
|
||||||
) -> Option<Diagnostic> {
|
) -> Option<Diagnostic> {
|
||||||
// If we have more than one target, we can't do anything.
|
// If we have more than one target, we can't do anything.
|
||||||
if targets.len() != 1 {
|
let [target] = targets else {
|
||||||
return None;
|
return None;
|
||||||
}
|
};
|
||||||
|
|
||||||
let target = &targets[0];
|
|
||||||
let Expr::Name(ast::ExprName { id, .. }) = target else {
|
let Expr::Name(ast::ExprName { id, .. }) = target else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@ use rustpython_parser::ast::{self, Expr, Operator, Ranged};
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::any_over_expr;
|
use ruff_python_ast::helpers::any_over_expr;
|
||||||
|
use ruff_python_semantic::SemanticModel;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ fn matches_sql_statement(string: &str) -> bool {
|
||||||
SQL_REGEX.is_match(string)
|
SQL_REGEX.is_match(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unparse_string_format_expression(checker: &mut Checker, expr: &Expr) -> Option<String> {
|
fn matches_string_format_expression(expr: &Expr, model: &SemanticModel) -> bool {
|
||||||
match expr {
|
match expr {
|
||||||
// "select * from table where val = " + "str" + ...
|
// "select * from table where val = " + "str" + ...
|
||||||
// "select * from table where val = %s" % ...
|
// "select * from table where val = %s" % ...
|
||||||
|
@ -60,45 +61,37 @@ fn unparse_string_format_expression(checker: &mut Checker, expr: &Expr) -> Optio
|
||||||
op: Operator::Add | Operator::Mod,
|
op: Operator::Add | Operator::Mod,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
let Some(parent) = checker.semantic().expr_parent() else {
|
|
||||||
if any_over_expr(expr, &has_string_literal) {
|
|
||||||
return Some(checker.generator().expr(expr));
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
// Only evaluate the full BinOp, not the nested components.
|
// Only evaluate the full BinOp, not the nested components.
|
||||||
let Expr::BinOp(_) = parent else {
|
if model
|
||||||
|
.expr_parent()
|
||||||
|
.map_or(true, |parent| !parent.is_bin_op_expr())
|
||||||
|
{
|
||||||
if any_over_expr(expr, &has_string_literal) {
|
if any_over_expr(expr, &has_string_literal) {
|
||||||
return Some(checker.generator().expr(expr));
|
return true;
|
||||||
}
|
}
|
||||||
return None;
|
}
|
||||||
};
|
false
|
||||||
None
|
|
||||||
}
|
}
|
||||||
Expr::Call(ast::ExprCall { func, .. }) => {
|
Expr::Call(ast::ExprCall { func, .. }) => {
|
||||||
let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func.as_ref() else {
|
let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func.as_ref() else {
|
||||||
return None;
|
return false;
|
||||||
};
|
};
|
||||||
// "select * from table where val = {}".format(...)
|
// "select * from table where val = {}".format(...)
|
||||||
if attr == "format" && string_literal(value).is_some() {
|
attr == "format" && string_literal(value).is_some()
|
||||||
return Some(checker.generator().expr(expr));
|
|
||||||
};
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
// f"select * from table where val = {val}"
|
// f"select * from table where val = {val}"
|
||||||
Expr::JoinedStr(_) => Some(checker.generator().expr(expr)),
|
Expr::JoinedStr(_) => true,
|
||||||
_ => None,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// S608
|
/// S608
|
||||||
pub(crate) fn hardcoded_sql_expression(checker: &mut Checker, expr: &Expr) {
|
pub(crate) fn hardcoded_sql_expression(checker: &mut Checker, expr: &Expr) {
|
||||||
match unparse_string_format_expression(checker, expr) {
|
if matches_string_format_expression(expr, checker.semantic()) {
|
||||||
Some(string) if matches_sql_statement(&string) => {
|
if matches_sql_statement(&checker.generator().expr(expr)) {
|
||||||
checker
|
checker
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.push(Diagnostic::new(HardcodedSQLExpression, expr.range()));
|
.push(Diagnostic::new(HardcodedSQLExpression, expr.range()));
|
||||||
}
|
}
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,12 +47,12 @@ impl Violation for AssignmentToOsEnviron {
|
||||||
format!("Assigning to `os.environ` doesn't clear the environment")
|
format!("Assigning to `os.environ` doesn't clear the environment")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// B003
|
/// B003
|
||||||
pub(crate) fn assignment_to_os_environ(checker: &mut Checker, targets: &[Expr]) {
|
pub(crate) fn assignment_to_os_environ(checker: &mut Checker, targets: &[Expr]) {
|
||||||
if targets.len() != 1 {
|
let [target] = targets else {
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
let target = &targets[0];
|
|
||||||
let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = target else {
|
let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = target else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use ruff_text_size::TextRange;
|
use rustpython_parser::ast::{self, Constant, Expr, Ranged};
|
||||||
use rustpython_parser::ast::{self, Constant, Expr, ExprContext, Identifier, Ranged};
|
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -47,15 +46,6 @@ impl AlwaysAutofixableViolation for GetAttrWithConstant {
|
||||||
"Replace `getattr` with attribute access".to_string()
|
"Replace `getattr` with attribute access".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn attribute(value: &Expr, attr: &str) -> Expr {
|
|
||||||
ast::ExprAttribute {
|
|
||||||
value: Box::new(value.clone()),
|
|
||||||
attr: Identifier::new(attr.to_string(), TextRange::default()),
|
|
||||||
ctx: ExprContext::Load,
|
|
||||||
range: TextRange::default(),
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// B009
|
/// B009
|
||||||
pub(crate) fn getattr_with_constant(
|
pub(crate) fn getattr_with_constant(
|
||||||
|
@ -83,14 +73,14 @@ pub(crate) fn getattr_with_constant(
|
||||||
if !is_identifier(value) {
|
if !is_identifier(value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if is_mangled_private(value.as_str()) {
|
if is_mangled_private(value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut diagnostic = Diagnostic::new(GetAttrWithConstant, expr.range());
|
let mut diagnostic = Diagnostic::new(GetAttrWithConstant, expr.range());
|
||||||
if checker.patch(diagnostic.kind.rule()) {
|
if checker.patch(diagnostic.kind.rule()) {
|
||||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||||
checker.generator().expr(&attribute(obj, value)),
|
format!("{}.{}", checker.locator.slice(obj.range()), value),
|
||||||
expr.range(),
|
expr.range(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,14 +55,11 @@ pub(crate) fn strip_with_multi_characters(
|
||||||
if !matches!(attr.as_str(), "strip" | "lstrip" | "rstrip") {
|
if !matches!(attr.as_str(), "strip" | "lstrip" | "rstrip") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if args.len() != 1 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let [Expr::Constant(ast::ExprConstant {
|
||||||
value: Constant::Str(value),
|
value: Constant::Str(value),
|
||||||
..
|
..
|
||||||
}) = &args[0]
|
})] = args
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,16 +14,16 @@ pub(super) fn exactly_one_argument_with_matching_function<'a>(
|
||||||
args: &'a [Expr],
|
args: &'a [Expr],
|
||||||
keywords: &[Keyword],
|
keywords: &[Keyword],
|
||||||
) -> Option<&'a Expr> {
|
) -> Option<&'a Expr> {
|
||||||
if !keywords.is_empty() {
|
let [arg] = args else {
|
||||||
return None;
|
return None;
|
||||||
}
|
};
|
||||||
if args.len() != 1 {
|
if !keywords.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if expr_name(func)? != name {
|
if expr_name(func)? != name {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(&args[0])
|
Some(arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn first_argument_with_matching_function<'a>(
|
pub(super) fn first_argument_with_matching_function<'a>(
|
||||||
|
|
|
@ -82,10 +82,9 @@ pub(crate) fn unnecessary_dict_comprehension(
|
||||||
value: &Expr,
|
value: &Expr,
|
||||||
generators: &[Comprehension],
|
generators: &[Comprehension],
|
||||||
) {
|
) {
|
||||||
if generators.len() != 1 {
|
let [generator] = generators else {
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
let generator = &generators[0];
|
|
||||||
if !generator.ifs.is_empty() || generator.is_async {
|
if !generator.ifs.is_empty() || generator.is_async {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -123,10 +122,9 @@ pub(crate) fn unnecessary_list_set_comprehension(
|
||||||
elt: &Expr,
|
elt: &Expr,
|
||||||
generators: &[Comprehension],
|
generators: &[Comprehension],
|
||||||
) {
|
) {
|
||||||
if generators.len() != 1 {
|
let [generator] = generators else {
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
let generator = &generators[0];
|
|
||||||
if !generator.ifs.is_empty() || generator.is_async {
|
if !generator.ifs.is_empty() || generator.is_async {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,10 +61,7 @@ pub(crate) fn duplicate_class_field_definition<'a, 'b>(
|
||||||
// Extract the property name from the assignment statement.
|
// Extract the property name from the assignment statement.
|
||||||
let target = match stmt {
|
let target = match stmt {
|
||||||
Stmt::Assign(ast::StmtAssign { targets, .. }) => {
|
Stmt::Assign(ast::StmtAssign { targets, .. }) => {
|
||||||
if targets.len() != 1 {
|
if let [Expr::Name(ast::ExprName { id, .. })] = targets.as_slice() {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = &targets[0] {
|
|
||||||
id
|
id
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -52,10 +52,9 @@ pub(crate) fn duplicate_union_member<'a>(checker: &mut Checker, expr: &'a Expr)
|
||||||
// If the parent node is not a `BinOp` we will not perform a fix
|
// If the parent node is not a `BinOp` we will not perform a fix
|
||||||
if let Some(Expr::BinOp(ast::ExprBinOp { left, right, .. })) = parent {
|
if let Some(Expr::BinOp(ast::ExprBinOp { left, right, .. })) = parent {
|
||||||
// Replace the parent with its non-duplicate child.
|
// Replace the parent with its non-duplicate child.
|
||||||
|
let child = if expr == left.as_ref() { right } else { left };
|
||||||
diagnostic.set_fix(Fix::automatic(Edit::range_replacement(
|
diagnostic.set_fix(Fix::automatic(Edit::range_replacement(
|
||||||
checker
|
checker.locator.slice(child.range()).to_string(),
|
||||||
.generator()
|
|
||||||
.expr(if expr == left.as_ref() { right } else { left }),
|
|
||||||
parent.unwrap().range(),
|
parent.unwrap().range(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,10 +60,10 @@ impl Violation for UnprefixedTypeParam {
|
||||||
|
|
||||||
/// PYI001
|
/// PYI001
|
||||||
pub(crate) fn prefix_type_params(checker: &mut Checker, value: &Expr, targets: &[Expr]) {
|
pub(crate) fn prefix_type_params(checker: &mut Checker, value: &Expr, targets: &[Expr]) {
|
||||||
if targets.len() != 1 {
|
let [target] = targets else {
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = &targets[0] {
|
if let Expr::Name(ast::ExprName { id, .. }) = target {
|
||||||
if id.starts_with('_') {
|
if id.starts_with('_') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -399,10 +399,9 @@ pub(crate) fn argument_simple_defaults(checker: &mut Checker, arguments: &Argume
|
||||||
|
|
||||||
/// PYI015
|
/// PYI015
|
||||||
pub(crate) fn assignment_default_in_stub(checker: &mut Checker, targets: &[Expr], value: &Expr) {
|
pub(crate) fn assignment_default_in_stub(checker: &mut Checker, targets: &[Expr], value: &Expr) {
|
||||||
if targets.len() != 1 {
|
let [target] = targets else {
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
let target = &targets[0];
|
|
||||||
if !target.is_name_expr() {
|
if !target.is_name_expr() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -471,10 +470,9 @@ pub(crate) fn unannotated_assignment_in_stub(
|
||||||
targets: &[Expr],
|
targets: &[Expr],
|
||||||
value: &Expr,
|
value: &Expr,
|
||||||
) {
|
) {
|
||||||
if targets.len() != 1 {
|
let [target] = targets else {
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
let target = &targets[0];
|
|
||||||
let Expr::Name(ast::ExprName { id, .. }) = target else {
|
let Expr::Name(ast::ExprName { id, .. }) = target else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
|
@ -395,68 +395,90 @@ PYI016.pyi:57:5: PYI016 Duplicate union member `set[int]`
|
||||||
|
|
|
|
||||||
= help: Remove duplicate union member `set[int]`
|
= help: Remove duplicate union member `set[int]`
|
||||||
|
|
||||||
PYI016.pyi:64:28: PYI016 Duplicate union member `int`
|
PYI016.pyi:63:28: PYI016 Duplicate union member `int`
|
||||||
|
|
|
|
||||||
63 | # Should emit in cases with `typing.Union` instead of `|`
|
62 | # Should emit in cases with `typing.Union` instead of `|`
|
||||||
64 | field19: typing.Union[int, int] # Error
|
63 | field19: typing.Union[int, int] # Error
|
||||||
| ^^^ PYI016
|
| ^^^ PYI016
|
||||||
65 |
|
64 |
|
||||||
66 | # Should emit in cases with nested `typing.Union`
|
65 | # Should emit in cases with nested `typing.Union`
|
||||||
|
|
|
|
||||||
= help: Remove duplicate union member `int`
|
= help: Remove duplicate union member `int`
|
||||||
|
|
||||||
PYI016.pyi:67:41: PYI016 Duplicate union member `int`
|
PYI016.pyi:66:41: PYI016 Duplicate union member `int`
|
||||||
|
|
|
|
||||||
66 | # Should emit in cases with nested `typing.Union`
|
65 | # Should emit in cases with nested `typing.Union`
|
||||||
67 | field20: typing.Union[int, typing.Union[int, str]] # Error
|
66 | field20: typing.Union[int, typing.Union[int, str]] # Error
|
||||||
| ^^^ PYI016
|
| ^^^ PYI016
|
||||||
68 |
|
67 |
|
||||||
69 | # Should emit in cases with mixed `typing.Union` and `|`
|
68 | # Should emit in cases with mixed `typing.Union` and `|`
|
||||||
|
|
|
|
||||||
= help: Remove duplicate union member `int`
|
= help: Remove duplicate union member `int`
|
||||||
|
|
||||||
PYI016.pyi:70:28: PYI016 [*] Duplicate union member `int`
|
PYI016.pyi:69:28: PYI016 [*] Duplicate union member `int`
|
||||||
|
|
|
|
||||||
69 | # Should emit in cases with mixed `typing.Union` and `|`
|
68 | # Should emit in cases with mixed `typing.Union` and `|`
|
||||||
70 | field21: typing.Union[int, int | str] # Error
|
69 | field21: typing.Union[int, int | str] # Error
|
||||||
| ^^^ PYI016
|
| ^^^ PYI016
|
||||||
71 |
|
70 |
|
||||||
72 | # Should emit only once in cases with multiple nested `typing.Union`
|
71 | # Should emit only once in cases with multiple nested `typing.Union`
|
||||||
|
|
|
|
||||||
= help: Remove duplicate union member `int`
|
= help: Remove duplicate union member `int`
|
||||||
|
|
||||||
ℹ Fix
|
ℹ Fix
|
||||||
67 67 | field20: typing.Union[int, typing.Union[int, str]] # Error
|
66 66 | field20: typing.Union[int, typing.Union[int, str]] # Error
|
||||||
68 68 |
|
67 67 |
|
||||||
69 69 | # Should emit in cases with mixed `typing.Union` and `|`
|
68 68 | # Should emit in cases with mixed `typing.Union` and `|`
|
||||||
70 |-field21: typing.Union[int, int | str] # Error
|
69 |-field21: typing.Union[int, int | str] # Error
|
||||||
70 |+field21: typing.Union[int, str] # Error
|
69 |+field21: typing.Union[int, str] # Error
|
||||||
71 71 |
|
70 70 |
|
||||||
72 72 | # Should emit only once in cases with multiple nested `typing.Union`
|
71 71 | # Should emit only once in cases with multiple nested `typing.Union`
|
||||||
73 73 | field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
72 72 | field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
||||||
|
|
||||||
PYI016.pyi:73:41: PYI016 Duplicate union member `int`
|
PYI016.pyi:72:41: PYI016 Duplicate union member `int`
|
||||||
|
|
|
|
||||||
72 | # Should emit only once in cases with multiple nested `typing.Union`
|
71 | # Should emit only once in cases with multiple nested `typing.Union`
|
||||||
73 | field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
72 | field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
||||||
| ^^^ PYI016
|
| ^^^ PYI016
|
||||||
|
73 |
|
||||||
|
74 | # Should emit in cases with newlines
|
||||||
|
|
|
|
||||||
= help: Remove duplicate union member `int`
|
= help: Remove duplicate union member `int`
|
||||||
|
|
||||||
PYI016.pyi:73:59: PYI016 Duplicate union member `int`
|
PYI016.pyi:72:59: PYI016 Duplicate union member `int`
|
||||||
|
|
|
|
||||||
72 | # Should emit only once in cases with multiple nested `typing.Union`
|
71 | # Should emit only once in cases with multiple nested `typing.Union`
|
||||||
73 | field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
72 | field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
||||||
| ^^^ PYI016
|
| ^^^ PYI016
|
||||||
|
73 |
|
||||||
|
74 | # Should emit in cases with newlines
|
||||||
|
|
|
|
||||||
= help: Remove duplicate union member `int`
|
= help: Remove duplicate union member `int`
|
||||||
|
|
||||||
PYI016.pyi:73:64: PYI016 Duplicate union member `int`
|
PYI016.pyi:72:64: PYI016 Duplicate union member `int`
|
||||||
|
|
|
|
||||||
72 | # Should emit only once in cases with multiple nested `typing.Union`
|
71 | # Should emit only once in cases with multiple nested `typing.Union`
|
||||||
73 | field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
72 | field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
||||||
| ^^^ PYI016
|
| ^^^ PYI016
|
||||||
|
73 |
|
||||||
|
74 | # Should emit in cases with newlines
|
||||||
|
|
|
|
||||||
= help: Remove duplicate union member `int`
|
= help: Remove duplicate union member `int`
|
||||||
|
|
||||||
|
PYI016.pyi:76:12: PYI016 [*] Duplicate union member `set[int]`
|
||||||
|
|
|
||||||
|
74 | # Should emit in cases with newlines
|
||||||
|
75 | field23: set[ # foo
|
||||||
|
76 | int] | set[int]
|
||||||
|
| ^^^^^^^^ PYI016
|
||||||
|
|
|
||||||
|
= help: Remove duplicate union member `set[int]`
|
||||||
|
|
||||||
|
ℹ Fix
|
||||||
|
73 73 |
|
||||||
|
74 74 | # Should emit in cases with newlines
|
||||||
|
75 75 | field23: set[ # foo
|
||||||
|
76 |- int] | set[int]
|
||||||
|
76 |+ int]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -420,10 +420,10 @@ impl From<bool> for Bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_one_line_return_bool(stmts: &[Stmt]) -> Option<Bool> {
|
fn is_one_line_return_bool(stmts: &[Stmt]) -> Option<Bool> {
|
||||||
if stmts.len() != 1 {
|
let [stmt] = stmts else {
|
||||||
return None;
|
return None;
|
||||||
}
|
};
|
||||||
let Stmt::Return(ast::StmtReturn { value, range: _ }) = &stmts[0] else {
|
let Stmt::Return(ast::StmtReturn { value, range: _ }) = stmt else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let Some(Expr::Constant(ast::ExprConstant { value, .. })) = value.as_deref() else {
|
let Some(Expr::Constant(ast::ExprConstant { value, .. })) = value.as_deref() else {
|
||||||
|
@ -859,15 +859,12 @@ pub(crate) fn manual_dict_lookup(
|
||||||
let Expr::Name(ast::ExprName { id, .. }) = left.as_ref() else {
|
let Expr::Name(ast::ExprName { id, .. }) = left.as_ref() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !(id == target && ops.len() == 1 && ops[0] == CmpOp::Eq) {
|
if !(id == target && matches!(ops.as_slice(), [CmpOp::Eq])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if comparators.len() != 1 {
|
let [Expr::Constant(ast::ExprConstant {
|
||||||
return;
|
|
||||||
}
|
|
||||||
let Expr::Constant(ast::ExprConstant {
|
|
||||||
value: constant, ..
|
value: constant, ..
|
||||||
}) = &comparators[0]
|
})] = comparators.as_slice()
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use ruff_text_size::TextRange;
|
use ruff_text_size::TextRange;
|
||||||
use rustpython_parser::ast::{self, Constant, Expr, ExprContext, Ranged, UnaryOp};
|
use rustpython_parser::ast::{self, Expr, ExprContext, Ranged, UnaryOp};
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysAutofixableViolation, AutofixKind, Diagnostic, Edit, Fix, Violation};
|
use ruff_diagnostics::{AlwaysAutofixableViolation, AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use ruff_python_ast::helpers::{is_const_false, is_const_true};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::registry::AsRule;
|
use crate::registry::AsRule;
|
||||||
|
@ -141,16 +142,7 @@ pub(crate) fn explicit_true_false_in_ifexpr(
|
||||||
body: &Expr,
|
body: &Expr,
|
||||||
orelse: &Expr,
|
orelse: &Expr,
|
||||||
) {
|
) {
|
||||||
let Expr::Constant(ast::ExprConstant { value, .. }) = &body else {
|
if !is_const_true(body) || !is_const_false(orelse) {
|
||||||
return;
|
|
||||||
};
|
|
||||||
if !matches!(value, Constant::Bool(true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let Expr::Constant(ast::ExprConstant { value, .. }) = &orelse else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if !matches!(value, Constant::Bool(false)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,9 +153,9 @@ pub(crate) fn explicit_true_false_in_ifexpr(
|
||||||
expr.range(),
|
expr.range(),
|
||||||
);
|
);
|
||||||
if checker.patch(diagnostic.kind.rule()) {
|
if checker.patch(diagnostic.kind.rule()) {
|
||||||
if matches!(test, Expr::Compare(_)) {
|
if test.is_compare_expr() {
|
||||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||||
checker.generator().expr(&test.clone()),
|
checker.locator.slice(test.range()).to_string(),
|
||||||
expr.range(),
|
expr.range(),
|
||||||
)));
|
)));
|
||||||
} else if checker.semantic().is_builtin("bool") {
|
} else if checker.semantic().is_builtin("bool") {
|
||||||
|
@ -195,16 +187,7 @@ pub(crate) fn explicit_false_true_in_ifexpr(
|
||||||
body: &Expr,
|
body: &Expr,
|
||||||
orelse: &Expr,
|
orelse: &Expr,
|
||||||
) {
|
) {
|
||||||
let Expr::Constant(ast::ExprConstant { value, .. }) = &body else {
|
if !is_const_false(body) || !is_const_true(orelse) {
|
||||||
return;
|
|
||||||
};
|
|
||||||
if !matches!(value, Constant::Bool(false)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let Expr::Constant(ast::ExprConstant { value, .. }) = &orelse else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if !matches!(value, Constant::Bool(true)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +222,7 @@ pub(crate) fn twisted_arms_in_ifexpr(
|
||||||
) {
|
) {
|
||||||
let Expr::UnaryOp(ast::ExprUnaryOp {
|
let Expr::UnaryOp(ast::ExprUnaryOp {
|
||||||
op,
|
op,
|
||||||
operand: test_operand,
|
operand,
|
||||||
range: _,
|
range: _,
|
||||||
}) = &test
|
}) = &test
|
||||||
else {
|
else {
|
||||||
|
@ -250,7 +233,7 @@ pub(crate) fn twisted_arms_in_ifexpr(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the test operand and else branch use the same variable.
|
// Check if the test operand and else branch use the same variable.
|
||||||
let Expr::Name(ast::ExprName { id: test_id, .. }) = test_operand.as_ref() else {
|
let Expr::Name(ast::ExprName { id: test_id, .. }) = operand.as_ref() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Expr::Name(ast::ExprName { id: orelse_id, .. }) = orelse else {
|
let Expr::Name(ast::ExprName { id: orelse_id, .. }) = orelse else {
|
||||||
|
|
|
@ -136,13 +136,7 @@ fn is_exception_check(stmt: &Stmt) -> bool {
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if body.len() != 1 {
|
matches!(body.as_slice(), [Stmt::Raise(_)])
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if matches!(body[0], Stmt::Raise(_)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SIM201
|
/// SIM201
|
||||||
|
@ -287,7 +281,7 @@ pub(crate) fn double_negation(checker: &mut Checker, expr: &Expr, op: UnaryOp, o
|
||||||
if checker.patch(diagnostic.kind.rule()) {
|
if checker.patch(diagnostic.kind.rule()) {
|
||||||
if checker.semantic().in_boolean_test() {
|
if checker.semantic().in_boolean_test() {
|
||||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||||
checker.generator().expr(operand),
|
checker.locator.slice(operand.range()).to_string(),
|
||||||
expr.range(),
|
expr.range(),
|
||||||
)));
|
)));
|
||||||
} else if checker.semantic().is_builtin("bool") {
|
} else if checker.semantic().is_builtin("bool") {
|
||||||
|
|
|
@ -48,11 +48,9 @@ impl AlwaysAutofixableViolation for EmptyTypeCheckingBlock {
|
||||||
|
|
||||||
/// TCH005
|
/// TCH005
|
||||||
pub(crate) fn empty_type_checking_block(checker: &mut Checker, stmt: &ast::StmtIf) {
|
pub(crate) fn empty_type_checking_block(checker: &mut Checker, stmt: &ast::StmtIf) {
|
||||||
if stmt.body.len() != 1 {
|
let [stmt] = stmt.body.as_slice() else {
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
|
|
||||||
let stmt = &stmt.body[0];
|
|
||||||
if !stmt.is_pass_stmt() {
|
if !stmt.is_pass_stmt() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,10 +38,9 @@ impl Violation for PandasDfVariableName {
|
||||||
|
|
||||||
/// PD901
|
/// PD901
|
||||||
pub(crate) fn assignment_to_df(targets: &[Expr]) -> Option<Diagnostic> {
|
pub(crate) fn assignment_to_df(targets: &[Expr]) -> Option<Diagnostic> {
|
||||||
if targets.len() != 1 {
|
let [target] = targets else {
|
||||||
return None;
|
return None;
|
||||||
}
|
};
|
||||||
let target = &targets[0];
|
|
||||||
let Expr::Name(ast::ExprName { id, .. }) = target else {
|
let Expr::Name(ast::ExprName { id, .. }) = target else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
|
@ -128,10 +128,9 @@ fn is_valid_constant(formats: &[CFormatStrOrBytes<String>], value: &Expr) -> boo
|
||||||
let formats = collect_specs(formats);
|
let formats = collect_specs(formats);
|
||||||
// If there is more than one format, this is not valid python and we should
|
// If there is more than one format, this is not valid python and we should
|
||||||
// return true so that no error is reported
|
// return true so that no error is reported
|
||||||
if formats.len() != 1 {
|
let [format] = formats.as_slice() else {
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
||||||
let format = formats[0];
|
|
||||||
equivalent(format, value)
|
equivalent(format, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,10 +48,7 @@ pub(crate) fn useless_metaclass_type(
|
||||||
value: &Expr,
|
value: &Expr,
|
||||||
targets: &[Expr],
|
targets: &[Expr],
|
||||||
) {
|
) {
|
||||||
if targets.len() != 1 {
|
let [Expr::Name(ast::ExprName { id, .. })] = targets else {
|
||||||
return;
|
|
||||||
}
|
|
||||||
let Expr::Name(ast::ExprName { id, .. }) = targets.first().unwrap() else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if id != "__metaclass__" {
|
if id != "__metaclass__" {
|
||||||
|
|
|
@ -99,11 +99,10 @@ impl<'a> StatementVisitor<'a> for YieldFromVisitor<'a> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If there's any logic besides a yield, don't rewrite.
|
// If there's any logic besides a yield, don't rewrite.
|
||||||
if body.len() != 1 {
|
let [body] = body.as_slice() else {
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
// If the body is not a yield, don't rewrite.
|
// If the body is not a yield, don't rewrite.
|
||||||
let body = &body[0];
|
|
||||||
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = &body {
|
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = &body {
|
||||||
if let Expr::Yield(ast::ExprYield {
|
if let Expr::Yield(ast::ExprYield {
|
||||||
value: Some(value),
|
value: Some(value),
|
||||||
|
|
|
@ -549,10 +549,7 @@ pub fn is_assignment_to_a_dunder(stmt: &Stmt) -> bool {
|
||||||
// annotation. This is what pycodestyle (as of 2.9.1) does.
|
// annotation. This is what pycodestyle (as of 2.9.1) does.
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Assign(ast::StmtAssign { targets, .. }) => {
|
Stmt::Assign(ast::StmtAssign { targets, .. }) => {
|
||||||
if targets.len() != 1 {
|
if let [Expr::Name(ast::ExprName { id, .. })] = targets.as_slice() {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = &targets[0] {
|
|
||||||
is_dunder(id)
|
is_dunder(id)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue