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.
This commit is contained in:
Dhruv Manilawala
2024-08-13 19:25:49 +05:30
committed by GitHub
parent 82a3e69b8a
commit 899a52390b
3 changed files with 19 additions and 3 deletions

View File

@@ -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

View File

@@ -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 {

View File

@@ -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
|