Skip to content

Commit

Permalink
Assorted cleanups and simplifications enabled by the refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood committed Mar 6, 2024
1 parent b70fd1b commit a2513c3
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 75 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::str::leading_quote;
use ruff_python_parser::lexer::LexResult;
use ruff_python_parser::Tok;
use ruff_source_file::Locator;
Expand Down Expand Up @@ -176,9 +175,7 @@ pub(crate) fn avoidable_escaped_quote(
}

// Check if we're using the preferred quotation style.
if !leading_quote(locator.slice(tok_range)).is_some_and(|text| {
contains_quote(text, quotes_settings.inline_quotes.as_char())
}) {
if !contains_quote(flags.quote_str(), quotes_settings.inline_quotes.as_char()) {
continue;
}

Expand Down Expand Up @@ -318,11 +315,7 @@ pub(crate) fn unnecessary_escaped_quote(
continue;
}

let leading = match leading_quote(locator.slice(tok_range)) {
Some("\"") => Quote::Double,
Some("'") => Quote::Single,
_ => continue,
};
let leading = flags.quote_style();
if !contains_escaped_quote(string_contents, leading.opposite().as_char()) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ pub(crate) fn invalid_escape_sequence(
invalid_escape_char.range(),
);

if flags.map_or(false, |flags| flags.is_ustring()) {
if flags.is_some_and(|flags| flags.is_ustring()) {
// Replace the Unicode prefix with `r`.
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
"r".to_string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ use std::str::FromStr;

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::str::{leading_quote, trailing_quote};
use ruff_python_ast::Expr;
use ruff_python_literal::{
cformat::{CFormatErrorType, CFormatString},
format::FormatPart,
format::FromTemplate,
format::{FormatSpec, FormatSpecError, FormatString},
};
use ruff_python_parser::{lexer, Mode};
use ruff_python_parser::{lexer, Mode, StringFlags, Tok};
use ruff_text_size::{Ranged, TextRange};

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -93,15 +92,15 @@ pub(crate) fn call(checker: &mut Checker, string: &str, range: TextRange) {
/// Ex) `"%z" % "1"`
pub(crate) fn percent(checker: &mut Checker, expr: &Expr) {
// Grab each string segment (in case there's an implicit concatenation).
let mut strings: Vec<TextRange> = vec![];
let mut strings: Vec<(TextRange, StringFlags)> = vec![];
for (tok, range) in
lexer::lex_starts_at(checker.locator().slice(expr), Mode::Module, expr.start()).flatten()
{
if tok.is_string() {
strings.push(range);
} else if tok.is_percent() {
match tok {
Tok::String { flags, .. } => strings.push((range, flags)),
// Break as soon as we find the modulo symbol.
break;
Tok::Percent => break,
_ => {}
}
}

Expand All @@ -110,12 +109,11 @@ pub(crate) fn percent(checker: &mut Checker, expr: &Expr) {
return;
}

for range in &strings {
for (range, flags) in &strings {
let string = checker.locator().slice(*range);
let (Some(leader), Some(trailer)) = (leading_quote(string), trailing_quote(string)) else {
return;
};
let string = &string[leader.len()..string.len() - trailer.len()];
let quote_len = usize::from(flags.quote_len());
let string =
&string[(usize::from(flags.prefix_len()) + quote_len)..(string.len() - quote_len)];

// Parse the format string (e.g. `"%s"`) into a list of `PercentFormat`.
if let Err(format_error) = CFormatString::from_str(string) {
Expand Down
22 changes: 10 additions & 12 deletions crates/ruff_linter/src/rules/pylint/rules/bad_string_format_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ use std::str::FromStr;

use ruff_python_ast::{self as ast, Expr};
use ruff_python_literal::cformat::{CFormatPart, CFormatSpec, CFormatStrOrBytes, CFormatString};
use ruff_python_parser::{lexer, AsMode};
use ruff_python_parser::{lexer, AsMode, StringFlags, Tok};
use ruff_text_size::{Ranged, TextRange};
use rustc_hash::FxHashMap;

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::str::{leading_quote, trailing_quote};
use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType};

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -219,15 +218,15 @@ fn is_valid_dict(
pub(crate) fn bad_string_format_type(checker: &mut Checker, expr: &Expr, right: &Expr) {
// Grab each string segment (in case there's an implicit concatenation).
let content = checker.locator().slice(expr);
let mut strings: Vec<TextRange> = vec![];
let mut strings: Vec<(TextRange, StringFlags)> = vec![];
for (tok, range) in
lexer::lex_starts_at(content, checker.source_type.as_mode(), expr.start()).flatten()
{
if tok.is_string() {
strings.push(range);
} else if tok.is_percent() {
match tok {
Tok::String { flags, .. } => strings.push((range, flags)),
// Break as soon as we find the modulo symbol.
break;
Tok::Percent => break,
_ => {}
}
}

Expand All @@ -238,12 +237,11 @@ pub(crate) fn bad_string_format_type(checker: &mut Checker, expr: &Expr, right:

// Parse each string segment.
let mut format_strings = vec![];
for range in &strings {
for (range, flags) in &strings {
let string = checker.locator().slice(*range);
let (Some(leader), Some(trailer)) = (leading_quote(string), trailing_quote(string)) else {
return;
};
let string = &string[leader.len()..string.len() - trailer.len()];
let quote_len = usize::from(flags.quote_len());
let string =
&string[(usize::from(flags.prefix_len()) + quote_len)..(string.len() - quote_len)];

// Parse the format string (e.g. `"%s"`) into a list of `PercentFormat`.
if let Ok(format_string) = CFormatString::from_str(string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ use std::str::FromStr;

use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::str::{leading_quote, trailing_quote};
use ruff_python_ast::whitespace::indentation;
use ruff_python_ast::{self as ast, Expr};
use ruff_python_codegen::Stylist;
use ruff_python_literal::cformat::{
CConversionFlags, CFormatPart, CFormatPrecision, CFormatQuantity, CFormatString,
};
use ruff_python_parser::{lexer, AsMode, Tok};
use ruff_python_parser::{lexer, AsMode, StringFlags, Tok};
use ruff_python_stdlib::identifiers::is_identifier;
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange};
Expand Down Expand Up @@ -353,7 +352,7 @@ fn convertible(format_string: &CFormatString, params: &Expr) -> bool {
/// UP031
pub(crate) fn printf_string_formatting(checker: &mut Checker, expr: &Expr, right: &Expr) {
// Grab each string segment (in case there's an implicit concatenation).
let mut strings: Vec<TextRange> = vec![];
let mut strings: Vec<(TextRange, StringFlags)> = vec![];
let mut extension = None;
for (tok, range) in lexer::lex_starts_at(
checker.locator().slice(expr),
Expand All @@ -362,14 +361,13 @@ pub(crate) fn printf_string_formatting(checker: &mut Checker, expr: &Expr, right
)
.flatten()
{
if tok.is_string() {
strings.push(range);
} else if matches!(tok, Tok::Rpar) {
match tok {
Tok::String { flags, .. } => strings.push((range, flags)),
// If we hit a right paren, we have to preserve it.
extension = Some(range);
} else if matches!(tok, Tok::Percent) {
Tok::Rpar => extension = Some(range),
// Break as soon as we find the modulo symbol.
break;
Tok::Percent => break,
_ => continue,
}
}

Expand All @@ -382,12 +380,11 @@ pub(crate) fn printf_string_formatting(checker: &mut Checker, expr: &Expr, right
let mut num_positional_arguments = 0;
let mut num_keyword_arguments = 0;
let mut format_strings = Vec::with_capacity(strings.len());
for range in &strings {
for (range, flags) in &strings {
let string = checker.locator().slice(*range);
let (Some(leader), Some(trailer)) = (leading_quote(string), trailing_quote(string)) else {
return;
};
let string = &string[leader.len()..string.len() - trailer.len()];
let quote_len = usize::from(flags.quote_len());
let string =
&string[(usize::from(flags.prefix_len()) + quote_len)..(string.len() - quote_len)];

// Parse the format string (e.g. `"%s"`) into a list of `PercentFormat`.
let Ok(format_string) = CFormatString::from_str(string) else {
Expand All @@ -410,8 +407,14 @@ pub(crate) fn printf_string_formatting(checker: &mut Checker, expr: &Expr, right
}

// Convert the `%`-format string to a `.format` string.
let format_string = percent_to_format(&format_string);
format_strings.push(format!("{leader}{format_string}{trailer}"));
let quotes = flags.quote_str();
format_strings.push(format!(
"{}{}{}{}",
flags.prefix_str(),
quotes,
percent_to_format(&format_string),
quotes
));
}

// Parse the parameters.
Expand Down Expand Up @@ -459,7 +462,7 @@ pub(crate) fn printf_string_formatting(checker: &mut Checker, expr: &Expr, right
// Reconstruct the string.
let mut contents = String::new();
let mut prev = None;
for (range, format_string) in strings.iter().zip(format_strings) {
for ((range, _), format_string) in strings.iter().zip(format_strings) {
// Add the content before the string segment.
match prev {
None => {
Expand Down
40 changes: 18 additions & 22 deletions crates/ruff_python_codegen/src/stylist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use ruff_python_parser::lexer::LexResult;
use ruff_python_parser::Tok;
use ruff_source_file::{find_newline, LineEnding};

use ruff_python_ast::str::leading_quote;
use ruff_source_file::Locator;

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -44,34 +43,22 @@ impl<'a> Stylist<'a> {
Self {
locator,
indentation,
quote: detect_quote(tokens, locator),
quote: detect_quote(tokens),
line_ending: OnceCell::default(),
}
}
}

fn detect_quote(tokens: &[LexResult], locator: &Locator) -> Quote {
let quote_range = tokens.iter().flatten().find_map(|(t, range)| match t {
Tok::String { flags, .. } if !flags.is_triple_quoted() => Some(*range),
// No need to check if it's triple-quoted as f-strings cannot be used
// as docstrings.
Tok::FStringStart(_) => Some(*range),
_ => None,
});

if let Some(quote_range) = quote_range {
let content = &locator.slice(quote_range);
if let Some(quotes) = leading_quote(content) {
return if quotes.contains('\'') {
Quote::Single
} else if quotes.contains('"') {
Quote::Double
} else {
unreachable!("Expected string to start with a valid quote prefix")
};
fn detect_quote(tokens: &[LexResult]) -> Quote {
for (token, _) in tokens.iter().flatten() {
match token {
Tok::String { flags, .. } if !flags.is_triple_quoted() => {
return flags.quote_style().into()
}
Tok::FStringStart(flags) => return flags.quote_style().into(),
_ => continue,
}
}

Quote::default()
}

Expand Down Expand Up @@ -115,6 +102,15 @@ pub enum Quote {
Double,
}

impl From<ruff_python_parser::QuoteStyle> for Quote {
fn from(value: ruff_python_parser::QuoteStyle) -> Self {
match value {
ruff_python_parser::QuoteStyle::Double => Self::Double,
ruff_python_parser::QuoteStyle::Single => Self::Single,
}
}
}

impl From<Quote> for char {
fn from(val: Quote) -> Self {
match val {
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_python_parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ pub use parser::{
};
use ruff_python_ast::{Mod, PySourceType, Suite};
pub use string::FStringErrorType;
pub use string_token_flags::{QuoteStyle, StringFlags};
pub use token::{Tok, TokenKind};

use crate::lexer::LexResult;
Expand Down
13 changes: 12 additions & 1 deletion crates/ruff_python_parser/src/string_token_flags.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use bitflags::bitflags;

use ruff_text_size::TextSize;

bitflags! {
Expand Down Expand Up @@ -227,11 +228,13 @@ impl StringFlags {
}
}

#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
/// TODO: Use this enum in `crates/ruff_python_formatter/src/string/mod.rs`?
#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)]
pub enum QuoteStyle {
/// E.g. '
Single,
/// E.g. "
#[default]
Double,
}

Expand All @@ -242,4 +245,12 @@ impl QuoteStyle {
Self::Double => '"',
}
}

#[must_use]
pub const fn opposite(self) -> Self {
match self {
Self::Single => Self::Double,
Self::Double => Self::Single,
}
}
}
2 changes: 1 addition & 1 deletion crates/ruff_python_parser/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub enum Tok {
value: Box<str>,
/// Flags that can be queried to determine the quote style
/// and prefixes of the string
flags: StringFlags
flags: StringFlags,
},
/// Token value for the end of an f-string. This includes the closing quote.
FStringEnd(StringFlags),
Expand Down

0 comments on commit a2513c3

Please sign in to comment.