[red-knot] Silence unresolved-attribute in unreachable code (#17305)

## Summary

Basically just repeat the same thing that we did for
`unresolved-reference`, but now for attribute expressions.

We now also handle the case where the unresolved attribute (or the
unresolved reference) diagnostic originates from a stringified type
annotation.

And I made the evaluation of reachability constraints lazy (will only be
evaluated right before we are about to emit a diagnostic).

## Test Plan

New Markdown tests for stringified annotations.
This commit is contained in:
David Peter 2025-04-10 17:15:47 +02:00 committed by GitHub
parent ec74f2d522
commit 5b6e94981d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 191 additions and 92 deletions

View file

@ -11,7 +11,7 @@ use salsa::Update;
use crate::module_name::ModuleName;
use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey;
use crate::semantic_index::ast_ids::{AstIds, ScopedUseId};
use crate::semantic_index::ast_ids::{AstIds, ScopedExpressionId};
use crate::semantic_index::attribute_assignment::AttributeAssignments;
use crate::semantic_index::builder::SemanticIndexBuilder;
use crate::semantic_index::definition::{Definition, DefinitionNodeKey, Definitions};
@ -254,8 +254,8 @@ impl<'db> SemanticIndex<'db> {
})
}
/// Returns true if a given 'use' of a symbol is reachable from the start of the scope.
/// For example, in the following code, use `2` is reachable, but `1` and `3` are not:
/// Returns true if a given expression is reachable from the start of the scope. For example,
/// in the following code, expression `2` is reachable, but expressions `1` and `3` are not:
/// ```py
/// def f():
/// x = 1
@ -265,16 +265,16 @@ impl<'db> SemanticIndex<'db> {
/// return
/// x # 3
/// ```
pub(crate) fn is_symbol_use_reachable(
pub(crate) fn is_expression_reachable(
&self,
db: &'db dyn crate::Db,
scope_id: FileScopeId,
use_id: ScopedUseId,
expression_id: ScopedExpressionId,
) -> bool {
self.is_scope_reachable(db, scope_id)
&& self
.use_def_map(scope_id)
.is_symbol_use_reachable(db, use_id)
.is_expression_reachable(db, expression_id)
}
/// Returns an iterator over the descendent scopes of `scope`.