[ruff] Allow dataclass attribute value instantiation from nested frozen dataclass (RUF009) (#20352)

## Summary
Resolves #20266

Definition of the frozen dataclass attribute can be instantiation of a
nested frozen dataclass as well as a non-nested one.

### Problem explanation
The `function_call_in_dataclass_default` function is invoked during the
"defined scope" stage, after all scopes have been processed. At this
point, the semantic references the top-level scope. When
`SemanticModel::lookup_attribute` executes, it searches for bindings in
the top-level module scope rather than the class scope, resulting in an
error.

To solve this issue, the lookup should be evaluated through the class
scope.

## Test Plan
- Added test case from issue

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
This commit is contained in:
Igor Drokin 2025-09-12 23:46:49 +03:00 committed by GitHub
parent 151ba49b36
commit c7f6b85fb3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 42 additions and 17 deletions

View file

@ -859,11 +859,17 @@ impl<'a> SemanticModel<'a> {
/// associated with `Class`, then the `BindingKind::FunctionDefinition` associated with
/// `Class.method`.
pub fn lookup_attribute(&self, value: &Expr) -> Option<BindingId> {
self.lookup_attribute_in_scope(value, self.scope_id)
}
/// Lookup a qualified attribute in the certain scope.
pub fn lookup_attribute_in_scope(&self, value: &Expr, scope_id: ScopeId) -> Option<BindingId> {
let unqualified_name = UnqualifiedName::from_expr(value)?;
// Find the symbol in the current scope.
let (symbol, attribute) = unqualified_name.segments().split_first()?;
let mut binding_id = self.lookup_symbol(symbol)?;
let mut binding_id =
self.lookup_symbol_in_scope(symbol, scope_id, self.in_forward_reference())?;
// Recursively resolve class attributes, e.g., `foo.bar.baz` in.
let mut tail = attribute;