Improve API exposed on ExprStringLiteral nodes (#16192)

## Summary

This PR makes the following changes:
- It adjusts various callsites to use the new
`ast::StringLiteral::contents_range()` method that was introduced in
https://github.com/astral-sh/ruff/pull/16183. This is less verbose and
more type-safe than using the `ast::str::raw_contents()` helper
function.
- It adds a new `ast::ExprStringLiteral::as_unconcatenated_literal()`
helper method, and adjusts various callsites to use it. This addresses
@MichaReiser's review comment at
https://github.com/astral-sh/ruff/pull/16183#discussion_r1957334365.
There is no functional change here, but it helps readability to make it
clearer that we're differentiating between implicitly concatenated
strings and unconcatenated strings at various points.
- It renames the `StringLiteralValue::flags()` method to
`StringLiteralFlags::first_literal_flags()`. If you're dealing with an
implicitly concatenated string `string_node`,
`string_node.value.flags().closer_len()` could give an incorrect result;
this renaming makes it clearer that the `StringLiteralFlags` instance
returned by the method is only guaranteed to give accurate information
for the first `StringLiteral` contained in the `ExprStringLiteral` node.
- It deletes the unused `BytesLiteralValue::flags()` method. This seems
prone to misuse in the same way as `StringLiteralValue::flags()`: if
it's an implicitly concatenated bytestring, the `BytesLiteralFlags`
instance returned by the method would only give accurate information for
the first `BytesLiteral` in the bytestring.

## Test Plan

`cargo test`
This commit is contained in:
Alex Waygood 2025-02-17 07:58:54 +00:00 committed by GitHub
parent 21999b3be7
commit b6b1947010
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 37 additions and 43 deletions

View file

@ -1,7 +1,6 @@
//! This module takes care of parsing a type annotation.
use ruff_python_ast::relocate::relocate_expr;
use ruff_python_ast::str::raw_contents;
use ruff_python_ast::{Expr, ExprStringLiteral, ModExpression, StringLiteral};
use ruff_text_size::Ranged;
@ -57,14 +56,10 @@ pub fn parse_type_annotation(
string_expr: &ExprStringLiteral,
source: &str,
) -> AnnotationParseResult {
let expr_text = &source[string_expr.range()];
if let [string_literal] = string_expr.value.as_slice() {
if let Some(string_literal) = string_expr.as_unconcatenated_literal() {
// Compare the raw contents (without quotes) of the expression with the parsed contents
// contained in the string literal.
if raw_contents(expr_text)
.is_some_and(|raw_contents| raw_contents == string_literal.as_str())
{
if &source[string_literal.content_range()] == string_literal.as_str() {
parse_simple_type_annotation(string_literal, source)
} else {
// The raw contents of the string doesn't match the parsed content. This could be the