Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@
## False negatives

print(("%" "z") % 1)

## `%b` is only valid for bytes formatting.
"%b" % b"25"
b"%b" % b"25"
Comment thread
ntBre marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,6 @@
"%c" % ("x",)
"%c" % "x"
"%c" % "œ"

# No errors here, will be reported separately by bad-string-format-character.
"%b" % b"xx"
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,12 @@ PLE1300 Unsupported format character 'y'
20 | "{0:.{prec}g}".format(1.23, prec=15) # OK (cannot validate after nested placeholder)
21 | "{0:.{foo}{bar}{foobar}y}".format(...) # OK (cannot validate after nested placeholders)
|

PLE1300 Unsupported format character 'b'
--> bad_string_format_character.py:37:1
|
36 | ## `%b` is only valid for bytes formatting.
37 | "%b" % b"25"
| ^^^^^^^^^^^^
38 | b"%b" % b"25"
|
40 changes: 29 additions & 11 deletions crates/ruff_python_literal/src/cformat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ pub enum CFormatType {
String(CFormatConversion),
}

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum CFormatContext {
Comment thread
ntBre marked this conversation as resolved.
Str,
Bytes,
}

#[derive(Debug, PartialEq)]
pub enum CFormatPrecision {
Quantity(CFormatQuantity),
Expand Down Expand Up @@ -123,14 +129,17 @@ impl FromStr for CFormatSpec {
return Err((CFormatErrorType::MissingModuloSign, 1));
}

CFormatSpec::parse(&mut chars)
CFormatSpec::parse(&mut chars, CFormatContext::Str)
}
}

pub type ParseIter<I> = Peekable<Enumerate<I>>;

impl CFormatSpec {
pub fn parse<T, I>(iter: &mut ParseIter<I>) -> Result<Self, ParsingError>
pub fn parse<T, I>(
iter: &mut ParseIter<I>,
context: CFormatContext,
) -> Result<Self, ParsingError>
where
T: Into<char> + Copy,
I: Iterator<Item = T>,
Expand All @@ -140,7 +149,7 @@ impl CFormatSpec {
let min_field_width = parse_quantity(iter)?;
let precision = parse_precision(iter)?;
consume_length(iter);
let (format_type, format_char) = parse_format_type(iter)?;
let (format_type, format_char) = parse_format_type(iter, context)?;

Ok(CFormatSpec {
mapping_key,
Expand Down Expand Up @@ -204,7 +213,10 @@ where
}
}

fn parse_format_type<T, I>(iter: &mut ParseIter<I>) -> Result<(CFormatType, char), ParsingError>
fn parse_format_type<T, I>(
iter: &mut ParseIter<I>,
context: CFormatContext,
) -> Result<(CFormatType, char), ParsingError>
where
T: Into<char>,
I: Iterator<Item = T>,
Expand Down Expand Up @@ -234,7 +246,9 @@ where
'c' => CFormatType::Character,
'r' => CFormatType::String(CFormatConversion::Repr),
's' => CFormatType::String(CFormatConversion::Str),
'b' => CFormatType::String(CFormatConversion::Bytes),
// `%b` is only valid for bytes formatting (e.g. `b"%b" % b"x"`), not for string
// formatting.
'b' if context == CFormatContext::Bytes => CFormatType::String(CFormatConversion::Bytes),
'a' => CFormatType::String(CFormatConversion::Ascii),
_ => return Err((CFormatErrorType::UnsupportedFormatChar(c), index)),
};
Expand Down Expand Up @@ -363,9 +377,11 @@ impl CFormatBytes {
CFormatPart::Literal(std::mem::take(&mut literal)),
));
}
let spec = CFormatSpec::parse(iter).map_err(|err| CFormatError {
typ: err.0,
index: err.1,
let spec = CFormatSpec::parse(iter, CFormatContext::Bytes).map_err(|err| {
CFormatError {
typ: err.0,
index: err.1,
}
})?;
parts.push((index, CFormatPart::Spec(spec)));
if let Some(&(index, _)) = iter.peek() {
Expand Down Expand Up @@ -418,9 +434,11 @@ impl CFormatString {
CFormatPart::Literal(std::mem::take(&mut literal)),
));
}
let spec = CFormatSpec::parse(iter).map_err(|err| CFormatError {
typ: err.0,
index: err.1,
let spec = CFormatSpec::parse(iter, CFormatContext::Str).map_err(|err| {
CFormatError {
typ: err.0,
index: err.1,
}
})?;
parts.push((index, CFormatPart::Spec(spec)));
if let Some(&(index, _)) = iter.peek() {
Expand Down
Loading