Skip to content

Commit 2ac02b9

Browse files
committed
Implement parser
1 parent 5e7d715 commit 2ac02b9

File tree

3 files changed

+175
-12
lines changed

3 files changed

+175
-12
lines changed

lib/rbs/inline_parser.rb

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def initialize(location, message)
3535
MixinMultipleArguments = _ = Class.new(Base)
3636
MixinNonConstantModule = _ = Class.new(Base)
3737
AttributeNonSymbolName = _ = Class.new(Base)
38+
ClassModuleAliasDeclarationMissingTypeName = _ = Class.new(Base)
3839
end
3940

4041
def self.parse(buffer, prism)
@@ -392,32 +393,75 @@ def parse_constant_declaration(node)
392393
# Look for trailing type annotation (#: Type)
393394
trailing_block = comments.trailing_block!(node.location)
394395
type_annotation = nil
396+
alias_annotation = nil
395397

396398
if trailing_block
397399
case annotation = trailing_block.trailing_annotation([])
398400
when AST::Ruby::Annotations::NodeTypeAssertion
399401
type_annotation = annotation
402+
when AST::Ruby::Annotations::ClassAliasAnnotation, AST::Ruby::Annotations::ModuleAliasAnnotation
403+
alias_annotation = annotation
400404
when AST::Ruby::CommentBlock::AnnotationSyntaxError
401405
diagnostics << Diagnostic::AnnotationSyntaxError.new(
402406
annotation.location, "Syntax error: " + annotation.error.error_message
403407
)
404408
end
405409
end
406410

407-
# Create the constant declaration
408-
constant_decl = AST::Ruby::Declarations::ConstantDecl.new(
409-
buffer,
410-
constant_name,
411-
node,
412-
leading_block,
413-
type_annotation
414-
)
411+
# Handle class/module alias declarations
412+
if alias_annotation
413+
# Try to infer the old name from the right-hand side
414+
infered_old_name = constant_as_type_name(node.value)
415+
416+
# Check if we have either an explicit type name or can infer one
417+
if alias_annotation.type_name.nil? && infered_old_name.nil?
418+
message =
419+
if alias_annotation.is_a?(AST::Ruby::Annotations::ClassAliasAnnotation)
420+
"Class name is missing in class alias declaration"
421+
else
422+
"Module name is missing in module alias declaration"
423+
end
415424

416-
# Insert the constant declaration appropriately
417-
if current_module
418-
current_module.members << constant_decl
425+
diagnostics << Diagnostic::ClassModuleAliasDeclarationMissingTypeName.new(
426+
alias_annotation.location,
427+
message
428+
)
429+
return
430+
end
431+
432+
# Create class/module alias declaration
433+
alias_decl = AST::Ruby::Declarations::ClassModuleAliasDecl.new(
434+
buffer,
435+
node,
436+
constant_name,
437+
infered_old_name,
438+
leading_block,
439+
alias_annotation
440+
)
441+
442+
# Insert the alias declaration appropriately
443+
444+
if current_module
445+
current_module.members << alias_decl
446+
else
447+
result.declarations << alias_decl
448+
end
419449
else
420-
result.declarations << constant_decl
450+
# Create regular constant declaration
451+
constant_decl = AST::Ruby::Declarations::ConstantDecl.new(
452+
buffer,
453+
constant_name,
454+
node,
455+
leading_block,
456+
type_annotation
457+
)
458+
459+
# Insert the constant declaration appropriately
460+
if current_module
461+
current_module.members << constant_decl
462+
else
463+
result.declarations << constant_decl
464+
end
421465
end
422466
end
423467

sig/inline_parser.rbs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,17 @@ module RBS
5656
class NonConstantConstantDeclaration < Base
5757
end
5858

59+
class ClassModuleAliasDeclarationMissingTypeName < Base
60+
end
61+
5962
type t = NotImplementedYet
6063
| NonConstantClassName | NonConstantModuleName | NonConstantSuperClassName
6164
| TopLevelMethodDefinition | TopLevelAttributeDefinition
6265
| UnusedInlineAnnotation | AnnotationSyntaxError
6366
| MixinMultipleArguments | MixinNonConstantModule
6467
| AttributeNonSymbolName
6568
| NonConstantConstantDeclaration
69+
| ClassModuleAliasDeclarationMissingTypeName
6670
end
6771

6872
def self.parse: (Buffer, Prism::ParseResult) -> Result

test/rbs/inline_parser_test.rb

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,4 +1571,119 @@ module Outer
15711571
end
15721572
end
15731573
end
1574+
1575+
def test_parse__class_alias_without_type_name
1576+
result = parse(<<~RUBY)
1577+
MyString = String #: class-alias
1578+
RUBY
1579+
1580+
assert_empty result.diagnostics
1581+
1582+
result.declarations[0].tap do |decl|
1583+
assert_instance_of RBS::AST::Ruby::Declarations::ClassModuleAliasDecl, decl
1584+
assert_equal RBS::TypeName.parse("MyString"), decl.new_name
1585+
assert_equal RBS::TypeName.parse("String"), decl.infered_old_name
1586+
assert_instance_of RBS::AST::Ruby::Annotations::ClassAliasAnnotation, decl.annotation
1587+
assert_nil decl.annotation.type_name
1588+
assert_equal RBS::TypeName.parse("String"), decl.old_name
1589+
assert_nil decl.leading_comment
1590+
end
1591+
end
1592+
1593+
def test_parse__class_alias_with_type_name
1594+
result = parse(<<~RUBY)
1595+
# Alias for the standard Object class
1596+
MyObject = some_object #: class-alias Object
1597+
RUBY
1598+
1599+
assert_empty result.diagnostics
1600+
1601+
result.declarations[0].tap do |decl|
1602+
assert_instance_of RBS::AST::Ruby::Declarations::ClassModuleAliasDecl, decl
1603+
assert_equal RBS::TypeName.parse("MyObject"), decl.new_name
1604+
assert_nil decl.infered_old_name
1605+
assert_instance_of RBS::AST::Ruby::Annotations::ClassAliasAnnotation, decl.annotation
1606+
assert_equal RBS::TypeName.parse("Object"), decl.annotation.type_name
1607+
assert_equal RBS::TypeName.parse("Object"), decl.old_name
1608+
assert_equal "Alias for the standard Object class", decl.leading_comment.as_comment.string
1609+
end
1610+
end
1611+
1612+
def test_parse__module_alias_without_type_name
1613+
result = parse(<<~RUBY)
1614+
MyKernel = Kernel #: module-alias
1615+
RUBY
1616+
1617+
assert_empty result.diagnostics
1618+
1619+
result.declarations[0].tap do |decl|
1620+
assert_instance_of RBS::AST::Ruby::Declarations::ClassModuleAliasDecl, decl
1621+
assert_equal RBS::TypeName.parse("MyKernel"), decl.new_name
1622+
assert_equal RBS::TypeName.parse("Kernel"), decl.infered_old_name
1623+
assert_instance_of RBS::AST::Ruby::Annotations::ModuleAliasAnnotation, decl.annotation
1624+
assert_nil decl.annotation.type_name
1625+
assert_equal RBS::TypeName.parse("Kernel"), decl.old_name
1626+
assert_nil decl.leading_comment
1627+
end
1628+
end
1629+
1630+
def test_parse__module_alias_with_type_name
1631+
result = parse(<<~RUBY)
1632+
# Alias for Enumerable module
1633+
MyEnum = some_enumerable #: module-alias Enumerable
1634+
RUBY
1635+
1636+
assert_empty result.diagnostics
1637+
1638+
result.declarations[0].tap do |decl|
1639+
assert_instance_of RBS::AST::Ruby::Declarations::ClassModuleAliasDecl, decl
1640+
assert_equal RBS::TypeName.parse("MyEnum"), decl.new_name
1641+
assert_nil decl.infered_old_name
1642+
assert_instance_of RBS::AST::Ruby::Annotations::ModuleAliasAnnotation, decl.annotation
1643+
assert_equal RBS::TypeName.parse("Enumerable"), decl.annotation.type_name
1644+
assert_equal RBS::TypeName.parse("Enumerable"), decl.old_name
1645+
assert_equal "Alias for Enumerable module", decl.leading_comment.as_comment.string
1646+
end
1647+
end
1648+
1649+
def test_parse__class_alias_with_skip_annotation
1650+
result = parse(<<~RUBY)
1651+
# @rbs skip
1652+
MyString = String #: class-alias
1653+
RUBY
1654+
1655+
assert_empty result.diagnostics
1656+
1657+
assert_equal 0, result.declarations.size
1658+
end
1659+
1660+
def test_error__class_alias_non_constant_without_type_name
1661+
result = parse(<<~RUBY)
1662+
MyString = some_function() #: class-alias
1663+
RUBY
1664+
1665+
assert_equal 1, result.diagnostics.size
1666+
assert_any!(result.diagnostics) do |diagnostic|
1667+
assert_instance_of RBS::InlineParser::Diagnostic::ClassModuleAliasDeclarationMissingTypeName, diagnostic
1668+
assert_equal ": class-alias", diagnostic.location.source
1669+
assert_equal "Class name is missing in class alias declaration", diagnostic.message
1670+
end
1671+
1672+
assert_empty result.declarations
1673+
end
1674+
1675+
def test_error__module_alias_non_constant_without_type_name
1676+
result = parse(<<~RUBY)
1677+
MyModule = some_function() #: module-alias
1678+
RUBY
1679+
1680+
assert_equal 1, result.diagnostics.size
1681+
assert_any!(result.diagnostics) do |diagnostic|
1682+
assert_instance_of RBS::InlineParser::Diagnostic::ClassModuleAliasDeclarationMissingTypeName, diagnostic
1683+
assert_equal ": module-alias", diagnostic.location.source
1684+
assert_equal "Module name is missing in module alias declaration", diagnostic.message
1685+
end
1686+
1687+
assert_empty result.declarations
1688+
end
15741689
end

0 commit comments

Comments
 (0)