mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 04:45:01 +00:00
[red-knot] detect unreachable attribute assignments (#16852)
## Summary This PR closes #15967. Attribute assignments that are statically known to be unreachable are excluded from consideration for implicit instance attribute type inference. If none of the assignments are found to be reachable, an `unresolved-attribute` error is reported. ## Test Plan [A test case](https://github.com/astral-sh/ruff/blob/main/crates/red_knot_python_semantic/resources/mdtest/attributes.md#attributes-defined-in-statically-known-to-be-false-branches) marked as TODO now work as intended, and new test cases have been added. --------- Co-authored-by: David Peter <mail@david-peter.de>
This commit is contained in:
parent
3aa3ee8b89
commit
dfd8eaeb32
10 changed files with 764 additions and 328 deletions
|
@ -13,7 +13,6 @@ use crate::module_name::ModuleName;
|
|||
use crate::node_key::NodeKey;
|
||||
use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey;
|
||||
use crate::semantic_index::ast_ids::AstIds;
|
||||
use crate::semantic_index::attribute_assignment::AttributeAssignments;
|
||||
use crate::semantic_index::builder::SemanticIndexBuilder;
|
||||
use crate::semantic_index::definition::{Definition, DefinitionNodeKey, Definitions};
|
||||
use crate::semantic_index::expression::Expression;
|
||||
|
@ -24,7 +23,6 @@ use crate::semantic_index::use_def::{EagerBindingsKey, ScopedEagerBindingsId, Us
|
|||
use crate::Db;
|
||||
|
||||
pub mod ast_ids;
|
||||
pub mod attribute_assignment;
|
||||
mod builder;
|
||||
pub mod definition;
|
||||
pub mod expression;
|
||||
|
@ -98,23 +96,25 @@ pub(crate) fn use_def_map<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> Arc<UseD
|
|||
index.use_def_map(scope.file_scope_id(db))
|
||||
}
|
||||
|
||||
/// Returns all attribute assignments for a specific class body scope.
|
||||
///
|
||||
/// Using [`attribute_assignments`] over [`semantic_index`] has the advantage that
|
||||
/// Salsa can avoid invalidating dependent queries if this scope's instance attributes
|
||||
/// are unchanged.
|
||||
#[salsa::tracked]
|
||||
pub(crate) fn attribute_assignments<'db>(
|
||||
/// Returns all attribute assignments (and their method scope IDs) for a specific class body scope.
|
||||
/// Only call this when doing type inference on the same file as `class_body_scope`, otherwise it
|
||||
/// introduces a direct dependency on that file's AST.
|
||||
pub(crate) fn attribute_assignments<'db, 's>(
|
||||
db: &'db dyn Db,
|
||||
class_body_scope: ScopeId<'db>,
|
||||
) -> Option<Arc<AttributeAssignments<'db>>> {
|
||||
name: &'s str,
|
||||
) -> impl Iterator<Item = (BindingWithConstraintsIterator<'db, 'db>, FileScopeId)> + use<'s, 'db> {
|
||||
let file = class_body_scope.file(db);
|
||||
let index = semantic_index(db, file);
|
||||
let class_scope_id = class_body_scope.file_scope_id(db);
|
||||
|
||||
index
|
||||
.attribute_assignments
|
||||
.get(&class_body_scope.file_scope_id(db))
|
||||
.cloned()
|
||||
ChildrenIter::new(index, class_scope_id).filter_map(|(file_scope_id, maybe_method)| {
|
||||
maybe_method.node().as_function()?;
|
||||
let attribute_table = index.instance_attribute_table(file_scope_id);
|
||||
let symbol = attribute_table.symbol_id_by_name(name)?;
|
||||
let use_def = &index.use_def_maps[file_scope_id];
|
||||
Some((use_def.instance_attribute_bindings(symbol), file_scope_id))
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the module global scope of `file`.
|
||||
|
@ -137,6 +137,9 @@ pub(crate) struct SemanticIndex<'db> {
|
|||
/// List of all symbol tables in this file, indexed by scope.
|
||||
symbol_tables: IndexVec<FileScopeId, Arc<SymbolTable>>,
|
||||
|
||||
/// List of all instance attribute tables in this file, indexed by scope.
|
||||
instance_attribute_tables: IndexVec<FileScopeId, SymbolTable>,
|
||||
|
||||
/// List of all scopes in this file.
|
||||
scopes: IndexVec<FileScopeId, Scope>,
|
||||
|
||||
|
@ -170,10 +173,6 @@ pub(crate) struct SemanticIndex<'db> {
|
|||
/// Flags about the global scope (code usage impacting inference)
|
||||
has_future_annotations: bool,
|
||||
|
||||
/// Maps from class body scopes to attribute assignments that were found
|
||||
/// in methods of that class.
|
||||
attribute_assignments: FxHashMap<FileScopeId, Arc<AttributeAssignments<'db>>>,
|
||||
|
||||
/// Map of all of the eager bindings that appear in this file.
|
||||
eager_bindings: FxHashMap<EagerBindingsKey, ScopedEagerBindingsId>,
|
||||
}
|
||||
|
@ -188,6 +187,10 @@ impl<'db> SemanticIndex<'db> {
|
|||
self.symbol_tables[scope_id].clone()
|
||||
}
|
||||
|
||||
pub(super) fn instance_attribute_table(&self, scope_id: FileScopeId) -> &SymbolTable {
|
||||
&self.instance_attribute_tables[scope_id]
|
||||
}
|
||||
|
||||
/// Returns the use-def map for a specific scope.
|
||||
///
|
||||
/// Use the Salsa cached [`use_def_map()`] query if you only need the
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue