diff --git a/ext/rbs_extension/constants.c b/ext/rbs_extension/constants.c index 57ec73ee3..0e21a1860 100644 --- a/ext/rbs_extension/constants.c +++ b/ext/rbs_extension/constants.c @@ -61,6 +61,7 @@ VALUE RBS_Types_ClassInstance; VALUE RBS_Types_ClassSingleton; VALUE RBS_Types_Function_Param; VALUE RBS_Types_Function; +VALUE RBS_Types_UntypedFunction; VALUE RBS_Types_Interface; VALUE RBS_Types_Intersection; VALUE RBS_Types_Literal; @@ -138,6 +139,7 @@ void rbs__init_constants(void) { IMPORT_CONSTANT(RBS_Types_ClassInstance, RBS_Types, "ClassInstance"); IMPORT_CONSTANT(RBS_Types_ClassSingleton, RBS_Types, "ClassSingleton"); IMPORT_CONSTANT(RBS_Types_Function, RBS_Types, "Function"); + IMPORT_CONSTANT(RBS_Types_UntypedFunction, RBS_Types, "UntypedFunction"); IMPORT_CONSTANT(RBS_Types_Function_Param, RBS_Types_Function, "Param"); IMPORT_CONSTANT(RBS_Types_Interface, RBS_Types, "Interface"); IMPORT_CONSTANT(RBS_Types_Intersection, RBS_Types, "Intersection"); diff --git a/ext/rbs_extension/constants.h b/ext/rbs_extension/constants.h index 978bc32d0..2d1ca63bf 100644 --- a/ext/rbs_extension/constants.h +++ b/ext/rbs_extension/constants.h @@ -64,6 +64,7 @@ extern VALUE RBS_Types_ClassInstance; extern VALUE RBS_Types_ClassSingleton; extern VALUE RBS_Types_Function_Param; extern VALUE RBS_Types_Function; +extern VALUE RBS_Types_UntypedFunction; extern VALUE RBS_Types_Interface; extern VALUE RBS_Types_Intersection; extern VALUE RBS_Types_Literal; diff --git a/ext/rbs_extension/parser.c b/ext/rbs_extension/parser.c index f040a1e86..04fc79a80 100644 --- a/ext/rbs_extension/parser.c +++ b/ext/rbs_extension/parser.c @@ -52,6 +52,10 @@ typedef struct { VALUE rest_keywords; } method_params; +static bool rbs_is_untyped_params(method_params *params) { + return NIL_P(params->required_positionals); +} + // /** // * Returns RBS::Location object of `current_token` of a parser state. // * @@ -362,6 +366,7 @@ static bool is_keyword(parserstate *state) { /* params ::= {} `)` + | {} `?` `)` -- Untyped function params (assign params.required = nil) | `)` | `,` `)` @@ -389,6 +394,11 @@ static bool is_keyword(parserstate *state) { | {} `**` */ static void parse_params(parserstate *state, method_params *params) { + if (state->next_token.type == pQUESTION && state->next_token2.type == pRPAREN) { + params->required_positionals = Qnil; + parser_advance(state); + return; + } if (state->next_token.type == pRPAREN) { return; } @@ -613,6 +623,13 @@ static void parse_function(parserstate *state, VALUE *function, VALUE *block, VA parser_advance_assert(state, pRPAREN); } + // Untyped method parameter means it cannot have block + if (rbs_is_untyped_params(¶ms)) { + if (state->next_token.type != pARROW) { + raise_syntax_error(state, state->next_token2, "A method type with untyped method parameter cannot have block"); + } + } + // Passing NULL to function_self_type means the function itself doesn't accept self type binding. (== method type) if (function_self_type) { *function_self_type = parse_self_type_binding(state); @@ -641,8 +658,11 @@ static void parse_function(parserstate *state, VALUE *function, VALUE *block, VA parser_advance_assert(state, pARROW); VALUE block_return_type = parse_optional(state); - *block = rbs_block( - rbs_function( + VALUE block_function = Qnil; + if (rbs_is_untyped_params(&block_params)) { + block_function = rbs_untyped_function(block_return_type); + } else { + block_function = rbs_function( block_params.required_positionals, block_params.optional_positionals, block_params.rest_positionals, @@ -651,10 +671,10 @@ static void parse_function(parserstate *state, VALUE *function, VALUE *block, VA block_params.optional_keywords, block_params.rest_keywords, block_return_type - ), - required, - block_self_type - ); + ); + } + + *block = rbs_block(block_function, required, block_self_type); parser_advance_assert(state, pRBRACE); } @@ -662,16 +682,20 @@ static void parse_function(parserstate *state, VALUE *function, VALUE *block, VA parser_advance_assert(state, pARROW); VALUE type = parse_optional(state); - *function = rbs_function( - params.required_positionals, - params.optional_positionals, - params.rest_positionals, - params.trailing_positionals, - params.required_keywords, - params.optional_keywords, - params.rest_keywords, - type - ); + if (rbs_is_untyped_params(¶ms)) { + *function = rbs_untyped_function(type); + } else { + *function = rbs_function( + params.required_positionals, + params.optional_positionals, + params.rest_positionals, + params.trailing_positionals, + params.required_keywords, + params.optional_keywords, + params.rest_keywords, + type + ); + } } /* diff --git a/ext/rbs_extension/ruby_objs.c b/ext/rbs_extension/ruby_objs.c index 2a92d1c56..0e9760ebb 100644 --- a/ext/rbs_extension/ruby_objs.c +++ b/ext/rbs_extension/ruby_objs.c @@ -170,6 +170,17 @@ VALUE rbs_function_param(VALUE type, VALUE name, VALUE location) { ); } +VALUE rbs_untyped_function(VALUE return_type) { + VALUE args = rb_hash_new(); + rb_hash_aset(args, ID2SYM(rb_intern("return_type")), return_type); + + return CLASS_NEW_INSTANCE( + RBS_Types_UntypedFunction, + 1, + &args + ); +} + VALUE rbs_function( VALUE required_positional_params, VALUE optional_positional_params, diff --git a/ext/rbs_extension/ruby_objs.h b/ext/rbs_extension/ruby_objs.h index d9ee32262..bf5ea0763 100644 --- a/ext/rbs_extension/ruby_objs.h +++ b/ext/rbs_extension/ruby_objs.h @@ -30,6 +30,7 @@ VALUE rbs_class_instance(VALUE typename, VALUE type_args, VALUE location); VALUE rbs_class_singleton(VALUE typename, VALUE location); VALUE rbs_function_param(VALUE type, VALUE name, VALUE location); VALUE rbs_function(VALUE required_positional_params, VALUE optional_positional_params, VALUE rest_positional_params, VALUE trailing_positional_params, VALUE required_keywords, VALUE optional_keywords, VALUE rest_keywords, VALUE return_type); +VALUE rbs_untyped_function(VALUE return_type); VALUE rbs_interface(VALUE typename, VALUE type_args, VALUE location); VALUE rbs_intersection(VALUE types, VALUE location); VALUE rbs_literal(VALUE literal, VALUE location); diff --git a/test/rbs/parser_test.rb b/test/rbs/parser_test.rb index ecd5b6c01..a29d992ac 100644 --- a/test/rbs/parser_test.rb +++ b/test/rbs/parser_test.rb @@ -751,4 +751,20 @@ def test_parse_require_eof RBS::Parser.parse_method_type("() -> void () -> void", range: 0..., require_eof: true) end end + + def test_proc__untyped_function + RBS::Parser.parse_type("^(?) -> Integer").tap do |type| + assert_instance_of RBS::Types::UntypedFunction, type.type + end + + RBS::Parser.parse_type("^() { (?) -> String } -> Integer").tap do |type| + assert_instance_of RBS::Types::UntypedFunction, type.block.type + end + end + + def test_proc__untyped_function_parse_error + assert_raises(RBS::ParsingError) do + RBS::Parser.parse_type("^(?) { (?) -> void } -> Integer") + end + end end