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