Introduce HasTy trait and SemanticModel facade (#11963)

This commit is contained in:
Micha Reiser 2024-07-01 14:48:27 +02:00 committed by GitHub
parent 3f25561511
commit 37f260b5af
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 569 additions and 318 deletions

View file

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