mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:37 +00:00
Introduce HasTy
trait and SemanticModel
facade (#11963)
This commit is contained in:
parent
3f25561511
commit
37f260b5af
13 changed files with 569 additions and 318 deletions
|
@ -9,15 +9,12 @@ use ruff_python_ast::name::Name;
|
|||
use ruff_python_ast::visitor::{walk_expr, walk_stmt, Visitor};
|
||||
|
||||
use crate::node_key::NodeKey;
|
||||
use crate::semantic_index::ast_ids::{
|
||||
AstId, AstIdsBuilder, ScopeAssignmentId, ScopeClassId, ScopeFunctionId, ScopeImportFromId,
|
||||
ScopeImportId, ScopeNamedExprId,
|
||||
};
|
||||
use crate::semantic_index::ast_ids::{AstId, AstIdsBuilder, ScopedClassId, ScopedFunctionId};
|
||||
use crate::semantic_index::definition::{Definition, ImportDefinition, ImportFromDefinition};
|
||||
use crate::semantic_index::symbol::{
|
||||
FileScopeId, FileSymbolId, Scope, ScopedSymbolId, SymbolFlags, SymbolTableBuilder,
|
||||
FileScopeId, FileSymbolId, Scope, ScopeKind, ScopedSymbolId, SymbolFlags, SymbolTableBuilder,
|
||||
};
|
||||
use crate::semantic_index::{NodeWithScopeId, SemanticIndex};
|
||||
use crate::semantic_index::{NodeWithScopeId, NodeWithScopeKey, SemanticIndex};
|
||||
|
||||
pub(super) struct SemanticIndexBuilder<'a> {
|
||||
// Builder state
|
||||
|
@ -32,6 +29,7 @@ pub(super) struct SemanticIndexBuilder<'a> {
|
|||
ast_ids: IndexVec<FileScopeId, AstIdsBuilder>,
|
||||
expression_scopes: FxHashMap<NodeKey, FileScopeId>,
|
||||
scope_nodes: IndexVec<FileScopeId, NodeWithScopeId>,
|
||||
node_scopes: FxHashMap<NodeWithScopeKey, FileScopeId>,
|
||||
}
|
||||
|
||||
impl<'a> SemanticIndexBuilder<'a> {
|
||||
|
@ -45,12 +43,16 @@ impl<'a> SemanticIndexBuilder<'a> {
|
|||
symbol_tables: IndexVec::new(),
|
||||
ast_ids: IndexVec::new(),
|
||||
expression_scopes: FxHashMap::default(),
|
||||
node_scopes: FxHashMap::default(),
|
||||
scope_nodes: IndexVec::new(),
|
||||
};
|
||||
|
||||
builder.push_scope_with_parent(
|
||||
NodeWithScopeId::Module,
|
||||
&Name::new_static("<module>"),
|
||||
NodeWithScope::new(
|
||||
parsed.syntax(),
|
||||
NodeWithScopeId::Module,
|
||||
Name::new_static("<module>"),
|
||||
),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
|
@ -68,42 +70,44 @@ impl<'a> SemanticIndexBuilder<'a> {
|
|||
|
||||
fn push_scope(
|
||||
&mut self,
|
||||
node: NodeWithScopeId,
|
||||
name: &Name,
|
||||
node: NodeWithScope,
|
||||
defining_symbol: Option<FileSymbolId>,
|
||||
definition: Option<Definition>,
|
||||
) {
|
||||
let parent = self.current_scope();
|
||||
self.push_scope_with_parent(node, name, defining_symbol, definition, Some(parent));
|
||||
self.push_scope_with_parent(node, defining_symbol, definition, Some(parent));
|
||||
}
|
||||
|
||||
fn push_scope_with_parent(
|
||||
&mut self,
|
||||
node: NodeWithScopeId,
|
||||
name: &Name,
|
||||
node: NodeWithScope,
|
||||
defining_symbol: Option<FileSymbolId>,
|
||||
definition: Option<Definition>,
|
||||
parent: Option<FileScopeId>,
|
||||
) {
|
||||
let children_start = self.scopes.next_index() + 1;
|
||||
let node_key = node.key();
|
||||
let node_id = node.id();
|
||||
let scope_kind = node.scope_kind();
|
||||
|
||||
let scope = Scope {
|
||||
name: name.clone(),
|
||||
name: node.name,
|
||||
parent,
|
||||
defining_symbol,
|
||||
definition,
|
||||
kind: node.scope_kind(),
|
||||
kind: scope_kind,
|
||||
descendents: children_start..children_start,
|
||||
};
|
||||
|
||||
let scope_id = self.scopes.push(scope);
|
||||
self.symbol_tables.push(SymbolTableBuilder::new());
|
||||
let ast_id_scope = self.ast_ids.push(AstIdsBuilder::new());
|
||||
let scope_node_id = self.scope_nodes.push(node);
|
||||
let scope_node_id = self.scope_nodes.push(node_id);
|
||||
|
||||
debug_assert_eq!(ast_id_scope, scope_id);
|
||||
debug_assert_eq!(scope_id, scope_node_id);
|
||||
self.scope_stack.push(scope_id);
|
||||
self.node_scopes.insert(node_key, scope_id);
|
||||
}
|
||||
|
||||
fn pop_scope(&mut self) -> FileScopeId {
|
||||
|
@ -124,10 +128,18 @@ impl<'a> SemanticIndexBuilder<'a> {
|
|||
&mut self.ast_ids[scope_id]
|
||||
}
|
||||
|
||||
fn add_or_update_symbol(&mut self, name: Name, flags: SymbolFlags) -> ScopedSymbolId {
|
||||
let symbol_table = self.current_symbol_table();
|
||||
fn add_or_update_symbol(&mut self, name: Name, flags: SymbolFlags) -> FileSymbolId {
|
||||
for scope in self.scope_stack.iter().rev().skip(1) {
|
||||
let builder = &self.symbol_tables[*scope];
|
||||
|
||||
symbol_table.add_or_update_symbol(name, flags, None)
|
||||
if let Some(symbol) = builder.symbol_by_name(&name) {
|
||||
return FileSymbolId::new(*scope, symbol);
|
||||
}
|
||||
}
|
||||
|
||||
let scope = self.current_scope();
|
||||
let symbol_table = self.current_symbol_table();
|
||||
FileSymbolId::new(scope, symbol_table.add_or_update_symbol(name, flags, None))
|
||||
}
|
||||
|
||||
fn add_or_update_symbol_with_definition(
|
||||
|
@ -142,7 +154,7 @@ impl<'a> SemanticIndexBuilder<'a> {
|
|||
|
||||
fn with_type_params(
|
||||
&mut self,
|
||||
name: &Name,
|
||||
name: Name,
|
||||
with_params: &WithTypeParams,
|
||||
defining_symbol: FileSymbolId,
|
||||
nested: impl FnOnce(&mut Self) -> FileScopeId,
|
||||
|
@ -150,14 +162,13 @@ impl<'a> SemanticIndexBuilder<'a> {
|
|||
let type_params = with_params.type_parameters();
|
||||
|
||||
if let Some(type_params) = type_params {
|
||||
let type_node = match with_params {
|
||||
let type_params_id = match with_params {
|
||||
WithTypeParams::ClassDef { id, .. } => NodeWithScopeId::ClassTypeParams(*id),
|
||||
WithTypeParams::FunctionDef { id, .. } => NodeWithScopeId::FunctionTypeParams(*id),
|
||||
};
|
||||
|
||||
self.push_scope(
|
||||
type_node,
|
||||
name,
|
||||
NodeWithScope::new(type_params, type_params_id, name),
|
||||
Some(defining_symbol),
|
||||
Some(with_params.definition()),
|
||||
);
|
||||
|
@ -211,9 +222,10 @@ impl<'a> SemanticIndexBuilder<'a> {
|
|||
SemanticIndex {
|
||||
symbol_tables,
|
||||
scopes: self.scopes,
|
||||
scope_nodes: self.scope_nodes,
|
||||
nodes_by_scope: self.scope_nodes,
|
||||
scopes_by_node: self.node_scopes,
|
||||
ast_ids,
|
||||
expression_scopes: self.expression_scopes,
|
||||
scopes_by_expression: self.expression_scopes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +245,7 @@ impl Visitor<'_> for SemanticIndexBuilder<'_> {
|
|||
self.visit_decorator(decorator);
|
||||
}
|
||||
let name = &function_def.name.id;
|
||||
let function_id = ScopeFunctionId(statement_id);
|
||||
let function_id = ScopedFunctionId(statement_id);
|
||||
let definition = Definition::FunctionDef(function_id);
|
||||
let scope = self.current_scope();
|
||||
let symbol = FileSymbolId::new(
|
||||
|
@ -242,7 +254,7 @@ impl Visitor<'_> for SemanticIndexBuilder<'_> {
|
|||
);
|
||||
|
||||
self.with_type_params(
|
||||
name,
|
||||
name.clone(),
|
||||
&WithTypeParams::FunctionDef {
|
||||
node: function_def,
|
||||
id: AstId::new(scope, function_id),
|
||||
|
@ -255,8 +267,11 @@ impl Visitor<'_> for SemanticIndexBuilder<'_> {
|
|||
}
|
||||
|
||||
builder.push_scope(
|
||||
NodeWithScopeId::Function(AstId::new(scope, function_id)),
|
||||
name,
|
||||
NodeWithScope::new(
|
||||
function_def,
|
||||
NodeWithScopeId::Function(AstId::new(scope, function_id)),
|
||||
name.clone(),
|
||||
),
|
||||
Some(symbol),
|
||||
Some(definition),
|
||||
);
|
||||
|
@ -271,15 +286,15 @@ impl Visitor<'_> for SemanticIndexBuilder<'_> {
|
|||
}
|
||||
|
||||
let name = &class.name.id;
|
||||
let class_id = ScopeClassId(statement_id);
|
||||
let definition = Definition::from(class_id);
|
||||
let class_id = ScopedClassId(statement_id);
|
||||
let definition = Definition::ClassDef(class_id);
|
||||
let scope = self.current_scope();
|
||||
let id = FileSymbolId::new(
|
||||
self.current_scope(),
|
||||
self.add_or_update_symbol_with_definition(name.clone(), definition),
|
||||
);
|
||||
self.with_type_params(
|
||||
name,
|
||||
name.clone(),
|
||||
&WithTypeParams::ClassDef {
|
||||
node: class,
|
||||
id: AstId::new(scope, class_id),
|
||||
|
@ -291,8 +306,11 @@ impl Visitor<'_> for SemanticIndexBuilder<'_> {
|
|||
}
|
||||
|
||||
builder.push_scope(
|
||||
NodeWithScopeId::Class(AstId::new(scope, class_id)),
|
||||
name,
|
||||
NodeWithScope::new(
|
||||
class,
|
||||
NodeWithScopeId::Class(AstId::new(scope, class_id)),
|
||||
name.clone(),
|
||||
),
|
||||
Some(id),
|
||||
Some(definition),
|
||||
);
|
||||
|
@ -311,7 +329,7 @@ impl Visitor<'_> for SemanticIndexBuilder<'_> {
|
|||
};
|
||||
|
||||
let def = Definition::Import(ImportDefinition {
|
||||
import_id: ScopeImportId(statement_id),
|
||||
import_id: statement_id,
|
||||
alias: u32::try_from(i).unwrap(),
|
||||
});
|
||||
self.add_or_update_symbol_with_definition(symbol_name, def);
|
||||
|
@ -330,7 +348,7 @@ impl Visitor<'_> for SemanticIndexBuilder<'_> {
|
|||
&alias.name.id
|
||||
};
|
||||
let def = Definition::ImportFrom(ImportFromDefinition {
|
||||
import_id: ScopeImportFromId(statement_id),
|
||||
import_id: statement_id,
|
||||
name: u32::try_from(i).unwrap(),
|
||||
});
|
||||
self.add_or_update_symbol_with_definition(symbol_name.clone(), def);
|
||||
|
@ -339,8 +357,7 @@ impl Visitor<'_> for SemanticIndexBuilder<'_> {
|
|||
ast::Stmt::Assign(node) => {
|
||||
debug_assert!(self.current_definition.is_none());
|
||||
self.visit_expr(&node.value);
|
||||
self.current_definition =
|
||||
Some(Definition::Assignment(ScopeAssignmentId(statement_id)));
|
||||
self.current_definition = Some(Definition::Assignment(statement_id));
|
||||
for target in &node.targets {
|
||||
self.visit_expr(target);
|
||||
}
|
||||
|
@ -385,8 +402,7 @@ impl Visitor<'_> for SemanticIndexBuilder<'_> {
|
|||
}
|
||||
ast::Expr::Named(node) => {
|
||||
debug_assert!(self.current_definition.is_none());
|
||||
self.current_definition =
|
||||
Some(Definition::NamedExpr(ScopeNamedExprId(expression_id)));
|
||||
self.current_definition = Some(Definition::NamedExpr(expression_id));
|
||||
// TODO walrus in comprehensions is implicitly nonlocal
|
||||
self.visit_expr(&node.target);
|
||||
self.current_definition = None;
|
||||
|
@ -428,11 +444,11 @@ impl Visitor<'_> for SemanticIndexBuilder<'_> {
|
|||
enum WithTypeParams<'a> {
|
||||
ClassDef {
|
||||
node: &'a ast::StmtClassDef,
|
||||
id: AstId<ScopeClassId>,
|
||||
id: AstId<ScopedClassId>,
|
||||
},
|
||||
FunctionDef {
|
||||
node: &'a ast::StmtFunctionDef,
|
||||
id: AstId<ScopeFunctionId>,
|
||||
id: AstId<ScopedFunctionId>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -451,3 +467,38 @@ impl<'a> WithTypeParams<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NodeWithScope {
|
||||
id: NodeWithScopeId,
|
||||
key: NodeWithScopeKey,
|
||||
name: Name,
|
||||
}
|
||||
|
||||
impl NodeWithScope {
|
||||
fn new(node: impl Into<NodeWithScopeKey>, id: NodeWithScopeId, name: Name) -> Self {
|
||||
Self {
|
||||
id,
|
||||
key: node.into(),
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
fn id(&self) -> NodeWithScopeId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn key(&self) -> NodeWithScopeKey {
|
||||
self.key
|
||||
}
|
||||
|
||||
fn scope_kind(&self) -> ScopeKind {
|
||||
match self.id {
|
||||
NodeWithScopeId::Module => ScopeKind::Module,
|
||||
NodeWithScopeId::Class(_) => ScopeKind::Class,
|
||||
NodeWithScopeId::Function(_) => ScopeKind::Function,
|
||||
NodeWithScopeId::ClassTypeParams(_) | NodeWithScopeId::FunctionTypeParams(_) => {
|
||||
ScopeKind::Annotation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue