diff --git a/rust/ruby-rbs/src/ast/annotation.rs b/rust/ruby-rbs/src/ast/annotation.rs new file mode 100644 index 000000000..f4592da10 --- /dev/null +++ b/rust/ruby-rbs/src/ast/annotation.rs @@ -0,0 +1,8 @@ +use crate::ast::location::LocationRange; +use crate::ids::SymbolId; + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct Annotation { + pub string: SymbolId, + pub location: Option, +} diff --git a/rust/ruby-rbs/src/ast/comment.rs b/rust/ruby-rbs/src/ast/comment.rs new file mode 100644 index 000000000..ad5928226 --- /dev/null +++ b/rust/ruby-rbs/src/ast/comment.rs @@ -0,0 +1,8 @@ +use crate::ast::location::LocationRange; +use crate::ids::SymbolId; + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct Comment { + pub string: SymbolId, + pub location: Option, +} diff --git a/rust/ruby-rbs/src/ast/convert.rs b/rust/ruby-rbs/src/ast/convert.rs new file mode 100644 index 000000000..24a797f78 --- /dev/null +++ b/rust/ruby-rbs/src/ast/convert.rs @@ -0,0 +1,1255 @@ +use crate::ast::annotation::Annotation; +use crate::ast::comment::Comment; +use crate::ast::declarations::{ + ClassAliasDeclaration, ClassDeclaration, ClassMember, ClassSuper, ConstantDeclaration, + Declaration, GlobalDeclaration, InterfaceDeclaration, ModuleAliasDeclaration, + ModuleDeclaration, ModuleMember, ModuleSelf, TypeAliasDeclaration, +}; +use crate::ast::directives::{ + Directive, UseClause, UseDirective, UseSingleClause, UseWildcardClause, +}; +use crate::ast::location::{ + AliasDeclarationLocation, AliasLocation, AliasMemberLocation, AttributeMemberLocation, + ClassDeclarationLocation, ClassInstanceLocation, ClassSingletonLocation, ClassSuperLocation, + ConstantDeclarationLocation, FunctionParamLocation, GlobalDeclarationLocation, + InterfaceDeclarationLocation, InterfaceLocation, LocationRange, MethodDefinitionLocation, + MethodTypeLocation, MixinMemberLocation, ModuleDeclarationLocation, ModuleSelfLocation, + TypeAliasDeclarationLocation, TypeParamLocation, UseDirectiveLocation, UseSingleClauseLocation, + UseWildcardClauseLocation, VariableMemberLocation, +}; +use crate::ast::members::{ + AliasKind, AliasMember, AttrAccessorMember, AttrReaderMember, AttrWriterMember, AttributeKind, + ClassInstanceVariableMember, ClassVariableMember, ExtendMember, IncludeMember, + InstanceVariableMember, IvarName, Member, MethodDefinitionMember, MethodDefinitionOverload, + MethodKind, PrependMember, PrivateMember, PublicMember, Visibility, +}; +use crate::ast::method_type::MethodType; +use crate::ast::type_param::{TypeParam, Variance}; +use crate::ast::types::{ + AliasType, BaseType, BaseTypeKind, BlockType, ClassInstanceType, ClassSingletonType, Function, + FunctionParam, FunctionType, InterfaceType, IntersectionType, KeywordParam, Literal, + LiteralType, OptionalType, ProcType, RecordField, RecordKey, RecordType, TupleType, Type, + UnionType, UntypedFunctionType, VariableType, +}; +use crate::ids::{SymbolId, TypeName}; +use crate::interner::StringInterner; +use crate::node::{ + AliasKind as NodeAliasKind, AliasNode, AliasTypeNode, AnnotationNode, AttrAccessorNode, + AttrIvarName, AttrReaderNode, AttrWriterNode, AttributeKind as NodeAttributeKind, + AttributeVisibility as NodeAttributeVisibility, BlockTypeNode, ClassAliasNode, + ClassInstanceTypeNode, ClassInstanceVariableNode, ClassNode, ClassSingletonTypeNode, + ClassSuperNode, ClassVariableNode, CommentNode, ConstantNode, ExtendNode, FunctionParamNode, + FunctionTypeNode, GlobalNode, IncludeNode, InstanceVariableNode, InterfaceNode, + InterfaceTypeNode, MethodDefinitionKind as NodeMethodDefinitionKind, MethodDefinitionNode, + MethodDefinitionOverloadNode, MethodDefinitionVisibility as NodeMethodDefinitionVisibility, + MethodTypeNode, ModuleAliasNode, ModuleNode, ModuleSelfNode, NamespaceNode, Node, PrependNode, + PrivateNode, PublicNode, RBSLocationRange, SymbolNode, TypeAliasNode, TypeNameNode, + TypeParamNode, TypeParamVariance, UntypedFunctionTypeNode, UseNode, UseSingleClauseNode, + UseWildcardClauseNode, +}; +use crate::type_name::TypeNameInterner; + +pub struct AstConverter<'a> { + strings: &'a mut StringInterner, + type_names: &'a mut TypeNameInterner, +} + +impl<'a> AstConverter<'a> { + pub fn new(strings: &'a mut StringInterner, type_names: &'a mut TypeNameInterner) -> Self { + Self { + strings, + type_names, + } + } + + pub fn convert_declaration(&mut self, node: &Node<'_>) -> Declaration { + match node { + Node::Class(node) => Declaration::Class(self.convert_class_declaration(node)), + Node::Module(node) => Declaration::Module(self.convert_module_declaration(node)), + Node::Interface(node) => { + Declaration::Interface(self.convert_interface_declaration(node)) + } + Node::Constant(node) => Declaration::Constant(self.convert_constant_declaration(node)), + Node::Global(node) => Declaration::Global(self.convert_global_declaration(node)), + Node::TypeAlias(node) => { + Declaration::TypeAlias(self.convert_type_alias_declaration(node)) + } + Node::ClassAlias(node) => { + Declaration::ClassAlias(self.convert_class_alias_declaration(node)) + } + Node::ModuleAlias(node) => { + Declaration::ModuleAlias(self.convert_module_alias_declaration(node)) + } + _ => panic_expected("declaration node while converting declaration", node), + } + } + + pub fn convert_member(&mut self, node: &Node<'_>) -> Member { + match node { + Node::MethodDefinition(node) => { + Member::MethodDefinition(self.convert_method_definition_member(node)) + } + Node::InstanceVariable(node) => { + Member::InstanceVariable(self.convert_instance_variable_member(node)) + } + Node::ClassInstanceVariable(node) => { + Member::ClassInstanceVariable(self.convert_class_instance_variable_member(node)) + } + Node::ClassVariable(node) => { + Member::ClassVariable(self.convert_class_variable_member(node)) + } + Node::Include(node) => Member::Include(self.convert_include_member(node)), + Node::Extend(node) => Member::Extend(self.convert_extend_member(node)), + Node::Prepend(node) => Member::Prepend(self.convert_prepend_member(node)), + Node::AttrReader(node) => Member::AttrReader(self.convert_attr_reader_member(node)), + Node::AttrWriter(node) => Member::AttrWriter(self.convert_attr_writer_member(node)), + Node::AttrAccessor(node) => { + Member::AttrAccessor(self.convert_attr_accessor_member(node)) + } + Node::Public(node) => Member::Public(self.convert_public_member(node)), + Node::Private(node) => Member::Private(self.convert_private_member(node)), + Node::Alias(node) => Member::Alias(self.convert_alias_member(node)), + _ => panic_expected("member node while converting member", node), + } + } + + pub fn convert_directive(&mut self, node: &Node<'_>) -> Directive { + match node { + Node::Use(node) => Directive::Use(self.convert_use_directive(node)), + _ => panic_expected("directive node while converting directive", node), + } + } + + pub fn convert_type(&mut self, node: &Node<'_>) -> Type { + match node { + Node::AliasType(node) => Type::Alias(self.convert_alias_type(node)), + Node::AnyType(node) => Type::Base(BaseType { + kind: BaseTypeKind::Any { todo: node.todo() }, + location: Some(convert_range(node.location())), + }), + Node::BoolType(node) => self.base(BaseTypeKind::Bool, node.location()), + Node::BottomType(node) => self.base(BaseTypeKind::Bottom, node.location()), + Node::ClassType(node) => self.base(BaseTypeKind::Class, node.location()), + Node::InstanceType(node) => self.base(BaseTypeKind::Instance, node.location()), + Node::NilType(node) => self.base(BaseTypeKind::Nil, node.location()), + Node::SelfType(node) => self.base(BaseTypeKind::SelfType, node.location()), + Node::TopType(node) => self.base(BaseTypeKind::Top, node.location()), + Node::VoidType(node) => self.base(BaseTypeKind::Void, node.location()), + Node::ClassInstanceType(node) => { + Type::ClassInstance(self.convert_class_instance_type(node)) + } + Node::ClassSingletonType(node) => { + Type::ClassSingleton(self.convert_class_singleton_type(node)) + } + Node::InterfaceType(node) => Type::Interface(self.convert_interface_type(node)), + Node::IntersectionType(node) => Type::Intersection(IntersectionType { + types: self.convert_type_list(node.types()), + location: Some(convert_range(node.location())), + }), + Node::LiteralType(node) => Type::Literal(LiteralType { + literal: self.convert_literal(&node.literal()), + location: Some(convert_range(node.location())), + }), + Node::OptionalType(node) => Type::Optional(OptionalType { + ty: Box::new(self.convert_type(&node.type_())), + location: Some(convert_range(node.location())), + }), + Node::ProcType(node) => Type::Proc(ProcType { + function: self.convert_function_type(&node.type_()), + block: node.block().map(|block| self.convert_block_type(&block)), + self_type: node.self_type().map(|ty| Box::new(self.convert_type(&ty))), + location: Some(convert_range(node.location())), + }), + Node::RecordType(node) => { + let mut fields = Vec::new(); + for (key, value) in node.all_fields().iter() { + let key = self.convert_record_key(&key); + let Node::RecordFieldType(field) = value else { + panic_expected("record field value while converting record type", &value); + }; + fields.push(RecordField { + key, + ty: self.convert_type(&field.type_()), + required: field.required(), + }); + } + Type::Record(RecordType { + fields, + location: Some(convert_range(node.location())), + }) + } + Node::TupleType(node) => Type::Tuple(TupleType { + types: self.convert_type_list(node.types()), + location: Some(convert_range(node.location())), + }), + Node::UnionType(node) => Type::Union(UnionType { + types: self.convert_type_list(node.types()), + location: Some(convert_range(node.location())), + }), + Node::VariableType(node) => Type::Variable(VariableType { + name: self.intern_symbol(&node.name()), + location: Some(convert_range(node.location())), + }), + _ => panic_expected("type node while converting type", node), + } + } + + pub fn convert_function_type(&mut self, node: &Node<'_>) -> Function { + match node { + Node::FunctionType(node) => Function::Typed(self.convert_function(node)), + Node::UntypedFunctionType(node) => { + Function::Untyped(self.convert_untyped_function(node)) + } + _ => panic_expected("function type node while converting function type", node), + } + } + + pub fn convert_method_type(&mut self, node: &MethodTypeNode<'_>) -> MethodType { + MethodType { + type_params: self.convert_type_params(node.type_params()), + function: self.convert_function_type(&node.type_()), + block: node.block().map(|block| self.convert_block_type(&block)), + location: Some(MethodTypeLocation { + range: convert_range(node.location()), + type_range: convert_range(node.type_location()), + type_params_range: convert_optional_range(node.type_params_location()), + }), + } + } + + fn base(&self, kind: BaseTypeKind, location: RBSLocationRange) -> Type { + Type::Base(BaseType { + kind, + location: Some(convert_range(location)), + }) + } + + fn convert_class_declaration(&mut self, node: &ClassNode<'_>) -> ClassDeclaration { + ClassDeclaration { + name: self.convert_type_name(&node.name()), + type_params: self.convert_type_params(node.type_params()), + members: self.convert_class_members(node.members()), + super_class: node + .super_class() + .map(|super_class| self.convert_class_super(&super_class)), + annotations: self.convert_annotations(node.annotations()), + location: Some(ClassDeclarationLocation { + range: convert_range(node.location()), + keyword_range: convert_range(node.keyword_location()), + name_range: convert_range(node.name_location()), + end_range: convert_range(node.end_location()), + type_params_range: convert_optional_range(node.type_params_location()), + lt_range: convert_optional_range(node.lt_location()), + }), + comment: self.convert_optional_comment(node.comment()), + } + } + + fn convert_class_super(&mut self, node: &ClassSuperNode<'_>) -> ClassSuper { + ClassSuper { + name: self.convert_type_name(&node.name()), + args: self.convert_type_list(node.args()), + location: Some(ClassSuperLocation { + range: convert_range(node.location()), + name_range: convert_range(node.name_location()), + args_range: convert_optional_range(node.args_location()), + }), + } + } + + fn convert_use_directive(&mut self, node: &UseNode<'_>) -> UseDirective { + UseDirective { + clauses: self.convert_use_clauses(node.clauses()), + location: Some(UseDirectiveLocation { + range: convert_range(node.location()), + keyword_range: convert_range(node.keyword_location()), + }), + } + } + + fn convert_use_clause(&mut self, node: &Node<'_>) -> UseClause { + match node { + Node::UseSingleClause(node) => UseClause::Single(self.convert_use_single_clause(node)), + Node::UseWildcardClause(node) => { + UseClause::Wildcard(self.convert_use_wildcard_clause(node)) + } + _ => panic_expected("use clause node while converting use directive", node), + } + } + + fn convert_use_single_clause(&mut self, node: &UseSingleClauseNode<'_>) -> UseSingleClause { + UseSingleClause { + type_name: self.convert_type_name(&node.type_name()), + new_name: node.new_name().map(|name| self.intern_symbol(&name)), + location: Some(UseSingleClauseLocation { + range: convert_range(node.location()), + type_name_range: convert_range(node.type_name_location()), + keyword_range: convert_optional_range(node.keyword_location()), + new_name_range: convert_optional_range(node.new_name_location()), + }), + } + } + + fn convert_use_wildcard_clause( + &mut self, + node: &UseWildcardClauseNode<'_>, + ) -> UseWildcardClause { + UseWildcardClause { + namespace: self.convert_namespace(&node.namespace()), + location: Some(UseWildcardClauseLocation { + range: convert_range(node.location()), + namespace_range: convert_range(node.namespace_location()), + star_range: convert_range(node.star_location()), + }), + } + } + + fn convert_module_declaration(&mut self, node: &ModuleNode<'_>) -> ModuleDeclaration { + ModuleDeclaration { + name: self.convert_type_name(&node.name()), + type_params: self.convert_type_params(node.type_params()), + members: self.convert_module_members(node.members()), + location: Some(ModuleDeclarationLocation { + range: convert_range(node.location()), + keyword_range: convert_range(node.keyword_location()), + name_range: convert_range(node.name_location()), + end_range: convert_range(node.end_location()), + type_params_range: convert_optional_range(node.type_params_location()), + colon_range: convert_optional_range(node.colon_location()), + self_types_range: convert_optional_range(node.self_types_location()), + }), + annotations: self.convert_annotations(node.annotations()), + self_types: self.convert_module_selfs(node.self_types()), + comment: self.convert_optional_comment(node.comment()), + } + } + + fn convert_module_self(&mut self, node: &ModuleSelfNode<'_>) -> ModuleSelf { + ModuleSelf { + name: self.convert_type_name(&node.name()), + args: self.convert_type_list(node.args()), + location: Some(ModuleSelfLocation { + range: convert_range(node.location()), + name_range: convert_range(node.name_location()), + args_range: convert_optional_range(node.args_location()), + }), + } + } + + fn convert_interface_declaration(&mut self, node: &InterfaceNode<'_>) -> InterfaceDeclaration { + InterfaceDeclaration { + name: self.convert_type_name(&node.name()), + type_params: self.convert_type_params(node.type_params()), + members: self.convert_members(node.members()), + annotations: self.convert_annotations(node.annotations()), + location: Some(InterfaceDeclarationLocation { + range: convert_range(node.location()), + keyword_range: convert_range(node.keyword_location()), + name_range: convert_range(node.name_location()), + end_range: convert_range(node.end_location()), + type_params_range: convert_optional_range(node.type_params_location()), + }), + comment: self.convert_optional_comment(node.comment()), + } + } + + fn convert_type_alias_declaration(&mut self, node: &TypeAliasNode<'_>) -> TypeAliasDeclaration { + TypeAliasDeclaration { + name: self.convert_type_name(&node.name()), + type_params: self.convert_type_params(node.type_params()), + ty: self.convert_type(&node.type_()), + annotations: self.convert_annotations(node.annotations()), + location: Some(TypeAliasDeclarationLocation { + range: convert_range(node.location()), + keyword_range: convert_range(node.keyword_location()), + name_range: convert_range(node.name_location()), + eq_range: convert_range(node.eq_location()), + type_params_range: convert_optional_range(node.type_params_location()), + }), + comment: self.convert_optional_comment(node.comment()), + } + } + + fn convert_constant_declaration(&mut self, node: &ConstantNode<'_>) -> ConstantDeclaration { + ConstantDeclaration { + name: self.convert_type_name(&node.name()), + ty: self.convert_type(&node.type_()), + location: Some(ConstantDeclarationLocation { + range: convert_range(node.location()), + name_range: convert_range(node.name_location()), + colon_range: convert_range(node.colon_location()), + }), + comment: self.convert_optional_comment(node.comment()), + annotations: self.convert_annotations(node.annotations()), + } + } + + fn convert_global_declaration(&mut self, node: &GlobalNode<'_>) -> GlobalDeclaration { + GlobalDeclaration { + name: self.intern_symbol(&node.name()), + ty: self.convert_type(&node.type_()), + location: Some(GlobalDeclarationLocation { + range: convert_range(node.location()), + name_range: convert_range(node.name_location()), + colon_range: convert_range(node.colon_location()), + }), + comment: self.convert_optional_comment(node.comment()), + annotations: self.convert_annotations(node.annotations()), + } + } + + fn convert_class_alias_declaration( + &mut self, + node: &ClassAliasNode<'_>, + ) -> ClassAliasDeclaration { + ClassAliasDeclaration { + new_name: self.convert_type_name(&node.new_name()), + old_name: self.convert_type_name(&node.old_name()), + location: Some(AliasDeclarationLocation { + range: convert_range(node.location()), + keyword_range: convert_range(node.keyword_location()), + new_name_range: convert_range(node.new_name_location()), + eq_range: convert_range(node.eq_location()), + old_name_range: convert_range(node.old_name_location()), + }), + comment: self.convert_optional_comment(node.comment()), + annotations: self.convert_annotations(node.annotations()), + } + } + + fn convert_module_alias_declaration( + &mut self, + node: &ModuleAliasNode<'_>, + ) -> ModuleAliasDeclaration { + ModuleAliasDeclaration { + new_name: self.convert_type_name(&node.new_name()), + old_name: self.convert_type_name(&node.old_name()), + location: Some(AliasDeclarationLocation { + range: convert_range(node.location()), + keyword_range: convert_range(node.keyword_location()), + new_name_range: convert_range(node.new_name_location()), + eq_range: convert_range(node.eq_location()), + old_name_range: convert_range(node.old_name_location()), + }), + comment: self.convert_optional_comment(node.comment()), + annotations: self.convert_annotations(node.annotations()), + } + } + + fn convert_method_definition_member( + &mut self, + node: &MethodDefinitionNode<'_>, + ) -> MethodDefinitionMember { + MethodDefinitionMember { + name: self.intern_symbol(&node.name()), + kind: convert_method_kind(node.kind()), + overloads: self.convert_method_definition_overloads(node.overloads()), + annotations: self.convert_annotations(node.annotations()), + location: Some(MethodDefinitionLocation { + range: convert_range(node.location()), + keyword_range: convert_range(node.keyword_location()), + name_range: convert_range(node.name_location()), + kind_range: convert_optional_range(node.kind_location()), + overloading_range: convert_optional_range(node.overloading_location()), + visibility_range: convert_optional_range(node.visibility_location()), + }), + comment: self.convert_optional_comment(node.comment()), + overloading: node.overloading(), + visibility: convert_method_visibility(node.visibility()), + } + } + + fn convert_method_definition_overload( + &mut self, + node: &MethodDefinitionOverloadNode<'_>, + ) -> MethodDefinitionOverload { + MethodDefinitionOverload { + method_type: self.convert_method_type_node(&node.method_type()), + annotations: self.convert_annotations(node.annotations()), + } + } + + fn convert_instance_variable_member( + &mut self, + node: &InstanceVariableNode<'_>, + ) -> InstanceVariableMember { + InstanceVariableMember { + name: self.intern_symbol(&node.name()), + ty: self.convert_type(&node.type_()), + location: Some(variable_member_location( + node.location(), + node.name_location(), + node.colon_location(), + node.kind_location(), + )), + comment: self.convert_optional_comment(node.comment()), + } + } + + fn convert_class_instance_variable_member( + &mut self, + node: &ClassInstanceVariableNode<'_>, + ) -> ClassInstanceVariableMember { + ClassInstanceVariableMember { + name: self.intern_symbol(&node.name()), + ty: self.convert_type(&node.type_()), + location: Some(variable_member_location( + node.location(), + node.name_location(), + node.colon_location(), + node.kind_location(), + )), + comment: self.convert_optional_comment(node.comment()), + } + } + + fn convert_class_variable_member( + &mut self, + node: &ClassVariableNode<'_>, + ) -> ClassVariableMember { + ClassVariableMember { + name: self.intern_symbol(&node.name()), + ty: self.convert_type(&node.type_()), + location: Some(variable_member_location( + node.location(), + node.name_location(), + node.colon_location(), + node.kind_location(), + )), + comment: self.convert_optional_comment(node.comment()), + } + } + + fn convert_include_member(&mut self, node: &IncludeNode<'_>) -> IncludeMember { + IncludeMember { + name: self.convert_type_name(&node.name()), + args: self.convert_type_list(node.args()), + annotations: self.convert_annotations(node.annotations()), + location: Some(mixin_member_location( + node.location(), + node.keyword_location(), + node.name_location(), + node.args_location(), + )), + comment: self.convert_optional_comment(node.comment()), + } + } + + fn convert_extend_member(&mut self, node: &ExtendNode<'_>) -> ExtendMember { + ExtendMember { + name: self.convert_type_name(&node.name()), + args: self.convert_type_list(node.args()), + annotations: self.convert_annotations(node.annotations()), + location: Some(mixin_member_location( + node.location(), + node.keyword_location(), + node.name_location(), + node.args_location(), + )), + comment: self.convert_optional_comment(node.comment()), + } + } + + fn convert_prepend_member(&mut self, node: &PrependNode<'_>) -> PrependMember { + PrependMember { + name: self.convert_type_name(&node.name()), + args: self.convert_type_list(node.args()), + annotations: self.convert_annotations(node.annotations()), + location: Some(mixin_member_location( + node.location(), + node.keyword_location(), + node.name_location(), + node.args_location(), + )), + comment: self.convert_optional_comment(node.comment()), + } + } + + fn convert_attr_reader_member(&mut self, node: &AttrReaderNode<'_>) -> AttrReaderMember { + AttrReaderMember { + name: self.intern_symbol(&node.name()), + ty: self.convert_type(&node.type_()), + ivar_name: self.convert_ivar_name(node.ivar_name(), node.ivar_name_string()), + kind: convert_attribute_kind(node.kind()), + annotations: self.convert_annotations(node.annotations()), + location: Some(attribute_member_location( + node.location(), + node.keyword_location(), + node.name_location(), + node.colon_location(), + node.kind_location(), + node.ivar_location(), + node.ivar_name_location(), + node.visibility_location(), + )), + comment: self.convert_optional_comment(node.comment()), + visibility: convert_attribute_visibility(node.visibility()), + } + } + + fn convert_attr_accessor_member(&mut self, node: &AttrAccessorNode<'_>) -> AttrAccessorMember { + AttrAccessorMember { + name: self.intern_symbol(&node.name()), + ty: self.convert_type(&node.type_()), + ivar_name: self.convert_ivar_name(node.ivar_name(), node.ivar_name_string()), + kind: convert_attribute_kind(node.kind()), + annotations: self.convert_annotations(node.annotations()), + location: Some(attribute_member_location( + node.location(), + node.keyword_location(), + node.name_location(), + node.colon_location(), + node.kind_location(), + node.ivar_location(), + node.ivar_name_location(), + node.visibility_location(), + )), + comment: self.convert_optional_comment(node.comment()), + visibility: convert_attribute_visibility(node.visibility()), + } + } + + fn convert_attr_writer_member(&mut self, node: &AttrWriterNode<'_>) -> AttrWriterMember { + AttrWriterMember { + name: self.intern_symbol(&node.name()), + ty: self.convert_type(&node.type_()), + ivar_name: self.convert_ivar_name(node.ivar_name(), node.ivar_name_string()), + kind: convert_attribute_kind(node.kind()), + annotations: self.convert_annotations(node.annotations()), + location: Some(attribute_member_location( + node.location(), + node.keyword_location(), + node.name_location(), + node.colon_location(), + node.kind_location(), + node.ivar_location(), + node.ivar_name_location(), + node.visibility_location(), + )), + comment: self.convert_optional_comment(node.comment()), + visibility: convert_attribute_visibility(node.visibility()), + } + } + + fn convert_public_member(&mut self, node: &PublicNode<'_>) -> PublicMember { + PublicMember { + location: Some(convert_range(node.location())), + } + } + + fn convert_private_member(&mut self, node: &PrivateNode<'_>) -> PrivateMember { + PrivateMember { + location: Some(convert_range(node.location())), + } + } + + fn convert_alias_member(&mut self, node: &AliasNode<'_>) -> AliasMember { + AliasMember { + new_name: self.intern_symbol(&node.new_name()), + old_name: self.intern_symbol(&node.old_name()), + kind: convert_alias_kind(node.kind()), + annotations: self.convert_annotations(node.annotations()), + location: Some(AliasMemberLocation { + range: convert_range(node.location()), + keyword_range: convert_range(node.keyword_location()), + new_name_range: convert_range(node.new_name_location()), + old_name_range: convert_range(node.old_name_location()), + new_kind_range: convert_optional_range(node.new_kind_location()), + old_kind_range: convert_optional_range(node.old_kind_location()), + }), + comment: self.convert_optional_comment(node.comment()), + } + } + + fn convert_alias_type(&mut self, node: &AliasTypeNode<'_>) -> AliasType { + AliasType { + name: self.convert_type_name(&node.name()), + args: self.convert_type_list(node.args()), + location: Some(AliasLocation { + range: convert_range(node.location()), + name_range: convert_range(node.name_location()), + args_range: convert_optional_range(node.args_location()), + }), + } + } + + fn convert_class_instance_type( + &mut self, + node: &ClassInstanceTypeNode<'_>, + ) -> ClassInstanceType { + ClassInstanceType { + name: self.convert_type_name(&node.name()), + args: self.convert_type_list(node.args()), + location: Some(ClassInstanceLocation { + range: convert_range(node.location()), + name_range: convert_range(node.name_location()), + args_range: convert_optional_range(node.args_location()), + }), + } + } + + fn convert_class_singleton_type( + &mut self, + node: &ClassSingletonTypeNode<'_>, + ) -> ClassSingletonType { + ClassSingletonType { + name: self.convert_type_name(&node.name()), + args: self.convert_type_list(node.args()), + location: Some(ClassSingletonLocation { + range: convert_range(node.location()), + name_range: convert_range(node.name_location()), + args_range: convert_optional_range(node.args_location()), + }), + } + } + + fn convert_interface_type(&mut self, node: &InterfaceTypeNode<'_>) -> InterfaceType { + InterfaceType { + name: self.convert_type_name(&node.name()), + args: self.convert_type_list(node.args()), + location: Some(InterfaceLocation { + range: convert_range(node.location()), + name_range: convert_range(node.name_location()), + args_range: convert_optional_range(node.args_location()), + }), + } + } + + fn convert_function(&mut self, node: &FunctionTypeNode<'_>) -> FunctionType { + FunctionType { + required_positionals: self.convert_function_params(node.required_positionals()), + optional_positionals: self.convert_function_params(node.optional_positionals()), + rest_positionals: node + .rest_positionals() + .map(|param| Box::new(self.convert_function_param_node(¶m))), + trailing_positionals: self.convert_function_params(node.trailing_positionals()), + required_keywords: self.convert_keyword_params(node.required_keywords()), + optional_keywords: self.convert_keyword_params(node.optional_keywords()), + rest_keywords: node + .rest_keywords() + .map(|param| Box::new(self.convert_function_param_node(¶m))), + return_type: Box::new(self.convert_type(&node.return_type())), + location: Some(convert_range(node.location())), + } + } + + fn convert_untyped_function( + &mut self, + node: &UntypedFunctionTypeNode<'_>, + ) -> UntypedFunctionType { + UntypedFunctionType { + return_type: Box::new(self.convert_type(&node.return_type())), + location: Some(convert_range(node.location())), + } + } + + fn convert_block_type(&mut self, node: &BlockTypeNode<'_>) -> BlockType { + BlockType { + function: self.convert_function_type(&node.type_()), + required: node.required(), + self_type: node.self_type().map(|ty| Box::new(self.convert_type(&ty))), + location: Some(convert_range(node.location())), + } + } + + fn convert_function_param_node(&mut self, node: &Node<'_>) -> FunctionParam { + let Node::FunctionParam(node) = node else { + panic_expected( + "function parameter node while converting function type", + node, + ); + }; + self.convert_function_param(node) + } + + fn convert_function_param(&mut self, node: &FunctionParamNode<'_>) -> FunctionParam { + FunctionParam { + ty: Box::new(self.convert_type(&node.type_())), + name: node.name().map(|name| self.intern_symbol(&name)), + location: Some(FunctionParamLocation { + range: convert_range(node.location()), + name_range: convert_optional_range(node.name_location()), + }), + } + } + + fn convert_type_param_node(&mut self, node: &Node<'_>) -> TypeParam { + let Node::TypeParam(node) = node else { + panic_expected("type parameter node while converting type parameters", node); + }; + self.convert_type_param(node) + } + + fn convert_type_param(&mut self, node: &TypeParamNode<'_>) -> TypeParam { + TypeParam { + name: self.intern_symbol(&node.name()), + variance: convert_variance(node.variance()), + upper_bound: node.upper_bound().map(|ty| self.convert_type(&ty)), + lower_bound: node.lower_bound().map(|ty| self.convert_type(&ty)), + default_type: node.default_type().map(|ty| self.convert_type(&ty)), + unchecked: node.unchecked(), + location: Some(TypeParamLocation { + range: convert_range(node.location()), + name_range: convert_range(node.name_location()), + variance_range: convert_optional_range(node.variance_location()), + unchecked_range: convert_optional_range(node.unchecked_location()), + upper_bound_range: convert_optional_range(node.upper_bound_location()), + lower_bound_range: convert_optional_range(node.lower_bound_location()), + default_range: convert_optional_range(node.default_location()), + }), + } + } + + fn convert_type_list(&mut self, list: crate::node::NodeList<'_>) -> Vec { + list.iter().map(|node| self.convert_type(&node)).collect() + } + + fn convert_function_params(&mut self, list: crate::node::NodeList<'_>) -> Vec { + list.iter() + .map(|node| self.convert_function_param_node(&node)) + .collect() + } + + fn convert_type_params(&mut self, list: crate::node::NodeList<'_>) -> Vec { + list.iter() + .map(|node| self.convert_type_param_node(&node)) + .collect() + } + + fn convert_use_clauses(&mut self, list: crate::node::NodeList<'_>) -> Vec { + list.iter() + .map(|node| self.convert_use_clause(&node)) + .collect() + } + + fn convert_class_members(&mut self, list: crate::node::NodeList<'_>) -> Vec { + list.iter() + .map(|node| match node { + Node::Class(_) + | Node::Module(_) + | Node::Interface(_) + | Node::Constant(_) + | Node::Global(_) + | Node::TypeAlias(_) + | Node::ClassAlias(_) + | Node::ModuleAlias(_) => ClassMember::Declaration(self.convert_declaration(&node)), + Node::Alias(_) + | Node::AttrAccessor(_) + | Node::AttrReader(_) + | Node::AttrWriter(_) + | Node::ClassInstanceVariable(_) + | Node::ClassVariable(_) + | Node::Extend(_) + | Node::Include(_) + | Node::InstanceVariable(_) + | Node::MethodDefinition(_) + | Node::Prepend(_) + | Node::Private(_) + | Node::Public(_) => ClassMember::Member(self.convert_member(&node)), + _ => panic_expected("class member while converting class declaration", &node), + }) + .collect() + } + + fn convert_module_members(&mut self, list: crate::node::NodeList<'_>) -> Vec { + list.iter() + .map(|node| match node { + Node::Class(_) + | Node::Module(_) + | Node::Interface(_) + | Node::Constant(_) + | Node::Global(_) + | Node::TypeAlias(_) + | Node::ClassAlias(_) + | Node::ModuleAlias(_) => { + ModuleMember::Declaration(self.convert_declaration(&node)) + } + Node::Alias(_) + | Node::AttrAccessor(_) + | Node::AttrReader(_) + | Node::AttrWriter(_) + | Node::ClassInstanceVariable(_) + | Node::ClassVariable(_) + | Node::Extend(_) + | Node::Include(_) + | Node::InstanceVariable(_) + | Node::MethodDefinition(_) + | Node::Prepend(_) + | Node::Private(_) + | Node::Public(_) => ModuleMember::Member(self.convert_member(&node)), + _ => panic_expected("module member while converting module declaration", &node), + }) + .collect() + } + + fn convert_members(&mut self, list: crate::node::NodeList<'_>) -> Vec { + list.iter().map(|node| self.convert_member(&node)).collect() + } + + fn convert_module_selfs(&mut self, list: crate::node::NodeList<'_>) -> Vec { + list.iter() + .map(|node| { + let Node::ModuleSelf(node) = node else { + panic_expected( + "module self type while converting module declaration", + &node, + ); + }; + self.convert_module_self(&node) + }) + .collect() + } + + fn convert_method_definition_overloads( + &mut self, + list: crate::node::NodeList<'_>, + ) -> Vec { + list.iter() + .map(|node| { + let Node::MethodDefinitionOverload(node) = node else { + panic_expected( + "method definition overload while converting method definition", + &node, + ); + }; + self.convert_method_definition_overload(&node) + }) + .collect() + } + + fn convert_keyword_params(&mut self, hash: crate::node::RBSHash<'_>) -> Vec { + hash.iter() + .map(|(key, value)| { + let Node::Symbol(symbol) = key else { + panic_expected( + "symbol keyword name while converting keyword parameter", + &key, + ); + }; + KeywordParam { + name: self.intern_symbol(&symbol), + param: self.convert_function_param_node(&value), + } + }) + .collect() + } + + fn convert_record_key(&mut self, node: &Node<'_>) -> RecordKey { + match node { + Node::Symbol(symbol) => RecordKey::Symbol(self.intern_symbol(symbol)), + Node::String(string) => RecordKey::String(string.string().as_str().to_owned()), + Node::Integer(integer) => { + RecordKey::Integer(integer.string_representation().as_str().to_owned()) + } + Node::Bool(boolean) => RecordKey::Bool(boolean.value()), + _ => panic_expected("record key while converting record type", node), + } + } + + fn convert_literal(&mut self, node: &Node<'_>) -> Literal { + match node { + Node::String(string) => Literal::String(string.string().as_str().to_owned()), + Node::Integer(integer) => { + Literal::Integer(integer.string_representation().as_str().to_owned()) + } + Node::Symbol(symbol) => Literal::Symbol(self.intern_symbol(symbol)), + Node::Bool(boolean) => Literal::Bool(boolean.value()), + _ => panic_expected("literal value while converting literal type", node), + } + } + + fn convert_method_type_node(&mut self, node: &Node<'_>) -> MethodType { + let Node::MethodType(node) = node else { + panic_expected( + "method type node while converting method definition overload", + node, + ); + }; + self.convert_method_type(node) + } + + fn convert_annotations(&mut self, list: crate::node::NodeList<'_>) -> Vec { + list.iter() + .map(|node| { + let Node::Annotation(node) = node else { + panic_expected("annotation node while converting annotations", &node); + }; + self.convert_annotation(&node) + }) + .collect() + } + + fn convert_annotation(&mut self, node: &AnnotationNode<'_>) -> Annotation { + Annotation { + string: self.strings.intern(node.string().as_str()), + location: Some(convert_range(node.location())), + } + } + + fn convert_optional_comment(&mut self, node: Option>) -> Option { + node.map(|node| self.convert_comment(&node)) + } + + fn convert_comment(&mut self, node: &CommentNode<'_>) -> Comment { + Comment { + string: self.strings.intern(node.string().as_str()), + location: Some(convert_range(node.location())), + } + } + + fn convert_ivar_name(&mut self, ivar_name: AttrIvarName, name: Option) -> IvarName { + match ivar_name { + AttrIvarName::Unspecified => IvarName::Unspecified, + AttrIvarName::Empty => IvarName::Empty, + AttrIvarName::Name(_) => { + let name = name.unwrap_or_else(|| { + panic!("invalid RBS AST while converting to owned AST: explicit ivar name is missing from the constant pool") + }); + IvarName::Name(self.strings.intern(&name)) + } + } + } + + fn convert_type_name(&mut self, node: &TypeNameNode<'_>) -> TypeName { + let name = self.convert_namespace(&node.namespace()); + let final_segment = self.intern_symbol(&node.name()); + self.type_names.append(name, final_segment) + } + + fn convert_namespace(&mut self, node: &NamespaceNode<'_>) -> TypeName { + let mut name = self.type_names.root(node.absolute()); + for segment_node in node.path().iter() { + match segment_node { + Node::Symbol(segment) => { + let segment = self.intern_symbol(&segment); + name = self.type_names.append(name, segment); + } + _ => panic_expected( + "symbol in type name namespace path while converting type name", + &segment_node, + ), + } + } + name + } + + fn intern_symbol(&mut self, node: &SymbolNode<'_>) -> SymbolId { + self.strings.intern(node.as_str()) + } +} + +fn convert_range(range: RBSLocationRange) -> LocationRange { + let start_char = range.start_char(); + let start_byte = range.start_byte(); + let end_char = range.end_char(); + let end_byte = range.end_byte(); + + LocationRange { + start_char: convert_range_component("start_char", start_char, &range), + start_byte: convert_range_component("start_byte", start_byte, &range), + end_char: convert_range_component("end_char", end_char, &range), + end_byte: convert_range_component("end_byte", end_byte, &range), + } +} + +fn convert_range_component(name: &str, value: i32, range: &RBSLocationRange) -> u32 { + u32::try_from(value).unwrap_or_else(|_| { + panic!( + "invalid RBS location range while converting to owned AST: {name} must be non-negative and fit in u32, got {value}; full range is start_char={}, start_byte={}, end_char={}, end_byte={}", + range.start_char(), + range.start_byte(), + range.end_char(), + range.end_byte() + ) + }) +} + +fn convert_optional_range(range: Option) -> Option { + range.map(convert_range) +} + +fn variable_member_location( + range: RBSLocationRange, + name_range: RBSLocationRange, + colon_range: RBSLocationRange, + kind_range: Option, +) -> VariableMemberLocation { + VariableMemberLocation { + range: convert_range(range), + name_range: convert_range(name_range), + colon_range: convert_range(colon_range), + kind_range: convert_optional_range(kind_range), + } +} + +fn mixin_member_location( + range: RBSLocationRange, + keyword_range: RBSLocationRange, + name_range: RBSLocationRange, + args_range: Option, +) -> MixinMemberLocation { + MixinMemberLocation { + range: convert_range(range), + keyword_range: convert_range(keyword_range), + name_range: convert_range(name_range), + args_range: convert_optional_range(args_range), + } +} + +#[allow(clippy::too_many_arguments)] +fn attribute_member_location( + range: RBSLocationRange, + keyword_range: RBSLocationRange, + name_range: RBSLocationRange, + colon_range: RBSLocationRange, + kind_range: Option, + ivar_range: Option, + ivar_name_range: Option, + visibility_range: Option, +) -> AttributeMemberLocation { + AttributeMemberLocation { + range: convert_range(range), + keyword_range: convert_range(keyword_range), + name_range: convert_range(name_range), + colon_range: convert_range(colon_range), + kind_range: convert_optional_range(kind_range), + ivar_range: convert_optional_range(ivar_range), + ivar_name_range: convert_optional_range(ivar_name_range), + visibility_range: convert_optional_range(visibility_range), + } +} + +fn convert_variance(variance: TypeParamVariance) -> Variance { + match variance { + TypeParamVariance::Invariant => Variance::Invariant, + TypeParamVariance::Covariant => Variance::Covariant, + TypeParamVariance::Contravariant => Variance::Contravariant, + } +} + +fn convert_method_kind(kind: NodeMethodDefinitionKind) -> MethodKind { + match kind { + NodeMethodDefinitionKind::Instance => MethodKind::Instance, + NodeMethodDefinitionKind::Singleton => MethodKind::Singleton, + NodeMethodDefinitionKind::SingletonInstance => MethodKind::SingletonInstance, + } +} + +fn convert_method_visibility(visibility: NodeMethodDefinitionVisibility) -> Option { + match visibility { + NodeMethodDefinitionVisibility::Unspecified => None, + NodeMethodDefinitionVisibility::Public => Some(Visibility::Public), + NodeMethodDefinitionVisibility::Private => Some(Visibility::Private), + } +} + +fn convert_attribute_kind(kind: NodeAttributeKind) -> AttributeKind { + match kind { + NodeAttributeKind::Instance => AttributeKind::Instance, + NodeAttributeKind::Singleton => AttributeKind::Singleton, + } +} + +fn convert_attribute_visibility(visibility: NodeAttributeVisibility) -> Option { + match visibility { + NodeAttributeVisibility::Unspecified => None, + NodeAttributeVisibility::Public => Some(Visibility::Public), + NodeAttributeVisibility::Private => Some(Visibility::Private), + } +} + +fn convert_alias_kind(kind: NodeAliasKind) -> AliasKind { + match kind { + NodeAliasKind::Instance => AliasKind::Instance, + NodeAliasKind::Singleton => AliasKind::Singleton, + } +} + +fn panic_expected(expected: &str, actual: &Node<'_>) -> ! { + panic!( + "invalid RBS AST while converting to owned AST: expected {expected}, got {}", + node_kind(actual) + ) +} + +fn node_kind(node: &Node<'_>) -> &'static str { + match node { + Node::Annotation(_) => "Annotation", + Node::Bool(_) => "Bool", + Node::Comment(_) => "Comment", + Node::Class(_) => "Class", + Node::ClassSuper(_) => "ClassSuper", + Node::ClassAlias(_) => "ClassAlias", + Node::Constant(_) => "Constant", + Node::Global(_) => "Global", + Node::Interface(_) => "Interface", + Node::Module(_) => "Module", + Node::ModuleSelf(_) => "ModuleSelf", + Node::ModuleAlias(_) => "ModuleAlias", + Node::TypeAlias(_) => "TypeAlias", + Node::Use(_) => "Use", + Node::UseSingleClause(_) => "UseSingleClause", + Node::UseWildcardClause(_) => "UseWildcardClause", + Node::Integer(_) => "Integer", + Node::Alias(_) => "Alias", + Node::AttrAccessor(_) => "AttrAccessor", + Node::AttrReader(_) => "AttrReader", + Node::AttrWriter(_) => "AttrWriter", + Node::ClassInstanceVariable(_) => "ClassInstanceVariable", + Node::ClassVariable(_) => "ClassVariable", + Node::Extend(_) => "Extend", + Node::Include(_) => "Include", + Node::InstanceVariable(_) => "InstanceVariable", + Node::MethodDefinition(_) => "MethodDefinition", + Node::MethodDefinitionOverload(_) => "MethodDefinitionOverload", + Node::Prepend(_) => "Prepend", + Node::Private(_) => "Private", + Node::Public(_) => "Public", + Node::BlockParamTypeAnnotation(_) => "BlockParamTypeAnnotation", + Node::ClassAliasAnnotation(_) => "ClassAliasAnnotation", + Node::ColonMethodTypeAnnotation(_) => "ColonMethodTypeAnnotation", + Node::DoubleSplatParamTypeAnnotation(_) => "DoubleSplatParamTypeAnnotation", + Node::InstanceVariableAnnotation(_) => "InstanceVariableAnnotation", + Node::MethodTypesAnnotation(_) => "MethodTypesAnnotation", + Node::ModuleAliasAnnotation(_) => "ModuleAliasAnnotation", + Node::NodeTypeAssertion(_) => "NodeTypeAssertion", + Node::ParamTypeAnnotation(_) => "ParamTypeAnnotation", + Node::ReturnTypeAnnotation(_) => "ReturnTypeAnnotation", + Node::SkipAnnotation(_) => "SkipAnnotation", + Node::SplatParamTypeAnnotation(_) => "SplatParamTypeAnnotation", + Node::TypeApplicationAnnotation(_) => "TypeApplicationAnnotation", + Node::String(_) => "String", + Node::Symbol(_) => "Symbol", + Node::TypeParam(_) => "TypeParam", + Node::MethodType(_) => "MethodType", + Node::Namespace(_) => "Namespace", + Node::Signature(_) => "Signature", + Node::TypeName(_) => "TypeName", + Node::AliasType(_) => "AliasType", + Node::AnyType(_) => "AnyType", + Node::BoolType(_) => "BoolType", + Node::BottomType(_) => "BottomType", + Node::ClassType(_) => "ClassType", + Node::InstanceType(_) => "InstanceType", + Node::NilType(_) => "NilType", + Node::SelfType(_) => "SelfType", + Node::TopType(_) => "TopType", + Node::VoidType(_) => "VoidType", + Node::BlockType(_) => "BlockType", + Node::ClassInstanceType(_) => "ClassInstanceType", + Node::ClassSingletonType(_) => "ClassSingletonType", + Node::FunctionType(_) => "FunctionType", + Node::FunctionParam(_) => "FunctionParam", + Node::InterfaceType(_) => "InterfaceType", + Node::IntersectionType(_) => "IntersectionType", + Node::LiteralType(_) => "LiteralType", + Node::OptionalType(_) => "OptionalType", + Node::ProcType(_) => "ProcType", + Node::RecordType(_) => "RecordType", + Node::RecordFieldType(_) => "RecordFieldType", + Node::TupleType(_) => "TupleType", + Node::UnionType(_) => "UnionType", + Node::UntypedFunctionType(_) => "UntypedFunctionType", + Node::VariableType(_) => "VariableType", + } +} diff --git a/rust/ruby-rbs/src/ast/declarations.rs b/rust/ruby-rbs/src/ast/declarations.rs new file mode 100644 index 000000000..5e392bc32 --- /dev/null +++ b/rust/ruby-rbs/src/ast/declarations.rs @@ -0,0 +1,127 @@ +use crate::ast::annotation::Annotation; +use crate::ast::comment::Comment; +use crate::ast::location::{ + AliasDeclarationLocation, ClassDeclarationLocation, ClassSuperLocation, + ConstantDeclarationLocation, GlobalDeclarationLocation, InterfaceDeclarationLocation, + ModuleDeclarationLocation, ModuleSelfLocation, TypeAliasDeclarationLocation, +}; +use crate::ast::members::Member; +use crate::ast::type_param::TypeParam; +use crate::ast::types::Type; +use crate::ids::{SymbolId, TypeName}; + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum Declaration { + Class(ClassDeclaration), + Module(ModuleDeclaration), + Interface(InterfaceDeclaration), + Constant(ConstantDeclaration), + Global(GlobalDeclaration), + TypeAlias(TypeAliasDeclaration), + ClassAlias(ClassAliasDeclaration), + ModuleAlias(ModuleAliasDeclaration), +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum ClassMember { + Member(Member), + Declaration(Declaration), +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum ModuleMember { + Member(Member), + Declaration(Declaration), +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ClassSuper { + pub name: TypeName, + pub args: Vec, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ClassDeclaration { + pub name: TypeName, + pub type_params: Vec, + pub members: Vec, + pub super_class: Option, + pub annotations: Vec, + pub location: Option, + pub comment: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ModuleSelf { + pub name: TypeName, + pub args: Vec, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ModuleDeclaration { + pub name: TypeName, + pub type_params: Vec, + pub members: Vec, + pub location: Option, + pub annotations: Vec, + pub self_types: Vec, + pub comment: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct InterfaceDeclaration { + pub name: TypeName, + pub type_params: Vec, + pub members: Vec, + pub annotations: Vec, + pub location: Option, + pub comment: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct TypeAliasDeclaration { + pub name: TypeName, + pub type_params: Vec, + pub ty: Type, + pub annotations: Vec, + pub location: Option, + pub comment: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ConstantDeclaration { + pub name: TypeName, + pub ty: Type, + pub location: Option, + pub comment: Option, + pub annotations: Vec, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct GlobalDeclaration { + pub name: SymbolId, + pub ty: Type, + pub location: Option, + pub comment: Option, + pub annotations: Vec, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ClassAliasDeclaration { + pub new_name: TypeName, + pub old_name: TypeName, + pub location: Option, + pub comment: Option, + pub annotations: Vec, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ModuleAliasDeclaration { + pub new_name: TypeName, + pub old_name: TypeName, + pub location: Option, + pub comment: Option, + pub annotations: Vec, +} diff --git a/rust/ruby-rbs/src/ast/directives.rs b/rust/ruby-rbs/src/ast/directives.rs new file mode 100644 index 000000000..b21dd7ab6 --- /dev/null +++ b/rust/ruby-rbs/src/ast/directives.rs @@ -0,0 +1,42 @@ +use crate::ast::location::{ + ResolveTypeNamesDirectiveLocation, UseDirectiveLocation, UseSingleClauseLocation, + UseWildcardClauseLocation, +}; +use crate::ids::{SymbolId, TypeName}; + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum Directive { + Use(UseDirective), + ResolveTypeNames(ResolveTypeNamesDirective), +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct UseDirective { + pub clauses: Vec, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum UseClause { + Single(UseSingleClause), + Wildcard(UseWildcardClause), +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct UseSingleClause { + pub type_name: TypeName, + pub new_name: Option, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct UseWildcardClause { + pub namespace: TypeName, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ResolveTypeNamesDirective { + pub value: bool, + pub location: Option, +} diff --git a/rust/ruby-rbs/src/ast/location.rs b/rust/ruby-rbs/src/ast/location.rs new file mode 100644 index 000000000..9b9cecc72 --- /dev/null +++ b/rust/ruby-rbs/src/ast/location.rs @@ -0,0 +1,439 @@ +/// A byte and character range in the source buffer. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct LocationRange { + pub start_char: u32, + pub start_byte: u32, + pub end_char: u32, + pub end_byte: u32, +} + +impl LocationRange { + #[must_use] + pub fn new(start_char: u32, start_byte: u32, end_char: u32, end_byte: u32) -> Self { + Self { + start_char, + start_byte, + end_char, + end_byte, + } + } +} + +/// ```rbs +/// foo +/// ^^^ name +/// +/// foo[bar, baz] +/// ^^^ name +/// ^^^^^^^^^^ args +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct AliasLocation { + pub range: LocationRange, + pub name_range: LocationRange, + pub args_range: Option, +} + +/// ```rbs +/// Foo +/// ^^^ name +/// +/// Foo[Bar, Baz] +/// ^^^ name +/// ^^^^^^^^^^ args +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ClassInstanceLocation { + pub range: LocationRange, + pub name_range: LocationRange, + pub args_range: Option, +} + +/// ```rbs +/// singleton(::Foo) +/// ^^^^^ name +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ClassSingletonLocation { + pub range: LocationRange, + pub name_range: LocationRange, + pub args_range: Option, +} + +/// ```rbs +/// String name +/// ^^^^ name +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct FunctionParamLocation { + pub range: LocationRange, + pub name_range: Option, +} + +/// ```rbs +/// _Foo +/// ^^^^ name +/// +/// _Foo[Bar, Baz] +/// ^^^^ name +/// ^^^^^^^^^^ args +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct InterfaceLocation { + pub range: LocationRange, + pub name_range: LocationRange, + pub args_range: Option, +} + +/// ```rbs +/// () -> void +/// ^^^^^^^^^^ type +/// +/// [A] () { () -> A } -> A +/// ^^^ type_params +/// ^^^^^^^^^^^^^^^^^^^ type +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct MethodTypeLocation { + pub range: LocationRange, + pub type_range: LocationRange, + pub type_params_range: Option, +} + +/// ```rbs +/// Key +/// ^^^ name +/// +/// unchecked out Elem < _ToJson > bot = untyped +/// ^^^^^^^^^ unchecked +/// ^^^ variance +/// ^^^^ name +/// ^^^^^^^^^ upper_bound +/// ^^^^^ lower_bound +/// ^^^^^^^^ default +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct TypeParamLocation { + pub range: LocationRange, + pub name_range: LocationRange, + pub variance_range: Option, + pub unchecked_range: Option, + pub upper_bound_range: Option, + pub lower_bound_range: Option, + pub default_range: Option, +} + +/// ```rbs +/// String +/// ^^^^^^ name +/// +/// Array[String] +/// ^^^^^ name +/// ^^^^^^^^ args +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ClassSuperLocation { + pub range: LocationRange, + pub name_range: LocationRange, + pub args_range: Option, +} + +/// ```rbs +/// class Foo end +/// ^^^^^ keyword +/// ^^^ name +/// ^^^ end +/// +/// class Foo[A] < String end +/// ^^^^^ keyword +/// ^^^ name +/// ^^^ type_params +/// ^ lt +/// ^^^ end +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ClassDeclarationLocation { + pub range: LocationRange, + pub keyword_range: LocationRange, + pub name_range: LocationRange, + pub end_range: LocationRange, + pub type_params_range: Option, + pub lt_range: Option, +} + +/// ```rbs +/// _Each[String] +/// ^^^^^ name +/// ^^^^^^^^ args +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ModuleSelfLocation { + pub range: LocationRange, + pub name_range: LocationRange, + pub args_range: Option, +} + +/// ```rbs +/// module Foo end +/// ^^^^^^ keyword +/// ^^^ name +/// ^^^ end +/// +/// module Foo[A] : BasicObject end +/// ^^^^^^ keyword +/// ^^^ name +/// ^^^ type_params +/// ^ colon +/// ^^^^^^^^^^^ self_types +/// ^^^ end +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ModuleDeclarationLocation { + pub range: LocationRange, + pub keyword_range: LocationRange, + pub name_range: LocationRange, + pub end_range: LocationRange, + pub type_params_range: Option, + pub colon_range: Option, + pub self_types_range: Option, +} + +/// ```rbs +/// interface _Foo end +/// ^^^^^^^^^ keyword +/// ^^^^ name +/// ^^^ end +/// +/// interface _Bar[A, B] end +/// ^^^^^^^^^ keyword +/// ^^^^ name +/// ^^^^^^ type_params +/// ^^^ end +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct InterfaceDeclarationLocation { + pub range: LocationRange, + pub keyword_range: LocationRange, + pub name_range: LocationRange, + pub end_range: LocationRange, + pub type_params_range: Option, +} + +/// ```rbs +/// type loc[T] = Location[T, bot] +/// ^^^^ keyword +/// ^^^ name +/// ^^^ type_params +/// ^ eq +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct TypeAliasDeclarationLocation { + pub range: LocationRange, + pub keyword_range: LocationRange, + pub name_range: LocationRange, + pub eq_range: LocationRange, + pub type_params_range: Option, +} + +/// ```rbs +/// VERSION: String +/// ^^^^^^^ name +/// ^ colon +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ConstantDeclarationLocation { + pub range: LocationRange, + pub name_range: LocationRange, + pub colon_range: LocationRange, +} + +/// ```rbs +/// $SIZE: String +/// ^^^^^ name +/// ^ colon +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct GlobalDeclarationLocation { + pub range: LocationRange, + pub name_range: LocationRange, + pub colon_range: LocationRange, +} + +/// ```rbs +/// module Foo = Bar +/// ^^^^^^ keyword +/// ^^^ new_name +/// ^ eq +/// ^^^ old_name +/// +/// class Foo = Bar +/// ^^^^^ keyword +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct AliasDeclarationLocation { + pub range: LocationRange, + pub keyword_range: LocationRange, + pub new_name_range: LocationRange, + pub eq_range: LocationRange, + pub old_name_range: LocationRange, +} + +/// ```rbs +/// def foo: () -> void +/// ^^^ keyword +/// ^^^ name +/// +/// private def self.bar: () -> void | ... +/// ^^^^^^^ visibility +/// ^^^ keyword +/// ^^^^^ kind +/// ^^^ name +/// ^^^ overloading +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct MethodDefinitionLocation { + pub range: LocationRange, + pub keyword_range: LocationRange, + pub name_range: LocationRange, + pub kind_range: Option, + pub overloading_range: Option, + pub visibility_range: Option, +} + +/// ```rbs +/// @foo: String +/// ^^^^ name +/// ^ colon +/// +/// self.@all: Array[String] +/// ^^^^^ kind +/// ^^^^ name +/// ^ colon +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct VariableMemberLocation { + pub range: LocationRange, + pub name_range: LocationRange, + pub colon_range: LocationRange, + pub kind_range: Option, +} + +/// ```rbs +/// include Foo +/// ^^^^^^^ keyword +/// ^^^ name +/// +/// include Array[String] +/// ^^^^^^^ keyword +/// ^^^^^ name +/// ^^^^^^^^ args +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct MixinMemberLocation { + pub range: LocationRange, + pub keyword_range: LocationRange, + pub name_range: LocationRange, + pub args_range: Option, +} + +/// ```rbs +/// attr_reader name: String +/// ^^^^^^^^^^^ keyword +/// ^^^^ name +/// ^ colon +/// +/// public attr_accessor self.name (@foo) : String +/// ^^^^^^ visibility +/// ^^^^^^^^^^^^^ keyword +/// ^^^^^ kind +/// ^^^^ name +/// ^^^^^^ ivar +/// ^^^^ ivar_name +/// ^ colon +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct AttributeMemberLocation { + pub range: LocationRange, + pub keyword_range: LocationRange, + pub name_range: LocationRange, + pub colon_range: LocationRange, + pub kind_range: Option, + pub ivar_range: Option, + pub ivar_name_range: Option, + pub visibility_range: Option, +} + +/// ```rbs +/// alias foo bar +/// ^^^^^ keyword +/// ^^^ new_name +/// ^^^ old_name +/// +/// alias self.foo self.bar +/// ^^^^^ keyword +/// ^^^^^ new_kind +/// ^^^ new_name +/// ^^^^^ old_kind +/// ^^^ old_name +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct AliasMemberLocation { + pub range: LocationRange, + pub keyword_range: LocationRange, + pub new_name_range: LocationRange, + pub old_name_range: LocationRange, + pub new_kind_range: Option, + pub old_kind_range: Option, +} + +/// ```rbs +/// use Foo +/// ^^^ keyword +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct UseDirectiveLocation { + pub range: LocationRange, + pub keyword_range: LocationRange, +} + +/// ```rbs +/// Foo::Bar +/// ^^^^^^^^ type_name +/// +/// Foo::Bar as X +/// ^^ keyword +/// ^ new_name +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct UseSingleClauseLocation { + pub range: LocationRange, + pub type_name_range: LocationRange, + pub keyword_range: Option, + pub new_name_range: Option, +} + +/// ```rbs +/// Foo::Bar::* +/// ^^^^^^^^^^ namespace +/// ^ star +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct UseWildcardClauseLocation { + pub range: LocationRange, + pub namespace_range: LocationRange, + pub star_range: LocationRange, +} + +/// ```rbs +/// # resolve-type-names: false +/// ^^^^^^^^^^^^^^^^^^ keyword +/// ^ colon +/// ^^^^^ value +/// ``` +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ResolveTypeNamesDirectiveLocation { + pub range: LocationRange, + pub keyword_range: LocationRange, + pub colon_range: LocationRange, + pub value_range: LocationRange, +} diff --git a/rust/ruby-rbs/src/ast/members.rs b/rust/ruby-rbs/src/ast/members.rs new file mode 100644 index 000000000..bbe4e3111 --- /dev/null +++ b/rust/ruby-rbs/src/ast/members.rs @@ -0,0 +1,183 @@ +use crate::ast::annotation::Annotation; +use crate::ast::comment::Comment; +use crate::ast::location::{ + AliasMemberLocation, AttributeMemberLocation, MethodDefinitionLocation, MixinMemberLocation, + VariableMemberLocation, +}; +use crate::ast::method_type::MethodType; +use crate::ast::types::Type; +use crate::ids::{SymbolId, TypeName}; + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum Member { + MethodDefinition(MethodDefinitionMember), + InstanceVariable(InstanceVariableMember), + ClassInstanceVariable(ClassInstanceVariableMember), + ClassVariable(ClassVariableMember), + Include(IncludeMember), + Extend(ExtendMember), + Prepend(PrependMember), + AttrReader(AttrReaderMember), + AttrWriter(AttrWriterMember), + AttrAccessor(AttrAccessorMember), + Public(PublicMember), + Private(PrivateMember), + Alias(AliasMember), +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum Visibility { + Public, + Private, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum MethodKind { + Instance, + Singleton, + SingletonInstance, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct MethodDefinitionMember { + pub name: SymbolId, + pub kind: MethodKind, + pub overloads: Vec, + pub annotations: Vec, + pub location: Option, + pub comment: Option, + pub overloading: bool, + pub visibility: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct MethodDefinitionOverload { + pub method_type: MethodType, + pub annotations: Vec, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct InstanceVariableMember { + pub name: SymbolId, + pub ty: Type, + pub location: Option, + pub comment: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ClassInstanceVariableMember { + pub name: SymbolId, + pub ty: Type, + pub location: Option, + pub comment: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ClassVariableMember { + pub name: SymbolId, + pub ty: Type, + pub location: Option, + pub comment: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct IncludeMember { + pub name: TypeName, + pub args: Vec, + pub annotations: Vec, + pub location: Option, + pub comment: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ExtendMember { + pub name: TypeName, + pub args: Vec, + pub annotations: Vec, + pub location: Option, + pub comment: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct PrependMember { + pub name: TypeName, + pub args: Vec, + pub annotations: Vec, + pub location: Option, + pub comment: Option, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum AttributeKind { + Instance, + Singleton, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum IvarName { + Unspecified, + Empty, + Name(SymbolId), +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct AttrReaderMember { + pub name: SymbolId, + pub ty: Type, + pub ivar_name: IvarName, + pub kind: AttributeKind, + pub annotations: Vec, + pub location: Option, + pub comment: Option, + pub visibility: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct AttrAccessorMember { + pub name: SymbolId, + pub ty: Type, + pub ivar_name: IvarName, + pub kind: AttributeKind, + pub annotations: Vec, + pub location: Option, + pub comment: Option, + pub visibility: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct AttrWriterMember { + pub name: SymbolId, + pub ty: Type, + pub ivar_name: IvarName, + pub kind: AttributeKind, + pub annotations: Vec, + pub location: Option, + pub comment: Option, + pub visibility: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct PublicMember { + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct PrivateMember { + pub location: Option, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum AliasKind { + Instance, + Singleton, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct AliasMember { + pub new_name: SymbolId, + pub old_name: SymbolId, + pub kind: AliasKind, + pub annotations: Vec, + pub location: Option, + pub comment: Option, +} diff --git a/rust/ruby-rbs/src/ast/method_type.rs b/rust/ruby-rbs/src/ast/method_type.rs new file mode 100644 index 000000000..9c9abe6a2 --- /dev/null +++ b/rust/ruby-rbs/src/ast/method_type.rs @@ -0,0 +1,11 @@ +use crate::ast::location::MethodTypeLocation; +use crate::ast::type_param::TypeParam; +use crate::ast::types::{BlockType, Function}; + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct MethodType { + pub type_params: Vec, + pub function: Function, + pub block: Option, + pub location: Option, +} diff --git a/rust/ruby-rbs/src/ast/mod.rs b/rust/ruby-rbs/src/ast/mod.rs new file mode 100644 index 000000000..99e341184 --- /dev/null +++ b/rust/ruby-rbs/src/ast/mod.rs @@ -0,0 +1,327 @@ +//! Owned pure-Rust AST data structures. +//! +//! The [`node`] module exposes borrowed wrappers over the C parser AST. This +//! module is the Rust-owned representation that can be built from parser nodes, +//! generated directly, or transformed without keeping the parser allocation +//! alive. +//! +//! [`node`]: crate::node + +pub mod annotation; +pub mod comment; +pub mod convert; +pub mod declarations; +pub mod directives; +pub mod location; +pub mod members; +pub mod method_type; +pub mod type_param; +pub mod types; + +pub use annotation::Annotation; +pub use comment::Comment; +pub use convert::AstConverter; +pub use declarations::{ + ClassAliasDeclaration, ClassDeclaration, ClassMember, ClassSuper, ConstantDeclaration, + Declaration, GlobalDeclaration, InterfaceDeclaration, ModuleAliasDeclaration, + ModuleDeclaration, ModuleMember, ModuleSelf, TypeAliasDeclaration, +}; +pub use directives::{ + Directive, ResolveTypeNamesDirective, UseClause, UseDirective, UseSingleClause, + UseWildcardClause, +}; +pub use location::{ + AliasDeclarationLocation, AliasLocation, AliasMemberLocation, AttributeMemberLocation, + ClassDeclarationLocation, ClassInstanceLocation, ClassSingletonLocation, ClassSuperLocation, + ConstantDeclarationLocation, FunctionParamLocation, GlobalDeclarationLocation, + InterfaceDeclarationLocation, InterfaceLocation, LocationRange, MethodDefinitionLocation, + MethodTypeLocation, MixinMemberLocation, ModuleDeclarationLocation, ModuleSelfLocation, + ResolveTypeNamesDirectiveLocation, TypeAliasDeclarationLocation, TypeParamLocation, + UseDirectiveLocation, UseSingleClauseLocation, UseWildcardClauseLocation, + VariableMemberLocation, +}; +pub use members::{ + AliasKind, AliasMember, AttrAccessorMember, AttrReaderMember, AttrWriterMember, AttributeKind, + ClassInstanceVariableMember, ClassVariableMember, ExtendMember, IncludeMember, + InstanceVariableMember, IvarName, Member, MethodDefinitionMember, MethodDefinitionOverload, + MethodKind, PrependMember, PrivateMember, PublicMember, Visibility, +}; +pub use method_type::MethodType; +pub use type_param::{TypeParam, Variance}; +pub use types::{ + AliasType, BaseType, BaseTypeKind, BlockType, ClassInstanceType, ClassSingletonType, Function, + FunctionParam, FunctionType, InterfaceType, IntersectionType, KeywordParam, Literal, + LiteralType, OptionalType, ProcType, RecordField, RecordKey, RecordType, TupleType, Type, + UnionType, UntypedFunctionType, VariableType, +}; + +#[cfg(test)] +mod tests { + use crate::ast::{ + AstConverter, BaseType, BaseTypeKind, ClassMember, Declaration, Directive, IvarName, + Literal, Member, MethodKind, ModuleMember, RecordKey, Type, UseClause, + }; + use crate::interner::StringInterner; + use crate::node::{Node, parse}; + use crate::type_name::TypeNameInterner; + + #[test] + fn builds_owned_type_ast() { + let ty = Type::Base(BaseType { + kind: BaseTypeKind::Any { todo: false }, + location: None, + }); + + assert_eq!( + ty, + Type::Base(BaseType { + kind: BaseTypeKind::Any { todo: false }, + location: None, + }) + ); + } + + #[test] + fn converts_type_node_to_owned_ast() { + let signature = parse( + r#"type foo = { + name: String, + ?age: Integer, + active: true, + tags: Array[String | Symbol] + }"#, + ) + .unwrap(); + + let Node::TypeAlias(alias) = signature.declarations().iter().next().unwrap() else { + panic!("expected type alias"); + }; + + let mut strings = StringInterner::new(); + let mut type_names = TypeNameInterner::new(); + let mut converter = AstConverter::new(&mut strings, &mut type_names); + let ty = converter.convert_type(&alias.type_()); + + let Type::Record(record) = ty else { + panic!("expected record type"); + }; + + assert_eq!(record.fields.len(), 4); + assert_eq!( + record.fields[0].key, + RecordKey::Symbol(strings.intern("name")) + ); + assert!(record.fields[0].required); + assert_eq!( + record.fields[1].key, + RecordKey::Symbol(strings.intern("age")) + ); + assert!(!record.fields[1].required); + + let Type::Literal(literal) = &record.fields[2].ty else { + panic!("expected literal type"); + }; + assert_eq!(literal.literal, Literal::Bool(true)); + + let Type::ClassInstance(array) = &record.fields[3].ty else { + panic!("expected Array class instance"); + }; + assert_eq!(type_names.display(array.name, &strings), "Array"); + assert_eq!(array.args.len(), 1); + } + + #[test] + fn converts_declarations_and_members_to_owned_ast() { + let signature = parse( + r#" + class Foo[T] < Bar[String] + public + include Enumerable[String] + attr_reader name: String + attr_writer email(@email): String + def self.process: (String) -> void + @ivar: Integer + class Nested + end + end + + module M : _Each[String] + extend Kernel + end + + interface _I + def foo: () -> void + end + + type pair[T] = [T, T] + VERSION: String + $global: Integer + class Old = New + module OldM = NewM + "#, + ) + .unwrap(); + + let mut strings = StringInterner::new(); + let mut type_names = TypeNameInterner::new(); + let mut converter = AstConverter::new(&mut strings, &mut type_names); + let declarations = signature + .declarations() + .iter() + .map(|node| converter.convert_declaration(&node)) + .collect::>(); + + assert_eq!(declarations.len(), 8); + + let Declaration::Class(class_decl) = &declarations[0] else { + panic!("expected class declaration"); + }; + assert_eq!(type_names.display(class_decl.name, &strings), "Foo"); + assert_eq!(class_decl.type_params.len(), 1); + assert_eq!( + type_names.display(class_decl.super_class.as_ref().unwrap().name, &strings), + "Bar" + ); + assert_eq!(class_decl.members.len(), 7); + + let ClassMember::Member(Member::Public(public_member)) = &class_decl.members[0] else { + panic!("expected public member"); + }; + assert!(public_member.location.is_some()); + + let ClassMember::Member(Member::Include(include_member)) = &class_decl.members[1] else { + panic!("expected include member"); + }; + assert_eq!( + type_names.display(include_member.name, &strings), + "Enumerable" + ); + assert_eq!(include_member.args.len(), 1); + + let ClassMember::Member(Member::AttrReader(attr_reader)) = &class_decl.members[2] else { + panic!("expected attr_reader member"); + }; + assert_eq!(attr_reader.name, strings.intern("name")); + assert_eq!(attr_reader.visibility, None); + + let ClassMember::Member(Member::AttrWriter(attr_writer)) = &class_decl.members[3] else { + panic!("expected attr_writer member"); + }; + assert_eq!( + attr_writer.ivar_name, + IvarName::Name(strings.intern("@email")) + ); + + let ClassMember::Member(Member::MethodDefinition(method)) = &class_decl.members[4] else { + panic!("expected method definition member"); + }; + assert_eq!(method.name, strings.intern("process")); + assert_eq!(method.kind, MethodKind::Singleton); + assert_eq!(method.visibility, None); + assert_eq!(method.overloads.len(), 1); + + let ClassMember::Member(Member::InstanceVariable(ivar)) = &class_decl.members[5] else { + panic!("expected instance variable member"); + }; + assert_eq!(ivar.name, strings.intern("@ivar")); + + let ClassMember::Declaration(Declaration::Class(nested)) = &class_decl.members[6] else { + panic!("expected nested class declaration"); + }; + assert_eq!(type_names.display(nested.name, &strings), "Nested"); + + let Declaration::Module(module_decl) = &declarations[1] else { + panic!("expected module declaration"); + }; + assert_eq!(type_names.display(module_decl.name, &strings), "M"); + assert_eq!(module_decl.self_types.len(), 1); + let ModuleMember::Member(Member::Extend(extend_member)) = &module_decl.members[0] else { + panic!("expected extend member"); + }; + assert_eq!(type_names.display(extend_member.name, &strings), "Kernel"); + + let Declaration::Interface(interface_decl) = &declarations[2] else { + panic!("expected interface declaration"); + }; + assert_eq!(type_names.display(interface_decl.name, &strings), "_I"); + let Member::MethodDefinition(interface_method) = &interface_decl.members[0] else { + panic!("expected interface method definition"); + }; + assert_eq!(interface_method.kind, MethodKind::Instance); + + let Declaration::TypeAlias(type_alias) = &declarations[3] else { + panic!("expected type alias declaration"); + }; + assert_eq!(type_names.display(type_alias.name, &strings), "pair"); + + let Declaration::Constant(constant) = &declarations[4] else { + panic!("expected constant declaration"); + }; + assert_eq!(type_names.display(constant.name, &strings), "VERSION"); + + let Declaration::Global(global) = &declarations[5] else { + panic!("expected global declaration"); + }; + assert_eq!(global.name, strings.intern("$global")); + + let Declaration::ClassAlias(class_alias) = &declarations[6] else { + panic!("expected class alias declaration"); + }; + assert_eq!(type_names.display(class_alias.new_name, &strings), "Old"); + + let Declaration::ModuleAlias(module_alias) = &declarations[7] else { + panic!("expected module alias declaration"); + }; + assert_eq!(type_names.display(module_alias.old_name, &strings), "NewM"); + } + + #[test] + fn converts_directives_to_owned_ast() { + let signature = parse( + r#" + use Foo, Foo::Bar as FBar, Foo::Baz::* + + class Foo + end + "#, + ) + .unwrap(); + + let mut strings = StringInterner::new(); + let mut type_names = TypeNameInterner::new(); + let mut converter = AstConverter::new(&mut strings, &mut type_names); + let directives = signature + .directives() + .iter() + .map(|node| converter.convert_directive(&node)) + .collect::>(); + + assert_eq!(directives.len(), 1); + + let Directive::Use(use_directive) = &directives[0] else { + panic!("expected use directive"); + }; + assert_eq!(use_directive.clauses.len(), 3); + assert!(use_directive.location.is_some()); + + let UseClause::Single(single) = &use_directive.clauses[0] else { + panic!("expected single use clause"); + }; + assert_eq!(type_names.display(single.type_name, &strings), "Foo"); + assert_eq!(single.new_name, None); + + let UseClause::Single(aliased) = &use_directive.clauses[1] else { + panic!("expected aliased single use clause"); + }; + assert_eq!(type_names.display(aliased.type_name, &strings), "Foo::Bar"); + assert_eq!(aliased.new_name, Some(strings.intern("FBar"))); + assert!(aliased.location.as_ref().unwrap().keyword_range.is_some()); + assert!(aliased.location.as_ref().unwrap().new_name_range.is_some()); + + let UseClause::Wildcard(wildcard) = &use_directive.clauses[2] else { + panic!("expected wildcard use clause"); + }; + assert_eq!(type_names.display(wildcard.namespace, &strings), "Foo::Baz"); + assert!(wildcard.location.is_some()); + } +} diff --git a/rust/ruby-rbs/src/ast/type_param.rs b/rust/ruby-rbs/src/ast/type_param.rs new file mode 100644 index 000000000..4b2a979e6 --- /dev/null +++ b/rust/ruby-rbs/src/ast/type_param.rs @@ -0,0 +1,21 @@ +use crate::ast::location::TypeParamLocation; +use crate::ast::types::Type; +use crate::ids::SymbolId; + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum Variance { + Invariant, + Covariant, + Contravariant, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct TypeParam { + pub name: SymbolId, + pub variance: Variance, + pub upper_bound: Option, + pub lower_bound: Option, + pub default_type: Option, + pub unchecked: bool, + pub location: Option, +} diff --git a/rust/ruby-rbs/src/ast/types.rs b/rust/ruby-rbs/src/ast/types.rs new file mode 100644 index 000000000..317b8d371 --- /dev/null +++ b/rust/ruby-rbs/src/ast/types.rs @@ -0,0 +1,188 @@ +use crate::ast::location::{ + AliasLocation, ClassInstanceLocation, ClassSingletonLocation, FunctionParamLocation, + InterfaceLocation, LocationRange, +}; +use crate::ids::{SymbolId, TypeName}; + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum Type { + Base(BaseType), + Variable(VariableType), + ClassSingleton(ClassSingletonType), + Interface(InterfaceType), + ClassInstance(ClassInstanceType), + Alias(AliasType), + Tuple(TupleType), + Record(RecordType), + Optional(OptionalType), + Union(UnionType), + Intersection(IntersectionType), + Proc(ProcType), + Literal(LiteralType), +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct BaseType { + pub kind: BaseTypeKind, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum BaseTypeKind { + Bool, + Void, + Any { todo: bool }, + Nil, + Top, + Bottom, + SelfType, + Instance, + Class, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct VariableType { + pub name: SymbolId, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ClassSingletonType { + pub name: TypeName, + pub args: Vec, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct InterfaceType { + pub name: TypeName, + pub args: Vec, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ClassInstanceType { + pub name: TypeName, + pub args: Vec, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct AliasType { + pub name: TypeName, + pub args: Vec, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct TupleType { + pub types: Vec, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct RecordType { + pub fields: Vec, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub enum RecordKey { + Symbol(SymbolId), + String(String), + Integer(String), + Bool(bool), +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct RecordField { + pub key: RecordKey, + pub ty: Type, + pub required: bool, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct OptionalType { + pub ty: Box, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct UnionType { + pub types: Vec, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct IntersectionType { + pub types: Vec, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct FunctionType { + pub required_positionals: Vec, + pub optional_positionals: Vec, + pub rest_positionals: Option>, + pub trailing_positionals: Vec, + pub required_keywords: Vec, + pub optional_keywords: Vec, + pub rest_keywords: Option>, + pub return_type: Box, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct KeywordParam { + pub name: SymbolId, + pub param: FunctionParam, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct FunctionParam { + pub ty: Box, + pub name: Option, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct UntypedFunctionType { + pub return_type: Box, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum Function { + Typed(FunctionType), + Untyped(UntypedFunctionType), +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct BlockType { + pub function: Function, + pub required: bool, + pub self_type: Option>, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct ProcType { + pub function: Function, + pub block: Option, + pub self_type: Option>, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct LiteralType { + pub literal: Literal, + pub location: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum Literal { + String(String), + Integer(String), + Symbol(SymbolId), + Bool(bool), +} diff --git a/rust/ruby-rbs/src/lib.rs b/rust/ruby-rbs/src/lib.rs index d63bbfa3c..328898158 100644 --- a/rust/ruby-rbs/src/lib.rs +++ b/rust/ruby-rbs/src/lib.rs @@ -1,3 +1,4 @@ +pub mod ast; pub mod ids; pub mod interner; pub mod node; diff --git a/rust/ruby-rbs/src/node/mod.rs b/rust/ruby-rbs/src/node/mod.rs index 2f52b5048..d007dfc1c 100644 --- a/rust/ruby-rbs/src/node/mod.rs +++ b/rust/ruby-rbs/src/node/mod.rs @@ -199,6 +199,26 @@ impl RBSLocationRange { pub fn end(&self) -> i32 { self.range.end_byte } + + #[must_use] + pub fn start_char(&self) -> i32 { + self.range.start_char + } + + #[must_use] + pub fn start_byte(&self) -> i32 { + self.range.start_byte + } + + #[must_use] + pub fn end_char(&self) -> i32 { + self.range.end_char + } + + #[must_use] + pub fn end_byte(&self) -> i32 { + self.range.end_byte + } } pub struct RBSLocationRangeList<'a> { @@ -302,6 +322,56 @@ impl std::fmt::Display for SymbolNode<'_> { } } +fn constant_id_to_string(parser: NonNull, constant_id: rbs_constant_id_t) -> String { + unsafe { + let constant_ptr = + rbs_constant_pool_id_to_constant(&(*parser.as_ptr()).constant_pool, constant_id); + if constant_ptr.is_null() { + panic!("Constant ID is not present in the pool"); + } + + let constant = &*constant_ptr; + let bytes = std::slice::from_raw_parts(constant.start, constant.length); + std::str::from_utf8_unchecked(bytes).to_owned() + } +} + +impl AttrAccessorNode<'_> { + #[must_use] + pub fn ivar_name_string(&self) -> Option { + match self.ivar_name() { + AttrIvarName::Name(constant_id) => { + Some(constant_id_to_string(self.parser, constant_id)) + } + AttrIvarName::Unspecified | AttrIvarName::Empty => None, + } + } +} + +impl AttrReaderNode<'_> { + #[must_use] + pub fn ivar_name_string(&self) -> Option { + match self.ivar_name() { + AttrIvarName::Name(constant_id) => { + Some(constant_id_to_string(self.parser, constant_id)) + } + AttrIvarName::Unspecified | AttrIvarName::Empty => None, + } + } +} + +impl AttrWriterNode<'_> { + #[must_use] + pub fn ivar_name_string(&self) -> Option { + match self.ivar_name() { + AttrIvarName::Name(constant_id) => { + Some(constant_id_to_string(self.parser, constant_id)) + } + AttrIvarName::Unspecified | AttrIvarName::Empty => None, + } + } +} + #[cfg(test)] mod tests { use super::*;