From 899a52390ba5e819f4a2cfcce3c696a8ffef89e2 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Tue, 13 Aug 2024 19:25:49 +0530 Subject: [PATCH] Evaluate default parameter value in enclosing scope (#12852) ## Summary This PR fixes a bug in the semantic model where it would evaluate the default parameter value in the type parameter scope. For example, ```py def foo[T1: int](a = T1): pass ``` Here, the `T1` in `a = T1` is undefined but Ruff doesn't flag it (https://play.ruff.rs/ba2f7c2f-4da6-417e-aa2a-104aa63e6d5e). The fix here is to evaluate the default parameter value in the _enclosing_ scope instead. ## Test Plan Add a test case which includes the above code under `F821` (`undefined-name`) and validate the snapshot. --- .../resources/test/fixtures/pyflakes/F821_17.py | 4 ++++ crates/ruff_linter/src/checkers/ast/mod.rs | 11 ++++++++--- ...nter__rules__pyflakes__tests__F821_F821_17.py.snap | 7 +++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_17.py b/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_17.py index b2a7b7b4dd..649f88a672 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_17.py +++ b/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_17.py @@ -111,3 +111,7 @@ def can_access_inside_nested[T](t: T) -> T: # OK return x bar(t) + + +def cannot_access_in_default[T](t: T = T): # F821 + pass diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index 8e8a5f04f4..814f2d9f38 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -691,6 +691,14 @@ impl<'a> Visitor<'a> for Checker<'a> { self.semantic(), ); + // The default values of the parameters needs to be evaluated in the enclosing + // scope. + for parameter in &**parameters { + if let Some(expr) = parameter.default() { + self.visit_expr(expr); + } + } + self.semantic.push_scope(ScopeKind::Type); if let Some(type_params) = type_params { @@ -715,9 +723,6 @@ impl<'a> Visitor<'a> for Checker<'a> { } } } - if let Some(expr) = parameter.default() { - self.visit_expr(expr); - } } if let Some(expr) = returns { match annotation { diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_17.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_17.py.snap index bb12cf83ab..ad2011d46c 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_17.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_17.py.snap @@ -258,3 +258,10 @@ F821_17.py:103:17: F821 Undefined name `t` | ^ F821 104 | return x | + +F821_17.py:116:40: F821 Undefined name `T` + | +116 | def cannot_access_in_default[T](t: T = T): # F821 + | ^ F821 +117 | pass + |