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); let mut rule_checker = AvoidableEscapedQuoteChecker::new(checker.locator(), checker.settings);
match string_like { for part in string_like.parts() {
StringLike::String(expr) => { match part {
for string_literal in &expr.value { ast::StringLikePart::String(string_literal) => {
rule_checker.visit_string_literal(string_literal); rule_checker.visit_string_literal(string_literal);
} }
} ast::StringLikePart::Bytes(bytes_literal) => {
StringLike::Bytes(expr) => {
for bytes_literal in &expr.value {
rule_checker.visit_bytes_literal(bytes_literal); rule_checker.visit_bytes_literal(bytes_literal);
} }
} ast::StringLikePart::FString(f_string) => rule_checker.visit_f_string(f_string),
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);
}
}
}
} }
} }

View File

@@ -454,11 +454,10 @@ pub(crate) fn check_string_quotes(checker: &mut Checker, string_like: StringLike
return; return;
} }
let ranges: Vec<TextRange> = match string_like { let ranges = string_like
StringLike::String(node) => node.value.iter().map(Ranged::range).collect(), .parts()
StringLike::Bytes(node) => node.value.iter().map(Ranged::range).collect(), .map(|part| part.range())
StringLike::FString(node) => node.value.iter().map(Ranged::range).collect(), .collect::<Vec<_>>();
};
if checker.semantic().in_pep_257_docstring() { if checker.semantic().in_pep_257_docstring() {
if checker.enabled(Rule::BadQuotesDocstring) { 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(); let locator = checker.locator();
match string_like { for part in string_like.parts() {
StringLike::String(expr) => { match part {
for string in &expr.value { ast::StringLikePart::String(string_literal) => {
if let Some(diagnostic) = check_string_or_bytes( if let Some(diagnostic) = check_string_or_bytes(
locator, locator,
string.range(), string_literal.range(),
AnyStringKind::from(string.flags), AnyStringKind::from(string_literal.flags),
) { ) {
checker.diagnostics.push(diagnostic); checker.diagnostics.push(diagnostic);
} }
} }
} ast::StringLikePart::Bytes(bytes_literal) => {
StringLike::Bytes(expr) => { if let Some(diagnostic) = check_string_or_bytes(
for bytes in &expr.value { locator,
if let Some(diagnostic) = bytes_literal.range(),
check_string_or_bytes(locator, bytes.range(), AnyStringKind::from(bytes.flags)) AnyStringKind::from(bytes_literal.flags),
{ ) {
checker.diagnostics.push(diagnostic); checker.diagnostics.push(diagnostic);
} }
} }
} ast::StringLikePart::FString(f_string) => {
StringLike::FString(expr) => { if let Some(diagnostic) = check_f_string(locator, f_string) {
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),
} {
checker.diagnostics.push(diagnostic); checker.diagnostics.push(diagnostic);
}; }
} }
} }
} }

View File

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

View File

@@ -1,3 +1,5 @@
use std::iter::FusedIterator;
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
use crate::AnyNodeRef; use crate::AnyNodeRef;
@@ -394,9 +396,8 @@ impl LiteralExpressionRef<'_> {
} }
} }
/// An enum that holds a reference to a string-like literal from the AST. /// An enum that holds a reference to a string-like expression from the AST. This includes string
/// This includes string literals, bytes literals, and the literal parts of /// literals, bytes literals, and f-strings.
/// f-strings.
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum StringLike<'a> { pub enum StringLike<'a> {
String(&'a ast::ExprStringLiteral), String(&'a ast::ExprStringLiteral),
@@ -404,6 +405,17 @@ pub enum StringLike<'a> {
FString(&'a ast::ExprFString), 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> { impl<'a> From<&'a ast::ExprStringLiteral> for StringLike<'a> {
fn from(value: &'a ast::ExprStringLiteral) -> Self { fn from(value: &'a ast::ExprStringLiteral) -> Self {
StringLike::String(value) 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>, 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 { impl Ranged for FStringFormatSpec {
fn range(&self) -> TextRange { fn range(&self) -> TextRange {
self.range self.range