mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 20:10:09 +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
|
# https://github.com/astral-sh/ruff/issues/10546
|
||||||
x: "Literal['foo', 'bar']"
|
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 log::debug;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, all::DunderAllName, Comprehension, ElifElseClause, ExceptHandler, Expr,
|
self as ast, all::DunderAllName, Comprehension, ElifElseClause, ExceptHandler, Expr,
|
||||||
ExprContext, Keyword, MatchCase, Parameter, ParameterWithDefault, Parameters, Pattern, Stmt,
|
ExprContext, FStringElement, Keyword, MatchCase, Parameter, ParameterWithDefault, Parameters,
|
||||||
Suite, UnaryOp,
|
Pattern, Stmt, Suite, UnaryOp,
|
||||||
};
|
};
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
|
@ -1580,6 +1580,15 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
||||||
.push((bound, self.semantic.snapshot()));
|
.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> {
|
impl<'a> Checker<'a> {
|
||||||
|
|
|
@ -449,13 +449,8 @@ pub(crate) fn check_string_quotes(checker: &mut Checker, string_like: StringLike
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the string is part of a f-string, ignore it.
|
// TODO(dhruvmanila): Support checking for escaped quotes in f-strings.
|
||||||
if checker
|
if checker.semantic().in_f_string_replacement_field() {
|
||||||
.indexer()
|
|
||||||
.fstring_ranges()
|
|
||||||
.outermost(string_like.start())
|
|
||||||
.is_some_and(|outer| outer.start() < string_like.start() && string_like.end() < outer.end())
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1525,6 +1525,12 @@ impl<'a> SemanticModel<'a> {
|
||||||
self.flags.intersects(SemanticModelFlags::F_STRING)
|
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.
|
/// Return `true` if the model is in boolean test.
|
||||||
pub const fn in_boolean_test(&self) -> bool {
|
pub const fn in_boolean_test(&self) -> bool {
|
||||||
self.flags.intersects(SemanticModelFlags::BOOLEAN_TEST)
|
self.flags.intersects(SemanticModelFlags::BOOLEAN_TEST)
|
||||||
|
@ -1960,6 +1966,15 @@ bitflags! {
|
||||||
/// ```
|
/// ```
|
||||||
const DUNDER_ALL_DEFINITION = 1 << 22;
|
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.
|
/// The context is in any type annotation.
|
||||||
const ANNOTATION = Self::TYPING_ONLY_ANNOTATION.bits() | Self::RUNTIME_EVALUATED_ANNOTATION.bits() | Self::RUNTIME_REQUIRED_ANNOTATION.bits();
|
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