diff --git a/crates/red_knot/src/ast_ids.rs b/crates/red_knot/src/ast_ids.rs index 7d1c8dff4e827..e456c8c95af86 100644 --- a/crates/red_knot/src/ast_ids.rs +++ b/crates/red_knot/src/ast_ids.rs @@ -1,4 +1,6 @@ -use std::fmt::Formatter; +use std::any::type_name; +use std::fmt::{Debug, Formatter}; +use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use rustc_hash::FxHashMap; @@ -7,9 +9,10 @@ use ruff_index::{Idx, IndexVec}; use ruff_python_ast::visitor::preorder; use ruff_python_ast::visitor::preorder::{PreorderVisitor, TraversalSignal}; use ruff_python_ast::{ - AnyNodeRef, AstNode, ModModule, NodeKind, Parameter, Stmt, StmtAnnAssign, StmtAssign, - StmtAugAssign, StmtClassDef, StmtFunctionDef, StmtImport, StmtImportFrom, StmtTypeAlias, - TypeParam, TypeParamParamSpec, TypeParamTypeVar, TypeParamTypeVarTuple, + AnyNodeRef, AstNode, ExceptHandler, ExceptHandlerExceptHandler, Expr, MatchCase, ModModule, + NodeKind, Parameter, Stmt, StmtAnnAssign, StmtAssign, StmtAugAssign, StmtClassDef, + StmtFunctionDef, StmtGlobal, StmtImport, StmtImportFrom, StmtNonlocal, StmtTypeAlias, + TypeParam, TypeParamParamSpec, TypeParamTypeVar, TypeParamTypeVarTuple, WithItem, }; use ruff_text_size::{Ranged, TextRange}; @@ -22,7 +25,6 @@ pub struct AstId; /// This is different from [`AstId`] in that it is a combination of ID and the type of the node the ID identifies. /// Typing the ID prevents mixing IDs of different node types and allows to restrict the API to only accept /// nodes for which an ID has been created (not all AST nodes get an ID). -#[derive(Debug, Eq, PartialEq, Hash)] pub struct TypedAstId { erased: AstId, _marker: PhantomData N>, @@ -48,6 +50,28 @@ impl Clone for TypedAstId { } } +impl PartialEq for TypedAstId { + fn eq(&self, other: &Self) -> bool { + self.erased == other.erased + } +} + +impl Eq for TypedAstId {} +impl Hash for TypedAstId { + fn hash(&self, state: &mut H) { + self.erased.hash(state); + } +} + +impl Debug for TypedAstId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("TypedAstId") + .field(&self.erased) + .field(&type_name::()) + .finish() + } +} + pub struct AstIds { ids: IndexVec, reverse: FxHashMap, @@ -194,8 +218,8 @@ impl<'a> PreorderVisitor<'a> for AstIdsVisitor<'a> { Stmt::Assert(_) => {} Stmt::Import(import) => self.create_id(import), Stmt::ImportFrom(import_from) => self.create_id(import_from), - Stmt::Global(_) => {} - Stmt::Nonlocal(_) => {} + Stmt::Global(global) => self.create_id(global), + Stmt::Nonlocal(non_local) => self.create_id(non_local), Stmt::Pass(_) => {} Stmt::Break(_) => {} Stmt::Continue(_) => {} @@ -204,6 +228,37 @@ impl<'a> PreorderVisitor<'a> for AstIdsVisitor<'a> { preorder::walk_stmt(self, stmt); } + + fn visit_expr(&mut self, _expr: &'a Expr) {} + + fn visit_parameter(&mut self, parameter: &'a Parameter) { + self.create_id(parameter); + preorder::walk_parameter(self, parameter); + } + + fn visit_except_handler(&mut self, except_handler: &'a ExceptHandler) { + match except_handler { + ExceptHandler::ExceptHandler(except_handler) => { + self.create_id(except_handler); + } + } + + preorder::walk_except_handler(self, except_handler); + } + + fn visit_with_item(&mut self, with_item: &'a WithItem) { + self.create_id(with_item); + preorder::walk_with_item(self, with_item); + } + + fn visit_match_case(&mut self, match_case: &'a MatchCase) { + self.create_id(match_case); + preorder::walk_match_case(self, match_case); + } + + fn visit_type_param(&mut self, type_param: &'a TypeParam) { + self.create_id(type_param); + } } enum DeferredNode<'a> { @@ -332,3 +387,9 @@ impl HasAstId for Stmt {} impl HasAstId for TypeParamTypeVar {} impl HasAstId for TypeParamTypeVarTuple {} impl HasAstId for TypeParamParamSpec {} +impl HasAstId for StmtGlobal {} +impl HasAstId for StmtNonlocal {} + +impl HasAstId for ExceptHandlerExceptHandler {} +impl HasAstId for WithItem {} +impl HasAstId for MatchCase {} diff --git a/crates/red_knot/src/db.rs b/crates/red_knot/src/db.rs index 000ec3b8a40a3..616d3966a47a2 100644 --- a/crates/red_knot/src/db.rs +++ b/crates/red_knot/src/db.rs @@ -15,6 +15,7 @@ use ruff_text_size::{Ranged, TextRange}; use crate::ast_ids::AstIds; use crate::files::{FileId, Files}; +use crate::hir::definition::Definitions; // TODO salsa recommends to have one jar per crate and call it `Jar`. We're not doing this here // because I don't want that many crates just yet. @@ -63,6 +64,7 @@ pub struct SourceJar( check_syntax, check_physical_lines, ast_ids, + definitions, ); pub trait Db: salsa::DbWithJar { @@ -300,3 +302,12 @@ pub fn ast_ids(db: &dyn Db, source: SourceText) -> Arc { Arc::new(AstIds::from_module(ast)) } + +#[salsa::tracked(jar=SourceJar)] +pub fn definitions(db: &dyn Db, source_text: SourceText) -> Arc { + let parsed = parse(db, source_text); + let ast = parsed.ast(db); + let ids = ast_ids(db, source_text); + + Arc::new(Definitions::from_module(ast, &*ids, source_text.file(db))) +} diff --git a/crates/red_knot/src/hir.rs b/crates/red_knot/src/hir.rs index 009d83e1029a4..4461d20ee8c07 100644 --- a/crates/red_knot/src/hir.rs +++ b/crates/red_knot/src/hir.rs @@ -13,19 +13,13 @@ //! //! -use std::ops::{Index, Range}; - use crate::ast_ids::{HasAstId, TypedAstId}; -use ruff_index::{newtype_index, IndexVec}; -use ruff_python_ast::{ - Stmt, StmtAnnAssign, StmtAssign, StmtClassDef, StmtFunctionDef, StmtTypeAlias, TypeParam, - TypeParamParamSpec, TypeParamTypeVar, TypeParamTypeVarTuple, -}; - use crate::files::FileId; -use crate::Name; +use std::fmt::Formatter; +use std::hash::{Hash, Hasher}; + +pub mod definition; -#[derive(Eq, PartialEq, Hash, Debug)] pub struct HirAstId { file_id: FileId, node_id: TypedAstId, @@ -38,216 +32,38 @@ impl Clone for HirAstId { } } -impl HirAstId { - pub fn upcast(self) -> HirAstId - where - N: Into, - { - HirAstId { - file_id: self.file_id, - node_id: self.node_id.upcast(), - } +impl PartialEq for HirAstId { + fn eq(&self, other: &Self) -> bool { + self.file_id == other.file_id && self.node_id == other.node_id } } -#[newtype_index] -pub struct FunctionId; - -#[derive(Debug, Clone)] -pub struct Function { - ast_id: HirAstId, - name: Name, - parameters: Range, - // TODO: type_parameters, return expression, decorators -} - -#[newtype_index] -pub struct ParameterId; - -#[derive(Debug, Clone)] -pub struct Parameter { - kind: ParameterKind, - name: Name, - default: Option<()>, // TODO use expression HIR - ast_id: HirAstId, -} - -// TODO or should `Parameter` be an enum? -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub enum ParameterKind { - PositionalOnly, - Arguments, - Vararg, - KeywordOnly, - Kwarg, -} - -#[newtype_index] -pub struct ClassId; +impl Eq for HirAstId {} -#[derive(Debug, Clone)] -pub struct Class { - name: Name, - ast_id: HirAstId, - // TODO type parameters, inheritance, decorators, members -} - -#[newtype_index] -pub struct AssignmentId; - -// This can have more than one name... -// but that means we can't implement `name()` on `ModuleItem`. - -#[derive(Debug, Clone)] -pub struct Assignment { - // TODO: Handle multiple names / targets - name: Name, - ast_id: HirAstId, -} - -#[derive(Debug, Clone)] -pub struct AnnotatedAssignment { - name: Name, - ast_id: HirAstId, -} - -#[newtype_index] -pub struct AnnotatedAssignmentId; - -#[newtype_index] -pub struct TypeAliasId; - -#[derive(Debug, Clone)] -pub struct TypeAlias { - name: Name, - ast_id: HirAstId, - parameters: Range, -} - -#[newtype_index] -pub struct TypeParameterId; - -#[derive(Debug, Clone)] -pub enum TypeParameter { - TypeVar(TypeParameterTypeVar), - ParamSpec(TypeParameterParamSpec), - TypeVarTuple(TypeParameterTypeVarTuple), -} - -impl TypeParameter { - pub fn ast_id(&self) -> HirAstId { - match self { - TypeParameter::TypeVar(type_var) => type_var.ast_id.upcast(), - TypeParameter::ParamSpec(param_spec) => param_spec.ast_id.upcast(), - TypeParameter::TypeVarTuple(type_var_tuple) => type_var_tuple.ast_id.upcast(), - } +impl std::fmt::Debug for HirAstId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("HirAstId") + .field("file_id", &self.file_id) + .field("node_id", &self.node_id) + .finish() } } -#[derive(Debug, Clone)] -pub struct TypeParameterTypeVar { - name: Name, - ast_id: HirAstId, -} - -#[derive(Debug, Clone)] -pub struct TypeParameterParamSpec { - name: Name, - ast_id: HirAstId, -} - -#[derive(Debug, Clone)] -pub struct TypeParameterTypeVarTuple { - name: Name, - ast_id: HirAstId, -} -// TODO We probably need to track more but I'm not sure. It kind of depends on how these nodes are used -// downstream. For example, do we need to track augmented assignments? We probably should because they can change the -// public interface by re-assigning. How about expression statements? Or do we even have to track all possible top-level statements? -// The advantage of the current approach (by storing functions separate) is that function definitions -// have very stable ids. A function id only changes if a function is added or removed, but it remains -// unaffected if e.g. a class is added or removed. -// -// TODO how to handle imports? -pub enum DefinitionId { - Function(FunctionId), - Class(ClassId), - Assignment(AssignmentId), - AnnotatedAssignment(AnnotatedAssignmentId), -} - -pub enum ModuleItem { - Function(Function), - Class(Class), - Assignment(Assignment), - AnnotatedAssignment(AnnotatedAssignment), -} - -impl ModuleItem { - pub fn ast_id(&self) -> HirAstId { - match self { - ModuleItem::Function(function) => function.ast_id.upcast(), - ModuleItem::Class(class) => class.ast_id.upcast(), - ModuleItem::Assignment(assignment) => assignment.ast_id.upcast(), - ModuleItem::AnnotatedAssignment(annotation) => annotation.ast_id.upcast(), - } - } - - pub fn name(&self) -> Option<&Name> { - match self { - ModuleItem::Function(function) => Some(&function.name), - ModuleItem::Class(class) => Some(&class.name), - ModuleItem::Assignment(assignment) => Some(&assignment.name), - ModuleItem::AnnotatedAssignment(annotation) => Some(&annotation.name), - } - } -} - -#[derive(Debug, Clone)] -pub struct Module { - functions: IndexVec, - classes: IndexVec, - assignments: IndexVec, - annotated_assignments: IndexVec, - type_aliases: IndexVec, -} - -impl Index for Module { - type Output = Function; - - fn index(&self, index: FunctionId) -> &Self::Output { - &self.functions[index] - } -} - -impl Index for Module { - type Output = Class; - - fn index(&self, index: ClassId) -> &Self::Output { - &self.classes[index] - } -} - -impl Index for Module { - type Output = Assignment; - - fn index(&self, index: AssignmentId) -> &Self::Output { - &self.assignments[index] +impl Hash for HirAstId { + fn hash(&self, state: &mut H) { + self.file_id.hash(state); + self.node_id.hash(state); } } -impl Index for Module { - type Output = AnnotatedAssignment; - - fn index(&self, index: AnnotatedAssignmentId) -> &Self::Output { - &self.annotated_assignments[index] - } -} - -impl Index for Module { - type Output = TypeAlias; - - fn index(&self, index: TypeAliasId) -> &Self::Output { - &self.type_aliases[index] +impl HirAstId { + pub fn upcast(self) -> HirAstId + where + N: Into, + { + HirAstId { + file_id: self.file_id, + node_id: self.node_id.upcast(), + } } } diff --git a/crates/red_knot/src/hir/definition.rs b/crates/red_knot/src/hir/definition.rs new file mode 100644 index 0000000000000..548783db4e0de --- /dev/null +++ b/crates/red_knot/src/hir/definition.rs @@ -0,0 +1,560 @@ +use std::ops::{Index, Range}; + +use ruff_index::{newtype_index, IndexVec}; +use ruff_python_ast::visitor::preorder; +use ruff_python_ast::visitor::preorder::PreorderVisitor; +use ruff_python_ast::{ + Decorator, ExceptHandler, ExceptHandlerExceptHandler, Expr, MatchCase, ModModule, Stmt, + StmtAnnAssign, StmtAssign, StmtClassDef, StmtFunctionDef, StmtGlobal, StmtImport, + StmtImportFrom, StmtNonlocal, StmtTypeAlias, TypeParam, TypeParamParamSpec, TypeParamTypeVar, + TypeParamTypeVarTuple, WithItem, +}; + +use crate::ast_ids::{AstIds, HasAstId}; +use crate::files::FileId; +use crate::hir::HirAstId; +use crate::Name; + +#[newtype_index] +pub struct FunctionId; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Function { + ast_id: HirAstId, + name: Name, + parameters: Range, + type_parameters: Range, // TODO: type_parameters, return expression, decorators +} + +#[newtype_index] +pub struct ParameterId; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Parameter { + kind: ParameterKind, + name: Name, + default: Option<()>, // TODO use expression HIR + ast_id: HirAstId, +} + +// TODO or should `Parameter` be an enum? +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum ParameterKind { + PositionalOnly, + Arguments, + Vararg, + KeywordOnly, + Kwarg, +} + +#[newtype_index] +pub struct ClassId; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Class { + name: Name, + ast_id: HirAstId, + // TODO type parameters, inheritance, decorators, members +} + +#[newtype_index] +pub struct AssignmentId; + +// This can have more than one name... +// but that means we can't implement `name()` on `ModuleItem`. + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Assignment { + // TODO: Handle multiple names / targets + name: Name, + ast_id: HirAstId, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct AnnotatedAssignment { + name: Name, + ast_id: HirAstId, +} + +#[newtype_index] +pub struct AnnotatedAssignmentId; + +#[newtype_index] +pub struct TypeAliasId; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct TypeAlias { + name: Name, + ast_id: HirAstId, + parameters: Range, +} + +#[newtype_index] +pub struct TypeParameterId; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum TypeParameter { + TypeVar(TypeParameterTypeVar), + ParamSpec(TypeParameterParamSpec), + TypeVarTuple(TypeParameterTypeVarTuple), +} + +impl TypeParameter { + pub fn ast_id(&self) -> HirAstId { + match self { + TypeParameter::TypeVar(type_var) => type_var.ast_id.upcast(), + TypeParameter::ParamSpec(param_spec) => param_spec.ast_id.upcast(), + TypeParameter::TypeVarTuple(type_var_tuple) => type_var_tuple.ast_id.upcast(), + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct TypeParameterTypeVar { + name: Name, + ast_id: HirAstId, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct TypeParameterParamSpec { + name: Name, + ast_id: HirAstId, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct TypeParameterTypeVarTuple { + name: Name, + ast_id: HirAstId, +} + +#[newtype_index] +pub struct GlobalId; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Global { + ast_id: HirAstId, +} + +#[newtype_index] +pub struct NonLocalId; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct NonLocal { + ast_id: HirAstId, +} + +pub enum DefinitionId { + Function(FunctionId), + Parameter(ParameterId), + Class(ClassId), + Assignment(AssignmentId), + AnnotatedAssignment(AnnotatedAssignmentId), + Global(GlobalId), + NonLocal(NonLocalId), + TypeParameter(TypeParameterId), + TypeAlias(TypeAlias), +} + +pub enum DefinitionItem { + Function(Function), + Parameter(Parameter), + Class(Class), + Assignment(Assignment), + AnnotatedAssignment(AnnotatedAssignment), + Global(Global), + NonLocal(NonLocal), + TypeParameter(TypeParameter), + TypeAlias(TypeAlias), +} + +// The closest is rust-analyzers item-tree. It only represents "Items" which make the public interface of a module +// (it excludes any other statement or expressions). rust-analyzer uses it as the main input to the name resolution +// algorithm +// > It is the input to the name resolution algorithm, as well as to the queries defined in `adt.rs`, +// > `data.rs`, and most things in `attr.rs`. +// +// > One important purpose of this layer is to provide an "invalidation barrier" for incremental +// > computations: when typing inside an item body, the `ItemTree` of the modified file is typically +// > unaffected, so we don't have to recompute name resolution results or item data (see `data.rs`). +// +// I haven't fully figured this out but I think that this composes the "public" interface of a module? +// But maybe that's too optimistic. +// +// +#[derive(Debug, Clone, Default, Eq, PartialEq)] +pub struct Definitions { + functions: IndexVec, + parameters: IndexVec, + classes: IndexVec, + assignments: IndexVec, + annotated_assignments: IndexVec, + type_aliases: IndexVec, + type_parameters: IndexVec, + globals: IndexVec, + non_locals: IndexVec, +} + +impl Definitions { + pub fn from_module(module: &ModModule, ast_ids: &AstIds, file_id: FileId) -> Self { + let mut visitor = DefinitionsVisitor { + definitions: Definitions::default(), + ast_ids, + file_id, + }; + + visitor.visit_body(&module.body); + + visitor.definitions + } +} + +impl Index for Definitions { + type Output = Function; + + fn index(&self, index: FunctionId) -> &Self::Output { + &self.functions[index] + } +} + +impl Index for Definitions { + type Output = Parameter; + + fn index(&self, index: ParameterId) -> &Self::Output { + &self.parameters[index] + } +} + +impl Index for Definitions { + type Output = Class; + + fn index(&self, index: ClassId) -> &Self::Output { + &self.classes[index] + } +} + +impl Index for Definitions { + type Output = Assignment; + + fn index(&self, index: AssignmentId) -> &Self::Output { + &self.assignments[index] + } +} + +impl Index for Definitions { + type Output = AnnotatedAssignment; + + fn index(&self, index: AnnotatedAssignmentId) -> &Self::Output { + &self.annotated_assignments[index] + } +} + +impl Index for Definitions { + type Output = TypeAlias; + + fn index(&self, index: TypeAliasId) -> &Self::Output { + &self.type_aliases[index] + } +} + +impl Index for Definitions { + type Output = Global; + + fn index(&self, index: GlobalId) -> &Self::Output { + &self.globals[index] + } +} + +impl Index for Definitions { + type Output = NonLocal; + + fn index(&self, index: NonLocalId) -> &Self::Output { + &self.non_locals[index] + } +} + +impl Index for Definitions { + type Output = TypeParameter; + + fn index(&self, index: TypeParameterId) -> &Self::Output { + &self.type_parameters[index] + } +} + +struct DefinitionsVisitor<'a> { + definitions: Definitions, + ast_ids: &'a AstIds, + file_id: FileId, +} + +impl DefinitionsVisitor<'_> { + fn ast_id(&self, node: &N) -> HirAstId { + HirAstId { + file_id: self.file_id, + node_id: self.ast_ids.ast_id(node), + } + } + + fn lower_function_def(&mut self, function: &StmtFunctionDef) -> FunctionId { + let name = Name::new(&function.name); + + let first_type_parameter_id = self.definitions.type_parameters.next_index(); + let mut last_type_parameter_id = first_type_parameter_id; + + if let Some(type_params) = &function.type_params { + for parameter in &type_params.type_params { + let id = self.lower_type_parameter(parameter); + last_type_parameter_id = id; + } + } + + let parameters = self.lower_parameters(&function.parameters); + + self.definitions.functions.push(Function { + name, + ast_id: self.ast_id(function), + parameters, + type_parameters: first_type_parameter_id..last_type_parameter_id, + }) + } + + fn lower_parameters(&mut self, parameters: &ruff_python_ast::Parameters) -> Range { + let first_parameter_id = self.definitions.parameters.next_index(); + let mut last_parameter_id = first_parameter_id; + + for parameter in ¶meters.posonlyargs { + last_parameter_id = self.definitions.parameters.push(Parameter { + kind: ParameterKind::PositionalOnly, + name: Name::new(¶meter.parameter.name), + default: None, + ast_id: self.ast_id(¶meter.parameter), + }); + } + + if let Some(vararg) = ¶meters.vararg { + last_parameter_id = self.definitions.parameters.push(Parameter { + kind: ParameterKind::Vararg, + name: Name::new(&vararg.name), + default: None, + ast_id: self.ast_id(vararg), + }); + } + + for parameter in ¶meters.kwonlyargs { + last_parameter_id = self.definitions.parameters.push(Parameter { + kind: ParameterKind::KeywordOnly, + name: Name::new(¶meter.parameter.name), + default: None, + ast_id: self.ast_id(¶meter.parameter), + }); + } + + if let Some(kwarg) = ¶meters.kwarg { + last_parameter_id = self.definitions.parameters.push(Parameter { + kind: ParameterKind::KeywordOnly, + name: Name::new(&kwarg.name), + default: None, + ast_id: self.ast_id(kwarg), + }); + } + + first_parameter_id..last_parameter_id + } + + fn lower_class_def(&mut self, class: &StmtClassDef) -> ClassId { + let name = Name::new(&class.name); + + self.definitions.classes.push(Class { + name, + ast_id: self.ast_id(class), + }) + } + + fn lower_assignment(&mut self, assignment: &StmtAssign) { + // FIXME handle multiple names + if let Some(Expr::Name(name)) = assignment.targets.first() { + self.definitions.assignments.push(Assignment { + name: Name::new(&name.id), + ast_id: self.ast_id(assignment), + }); + } + } + + fn lower_annotated_assignment(&mut self, annotated_assignment: &StmtAnnAssign) { + if let Expr::Name(name) = &*annotated_assignment.target { + self.definitions + .annotated_assignments + .push(AnnotatedAssignment { + name: Name::new(&name.id), + ast_id: self.ast_id(annotated_assignment), + }); + } + } + + fn lower_type_alias(&mut self, type_alias: &StmtTypeAlias) { + if let Expr::Name(name) = &*type_alias.name { + let name = Name::new(&name.id); + + let lower_parameters_id = self.definitions.type_parameters.next_index(); + let mut last_parameter_id = lower_parameters_id; + + if let Some(type_params) = &type_alias.type_params { + for type_parameter in &type_params.type_params { + let id = self.lower_type_parameter(type_parameter); + last_parameter_id = id; + } + } + + self.definitions.type_aliases.push(TypeAlias { + name, + ast_id: self.ast_id(type_alias), + parameters: lower_parameters_id..last_parameter_id, + }); + } + } + + fn lower_type_parameter(&mut self, type_parameter: &TypeParam) -> TypeParameterId { + match type_parameter { + TypeParam::TypeVar(type_var) => { + let id = self + .definitions + .type_parameters + .push(TypeParameter::TypeVar(TypeParameterTypeVar { + name: Name::new(&type_var.name), + ast_id: self.ast_id(type_var), + })); + id + } + TypeParam::ParamSpec(param_spec) => { + let id = self + .definitions + .type_parameters + .push(TypeParameter::ParamSpec(TypeParameterParamSpec { + name: Name::new(¶m_spec.name), + ast_id: self.ast_id(param_spec), + })); + id + } + TypeParam::TypeVarTuple(type_var_tuple) => { + let id = self + .definitions + .type_parameters + .push(TypeParameter::TypeVarTuple(TypeParameterTypeVarTuple { + name: Name::new(&type_var_tuple.name), + ast_id: self.ast_id(type_var_tuple), + })); + id + } + } + } + + fn lower_import(&mut self, import: &StmtImport) { + // TODO + } + + fn lower_import_from(&mut self, import_from: &StmtImportFrom) { + // TODO + } + + fn lower_global(&mut self, global: &StmtGlobal) -> GlobalId { + self.definitions.globals.push(Global { + ast_id: self.ast_id(global), + }) + } + + fn lower_non_local(&mut self, non_local: &StmtNonlocal) -> NonLocalId { + self.definitions.non_locals.push(NonLocal { + ast_id: self.ast_id(non_local), + }) + } + + fn lower_except_handler(&mut self, except_handler: &ExceptHandlerExceptHandler) { + // TODO + } + + fn lower_with_item(&mut self, with_item: &WithItem) { + // TODO + } + + fn lower_match_case(&mut self, match_case: &MatchCase) { + // TODO + } +} + +impl PreorderVisitor<'_> for DefinitionsVisitor<'_> { + fn visit_stmt(&mut self, stmt: &Stmt) { + match stmt { + // Definition statements + Stmt::FunctionDef(definition) => { + self.lower_function_def(definition); + self.visit_body(&definition.body); + } + Stmt::ClassDef(definition) => { + self.lower_class_def(definition); + self.visit_body(&definition.body); + } + Stmt::Assign(assignment) => { + self.lower_assignment(assignment); + } + Stmt::AnnAssign(annotated_assignment) => { + self.lower_annotated_assignment(annotated_assignment); + } + Stmt::TypeAlias(type_alias) => { + self.lower_type_alias(type_alias); + } + + Stmt::Import(import) => self.lower_import(import), + Stmt::ImportFrom(import_from) => self.lower_import_from(import_from), + Stmt::Global(global) => { + self.lower_global(global); + } + Stmt::Nonlocal(non_local) => { + self.lower_non_local(non_local); + } + + // Visit the compound statement bodies because they can contain other definitions. + Stmt::For(_) + | Stmt::While(_) + | Stmt::If(_) + | Stmt::With(_) + | Stmt::Match(_) + | Stmt::Try(_) => { + preorder::walk_stmt(self, stmt); + } + + // Skip over simple statements because they can't contain any other definitions. + Stmt::Return(_) + | Stmt::Delete(_) + | Stmt::AugAssign(_) + | Stmt::Raise(_) + | Stmt::Assert(_) + | Stmt::Expr(_) + | Stmt::Pass(_) + | Stmt::Break(_) + | Stmt::Continue(_) + | Stmt::IpyEscapeCommand(_) => { + // No op + } + } + } + + fn visit_expr(&mut self, _: &'_ Expr) {} + + fn visit_decorator(&mut self, _decorator: &'_ Decorator) {} + + fn visit_except_handler(&mut self, except_handler: &'_ ExceptHandler) { + match except_handler { + ExceptHandler::ExceptHandler(except_handler) => { + self.lower_except_handler(except_handler) + } + } + } + + fn visit_with_item(&mut self, with_item: &'_ WithItem) { + self.lower_with_item(&with_item); + } + + fn visit_match_case(&mut self, match_case: &'_ MatchCase) { + self.lower_match_case(&match_case); + self.visit_body(&match_case.body); + } +} diff --git a/crates/red_knot/src/main.rs b/crates/red_knot/src/main.rs index b275c70da696b..73d1b6fa0272e 100644 --- a/crates/red_knot/src/main.rs +++ b/crates/red_knot/src/main.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use rustc_hash::FxHashSet; use red_knot::db::{ - ast_ids, check_physical_lines, check_syntax, dependencies, parse, Database, Db, + ast_ids, check_physical_lines, check_syntax, definitions, dependencies, parse, Database, Db, }; use red_knot::{files, Workspace}; @@ -90,6 +90,10 @@ fn main() -> anyhow::Result<()> { dbg!(key.resolve(ast.into())); } + let definitions = definitions(&db, content); + + dbg!(&definitions); + // This is the HIR // I forgot how rust-analyzer reference from the HIR to the AST. // let item_tree = build_item_tree(&db, parsed.ast(&db)); // construct the item tree from the AST (the item tree is location agnostic)