mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-18 17:41:12 +00:00
Add semantic model flag when inside f-string replacement field (#10766)
## Summary This PR adds a new semantic model flag to indicate that the checker is inside an f-string replacement field. This will be used to ignore certain checks if the target version doesn't support a specific feature like PEP 701. fixes: #10761 ## Test Plan Add a test case from the raised issue.
This commit is contained in:
parent
6b4fa17097
commit
d02b1069b5
4 changed files with 30 additions and 9 deletions
|
@ -5,3 +5,5 @@ this_should_be_linted = f'double {"quote"} string'
|
|||
|
||||
# https://github.com/astral-sh/ruff/issues/10546
|
||||
x: "Literal['foo', 'bar']"
|
||||
# https://github.com/astral-sh/ruff/issues/10761
|
||||
f"Before {f'x {x}' if y else f'foo {z}'} after"
|
||||
|
|
|
@ -32,8 +32,8 @@ use itertools::Itertools;
|
|||
use log::debug;
|
||||
use ruff_python_ast::{
|
||||
self as ast, all::DunderAllName, Comprehension, ElifElseClause, ExceptHandler, Expr,
|
||||
ExprContext, Keyword, MatchCase, Parameter, ParameterWithDefault, Parameters, Pattern, Stmt,
|
||||
Suite, UnaryOp,
|
||||
ExprContext, FStringElement, Keyword, MatchCase, Parameter, ParameterWithDefault, Parameters,
|
||||
Pattern, Stmt, Suite, UnaryOp,
|
||||
};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
|
@ -1580,6 +1580,15 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
|||
.push((bound, self.semantic.snapshot()));
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_f_string_element(&mut self, f_string_element: &'a FStringElement) {
|
||||
let snapshot = self.semantic.flags;
|
||||
if f_string_element.is_expression() {
|
||||
self.semantic.flags |= SemanticModelFlags::F_STRING_REPLACEMENT_FIELD;
|
||||
}
|
||||
visitor::walk_f_string_element(self, f_string_element);
|
||||
self.semantic.flags = snapshot;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Checker<'a> {
|
||||
|
|
|
@ -449,13 +449,8 @@ pub(crate) fn check_string_quotes(checker: &mut Checker, string_like: StringLike
|
|||
return;
|
||||
}
|
||||
|
||||
// If the string is part of a f-string, ignore it.
|
||||
if checker
|
||||
.indexer()
|
||||
.fstring_ranges()
|
||||
.outermost(string_like.start())
|
||||
.is_some_and(|outer| outer.start() < string_like.start() && string_like.end() < outer.end())
|
||||
{
|
||||
// TODO(dhruvmanila): Support checking for escaped quotes in f-strings.
|
||||
if checker.semantic().in_f_string_replacement_field() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1525,6 +1525,12 @@ impl<'a> SemanticModel<'a> {
|
|||
self.flags.intersects(SemanticModelFlags::F_STRING)
|
||||
}
|
||||
|
||||
/// Return `true` if the model is in an f-string replacement field.
|
||||
pub const fn in_f_string_replacement_field(&self) -> bool {
|
||||
self.flags
|
||||
.intersects(SemanticModelFlags::F_STRING_REPLACEMENT_FIELD)
|
||||
}
|
||||
|
||||
/// Return `true` if the model is in boolean test.
|
||||
pub const fn in_boolean_test(&self) -> bool {
|
||||
self.flags.intersects(SemanticModelFlags::BOOLEAN_TEST)
|
||||
|
@ -1960,6 +1966,15 @@ bitflags! {
|
|||
/// ```
|
||||
const DUNDER_ALL_DEFINITION = 1 << 22;
|
||||
|
||||
/// The model is in an f-string replacement field.
|
||||
///
|
||||
/// For example, the model could be visiting `x` or `y` in:
|
||||
///
|
||||
/// ```python
|
||||
/// f"first {x} second {y}"
|
||||
/// ```
|
||||
const F_STRING_REPLACEMENT_FIELD = 1 << 23;
|
||||
|
||||
/// The context is in any type annotation.
|
||||
const ANNOTATION = Self::TYPING_ONLY_ANNOTATION.bits() | Self::RUNTIME_EVALUATED_ANNOTATION.bits() | Self::RUNTIME_REQUIRED_ANNOTATION.bits();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue