mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:35 +00:00
Implement iter()
, len()
and is_empty()
for all display-literal AST nodes (#12807)
This commit is contained in:
parent
a99a45868c
commit
aa0db338d9
56 changed files with 304 additions and 240 deletions
|
@ -1290,8 +1290,8 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
|||
let Keyword { arg, value, .. } = keyword;
|
||||
match (arg.as_ref(), value) {
|
||||
// Ex) NamedTuple("a", **{"a": int})
|
||||
(None, Expr::Dict(ast::ExprDict { items, .. })) => {
|
||||
for ast::DictItem { key, value } in items {
|
||||
(None, Expr::Dict(dict)) => {
|
||||
for ast::DictItem { key, value } in dict {
|
||||
if let Some(key) = key.as_ref() {
|
||||
self.visit_non_type_definition(key);
|
||||
self.visit_type_definition(value);
|
||||
|
|
|
@ -151,16 +151,15 @@ pub(crate) fn add_to_dunder_all<'a>(
|
|||
stylist: &Stylist,
|
||||
) -> Vec<Edit> {
|
||||
let (insertion_point, export_prefix_length) = match expr {
|
||||
Expr::List(ExprList { elts, range, .. }) => (
|
||||
elts.last()
|
||||
.map_or(range.end() - "]".text_len(), Ranged::end),
|
||||
Expr::List(ExprList { elts, .. }) => (
|
||||
elts.last().map_or(expr.end() - "]".text_len(), Ranged::end),
|
||||
elts.len(),
|
||||
),
|
||||
Expr::Tuple(tup) if tup.parenthesized => (
|
||||
tup.elts
|
||||
.last()
|
||||
.map_or(tup.end() - ")".text_len(), Ranged::end),
|
||||
tup.elts.len(),
|
||||
tup.len(),
|
||||
),
|
||||
Expr::Tuple(tup) if !tup.parenthesized => (
|
||||
tup.elts
|
||||
|
@ -168,7 +167,7 @@ pub(crate) fn add_to_dunder_all<'a>(
|
|||
.expect("unparenthesized empty tuple is not possible")
|
||||
.range()
|
||||
.end(),
|
||||
tup.elts.len(),
|
||||
tup.len(),
|
||||
),
|
||||
_ => {
|
||||
// we don't know how to insert into this expression
|
||||
|
|
|
@ -122,17 +122,15 @@ fn is_identical_types(
|
|||
return_value: &Expr,
|
||||
semantic: &SemanticModel,
|
||||
) -> bool {
|
||||
if let (Some(response_mode_name_expr), Some(return_value_name_expr)) = (
|
||||
response_model_arg.as_name_expr(),
|
||||
return_value.as_name_expr(),
|
||||
) {
|
||||
if let (Expr::Name(response_mode_name_expr), Expr::Name(return_value_name_expr)) =
|
||||
(response_model_arg, return_value)
|
||||
{
|
||||
return semantic.resolve_name(response_mode_name_expr)
|
||||
== semantic.resolve_name(return_value_name_expr);
|
||||
}
|
||||
if let (Some(response_mode_subscript), Some(return_value_subscript)) = (
|
||||
response_model_arg.as_subscript_expr(),
|
||||
return_value.as_subscript_expr(),
|
||||
) {
|
||||
if let (Expr::Subscript(response_mode_subscript), Expr::Subscript(return_value_subscript)) =
|
||||
(response_model_arg, return_value)
|
||||
{
|
||||
return is_identical_types(
|
||||
&response_mode_subscript.value,
|
||||
&return_value_subscript.value,
|
||||
|
@ -143,15 +141,13 @@ fn is_identical_types(
|
|||
semantic,
|
||||
);
|
||||
}
|
||||
if let (Some(response_mode_tuple), Some(return_value_tuple)) = (
|
||||
response_model_arg.as_tuple_expr(),
|
||||
return_value.as_tuple_expr(),
|
||||
) {
|
||||
return response_mode_tuple.elts.len() == return_value_tuple.elts.len()
|
||||
if let (Expr::Tuple(response_mode_tuple), Expr::Tuple(return_value_tuple)) =
|
||||
(response_model_arg, return_value)
|
||||
{
|
||||
return response_mode_tuple.len() == return_value_tuple.len()
|
||||
&& response_mode_tuple
|
||||
.elts
|
||||
.iter()
|
||||
.zip(return_value_tuple.elts.iter())
|
||||
.zip(return_value_tuple)
|
||||
.all(|(x, y)| is_identical_types(x, y, semantic));
|
||||
}
|
||||
false
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Expr, ExprAttribute, ExprDict, ExprList};
|
||||
use ruff_python_ast::{self as ast, Expr, ExprAttribute};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
@ -65,8 +65,8 @@ fn is_call_insecure(call: &ast::ExprCall) -> bool {
|
|||
if let Some(argument) = call.arguments.find_argument(argument_name, position) {
|
||||
match argument_name {
|
||||
"select" => match argument {
|
||||
Expr::Dict(ExprDict { items, .. }) => {
|
||||
if items.iter().any(|ast::DictItem { key, value }| {
|
||||
Expr::Dict(dict) => {
|
||||
if dict.iter().any(|ast::DictItem { key, value }| {
|
||||
key.as_ref()
|
||||
.is_some_and(|key| !key.is_string_literal_expr())
|
||||
|| !value.is_string_literal_expr()
|
||||
|
@ -77,8 +77,8 @@ fn is_call_insecure(call: &ast::ExprCall) -> bool {
|
|||
_ => return true,
|
||||
},
|
||||
"where" | "tables" => match argument {
|
||||
Expr::List(ExprList { elts, .. }) => {
|
||||
if !elts.iter().all(Expr::is_string_literal_expr) {
|
||||
Expr::List(list) => {
|
||||
if !list.iter().all(Expr::is_string_literal_expr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -530,11 +530,11 @@ fn is_partial_path(expr: &Expr) -> bool {
|
|||
/// subprocess.Popen(["/usr/local/bin/rsync", "*", "some_where:"], shell=True)
|
||||
/// ```
|
||||
fn is_wildcard_command(expr: &Expr) -> bool {
|
||||
if let Expr::List(ast::ExprList { elts, .. }) = expr {
|
||||
if let Expr::List(list) = expr {
|
||||
let mut has_star = false;
|
||||
let mut has_command = false;
|
||||
for elt in elts {
|
||||
if let Some(text) = string_literal(elt) {
|
||||
for item in list {
|
||||
if let Some(text) = string_literal(item) {
|
||||
has_star |= text.contains('*');
|
||||
has_command |= text.contains("chown")
|
||||
|| text.contains("chmod")
|
||||
|
|
|
@ -49,16 +49,16 @@ impl Violation for DuplicateValue {
|
|||
/// B033
|
||||
pub(crate) fn duplicate_value(checker: &mut Checker, set: &ast::ExprSet) {
|
||||
let mut seen_values: FxHashSet<ComparableExpr> = FxHashSet::default();
|
||||
for (index, elt) in set.elts.iter().enumerate() {
|
||||
if elt.is_literal_expr() {
|
||||
let comparable_value: ComparableExpr = elt.into();
|
||||
for (index, value) in set.iter().enumerate() {
|
||||
if value.is_literal_expr() {
|
||||
let comparable_value = ComparableExpr::from(value);
|
||||
|
||||
if !seen_values.insert(comparable_value) {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
DuplicateValue {
|
||||
value: checker.generator().expr(elt),
|
||||
value: checker.generator().expr(value),
|
||||
},
|
||||
elt.range(),
|
||||
value.range(),
|
||||
);
|
||||
|
||||
diagnostic.try_set_fix(|| {
|
||||
|
@ -73,7 +73,7 @@ pub(crate) fn duplicate_value(checker: &mut Checker, set: &ast::ExprSet) {
|
|||
|
||||
/// Remove the member at the given index from the [`ast::ExprSet`].
|
||||
fn remove_member(set: &ast::ExprSet, index: usize, source: &str) -> Result<Edit> {
|
||||
if index < set.elts.len() - 1 {
|
||||
if index < set.len() - 1 {
|
||||
// Case 1: the expression is _not_ the last node, so delete from the start of the
|
||||
// expression to the end of the subsequent comma.
|
||||
// Ex) Delete `"a"` in `{"a", "b", "c"}`.
|
||||
|
|
|
@ -315,15 +315,15 @@ pub(crate) fn reuse_of_groupby_generator(
|
|||
let Expr::Call(ast::ExprCall { func, .. }) = &iter else {
|
||||
return;
|
||||
};
|
||||
let Expr::Tuple(ast::ExprTuple { elts, .. }) = target else {
|
||||
let Expr::Tuple(tuple) = target else {
|
||||
// Ignore any `groupby()` invocation that isn't unpacked
|
||||
return;
|
||||
};
|
||||
if elts.len() != 2 {
|
||||
if tuple.len() != 2 {
|
||||
return;
|
||||
}
|
||||
// We have an invocation of groupby which is a simple unpacking
|
||||
let Expr::Name(ast::ExprName { id: group_name, .. }) = &elts[1] else {
|
||||
let Expr::Name(ast::ExprName { id: group_name, .. }) = &tuple.elts[1] else {
|
||||
return;
|
||||
};
|
||||
// Check if the function call is `itertools.groupby`
|
||||
|
|
|
@ -72,7 +72,7 @@ pub(crate) fn static_key_dict_comprehension(checker: &mut Checker, dict_comp: &a
|
|||
/// comprehension.
|
||||
fn is_constant(key: &Expr, names: &FxHashMap<&str, &ast::ExprName>) -> bool {
|
||||
match key {
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().all(|elt| is_constant(elt, names)),
|
||||
Expr::Tuple(tuple) => tuple.iter().all(|elem| is_constant(elem, names)),
|
||||
Expr::Name(ast::ExprName { id, .. }) => !names.contains_key(id.as_str()),
|
||||
Expr::Attribute(ast::ExprAttribute { value, .. }) => is_constant(value, names),
|
||||
Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
|
||||
|
|
|
@ -61,13 +61,13 @@ pub(crate) fn unnecessary_generator_dict(
|
|||
let Expr::Generator(ast::ExprGenerator { elt, .. }) = argument else {
|
||||
return;
|
||||
};
|
||||
let Expr::Tuple(ast::ExprTuple { elts, .. }) = elt.as_ref() else {
|
||||
let Expr::Tuple(tuple) = &**elt else {
|
||||
return;
|
||||
};
|
||||
if elts.len() != 2 {
|
||||
if tuple.len() != 2 {
|
||||
return;
|
||||
}
|
||||
if elts.iter().any(Expr::is_starred_expr) {
|
||||
if tuple.iter().any(Expr::is_starred_expr) {
|
||||
return;
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorDict, expr.range());
|
||||
|
|
|
@ -62,10 +62,10 @@ pub(crate) fn unnecessary_list_comprehension_dict(
|
|||
let Expr::ListComp(ast::ExprListComp { elt, .. }) = argument else {
|
||||
return;
|
||||
};
|
||||
let Expr::Tuple(ast::ExprTuple { elts, .. }) = elt.as_ref() else {
|
||||
let Expr::Tuple(tuple) = &**elt else {
|
||||
return;
|
||||
};
|
||||
if elts.len() != 2 {
|
||||
if tuple.len() != 2 {
|
||||
return;
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryListComprehensionDict, expr.range());
|
||||
|
|
|
@ -74,7 +74,7 @@ pub(crate) fn unnecessary_literal_dict(
|
|||
// Accept `dict((1, 2), ...))` `dict([(1, 2), ...])`.
|
||||
if !elts
|
||||
.iter()
|
||||
.all(|elt| matches!(&elt, Expr::Tuple(ast::ExprTuple { elts, .. }) if elts.len() == 2))
|
||||
.all(|elt| matches!(&elt, Expr::Tuple(tuple) if tuple.len() == 2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -163,8 +163,8 @@ pub(crate) fn multiple_starts_ends_with(checker: &mut Checker, expr: &Expr) {
|
|||
elts: words
|
||||
.iter()
|
||||
.flat_map(|value| {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = value {
|
||||
Left(elts.iter())
|
||||
if let Expr::Tuple(tuple) = value {
|
||||
Left(tuple.iter())
|
||||
} else {
|
||||
Right(iter::once(*value))
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ruff_python_ast::{self as ast, Expr, ExprLambda};
|
||||
use ruff_python_ast::{Expr, ExprLambda};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
||||
use ruff_diagnostics::{FixAvailability, Violation};
|
||||
|
@ -70,8 +70,8 @@ pub(crate) fn reimplemented_container_builtin(checker: &mut Checker, expr: &Expr
|
|||
}
|
||||
|
||||
let container = match &**body {
|
||||
Expr::List(ast::ExprList { elts, .. }) if elts.is_empty() => Container::List,
|
||||
Expr::Dict(ast::ExprDict { items, .. }) if items.is_empty() => Container::Dict,
|
||||
Expr::List(list) if list.is_empty() => Container::List,
|
||||
Expr::Dict(dict) if dict.is_empty() => Container::Dict,
|
||||
_ => return,
|
||||
};
|
||||
let mut diagnostic = Diagnostic::new(ReimplementedContainerBuiltin { container }, expr.range());
|
||||
|
|
|
@ -87,13 +87,13 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCal
|
|||
.iter_keys()
|
||||
.filter_map(|key| key.and_then(as_kwarg))
|
||||
.collect();
|
||||
if kwargs.len() != dict.items.len() {
|
||||
if kwargs.len() != dict.len() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryDictKwargs, keyword.range());
|
||||
|
||||
if dict.items.is_empty() {
|
||||
if dict.is_empty() {
|
||||
diagnostic.try_set_fix(|| {
|
||||
remove_argument(
|
||||
keyword,
|
||||
|
|
|
@ -49,7 +49,7 @@ impl Violation for UnnecessarySpread {
|
|||
pub(crate) fn unnecessary_spread(checker: &mut Checker, dict: &ast::ExprDict) {
|
||||
// The first "end" is the start of the dictionary, immediately following the open bracket.
|
||||
let mut prev_end = dict.start() + TextSize::from(1);
|
||||
for ast::DictItem { key, value } in &dict.items {
|
||||
for ast::DictItem { key, value } in dict {
|
||||
if key.is_none() {
|
||||
// We only care about when the key is None which indicates a spread `**`
|
||||
// inside a dict.
|
||||
|
|
|
@ -162,12 +162,11 @@ pub(crate) fn bad_generator_return_type(
|
|||
// - if not, don't emit the diagnostic
|
||||
let yield_type_info = match returns {
|
||||
ast::Expr::Subscript(ast::ExprSubscript { slice, .. }) => match slice.as_ref() {
|
||||
ast::Expr::Tuple(slice_tuple @ ast::ExprTuple { .. }) => {
|
||||
ast::Expr::Tuple(slice_tuple) => {
|
||||
if !slice_tuple
|
||||
.elts
|
||||
.iter()
|
||||
.skip(1)
|
||||
.all(|elt| is_any_or_none(elt, semantic))
|
||||
.all(|element| is_any_or_none(element, semantic))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -67,8 +67,8 @@ pub(crate) fn redundant_literal_union<'a>(checker: &mut Checker, union: &'a Expr
|
|||
let mut func = |expr: &'a Expr, _parent: &'a Expr| {
|
||||
if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr {
|
||||
if checker.semantic().match_typing_expr(value, "Literal") {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() {
|
||||
typing_literal_exprs.extend(elts.iter());
|
||||
if let Expr::Tuple(tuple) = &**slice {
|
||||
typing_literal_exprs.extend(tuple);
|
||||
} else {
|
||||
typing_literal_exprs.push(slice);
|
||||
}
|
||||
|
|
|
@ -298,10 +298,10 @@ fn is_valid_default_value_with_annotation(
|
|||
.iter()
|
||||
.all(|e| is_valid_default_value_with_annotation(e, false, locator, semantic));
|
||||
}
|
||||
Expr::Dict(ast::ExprDict { items, range: _ }) => {
|
||||
Expr::Dict(dict) => {
|
||||
return allow_container
|
||||
&& items.len() <= 10
|
||||
&& items.iter().all(|ast::DictItem { key, value }| {
|
||||
&& dict.len() <= 10
|
||||
&& dict.iter().all(|ast::DictItem { key, value }| {
|
||||
key.as_ref().is_some_and(|key| {
|
||||
is_valid_default_value_with_annotation(key, false, locator, semantic)
|
||||
}) && is_valid_default_value_with_annotation(value, false, locator, semantic)
|
||||
|
|
|
@ -70,19 +70,15 @@ pub(crate) fn unnecessary_literal_union<'a>(checker: &mut Checker, expr: &'a Exp
|
|||
literal_subscript = Some(value.as_ref());
|
||||
}
|
||||
|
||||
let slice = &**slice;
|
||||
|
||||
// flatten already-unioned literals to later union again
|
||||
if let Expr::Tuple(ast::ExprTuple {
|
||||
elts,
|
||||
range: _,
|
||||
ctx: _,
|
||||
parenthesized: _,
|
||||
}) = slice.as_ref()
|
||||
{
|
||||
for expr in elts {
|
||||
literal_exprs.push(expr);
|
||||
if let Expr::Tuple(tuple) = slice {
|
||||
for item in tuple {
|
||||
literal_exprs.push(item);
|
||||
}
|
||||
} else {
|
||||
literal_exprs.push(slice.as_ref());
|
||||
literal_exprs.push(slice);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -181,7 +181,7 @@ fn version_check(
|
|||
}
|
||||
|
||||
// Tuple comparison, e.g., `sys.version_info == (3, 4)`.
|
||||
let Expr::Tuple(ast::ExprTuple { elts, .. }) = comparator else {
|
||||
let Expr::Tuple(tuple) = comparator else {
|
||||
if checker.enabled(Rule::UnrecognizedVersionInfoCheck) {
|
||||
checker
|
||||
.diagnostics
|
||||
|
@ -190,7 +190,7 @@ fn version_check(
|
|||
return;
|
||||
};
|
||||
|
||||
if !elts.iter().all(is_int_constant) {
|
||||
if !tuple.iter().all(is_int_constant) {
|
||||
// All tuple elements must be integers, e.g., `sys.version_info == (3, 4)` instead of
|
||||
// `sys.version_info == (3.0, 4)`.
|
||||
if checker.enabled(Rule::UnrecognizedVersionInfoCheck) {
|
||||
|
@ -198,7 +198,7 @@ fn version_check(
|
|||
.diagnostics
|
||||
.push(Diagnostic::new(UnrecognizedVersionInfoCheck, test.range()));
|
||||
}
|
||||
} else if elts.len() > 2 {
|
||||
} else if tuple.len() > 2 {
|
||||
// Must compare against major and minor version only, e.g., `sys.version_info == (3, 4)`
|
||||
// instead of `sys.version_info == (3, 4, 0)`.
|
||||
if checker.enabled(Rule::PatchVersionComparison) {
|
||||
|
@ -216,7 +216,7 @@ fn version_check(
|
|||
_ => return,
|
||||
};
|
||||
|
||||
if elts.len() != expected_length {
|
||||
if tuple.len() != expected_length {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
WrongTupleLengthVersionComparison { expected_length },
|
||||
test.range(),
|
||||
|
|
|
@ -416,9 +416,7 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
|
|||
}
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => {
|
||||
if elts.len() == 1 {
|
||||
if let Some(first) = elts.first() {
|
||||
handle_single_name(checker, expr, first);
|
||||
}
|
||||
handle_single_name(checker, expr, &elts[0]);
|
||||
} else {
|
||||
match names_type {
|
||||
types::ParametrizeNameType::Tuple => {}
|
||||
|
@ -462,9 +460,7 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
|
|||
}
|
||||
Expr::List(ast::ExprList { elts, .. }) => {
|
||||
if elts.len() == 1 {
|
||||
if let Some(first) = elts.first() {
|
||||
handle_single_name(checker, expr, first);
|
||||
}
|
||||
handle_single_name(checker, expr, &elts[0]);
|
||||
} else {
|
||||
match names_type {
|
||||
types::ParametrizeNameType::List => {}
|
||||
|
|
|
@ -411,8 +411,8 @@ pub(crate) fn duplicate_isinstance_call(checker: &mut Checker, expr: &Expr) {
|
|||
elts: types
|
||||
.iter()
|
||||
.flat_map(|value| {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = value {
|
||||
Left(elts.iter())
|
||||
if let Expr::Tuple(tuple) = value {
|
||||
Left(tuple.iter())
|
||||
} else {
|
||||
Right(iter::once(*value))
|
||||
}
|
||||
|
@ -722,8 +722,7 @@ fn get_short_circuit_edit(
|
|||
generator.expr(expr)
|
||||
};
|
||||
Edit::range_replacement(
|
||||
if matches!(expr, Expr::Tuple(ast::ExprTuple { elts, ctx: _, range: _, parenthesized: _}) if !elts.is_empty())
|
||||
{
|
||||
if matches!(expr, Expr::Tuple(tuple) if !tuple.is_empty()) {
|
||||
format!("({content})")
|
||||
} else {
|
||||
content
|
||||
|
|
|
@ -91,18 +91,18 @@ impl From<&Expr> for ConstantLikelihood {
|
|||
ConstantLikelihood::from_identifier(attr)
|
||||
}
|
||||
Expr::Name(ast::ExprName { id, .. }) => ConstantLikelihood::from_identifier(id),
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts
|
||||
Expr::Tuple(tuple) => tuple
|
||||
.iter()
|
||||
.map(ConstantLikelihood::from)
|
||||
.min()
|
||||
.unwrap_or(ConstantLikelihood::Definitely),
|
||||
Expr::List(ast::ExprList { elts, .. }) => elts
|
||||
Expr::List(list) => list
|
||||
.iter()
|
||||
.map(ConstantLikelihood::from)
|
||||
.min()
|
||||
.unwrap_or(ConstantLikelihood::Definitely),
|
||||
Expr::Dict(ast::ExprDict { items, .. }) => {
|
||||
if items.is_empty() {
|
||||
Expr::Dict(dict) => {
|
||||
if dict.is_empty() {
|
||||
ConstantLikelihood::Definitely
|
||||
} else {
|
||||
ConstantLikelihood::Probably
|
||||
|
|
|
@ -102,16 +102,16 @@ pub(crate) fn manual_dict_comprehension(checker: &mut Checker, target: &Expr, bo
|
|||
};
|
||||
|
||||
match target {
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => {
|
||||
if !elts
|
||||
Expr::Tuple(tuple) => {
|
||||
if !tuple
|
||||
.iter()
|
||||
.any(|elt| ComparableExpr::from(slice) == ComparableExpr::from(elt))
|
||||
.any(|element| ComparableExpr::from(slice) == ComparableExpr::from(element))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if !elts
|
||||
if !tuple
|
||||
.iter()
|
||||
.any(|elt| ComparableExpr::from(value) == ComparableExpr::from(elt))
|
||||
.any(|element| ComparableExpr::from(value) == ComparableExpr::from(element))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ pub(crate) fn manual_dict_comprehension(checker: &mut Checker, target: &Expr, bo
|
|||
}
|
||||
|
||||
// Exclude non-dictionary value.
|
||||
let Some(name) = subscript_value.as_name_expr() else {
|
||||
let Expr::Name(name) = &**subscript_value else {
|
||||
return;
|
||||
};
|
||||
let Some(binding) = checker
|
||||
|
|
|
@ -613,7 +613,7 @@ impl<'a> Visitor<'a> for BodyVisitor<'a> {
|
|||
};
|
||||
|
||||
if let ast::Expr::Tuple(tuple) = exceptions {
|
||||
for exception in &tuple.elts {
|
||||
for exception in tuple {
|
||||
maybe_store_exception(exception);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||
use ruff_python_ast::{Expr, Stmt};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
@ -39,8 +39,8 @@ impl Violation for AssertTuple {
|
|||
|
||||
/// F631
|
||||
pub(crate) fn assert_tuple(checker: &mut Checker, stmt: &Stmt, test: &Expr) {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = &test {
|
||||
if !elts.is_empty() {
|
||||
if let Expr::Tuple(tuple) = &test {
|
||||
if !tuple.is_empty() {
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(AssertTuple, stmt.range()));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ruff_python_ast::{self as ast, Expr, StmtIf};
|
||||
use ruff_python_ast::{Expr, StmtIf};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
@ -41,10 +41,10 @@ impl Violation for IfTuple {
|
|||
/// F634
|
||||
pub(crate) fn if_tuple(checker: &mut Checker, stmt_if: &StmtIf) {
|
||||
for branch in if_elif_branches(stmt_if) {
|
||||
let Expr::Tuple(ast::ExprTuple { elts, .. }) = &branch.test else {
|
||||
let Expr::Tuple(tuple) = &branch.test else {
|
||||
continue;
|
||||
};
|
||||
if elts.is_empty() {
|
||||
if tuple.is_empty() {
|
||||
continue;
|
||||
}
|
||||
checker
|
||||
|
|
|
@ -130,10 +130,10 @@ impl Violation for MultiValueRepeatedKeyVariable {
|
|||
pub(crate) fn repeated_keys(checker: &mut Checker, dict: &ast::ExprDict) {
|
||||
// Generate a map from key to (index, value).
|
||||
let mut seen: FxHashMap<ComparableExpr, FxHashSet<ComparableExpr>> =
|
||||
FxHashMap::with_capacity_and_hasher(dict.items.len(), FxBuildHasher);
|
||||
FxHashMap::with_capacity_and_hasher(dict.len(), FxBuildHasher);
|
||||
|
||||
// Detect duplicate keys.
|
||||
for (i, ast::DictItem { key, value }) in dict.items.iter().enumerate() {
|
||||
for (i, ast::DictItem { key, value }) in dict.iter().enumerate() {
|
||||
let Some(key) = key else {
|
||||
continue;
|
||||
};
|
||||
|
|
|
@ -690,10 +690,10 @@ pub(crate) fn percent_format_positional_count_mismatch(
|
|||
return;
|
||||
}
|
||||
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = right {
|
||||
if let Expr::Tuple(tuple) = right {
|
||||
let mut found = 0;
|
||||
for elt in elts {
|
||||
if elt.is_starred_expr() {
|
||||
for element in tuple {
|
||||
if element.is_starred_expr() {
|
||||
return;
|
||||
}
|
||||
found += 1;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ruff_python_ast::{Expr, ExprTuple};
|
||||
use ruff_python_ast::{Expr, Stmt};
|
||||
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
@ -49,15 +49,15 @@ impl AlwaysFixableViolation for DictIterMissingItems {
|
|||
}
|
||||
|
||||
pub(crate) fn dict_iter_missing_items(checker: &mut Checker, target: &Expr, iter: &Expr) {
|
||||
let Expr::Tuple(ExprTuple { elts, .. }) = target else {
|
||||
let Expr::Tuple(tuple) = target else {
|
||||
return;
|
||||
};
|
||||
|
||||
if elts.len() != 2 {
|
||||
if tuple.len() != 2 {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(name) = iter.as_name_expr() else {
|
||||
let Expr::Name(name) = iter else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -91,20 +91,15 @@ fn is_dict_key_tuple_with_two_elements(binding: &Binding, semantic: &SemanticMod
|
|||
return false;
|
||||
};
|
||||
|
||||
let Some(assign_stmt) = statement.as_assign_stmt() else {
|
||||
let Stmt::Assign(assign_stmt) = statement else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Some(dict_expr) = assign_stmt.value.as_dict_expr() else {
|
||||
let Expr::Dict(dict_expr) = &*assign_stmt.value else {
|
||||
return false;
|
||||
};
|
||||
|
||||
dict_expr.iter_keys().all(|elt| {
|
||||
elt.is_some_and(|x| {
|
||||
if let Expr::Tuple(ExprTuple { elts, .. }) = x {
|
||||
return elts.len() == 2;
|
||||
}
|
||||
false
|
||||
})
|
||||
})
|
||||
dict_expr
|
||||
.iter_keys()
|
||||
.all(|key| matches!(key, Some(Expr::Tuple(tuple)) if tuple.len() == 2))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
@ -42,17 +42,17 @@ impl AlwaysFixableViolation for IterationOverSet {
|
|||
|
||||
/// PLC0208
|
||||
pub(crate) fn iteration_over_set(checker: &mut Checker, expr: &Expr) {
|
||||
let Expr::Set(ast::ExprSet { elts, .. }) = expr else {
|
||||
let Expr::Set(set) = expr else {
|
||||
return;
|
||||
};
|
||||
|
||||
if elts.iter().any(Expr::is_starred_expr) {
|
||||
if set.iter().any(Expr::is_starred_expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(IterationOverSet, expr.range());
|
||||
|
||||
let tuple = if let [elt] = elts.as_slice() {
|
||||
let tuple = if let [elt] = set.elts.as_slice() {
|
||||
let elt = checker.locator().slice(elt);
|
||||
format!("({elt},)")
|
||||
} else {
|
||||
|
|
|
@ -52,8 +52,8 @@ pub(crate) fn redeclared_assigned_name(checker: &mut Checker, targets: &Vec<Expr
|
|||
|
||||
fn check_expr(checker: &mut Checker, expr: &Expr, names: &mut Vec<Name>) {
|
||||
match expr {
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => {
|
||||
for target in elts {
|
||||
Expr::Tuple(tuple) => {
|
||||
for target in tuple {
|
||||
check_expr(checker, target, names);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,13 +71,10 @@ pub(crate) fn self_annotated_assignment(checker: &mut Checker, assign: &ast::Stm
|
|||
|
||||
fn visit_assignments(left: &Expr, right: &Expr, diagnostics: &mut Vec<Diagnostic>) {
|
||||
match (left, right) {
|
||||
(
|
||||
Expr::Tuple(ast::ExprTuple { elts: lhs_elts, .. }),
|
||||
Expr::Tuple(ast::ExprTuple { elts: rhs_elts, .. }),
|
||||
) if lhs_elts.len() == rhs_elts.len() => lhs_elts
|
||||
(Expr::Tuple(lhs), Expr::Tuple(rhs)) if lhs.len() == rhs.len() => lhs
|
||||
.iter()
|
||||
.zip(rhs_elts.iter())
|
||||
.for_each(|(lhs, rhs)| visit_assignments(lhs, rhs, diagnostics)),
|
||||
.zip(rhs)
|
||||
.for_each(|(lhs_elem, rhs_elem)| visit_assignments(lhs_elem, rhs_elem, diagnostics)),
|
||||
(
|
||||
Expr::Name(ast::ExprName { id: lhs_name, .. }),
|
||||
Expr::Name(ast::ExprName { id: rhs_name, .. }),
|
||||
|
|
|
@ -169,14 +169,15 @@ fn create_field_assignment_stmt(field: Name, annotation: &Expr) -> Stmt {
|
|||
|
||||
/// Create a list of field assignments from the `NamedTuple` fields argument.
|
||||
fn create_fields_from_fields_arg(fields: &Expr) -> Option<Vec<Stmt>> {
|
||||
let ast::ExprList { elts, .. } = fields.as_list_expr()?;
|
||||
if elts.is_empty() {
|
||||
let fields = fields.as_list_expr()?;
|
||||
if fields.is_empty() {
|
||||
let node = Stmt::Pass(ast::StmtPass {
|
||||
range: TextRange::default(),
|
||||
});
|
||||
Some(vec![node])
|
||||
} else {
|
||||
elts.iter()
|
||||
fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let ast::ExprTuple { elts, .. } = field.as_tuple_expr()?;
|
||||
let [field, annotation] = elts.as_slice() else {
|
||||
|
|
|
@ -98,22 +98,20 @@ fn tuple_diagnostic(checker: &mut Checker, tuple: &ast::ExprTuple, aliases: &[&E
|
|||
if semantic.has_builtin_binding("OSError") {
|
||||
// Filter out any `OSErrors` aliases.
|
||||
let mut remaining: Vec<Expr> = tuple
|
||||
.elts
|
||||
.iter()
|
||||
.filter_map(|elt| {
|
||||
if aliases.contains(&elt) {
|
||||
.filter_map(|element| {
|
||||
if aliases.contains(&element) {
|
||||
None
|
||||
} else {
|
||||
Some(elt.clone())
|
||||
Some(element.clone())
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// If `OSError` itself isn't already in the tuple, add it.
|
||||
if tuple
|
||||
.elts
|
||||
.iter()
|
||||
.all(|elt| !semantic.match_builtin_expr(elt, "OSError"))
|
||||
.all(|elem| !semantic.match_builtin_expr(elem, "OSError"))
|
||||
{
|
||||
let node = ast::ExprName {
|
||||
id: Name::new_static("OSError"),
|
||||
|
@ -159,9 +157,9 @@ pub(crate) fn os_error_alias_handlers(checker: &mut Checker, handlers: &[ExceptH
|
|||
Expr::Tuple(tuple) => {
|
||||
// List of aliases to replace with `OSError`.
|
||||
let mut aliases: Vec<&Expr> = vec![];
|
||||
for elt in &tuple.elts {
|
||||
if is_alias(elt, checker.semantic()) {
|
||||
aliases.push(elt);
|
||||
for element in tuple {
|
||||
if is_alias(element, checker.semantic()) {
|
||||
aliases.push(element);
|
||||
}
|
||||
}
|
||||
if !aliases.is_empty() {
|
||||
|
|
|
@ -198,8 +198,8 @@ fn percent_to_format(format_string: &CFormatString) -> String {
|
|||
|
||||
/// If a tuple has one argument, remove the comma; otherwise, return it as-is.
|
||||
fn clean_params_tuple<'a>(right: &Expr, locator: &Locator<'a>) -> Cow<'a, str> {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = &right {
|
||||
if elts.len() == 1 {
|
||||
if let Expr::Tuple(tuple) = &right {
|
||||
if tuple.len() == 1 {
|
||||
if !locator.contains_line_break(right.range()) {
|
||||
let mut contents = locator.slice(right).to_string();
|
||||
for (i, character) in contents.chars().rev().enumerate() {
|
||||
|
|
|
@ -110,22 +110,20 @@ fn tuple_diagnostic(checker: &mut Checker, tuple: &ast::ExprTuple, aliases: &[&E
|
|||
if semantic.has_builtin_binding("TimeoutError") {
|
||||
// Filter out any `TimeoutErrors` aliases.
|
||||
let mut remaining: Vec<Expr> = tuple
|
||||
.elts
|
||||
.iter()
|
||||
.filter_map(|elt| {
|
||||
if aliases.contains(&elt) {
|
||||
.filter_map(|element| {
|
||||
if aliases.contains(&element) {
|
||||
None
|
||||
} else {
|
||||
Some(elt.clone())
|
||||
Some(element.clone())
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// If `TimeoutError` itself isn't already in the tuple, add it.
|
||||
if tuple
|
||||
.elts
|
||||
.iter()
|
||||
.all(|elt| !semantic.match_builtin_expr(elt, "TimeoutError"))
|
||||
.all(|element| !semantic.match_builtin_expr(element, "TimeoutError"))
|
||||
{
|
||||
let node = ast::ExprName {
|
||||
id: Name::new_static("TimeoutError"),
|
||||
|
@ -171,9 +169,9 @@ pub(crate) fn timeout_error_alias_handlers(checker: &mut Checker, handlers: &[Ex
|
|||
Expr::Tuple(tuple) => {
|
||||
// List of aliases to replace with `TimeoutError`.
|
||||
let mut aliases: Vec<&Expr> = vec![];
|
||||
for elt in &tuple.elts {
|
||||
if is_alias(elt, checker.semantic(), checker.settings.target_version) {
|
||||
aliases.push(elt);
|
||||
for element in tuple {
|
||||
if is_alias(element, checker.semantic(), checker.settings.target_version) {
|
||||
aliases.push(element);
|
||||
}
|
||||
}
|
||||
if !aliases.is_empty() {
|
||||
|
|
|
@ -189,7 +189,7 @@ fn is_allowed_value(expr: &Expr) -> bool {
|
|||
| Expr::Subscript(_)
|
||||
| Expr::Name(_)
|
||||
| Expr::List(_) => true,
|
||||
Expr::Tuple(tuple) => tuple.elts.iter().all(is_allowed_value),
|
||||
Expr::Tuple(tuple) => tuple.iter().all(is_allowed_value),
|
||||
// Maybe require parentheses.
|
||||
Expr::Named(_) => false,
|
||||
// Invalid in binary expressions.
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt;
|
|||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::pep_604_union;
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
@ -90,15 +90,15 @@ pub(crate) fn use_pep604_isinstance(
|
|||
let Some(types) = args.get(1) else {
|
||||
return;
|
||||
};
|
||||
let Expr::Tuple(ast::ExprTuple { elts, .. }) = types else {
|
||||
let Expr::Tuple(tuple) = types else {
|
||||
return;
|
||||
};
|
||||
// Ex) `()`
|
||||
if elts.is_empty() {
|
||||
if tuple.is_empty() {
|
||||
return;
|
||||
}
|
||||
// Ex) `(*args,)`
|
||||
if elts.iter().any(Expr::is_starred_expr) {
|
||||
if tuple.iter().any(Expr::is_starred_expr) {
|
||||
return;
|
||||
}
|
||||
let Some(builtin_function_name) = checker.semantic().resolve_builtin_symbol(func) else {
|
||||
|
@ -110,7 +110,7 @@ pub(crate) fn use_pep604_isinstance(
|
|||
checker.diagnostics.push(
|
||||
Diagnostic::new(NonPEP604Isinstance { kind }, expr.range()).with_fix(Fix::unsafe_edit(
|
||||
Edit::range_replacement(
|
||||
checker.generator().expr(&pep_604_union(elts)),
|
||||
checker.generator().expr(&pep_604_union(&tuple.elts)),
|
||||
types.range(),
|
||||
),
|
||||
)),
|
||||
|
|
|
@ -135,11 +135,10 @@ fn is_same_expr(left: &Expr, right: &Expr) -> bool {
|
|||
match (&left, &right) {
|
||||
(Expr::Name(left), Expr::Name(right)) => left.id == right.id,
|
||||
(Expr::Tuple(left), Expr::Tuple(right)) => {
|
||||
left.elts.len() == right.elts.len()
|
||||
left.len() == right.len()
|
||||
&& left
|
||||
.elts
|
||||
.iter()
|
||||
.zip(right.elts.iter())
|
||||
.zip(right)
|
||||
.all(|(left, right)| is_same_expr(left, right))
|
||||
}
|
||||
_ => false,
|
||||
|
@ -153,7 +152,7 @@ fn collect_names<'a>(expr: &'a Expr) -> Box<dyn Iterator<Item = &ast::ExprName>
|
|||
expr.as_name_expr().into_iter().chain(
|
||||
expr.as_tuple_expr()
|
||||
.into_iter()
|
||||
.flat_map(|tuple| tuple.elts.iter().flat_map(collect_names)),
|
||||
.flat_map(|tuple| tuple.iter().flat_map(collect_names)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ fn is_none(expr: &Expr) -> bool {
|
|||
}
|
||||
|
||||
// Ex) `(type(None),)`
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().all(|elt| inner(elt, false)),
|
||||
Expr::Tuple(tuple) => tuple.iter().all(|element| inner(element, false)),
|
||||
|
||||
// Ex) `type(None) | type(None)`
|
||||
Expr::BinOp(ast::ExprBinOp {
|
||||
|
|
|
@ -254,13 +254,12 @@ fn itemgetter_op_tuple(
|
|||
let [arg] = params.args.as_slice() else {
|
||||
return None;
|
||||
};
|
||||
if expr.elts.is_empty() || expr.elts.len() == 1 {
|
||||
if expr.len() <= 1 {
|
||||
return None;
|
||||
}
|
||||
Some(Operator {
|
||||
name: "itemgetter",
|
||||
args: expr
|
||||
.elts
|
||||
.iter()
|
||||
.map(|expr| {
|
||||
expr.as_subscript_expr()
|
||||
|
|
|
@ -134,9 +134,9 @@ pub(crate) fn reimplemented_starmap(checker: &mut Checker, target: &StarmapCandi
|
|||
}
|
||||
// Ex) `f(x, y, z) for x, y, z in iter`
|
||||
ComprehensionTarget::Tuple(tuple) => {
|
||||
if tuple.elts.len() != args.len()
|
||||
|| !std::iter::zip(&tuple.elts, args)
|
||||
.all(|(x, y)| ComparableExpr::from(x) == ComparableExpr::from(y))
|
||||
if tuple.len() != args.len()
|
||||
|| std::iter::zip(tuple, args)
|
||||
.any(|(x, y)| ComparableExpr::from(x) != ComparableExpr::from(y))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -144,9 +144,8 @@ pub(crate) fn reimplemented_starmap(checker: &mut Checker, target: &StarmapCandi
|
|||
// If any of the members are used outside the function call, we can't replace it.
|
||||
if any_over_expr(func, &|expr| {
|
||||
tuple
|
||||
.elts
|
||||
.iter()
|
||||
.any(|elt| ComparableExpr::from(expr) == ComparableExpr::from(elt))
|
||||
.any(|elem| ComparableExpr::from(expr) == ComparableExpr::from(elem))
|
||||
}) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -63,16 +63,16 @@ impl AlwaysFixableViolation for IncorrectlyParenthesizedTupleInSubscript {
|
|||
pub(crate) fn subscript_with_parenthesized_tuple(checker: &mut Checker, subscript: &ExprSubscript) {
|
||||
let prefer_parentheses = checker.settings.ruff.parenthesize_tuple_in_subscript;
|
||||
|
||||
let Some(tuple_subscript) = subscript.slice.as_tuple_expr() else {
|
||||
let Expr::Tuple(tuple_subscript) = &*subscript.slice else {
|
||||
return;
|
||||
};
|
||||
|
||||
if tuple_subscript.parenthesized == prefer_parentheses || tuple_subscript.elts.is_empty() {
|
||||
if tuple_subscript.parenthesized == prefer_parentheses || tuple_subscript.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adding parentheses in the presence of a slice leads to a syntax error.
|
||||
if prefer_parentheses && tuple_subscript.elts.iter().any(Expr::is_slice_expr) {
|
||||
if prefer_parentheses && tuple_subscript.iter().any(Expr::is_slice_expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ pub(crate) fn subscript_with_parenthesized_tuple(checker: &mut Checker, subscrip
|
|||
// see https://peps.python.org/pep-0646/#change-1-star-expressions-in-indexes
|
||||
if checker.settings.target_version <= PythonVersion::Py310
|
||||
&& !prefer_parentheses
|
||||
&& tuple_subscript.elts.iter().any(Expr::is_starred_expr)
|
||||
&& tuple_subscript.iter().any(Expr::is_starred_expr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -112,25 +112,19 @@ pub(crate) fn never_union(checker: &mut Checker, expr: &Expr) {
|
|||
ctx: _,
|
||||
range: _,
|
||||
}) if checker.semantic().match_typing_expr(value, "Union") => {
|
||||
let Expr::Tuple(ast::ExprTuple {
|
||||
elts,
|
||||
ctx: _,
|
||||
range: _,
|
||||
parenthesized: _,
|
||||
}) = slice.as_ref()
|
||||
else {
|
||||
let Expr::Tuple(tuple_slice) = &**slice else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Analyze each element of the `Union`.
|
||||
for elt in elts {
|
||||
for elt in tuple_slice {
|
||||
if let Some(never_like) = NeverLike::from_expr(elt, checker.semantic()) {
|
||||
// Collect the other elements of the `Union`.
|
||||
let rest = elts
|
||||
let rest: Vec<Expr> = tuple_slice
|
||||
.iter()
|
||||
.filter(|other| *other != elt)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
.collect();
|
||||
|
||||
// Ignore, e.g., `typing.Union[typing.NoReturn]`.
|
||||
if rest.is_empty() {
|
||||
|
|
|
@ -136,7 +136,7 @@ fn start_is_empty_list(arguments: &Arguments, semantic: &SemanticModel) -> bool
|
|||
Expr::Call(ast::ExprCall {
|
||||
func, arguments, ..
|
||||
}) => arguments.is_empty() && semantic.match_builtin_expr(func, "list"),
|
||||
Expr::List(ast::ExprList { elts, ctx, .. }) => elts.is_empty() && ctx.is_load(),
|
||||
Expr::List(list) => list.is_empty() && list.ctx.is_load(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,11 +157,16 @@ fn sort_dunder_all(checker: &mut Checker, target: &ast::Expr, node: &ast::Expr)
|
|||
|
||||
let (elts, range, kind) = match node {
|
||||
ast::Expr::List(ast::ExprList { elts, range, .. }) => (elts, *range, SequenceKind::List),
|
||||
ast::Expr::Tuple(tuple_node @ ast::ExprTuple { elts, range, .. }) => (
|
||||
ast::Expr::Tuple(ast::ExprTuple {
|
||||
elts,
|
||||
range,
|
||||
parenthesized,
|
||||
..
|
||||
}) => (
|
||||
elts,
|
||||
*range,
|
||||
SequenceKind::Tuple {
|
||||
parenthesized: tuple_node.parenthesized,
|
||||
parenthesized: *parenthesized,
|
||||
},
|
||||
),
|
||||
_ => return,
|
||||
|
|
|
@ -168,9 +168,14 @@ impl<'a> StringLiteralDisplay<'a> {
|
|||
kind,
|
||||
}
|
||||
}
|
||||
ast::Expr::Tuple(tuple_node @ ast::ExprTuple { elts, range, .. }) => {
|
||||
ast::Expr::Tuple(ast::ExprTuple {
|
||||
elts,
|
||||
range,
|
||||
parenthesized,
|
||||
..
|
||||
}) => {
|
||||
let kind = DisplayKind::Sequence(SequenceKind::Tuple {
|
||||
parenthesized: tuple_node.parenthesized,
|
||||
parenthesized: *parenthesized,
|
||||
});
|
||||
Self {
|
||||
elts: Cow::Borrowed(elts),
|
||||
|
@ -186,8 +191,8 @@ impl<'a> StringLiteralDisplay<'a> {
|
|||
kind,
|
||||
}
|
||||
}
|
||||
ast::Expr::Dict(dict @ ast::ExprDict { items, range }) => {
|
||||
let mut narrowed_keys = Vec::with_capacity(items.len());
|
||||
ast::Expr::Dict(dict) => {
|
||||
let mut narrowed_keys = Vec::with_capacity(dict.len());
|
||||
for key in dict.iter_keys() {
|
||||
if let Some(key) = key {
|
||||
// This is somewhat unfortunate,
|
||||
|
@ -201,11 +206,11 @@ impl<'a> StringLiteralDisplay<'a> {
|
|||
// `__slots__ = {"foo": "bar", **other_dict}`
|
||||
// If `None` wasn't present in the keys,
|
||||
// the length of the keys should always equal the length of the values
|
||||
assert_eq!(narrowed_keys.len(), items.len());
|
||||
assert_eq!(narrowed_keys.len(), dict.len());
|
||||
Self {
|
||||
elts: Cow::Owned(narrowed_keys),
|
||||
range: *range,
|
||||
kind: DisplayKind::Dict { items },
|
||||
range: dict.range(),
|
||||
kind: DisplayKind::Dict { items: &dict.items },
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
|
|
|
@ -23,7 +23,7 @@ fn is_known_type(qualified_name: &QualifiedName, minor_version: u8) -> bool {
|
|||
/// tuple, the iterator will only yield the slice.
|
||||
fn resolve_slice_value(slice: &Expr) -> impl Iterator<Item = &Expr> {
|
||||
match slice {
|
||||
Expr::Tuple(ast::ExprTuple { elts: elements, .. }) => Left(elements.iter()),
|
||||
Expr::Tuple(tuple) => Left(tuple.iter()),
|
||||
_ => Right(std::iter::once(slice)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -582,8 +582,8 @@ pub const fn is_singleton(expr: &Expr) -> bool {
|
|||
|
||||
/// Return `true` if the [`Expr`] is a literal or tuple of literals.
|
||||
pub fn is_constant(expr: &Expr) -> bool {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = expr {
|
||||
elts.iter().all(is_constant)
|
||||
if let Expr::Tuple(tuple) = expr {
|
||||
tuple.iter().all(is_constant)
|
||||
} else {
|
||||
expr.is_literal_expr()
|
||||
}
|
||||
|
@ -630,8 +630,8 @@ pub fn extract_handled_exceptions(handlers: &[ExceptHandler]) -> Vec<&Expr> {
|
|||
match handler {
|
||||
ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { type_, .. }) => {
|
||||
if let Some(type_) = type_ {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = &type_.as_ref() {
|
||||
for type_ in elts {
|
||||
if let Expr::Tuple(tuple) = &**type_ {
|
||||
for type_ in tuple {
|
||||
handled_exceptions.push(type_);
|
||||
}
|
||||
} else {
|
||||
|
@ -1185,8 +1185,8 @@ impl Truthiness {
|
|||
Self::Truthy
|
||||
}
|
||||
}
|
||||
Expr::Dict(ast::ExprDict { items, .. }) => {
|
||||
if items.is_empty() {
|
||||
Expr::Dict(dict) => {
|
||||
if dict.is_empty() {
|
||||
Self::Falsey
|
||||
} else {
|
||||
Self::Truthy
|
||||
|
|
|
@ -856,6 +856,27 @@ impl ExprDict {
|
|||
pub fn value(&self, n: usize) -> &Expr {
|
||||
self.items[n].value()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, DictItem> {
|
||||
self.items.iter()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.items.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a ExprDict {
|
||||
type IntoIter = std::slice::Iter<'a, DictItem>;
|
||||
type Item = &'a DictItem;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExprDict> for Expr {
|
||||
|
@ -955,6 +976,29 @@ pub struct ExprSet {
|
|||
pub elts: Vec<Expr>,
|
||||
}
|
||||
|
||||
impl ExprSet {
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, Expr> {
|
||||
self.elts.iter()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.elts.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.elts.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a ExprSet {
|
||||
type IntoIter = std::slice::Iter<'a, Expr>;
|
||||
type Item = &'a Expr;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExprSet> for Expr {
|
||||
fn from(payload: ExprSet) -> Self {
|
||||
Expr::Set(payload)
|
||||
|
@ -2759,6 +2803,29 @@ pub struct ExprList {
|
|||
pub ctx: ExprContext,
|
||||
}
|
||||
|
||||
impl ExprList {
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, Expr> {
|
||||
self.elts.iter()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.elts.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.elts.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a ExprList {
|
||||
type IntoIter = std::slice::Iter<'a, Expr>;
|
||||
type Item = &'a Expr;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExprList> for Expr {
|
||||
fn from(payload: ExprList) -> Self {
|
||||
Expr::List(payload)
|
||||
|
@ -2776,6 +2843,29 @@ pub struct ExprTuple {
|
|||
pub parenthesized: bool,
|
||||
}
|
||||
|
||||
impl ExprTuple {
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, Expr> {
|
||||
self.elts.iter()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.elts.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.elts.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a ExprTuple {
|
||||
type IntoIter = std::slice::Iter<'a, Expr>;
|
||||
type Item = &'a Expr;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExprTuple> for Expr {
|
||||
fn from(payload: ExprTuple) -> Self {
|
||||
Expr::Tuple(payload)
|
||||
|
|
|
@ -921,10 +921,10 @@ impl<'a> Generator<'a> {
|
|||
self.unparse_expr(orelse, precedence::IF_EXP);
|
||||
});
|
||||
}
|
||||
Expr::Dict(ast::ExprDict { items, range: _ }) => {
|
||||
Expr::Dict(dict) => {
|
||||
self.p("{");
|
||||
let mut first = true;
|
||||
for ast::DictItem { key, value } in items {
|
||||
for ast::DictItem { key, value } in dict {
|
||||
self.p_delim(&mut first, ", ");
|
||||
if let Some(key) = key {
|
||||
self.unparse_expr(key, precedence::COMMA);
|
||||
|
@ -937,15 +937,15 @@ impl<'a> Generator<'a> {
|
|||
}
|
||||
self.p("}");
|
||||
}
|
||||
Expr::Set(ast::ExprSet { elts, range: _ }) => {
|
||||
if elts.is_empty() {
|
||||
Expr::Set(set) => {
|
||||
if set.is_empty() {
|
||||
self.p("set()");
|
||||
} else {
|
||||
self.p("{");
|
||||
let mut first = true;
|
||||
for v in elts {
|
||||
for item in set {
|
||||
self.p_delim(&mut first, ", ");
|
||||
self.unparse_expr(v, precedence::COMMA);
|
||||
self.unparse_expr(item, precedence::COMMA);
|
||||
}
|
||||
self.p("}");
|
||||
}
|
||||
|
@ -1164,26 +1164,26 @@ impl<'a> Generator<'a> {
|
|||
self.unparse_expr(value, precedence::MAX);
|
||||
}
|
||||
Expr::Name(ast::ExprName { id, .. }) => self.p(id.as_str()),
|
||||
Expr::List(ast::ExprList { elts, .. }) => {
|
||||
Expr::List(list) => {
|
||||
self.p("[");
|
||||
let mut first = true;
|
||||
for elt in elts {
|
||||
for item in list {
|
||||
self.p_delim(&mut first, ", ");
|
||||
self.unparse_expr(elt, precedence::COMMA);
|
||||
self.unparse_expr(item, precedence::COMMA);
|
||||
}
|
||||
self.p("]");
|
||||
}
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => {
|
||||
if elts.is_empty() {
|
||||
Expr::Tuple(tuple) => {
|
||||
if tuple.is_empty() {
|
||||
self.p("()");
|
||||
} else {
|
||||
group_if!(precedence::TUPLE, {
|
||||
let mut first = true;
|
||||
for elt in elts {
|
||||
for item in tuple {
|
||||
self.p_delim(&mut first, ", ");
|
||||
self.unparse_expr(elt, precedence::COMMA);
|
||||
self.unparse_expr(item, precedence::COMMA);
|
||||
}
|
||||
self.p_if(elts.len() == 1, ",");
|
||||
self.p_if(tuple.len() == 1, ",");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,7 +193,7 @@ impl FormatNodeRule<ExprTuple> for FormatExprTuple {
|
|||
TupleParentheses::NeverPreserve => {
|
||||
optional_parentheses(&ExprSequence::new(item)).fmt(f)
|
||||
}
|
||||
TupleParentheses::OptionalParentheses if item.elts.len() == 2 => {
|
||||
TupleParentheses::OptionalParentheses if item.len() == 2 => {
|
||||
optional_parentheses(&ExprSequence::new(item)).fmt(f)
|
||||
}
|
||||
TupleParentheses::Default | TupleParentheses::OptionalParentheses => {
|
||||
|
|
|
@ -1052,7 +1052,7 @@ pub(crate) fn has_own_parentheses(
|
|||
..
|
||||
},
|
||||
) => {
|
||||
if !tuple.elts.is_empty() || context.comments().has_dangling(AnyNodeRef::from(expr)) {
|
||||
if !tuple.is_empty() || context.comments().has_dangling(AnyNodeRef::from(expr)) {
|
||||
Some(OwnParentheses::NonEmpty)
|
||||
} else {
|
||||
Some(OwnParentheses::Empty)
|
||||
|
@ -1216,10 +1216,10 @@ pub(crate) fn is_splittable_expression(expr: &Expr, context: &PyFormatContext) -
|
|||
| Expr::YieldFrom(_) => true,
|
||||
|
||||
// Sequence types can split if they contain at least one element.
|
||||
Expr::Tuple(tuple) => !tuple.elts.is_empty(),
|
||||
Expr::Dict(dict) => !dict.items.is_empty(),
|
||||
Expr::Set(set) => !set.elts.is_empty(),
|
||||
Expr::List(list) => !list.elts.is_empty(),
|
||||
Expr::Tuple(tuple) => !tuple.is_empty(),
|
||||
Expr::Dict(dict) => !dict.is_empty(),
|
||||
Expr::Set(set) => !set.is_empty(),
|
||||
Expr::List(list) => !list.is_empty(),
|
||||
|
||||
Expr::UnaryOp(unary) => is_splittable_expression(unary.operand.as_ref(), context),
|
||||
Expr::Yield(ast::ExprYield { value, .. }) => value.is_some(),
|
||||
|
|
|
@ -151,7 +151,7 @@ pub fn to_pep604_operator(
|
|||
fn quoted_annotation(slice: &Expr) -> bool {
|
||||
match slice {
|
||||
Expr::StringLiteral(_) => true,
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().any(quoted_annotation),
|
||||
Expr::Tuple(tuple) => tuple.iter().any(quoted_annotation),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ pub fn to_pep604_operator(
|
|||
fn starred_annotation(slice: &Expr) -> bool {
|
||||
match slice {
|
||||
Expr::Starred(_) => true,
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().any(starred_annotation),
|
||||
Expr::Tuple(tuple) => tuple.iter().any(starred_annotation),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -237,9 +237,9 @@ pub fn is_immutable_annotation(
|
|||
if is_immutable_generic_type(qualified_name.segments()) {
|
||||
true
|
||||
} else if matches!(qualified_name.segments(), ["typing", "Union"]) {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() {
|
||||
elts.iter().all(|elt| {
|
||||
is_immutable_annotation(elt, semantic, extend_immutable_calls)
|
||||
if let Expr::Tuple(tuple) = &**slice {
|
||||
tuple.iter().all(|element| {
|
||||
is_immutable_annotation(element, semantic, extend_immutable_calls)
|
||||
})
|
||||
} else {
|
||||
false
|
||||
|
@ -399,11 +399,12 @@ where
|
|||
// Ex) Union[x, y]
|
||||
if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr {
|
||||
if semantic.match_typing_expr(value, "Union") {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() {
|
||||
if let Expr::Tuple(tuple) = &**slice {
|
||||
// Traverse each element of the tuple within the union recursively to handle cases
|
||||
// such as `Union[..., Union[...]]
|
||||
elts.iter()
|
||||
.for_each(|elt| inner(func, semantic, elt, Some(expr)));
|
||||
tuple
|
||||
.iter()
|
||||
.for_each(|elem| inner(func, semantic, elem, Some(expr)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -438,11 +439,11 @@ where
|
|||
if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr {
|
||||
if semantic.match_typing_expr(value, "Literal") {
|
||||
match &**slice {
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => {
|
||||
Expr::Tuple(tuple) => {
|
||||
// Traverse each element of the tuple within the literal recursively to handle cases
|
||||
// such as `Literal[..., Literal[...]]
|
||||
for elt in elts {
|
||||
inner(func, semantic, elt, Some(expr));
|
||||
for element in tuple {
|
||||
inner(func, semantic, element, Some(expr));
|
||||
}
|
||||
}
|
||||
other => {
|
||||
|
|
|
@ -1432,9 +1432,7 @@ impl<'a> SemanticModel<'a> {
|
|||
/// variable to be "used" if it's shadowed by another variable with usages.
|
||||
pub fn is_unused(&self, expr: &Expr) -> bool {
|
||||
match expr {
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => {
|
||||
elts.iter().all(|expr| self.is_unused(expr))
|
||||
}
|
||||
Expr::Tuple(tuple) => tuple.iter().all(|expr| self.is_unused(expr)),
|
||||
Expr::Name(ast::ExprName { id, .. }) => {
|
||||
// Treat a variable as used if it has any usages, _or_ it's shadowed by another variable
|
||||
// with usages.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue