Skip to content

Commit 18452cf

Browse files
authored
Add as_slice method for all string nodes (#9111)
This PR adds a `as_slice` method to all the string nodes which returns all the parts of the nodes as a slice. This will be useful in the next PR to split the string formatting to use this method to extract the _single node_ or _implicitly concanated nodes_.
1 parent cb99815 commit 18452cf

File tree

13 files changed

+120
-64
lines changed

13 files changed

+120
-64
lines changed

crates/ruff_linter/src/checkers/ast/analyze/expression.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1277,7 +1277,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
12771277
}
12781278
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
12791279
if checker.enabled(Rule::UnicodeKindPrefix) {
1280-
for string_part in value.parts() {
1280+
for string_part in value {
12811281
pyupgrade::rules::unicode_kind_prefix(checker, string_part);
12821282
}
12831283
}

crates/ruff_linter/src/rules/flake8_bandit/rules/hardcoded_sql_expression.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ impl Violation for HardcodedSQLExpression {
5757
/// becomes `foobar {x}baz`.
5858
fn concatenated_f_string(expr: &ast::ExprFString, locator: &Locator) -> String {
5959
expr.value
60-
.parts()
60+
.iter()
6161
.filter_map(|part| {
6262
raw_contents(locator.slice(part)).map(|s| s.escape_default().to_string())
6363
})

crates/ruff_linter/src/rules/flake8_pytest_style/rules/helpers.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub(super) fn is_empty_or_null_string(expr: &Expr) -> bool {
5656
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.is_empty(),
5757
Expr::NoneLiteral(_) => true,
5858
Expr::FString(ast::ExprFString { value, .. }) => {
59-
value.parts().all(|f_string_part| match f_string_part {
59+
value.iter().all(|f_string_part| match f_string_part {
6060
ast::FStringPart::Literal(literal) => literal.is_empty(),
6161
ast::FStringPart::FString(f_string) => f_string
6262
.elements

crates/ruff_linter/src/rules/pylint/rules/assert_on_string_literal.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub(crate) fn assert_on_string_literal(checker: &mut Checker, test: &Expr) {
7070
));
7171
}
7272
Expr::FString(ast::ExprFString { value, .. }) => {
73-
let kind = if value.parts().all(|f_string_part| match f_string_part {
73+
let kind = if value.iter().all(|f_string_part| match f_string_part {
7474
ast::FStringPart::Literal(literal) => literal.is_empty(),
7575
ast::FStringPart::FString(f_string) => {
7676
f_string.elements.iter().all(|element| match element {
@@ -82,7 +82,7 @@ pub(crate) fn assert_on_string_literal(checker: &mut Checker, test: &Expr) {
8282
}
8383
}) {
8484
Kind::Empty
85-
} else if value.parts().any(|f_string_part| match f_string_part {
85+
} else if value.iter().any(|f_string_part| match f_string_part {
8686
ast::FStringPart::Literal(literal) => !literal.is_empty(),
8787
ast::FStringPart::FString(f_string) => {
8888
f_string.elements.iter().any(|element| match element {

crates/ruff_linter/src/rules/tryceratops/rules/raise_vanilla_args.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub(crate) fn raise_vanilla_args(checker: &mut Checker, expr: &Expr) {
9090
fn contains_message(expr: &Expr) -> bool {
9191
match expr {
9292
Expr::FString(ast::ExprFString { value, .. }) => {
93-
for f_string_part in value.parts() {
93+
for f_string_part in value {
9494
match f_string_part {
9595
ast::FStringPart::Literal(literal) => {
9696
if literal.chars().any(char::is_whitespace) {

crates/ruff_python_ast/src/comparable.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -583,10 +583,10 @@ impl<'a> From<ast::LiteralExpressionRef<'a>> for ComparableLiteral<'a> {
583583
value, ..
584584
}) => Self::Bool(value),
585585
ast::LiteralExpressionRef::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
586-
Self::Str(value.parts().map(Into::into).collect())
586+
Self::Str(value.iter().map(Into::into).collect())
587587
}
588588
ast::LiteralExpressionRef::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
589-
Self::Bytes(value.parts().map(Into::into).collect())
589+
Self::Bytes(value.iter().map(Into::into).collect())
590590
}
591591
ast::LiteralExpressionRef::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => {
592592
Self::Number(value.into())
@@ -1012,17 +1012,17 @@ impl<'a> From<&'a ast::Expr> for ComparableExpr<'a> {
10121012
}),
10131013
ast::Expr::FString(ast::ExprFString { value, range: _ }) => {
10141014
Self::FString(ExprFString {
1015-
parts: value.parts().map(Into::into).collect(),
1015+
parts: value.iter().map(Into::into).collect(),
10161016
})
10171017
}
10181018
ast::Expr::StringLiteral(ast::ExprStringLiteral { value, range: _ }) => {
10191019
Self::StringLiteral(ExprStringLiteral {
1020-
parts: value.parts().map(Into::into).collect(),
1020+
parts: value.iter().map(Into::into).collect(),
10211021
})
10221022
}
10231023
ast::Expr::BytesLiteral(ast::ExprBytesLiteral { value, range: _ }) => {
10241024
Self::BytesLiteral(ExprBytesLiteral {
1025-
parts: value.parts().map(Into::into).collect(),
1025+
parts: value.iter().map(Into::into).collect(),
10261026
})
10271027
}
10281028
ast::Expr::NumberLiteral(ast::ExprNumberLiteral { value, range: _ }) => {

crates/ruff_python_ast/src/helpers.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1326,7 +1326,7 @@ impl Truthiness {
13261326
Expr::NoneLiteral(_) => Self::Falsey,
13271327
Expr::EllipsisLiteral(_) => Self::Truthy,
13281328
Expr::FString(ast::ExprFString { value, .. }) => {
1329-
if value.parts().all(|part| match part {
1329+
if value.iter().all(|part| match part {
13301330
ast::FStringPart::Literal(string_literal) => string_literal.is_empty(),
13311331
ast::FStringPart::FString(f_string) => f_string.elements.is_empty(),
13321332
}) {

crates/ruff_python_ast/src/node.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -2742,7 +2742,7 @@ impl AstNode for ast::ExprFString {
27422742
{
27432743
let ast::ExprFString { value, range: _ } = self;
27442744

2745-
for f_string_part in value.parts() {
2745+
for f_string_part in value {
27462746
match f_string_part {
27472747
ast::FStringPart::Literal(string_literal) => {
27482748
visitor.visit_string_literal(string_literal);
@@ -2788,7 +2788,7 @@ impl AstNode for ast::ExprStringLiteral {
27882788
{
27892789
let ast::ExprStringLiteral { value, range: _ } = self;
27902790

2791-
for string_literal in value.parts() {
2791+
for string_literal in value {
27922792
visitor.visit_string_literal(string_literal);
27932793
}
27942794
}
@@ -2827,7 +2827,7 @@ impl AstNode for ast::ExprBytesLiteral {
28272827
{
28282828
let ast::ExprBytesLiteral { value, range: _ } = self;
28292829

2830-
for bytes_literal in value.parts() {
2830+
for bytes_literal in value {
28312831
visitor.visit_bytes_literal(bytes_literal);
28322832
}
28332833
}

crates/ruff_python_ast/src/nodes.rs

+93-37
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use std::cell::OnceCell;
44
use std::fmt;
55
use std::fmt::Debug;
66
use std::ops::Deref;
7+
use std::slice::{Iter, IterMut};
78

8-
use itertools::Either::{Left, Right};
99
use itertools::Itertools;
1010

1111
use ruff_text_size::{Ranged, TextRange, TextSize};
@@ -1051,23 +1051,33 @@ impl FStringValue {
10511051
matches!(self.inner, FStringValueInner::Concatenated(_))
10521052
}
10531053

1054-
/// Returns an iterator over all the [`FStringPart`]s contained in this value.
1055-
pub fn parts(&self) -> impl Iterator<Item = &FStringPart> {
1054+
/// Returns a slice of all the [`FStringPart`]s contained in this value.
1055+
pub fn as_slice(&self) -> &[FStringPart] {
10561056
match &self.inner {
1057-
FStringValueInner::Single(part) => Left(std::iter::once(part)),
1058-
FStringValueInner::Concatenated(parts) => Right(parts.iter()),
1057+
FStringValueInner::Single(part) => std::slice::from_ref(part),
1058+
FStringValueInner::Concatenated(parts) => parts,
10591059
}
10601060
}
10611061

1062-
/// Returns an iterator over all the [`FStringPart`]s contained in this value
1063-
/// that allows modification.
1064-
pub(crate) fn parts_mut(&mut self) -> impl Iterator<Item = &mut FStringPart> {
1062+
/// Returns a mutable slice of all the [`FStringPart`]s contained in this value.
1063+
fn as_mut_slice(&mut self) -> &mut [FStringPart] {
10651064
match &mut self.inner {
1066-
FStringValueInner::Single(part) => Left(std::iter::once(part)),
1067-
FStringValueInner::Concatenated(parts) => Right(parts.iter_mut()),
1065+
FStringValueInner::Single(part) => std::slice::from_mut(part),
1066+
FStringValueInner::Concatenated(parts) => parts,
10681067
}
10691068
}
10701069

1070+
/// Returns an iterator over all the [`FStringPart`]s contained in this value.
1071+
pub fn iter(&self) -> Iter<FStringPart> {
1072+
self.as_slice().iter()
1073+
}
1074+
1075+
/// Returns an iterator over all the [`FStringPart`]s contained in this value
1076+
/// that allows modification.
1077+
pub(crate) fn iter_mut(&mut self) -> IterMut<FStringPart> {
1078+
self.as_mut_slice().iter_mut()
1079+
}
1080+
10711081
/// Returns an iterator over the [`StringLiteral`] parts contained in this value.
10721082
///
10731083
/// Note that this doesn't nest into the f-string parts. For example,
@@ -1078,7 +1088,7 @@ impl FStringValue {
10781088
///
10791089
/// Here, the string literal parts returned would be `"foo"` and `"baz"`.
10801090
pub fn literals(&self) -> impl Iterator<Item = &StringLiteral> {
1081-
self.parts().filter_map(|part| part.as_literal())
1091+
self.iter().filter_map(|part| part.as_literal())
10821092
}
10831093

10841094
/// Returns an iterator over the [`FString`] parts contained in this value.
@@ -1091,7 +1101,7 @@ impl FStringValue {
10911101
///
10921102
/// Here, the f-string parts returned would be `f"bar {x}"` and `f"qux"`.
10931103
pub fn f_strings(&self) -> impl Iterator<Item = &FString> {
1094-
self.parts().filter_map(|part| part.as_f_string())
1104+
self.iter().filter_map(|part| part.as_f_string())
10951105
}
10961106

10971107
/// Returns an iterator over all the [`FStringElement`] contained in this value.
@@ -1110,6 +1120,15 @@ impl FStringValue {
11101120
}
11111121
}
11121122

1123+
impl<'a> IntoIterator for &'a FStringValue {
1124+
type Item = &'a FStringPart;
1125+
type IntoIter = Iter<'a, FStringPart>;
1126+
1127+
fn into_iter(self) -> Self::IntoIter {
1128+
self.iter()
1129+
}
1130+
}
1131+
11131132
/// An internal representation of [`FStringValue`].
11141133
#[derive(Clone, Debug, PartialEq)]
11151134
enum FStringValueInner {
@@ -1238,26 +1257,36 @@ impl StringLiteralValue {
12381257
/// For an implicitly concatenated string, it returns `true` only if the first
12391258
/// string literal is a unicode string.
12401259
pub fn is_unicode(&self) -> bool {
1241-
self.parts().next().map_or(false, |part| part.unicode)
1260+
self.iter().next().map_or(false, |part| part.unicode)
12421261
}
12431262

1244-
/// Returns an iterator over all the [`StringLiteral`] parts contained in this value.
1245-
pub fn parts(&self) -> impl Iterator<Item = &StringLiteral> {
1263+
/// Returns a slice of all the [`StringLiteral`] parts contained in this value.
1264+
pub fn as_slice(&self) -> &[StringLiteral] {
12461265
match &self.inner {
1247-
StringLiteralValueInner::Single(value) => Left(std::iter::once(value)),
1248-
StringLiteralValueInner::Concatenated(value) => Right(value.strings.iter()),
1266+
StringLiteralValueInner::Single(value) => std::slice::from_ref(value),
1267+
StringLiteralValueInner::Concatenated(value) => value.strings.as_slice(),
12491268
}
12501269
}
12511270

1252-
/// Returns an iterator over all the [`StringLiteral`] parts contained in this value
1253-
/// that allows modification.
1254-
pub(crate) fn parts_mut(&mut self) -> impl Iterator<Item = &mut StringLiteral> {
1271+
/// Returns a mutable slice of all the [`StringLiteral`] parts contained in this value.
1272+
fn as_mut_slice(&mut self) -> &mut [StringLiteral] {
12551273
match &mut self.inner {
1256-
StringLiteralValueInner::Single(value) => Left(std::iter::once(value)),
1257-
StringLiteralValueInner::Concatenated(value) => Right(value.strings.iter_mut()),
1274+
StringLiteralValueInner::Single(value) => std::slice::from_mut(value),
1275+
StringLiteralValueInner::Concatenated(value) => value.strings.as_mut_slice(),
12581276
}
12591277
}
12601278

1279+
/// Returns an iterator over all the [`StringLiteral`] parts contained in this value.
1280+
pub fn iter(&self) -> Iter<StringLiteral> {
1281+
self.as_slice().iter()
1282+
}
1283+
1284+
/// Returns an iterator over all the [`StringLiteral`] parts contained in this value
1285+
/// that allows modification.
1286+
pub(crate) fn iter_mut(&mut self) -> IterMut<StringLiteral> {
1287+
self.as_mut_slice().iter_mut()
1288+
}
1289+
12611290
/// Returns `true` if the string literal value is empty.
12621291
pub fn is_empty(&self) -> bool {
12631292
self.len() == 0
@@ -1266,12 +1295,12 @@ impl StringLiteralValue {
12661295
/// Returns the total length of the string literal value, in bytes, not
12671296
/// [`char`]s or graphemes.
12681297
pub fn len(&self) -> usize {
1269-
self.parts().fold(0, |acc, part| acc + part.value.len())
1298+
self.iter().fold(0, |acc, part| acc + part.value.len())
12701299
}
12711300

12721301
/// Returns an iterator over the [`char`]s of each string literal part.
12731302
pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
1274-
self.parts().flat_map(|part| part.value.chars())
1303+
self.iter().flat_map(|part| part.value.chars())
12751304
}
12761305

12771306
/// Returns the concatenated string value as a [`str`].
@@ -1286,6 +1315,15 @@ impl StringLiteralValue {
12861315
}
12871316
}
12881317

1318+
impl<'a> IntoIterator for &'a StringLiteralValue {
1319+
type Item = &'a StringLiteral;
1320+
type IntoIter = Iter<'a, StringLiteral>;
1321+
1322+
fn into_iter(self) -> Self::IntoIter {
1323+
self.iter()
1324+
}
1325+
}
1326+
12891327
impl PartialEq<str> for StringLiteralValue {
12901328
fn eq(&self, other: &str) -> bool {
12911329
if self.len() != other.len() {
@@ -1457,37 +1495,55 @@ impl BytesLiteralValue {
14571495
matches!(self.inner, BytesLiteralValueInner::Concatenated(_))
14581496
}
14591497

1460-
/// Returns an iterator over all the [`BytesLiteral`] parts contained in this value.
1461-
pub fn parts(&self) -> impl Iterator<Item = &BytesLiteral> {
1498+
/// Returns a slice of all the [`BytesLiteral`] parts contained in this value.
1499+
pub fn as_slice(&self) -> &[BytesLiteral] {
14621500
match &self.inner {
1463-
BytesLiteralValueInner::Single(value) => Left(std::iter::once(value)),
1464-
BytesLiteralValueInner::Concatenated(values) => Right(values.iter()),
1501+
BytesLiteralValueInner::Single(value) => std::slice::from_ref(value),
1502+
BytesLiteralValueInner::Concatenated(value) => value.as_slice(),
14651503
}
14661504
}
14671505

1468-
/// Returns an iterator over all the [`BytesLiteral`] parts contained in this value
1469-
/// that allows modification.
1470-
pub(crate) fn parts_mut(&mut self) -> impl Iterator<Item = &mut BytesLiteral> {
1506+
/// Returns a mutable slice of all the [`BytesLiteral`] parts contained in this value.
1507+
fn as_mut_slice(&mut self) -> &mut [BytesLiteral] {
14711508
match &mut self.inner {
1472-
BytesLiteralValueInner::Single(value) => Left(std::iter::once(value)),
1473-
BytesLiteralValueInner::Concatenated(values) => Right(values.iter_mut()),
1509+
BytesLiteralValueInner::Single(value) => std::slice::from_mut(value),
1510+
BytesLiteralValueInner::Concatenated(value) => value.as_mut_slice(),
14741511
}
14751512
}
14761513

1514+
/// Returns an iterator over all the [`BytesLiteral`] parts contained in this value.
1515+
pub fn iter(&self) -> Iter<BytesLiteral> {
1516+
self.as_slice().iter()
1517+
}
1518+
1519+
/// Returns an iterator over all the [`BytesLiteral`] parts contained in this value
1520+
/// that allows modification.
1521+
pub(crate) fn iter_mut(&mut self) -> IterMut<BytesLiteral> {
1522+
self.as_mut_slice().iter_mut()
1523+
}
1524+
14771525
/// Returns `true` if the concatenated bytes has a length of zero.
14781526
pub fn is_empty(&self) -> bool {
1479-
self.parts().all(|part| part.is_empty())
1527+
self.iter().all(|part| part.is_empty())
14801528
}
14811529

14821530
/// Returns the length of the concatenated bytes.
14831531
pub fn len(&self) -> usize {
1484-
self.parts().map(|part| part.len()).sum()
1532+
self.iter().map(|part| part.len()).sum()
14851533
}
14861534

14871535
/// Returns an iterator over the bytes of the concatenated bytes.
14881536
fn bytes(&self) -> impl Iterator<Item = u8> + '_ {
1489-
self.parts()
1490-
.flat_map(|part| part.as_slice().iter().copied())
1537+
self.iter().flat_map(|part| part.as_slice().iter().copied())
1538+
}
1539+
}
1540+
1541+
impl<'a> IntoIterator for &'a BytesLiteralValue {
1542+
type Item = &'a BytesLiteral;
1543+
type IntoIter = Iter<'a, BytesLiteral>;
1544+
1545+
fn into_iter(self) -> Self::IntoIter {
1546+
self.iter()
14911547
}
14921548
}
14931549

crates/ruff_python_ast/src/visitor.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ pub fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a Expr) {
477477
visitor.visit_arguments(arguments);
478478
}
479479
Expr::FString(ast::ExprFString { value, .. }) => {
480-
for part in value.parts() {
480+
for part in value {
481481
match part {
482482
FStringPart::Literal(string_literal) => {
483483
visitor.visit_string_literal(string_literal);
@@ -487,12 +487,12 @@ pub fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a Expr) {
487487
}
488488
}
489489
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
490-
for string_literal in value.parts() {
490+
for string_literal in value {
491491
visitor.visit_string_literal(string_literal);
492492
}
493493
}
494494
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
495-
for bytes_literal in value.parts() {
495+
for bytes_literal in value {
496496
visitor.visit_bytes_literal(bytes_literal);
497497
}
498498
}

0 commit comments

Comments
 (0)