From 7c895a75ee698565130994199e9f386dbeaa94e8 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 12 Mar 2024 15:57:03 +0000 Subject: [PATCH] F821: Fix false negatives in `.pyi` files when `from __future__ import annotations` is active --- .../test/fixtures/pyflakes/F821_27.py | 13 +++++++++ crates/ruff_linter/src/checkers/ast/mod.rs | 1 + ...les__pyflakes__tests__F821_F821_27.py.snap | 27 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_27.py b/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_27.py index f9004a6dea5af..6928429d4dd5d 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_27.py +++ b/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_27.py @@ -33,3 +33,16 @@ class MyClass: baz: MyClass eggs = baz # Still invalid even when `__future__.annotations` are enabled eggs = "baz" # always okay + +# Forward references: +MaybeDStr: TypeAlias = Optional[DStr] # Still invalid even when `__future__.annotations` are enabled +MaybeDStr2: TypeAlias = Optional["DStr"] # always okay +DStr: TypeAlias = Union[D, str] # Still invalid even when `__future__.annotations` are enabled +DStr2: TypeAlias = Union["D", str] # always okay + +class D: ... + +# More circular references +class Leaf: ... +class Tree(list[Tree | Leaf]): ... # Still invalid even when `__future__.annotations` are enabled +class Tree2(list["Tree | Leaf"]): ... # always okay diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index 85d2f5a5b48ac..11c8b47aa59e6 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -938,6 +938,7 @@ impl<'a> Visitor<'a> for Checker<'a> { && !self.semantic.in_deferred_type_definition() && self.semantic.in_type_definition() && self.semantic.future_annotations() + && (self.semantic.in_typing_only_annotation() || self.source_type.is_stub()) { if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = expr { self.visit.string_type_definitions.push(( diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_27.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_27.py.snap index de22fa9f7baf9..b0ef6067d4274 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_27.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_27.py.snap @@ -17,3 +17,30 @@ F821_27.py:34:8: F821 Undefined name `baz` | ^^^ F821 35 | eggs = "baz" # always okay | + +F821_27.py:38:33: F821 Undefined name `DStr` + | +37 | # Forward references: +38 | MaybeDStr: TypeAlias = Optional[DStr] # Still invalid even when `__future__.annotations` are enabled + | ^^^^ F821 +39 | MaybeDStr2: TypeAlias = Optional["DStr"] # always okay +40 | DStr: TypeAlias = Union[D, str] # Still invalid even when `__future__.annotations` are enabled + | + +F821_27.py:40:25: F821 Undefined name `D` + | +38 | MaybeDStr: TypeAlias = Optional[DStr] # Still invalid even when `__future__.annotations` are enabled +39 | MaybeDStr2: TypeAlias = Optional["DStr"] # always okay +40 | DStr: TypeAlias = Union[D, str] # Still invalid even when `__future__.annotations` are enabled + | ^ F821 +41 | DStr2: TypeAlias = Union["D", str] # always okay + | + +F821_27.py:47:17: F821 Undefined name `Tree` + | +45 | # More circular references +46 | class Leaf: ... +47 | class Tree(list[Tree | Leaf]): ... # Still invalid even when `__future__.annotations` are enabled + | ^^^^ F821 +48 | class Tree2(list["Tree | Leaf"]): ... # always okay + |