Add an implicit concatenation flag to string and bytes constants (#6512)

## Summary

Per the discussion in
https://github.com/astral-sh/ruff/discussions/6183, this PR adds an
`implicit_concatenated` flag to the string and bytes constant variants.
It's not actually _used_ anywhere as of this PR, but it is covered by
the tests.

Specifically, we now use a struct for the string and bytes cases, along
with the `Expr::FString` node. That struct holds the value, plus the
flag:

```rust
#[derive(Clone, Debug, PartialEq, is_macro::Is)]
pub enum Constant {
    Str(StringConstant),
    Bytes(BytesConstant),
    ...
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StringConstant {
    /// The string value as resolved by the parser (i.e., without quotes, or escape sequences, or
    /// implicit concatenations).
    pub value: String,
    /// Whether the string contains multiple string tokens that were implicitly concatenated.
    pub implicit_concatenated: bool,
}

impl Deref for StringConstant {
    type Target = str;
    fn deref(&self) -> &Self::Target {
        self.value.as_str()
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BytesConstant {
    /// The bytes value as resolved by the parser (i.e., without quotes, or escape sequences, or
    /// implicit concatenations).
    pub value: Vec<u8>,
    /// Whether the string contains multiple string tokens that were implicitly concatenated.
    pub implicit_concatenated: bool,
}

impl Deref for BytesConstant {
    type Target = [u8];
    fn deref(&self) -> &Self::Target {
        self.value.as_slice()
    }
}
```

## Test Plan

`cargo test`
This commit is contained in:
Charlie Marsh 2023-08-14 09:46:54 -04:00 committed by GitHub
parent fc0c9507d0
commit f16e780e0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
88 changed files with 1252 additions and 761 deletions

View file

@ -881,6 +881,8 @@ pub struct DebugText {
pub struct ExprFString {
pub range: TextRange,
pub values: Vec<Expr>,
/// Whether the f-string contains multiple string tokens that were implicitly concatenated.
pub implicit_concatenated: bool,
}
impl From<ExprFString> for Expr {
@ -2463,8 +2465,8 @@ impl std::cmp::PartialEq<usize> for Int {
pub enum Constant {
None,
Bool(bool),
Str(String),
Bytes(Vec<u8>),
Str(StringConstant),
Bytes(BytesConstant),
Int(BigInt),
Float(f64),
Complex { real: f64, imag: f64 },
@ -2472,38 +2474,68 @@ pub enum Constant {
}
impl Constant {
pub fn is_true(self) -> bool {
self.bool().is_some_and(|b| b)
}
pub fn is_false(self) -> bool {
self.bool().is_some_and(|b| !b)
}
pub fn complex(self) -> Option<(f64, f64)> {
/// Returns `true` if the constant is a string or bytes constant that contains multiple,
/// implicitly concatenated string tokens.
pub fn is_implicit_concatenated(&self) -> bool {
match self {
Constant::Complex { real, imag } => Some((real, imag)),
_ => None,
Constant::Str(value) => value.implicit_concatenated,
Constant::Bytes(value) => value.implicit_concatenated,
_ => false,
}
}
}
impl From<String> for Constant {
fn from(s: String) -> Constant {
Self::Str(s)
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StringConstant {
/// The string value as resolved by the parser (i.e., without quotes, or escape sequences, or
/// implicit concatenations).
pub value: String,
/// Whether the string contains multiple string tokens that were implicitly concatenated.
pub implicit_concatenated: bool,
}
impl Deref for StringConstant {
type Target = str;
fn deref(&self) -> &Self::Target {
self.value.as_str()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BytesConstant {
/// The bytes value as resolved by the parser (i.e., without quotes, or escape sequences, or
/// implicit concatenations).
pub value: Vec<u8>,
/// Whether the string contains multiple string tokens that were implicitly concatenated.
pub implicit_concatenated: bool,
}
impl Deref for BytesConstant {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.value.as_slice()
}
}
impl From<Vec<u8>> for Constant {
fn from(b: Vec<u8>) -> Constant {
Self::Bytes(b)
fn from(value: Vec<u8>) -> Constant {
Self::Bytes(BytesConstant {
value,
implicit_concatenated: false,
})
}
}
impl From<String> for Constant {
fn from(value: String) -> Constant {
Self::Str(StringConstant {
value,
implicit_concatenated: false,
})
}
}
impl From<bool> for Constant {
fn from(b: bool) -> Constant {
Self::Bool(b)
}
}
impl From<BigInt> for Constant {
fn from(i: BigInt) -> Constant {
Self::Int(i)
fn from(value: bool) -> Constant {
Self::Bool(value)
}
}
@ -3056,7 +3088,7 @@ mod size_assertions {
assert_eq_size!(StmtClassDef, [u8; 104]);
assert_eq_size!(StmtTry, [u8; 104]);
assert_eq_size!(Expr, [u8; 80]);
assert_eq_size!(Constant, [u8; 32]);
assert_eq_size!(Constant, [u8; 40]);
assert_eq_size!(Pattern, [u8; 96]);
assert_eq_size!(Mod, [u8; 32]);
}