Detect and reject out-of-range integers in format string literals

Until now out-of-range integers in format string literals
were silently ignored. They wrapped around to zero at
usize::MAX, producing unexpected results.

When using debug builds of rustc, such integers in format string
literals even cause an 'attempt to add with overflow' panic in
rustc.

Fix this by producing an error diagnostic for integers in format
string literals which do not fit into usize.

Fixes #102528
This commit is contained in:
Colin Baumgarten 2022-10-01 00:40:59 +02:00
parent 75d3027fb5
commit b9e85bf60a
3 changed files with 40 additions and 5 deletions

View File

@ -740,20 +740,40 @@ fn word(&mut self) -> &'a str {
word
}
/// Optionally parses an integer at the current position. This doesn't deal
/// with overflow at all, it's just accumulating digits.
fn integer(&mut self) -> Option<usize> {
let mut cur = 0;
let mut cur: usize = 0;
let mut found = false;
let mut overflow = false;
let start = self.current_pos();
while let Some(&(_, c)) = self.cur.peek() {
if let Some(i) = c.to_digit(10) {
cur = cur * 10 + i as usize;
let (tmp, mul_overflow) = cur.overflowing_mul(10);
let (tmp, add_overflow) = tmp.overflowing_add(i as usize);
if mul_overflow || add_overflow {
overflow = true;
}
cur = tmp;
found = true;
self.cur.next();
} else {
break;
}
}
if overflow {
let end = self.current_pos();
let overflowed_int = &self.input[start..end];
self.err(
format!(
"integer `{}` does not fit into the type `usize` whose range is `0..={}`",
overflowed_int,
usize::MAX
),
"integer out of range for `usize`",
self.span(start, end),
);
}
if found { Some(cur) } else { None }
}

View File

@ -57,6 +57,21 @@ fn invalid06() {
musterr("{:>>>}")
}
#[test]
fn invalid_position() {
musterr("{18446744073709551616}");
}
#[test]
fn invalid_width() {
musterr("{:18446744073709551616}");
}
#[test]
fn invalid_precision() {
musterr("{:.18446744073709551616}");
}
#[test]
fn format_nothing() {
same(

View File

@ -327,7 +327,7 @@
//! - `text` must not contain any `'{'` or `'}'` characters,
//! - `ws` is any character for which [`char::is_whitespace`] returns `true`, has no semantic
//! meaning and is completely optional,
//! - `integer` is a decimal integer that may contain leading zeroes and
//! - `integer` is a decimal integer that may contain leading zeroes and must fit into an `usize` and
//! - `identifier` is an `IDENTIFIER_OR_KEYWORD` (not an `IDENTIFIER`) as defined by the [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html).
//!
//! # Formatting traits