diff --git a/crates/red_knot_python_semantic/src/semantic_index/builder.rs b/crates/red_knot_python_semantic/src/semantic_index/builder.rs index fda1f07cbc..3b8d8c5c9a 100644 --- a/crates/red_knot_python_semantic/src/semantic_index/builder.rs +++ b/crates/red_knot_python_semantic/src/semantic_index/builder.rs @@ -589,6 +589,27 @@ where }, ); } + ast::Stmt::TypeAlias(type_alias) => { + let symbol = self.add_symbol( + type_alias + .name + .as_name_expr() + .expect("type alias name is a name expr") + .id + .clone(), + ); + self.add_definition(symbol, type_alias); + + self.with_type_params( + NodeWithScopeRef::TypeAliasTypeParameters(type_alias), + type_alias.type_params.as_ref(), + |builder| { + builder.push_scope(NodeWithScopeRef::TypeAliasTypeParameters(type_alias)); + builder.visit_expr(&type_alias.value); + builder.pop_scope() + }, + ); + } ast::Stmt::Import(node) => { for alias in &node.names { let symbol_name = if let Some(asname) = &alias.asname { diff --git a/crates/red_knot_python_semantic/src/semantic_index/definition.rs b/crates/red_knot_python_semantic/src/semantic_index/definition.rs index 11822e5d75..82f8cf7557 100644 --- a/crates/red_knot_python_semantic/src/semantic_index/definition.rs +++ b/crates/red_knot_python_semantic/src/semantic_index/definition.rs @@ -83,6 +83,7 @@ pub(crate) enum DefinitionNodeRef<'a> { For(ForStmtDefinitionNodeRef<'a>), Function(&'a ast::StmtFunctionDef), Class(&'a ast::StmtClassDef), + TypeAlias(&'a ast::StmtTypeAlias), NamedExpression(&'a ast::ExprNamed), Assignment(AssignmentDefinitionNodeRef<'a>), AnnotatedAssignment(&'a ast::StmtAnnAssign), @@ -109,6 +110,12 @@ impl<'a> From<&'a ast::StmtClassDef> for DefinitionNodeRef<'a> { } } +impl<'a> From<&'a ast::StmtTypeAlias> for DefinitionNodeRef<'a> { + fn from(node: &'a ast::StmtTypeAlias) -> Self { + Self::TypeAlias(node) + } +} + impl<'a> From<&'a ast::ExprNamed> for DefinitionNodeRef<'a> { fn from(node: &'a ast::ExprNamed) -> Self { Self::NamedExpression(node) @@ -265,6 +272,9 @@ impl<'db> DefinitionNodeRef<'db> { DefinitionNodeRef::Class(class) => { DefinitionKind::Class(AstNodeRef::new(parsed, class)) } + DefinitionNodeRef::TypeAlias(type_alias) => { + DefinitionKind::TypeAlias(AstNodeRef::new(parsed, type_alias)) + } DefinitionNodeRef::NamedExpression(named) => { DefinitionKind::NamedExpression(AstNodeRef::new(parsed, named)) } @@ -358,6 +368,7 @@ impl<'db> DefinitionNodeRef<'db> { } Self::Function(node) => node.into(), Self::Class(node) => node.into(), + Self::TypeAlias(node) => node.into(), Self::NamedExpression(node) => node.into(), Self::Assignment(AssignmentDefinitionNodeRef { value: _, @@ -434,6 +445,7 @@ pub enum DefinitionKind<'db> { ImportFrom(ImportFromDefinitionKind), Function(AstNodeRef), Class(AstNodeRef), + TypeAlias(AstNodeRef), NamedExpression(AstNodeRef), Assignment(AssignmentDefinitionKind<'db>), AnnotatedAssignment(AstNodeRef), @@ -456,6 +468,7 @@ impl DefinitionKind<'_> { // functions, classes, and imports always bind, and we consider them declarations DefinitionKind::Function(_) | DefinitionKind::Class(_) + | DefinitionKind::TypeAlias(_) | DefinitionKind::Import(_) | DefinitionKind::ImportFrom(_) | DefinitionKind::TypeVar(_) @@ -682,6 +695,12 @@ impl From<&ast::StmtClassDef> for DefinitionNodeKey { } } +impl From<&ast::StmtTypeAlias> for DefinitionNodeKey { + fn from(node: &ast::StmtTypeAlias) -> Self { + Self(NodeKey::from_node(node)) + } +} + impl From<&ast::ExprName> for DefinitionNodeKey { fn from(node: &ast::ExprName) -> Self { Self(NodeKey::from_node(node)) diff --git a/crates/red_knot_python_semantic/src/semantic_index/symbol.rs b/crates/red_knot_python_semantic/src/semantic_index/symbol.rs index f42099dd7d..5eaa86559a 100644 --- a/crates/red_knot_python_semantic/src/semantic_index/symbol.rs +++ b/crates/red_knot_python_semantic/src/semantic_index/symbol.rs @@ -142,6 +142,11 @@ impl<'db> ScopeId<'db> { NodeWithScopeKind::Class(class) | NodeWithScopeKind::ClassTypeParameters(class) => { class.name.as_str() } + NodeWithScopeKind::TypeAliasTypeParameters(type_alias) => type_alias + .name + .as_name_expr() + .map(|name| name.id.as_str()) + .unwrap_or(""), NodeWithScopeKind::Function(function) | NodeWithScopeKind::FunctionTypeParameters(function) => function.name.as_str(), NodeWithScopeKind::Lambda(_) => "", @@ -339,6 +344,7 @@ pub(crate) enum NodeWithScopeRef<'a> { Lambda(&'a ast::ExprLambda), FunctionTypeParameters(&'a ast::StmtFunctionDef), ClassTypeParameters(&'a ast::StmtClassDef), + TypeAliasTypeParameters(&'a ast::StmtTypeAlias), ListComprehension(&'a ast::ExprListComp), SetComprehension(&'a ast::ExprSetComp), DictComprehension(&'a ast::ExprDictComp), @@ -360,6 +366,9 @@ impl NodeWithScopeRef<'_> { NodeWithScopeRef::Function(function) => { NodeWithScopeKind::Function(AstNodeRef::new(module, function)) } + NodeWithScopeRef::TypeAliasTypeParameters(type_alias) => { + NodeWithScopeKind::TypeAliasTypeParameters(AstNodeRef::new(module, type_alias)) + } NodeWithScopeRef::Lambda(lambda) => { NodeWithScopeKind::Lambda(AstNodeRef::new(module, lambda)) } @@ -400,6 +409,9 @@ impl NodeWithScopeRef<'_> { NodeWithScopeRef::ClassTypeParameters(class) => { NodeWithScopeKey::ClassTypeParameters(NodeKey::from_node(class)) } + NodeWithScopeRef::TypeAliasTypeParameters(type_alias) => { + NodeWithScopeKey::TypeAliasTypeParameters(NodeKey::from_node(type_alias)) + } NodeWithScopeRef::ListComprehension(comprehension) => { NodeWithScopeKey::ListComprehension(NodeKey::from_node(comprehension)) } @@ -424,6 +436,7 @@ pub enum NodeWithScopeKind { ClassTypeParameters(AstNodeRef), Function(AstNodeRef), FunctionTypeParameters(AstNodeRef), + TypeAliasTypeParameters(AstNodeRef), Lambda(AstNodeRef), ListComprehension(AstNodeRef), SetComprehension(AstNodeRef), @@ -438,7 +451,9 @@ impl NodeWithScopeKind { Self::Class(_) => ScopeKind::Class, Self::Function(_) => ScopeKind::Function, Self::Lambda(_) => ScopeKind::Function, - Self::FunctionTypeParameters(_) | Self::ClassTypeParameters(_) => ScopeKind::Annotation, + Self::FunctionTypeParameters(_) + | Self::ClassTypeParameters(_) + | Self::TypeAliasTypeParameters(_) => ScopeKind::Annotation, Self::ListComprehension(_) | Self::SetComprehension(_) | Self::DictComprehension(_) @@ -468,6 +483,7 @@ pub(crate) enum NodeWithScopeKey { ClassTypeParameters(NodeKey), Function(NodeKey), FunctionTypeParameters(NodeKey), + TypeAliasTypeParameters(NodeKey), Lambda(NodeKey), ListComprehension(NodeKey), SetComprehension(NodeKey), diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index 23f2cd8674..729ab6e581 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -425,6 +425,9 @@ impl<'db> TypeInferenceBuilder<'db> { NodeWithScopeKind::FunctionTypeParameters(function) => { self.infer_function_type_params(function.node()); } + NodeWithScopeKind::TypeAliasTypeParameters(type_alias) => { + self.infer_type_alias_type_params(type_alias.node()); + } NodeWithScopeKind::ListComprehension(comprehension) => { self.infer_list_comprehension_expression_scope(comprehension.node()); } @@ -588,6 +591,9 @@ impl<'db> TypeInferenceBuilder<'db> { self.infer_function_definition(function.node(), definition); } DefinitionKind::Class(class) => self.infer_class_definition(class.node(), definition), + DefinitionKind::TypeAlias(type_alias) => { + self.infer_type_alias_definition(type_alias.node(), definition); + } DefinitionKind::Import(import) => { self.infer_import_definition(import.node(), definition); } @@ -822,6 +828,16 @@ impl<'db> TypeInferenceBuilder<'db> { self.infer_parameters(&function.parameters); } + fn infer_type_alias_type_params(&mut self, type_alias: &ast::StmtTypeAlias) { + let type_params = type_alias + .type_params + .as_ref() + .expect("type alias type params scope without type params"); + + self.infer_type_parameters(type_params); + // self.infer_optional_expression(type_alias.value.as_deref()); + } + fn infer_function_body(&mut self, function: &ast::StmtFunctionDef) { self.infer_body(&function.body); } @@ -1075,6 +1091,21 @@ impl<'db> TypeInferenceBuilder<'db> { } } + fn infer_type_alias_definition( + &mut self, + type_alias: &ast::StmtTypeAlias, + definition: Definition<'db>, + ) { + let type_alias_ty = Type::Todo; + + self.add_declaration_with_binding( + type_alias.into(), + definition, + type_alias_ty, + type_alias_ty, + ); + } + fn infer_if_statement(&mut self, if_statement: &ast::StmtIf) { let ast::StmtIf { range: _, @@ -1792,18 +1823,19 @@ impl<'db> TypeInferenceBuilder<'db> { self.infer_augmented_op(assignment, target_type, value_type) } - fn infer_type_alias_statement(&mut self, type_alias_statement: &ast::StmtTypeAlias) { - let ast::StmtTypeAlias { - range: _, - name, - type_params, - value, - } = type_alias_statement; - self.infer_expression(value); - self.infer_expression(name); - if let Some(type_params) = type_params { - self.infer_type_parameters(type_params); - } + fn infer_type_alias_statement(&mut self, node: &ast::StmtTypeAlias) { + self.infer_definition(node); + // let ast::StmtTypeAlias { + // range: _, + // name, + // type_params, + // value, + // } = type_alias_statement; + // // self.infer_expression(value); + // // self.infer_expression(name); + // if let Some(type_params) = type_params { + // self.infer_type_parameters(type_params); + // } } fn infer_for_statement(&mut self, for_statement: &ast::StmtFor) { diff --git a/crates/red_knot_workspace/resources/test/corpus/89_type_alias.py b/crates/red_knot_workspace/resources/test/corpus/89_type_alias.py index 5bdfc4b05b..3acd9fb91e 100644 --- a/crates/red_knot_workspace/resources/test/corpus/89_type_alias.py +++ b/crates/red_knot_workspace/resources/test/corpus/89_type_alias.py @@ -1 +1,2 @@ type foo = int +type ListOrSet[T] = list[T] | set[T]