mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 20:10:09 +00:00
Preserve scopes when checking deferred strings (#758)
This commit is contained in:
parent
374d57d822
commit
ff0e5f5cb4
4 changed files with 52 additions and 8 deletions
14
resources/test/fixtures/F821_5.py
vendored
Normal file
14
resources/test/fixtures/F821_5.py
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
"""Test: inner class annotation."""
|
||||||
|
|
||||||
|
|
||||||
|
class RandomClass:
|
||||||
|
def random_func(self) -> "InnerClass":
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class OuterClass:
|
||||||
|
class InnerClass:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def failing_func(self) -> "InnerClass":
|
||||||
|
return self.InnerClass()
|
|
@ -65,7 +65,7 @@ pub struct Checker<'a> {
|
||||||
scopes: Vec<Scope<'a>>,
|
scopes: Vec<Scope<'a>>,
|
||||||
scope_stack: Vec<usize>,
|
scope_stack: Vec<usize>,
|
||||||
dead_scopes: Vec<usize>,
|
dead_scopes: Vec<usize>,
|
||||||
deferred_string_annotations: Vec<(Range, &'a str)>,
|
deferred_string_annotations: Vec<(Range, &'a str, Vec<usize>, Vec<usize>)>,
|
||||||
deferred_annotations: Vec<(&'a Expr, Vec<usize>, Vec<usize>)>,
|
deferred_annotations: Vec<(&'a Expr, Vec<usize>, Vec<usize>)>,
|
||||||
deferred_functions: Vec<(&'a Stmt, Vec<usize>, Vec<usize>, VisibleScope)>,
|
deferred_functions: Vec<(&'a Stmt, Vec<usize>, Vec<usize>, VisibleScope)>,
|
||||||
deferred_lambdas: Vec<(&'a Expr, Vec<usize>, Vec<usize>)>,
|
deferred_lambdas: Vec<(&'a Expr, Vec<usize>, Vec<usize>)>,
|
||||||
|
@ -1042,8 +1042,12 @@ where
|
||||||
..
|
..
|
||||||
} = &expr.node
|
} = &expr.node
|
||||||
{
|
{
|
||||||
self.deferred_string_annotations
|
self.deferred_string_annotations.push((
|
||||||
.push((Range::from_located(expr), value));
|
Range::from_located(expr),
|
||||||
|
value,
|
||||||
|
self.scope_stack.clone(),
|
||||||
|
self.parent_stack.clone(),
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
self.deferred_annotations.push((
|
self.deferred_annotations.push((
|
||||||
expr,
|
expr,
|
||||||
|
@ -1569,8 +1573,12 @@ where
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if self.in_annotation && !self.in_literal {
|
if self.in_annotation && !self.in_literal {
|
||||||
self.deferred_string_annotations
|
self.deferred_string_annotations.push((
|
||||||
.push((Range::from_located(expr), value));
|
Range::from_located(expr),
|
||||||
|
value,
|
||||||
|
self.scope_stack.clone(),
|
||||||
|
self.parent_stack.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if self.settings.enabled.contains(&CheckCode::S104) {
|
if self.settings.enabled.contains(&CheckCode::S104) {
|
||||||
if let Some(check) = flake8_bandit::plugins::hardcoded_bind_all_interfaces(
|
if let Some(check) = flake8_bandit::plugins::hardcoded_bind_all_interfaces(
|
||||||
|
@ -2128,6 +2136,7 @@ impl<'a> Checker<'a> {
|
||||||
let mut import_starred = false;
|
let mut import_starred = false;
|
||||||
for scope_index in self.scope_stack.iter().rev() {
|
for scope_index in self.scope_stack.iter().rev() {
|
||||||
let scope = &mut self.scopes[*scope_index];
|
let scope = &mut self.scopes[*scope_index];
|
||||||
|
|
||||||
if matches!(scope.kind, ScopeKind::Class(_)) {
|
if matches!(scope.kind, ScopeKind::Class(_)) {
|
||||||
if id == "__class__" {
|
if id == "__class__" {
|
||||||
return;
|
return;
|
||||||
|
@ -2344,8 +2353,8 @@ impl<'a> Checker<'a> {
|
||||||
|
|
||||||
fn check_deferred_annotations(&mut self) {
|
fn check_deferred_annotations(&mut self) {
|
||||||
while let Some((expr, scopes, parents)) = self.deferred_annotations.pop() {
|
while let Some((expr, scopes, parents)) = self.deferred_annotations.pop() {
|
||||||
self.parent_stack = parents;
|
|
||||||
self.scope_stack = scopes;
|
self.scope_stack = scopes;
|
||||||
|
self.parent_stack = parents;
|
||||||
self.visit_expr(expr);
|
self.visit_expr(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2354,10 +2363,14 @@ impl<'a> Checker<'a> {
|
||||||
where
|
where
|
||||||
'b: 'a,
|
'b: 'a,
|
||||||
{
|
{
|
||||||
while let Some((range, expression)) = self.deferred_string_annotations.pop() {
|
let mut stacks = vec![];
|
||||||
|
while let Some((range, expression, scopes, parents)) =
|
||||||
|
self.deferred_string_annotations.pop()
|
||||||
|
{
|
||||||
if let Ok(mut expr) = parser::parse_expression(expression, "<filename>") {
|
if let Ok(mut expr) = parser::parse_expression(expression, "<filename>") {
|
||||||
relocate_expr(&mut expr, range);
|
relocate_expr(&mut expr, range);
|
||||||
allocator.push(expr);
|
allocator.push(expr);
|
||||||
|
stacks.push((scopes, parents));
|
||||||
} else {
|
} else {
|
||||||
if self.settings.enabled.contains(&CheckCode::F722) {
|
if self.settings.enabled.contains(&CheckCode::F722) {
|
||||||
self.add_check(Check::new(
|
self.add_check(Check::new(
|
||||||
|
@ -2367,7 +2380,9 @@ impl<'a> Checker<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for expr in allocator {
|
for (expr, (scopes, parents)) in allocator.iter().zip(stacks) {
|
||||||
|
self.scope_stack = scopes;
|
||||||
|
self.parent_stack = parents;
|
||||||
self.visit_expr(expr);
|
self.visit_expr(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -456,6 +456,7 @@ mod tests {
|
||||||
#[test_case(CheckCode::F821, Path::new("F821_2.py"); "F821_2")]
|
#[test_case(CheckCode::F821, Path::new("F821_2.py"); "F821_2")]
|
||||||
#[test_case(CheckCode::F821, Path::new("F821_3.py"); "F821_3")]
|
#[test_case(CheckCode::F821, Path::new("F821_3.py"); "F821_3")]
|
||||||
#[test_case(CheckCode::F821, Path::new("F821_4.py"); "F821_4")]
|
#[test_case(CheckCode::F821, Path::new("F821_4.py"); "F821_4")]
|
||||||
|
#[test_case(CheckCode::F821, Path::new("F821_5.py"); "F821_5")]
|
||||||
#[test_case(CheckCode::F822, Path::new("F822.py"); "F822")]
|
#[test_case(CheckCode::F822, Path::new("F822.py"); "F822")]
|
||||||
#[test_case(CheckCode::F823, Path::new("F823.py"); "F823")]
|
#[test_case(CheckCode::F823, Path::new("F823.py"); "F823")]
|
||||||
#[test_case(CheckCode::F831, Path::new("F831.py"); "F831")]
|
#[test_case(CheckCode::F831, Path::new("F831.py"); "F831")]
|
||||||
|
|
14
src/snapshots/ruff__linter__tests__F821_F821_5.py.snap
Normal file
14
src/snapshots/ruff__linter__tests__F821_F821_5.py.snap
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
source: src/linter.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
UndefinedName: InnerClass
|
||||||
|
location:
|
||||||
|
row: 5
|
||||||
|
column: 29
|
||||||
|
end_location:
|
||||||
|
row: 5
|
||||||
|
column: 41
|
||||||
|
fix: ~
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue