Compare commits

...

2 Commits

Author SHA1 Message Date
Dhruv Manilawala
ce4d4ae6ac Add methods to iter over format spec elements 2024-05-13 14:17:23 +05:30
Dhruv Manilawala
128414cd95 Add Iterator impl for StringLike parts 2024-05-13 13:48:54 +05:30
6 changed files with 150 additions and 81 deletions

View File

@@ -64,28 +64,15 @@ pub(crate) fn avoidable_escaped_quote(checker: &mut Checker, string_like: String
let mut rule_checker = AvoidableEscapedQuoteChecker::new(checker.locator(), checker.settings);
match string_like {
StringLike::String(expr) => {
for string_literal in &expr.value {
for part in string_like.parts() {
match part {
ast::StringLikePart::String(string_literal) => {
rule_checker.visit_string_literal(string_literal);
}
}
StringLike::Bytes(expr) => {
for bytes_literal in &expr.value {
ast::StringLikePart::Bytes(bytes_literal) => {
rule_checker.visit_bytes_literal(bytes_literal);
}
}
StringLike::FString(expr) => {
for part in &expr.value {
match part {
ast::FStringPart::Literal(string_literal) => {
rule_checker.visit_string_literal(string_literal);
}
ast::FStringPart::FString(f_string) => {
rule_checker.visit_f_string(f_string);
}
}
}
ast::StringLikePart::FString(f_string) => rule_checker.visit_f_string(f_string),
}
}

View File

@@ -454,11 +454,10 @@ pub(crate) fn check_string_quotes(checker: &mut Checker, string_like: StringLike
return;
}
let ranges: Vec<TextRange> = match string_like {
StringLike::String(node) => node.value.iter().map(Ranged::range).collect(),
StringLike::Bytes(node) => node.value.iter().map(Ranged::range).collect(),
StringLike::FString(node) => node.value.iter().map(Ranged::range).collect(),
};
let ranges = string_like
.parts()
.map(|part| part.range())
.collect::<Vec<_>>();
if checker.semantic().in_pep_257_docstring() {
if checker.enabled(Rule::BadQuotesDocstring) {

View File

@@ -53,39 +53,30 @@ pub(crate) fn unnecessary_escaped_quote(checker: &mut Checker, string_like: Stri
let locator = checker.locator();
match string_like {
StringLike::String(expr) => {
for string in &expr.value {
for part in string_like.parts() {
match part {
ast::StringLikePart::String(string_literal) => {
if let Some(diagnostic) = check_string_or_bytes(
locator,
string.range(),
AnyStringKind::from(string.flags),
string_literal.range(),
AnyStringKind::from(string_literal.flags),
) {
checker.diagnostics.push(diagnostic);
}
}
}
StringLike::Bytes(expr) => {
for bytes in &expr.value {
if let Some(diagnostic) =
check_string_or_bytes(locator, bytes.range(), AnyStringKind::from(bytes.flags))
{
ast::StringLikePart::Bytes(bytes_literal) => {
if let Some(diagnostic) = check_string_or_bytes(
locator,
bytes_literal.range(),
AnyStringKind::from(bytes_literal.flags),
) {
checker.diagnostics.push(diagnostic);
}
}
}
StringLike::FString(expr) => {
for part in &expr.value {
if let Some(diagnostic) = match part {
ast::FStringPart::Literal(string) => check_string_or_bytes(
locator,
string.range(),
AnyStringKind::from(string.flags),
),
ast::FStringPart::FString(f_string) => check_f_string(locator, f_string),
} {
ast::StringLikePart::FString(f_string) => {
if let Some(diagnostic) = check_f_string(locator, f_string) {
checker.diagnostics.push(diagnostic);
};
}
}
}
}

View File

@@ -192,48 +192,32 @@ pub(crate) fn ambiguous_unicode_character_string(checker: &mut Checker, string_l
Context::String
};
match string_like {
StringLike::String(node) => {
for literal in &node.value {
let text = checker.locator().slice(literal);
for part in string_like.parts() {
match part {
ast::StringLikePart::String(string_literal) => {
let text = checker.locator().slice(string_literal);
ambiguous_unicode_character(
&mut checker.diagnostics,
text,
literal.range(),
string_literal.range(),
context,
checker.settings,
);
}
}
StringLike::FString(node) => {
for part in &node.value {
match part {
ast::FStringPart::Literal(literal) => {
let text = checker.locator().slice(literal);
ambiguous_unicode_character(
&mut checker.diagnostics,
text,
literal.range(),
context,
checker.settings,
);
}
ast::FStringPart::FString(f_string) => {
for literal in f_string.literals() {
let text = checker.locator().slice(literal);
ambiguous_unicode_character(
&mut checker.diagnostics,
text,
literal.range(),
context,
checker.settings,
);
}
}
ast::StringLikePart::Bytes(_) => {}
ast::StringLikePart::FString(f_string) => {
for literal in f_string.literals() {
let text = checker.locator().slice(literal);
ambiguous_unicode_character(
&mut checker.diagnostics,
text,
literal.range(),
context,
checker.settings,
);
}
}
}
StringLike::Bytes(_) => (),
}
}

View File

@@ -1,3 +1,5 @@
use std::iter::FusedIterator;
use ruff_text_size::{Ranged, TextRange};
use crate::AnyNodeRef;
@@ -394,9 +396,8 @@ impl LiteralExpressionRef<'_> {
}
}
/// An enum that holds a reference to a string-like literal from the AST.
/// This includes string literals, bytes literals, and the literal parts of
/// f-strings.
/// An enum that holds a reference to a string-like expression from the AST. This includes string
/// literals, bytes literals, and f-strings.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum StringLike<'a> {
String(&'a ast::ExprStringLiteral),
@@ -404,6 +405,17 @@ pub enum StringLike<'a> {
FString(&'a ast::ExprFString),
}
impl<'a> StringLike<'a> {
/// Returns an iterator over the [`StringLikePart`] contained in this string-like expression.
pub fn parts(&self) -> StringLikePartIter<'_> {
match self {
StringLike::String(expr) => StringLikePartIter::String(expr.value.iter()),
StringLike::Bytes(expr) => StringLikePartIter::Bytes(expr.value.iter()),
StringLike::FString(expr) => StringLikePartIter::FString(expr.value.iter()),
}
}
}
impl<'a> From<&'a ast::ExprStringLiteral> for StringLike<'a> {
fn from(value: &'a ast::ExprStringLiteral) -> Self {
StringLike::String(value)
@@ -431,3 +443,81 @@ impl Ranged for StringLike<'_> {
}
}
}
/// An enum that holds a reference to an individual part of a string-like expression.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum StringLikePart<'a> {
String(&'a ast::StringLiteral),
Bytes(&'a ast::BytesLiteral),
FString(&'a ast::FString),
}
impl<'a> From<&'a ast::StringLiteral> for StringLikePart<'a> {
fn from(value: &'a ast::StringLiteral) -> Self {
StringLikePart::String(value)
}
}
impl<'a> From<&'a ast::BytesLiteral> for StringLikePart<'a> {
fn from(value: &'a ast::BytesLiteral) -> Self {
StringLikePart::Bytes(value)
}
}
impl<'a> From<&'a ast::FString> for StringLikePart<'a> {
fn from(value: &'a ast::FString) -> Self {
StringLikePart::FString(value)
}
}
impl Ranged for StringLikePart<'_> {
fn range(&self) -> TextRange {
match self {
StringLikePart::String(part) => part.range(),
StringLikePart::Bytes(part) => part.range(),
StringLikePart::FString(part) => part.range(),
}
}
}
/// An iterator over all the [`StringLikePart`] of a string-like expression.
///
/// This is created by the [`StringLike::parts`] method.
pub enum StringLikePartIter<'a> {
String(std::slice::Iter<'a, ast::StringLiteral>),
Bytes(std::slice::Iter<'a, ast::BytesLiteral>),
FString(std::slice::Iter<'a, ast::FStringPart>),
}
impl<'a> Iterator for StringLikePartIter<'a> {
type Item = StringLikePart<'a>;
fn next(&mut self) -> Option<Self::Item> {
let part = match self {
StringLikePartIter::String(inner) => StringLikePart::String(inner.next()?),
StringLikePartIter::Bytes(inner) => StringLikePart::Bytes(inner.next()?),
StringLikePartIter::FString(inner) => {
let part = inner.next()?;
match part {
ast::FStringPart::Literal(string_literal) => {
StringLikePart::String(string_literal)
}
ast::FStringPart::FString(f_string) => StringLikePart::FString(f_string),
}
}
};
Some(part)
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
StringLikePartIter::String(inner) => inner.size_hint(),
StringLikePartIter::Bytes(inner) => inner.size_hint(),
StringLikePartIter::FString(inner) => inner.size_hint(),
}
}
}
impl FusedIterator for StringLikePartIter<'_> {}
impl ExactSizeIterator for StringLikePartIter<'_> {}

View File

@@ -1087,6 +1087,24 @@ pub struct FStringFormatSpec {
pub elements: Vec<FStringElement>,
}
impl FStringFormatSpec {
/// Returns an iterator over all the [`FStringLiteralElement`] nodes contained in this format
/// spec of the f-string.
pub fn literals(&self) -> impl Iterator<Item = &FStringLiteralElement> {
self.elements
.iter()
.filter_map(|element| element.as_literal())
}
/// Returns an iterator over all the [`FStringExpressionElement`] nodes contained in this
/// format spec of the f-string.
pub fn expressions(&self) -> impl Iterator<Item = &FStringExpressionElement> {
self.elements
.iter()
.filter_map(|element| element.as_expression())
}
}
impl Ranged for FStringFormatSpec {
fn range(&self) -> TextRange {
self.range