mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
Reduce size of Expr
from 80 to 64 bytes (#9900)
## Summary This PR reduces the size of `Expr` from 80 to 64 bytes, by reducing the sizes of... - `ExprCall` from 72 to 56 bytes, by using boxed slices for `Arguments`. - `ExprCompare` from 64 to 48 bytes, by using boxed slices for its various vectors. In testing, the parser gets a bit faster, and the linter benchmarks improve quite a bit.
This commit is contained in:
parent
bd8123c0d8
commit
49fe1b85f2
78 changed files with 326 additions and 258 deletions
|
@ -31,8 +31,8 @@ use std::path::Path;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, Arguments, Comprehension, ElifElseClause, ExceptHandler, Expr, ExprContext,
|
self as ast, Comprehension, ElifElseClause, ExceptHandler, Expr, ExprContext, Keyword,
|
||||||
Keyword, MatchCase, Parameter, ParameterWithDefault, Parameters, Pattern, Stmt, Suite, UnaryOp,
|
MatchCase, Parameter, ParameterWithDefault, Parameters, Pattern, Stmt, Suite, UnaryOp,
|
||||||
};
|
};
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
|
@ -989,12 +989,7 @@ where
|
||||||
}
|
}
|
||||||
Expr::Call(ast::ExprCall {
|
Expr::Call(ast::ExprCall {
|
||||||
func,
|
func,
|
||||||
arguments:
|
arguments,
|
||||||
Arguments {
|
|
||||||
args,
|
|
||||||
keywords,
|
|
||||||
range: _,
|
|
||||||
},
|
|
||||||
range: _,
|
range: _,
|
||||||
}) => {
|
}) => {
|
||||||
self.visit_expr(func);
|
self.visit_expr(func);
|
||||||
|
@ -1037,7 +1032,7 @@ where
|
||||||
});
|
});
|
||||||
match callable {
|
match callable {
|
||||||
Some(typing::Callable::Bool) => {
|
Some(typing::Callable::Bool) => {
|
||||||
let mut args = args.iter();
|
let mut args = arguments.args.iter();
|
||||||
if let Some(arg) = args.next() {
|
if let Some(arg) = args.next() {
|
||||||
self.visit_boolean_test(arg);
|
self.visit_boolean_test(arg);
|
||||||
}
|
}
|
||||||
|
@ -1046,7 +1041,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(typing::Callable::Cast) => {
|
Some(typing::Callable::Cast) => {
|
||||||
let mut args = args.iter();
|
let mut args = arguments.args.iter();
|
||||||
if let Some(arg) = args.next() {
|
if let Some(arg) = args.next() {
|
||||||
self.visit_type_definition(arg);
|
self.visit_type_definition(arg);
|
||||||
}
|
}
|
||||||
|
@ -1055,7 +1050,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(typing::Callable::NewType) => {
|
Some(typing::Callable::NewType) => {
|
||||||
let mut args = args.iter();
|
let mut args = arguments.args.iter();
|
||||||
if let Some(arg) = args.next() {
|
if let Some(arg) = args.next() {
|
||||||
self.visit_non_type_definition(arg);
|
self.visit_non_type_definition(arg);
|
||||||
}
|
}
|
||||||
|
@ -1064,21 +1059,21 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(typing::Callable::TypeVar) => {
|
Some(typing::Callable::TypeVar) => {
|
||||||
let mut args = args.iter();
|
let mut args = arguments.args.iter();
|
||||||
if let Some(arg) = args.next() {
|
if let Some(arg) = args.next() {
|
||||||
self.visit_non_type_definition(arg);
|
self.visit_non_type_definition(arg);
|
||||||
}
|
}
|
||||||
for arg in args {
|
for arg in args {
|
||||||
self.visit_type_definition(arg);
|
self.visit_type_definition(arg);
|
||||||
}
|
}
|
||||||
for keyword in keywords {
|
for keyword in arguments.keywords.iter() {
|
||||||
let Keyword {
|
let Keyword {
|
||||||
arg,
|
arg,
|
||||||
value,
|
value,
|
||||||
range: _,
|
range: _,
|
||||||
} = keyword;
|
} = keyword;
|
||||||
if let Some(id) = arg {
|
if let Some(id) = arg {
|
||||||
if id == "bound" {
|
if id.as_str() == "bound" {
|
||||||
self.visit_type_definition(value);
|
self.visit_type_definition(value);
|
||||||
} else {
|
} else {
|
||||||
self.visit_non_type_definition(value);
|
self.visit_non_type_definition(value);
|
||||||
|
@ -1088,7 +1083,7 @@ where
|
||||||
}
|
}
|
||||||
Some(typing::Callable::NamedTuple) => {
|
Some(typing::Callable::NamedTuple) => {
|
||||||
// Ex) NamedTuple("a", [("a", int)])
|
// Ex) NamedTuple("a", [("a", int)])
|
||||||
let mut args = args.iter();
|
let mut args = arguments.args.iter();
|
||||||
if let Some(arg) = args.next() {
|
if let Some(arg) = args.next() {
|
||||||
self.visit_non_type_definition(arg);
|
self.visit_non_type_definition(arg);
|
||||||
}
|
}
|
||||||
|
@ -1117,7 +1112,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for keyword in keywords {
|
for keyword in arguments.keywords.iter() {
|
||||||
let Keyword { arg, value, .. } = keyword;
|
let Keyword { arg, value, .. } = keyword;
|
||||||
match (arg.as_ref(), value) {
|
match (arg.as_ref(), value) {
|
||||||
// Ex) NamedTuple("a", **{"a": int})
|
// Ex) NamedTuple("a", **{"a": int})
|
||||||
|
@ -1144,7 +1139,7 @@ where
|
||||||
}
|
}
|
||||||
Some(typing::Callable::TypedDict) => {
|
Some(typing::Callable::TypedDict) => {
|
||||||
// Ex) TypedDict("a", {"a": int})
|
// Ex) TypedDict("a", {"a": int})
|
||||||
let mut args = args.iter();
|
let mut args = arguments.args.iter();
|
||||||
if let Some(arg) = args.next() {
|
if let Some(arg) = args.next() {
|
||||||
self.visit_non_type_definition(arg);
|
self.visit_non_type_definition(arg);
|
||||||
}
|
}
|
||||||
|
@ -1167,13 +1162,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ex) TypedDict("a", a=int)
|
// Ex) TypedDict("a", a=int)
|
||||||
for keyword in keywords {
|
for keyword in arguments.keywords.iter() {
|
||||||
let Keyword { value, .. } = keyword;
|
let Keyword { value, .. } = keyword;
|
||||||
self.visit_type_definition(value);
|
self.visit_type_definition(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(typing::Callable::MypyExtension) => {
|
Some(typing::Callable::MypyExtension) => {
|
||||||
let mut args = args.iter();
|
let mut args = arguments.args.iter();
|
||||||
if let Some(arg) = args.next() {
|
if let Some(arg) = args.next() {
|
||||||
// Ex) DefaultNamedArg(bool | None, name="some_prop_name")
|
// Ex) DefaultNamedArg(bool | None, name="some_prop_name")
|
||||||
self.visit_type_definition(arg);
|
self.visit_type_definition(arg);
|
||||||
|
@ -1181,13 +1176,13 @@ where
|
||||||
for arg in args {
|
for arg in args {
|
||||||
self.visit_non_type_definition(arg);
|
self.visit_non_type_definition(arg);
|
||||||
}
|
}
|
||||||
for keyword in keywords {
|
for keyword in arguments.keywords.iter() {
|
||||||
let Keyword { value, .. } = keyword;
|
let Keyword { value, .. } = keyword;
|
||||||
self.visit_non_type_definition(value);
|
self.visit_non_type_definition(value);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Ex) DefaultNamedArg(type="bool", name="some_prop_name")
|
// Ex) DefaultNamedArg(type="bool", name="some_prop_name")
|
||||||
for keyword in keywords {
|
for keyword in arguments.keywords.iter() {
|
||||||
let Keyword {
|
let Keyword {
|
||||||
value,
|
value,
|
||||||
arg,
|
arg,
|
||||||
|
@ -1205,10 +1200,10 @@ where
|
||||||
// If we're in a type definition, we need to treat the arguments to any
|
// If we're in a type definition, we need to treat the arguments to any
|
||||||
// other callables as non-type definitions (i.e., we don't want to treat
|
// other callables as non-type definitions (i.e., we don't want to treat
|
||||||
// any strings as deferred type definitions).
|
// any strings as deferred type definitions).
|
||||||
for arg in args {
|
for arg in arguments.args.iter() {
|
||||||
self.visit_non_type_definition(arg);
|
self.visit_non_type_definition(arg);
|
||||||
}
|
}
|
||||||
for keyword in keywords {
|
for keyword in arguments.keywords.iter() {
|
||||||
let Keyword { value, .. } = keyword;
|
let Keyword { value, .. } = keyword;
|
||||||
self.visit_non_type_definition(value);
|
self.visit_non_type_definition(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,11 +59,11 @@ fn assertion_error(msg: Option<&Expr>) -> Stmt {
|
||||||
})),
|
})),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: if let Some(msg) = msg {
|
args: if let Some(msg) = msg {
|
||||||
vec![msg.clone()]
|
Box::from([msg.clone()])
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
Box::from([])
|
||||||
},
|
},
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -91,7 +91,7 @@ pub(crate) fn assert_raises_exception(checker: &mut Checker, items: &[WithItem])
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [arg] = arguments.args.as_slice() else {
|
let [arg] = &*arguments.args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::types::Node;
|
use ruff_python_ast::types::Node;
|
||||||
use ruff_python_ast::visitor;
|
use ruff_python_ast::visitor;
|
||||||
use ruff_python_ast::visitor::Visitor;
|
use ruff_python_ast::visitor::Visitor;
|
||||||
use ruff_python_ast::{self as ast, Arguments, Comprehension, Expr, ExprContext, Stmt};
|
use ruff_python_ast::{self as ast, Comprehension, Expr, ExprContext, Stmt};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -126,18 +126,13 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Call(ast::ExprCall {
|
Expr::Call(ast::ExprCall {
|
||||||
func,
|
func,
|
||||||
arguments:
|
arguments,
|
||||||
Arguments {
|
|
||||||
args,
|
|
||||||
keywords,
|
|
||||||
range: _,
|
|
||||||
},
|
|
||||||
range: _,
|
range: _,
|
||||||
}) => {
|
}) => {
|
||||||
match func.as_ref() {
|
match func.as_ref() {
|
||||||
Expr::Name(ast::ExprName { id, .. }) => {
|
Expr::Name(ast::ExprName { id, .. }) => {
|
||||||
if matches!(id.as_str(), "filter" | "reduce" | "map") {
|
if matches!(id.as_str(), "filter" | "reduce" | "map") {
|
||||||
for arg in args {
|
for arg in arguments.args.iter() {
|
||||||
if arg.is_lambda_expr() {
|
if arg.is_lambda_expr() {
|
||||||
self.safe_functions.push(arg);
|
self.safe_functions.push(arg);
|
||||||
}
|
}
|
||||||
|
@ -148,7 +143,7 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
|
||||||
if attr == "reduce" {
|
if attr == "reduce" {
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = value.as_ref() {
|
if let Expr::Name(ast::ExprName { id, .. }) = value.as_ref() {
|
||||||
if id == "functools" {
|
if id == "functools" {
|
||||||
for arg in args {
|
for arg in arguments.args.iter() {
|
||||||
if arg.is_lambda_expr() {
|
if arg.is_lambda_expr() {
|
||||||
self.safe_functions.push(arg);
|
self.safe_functions.push(arg);
|
||||||
}
|
}
|
||||||
|
@ -160,7 +155,7 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
for keyword in keywords {
|
for keyword in arguments.keywords.iter() {
|
||||||
if keyword.arg.as_ref().is_some_and(|arg| arg == "key")
|
if keyword.arg.as_ref().is_some_and(|arg| arg == "key")
|
||||||
&& keyword.value.is_lambda_expr()
|
&& keyword.value.is_lambda_expr()
|
||||||
{
|
{
|
||||||
|
|
|
@ -114,7 +114,7 @@ fn is_infinite_iterator(arg: &Expr, semantic: &SemanticModel) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ex) `iterools.repeat(1, times=None)`
|
// Ex) `iterools.repeat(1, times=None)`
|
||||||
for keyword in keywords {
|
for keyword in keywords.iter() {
|
||||||
if keyword.arg.as_ref().is_some_and(|name| name == "times") {
|
if keyword.arg.as_ref().is_some_and(|name| name == "times") {
|
||||||
if keyword.value.is_none_literal_expr() {
|
if keyword.value.is_none_literal_expr() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -88,7 +88,7 @@ fn is_nullable_field<'a>(value: &'a Expr, semantic: &'a SemanticModel) -> Option
|
||||||
let mut null_key = false;
|
let mut null_key = false;
|
||||||
let mut blank_key = false;
|
let mut blank_key = false;
|
||||||
let mut unique_key = false;
|
let mut unique_key = false;
|
||||||
for keyword in &call.arguments.keywords {
|
for keyword in call.arguments.keywords.iter() {
|
||||||
let Some(argument) = &keyword.arg else {
|
let Some(argument) = &keyword.arg else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
|
@ -113,7 +113,7 @@ fn check_log_record_attr_clash(checker: &mut Checker, extra: &Keyword) {
|
||||||
.resolve_call_path(func)
|
.resolve_call_path(func)
|
||||||
.is_some_and(|call_path| matches!(call_path.as_slice(), ["", "dict"]))
|
.is_some_and(|call_path| matches!(call_path.as_slice(), ["", "dict"]))
|
||||||
{
|
{
|
||||||
for keyword in keywords {
|
for keyword in keywords.iter() {
|
||||||
if let Some(attr) = &keyword.arg {
|
if let Some(attr) = &keyword.arg {
|
||||||
if is_reserved_attr(attr) {
|
if is_reserved_attr(attr) {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
|
|
@ -97,7 +97,7 @@ pub(crate) fn multiple_starts_ends_with(checker: &mut Checker, expr: &Expr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [arg] = args.as_slice() else {
|
let [arg] = &**args else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -188,8 +188,8 @@ pub(crate) fn multiple_starts_ends_with(checker: &mut Checker, expr: &Expr) {
|
||||||
let node3 = Expr::Call(ast::ExprCall {
|
let node3 = Expr::Call(ast::ExprCall {
|
||||||
func: Box::new(node2),
|
func: Box::new(node2),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: vec![node],
|
args: Box::from([node]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -59,7 +59,7 @@ impl Violation for UnnecessaryDictKwargs {
|
||||||
/// PIE804
|
/// PIE804
|
||||||
pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCall) {
|
pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCall) {
|
||||||
let mut duplicate_keywords = None;
|
let mut duplicate_keywords = None;
|
||||||
for keyword in &call.arguments.keywords {
|
for keyword in call.arguments.keywords.iter() {
|
||||||
// keyword is a spread operator (indicated by None).
|
// keyword is a spread operator (indicated by None).
|
||||||
if keyword.arg.is_some() {
|
if keyword.arg.is_some() {
|
||||||
continue;
|
continue;
|
||||||
|
@ -145,7 +145,7 @@ fn duplicates(call: &ast::ExprCall) -> FxHashSet<&str> {
|
||||||
call.arguments.keywords.len(),
|
call.arguments.keywords.len(),
|
||||||
BuildHasherDefault::default(),
|
BuildHasherDefault::default(),
|
||||||
);
|
);
|
||||||
for keyword in &call.arguments.keywords {
|
for keyword in call.arguments.keywords.iter() {
|
||||||
if let Some(name) = &keyword.arg {
|
if let Some(name) = &keyword.arg {
|
||||||
if !seen.insert(name.as_str()) {
|
if !seen.insert(name.as_str()) {
|
||||||
duplicates.insert(name.as_str());
|
duplicates.insert(name.as_str());
|
||||||
|
|
|
@ -60,7 +60,7 @@ pub(crate) fn unnecessary_range_start(checker: &mut Checker, call: &ast::ExprCal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that the call has exactly two arguments (no `step`).
|
// Verify that the call has exactly two arguments (no `step`).
|
||||||
let [start, _] = call.arguments.args.as_slice() else {
|
let [start, _] = &*call.arguments.args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ pub(crate) fn bad_version_info_comparison(checker: &mut Checker, test: &Expr) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let ([op], [_right]) = (ops.as_slice(), comparators.as_slice()) else {
|
let ([op], [_right]) = (&**ops, &**comparators) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ pub(crate) fn unrecognized_platform(checker: &mut Checker, test: &Expr) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let ([op], [right]) = (ops.as_slice(), comparators.as_slice()) else {
|
let ([op], [right]) = (&**ops, &**comparators) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,7 @@ pub(crate) fn unrecognized_version_info(checker: &mut Checker, test: &Expr) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let ([op], [comparator]) = (ops.as_slice(), comparators.as_slice()) else {
|
let ([op], [comparator]) = (&**ops, &**comparators) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -411,7 +411,7 @@ fn to_pytest_raises_args<'a>(
|
||||||
) -> Option<Cow<'a, str>> {
|
) -> Option<Cow<'a, str>> {
|
||||||
let args = match attr {
|
let args = match attr {
|
||||||
"assertRaises" | "failUnlessRaises" => {
|
"assertRaises" | "failUnlessRaises" => {
|
||||||
match (arguments.args.as_slice(), arguments.keywords.as_slice()) {
|
match (&*arguments.args, &*arguments.keywords) {
|
||||||
// Ex) `assertRaises(Exception)`
|
// Ex) `assertRaises(Exception)`
|
||||||
([arg], []) => Cow::Borrowed(checker.locator().slice(arg)),
|
([arg], []) => Cow::Borrowed(checker.locator().slice(arg)),
|
||||||
// Ex) `assertRaises(expected_exception=Exception)`
|
// Ex) `assertRaises(expected_exception=Exception)`
|
||||||
|
@ -427,7 +427,7 @@ fn to_pytest_raises_args<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"assertRaisesRegex" | "assertRaisesRegexp" => {
|
"assertRaisesRegex" | "assertRaisesRegexp" => {
|
||||||
match (arguments.args.as_slice(), arguments.keywords.as_slice()) {
|
match (&*arguments.args, &*arguments.keywords) {
|
||||||
// Ex) `assertRaisesRegex(Exception, regex)`
|
// Ex) `assertRaisesRegex(Exception, regex)`
|
||||||
([arg1, arg2], []) => Cow::Owned(format!(
|
([arg1, arg2], []) => Cow::Owned(format!(
|
||||||
"{}, match={}",
|
"{}, match={}",
|
||||||
|
|
|
@ -638,17 +638,17 @@ pub(crate) fn parametrize(checker: &mut Checker, decorators: &[Decorator]) {
|
||||||
}) = &decorator.expression
|
}) = &decorator.expression
|
||||||
{
|
{
|
||||||
if checker.enabled(Rule::PytestParametrizeNamesWrongType) {
|
if checker.enabled(Rule::PytestParametrizeNamesWrongType) {
|
||||||
if let [names, ..] = args.as_slice() {
|
if let [names, ..] = &**args {
|
||||||
check_names(checker, decorator, names);
|
check_names(checker, decorator, names);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if checker.enabled(Rule::PytestParametrizeValuesWrongType) {
|
if checker.enabled(Rule::PytestParametrizeValuesWrongType) {
|
||||||
if let [names, values, ..] = args.as_slice() {
|
if let [names, values, ..] = &**args {
|
||||||
check_values(checker, names, values);
|
check_values(checker, names, values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if checker.enabled(Rule::PytestDuplicateParametrizeTestCases) {
|
if checker.enabled(Rule::PytestDuplicateParametrizeTestCases) {
|
||||||
if let [_, values, ..] = args.as_slice() {
|
if let [_, values, ..] = &**args {
|
||||||
check_duplicates(checker, values);
|
check_duplicates(checker, values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,8 +173,8 @@ fn assert(expr: &Expr, msg: Option<&Expr>) -> Stmt {
|
||||||
fn compare(left: &Expr, cmp_op: CmpOp, right: &Expr) -> Expr {
|
fn compare(left: &Expr, cmp_op: CmpOp, right: &Expr) -> Expr {
|
||||||
Expr::Compare(ast::ExprCompare {
|
Expr::Compare(ast::ExprCompare {
|
||||||
left: Box::new(left.clone()),
|
left: Box::new(left.clone()),
|
||||||
ops: vec![cmp_op],
|
ops: Box::from([cmp_op]),
|
||||||
comparators: vec![right.clone()],
|
comparators: Box::from([right.clone()]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -390,8 +390,8 @@ impl UnittestAssert {
|
||||||
let node1 = ast::ExprCall {
|
let node1 = ast::ExprCall {
|
||||||
func: Box::new(node.into()),
|
func: Box::new(node.into()),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: vec![(**obj).clone(), (**cls).clone()],
|
args: Box::from([(**obj).clone(), (**cls).clone()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
@ -434,8 +434,8 @@ impl UnittestAssert {
|
||||||
let node2 = ast::ExprCall {
|
let node2 = ast::ExprCall {
|
||||||
func: Box::new(node1.into()),
|
func: Box::new(node1.into()),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: vec![(**regex).clone(), (**text).clone()],
|
args: Box::from([(**regex).clone(), (**text).clone()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -437,8 +437,8 @@ pub(crate) fn duplicate_isinstance_call(checker: &mut Checker, expr: &Expr) {
|
||||||
let node2 = ast::ExprCall {
|
let node2 = ast::ExprCall {
|
||||||
func: Box::new(node1.into()),
|
func: Box::new(node1.into()),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: vec![target.clone(), node.into()],
|
args: Box::from([target.clone(), node.into()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
@ -480,13 +480,13 @@ fn match_eq_target(expr: &Expr) -> Option<(&str, &Expr)> {
|
||||||
else {
|
else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
if ops != &[CmpOp::Eq] {
|
if **ops != [CmpOp::Eq] {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let Expr::Name(ast::ExprName { id, .. }) = left.as_ref() else {
|
let Expr::Name(ast::ExprName { id, .. }) = left.as_ref() else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let [comparator] = comparators.as_slice() else {
|
let [comparator] = &**comparators else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
if !comparator.is_name_expr() {
|
if !comparator.is_name_expr() {
|
||||||
|
@ -551,8 +551,8 @@ pub(crate) fn compare_with_tuple(checker: &mut Checker, expr: &Expr) {
|
||||||
};
|
};
|
||||||
let node2 = ast::ExprCompare {
|
let node2 = ast::ExprCompare {
|
||||||
left: Box::new(node1.into()),
|
left: Box::new(node1.into()),
|
||||||
ops: vec![CmpOp::In],
|
ops: Box::from([CmpOp::In]),
|
||||||
comparators: vec![node.into()],
|
comparators: Box::from([node.into()]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
};
|
};
|
||||||
let in_expr = node2.into();
|
let in_expr = node2.into();
|
||||||
|
|
|
@ -185,8 +185,8 @@ pub(crate) fn if_expr_with_true_false(
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: vec![test.clone()],
|
args: Box::from([test.clone()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -176,7 +176,7 @@ pub(crate) fn negation_with_equal_op(
|
||||||
);
|
);
|
||||||
let node = ast::ExprCompare {
|
let node = ast::ExprCompare {
|
||||||
left: left.clone(),
|
left: left.clone(),
|
||||||
ops: vec![CmpOp::NotEq],
|
ops: Box::from([CmpOp::NotEq]),
|
||||||
comparators: comparators.clone(),
|
comparators: comparators.clone(),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
};
|
};
|
||||||
|
@ -206,7 +206,7 @@ pub(crate) fn negation_with_not_equal_op(
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !matches!(&ops[..], [CmpOp::NotEq]) {
|
if !matches!(&**ops, [CmpOp::NotEq]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if is_exception_check(checker.semantic().current_statement()) {
|
if is_exception_check(checker.semantic().current_statement()) {
|
||||||
|
@ -231,7 +231,7 @@ pub(crate) fn negation_with_not_equal_op(
|
||||||
);
|
);
|
||||||
let node = ast::ExprCompare {
|
let node = ast::ExprCompare {
|
||||||
left: left.clone(),
|
left: left.clone(),
|
||||||
ops: vec![CmpOp::Eq],
|
ops: Box::from([CmpOp::Eq]),
|
||||||
comparators: comparators.clone(),
|
comparators: comparators.clone(),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
};
|
};
|
||||||
|
@ -279,8 +279,8 @@ pub(crate) fn double_negation(checker: &mut Checker, expr: &Expr, op: UnaryOp, o
|
||||||
let node1 = ast::ExprCall {
|
let node1 = ast::ExprCall {
|
||||||
func: Box::new(node.into()),
|
func: Box::new(node.into()),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: vec![*operand.clone()],
|
args: Box::from([*operand.clone()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -253,8 +253,7 @@ fn is_main_check(expr: &Expr) -> bool {
|
||||||
{
|
{
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = left.as_ref() {
|
if let Expr::Name(ast::ExprName { id, .. }) = left.as_ref() {
|
||||||
if id == "__name__" {
|
if id == "__name__" {
|
||||||
if let [Expr::StringLiteral(ast::ExprStringLiteral { value, .. })] =
|
if let [Expr::StringLiteral(ast::ExprStringLiteral { value, .. })] = &**comparators
|
||||||
comparators.as_slice()
|
|
||||||
{
|
{
|
||||||
if value == "__main__" {
|
if value == "__main__" {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -122,7 +122,7 @@ pub(crate) fn if_else_block_instead_of_dict_get(checker: &mut Checker, stmt_if:
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let [test_dict] = test_dict.as_slice() else {
|
let [test_dict] = &**test_dict else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let (expected_var, expected_value, default_var, default_value) = match ops[..] {
|
let (expected_var, expected_value, default_var, default_value) = match ops[..] {
|
||||||
|
@ -176,8 +176,8 @@ pub(crate) fn if_else_block_instead_of_dict_get(checker: &mut Checker, stmt_if:
|
||||||
let node3 = ast::ExprCall {
|
let node3 = ast::ExprCall {
|
||||||
func: Box::new(node2.into()),
|
func: Box::new(node2.into()),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: vec![node1, node],
|
args: Box::from([node1, node]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
@ -233,11 +233,11 @@ pub(crate) fn if_exp_instead_of_dict_get(
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let [test_dict] = test_dict.as_slice() else {
|
let [test_dict] = &**test_dict else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let (body, default_value) = match ops.as_slice() {
|
let (body, default_value) = match &**ops {
|
||||||
[CmpOp::In] => (body, orelse),
|
[CmpOp::In] => (body, orelse),
|
||||||
[CmpOp::NotIn] => (orelse, body),
|
[CmpOp::NotIn] => (orelse, body),
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -276,8 +276,8 @@ pub(crate) fn if_exp_instead_of_dict_get(
|
||||||
let fixed_node = ast::ExprCall {
|
let fixed_node = ast::ExprCall {
|
||||||
func: Box::new(dict_get_node.into()),
|
func: Box::new(dict_get_node.into()),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: vec![dict_key_node, default_value_node],
|
args: Box::from([dict_key_node, default_value_node]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -64,10 +64,10 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &mut Checker, stmt_i
|
||||||
let Expr::Name(ast::ExprName { id: target, .. }) = left.as_ref() else {
|
let Expr::Name(ast::ExprName { id: target, .. }) = left.as_ref() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if ops != &[CmpOp::Eq] {
|
if **ops != [CmpOp::Eq] {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let [expr] = comparators.as_slice() else {
|
let [expr] = &**comparators else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(literal_expr) = expr.as_literal_expr() else {
|
let Some(literal_expr) = expr.as_literal_expr() else {
|
||||||
|
@ -127,10 +127,10 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &mut Checker, stmt_i
|
||||||
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 != &[CmpOp::Eq] {
|
if id != target || **ops != [CmpOp::Eq] {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let [expr] = comparators.as_slice() else {
|
let [expr] = &**comparators else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(literal_expr) = expr.as_literal_expr() else {
|
let Some(literal_expr) = expr.as_literal_expr() else {
|
||||||
|
|
|
@ -194,7 +194,7 @@ pub(crate) fn key_in_dict_comprehension(checker: &mut Checker, comprehension: &C
|
||||||
|
|
||||||
/// SIM118 in a comparison.
|
/// SIM118 in a comparison.
|
||||||
pub(crate) fn key_in_dict_compare(checker: &mut Checker, compare: &ast::ExprCompare) {
|
pub(crate) fn key_in_dict_compare(checker: &mut Checker, compare: &ast::ExprCompare) {
|
||||||
let [op] = compare.ops.as_slice() else {
|
let [op] = &*compare.ops else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ pub(crate) fn key_in_dict_compare(checker: &mut Checker, compare: &ast::ExprComp
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [right] = compare.comparators.as_slice() else {
|
let [right] = &*compare.comparators else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -161,8 +161,8 @@ pub(crate) fn needless_bool(checker: &mut Checker, stmt_if: &ast::StmtIf) {
|
||||||
let value_node = ast::ExprCall {
|
let value_node = ast::ExprCall {
|
||||||
func: Box::new(func_node.into()),
|
func: Box::new(func_node.into()),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: vec![if_test.clone()],
|
args: Box::from([if_test.clone()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -140,7 +140,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) {
|
||||||
range: _,
|
range: _,
|
||||||
}) = &loop_.test
|
}) = &loop_.test
|
||||||
{
|
{
|
||||||
if let ([op], [comparator]) = (ops.as_slice(), comparators.as_slice()) {
|
if let ([op], [comparator]) = (&**ops, &**comparators) {
|
||||||
let op = match op {
|
let op = match op {
|
||||||
CmpOp::Eq => CmpOp::NotEq,
|
CmpOp::Eq => CmpOp::NotEq,
|
||||||
CmpOp::NotEq => CmpOp::Eq,
|
CmpOp::NotEq => CmpOp::Eq,
|
||||||
|
@ -155,8 +155,8 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) {
|
||||||
};
|
};
|
||||||
let node = ast::ExprCompare {
|
let node = ast::ExprCompare {
|
||||||
left: left.clone(),
|
left: left.clone(),
|
||||||
ops: vec![op],
|
ops: Box::from([op]),
|
||||||
comparators: vec![comparator.clone()],
|
comparators: Box::from([comparator.clone()]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
};
|
};
|
||||||
node.into()
|
node.into()
|
||||||
|
@ -391,8 +391,8 @@ fn return_stmt(id: &str, test: &Expr, target: &Expr, iter: &Expr, generator: Gen
|
||||||
let node2 = ast::ExprCall {
|
let node2 = ast::ExprCall {
|
||||||
func: Box::new(node1.into()),
|
func: Box::new(node1.into()),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: vec![node.into()],
|
args: Box::from([node.into()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use ruff_python_ast::{self as ast, Arguments, Expr, ExprCall};
|
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use ruff_python_ast::{self as ast, Expr, ExprCall};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
@ -53,19 +52,15 @@ pub(crate) fn path_constructor_current_directory(checker: &mut Checker, expr: &E
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Expr::Call(ExprCall {
|
let Expr::Call(ExprCall { arguments, .. }) = expr else {
|
||||||
arguments: Arguments { args, keywords, .. },
|
|
||||||
..
|
|
||||||
}) = expr
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if !keywords.is_empty() {
|
if !arguments.keywords.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [Expr::StringLiteral(ast::ExprStringLiteral { value, range })] = args.as_slice() else {
|
let [Expr::StringLiteral(ast::ExprStringLiteral { value, range })] = &*arguments.args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ pub(crate) fn static_join_to_fstring(checker: &mut Checker, expr: &Expr, joiner:
|
||||||
if !keywords.is_empty() {
|
if !keywords.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let [arg] = args.as_slice() else {
|
let [arg] = &**args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ pub(crate) fn manual_list_comprehension(checker: &mut Checker, target: &Expr, bo
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [arg] = args.as_slice() else {
|
let [arg] = &**args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ pub(crate) fn manual_list_copy(checker: &mut Checker, target: &Expr, body: &[Stm
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [arg] = args.as_slice() else {
|
let [arg] = &**args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub(crate) fn unnecessary_list_cast(checker: &mut Checker, iter: &Expr, body: &[
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let [arg] = args.as_slice() else {
|
let [arg] = &**args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -139,10 +139,10 @@ pub(crate) fn literal_comparisons(checker: &mut Checker, compare: &ast::ExprComp
|
||||||
|
|
||||||
// Check `left`.
|
// Check `left`.
|
||||||
let mut comparator = compare.left.as_ref();
|
let mut comparator = compare.left.as_ref();
|
||||||
let [op, ..] = compare.ops.as_slice() else {
|
let [op, ..] = &*compare.ops else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let [next, ..] = compare.comparators.as_slice() else {
|
let [next, ..] = &*compare.comparators else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ pub(crate) fn not_tests(checker: &mut Checker, unary_op: &ast::ExprUnaryOp) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
match ops.as_slice() {
|
match &**ops {
|
||||||
[CmpOp::In] => {
|
[CmpOp::In] => {
|
||||||
if checker.enabled(Rule::NotInTest) {
|
if checker.enabled(Rule::NotInTest) {
|
||||||
let mut diagnostic = Diagnostic::new(NotInTest, unary_op.operand.range());
|
let mut diagnostic = Diagnostic::new(NotInTest, unary_op.operand.range());
|
||||||
|
|
|
@ -84,10 +84,10 @@ pub(crate) fn comparison_with_itself(
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let [Expr::Name(left_arg)] = left_call.arguments.args.as_slice() else {
|
let [Expr::Name(left_arg)] = &*left_call.arguments.args else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let [Expr::Name(right_right)] = right_call.arguments.args.as_slice() else {
|
let [Expr::Name(right_right)] = &*right_call.arguments.args else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if left_arg.id != right_right.id {
|
if left_arg.id != right_right.id {
|
||||||
|
|
|
@ -59,7 +59,7 @@ pub(crate) fn duplicate_bases(checker: &mut Checker, name: &str, arguments: Opti
|
||||||
|
|
||||||
let mut seen: FxHashSet<&str> =
|
let mut seen: FxHashSet<&str> =
|
||||||
FxHashSet::with_capacity_and_hasher(bases.len(), BuildHasherDefault::default());
|
FxHashSet::with_capacity_and_hasher(bases.len(), BuildHasherDefault::default());
|
||||||
for base in bases {
|
for base in bases.iter() {
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = base {
|
if let Expr::Name(ast::ExprName { id, .. }) = base {
|
||||||
if !seen.insert(id) {
|
if !seen.insert(id) {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl AlwaysFixableViolation for LiteralMembership {
|
||||||
|
|
||||||
/// PLR6201
|
/// PLR6201
|
||||||
pub(crate) fn literal_membership(checker: &mut Checker, compare: &ast::ExprCompare) {
|
pub(crate) fn literal_membership(checker: &mut Checker, compare: &ast::ExprCompare) {
|
||||||
let [op] = compare.ops.as_slice() else {
|
let [op] = &*compare.ops else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ pub(crate) fn literal_membership(checker: &mut Checker, compare: &ast::ExprCompa
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [right] = compare.comparators.as_slice() else {
|
let [right] = &*compare.comparators else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ fn collect_nested_args(min_max: MinMax, args: &[Expr], semantic: &SemanticModel)
|
||||||
range: _,
|
range: _,
|
||||||
}) = arg
|
}) = arg
|
||||||
{
|
{
|
||||||
if let [arg] = args.as_slice() {
|
if let [arg] = &**args {
|
||||||
if arg.as_starred_expr().is_none() {
|
if arg.as_starred_expr().is_none() {
|
||||||
let new_arg = Expr::Starred(ast::ExprStarred {
|
let new_arg = Expr::Starred(ast::ExprStarred {
|
||||||
value: Box::new(arg.clone()),
|
value: Box::new(arg.clone()),
|
||||||
|
@ -164,8 +164,8 @@ pub(crate) fn nested_min_max(
|
||||||
let flattened_expr = Expr::Call(ast::ExprCall {
|
let flattened_expr = Expr::Call(ast::ExprCall {
|
||||||
func: Box::new(func.clone()),
|
func: Box::new(func.clone()),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: collect_nested_args(min_max, args, checker.semantic()),
|
args: collect_nested_args(min_max, args, checker.semantic()).into_boxed_slice(),
|
||||||
keywords: keywords.to_owned(),
|
keywords: Box::from(keywords),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -96,7 +96,7 @@ pub(crate) fn repeated_equality_comparison(checker: &mut Checker, bool_op: &ast:
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enforced via `is_allowed_value`.
|
// Enforced via `is_allowed_value`.
|
||||||
let [right] = comparators.as_slice() else {
|
let [right] = &**comparators else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -136,14 +136,14 @@ pub(crate) fn repeated_equality_comparison(checker: &mut Checker, bool_op: &ast:
|
||||||
checker.generator().expr(&Expr::Compare(ast::ExprCompare {
|
checker.generator().expr(&Expr::Compare(ast::ExprCompare {
|
||||||
left: Box::new(value.as_expr().clone()),
|
left: Box::new(value.as_expr().clone()),
|
||||||
ops: match bool_op.op {
|
ops: match bool_op.op {
|
||||||
BoolOp::Or => vec![CmpOp::In],
|
BoolOp::Or => Box::from([CmpOp::In]),
|
||||||
BoolOp::And => vec![CmpOp::NotIn],
|
BoolOp::And => Box::from([CmpOp::NotIn]),
|
||||||
},
|
},
|
||||||
comparators: vec![Expr::Tuple(ast::ExprTuple {
|
comparators: Box::from([Expr::Tuple(ast::ExprTuple {
|
||||||
elts: comparators.iter().copied().cloned().collect(),
|
elts: comparators.iter().copied().cloned().collect(),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
ctx: ExprContext::Load,
|
ctx: ExprContext::Load,
|
||||||
})],
|
})]),
|
||||||
range: bool_op.range(),
|
range: bool_op.range(),
|
||||||
})),
|
})),
|
||||||
bool_op.range(),
|
bool_op.range(),
|
||||||
|
@ -169,7 +169,7 @@ fn is_allowed_value(bool_op: BoolOp, value: &Expr) -> bool {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ignore, e.g., `foo == bar == baz`.
|
// Ignore, e.g., `foo == bar == baz`.
|
||||||
let [op] = ops.as_slice() else {
|
let [op] = &**ops else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ fn is_allowed_value(bool_op: BoolOp, value: &Expr) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore self-comparisons, e.g., `foo == foo`.
|
// Ignore self-comparisons, e.g., `foo == foo`.
|
||||||
let [right] = comparators.as_slice() else {
|
let [right] = &**comparators else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if ComparableExpr::from(left) == ComparableExpr::from(right) {
|
if ComparableExpr::from(left) == ComparableExpr::from(right) {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
|
|
||||||
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
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::{Arguments, Expr, ExprCall, ExprDict, ExprStringLiteral};
|
use ruff_python_ast::{Expr, ExprCall, ExprDict, ExprStringLiteral};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
use rustc_hash::FxHashSet;
|
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
@ -37,15 +38,14 @@ impl Violation for RepeatedKeywordArgument {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn repeated_keyword_argument(checker: &mut Checker, call: &ExprCall) {
|
pub(crate) fn repeated_keyword_argument(checker: &mut Checker, call: &ExprCall) {
|
||||||
let ExprCall {
|
let ExprCall { arguments, .. } = call;
|
||||||
arguments: Arguments { keywords, .. },
|
|
||||||
..
|
|
||||||
} = call;
|
|
||||||
|
|
||||||
let mut seen =
|
let mut seen = FxHashSet::with_capacity_and_hasher(
|
||||||
FxHashSet::with_capacity_and_hasher(keywords.len(), BuildHasherDefault::default());
|
arguments.keywords.len(),
|
||||||
|
BuildHasherDefault::default(),
|
||||||
|
);
|
||||||
|
|
||||||
for keyword in keywords {
|
for keyword in arguments.keywords.iter() {
|
||||||
if let Some(id) = &keyword.arg {
|
if let Some(id) = &keyword.arg {
|
||||||
// Ex) `func(a=1, a=2)`
|
// Ex) `func(a=1, a=2)`
|
||||||
if !seen.insert(id.as_str()) {
|
if !seen.insert(id.as_str()) {
|
||||||
|
|
|
@ -111,7 +111,7 @@ pub(crate) fn unnecessary_dunder_call(checker: &mut Checker, call: &ast::ExprCal
|
||||||
let mut title: Option<String> = None;
|
let mut title: Option<String> = None;
|
||||||
|
|
||||||
if let Some(dunder) = DunderReplacement::from_method(attr) {
|
if let Some(dunder) = DunderReplacement::from_method(attr) {
|
||||||
match (call.arguments.args.as_slice(), dunder) {
|
match (&*call.arguments.args, dunder) {
|
||||||
([], DunderReplacement::Builtin(replacement, message)) => {
|
([], DunderReplacement::Builtin(replacement, message)) => {
|
||||||
if !checker.semantic().is_builtin(replacement) {
|
if !checker.semantic().is_builtin(replacement) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -216,8 +216,8 @@ fn create_class_def_stmt(typename: &str, body: Vec<Stmt>, base_class: &Expr) ->
|
||||||
ast::StmtClassDef {
|
ast::StmtClassDef {
|
||||||
name: Identifier::new(typename.to_string(), TextRange::default()),
|
name: Identifier::new(typename.to_string(), TextRange::default()),
|
||||||
arguments: Some(Box::new(Arguments {
|
arguments: Some(Box::new(Arguments {
|
||||||
args: vec![base_class.clone()],
|
args: Box::from([base_class.clone()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
})),
|
})),
|
||||||
body,
|
body,
|
||||||
|
|
|
@ -148,10 +148,10 @@ fn create_class_def_stmt(
|
||||||
ast::StmtClassDef {
|
ast::StmtClassDef {
|
||||||
name: Identifier::new(class_name.to_string(), TextRange::default()),
|
name: Identifier::new(class_name.to_string(), TextRange::default()),
|
||||||
arguments: Some(Box::new(Arguments {
|
arguments: Some(Box::new(Arguments {
|
||||||
args: vec![base_class.clone()],
|
args: Box::from([base_class.clone()]),
|
||||||
keywords: match total_keyword {
|
keywords: match total_keyword {
|
||||||
Some(keyword) => vec![keyword.clone()],
|
Some(keyword) => Box::from([keyword.clone()]),
|
||||||
None => vec![],
|
None => Box::from([]),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
})),
|
})),
|
||||||
|
@ -226,7 +226,7 @@ fn fields_from_keywords(keywords: &[Keyword]) -> Option<Vec<Stmt>> {
|
||||||
|
|
||||||
/// Match the fields and `total` keyword from a `TypedDict` call.
|
/// Match the fields and `total` keyword from a `TypedDict` call.
|
||||||
fn match_fields_and_total(arguments: &Arguments) -> Option<(Vec<Stmt>, Option<&Keyword>)> {
|
fn match_fields_and_total(arguments: &Arguments) -> Option<(Vec<Stmt>, Option<&Keyword>)> {
|
||||||
match (arguments.args.as_slice(), arguments.keywords.as_slice()) {
|
match (&*arguments.args, &*arguments.keywords) {
|
||||||
// Ex) `TypedDict("MyType", {"a": int, "b": str})`
|
// Ex) `TypedDict("MyType", {"a": int, "b": str})`
|
||||||
([_typename, fields], [..]) => {
|
([_typename, fields], [..]) => {
|
||||||
let total = arguments.find_keyword("total");
|
let total = arguments.find_keyword("total");
|
||||||
|
|
|
@ -71,7 +71,7 @@ impl<'a> FormatSummaryValues<'a> {
|
||||||
let mut extracted_args: Vec<&Expr> = Vec::new();
|
let mut extracted_args: Vec<&Expr> = Vec::new();
|
||||||
let mut extracted_kwargs: FxHashMap<&str, &Expr> = FxHashMap::default();
|
let mut extracted_kwargs: FxHashMap<&str, &Expr> = FxHashMap::default();
|
||||||
|
|
||||||
for arg in &call.arguments.args {
|
for arg in call.arguments.args.iter() {
|
||||||
if matches!(arg, Expr::Starred(..))
|
if matches!(arg, Expr::Starred(..))
|
||||||
|| contains_quotes(locator.slice(arg))
|
|| contains_quotes(locator.slice(arg))
|
||||||
|| locator.contains_line_break(arg.range())
|
|| locator.contains_line_break(arg.range())
|
||||||
|
@ -80,7 +80,7 @@ impl<'a> FormatSummaryValues<'a> {
|
||||||
}
|
}
|
||||||
extracted_args.push(arg);
|
extracted_args.push(arg);
|
||||||
}
|
}
|
||||||
for keyword in &call.arguments.keywords {
|
for keyword in call.arguments.keywords.iter() {
|
||||||
let Keyword {
|
let Keyword {
|
||||||
arg,
|
arg,
|
||||||
value,
|
value,
|
||||||
|
|
|
@ -90,7 +90,7 @@ pub(crate) fn outdated_version_block(checker: &mut Checker, stmt_if: &StmtIf) {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let ([op], [comparison]) = (ops.as_slice(), comparators.as_slice()) else {
|
let ([op], [comparison]) = (&**ops, &**comparators) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ pub(crate) fn super_call_with_parameters(checker: &mut Checker, call: &ast::Expr
|
||||||
// For a `super` invocation to be unnecessary, the first argument needs to match
|
// For a `super` invocation to be unnecessary, the first argument needs to match
|
||||||
// the enclosing class, and the second argument needs to match the first
|
// the enclosing class, and the second argument needs to match the first
|
||||||
// argument to the enclosing function.
|
// argument to the enclosing function.
|
||||||
let [first_arg, second_arg] = call.arguments.args.as_slice() else {
|
let [first_arg, second_arg] = &*call.arguments.args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ enum EncodingArg<'a> {
|
||||||
/// Return the encoding argument to an `encode` call, if it can be determined to be a
|
/// Return the encoding argument to an `encode` call, if it can be determined to be a
|
||||||
/// UTF-8-equivalent encoding.
|
/// UTF-8-equivalent encoding.
|
||||||
fn match_encoding_arg(arguments: &Arguments) -> Option<EncodingArg> {
|
fn match_encoding_arg(arguments: &Arguments) -> Option<EncodingArg> {
|
||||||
match (arguments.args.as_slice(), arguments.keywords.as_slice()) {
|
match (&*arguments.args, &*arguments.keywords) {
|
||||||
// Ex `"".encode()`
|
// Ex `"".encode()`
|
||||||
([], []) => return Some(EncodingArg::Empty),
|
([], []) => return Some(EncodingArg::Empty),
|
||||||
// Ex `"".encode(encoding)`
|
// Ex `"".encode(encoding)`
|
||||||
|
|
|
@ -50,7 +50,7 @@ pub(crate) fn useless_object_inheritance(checker: &mut Checker, class_def: &ast:
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
for base in &arguments.args {
|
for base in arguments.args.iter() {
|
||||||
let Expr::Name(ast::ExprName { id, .. }) = base else {
|
let Expr::Name(ast::ExprName { id, .. }) = base else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,8 +21,8 @@ pub(super) fn generate_method_call(name: &str, method: &str, generator: Generato
|
||||||
let call = ast::ExprCall {
|
let call = ast::ExprCall {
|
||||||
func: Box::new(attr.into()),
|
func: Box::new(attr.into()),
|
||||||
arguments: ast::Arguments {
|
arguments: ast::Arguments {
|
||||||
args: vec![],
|
args: Box::from([]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
@ -55,8 +55,8 @@ pub(super) fn generate_none_identity_comparison(
|
||||||
};
|
};
|
||||||
let compare = ast::ExprCompare {
|
let compare = ast::ExprCompare {
|
||||||
left: Box::new(var.into()),
|
left: Box::new(var.into()),
|
||||||
ops: vec![op],
|
ops: Box::from([op]),
|
||||||
comparators: vec![ast::Expr::NoneLiteral(ast::ExprNoneLiteral::default())],
|
comparators: Box::from([ast::Expr::NoneLiteral(ast::ExprNoneLiteral::default())]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
};
|
};
|
||||||
generator.expr(&compare.into())
|
generator.expr(&compare.into())
|
||||||
|
|
|
@ -74,7 +74,7 @@ pub(crate) fn bit_count(checker: &mut Checker, call: &ExprCall) {
|
||||||
if !call.arguments.keywords.is_empty() {
|
if !call.arguments.keywords.is_empty() {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let [arg] = call.arguments.args.as_slice() else {
|
let [arg] = &*call.arguments.args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ pub(crate) fn bit_count(checker: &mut Checker, call: &ExprCall) {
|
||||||
if !arguments.keywords.is_empty() {
|
if !arguments.keywords.is_empty() {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let [arg] = arguments.args.as_slice() else {
|
let [arg] = &*arguments.args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -132,11 +132,11 @@ fn match_check(if_stmt: &ast::StmtIf) -> Option<(&Expr, &ast::ExprName)> {
|
||||||
..
|
..
|
||||||
} = if_stmt.test.as_compare_expr()?;
|
} = if_stmt.test.as_compare_expr()?;
|
||||||
|
|
||||||
if ops.as_slice() != [CmpOp::In] {
|
if **ops != [CmpOp::In] {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [Expr::Name(right @ ast::ExprName { .. })] = comparators.as_slice() else {
|
let [Expr::Name(right @ ast::ExprName { .. })] = &**comparators else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ fn match_remove(if_stmt: &ast::StmtIf) -> Option<(&Expr, &ast::ExprName)> {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
let [arg] = args.as_slice() else {
|
let [arg] = &**args else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -191,8 +191,8 @@ fn make_suggestion(set: &ast::ExprName, element: &Expr, generator: Generator) ->
|
||||||
let call = ast::ExprCall {
|
let call = ast::ExprCall {
|
||||||
func: Box::new(attr.into()),
|
func: Box::new(attr.into()),
|
||||||
arguments: ast::Arguments {
|
arguments: ast::Arguments {
|
||||||
args: vec![element.clone()],
|
args: Box::from([element.clone()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -88,7 +88,7 @@ pub(crate) fn if_expr_min_max(checker: &mut Checker, if_exp: &ast::ExprIfExp) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ignore, e.g., `foo < bar < baz`.
|
// Ignore, e.g., `foo < bar < baz`.
|
||||||
let [op] = ops.as_slice() else {
|
let [op] = &**ops else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ pub(crate) fn if_expr_min_max(checker: &mut Checker, if_exp: &ast::ExprIfExp) {
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let [right] = comparators.as_slice() else {
|
let [right] = &**comparators else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ pub(crate) fn no_implicit_cwd(checker: &mut Checker, call: &ExprCall) {
|
||||||
|
|
||||||
// Match on arguments, but ignore keyword arguments. `Path()` accepts keyword arguments, but
|
// Match on arguments, but ignore keyword arguments. `Path()` accepts keyword arguments, but
|
||||||
// ignores them. See: https://github.com/python/cpython/issues/98094.
|
// ignores them. See: https://github.com/python/cpython/issues/98094.
|
||||||
match arguments.args.as_slice() {
|
match &*arguments.args {
|
||||||
// Ex) `Path().resolve()`
|
// Ex) `Path().resolve()`
|
||||||
[] => {}
|
[] => {}
|
||||||
// Ex) `Path(".").resolve()`
|
// Ex) `Path(".").resolve()`
|
||||||
|
|
|
@ -79,7 +79,7 @@ pub(crate) fn print_empty_string(checker: &mut Checker, call: &ast::ExprCall) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match &call.arguments.args.as_slice() {
|
match &*call.arguments.args {
|
||||||
// Ex) `print("")` or `print("", sep="\t")`
|
// Ex) `print("")` or `print("", sep="\t")`
|
||||||
[arg] if is_empty_string(arg) => {
|
[arg] if is_empty_string(arg) => {
|
||||||
let reason = if call.arguments.find_keyword("sep").is_some() {
|
let reason = if call.arguments.find_keyword("sep").is_some() {
|
||||||
|
@ -211,16 +211,30 @@ fn generate_suggestion(call: &ast::ExprCall, separator: Separator, generator: Ge
|
||||||
let mut call = call.clone();
|
let mut call = call.clone();
|
||||||
|
|
||||||
// Remove all empty string positional arguments.
|
// Remove all empty string positional arguments.
|
||||||
call.arguments.args.retain(|arg| !is_empty_string(arg));
|
call.arguments.args = call
|
||||||
|
.arguments
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.filter(|arg| !is_empty_string(arg))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_boxed_slice();
|
||||||
|
|
||||||
// Remove the `sep` keyword argument if it exists.
|
// Remove the `sep` keyword argument if it exists.
|
||||||
if separator == Separator::Remove {
|
if separator == Separator::Remove {
|
||||||
call.arguments.keywords.retain(|keyword| {
|
call.arguments.keywords = call
|
||||||
keyword
|
.arguments
|
||||||
.arg
|
.keywords
|
||||||
.as_ref()
|
.iter()
|
||||||
.map_or(true, |arg| arg.as_str() != "sep")
|
.filter(|keyword| {
|
||||||
});
|
keyword
|
||||||
|
.arg
|
||||||
|
.as_ref()
|
||||||
|
.map_or(true, |arg| arg.as_str() != "sep")
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_boxed_slice();
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.expr(&call.into())
|
generator.expr(&call.into())
|
||||||
|
|
|
@ -322,7 +322,7 @@ fn make_suggestion(open: &FileOpen<'_>, generator: Generator) -> SourceCodeSnipp
|
||||||
let call = ast::ExprCall {
|
let call = ast::ExprCall {
|
||||||
func: Box::new(name.into()),
|
func: Box::new(name.into()),
|
||||||
arguments: ast::Arguments {
|
arguments: ast::Arguments {
|
||||||
args: vec![],
|
args: Box::from([]),
|
||||||
keywords: open.keywords.iter().copied().cloned().collect(),
|
keywords: open.keywords.iter().copied().cloned().collect(),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -70,7 +70,7 @@ pub(crate) fn redundant_log_base(checker: &mut Checker, call: &ast::ExprCall) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [arg, base] = &call.arguments.args.as_slice() else {
|
let [arg, base] = &*call.arguments.args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -232,10 +232,10 @@ fn cmp_op(expr: &ast::ExprCompare, params: &ast::Parameters) -> Option<&'static
|
||||||
let [arg1, arg2] = params.args.as_slice() else {
|
let [arg1, arg2] = params.args.as_slice() else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let [op] = expr.ops.as_slice() else {
|
let [op] = &*expr.ops else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let [right] = expr.comparators.as_slice() else {
|
let [right] = &*expr.comparators else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -304,8 +304,8 @@ fn construct_starmap_call(starmap_binding: String, iter: &Expr, func: &Expr) ->
|
||||||
ast::ExprCall {
|
ast::ExprCall {
|
||||||
func: Box::new(starmap.into()),
|
func: Box::new(starmap.into()),
|
||||||
arguments: ast::Arguments {
|
arguments: ast::Arguments {
|
||||||
args: vec![func.clone(), iter.clone()],
|
args: Box::from([func.clone(), iter.clone()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
@ -322,8 +322,8 @@ fn wrap_with_call_to(call: ast::ExprCall, func_name: &str) -> ast::ExprCall {
|
||||||
ast::ExprCall {
|
ast::ExprCall {
|
||||||
func: Box::new(name.into()),
|
func: Box::new(name.into()),
|
||||||
arguments: ast::Arguments {
|
arguments: ast::Arguments {
|
||||||
args: vec![call.into()],
|
args: Box::from([call.into()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -280,7 +280,7 @@ fn match_append<'a>(semantic: &'a SemanticModel, stmt: &'a Stmt) -> Option<Appen
|
||||||
};
|
};
|
||||||
|
|
||||||
// `append` should have just one argument, an element to be added.
|
// `append` should have just one argument, an element to be added.
|
||||||
let [argument] = arguments.args.as_slice() else {
|
let [argument] = &*arguments.args else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -360,8 +360,8 @@ fn make_suggestion(group: &AppendGroup, generator: Generator) -> String {
|
||||||
let call = ast::ExprCall {
|
let call = ast::ExprCall {
|
||||||
func: Box::new(attr.into()),
|
func: Box::new(attr.into()),
|
||||||
arguments: ast::Arguments {
|
arguments: ast::Arguments {
|
||||||
args: vec![tuple.into()],
|
args: Box::from([tuple.into()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -59,7 +59,7 @@ impl Violation for TypeNoneComparison {
|
||||||
|
|
||||||
/// FURB169
|
/// FURB169
|
||||||
pub(crate) fn type_none_comparison(checker: &mut Checker, compare: &ast::ExprCompare) {
|
pub(crate) fn type_none_comparison(checker: &mut Checker, compare: &ast::ExprCompare) {
|
||||||
let ([op], [right]) = (compare.ops.as_slice(), compare.comparators.as_slice()) else {
|
let ([op], [right]) = (&*compare.ops, &*compare.comparators) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -251,8 +251,8 @@ fn generate_range_len_call(name: &str, generator: Generator) -> String {
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: vec![var.into()],
|
args: Box::from([var.into()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
@ -268,8 +268,8 @@ fn generate_range_len_call(name: &str, generator: Generator) -> String {
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
args: vec![len.into()],
|
args: Box::from([len.into()]),
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
},
|
},
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
|
|
|
@ -88,7 +88,7 @@ pub(crate) fn explicit_f_string_type_conversion(checker: &mut Checker, f_string:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't be a conversion otherwise.
|
// Can't be a conversion otherwise.
|
||||||
let [arg] = args.as_slice() else {
|
let [arg] = &**args else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -123,12 +123,12 @@ fn should_be_fstring(
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for keyword in keywords {
|
for keyword in keywords.iter() {
|
||||||
if let Some(ident) = keyword.arg.as_ref() {
|
if let Some(ident) = keyword.arg.as_ref() {
|
||||||
arg_names.insert(ident.as_str());
|
arg_names.insert(ident.as_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for arg in args {
|
for arg in args.iter() {
|
||||||
if let ast::Expr::Name(ast::ExprName { id, .. }) = arg {
|
if let ast::Expr::Name(ast::ExprName { id, .. }) = arg {
|
||||||
arg_names.insert(id.as_str());
|
arg_names.insert(id.as_str());
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ pub(crate) fn mutable_fromkeys_value(checker: &mut Checker, call: &ast::ExprCall
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the value parameter is a mutable object.
|
// Check that the value parameter is a mutable object.
|
||||||
let [keys, value] = call.arguments.args.as_slice() else {
|
let [keys, value] = &*call.arguments.args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !is_mutable_expr(value, checker.semantic()) {
|
if !is_mutable_expr(value, checker.semantic()) {
|
||||||
|
|
|
@ -107,7 +107,7 @@ pub(crate) fn sort_dunder_all_extend_call(
|
||||||
..
|
..
|
||||||
}: &ast::ExprCall,
|
}: &ast::ExprCall,
|
||||||
) {
|
) {
|
||||||
let ([value_passed], []) = (args.as_slice(), keywords.as_slice()) else {
|
let ([value_passed], []) = (&**args, &**keywords) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let ast::Expr::Attribute(ast::ExprAttribute {
|
let ast::Expr::Attribute(ast::ExprAttribute {
|
||||||
|
|
|
@ -160,11 +160,11 @@ fn fix_unnecessary_dict_comprehension(value: &Expr, generator: &Comprehension) -
|
||||||
let iterable = generator.iter.clone();
|
let iterable = generator.iter.clone();
|
||||||
let args = Arguments {
|
let args = Arguments {
|
||||||
args: if value.is_none_literal_expr() {
|
args: if value.is_none_literal_expr() {
|
||||||
vec![iterable]
|
Box::from([iterable])
|
||||||
} else {
|
} else {
|
||||||
vec![iterable, value.clone()]
|
Box::from([iterable, value.clone()])
|
||||||
},
|
},
|
||||||
keywords: vec![],
|
keywords: Box::from([]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
};
|
};
|
||||||
Expr::Call(ExprCall {
|
Expr::Call(ExprCall {
|
||||||
|
|
|
@ -146,7 +146,7 @@ fn match_iteration_target(expr: &Expr, semantic: &SemanticModel) -> Option<Itera
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [arg] = args.as_slice() else {
|
let [arg] = &**args else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -72,11 +72,11 @@ pub(crate) fn unnecessary_key_check(checker: &mut Checker, expr: &Expr) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if !matches!(ops.as_slice(), [CmpOp::In]) {
|
if !matches!(&**ops, [CmpOp::In]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [obj_left] = comparators.as_slice() else {
|
let [obj_left] = &**comparators else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -60,16 +60,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Call(ast::ExprCall {
|
Expr::Call(ast::ExprCall {
|
||||||
func,
|
func, arguments, ..
|
||||||
arguments: ast::Arguments { args, keywords, .. },
|
|
||||||
..
|
|
||||||
}) => {
|
}) => {
|
||||||
// Allow `tuple()`, `list()`, and their generic forms, like `list[int]()`.
|
// Allow `tuple()`, `list()`, and their generic forms, like `list[int]()`.
|
||||||
if keywords.is_empty() && args.len() <= 1 {
|
if arguments.keywords.is_empty() && arguments.args.len() <= 1 {
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = map_subscript(func) {
|
if let Expr::Name(ast::ExprName { id, .. }) = map_subscript(func) {
|
||||||
let id = id.as_str();
|
let id = id.as_str();
|
||||||
if matches!(id, "tuple" | "list") && is_builtin(id) {
|
if matches!(id, "tuple" | "list") && is_builtin(id) {
|
||||||
let [arg] = args.as_slice() else {
|
let [arg] = arguments.args.as_ref() else {
|
||||||
return (None, DunderAllFlags::empty());
|
return (None, DunderAllFlags::empty());
|
||||||
};
|
};
|
||||||
match arg {
|
match arg {
|
||||||
|
|
|
@ -52,12 +52,12 @@ where
|
||||||
// Accept empty initializers.
|
// Accept empty initializers.
|
||||||
if let Expr::Call(ast::ExprCall {
|
if let Expr::Call(ast::ExprCall {
|
||||||
func,
|
func,
|
||||||
arguments: Arguments { args, keywords, .. },
|
arguments,
|
||||||
range: _,
|
range: _,
|
||||||
}) = expr
|
}) = expr
|
||||||
{
|
{
|
||||||
// Ex) `list()`
|
// Ex) `list()`
|
||||||
if args.is_empty() && keywords.is_empty() {
|
if arguments.is_empty() {
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
|
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
|
||||||
if !is_iterable_initializer(id.as_str(), |id| is_builtin(id)) {
|
if !is_iterable_initializer(id.as_str(), |id| is_builtin(id)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -221,14 +221,14 @@ pub fn any_over_expr(expr: &Expr, func: &dyn Fn(&Expr) -> bool) -> bool {
|
||||||
}) => any_over_expr(left, func) || comparators.iter().any(|expr| any_over_expr(expr, func)),
|
}) => any_over_expr(left, func) || comparators.iter().any(|expr| any_over_expr(expr, func)),
|
||||||
Expr::Call(ast::ExprCall {
|
Expr::Call(ast::ExprCall {
|
||||||
func: call_func,
|
func: call_func,
|
||||||
arguments: Arguments { args, keywords, .. },
|
arguments,
|
||||||
range: _,
|
range: _,
|
||||||
}) => {
|
}) => {
|
||||||
any_over_expr(call_func, func)
|
any_over_expr(call_func, func)
|
||||||
// Note that this is the evaluation order but not necessarily the declaration order
|
// Note that this is the evaluation order but not necessarily the declaration order
|
||||||
// (e.g. for `f(*args, a=2, *args2, **kwargs)` it's not)
|
// (e.g. for `f(*args, a=2, *args2, **kwargs)` it's not)
|
||||||
|| args.iter().any(|expr| any_over_expr(expr, func))
|
|| arguments.args.iter().any(|expr| any_over_expr(expr, func))
|
||||||
|| keywords
|
|| arguments.keywords
|
||||||
.iter()
|
.iter()
|
||||||
.any(|keyword| any_over_expr(&keyword.value, func))
|
.any(|keyword| any_over_expr(&keyword.value, func))
|
||||||
}
|
}
|
||||||
|
@ -1227,18 +1227,16 @@ impl Truthiness {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Call(ast::ExprCall {
|
Expr::Call(ast::ExprCall {
|
||||||
func,
|
func, arguments, ..
|
||||||
arguments: Arguments { args, keywords, .. },
|
|
||||||
..
|
|
||||||
}) => {
|
}) => {
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
|
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
|
||||||
if is_iterable_initializer(id.as_str(), |id| is_builtin(id)) {
|
if is_iterable_initializer(id.as_str(), |id| is_builtin(id)) {
|
||||||
if args.is_empty() && keywords.is_empty() {
|
if arguments.is_empty() {
|
||||||
// Ex) `list()`
|
// Ex) `list()`
|
||||||
Self::Falsey
|
Self::Falsey
|
||||||
} else if args.len() == 1 && keywords.is_empty() {
|
} else if arguments.args.len() == 1 && arguments.keywords.is_empty() {
|
||||||
// Ex) `list([1, 2, 3])`
|
// Ex) `list([1, 2, 3])`
|
||||||
Self::from_expr(&args[0], is_builtin)
|
Self::from_expr(&arguments.args[0], is_builtin)
|
||||||
} else {
|
} else {
|
||||||
Self::Unknown
|
Self::Unknown
|
||||||
}
|
}
|
||||||
|
|
|
@ -2588,7 +2588,7 @@ impl AstNode for ast::ExprCompare {
|
||||||
|
|
||||||
visitor.visit_expr(left);
|
visitor.visit_expr(left);
|
||||||
|
|
||||||
for (op, comparator) in ops.iter().zip(comparators) {
|
for (op, comparator) in ops.iter().zip(&**comparators) {
|
||||||
visitor.visit_cmp_op(op);
|
visitor.visit_cmp_op(op);
|
||||||
visitor.visit_expr(comparator);
|
visitor.visit_expr(comparator);
|
||||||
}
|
}
|
||||||
|
|
|
@ -894,8 +894,8 @@ impl From<ExprYieldFrom> for Expr {
|
||||||
pub struct ExprCompare {
|
pub struct ExprCompare {
|
||||||
pub range: TextRange,
|
pub range: TextRange,
|
||||||
pub left: Box<Expr>,
|
pub left: Box<Expr>,
|
||||||
pub ops: Vec<CmpOp>,
|
pub ops: Box<[CmpOp]>,
|
||||||
pub comparators: Vec<Expr>,
|
pub comparators: Box<[Expr]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ExprCompare> for Expr {
|
impl From<ExprCompare> for Expr {
|
||||||
|
@ -2987,8 +2987,8 @@ pub struct ParameterWithDefault {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Arguments {
|
pub struct Arguments {
|
||||||
pub range: TextRange,
|
pub range: TextRange,
|
||||||
pub args: Vec<Expr>,
|
pub args: Box<[Expr]>,
|
||||||
pub keywords: Vec<Keyword>,
|
pub keywords: Box<[Keyword]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An entry in the argument list of a function call.
|
/// An entry in the argument list of a function call.
|
||||||
|
@ -3894,10 +3894,42 @@ mod tests {
|
||||||
assert!(std::mem::size_of::<StmtFunctionDef>() <= 144);
|
assert!(std::mem::size_of::<StmtFunctionDef>() <= 144);
|
||||||
assert!(std::mem::size_of::<StmtClassDef>() <= 104);
|
assert!(std::mem::size_of::<StmtClassDef>() <= 104);
|
||||||
assert!(std::mem::size_of::<StmtTry>() <= 112);
|
assert!(std::mem::size_of::<StmtTry>() <= 112);
|
||||||
// 80 for Rustc < 1.76
|
assert!(std::mem::size_of::<Mod>() <= 32);
|
||||||
assert!(matches!(std::mem::size_of::<Expr>(), 72 | 80));
|
|
||||||
// 96 for Rustc < 1.76
|
// 96 for Rustc < 1.76
|
||||||
assert!(matches!(std::mem::size_of::<Pattern>(), 88 | 96));
|
assert!(matches!(std::mem::size_of::<Pattern>(), 88 | 96));
|
||||||
assert!(std::mem::size_of::<Mod>() <= 32);
|
|
||||||
|
assert_eq!(std::mem::size_of::<Expr>(), 64);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprAttribute>(), 56);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprAwait>(), 16);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprBinOp>(), 32);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprBoolOp>(), 40);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprBooleanLiteral>(), 12);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprBytesLiteral>(), 40);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprCall>(), 56);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprCompare>(), 48);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprDict>(), 56);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprDictComp>(), 48);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprEllipsisLiteral>(), 8);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprFString>(), 48);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprGeneratorExp>(), 40);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprIfExp>(), 32);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprIpyEscapeCommand>(), 32);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprLambda>(), 24);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprList>(), 40);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprListComp>(), 40);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprName>(), 40);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprNamedExpr>(), 24);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprNoneLiteral>(), 8);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprNumberLiteral>(), 32);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprSet>(), 32);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprSetComp>(), 40);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprSlice>(), 32);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprStarred>(), 24);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprStringLiteral>(), 48);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprSubscript>(), 32);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprTuple>(), 40);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprUnaryOp>(), 24);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprYield>(), 16);
|
||||||
|
assert_eq!(std::mem::size_of::<ExprYieldFrom>(), 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -461,10 +461,10 @@ pub fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a Expr) {
|
||||||
range: _,
|
range: _,
|
||||||
}) => {
|
}) => {
|
||||||
visitor.visit_expr(left);
|
visitor.visit_expr(left);
|
||||||
for cmp_op in ops {
|
for cmp_op in &**ops {
|
||||||
visitor.visit_cmp_op(cmp_op);
|
visitor.visit_cmp_op(cmp_op);
|
||||||
}
|
}
|
||||||
for expr in comparators {
|
for expr in &**comparators {
|
||||||
visitor.visit_expr(expr);
|
visitor.visit_expr(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -594,10 +594,10 @@ pub fn walk_arguments<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, arguments: &
|
||||||
// Note that the there might be keywords before the last arg, e.g. in
|
// Note that the there might be keywords before the last arg, e.g. in
|
||||||
// f(*args, a=2, *args2, **kwargs)`, but we follow Python in evaluating first `args` and then
|
// f(*args, a=2, *args2, **kwargs)`, but we follow Python in evaluating first `args` and then
|
||||||
// `keywords`. See also [Arguments::arguments_source_order`].
|
// `keywords`. See also [Arguments::arguments_source_order`].
|
||||||
for arg in &arguments.args {
|
for arg in arguments.args.iter() {
|
||||||
visitor.visit_expr(arg);
|
visitor.visit_expr(arg);
|
||||||
}
|
}
|
||||||
for keyword in &arguments.keywords {
|
for keyword in arguments.keywords.iter() {
|
||||||
visitor.visit_keyword(keyword);
|
visitor.visit_keyword(keyword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,10 +448,10 @@ pub fn walk_expr<V: Transformer + ?Sized>(visitor: &V, expr: &mut Expr) {
|
||||||
range: _,
|
range: _,
|
||||||
}) => {
|
}) => {
|
||||||
visitor.visit_expr(left);
|
visitor.visit_expr(left);
|
||||||
for cmp_op in ops {
|
for cmp_op in &mut **ops {
|
||||||
visitor.visit_cmp_op(cmp_op);
|
visitor.visit_cmp_op(cmp_op);
|
||||||
}
|
}
|
||||||
for expr in comparators {
|
for expr in &mut **comparators {
|
||||||
visitor.visit_expr(expr);
|
visitor.visit_expr(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -580,10 +580,10 @@ pub fn walk_arguments<V: Transformer + ?Sized>(visitor: &V, arguments: &mut Argu
|
||||||
// Note that the there might be keywords before the last arg, e.g. in
|
// Note that the there might be keywords before the last arg, e.g. in
|
||||||
// f(*args, a=2, *args2, **kwargs)`, but we follow Python in evaluating first `args` and then
|
// f(*args, a=2, *args2, **kwargs)`, but we follow Python in evaluating first `args` and then
|
||||||
// `keywords`. See also [Arguments::arguments_source_order`].
|
// `keywords`. See also [Arguments::arguments_source_order`].
|
||||||
for arg in &mut arguments.args {
|
for arg in arguments.args.iter_mut() {
|
||||||
visitor.visit_expr(arg);
|
visitor.visit_expr(arg);
|
||||||
}
|
}
|
||||||
for keyword in &mut arguments.keywords {
|
for keyword in arguments.keywords.iter_mut() {
|
||||||
visitor.visit_keyword(keyword);
|
visitor.visit_keyword(keyword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1007,7 +1007,7 @@ impl<'a> Generator<'a> {
|
||||||
group_if!(precedence::CMP, {
|
group_if!(precedence::CMP, {
|
||||||
let new_lvl = precedence::CMP + 1;
|
let new_lvl = precedence::CMP + 1;
|
||||||
self.unparse_expr(left, new_lvl);
|
self.unparse_expr(left, new_lvl);
|
||||||
for (op, cmp) in ops.iter().zip(comparators) {
|
for (op, cmp) in ops.iter().zip(&**comparators) {
|
||||||
let op = match op {
|
let op = match op {
|
||||||
CmpOp::Eq => " == ",
|
CmpOp::Eq => " == ",
|
||||||
CmpOp::NotEq => " != ",
|
CmpOp::NotEq => " != ",
|
||||||
|
@ -1039,7 +1039,7 @@ impl<'a> Generator<'a> {
|
||||||
range: _,
|
range: _,
|
||||||
})],
|
})],
|
||||||
[],
|
[],
|
||||||
) = (arguments.args.as_slice(), arguments.keywords.as_slice())
|
) = (arguments.args.as_ref(), arguments.keywords.as_ref())
|
||||||
{
|
{
|
||||||
// Ensure that a single generator doesn't get double-parenthesized.
|
// Ensure that a single generator doesn't get double-parenthesized.
|
||||||
self.unparse_expr(elt, precedence::COMMA);
|
self.unparse_expr(elt, precedence::COMMA);
|
||||||
|
|
|
@ -38,7 +38,7 @@ impl FormatNodeRule<Arguments> for FormatArguments {
|
||||||
let all_arguments = format_with(|f: &mut PyFormatter| {
|
let all_arguments = format_with(|f: &mut PyFormatter| {
|
||||||
let source = f.context().source();
|
let source = f.context().source();
|
||||||
let mut joiner = f.join_comma_separated(range.end());
|
let mut joiner = f.join_comma_separated(range.end());
|
||||||
match args.as_slice() {
|
match args.as_ref() {
|
||||||
[arg] if keywords.is_empty() => {
|
[arg] if keywords.is_empty() => {
|
||||||
match arg {
|
match arg {
|
||||||
Expr::GeneratorExp(generator_exp) => joiner.entry(
|
Expr::GeneratorExp(generator_exp) => joiner.entry(
|
||||||
|
@ -180,7 +180,7 @@ fn is_single_argument_parenthesized(argument: &Expr, call_end: TextSize, source:
|
||||||
/// of those collections.
|
/// of those collections.
|
||||||
fn is_arguments_huggable(arguments: &Arguments, context: &PyFormatContext) -> bool {
|
fn is_arguments_huggable(arguments: &Arguments, context: &PyFormatContext) -> bool {
|
||||||
// Find the lone argument or `**kwargs` keyword.
|
// Find the lone argument or `**kwargs` keyword.
|
||||||
let arg = match (arguments.args.as_slice(), arguments.keywords.as_slice()) {
|
let arg = match (arguments.args.as_ref(), arguments.keywords.as_ref()) {
|
||||||
([arg], []) => arg,
|
([arg], []) => arg,
|
||||||
([], [keyword]) if keyword.arg.is_none() && !context.comments().has(keyword) => {
|
([], [keyword]) if keyword.arg.is_none() && !context.comments().has(keyword) => {
|
||||||
&keyword.value
|
&keyword.value
|
||||||
|
|
|
@ -81,16 +81,16 @@ type FunctionArgument = (
|
||||||
pub(crate) fn parse_arguments(
|
pub(crate) fn parse_arguments(
|
||||||
function_arguments: Vec<FunctionArgument>,
|
function_arguments: Vec<FunctionArgument>,
|
||||||
) -> Result<ArgumentList, LexicalError> {
|
) -> Result<ArgumentList, LexicalError> {
|
||||||
let mut args = vec![];
|
// First, run through the comments to determine the number of positional and keyword arguments.
|
||||||
let mut keywords = vec![];
|
|
||||||
|
|
||||||
let mut keyword_names = FxHashSet::with_capacity_and_hasher(
|
let mut keyword_names = FxHashSet::with_capacity_and_hasher(
|
||||||
function_arguments.len(),
|
function_arguments.len(),
|
||||||
BuildHasherDefault::default(),
|
BuildHasherDefault::default(),
|
||||||
);
|
);
|
||||||
let mut double_starred = false;
|
let mut double_starred = false;
|
||||||
for (name, value) in function_arguments {
|
let mut num_args = 0;
|
||||||
if let Some((start, end, name)) = name {
|
let mut num_keywords = 0;
|
||||||
|
for (name, value) in &function_arguments {
|
||||||
|
if let Some((start, _end, name)) = name {
|
||||||
// Check for duplicate keyword arguments in the call.
|
// Check for duplicate keyword arguments in the call.
|
||||||
if let Some(keyword_name) = &name {
|
if let Some(keyword_name) = &name {
|
||||||
if !keyword_names.insert(keyword_name.to_string()) {
|
if !keyword_names.insert(keyword_name.to_string()) {
|
||||||
|
@ -98,21 +98,17 @@ pub(crate) fn parse_arguments(
|
||||||
LexicalErrorType::DuplicateKeywordArgumentError(
|
LexicalErrorType::DuplicateKeywordArgumentError(
|
||||||
keyword_name.to_string().into_boxed_str(),
|
keyword_name.to_string().into_boxed_str(),
|
||||||
),
|
),
|
||||||
start,
|
*start,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
double_starred = true;
|
double_starred = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
keywords.push(ast::Keyword {
|
num_keywords += 1;
|
||||||
arg: name,
|
|
||||||
value,
|
|
||||||
range: TextRange::new(start, end),
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// Positional arguments mustn't follow keyword arguments.
|
// Positional arguments mustn't follow keyword arguments.
|
||||||
if !keywords.is_empty() && !is_starred(&value) {
|
if num_keywords > 0 && !is_starred(value) {
|
||||||
return Err(LexicalError::new(
|
return Err(LexicalError::new(
|
||||||
LexicalErrorType::PositionalArgumentError,
|
LexicalErrorType::PositionalArgumentError,
|
||||||
value.start(),
|
value.start(),
|
||||||
|
@ -126,9 +122,26 @@ pub(crate) fn parse_arguments(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
num_args += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second, push the arguments into vectors of exact capacity. This avoids a vector resize later
|
||||||
|
// on when these vectors are boxed into slices.
|
||||||
|
let mut args = Vec::with_capacity(num_args);
|
||||||
|
let mut keywords = Vec::with_capacity(num_keywords);
|
||||||
|
for (name, value) in function_arguments {
|
||||||
|
if let Some((start, end, name)) = name {
|
||||||
|
keywords.push(ast::Keyword {
|
||||||
|
arg: name,
|
||||||
|
value,
|
||||||
|
range: TextRange::new(start, end),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
args.push(value);
|
args.push(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ArgumentList { args, keywords })
|
Ok(ArgumentList { args, keywords })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -569,8 +569,7 @@ mod tests {
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
#[test]
|
#[test]
|
||||||
fn size_assertions() {
|
fn size_assertions() {
|
||||||
// 80 with Rustc >= 1.76, 88 with Rustc < 1.76
|
assert_eq!(std::mem::size_of::<ParenthesizedExpr>(), 72);
|
||||||
assert!(matches!(std::mem::size_of::<ParenthesizedExpr>(), 80 | 88));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1406,8 +1406,18 @@ NotTest<Goal>: crate::parser::ParenthesizedExpr = {
|
||||||
|
|
||||||
Comparison<Goal>: crate::parser::ParenthesizedExpr = {
|
Comparison<Goal>: crate::parser::ParenthesizedExpr = {
|
||||||
<location:@L> <left:Expression<"all">> <comparisons:(CompOp Expression<"all">)+> <end_location:@R> => {
|
<location:@L> <left:Expression<"all">> <comparisons:(CompOp Expression<"all">)+> <end_location:@R> => {
|
||||||
let (ops, comparators) = comparisons.into_iter().map(|(op, comparator)| (op, ast::Expr::from(comparator))).unzip();
|
let mut ops = Vec::with_capacity(comparisons.len());
|
||||||
ast::ExprCompare { left: Box::new(left.into()), ops, comparators, range: (location..end_location).into() }.into()
|
let mut comparators = Vec::with_capacity(comparisons.len());
|
||||||
|
for (op, comparator) in comparisons {
|
||||||
|
ops.push(op);
|
||||||
|
comparators.push(comparator.into());
|
||||||
|
}
|
||||||
|
ast::ExprCompare {
|
||||||
|
left: Box::new(left.into()),
|
||||||
|
ops: ops.into_boxed_slice(),
|
||||||
|
comparators: comparators.into_boxed_slice(),
|
||||||
|
range: (location..end_location).into(),
|
||||||
|
}.into()
|
||||||
},
|
},
|
||||||
Expression<Goal>,
|
Expression<Goal>,
|
||||||
};
|
};
|
||||||
|
@ -1880,8 +1890,8 @@ Arguments: ast::Arguments = {
|
||||||
<location:@L> "(" <e: Comma<FunctionArgument>> ")" <end_location:@R> =>? {
|
<location:@L> "(" <e: Comma<FunctionArgument>> ")" <end_location:@R> =>? {
|
||||||
let ArgumentList { args, keywords } = parse_arguments(e)?;
|
let ArgumentList { args, keywords } = parse_arguments(e)?;
|
||||||
Ok(ast::Arguments {
|
Ok(ast::Arguments {
|
||||||
args,
|
args: args.into_boxed_slice(),
|
||||||
keywords,
|
keywords: keywords.into_boxed_slice(),
|
||||||
range: (location..end_location).into()
|
range: (location..end_location).into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// auto-generated: "lalrpop 0.20.0"
|
// auto-generated: "lalrpop 0.20.0"
|
||||||
// sha3: fd05d84d3b654796ff740a7f905ec0ae8915f43f952428717735481947ab55e1
|
// sha3: 02c60b5c591440061dda68775005d87a203b5448c205120bda1566a62fc2147c
|
||||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||||
use ruff_python_ast::{self as ast, Int, IpyEscapeKind};
|
use ruff_python_ast::{self as ast, Int, IpyEscapeKind};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -36771,8 +36771,8 @@ fn __action241<
|
||||||
{
|
{
|
||||||
let ArgumentList { args, keywords } = parse_arguments(e)?;
|
let ArgumentList { args, keywords } = parse_arguments(e)?;
|
||||||
Ok(ast::Arguments {
|
Ok(ast::Arguments {
|
||||||
args,
|
args: args.into_boxed_slice(),
|
||||||
keywords,
|
keywords: keywords.into_boxed_slice(),
|
||||||
range: (location..end_location).into()
|
range: (location..end_location).into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -40651,8 +40651,18 @@ fn __action515<
|
||||||
) -> crate::parser::ParenthesizedExpr
|
) -> crate::parser::ParenthesizedExpr
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
let (ops, comparators) = comparisons.into_iter().map(|(op, comparator)| (op, ast::Expr::from(comparator))).unzip();
|
let mut ops = Vec::with_capacity(comparisons.len());
|
||||||
ast::ExprCompare { left: Box::new(left.into()), ops, comparators, range: (location..end_location).into() }.into()
|
let mut comparators = Vec::with_capacity(comparisons.len());
|
||||||
|
for (op, comparator) in comparisons {
|
||||||
|
ops.push(op);
|
||||||
|
comparators.push(comparator.into());
|
||||||
|
}
|
||||||
|
ast::ExprCompare {
|
||||||
|
left: Box::new(left.into()),
|
||||||
|
ops: ops.into_boxed_slice(),
|
||||||
|
comparators: comparators.into_boxed_slice(),
|
||||||
|
range: (location..end_location).into(),
|
||||||
|
}.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40816,8 +40826,18 @@ fn __action526<
|
||||||
) -> crate::parser::ParenthesizedExpr
|
) -> crate::parser::ParenthesizedExpr
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
let (ops, comparators) = comparisons.into_iter().map(|(op, comparator)| (op, ast::Expr::from(comparator))).unzip();
|
let mut ops = Vec::with_capacity(comparisons.len());
|
||||||
ast::ExprCompare { left: Box::new(left.into()), ops, comparators, range: (location..end_location).into() }.into()
|
let mut comparators = Vec::with_capacity(comparisons.len());
|
||||||
|
for (op, comparator) in comparisons {
|
||||||
|
ops.push(op);
|
||||||
|
comparators.push(comparator.into());
|
||||||
|
}
|
||||||
|
ast::ExprCompare {
|
||||||
|
left: Box::new(left.into()),
|
||||||
|
ops: ops.into_boxed_slice(),
|
||||||
|
comparators: comparators.into_boxed_slice(),
|
||||||
|
range: (location..end_location).into(),
|
||||||
|
}.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue