From 3aea23c066c409085d444f2cba5815ac459c7fbb Mon Sep 17 00:00:00 2001 From: ksss Date: Wed, 6 Aug 2025 11:10:45 +0900 Subject: [PATCH 01/70] class/instance type is rejected depending on the context This is a Follow-up to https://github.com/ruby/rbs/pull/2590 --- Rakefile | 4 +- ext/rbs_extension/main.c | 13 +- include/rbs/parser.h | 4 +- lib/rbs/cli/validate.rb | 65 +----- lib/rbs/errors.rb | 13 +- lib/rbs/parser_aux.rb | 4 +- sig/cli/validate.rbs | 7 +- sig/errors.rbs | 8 - sig/parser.rbs | 4 +- src/parser.c | 151 ++++++------ test/rbs/cli_test.rb | 90 ------- test/rbs/inline_annotation_parsing_test.rb | 30 ++- test/rbs/signature_parsing_test.rb | 259 +++++++++++++++++++++ 13 files changed, 385 insertions(+), 267 deletions(-) diff --git a/Rakefile b/Rakefile index 2a9541de83..0f45dcb987 100644 --- a/Rakefile +++ b/Rakefile @@ -187,7 +187,7 @@ end task :validate => :compile do require 'yaml' - sh "#{ruby} #{rbs} validate --exit-error-on-syntax-error" + sh "#{ruby} #{rbs} validate" libs = FileList["stdlib/*"].map {|path| File.basename(path).to_s } @@ -208,7 +208,7 @@ task :validate => :compile do end libs.each do |lib| - sh "#{ruby} #{rbs} -r #{lib} validate --exit-error-on-syntax-error" + sh "#{ruby} #{rbs} -r #{lib} validate" end end diff --git a/ext/rbs_extension/main.c b/ext/rbs_extension/main.c index 766a6cc06b..9b296433dd 100644 --- a/ext/rbs_extension/main.c +++ b/ext/rbs_extension/main.c @@ -87,6 +87,7 @@ struct parse_type_arg { VALUE require_eof; VALUE void_allowed; VALUE self_allowed; + VALUE classish_allowed; }; struct parse_method_type_arg { @@ -118,9 +119,10 @@ static VALUE parse_type_try(VALUE a) { bool void_allowed = RTEST(arg->void_allowed); bool self_allowed = RTEST(arg->self_allowed); + bool classish_allowed = RTEST(arg->classish_allowed); rbs_node_t *type; - rbs_parse_type(parser, &type, void_allowed, self_allowed); + rbs_parse_type(parser, &type, void_allowed, self_allowed, classish_allowed); raise_error_if_any(parser, arg->buffer); @@ -176,7 +178,7 @@ static rbs_parser_t *alloc_parser_from_buffer(VALUE buffer, int start_pos, int e ); } -static VALUE rbsparser_parse_type(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos, VALUE variables, VALUE require_eof, VALUE void_allowed, VALUE self_allowed) { +static VALUE rbsparser_parse_type(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos, VALUE variables, VALUE require_eof, VALUE void_allowed, VALUE self_allowed, VALUE classish_allowed) { VALUE string = rb_funcall(buffer, rb_intern("content"), 0); StringValue(string); rb_encoding *encoding = rb_enc_get(string); @@ -189,7 +191,8 @@ static VALUE rbsparser_parse_type(VALUE self, VALUE buffer, VALUE start_pos, VAL .parser = parser, .require_eof = require_eof, .void_allowed = void_allowed, - .self_allowed = self_allowed + .self_allowed = self_allowed, + .classish_allowed = classish_allowed }; VALUE result = rb_ensure(parse_type_try, (VALUE) &arg, ensure_free_parser, (VALUE) parser); @@ -208,7 +211,7 @@ static VALUE parse_method_type_try(VALUE a) { } rbs_method_type_t *method_type = NULL; - rbs_parse_method_type(parser, &method_type); + rbs_parse_method_type(parser, &method_type, true); raise_error_if_any(parser, arg->buffer); @@ -453,7 +456,7 @@ void rbs__init_parser(void) { VALUE empty_array = rb_obj_freeze(rb_ary_new()); rb_gc_register_mark_object(empty_array); - rb_define_singleton_method(RBS_Parser, "_parse_type", rbsparser_parse_type, 7); + rb_define_singleton_method(RBS_Parser, "_parse_type", rbsparser_parse_type, 8); rb_define_singleton_method(RBS_Parser, "_parse_method_type", rbsparser_parse_method_type, 5); rb_define_singleton_method(RBS_Parser, "_parse_signature", rbsparser_parse_signature, 3); rb_define_singleton_method(RBS_Parser, "_parse_type_params", rbsparser_parse_type_params, 4); diff --git a/include/rbs/parser.h b/include/rbs/parser.h index 339235deb8..1c46b5c06b 100644 --- a/include/rbs/parser.h +++ b/include/rbs/parser.h @@ -126,8 +126,8 @@ rbs_ast_comment_t *rbs_parser_get_comment(rbs_parser_t *parser, int subject_line void rbs_parser_set_error(rbs_parser_t *parser, rbs_token_t tok, bool syntax_error, const char *fmt, ...) RBS_ATTRIBUTE_FORMAT(4, 5); -bool rbs_parse_type(rbs_parser_t *parser, rbs_node_t **type, bool void_allowed, bool self_allowed); -bool rbs_parse_method_type(rbs_parser_t *parser, rbs_method_type_t **method_type); +bool rbs_parse_type(rbs_parser_t *parser, rbs_node_t **type, bool void_allowed, bool self_allowed, bool classish_allowed); +bool rbs_parse_method_type(rbs_parser_t *parser, rbs_method_type_t **method_type, bool classish_allowed); bool rbs_parse_signature(rbs_parser_t *parser, rbs_signature_t **signature); bool rbs_parse_type_params(rbs_parser_t *parser, bool module_type_params, rbs_node_list_t **params); diff --git a/lib/rbs/cli/validate.rb b/lib/rbs/cli/validate.rb index 42be20eeee..8dc66fbda3 100644 --- a/lib/rbs/cli/validate.rb +++ b/lib/rbs/cli/validate.rb @@ -4,20 +4,13 @@ module RBS class CLI class Validate class Errors - def initialize(limit:, exit_error:) + def initialize(limit:) @limit = limit - @exit_error = exit_error @errors = [] - @has_syntax_error = false end def add(error) - if error.instance_of?(WillSyntaxError) - RBS.logger.warn(build_message(error)) - @has_syntax_error = true - else - @errors << error - end + @errors << error finish if @limit == 1 end @@ -30,13 +23,7 @@ def try(&block) end def finish - if @errors.empty? - if @exit_error && @has_syntax_error - throw @tag, 1 - else - # success - end - else + unless @errors.empty? @errors.each do |error| RBS.logger.error(build_message(error)) end @@ -63,7 +50,6 @@ def initialize(args:, options:) @env = Environment.from_loader(loader).resolve_type_names @builder = DefinitionBuilder.new(env: @env) @validator = Validator.new(env: @env) - exit_error = false limit = nil #: Integer? OptionParser.new do |opts| opts.banner = < error @@ -266,7 +241,6 @@ def validate_constant RBS.logger.info "Validating constant: `#{name}`..." @validator.validate_type const.decl.type, context: const.context @builder.ensure_namespace!(name.namespace, location: const.decl.location) - no_classish_type_validator(const.decl.type) rescue BaseError => error @errors.add(error) end @@ -276,7 +250,6 @@ def validate_global @env.global_decls.each do |name, global| RBS.logger.info "Validating global: `#{name}`..." @validator.validate_type global.decl.type, context: nil - no_classish_type_validator(global.decl.type) rescue BaseError => error @errors.add(error) end @@ -299,51 +272,23 @@ def validate_type_alias decl.decl.type_params.each do |param| if ub = param.upper_bound_type - no_classish_type_validator(ub) @validator.validate_type(ub, context: nil) end if lb = param.lower_bound_type - no_classish_type_validator(lb) @validator.validate_type(lb, context: nil) end if dt = param.default_type - no_classish_type_validator(dt) @validator.validate_type(dt, context: nil) end end TypeParamDefaultReferenceError.check!(decl.decl.type_params) - - no_classish_type_validator(decl.decl.type) rescue BaseError => error @errors.add(error) end end - - private - - def no_self_type_validator(type) - if type.has_self_type? - @errors.add WillSyntaxError.new("`self` type is not allowed in this context", location: type.location) - end - end - - def no_classish_type_validator(type) - if type.has_classish_type? - @errors.add WillSyntaxError.new("`instance` or `class` type is not allowed in this context", location: type.location) - end - end - - def void_type_context_validator(type, allowed_here = false) - if allowed_here - return if type.is_a?(Types::Bases::Void) - end - if type.with_nonreturn_void? # steep:ignore DeprecatedReference - @errors.add WillSyntaxError.new("`void` type is only allowed in return type or generics parameter", location: type.location) - end - end end end end diff --git a/lib/rbs/errors.rb b/lib/rbs/errors.rb index e272755826..48cb1a7c85 100644 --- a/lib/rbs/errors.rb +++ b/lib/rbs/errors.rb @@ -491,7 +491,7 @@ def self.check!(type_name:, env:, member:) else raise "Unknown member type: #{member.class}" end - + if env.class_decl?(name) raise new(type_name: type_name, member: member) end @@ -611,17 +611,6 @@ def location end end - class WillSyntaxError < DefinitionError - include DetailedMessageable - - attr_reader :location - - def initialize(message, location:) - super "#{Location.to_string(location)}: #{message}" - @location = location - end - end - class TypeParamDefaultReferenceError < DefinitionError include DetailedMessageable diff --git a/lib/rbs/parser_aux.rb b/lib/rbs/parser_aux.rb index 97b74f73cb..1607eb922b 100644 --- a/lib/rbs/parser_aux.rb +++ b/lib/rbs/parser_aux.rb @@ -5,9 +5,9 @@ module RBS class Parser - def self.parse_type(source, range: 0..., variables: [], require_eof: false, void_allowed: true, self_allowed: true) + def self.parse_type(source, range: 0..., variables: [], require_eof: false, void_allowed: true, self_allowed: true, classish_allowed: true) buf = buffer(source) - _parse_type(buf, range.begin || 0, range.end || buf.last_position, variables, require_eof, void_allowed, self_allowed) + _parse_type(buf, range.begin || 0, range.end || buf.last_position, variables, require_eof, void_allowed, self_allowed, classish_allowed) end def self.parse_method_type(source, range: 0..., variables: [], require_eof: false) diff --git a/sig/cli/validate.rbs b/sig/cli/validate.rbs index 338a609b18..e2f0b77d63 100644 --- a/sig/cli/validate.rbs +++ b/sig/cli/validate.rbs @@ -3,14 +3,12 @@ module RBS class Validate class Errors @limit: Integer? - @exit_error: boolish - @has_syntax_error: bool @errors: Array[BaseError] # The tag that will be thrown in #finish method @tag: top - def initialize: (limit: Integer?, exit_error: boolish) -> void + def initialize: (limit: Integer?) -> void def add: (BaseError) -> void @@ -44,9 +42,6 @@ module RBS def validate_constant: () -> void def validate_global: () -> void def validate_type_alias: () -> void - def no_classish_type_validator: (::RBS::Types::t | ::RBS::MethodType type) -> void - def no_self_type_validator: (::RBS::Types::t | ::RBS::MethodType type) -> void - %a{deprecated} def void_type_context_validator: (::RBS::Types::t | ::RBS::MethodType type, ?bool allowed_here) -> void end end end diff --git a/sig/errors.rbs b/sig/errors.rbs index cab03130dd..c2c716ade6 100644 --- a/sig/errors.rbs +++ b/sig/errors.rbs @@ -394,14 +394,6 @@ module RBS def location: () -> AST::Declarations::AliasDecl::loc? end - class WillSyntaxError < BaseError - include RBS::DetailedMessageable - - def initialize: (String message, location: Location[untyped, untyped]?) -> void - - attr_reader location: Location[untyped, untyped]? - end - class TypeParamDefaultReferenceError < BaseError include DetailedMessageable diff --git a/sig/parser.rbs b/sig/parser.rbs index 54f5d3ea52..028dc8df10 100644 --- a/sig/parser.rbs +++ b/sig/parser.rbs @@ -76,7 +76,7 @@ module RBS # RBS::Parser.parse_type("self", self_allowed: false) # => Raises an syntax error # ``` # - def self.parse_type: (Buffer | String, ?range: Range[Integer?], ?variables: Array[Symbol], ?require_eof: bool, ?void_allowed: bool, ?self_allowed: bool) -> Types::t? + def self.parse_type: (Buffer | String, ?range: Range[Integer?], ?variables: Array[Symbol], ?require_eof: bool, ?void_allowed: bool, ?self_allowed: bool, ?classish_allowed: bool) -> Types::t? # Parse whole RBS file and return an array of declarations # @@ -130,7 +130,7 @@ module RBS def self.buffer: (String | Buffer source) -> Buffer - def self._parse_type: (Buffer, Integer start_pos, Integer end_pos, Array[Symbol] variables, bool require_eof, bool void_allowed, bool self_allowed) -> Types::t? + def self._parse_type: (Buffer, Integer start_pos, Integer end_pos, Array[Symbol] variables, bool require_eof, bool void_allowed, bool self_allowed, bool classish_allowed) -> Types::t? def self._parse_method_type: (Buffer, Integer start_pos, Integer end_pos, Array[Symbol] variables, bool require_eof) -> MethodType? diff --git a/src/parser.c b/src/parser.c index 4719448ce0..116a886ec8 100644 --- a/src/parser.c +++ b/src/parser.c @@ -120,8 +120,8 @@ static rbs_location_t *rbs_location_current_token(rbs_parser_t *parser) { return rbs_location_new(ALLOCATOR(), parser->current_token.range); } -static bool parse_optional(rbs_parser_t *parser, rbs_node_t **optional, bool void_allowed, bool self_allowed); -static bool parse_simple(rbs_parser_t *parser, rbs_node_t **type, bool void_allowed, bool self_allowed); +static bool parse_optional(rbs_parser_t *parser, rbs_node_t **optional, bool void_allowed, bool self_allowed, bool classish_allowed); +static bool parse_simple(rbs_parser_t *parser, rbs_node_t **type, bool void_allowed, bool self_allowed, bool classish_allowed); /** * @returns A borrowed copy of the current token, which does *not* need to be freed. @@ -244,10 +244,10 @@ error_handling: { | {} type `,` ... `,` eol */ NODISCARD -static bool parse_type_list(rbs_parser_t *parser, enum RBSTokenType eol, rbs_node_list_t *types, bool void_allowed, bool self_allowed) { +static bool parse_type_list(rbs_parser_t *parser, enum RBSTokenType eol, rbs_node_list_t *types, bool void_allowed, bool self_allowed, bool classish_allowed) { while (true) { rbs_node_t *type; - CHECK_PARSE(rbs_parse_type(parser, &type, void_allowed, self_allowed)); + CHECK_PARSE(rbs_parse_type(parser, &type, void_allowed, self_allowed, classish_allowed)); rbs_node_list_append(types, type); if (parser->next_token.type == pCOMMA) { @@ -274,10 +274,10 @@ static bool parse_type_list(rbs_parser_t *parser, enum RBSTokenType eol, rbs_nod | {} type `,` ... `,` eol */ NODISCARD -static bool parse_type_list_with_commas(rbs_parser_t *parser, enum RBSTokenType eol, rbs_node_list_t *types, rbs_location_list_t *comma_locations, bool void_allowed, bool self_allowed) { +static bool parse_type_list_with_commas(rbs_parser_t *parser, enum RBSTokenType eol, rbs_node_list_t *types, rbs_location_list_t *comma_locations, bool void_allowed, bool self_allowed, bool classish_allowed) { while (true) { rbs_node_t *type; - CHECK_PARSE(rbs_parse_type(parser, &type, void_allowed, self_allowed)); + CHECK_PARSE(rbs_parse_type(parser, &type, void_allowed, self_allowed, classish_allowed)); rbs_node_list_append(types, type); if (parser->next_token.type == pCOMMA) { @@ -326,11 +326,11 @@ static bool is_keyword_token(enum RBSTokenType type) { | {} type */ NODISCARD -static bool parse_function_param(rbs_parser_t *parser, rbs_types_function_param_t **function_param, bool self_allowed) { +static bool parse_function_param(rbs_parser_t *parser, rbs_types_function_param_t **function_param, bool self_allowed, bool classish_allowed) { rbs_range_t type_range; type_range.start = parser->next_token.range.start; rbs_node_t *type; - CHECK_PARSE(rbs_parse_type(parser, &type, false, self_allowed)); + CHECK_PARSE(rbs_parse_type(parser, &type, false, self_allowed, classish_allowed)); type_range.end = parser->current_token.range.end; if (parser->next_token.type == pCOMMA || parser->next_token.type == pRPAREN) { @@ -409,7 +409,7 @@ static bool parse_keyword_key(rbs_parser_t *parser, rbs_ast_symbol_t **key) { keyword ::= {} keyword `:` */ NODISCARD -static bool parse_keyword(rbs_parser_t *parser, rbs_hash_t *keywords, rbs_hash_t *memo, bool self_allowed) { +static bool parse_keyword(rbs_parser_t *parser, rbs_hash_t *keywords, rbs_hash_t *memo, bool self_allowed, bool classish_allowed) { rbs_ast_symbol_t *key = NULL; CHECK_PARSE(parse_keyword_key(parser, &key)); @@ -423,7 +423,7 @@ static bool parse_keyword(rbs_parser_t *parser, rbs_hash_t *keywords, rbs_hash_t ADVANCE_ASSERT(parser, pCOLON); rbs_types_function_param_t *param = NULL; - CHECK_PARSE(parse_function_param(parser, ¶m, self_allowed)); + CHECK_PARSE(parse_function_param(parser, ¶m, self_allowed, classish_allowed)); rbs_hash_set(keywords, (rbs_node_t *) key, (rbs_node_t *) param); @@ -494,7 +494,7 @@ static bool parser_advance_if(rbs_parser_t *parser, enum RBSTokenType type) { | {} `**` */ NODISCARD -static bool parse_params(rbs_parser_t *parser, method_params *params, bool self_allowed) { +static bool parse_params(rbs_parser_t *parser, method_params *params, bool self_allowed, bool classish_allowed) { if (parser->next_token.type == pQUESTION && parser->next_token2.type == pRPAREN) { params->required_positionals = NULL; rbs_parser_advance(parser); @@ -523,7 +523,7 @@ static bool parse_params(rbs_parser_t *parser, method_params *params, bool self_ } rbs_types_function_param_t *param = NULL; - CHECK_PARSE(parse_function_param(parser, ¶m, self_allowed)); + CHECK_PARSE(parse_function_param(parser, ¶m, self_allowed, classish_allowed)); rbs_node_list_append(params->required_positionals, (rbs_node_t *) param); break; @@ -541,13 +541,13 @@ static bool parse_params(rbs_parser_t *parser, method_params *params, bool self_ rbs_parser_advance(parser); if (is_keyword(parser)) { - CHECK_PARSE(parse_keyword(parser, params->optional_keywords, memo, self_allowed)); + CHECK_PARSE(parse_keyword(parser, params->optional_keywords, memo, self_allowed, classish_allowed)); parser_advance_if(parser, pCOMMA); goto PARSE_KEYWORDS; } rbs_types_function_param_t *param = NULL; - CHECK_PARSE(parse_function_param(parser, ¶m, self_allowed)); + CHECK_PARSE(parse_function_param(parser, ¶m, self_allowed, classish_allowed)); rbs_node_list_append(params->optional_positionals, (rbs_node_t *) param); break; @@ -564,7 +564,7 @@ static bool parse_params(rbs_parser_t *parser, method_params *params, bool self_ if (parser->next_token.type == pSTAR) { rbs_parser_advance(parser); rbs_types_function_param_t *param = NULL; - CHECK_PARSE(parse_function_param(parser, ¶m, self_allowed)); + CHECK_PARSE(parse_function_param(parser, ¶m, self_allowed, classish_allowed)); params->rest_positionals = (rbs_node_t *) param; if (!parser_advance_if(parser, pCOMMA)) { @@ -591,7 +591,7 @@ static bool parse_params(rbs_parser_t *parser, method_params *params, bool self_ } rbs_types_function_param_t *param = NULL; - CHECK_PARSE(parse_function_param(parser, ¶m, self_allowed)); + CHECK_PARSE(parse_function_param(parser, ¶m, self_allowed, classish_allowed)); rbs_node_list_append(params->trailing_positionals, (rbs_node_t *) param); break; @@ -608,7 +608,7 @@ static bool parse_params(rbs_parser_t *parser, method_params *params, bool self_ case pQUESTION: rbs_parser_advance(parser); if (is_keyword(parser)) { - CHECK_PARSE(parse_keyword(parser, params->optional_keywords, memo, self_allowed)); + CHECK_PARSE(parse_keyword(parser, params->optional_keywords, memo, self_allowed, classish_allowed)); } else { rbs_parser_set_error(parser, parser->next_token, true, "optional keyword argument type is expected"); return false; @@ -618,7 +618,7 @@ static bool parse_params(rbs_parser_t *parser, method_params *params, bool self_ case pSTAR2: rbs_parser_advance(parser); rbs_types_function_param_t *param = NULL; - CHECK_PARSE(parse_function_param(parser, ¶m, self_allowed)); + CHECK_PARSE(parse_function_param(parser, ¶m, self_allowed, classish_allowed)); params->rest_keywords = (rbs_node_t *) param; break; @@ -630,7 +630,7 @@ static bool parse_params(rbs_parser_t *parser, method_params *params, bool self_ case tBANGIDENT: KEYWORD_CASES if (is_keyword(parser)) { - CHECK_PARSE(parse_keyword(parser, params->required_keywords, memo, self_allowed)); + CHECK_PARSE(parse_keyword(parser, params->required_keywords, memo, self_allowed, classish_allowed)); } else { rbs_parser_set_error(parser, parser->next_token, true, "required keyword argument type is expected"); return false; @@ -660,12 +660,12 @@ static bool parse_params(rbs_parser_t *parser, method_params *params, bool self_ | {} simple_type <`?`> */ NODISCARD -static bool parse_optional(rbs_parser_t *parser, rbs_node_t **optional, bool void_allowed, bool self_allowed) { +static bool parse_optional(rbs_parser_t *parser, rbs_node_t **optional, bool void_allowed, bool self_allowed, bool classish_allowed) { rbs_range_t rg; rg.start = parser->next_token.range.start; rbs_node_t *type = NULL; - CHECK_PARSE(parse_simple(parser, &type, void_allowed, self_allowed)); + CHECK_PARSE(parse_simple(parser, &type, void_allowed, self_allowed, classish_allowed)); if (parser->next_token.type == pQUESTION) { if (void_allowed && type->type == RBS_TYPES_BASES_VOID) { @@ -701,13 +701,13 @@ static void initialize_method_params(method_params *params, rbs_allocator_t *all | {} `[` `self` `:` type <`]`> */ NODISCARD -static bool parse_self_type_binding(rbs_parser_t *parser, rbs_node_t **self_type, bool self_allowed) { +static bool parse_self_type_binding(rbs_parser_t *parser, rbs_node_t **self_type, bool self_allowed, bool classish_allowed) { if (parser->next_token.type == pLBRACKET) { rbs_parser_advance(parser); ADVANCE_ASSERT(parser, kSELF); ADVANCE_ASSERT(parser, pCOLON); rbs_node_t *type; - CHECK_PARSE(rbs_parse_type(parser, &type, false, self_allowed)); + CHECK_PARSE(rbs_parse_type(parser, &type, false, self_allowed, classish_allowed)); ADVANCE_ASSERT(parser, pRBRACKET); *self_type = type; } @@ -729,7 +729,7 @@ typedef struct { | {} self_type_binding? `->` */ NODISCARD -static bool parse_function(rbs_parser_t *parser, bool accept_type_binding, parse_function_result **result, bool self_allowed) { +static bool parse_function(rbs_parser_t *parser, bool accept_type_binding, parse_function_result **result, bool self_allowed, bool classish_allowed) { rbs_node_t *function = NULL; rbs_types_block_t *block = NULL; rbs_node_t *function_self_type = NULL; @@ -741,13 +741,13 @@ static bool parse_function(rbs_parser_t *parser, bool accept_type_binding, parse if (parser->next_token.type == pLPAREN) { rbs_parser_advance(parser); - CHECK_PARSE(parse_params(parser, ¶ms, self_allowed)); + CHECK_PARSE(parse_params(parser, ¶ms, self_allowed, classish_allowed)); ADVANCE_ASSERT(parser, pRPAREN); } // Passing NULL to function_self_type means the function itself doesn't accept self type binding. (== method type) if (accept_type_binding) { - CHECK_PARSE(parse_self_type_binding(parser, &function_self_type, self_allowed)); + CHECK_PARSE(parse_self_type_binding(parser, &function_self_type, self_allowed, classish_allowed)); } else { if (rbs_is_untyped_params(¶ms)) { if (parser->next_token.type != pARROW) { @@ -777,16 +777,16 @@ static bool parse_function(rbs_parser_t *parser, bool accept_type_binding, parse if (parser->next_token.type == pLPAREN) { rbs_parser_advance(parser); - CHECK_PARSE(parse_params(parser, &block_params, self_allowed)); + CHECK_PARSE(parse_params(parser, &block_params, self_allowed, classish_allowed)); ADVANCE_ASSERT(parser, pRPAREN); } rbs_node_t *self_type = NULL; - CHECK_PARSE(parse_self_type_binding(parser, &self_type, self_allowed)); + CHECK_PARSE(parse_self_type_binding(parser, &self_type, self_allowed, classish_allowed)); ADVANCE_ASSERT(parser, pARROW); rbs_node_t *block_return_type = NULL; - CHECK_PARSE(parse_optional(parser, &block_return_type, true, self_allowed)); + CHECK_PARSE(parse_optional(parser, &block_return_type, true, self_allowed, classish_allowed)); ADVANCE_ASSERT(parser, pRBRACE); @@ -816,7 +816,7 @@ static bool parse_function(rbs_parser_t *parser, bool accept_type_binding, parse ADVANCE_ASSERT(parser, pARROW); rbs_node_t *type = NULL; - CHECK_PARSE(parse_optional(parser, &type, true, self_allowed)); + CHECK_PARSE(parse_optional(parser, &type, true, self_allowed, classish_allowed)); function_range.end = parser->current_token.range.end; rbs_location_t *loc = rbs_location_new(ALLOCATOR(), function_range); @@ -847,10 +847,10 @@ static bool parse_function(rbs_parser_t *parser, bool accept_type_binding, parse proc_type ::= {`^`} */ NODISCARD -static bool parse_proc_type(rbs_parser_t *parser, rbs_types_proc_t **proc, bool self_allowed) { +static bool parse_proc_type(rbs_parser_t *parser, rbs_types_proc_t **proc, bool self_allowed, bool classish_allowed) { rbs_position_t start = parser->current_token.range.start; parse_function_result *result = rbs_allocator_alloc(ALLOCATOR(), parse_function_result); - CHECK_PARSE(parse_function(parser, true, &result, self_allowed)); + CHECK_PARSE(parse_function(parser, true, &result, self_allowed, classish_allowed)); rbs_position_t end = parser->current_token.range.end; rbs_location_t *loc = rbs_location_new(ALLOCATOR(), (rbs_range_t) { .start = start, .end = end }); @@ -875,7 +875,7 @@ static void check_key_duplication(rbs_parser_t *parser, rbs_hash_t *fields, rbs_ | {} literal_type `=>` */ NODISCARD -static bool parse_record_attributes(rbs_parser_t *parser, rbs_hash_t **fields, bool self_allowed) { +static bool parse_record_attributes(rbs_parser_t *parser, rbs_hash_t **fields, bool self_allowed, bool classish_allowed) { *fields = rbs_hash_new(ALLOCATOR()); if (parser->next_token.type == pRBRACE) return true; @@ -908,7 +908,7 @@ static bool parse_record_attributes(rbs_parser_t *parser, rbs_hash_t **fields, b case kTRUE: case kFALSE: { rbs_node_t *type = NULL; - CHECK_PARSE(parse_simple(parser, &type, false, self_allowed)); + CHECK_PARSE(parse_simple(parser, &type, false, self_allowed, classish_allowed)); key = (rbs_ast_symbol_t *) ((rbs_types_literal_t *) type)->literal; break; @@ -925,7 +925,7 @@ static bool parse_record_attributes(rbs_parser_t *parser, rbs_hash_t **fields, b field_range.start = parser->current_token.range.end; rbs_node_t *type; - CHECK_PARSE(rbs_parse_type(parser, &type, false, self_allowed)); + CHECK_PARSE(rbs_parse_type(parser, &type, false, self_allowed, classish_allowed)); field_range.end = parser->current_token.range.end; rbs_location_t *loc = rbs_location_new(ALLOCATOR(), field_range); @@ -1032,7 +1032,7 @@ static bool parse_instance_type(rbs_parser_t *parser, bool parse_alias, rbs_node if (parser->next_token.type == pLBRACKET) { rbs_parser_advance(parser); args_range.start = parser->current_token.range.start; - CHECK_PARSE(parse_type_list(parser, pRBRACKET, types, true, true)); + CHECK_PARSE(parse_type_list(parser, pRBRACKET, types, true, true, true)); ADVANCE_ASSERT(parser, pRBRACKET); args_range.end = parser->current_token.range.end; } else { @@ -1119,13 +1119,13 @@ static bool parser_typevar_member(rbs_parser_t *parser, rbs_constant_id_t id) { | {} `^` */ NODISCARD -static bool parse_simple(rbs_parser_t *parser, rbs_node_t **type, bool void_allowed, bool self_allowed) { +static bool parse_simple(rbs_parser_t *parser, rbs_node_t **type, bool void_allowed, bool self_allowed, bool classish_allowed) { rbs_parser_advance(parser); switch (parser->current_token.type) { case pLPAREN: { rbs_node_t *lparen_type; - CHECK_PARSE(rbs_parse_type(parser, &lparen_type, false, self_allowed)); + CHECK_PARSE(rbs_parse_type(parser, &lparen_type, false, self_allowed, classish_allowed)); ADVANCE_ASSERT(parser, pRPAREN); *type = lparen_type; return true; @@ -1141,11 +1141,21 @@ static bool parse_simple(rbs_parser_t *parser, rbs_node_t **type, bool void_allo return true; } case kCLASS: { + if (!classish_allowed) { + rbs_parser_set_error(parser, parser->current_token, true, "class type is not allowed here"); + return false; + } + rbs_location_t *loc = rbs_location_current_token(parser); *type = (rbs_node_t *) rbs_types_bases_class_new(ALLOCATOR(), loc); return true; } case kINSTANCE: { + if (!classish_allowed) { + rbs_parser_set_error(parser, parser->current_token, true, "instance type is not allowed here"); + return false; + } + rbs_location_t *loc = rbs_location_current_token(parser); *type = (rbs_node_t *) rbs_types_bases_instance_new(ALLOCATOR(), loc); return true; @@ -1264,7 +1274,7 @@ static bool parse_simple(rbs_parser_t *parser, rbs_node_t **type, bool void_allo rg.start = parser->current_token.range.start; rbs_node_list_t *types = rbs_node_list_new(ALLOCATOR()); if (parser->next_token.type != pRBRACKET) { - CHECK_PARSE(parse_type_list(parser, pRBRACKET, types, false, self_allowed)); + CHECK_PARSE(parse_type_list(parser, pRBRACKET, types, false, self_allowed, classish_allowed)); } ADVANCE_ASSERT(parser, pRBRACKET); rg.end = parser->current_token.range.end; @@ -1282,7 +1292,7 @@ static bool parse_simple(rbs_parser_t *parser, rbs_node_t **type, bool void_allo case pLBRACE: { rbs_position_t start = parser->current_token.range.start; rbs_hash_t *fields = NULL; - CHECK_PARSE(parse_record_attributes(parser, &fields, self_allowed)); + CHECK_PARSE(parse_record_attributes(parser, &fields, self_allowed, classish_allowed)); ADVANCE_ASSERT(parser, pRBRACE); rbs_position_t end = parser->current_token.range.end; rbs_location_t *loc = rbs_location_new(ALLOCATOR(), (rbs_range_t) { .start = start, .end = end }); @@ -1291,7 +1301,7 @@ static bool parse_simple(rbs_parser_t *parser, rbs_node_t **type, bool void_allo } case pHAT: { rbs_types_proc_t *value = NULL; - CHECK_PARSE(parse_proc_type(parser, &value, self_allowed)); + CHECK_PARSE(parse_proc_type(parser, &value, self_allowed, classish_allowed)); *type = (rbs_node_t *) value; return true; } @@ -1306,12 +1316,12 @@ static bool parse_simple(rbs_parser_t *parser, rbs_node_t **type, bool void_allo | {} */ NODISCARD -static bool parse_intersection(rbs_parser_t *parser, rbs_node_t **type, bool void_allowed, bool self_allowed) { +static bool parse_intersection(rbs_parser_t *parser, rbs_node_t **type, bool void_allowed, bool self_allowed, bool classish_allowed) { rbs_range_t rg; rg.start = parser->next_token.range.start; rbs_node_t *optional = NULL; - CHECK_PARSE(parse_optional(parser, &optional, void_allowed, self_allowed)); + CHECK_PARSE(parse_optional(parser, &optional, void_allowed, self_allowed, classish_allowed)); *type = optional; rbs_node_list_t *intersection_types = rbs_node_list_new(ALLOCATOR()); @@ -1325,7 +1335,7 @@ static bool parse_intersection(rbs_parser_t *parser, rbs_node_t **type, bool voi rbs_parser_advance(parser); rbs_node_t *type = NULL; - CHECK_PARSE(parse_optional(parser, &type, false, self_allowed)); + CHECK_PARSE(parse_optional(parser, &type, false, self_allowed, classish_allowed)); rbs_node_list_append(intersection_types, type); } @@ -1343,12 +1353,12 @@ static bool parse_intersection(rbs_parser_t *parser, rbs_node_t **type, bool voi union ::= {} intersection '|' ... '|' | {} */ -bool rbs_parse_type(rbs_parser_t *parser, rbs_node_t **type, bool void_allowed, bool self_allowed) { +bool rbs_parse_type(rbs_parser_t *parser, rbs_node_t **type, bool void_allowed, bool self_allowed, bool classish_allowed) { rbs_range_t rg; rg.start = parser->next_token.range.start; rbs_node_list_t *union_types = rbs_node_list_new(ALLOCATOR()); - CHECK_PARSE(parse_intersection(parser, type, void_allowed, self_allowed)); + CHECK_PARSE(parse_intersection(parser, type, void_allowed, self_allowed, classish_allowed)); rbs_node_list_append(union_types, *type); @@ -1360,7 +1370,7 @@ bool rbs_parse_type(rbs_parser_t *parser, rbs_node_t **type, bool void_allowed, rbs_parser_advance(parser); rbs_node_t *intersection = NULL; - CHECK_PARSE(parse_intersection(parser, &intersection, false, self_allowed)); + CHECK_PARSE(parse_intersection(parser, &intersection, false, self_allowed, classish_allowed)); rbs_node_list_append(union_types, intersection); } @@ -1453,7 +1463,7 @@ static bool parse_type_params(rbs_parser_t *parser, rbs_range_t *rg, bool module rbs_parser_advance(parser); upper_bound_range.start = parser->current_token.range.start; - CHECK_PARSE(rbs_parse_type(parser, &upper_bound, false, false)); + CHECK_PARSE(rbs_parse_type(parser, &upper_bound, false, false, false)); upper_bound_range.end = parser->current_token.range.end; break; @@ -1465,7 +1475,7 @@ static bool parse_type_params(rbs_parser_t *parser, rbs_range_t *rg, bool module rbs_parser_advance(parser); lower_bound_range.start = parser->current_token.range.start; - CHECK_PARSE(rbs_parse_type(parser, &lower_bound, false, false)); + CHECK_PARSE(rbs_parse_type(parser, &lower_bound, false, false, false)); lower_bound_range.end = parser->current_token.range.end; break; @@ -1480,7 +1490,7 @@ static bool parse_type_params(rbs_parser_t *parser, rbs_range_t *rg, bool module rbs_parser_advance(parser); default_type_range.start = parser->current_token.range.start; - CHECK_PARSE(rbs_parse_type(parser, &default_type, true, false)); + CHECK_PARSE(rbs_parse_type(parser, &default_type, true, false, false)); default_type_range.end = parser->current_token.range.end; required_param_allowed = false; @@ -1550,7 +1560,7 @@ static bool parser_pop_typevar_table(rbs_parser_t *parser) { method_type ::= {} type_params */ // TODO: Should this be NODISCARD? -bool rbs_parse_method_type(rbs_parser_t *parser, rbs_method_type_t **method_type) { +bool rbs_parse_method_type(rbs_parser_t *parser, rbs_method_type_t **method_type, bool classish_allowed) { rbs_parser_push_typevar_table(parser, false); rbs_range_t rg; @@ -1564,7 +1574,7 @@ bool rbs_parse_method_type(rbs_parser_t *parser, rbs_method_type_t **method_type type_range.start = parser->next_token.range.start; parse_function_result *result = rbs_allocator_alloc(ALLOCATOR(), parse_function_result); - CHECK_PARSE(parse_function(parser, false, &result, true)); + CHECK_PARSE(parse_function(parser, false, &result, true, classish_allowed)); rg.end = parser->current_token.range.end; type_range.end = rg.end; @@ -1599,7 +1609,7 @@ static bool parse_global_decl(rbs_parser_t *parser, rbs_node_list_t *annotations rbs_range_t colon_range = parser->current_token.range; rbs_node_t *type; - CHECK_PARSE(rbs_parse_type(parser, &type, false, false)); + CHECK_PARSE(rbs_parse_type(parser, &type, false, false, false)); decl_range.end = parser->current_token.range.end; rbs_location_t *loc = rbs_location_new(ALLOCATOR(), decl_range); @@ -1629,7 +1639,7 @@ static bool parse_const_decl(rbs_parser_t *parser, rbs_node_list_t *annotations, rbs_range_t colon_range = parser->current_token.range; rbs_node_t *type; - CHECK_PARSE(rbs_parse_type(parser, &type, false, false)); + CHECK_PARSE(rbs_parse_type(parser, &type, false, false, false)); decl_range.end = parser->current_token.range.end; @@ -1669,7 +1679,7 @@ static bool parse_type_decl(rbs_parser_t *parser, rbs_position_t comment_pos, rb rbs_range_t eq_range = parser->current_token.range; rbs_node_t *type; - CHECK_PARSE(rbs_parse_type(parser, &type, false, false)); + CHECK_PARSE(rbs_parse_type(parser, &type, false, false, false)); decl_range.end = parser->current_token.range.end; @@ -1978,7 +1988,7 @@ static bool parse_member_def(rbs_parser_t *parser, bool instance_only, bool acce case pLBRACKET: case pQUESTION: { rbs_method_type_t *method_type = NULL; - CHECK_PARSE(rbs_parse_method_type(parser, &method_type)); + CHECK_PARSE(rbs_parse_method_type(parser, &method_type, !instance_only)); overload_range.end = parser->current_token.range.end; rbs_location_t *loc = rbs_location_new(ALLOCATOR(), overload_range); @@ -2053,7 +2063,7 @@ static bool parse_member_def(rbs_parser_t *parser, bool instance_only, bool acce * @param kind * */ NODISCARD -static bool class_instance_name(rbs_parser_t *parser, TypeNameKind kind, rbs_node_list_t *args, rbs_range_t *name_range, rbs_range_t *args_range, rbs_type_name_t **name) { +static bool class_instance_name(rbs_parser_t *parser, TypeNameKind kind, rbs_node_list_t *args, rbs_range_t *name_range, rbs_range_t *args_range, rbs_type_name_t **name, bool classish_allowed) { rbs_parser_advance(parser); rbs_type_name_t *type_name = NULL; @@ -2063,7 +2073,7 @@ static bool class_instance_name(rbs_parser_t *parser, TypeNameKind kind, rbs_nod if (parser->next_token.type == pLBRACKET) { rbs_parser_advance(parser); args_range->start = parser->current_token.range.start; - CHECK_PARSE(parse_type_list(parser, pRBRACKET, args, true, false)); + CHECK_PARSE(parse_type_list(parser, pRBRACKET, args, true, false, classish_allowed)); ADVANCE_ASSERT(parser, pRBRACKET); args_range->end = parser->current_token.range.end; } else { @@ -2124,7 +2134,8 @@ static bool parse_mixin_member(rbs_parser_t *parser, bool from_interface, rbs_po args, &name_range, &args_range, - &name + &name, + true )); CHECK_PARSE(parser_pop_typevar_table(parser)); @@ -2238,7 +2249,7 @@ static bool parse_variable_member(rbs_parser_t *parser, rbs_position_t comment_p rbs_range_t colon_range = parser->current_token.range; rbs_node_t *type; - CHECK_PARSE(rbs_parse_type(parser, &type, false, true)); + CHECK_PARSE(rbs_parse_type(parser, &type, false, true, true)); member_range.end = parser->current_token.range.end; rbs_location_t *loc = rbs_location_new(ALLOCATOR(), member_range); @@ -2261,7 +2272,7 @@ static bool parse_variable_member(rbs_parser_t *parser, rbs_position_t comment_p rbs_parser_push_typevar_table(parser, true); rbs_node_t *type; - CHECK_PARSE(rbs_parse_type(parser, &type, false, false)); + CHECK_PARSE(rbs_parse_type(parser, &type, false, false, true)); CHECK_PARSE(parser_pop_typevar_table(parser)); @@ -2300,7 +2311,7 @@ static bool parse_variable_member(rbs_parser_t *parser, rbs_position_t comment_p rbs_parser_push_typevar_table(parser, true); rbs_node_t *type; - CHECK_PARSE(rbs_parse_type(parser, &type, false, true)); + CHECK_PARSE(rbs_parse_type(parser, &type, false, true, true)); CHECK_PARSE(parser_pop_typevar_table(parser)); @@ -2441,7 +2452,7 @@ static bool parse_attribute_member(rbs_parser_t *parser, rbs_position_t comment_ rbs_parser_push_typevar_table(parser, is_kind == SINGLETON_KIND); rbs_node_t *type; - CHECK_PARSE(rbs_parse_type(parser, &type, false, true)); + CHECK_PARSE(rbs_parse_type(parser, &type, false, true, true)); CHECK_PARSE(parser_pop_typevar_table(parser)); @@ -2594,7 +2605,7 @@ static bool parse_module_self_types(rbs_parser_t *parser, rbs_node_list_t *array if (parser->next_token.type == pLBRACKET) { rbs_parser_advance(parser); args_range.start = parser->current_token.range.start; - CHECK_PARSE(parse_type_list(parser, pRBRACKET, args, true, false)); + CHECK_PARSE(parse_type_list(parser, pRBRACKET, args, true, false, false)); rbs_parser_advance(parser); self_range.end = args_range.end = parser->current_token.range.end; } @@ -2829,7 +2840,7 @@ static bool parse_class_decl_super(rbs_parser_t *parser, rbs_range_t *lt_range, rbs_node_list_t *args = rbs_node_list_new(ALLOCATOR()); rbs_type_name_t *name = NULL; rbs_range_t name_range, args_range; - CHECK_PARSE(class_instance_name(parser, CLASS_NAME, args, &name_range, &args_range, &name)); + CHECK_PARSE(class_instance_name(parser, CLASS_NAME, args, &name_range, &args_range, &name, false)); super_range.end = parser->current_token.range.end; @@ -3584,7 +3595,7 @@ static bool parse_method_overload(rbs_parser_t *parser, rbs_node_list_t *annotat return false; } - return rbs_parse_method_type(parser, method_type); + return rbs_parse_method_type(parser, method_type, true); } /* @@ -3743,7 +3754,7 @@ static bool parse_inline_leading_annotation(rbs_parser_t *parser, rbs_ast_ruby_a rbs_location_t *colon_loc = rbs_location_new(ALLOCATOR(), colon_range); rbs_node_t *return_type = NULL; - if (!rbs_parse_type(parser, &return_type, true, true)) { + if (!rbs_parse_type(parser, &return_type, true, true, true)) { return false; } @@ -3789,7 +3800,7 @@ static bool parse_inline_leading_annotation(rbs_parser_t *parser, rbs_ast_ruby_a rbs_location_t *colon_loc = rbs_location_new(ALLOCATOR(), colon_range); rbs_node_t *type = NULL; - if (!rbs_parse_type(parser, &type, false, true)) { + if (!rbs_parse_type(parser, &type, false, true, true)) { return false; } @@ -3840,7 +3851,7 @@ static bool parse_inline_trailing_annotation(rbs_parser_t *parser, rbs_ast_ruby_ rbs_parser_advance(parser); rbs_node_t *type = NULL; - if (!rbs_parse_type(parser, &type, true, true)) { + if (!rbs_parse_type(parser, &type, true, true, true)) { return false; } @@ -3873,7 +3884,7 @@ static bool parse_inline_trailing_annotation(rbs_parser_t *parser, rbs_ast_ruby_ } // Parse type list with comma tracking - CHECK_PARSE(parse_type_list_with_commas(parser, pRBRACKET, type_args, comma_locations, true, true)); + CHECK_PARSE(parse_type_list_with_commas(parser, pRBRACKET, type_args, comma_locations, true, true, false)); rbs_range_t close_bracket_range = parser->next_token.range; rbs_location_t *close_bracket_loc = rbs_location_new(ALLOCATOR(), close_bracket_range); diff --git a/test/rbs/cli_test.rb b/test/rbs/cli_test.rb index 36f25fec5d..c37a2b4c4a 100644 --- a/test/rbs/cli_test.rb +++ b/test/rbs/cli_test.rb @@ -665,42 +665,6 @@ def bar: () -> Nothing end end - def test_validate_multiple_with_exit_error_on_syntax_error - with_cli do |cli| - Dir.mktmpdir do |dir| - (Pathname(dir) + 'a.rbs').write(<<~RBS) - class Foo - type bar = instance - end - RBS - - refute_cli_success do - cli.run(["-I", dir, "--log-level=warn", "validate", "--exit-error-on-syntax-error"]) - end - assert_include stdout.string, "a.rbs:2:13...2:21: `instance` or `class` type is not allowed in this context (RBS::WillSyntaxError)" - end - end - end - - def test_validate_multiple_with_fail_fast_and_exit_error_on_syntax_error - with_cli do |cli| - Dir.mktmpdir do |dir| - (Pathname(dir) + 'a.rbs').write(<<~RBS) - class Foo - def foo: (T) -> void - def bar: (T) -> void - end - RBS - - refute_cli_success do - cli.run(["-I", dir, "--log-level=warn", "validate", "--fail-fast", "--exit-error-on-syntax-error"]) - end - assert_include stdout.string, "/a.rbs:2:12...2:13: Could not find T (RBS::NoTypeFoundError)" - assert_not_include stdout.string, "/a.rbs:3:12...3:13: Could not find T (RBS::NoTypeFoundError)" - end - end - end - def test_validate_multiple_with_many_errors with_cli do |cli| refute_cli_success do @@ -739,60 +703,6 @@ def test_validate_multiple_fail_fast end end - def test_validate_multiple_fail_fast_and_exit_error_on_syntax_error - with_cli do |cli| - refute_cli_success do - cli.run(%w(--log-level=warn -I test/multiple_error.rbs validate --fail-fast --exit-error-on-syntax-error)) - end - assert_include(stdout.string, "test/multiple_error.rbs:6:17...6:24: ::TypeArg expects parameters [T], but given args [] (RBS::InvalidTypeApplicationError)") - end - end - - def test_context_validation - tests = [ - <<~RBS, - class Bar[A] - end - class Foo < Bar[instance] - end - RBS - <<~RBS, - module Bar : _Each[instance] - end - RBS - <<~RBS, - type foo = instance - RBS - <<~RBS, - BAR: instance - RBS - <<~RBS, - $FOO: instance - RBS - ] - - tests.each do |rbs| - with_cli do |cli| - Dir.mktmpdir do |dir| - (Pathname(dir) + 'a.rbs').write(rbs) - - assert_cli_success do - cli.run(["-I", dir, "validate"]) - end - - assert_match(/void|self|instance|class/, stdout.string) - - assert_cli_success do - cli.run(["-I", dir, "validate", "--no-exit-error-on-syntax-error"]) - end - refute_cli_success do - cli.run(["-I", dir, "validate", "--exit-error-on-syntax-error"]) - end - end - end - end - end - def test_validate_878 with_cli do |cli| Dir.mktmpdir do |dir| diff --git a/test/rbs/inline_annotation_parsing_test.rb b/test/rbs/inline_annotation_parsing_test.rb index 4cea75cd33..3c7801e053 100644 --- a/test/rbs/inline_annotation_parsing_test.rb +++ b/test/rbs/inline_annotation_parsing_test.rb @@ -12,6 +12,20 @@ def test_parse__trailing_assertion assert_equal ":", annot.prefix_location.source assert_equal "String", annot.type.location.source end + + Parser.parse_inline_trailing_annotation(": void", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotations::NodeTypeAssertion, annot + assert_equal ": void", annot.location.source + assert_equal ":", annot.prefix_location.source + assert_equal "void", annot.type.location.source + end + + Parser.parse_inline_trailing_annotation(": self | class | instance", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotations::NodeTypeAssertion, annot + assert_equal ": self | class | instance", annot.location.source + assert_equal ":", annot.prefix_location.source + assert_equal "self | class | instance", annot.type.location.source + end end def test_error__trailing_assertion @@ -106,12 +120,12 @@ def test_parse__return assert_nil annot.comment_location end - Parser.parse_inline_leading_annotation("@rbs return: self -- some comment here", 0...).tap do |annot| + Parser.parse_inline_leading_annotation("@rbs return: self | class | instance -- some comment here", 0...).tap do |annot| assert_instance_of AST::Ruby::Annotations::ReturnTypeAnnotation, annot - assert_equal "@rbs return: self -- some comment here", annot.location.source + assert_equal "@rbs return: self | class | instance -- some comment here", annot.location.source assert_equal "return", annot.return_location.source assert_equal ":", annot.colon_location.source - assert_equal "self", annot.return_type.location.source + assert_equal "self | class | instance", annot.return_type.location.source assert_equal "-- some comment here", annot.comment_location.source end end @@ -191,14 +205,14 @@ def test_parse__instance_variable assert_nil annot.comment_location end - Parser.parse_inline_leading_annotation("@rbs @self: self", 0...).tap do |annot| + Parser.parse_inline_leading_annotation("@rbs @sci: self | class | instance", 0...).tap do |annot| assert_instance_of AST::Ruby::Annotations::InstanceVariableAnnotation, annot - assert_equal "@rbs @self: self", annot.location.source + assert_equal "@rbs @sci: self | class | instance", annot.location.source assert_equal "@rbs", annot.prefix_location.source - assert_equal "@self", annot.ivar_name_location.source - assert_equal :@self, annot.ivar_name + assert_equal "@sci", annot.ivar_name_location.source + assert_equal :@sci, annot.ivar_name assert_equal ":", annot.colon_location.source - assert_equal "self", annot.type.location.source + assert_equal "self | class | instance", annot.type.location.source assert_nil annot.comment_location end end diff --git a/test/rbs/signature_parsing_test.rb b/test/rbs/signature_parsing_test.rb index bd8ad4654a..4167f7359a 100644 --- a/test/rbs/signature_parsing_test.rb +++ b/test/rbs/signature_parsing_test.rb @@ -2387,6 +2387,7 @@ def foo: (Array[void]) -> void def foo: (self) -> self def foo: (class) -> class def foo: (instance) -> instance + def foo: () -> A[void, self, class, instance] end RBS end @@ -2483,6 +2484,46 @@ def foo: () -> ^(void) -> void end assert_equal [2, 19], ex.location.start_loc assert_equal [2, 23], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature(<<~RBS) + interface _Foo + def foo: () -> class + end + RBS + end + assert_equal [2, 17], ex.location.start_loc + assert_equal [2, 22], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature(<<~RBS) + interface _Foo + def foo: () -> ^() -> class + end + RBS + end + assert_equal [2, 24], ex.location.start_loc + assert_equal [2, 29], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature(<<~RBS) + interface _Foo + def foo: () -> instance + end + RBS + end + assert_equal [2, 17], ex.location.start_loc + assert_equal [2, 25], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature(<<~RBS) + interface _Foo + def foo: () { () -> instance } -> void + end + RBS + end + assert_equal [2, 22], ex.location.start_loc + assert_equal [2, 30], ex.location.end_loc end def test_context_syntax_error_attribute @@ -2537,6 +2578,18 @@ def test_context_syntax_error_super_class end assert_equal [1, 18], ex.location.start_loc assert_equal [1, 22], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("class Foo < Array[class] end") + end + assert_equal [1, 18], ex.location.start_loc + assert_equal [1, 23], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("class Foo < Array[instance] end") + end + assert_equal [1, 18], ex.location.start_loc + assert_equal [1, 26], ex.location.end_loc end def test_context_syntax_error_module_self_type @@ -2555,6 +2608,18 @@ def test_context_syntax_error_module_self_type end assert_equal [1, 19], ex.location.start_loc assert_equal [1, 23], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("module Foo : Array[class] end") + end + assert_equal [1, 19], ex.location.start_loc + assert_equal [1, 24], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("module Foo : Array[instance] end") + end + assert_equal [1, 19], ex.location.start_loc + assert_equal [1, 27], ex.location.end_loc end def test_context_syntax_error_global @@ -2569,6 +2634,18 @@ def test_context_syntax_error_global end assert_equal [1, 7], ex.location.start_loc assert_equal [1, 11], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("$glob: class") + end + assert_equal [1, 7], ex.location.start_loc + assert_equal [1, 12], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("$glob: instance") + end + assert_equal [1, 7], ex.location.start_loc + assert_equal [1, 15], ex.location.end_loc end def test_context_syntax_error_constant @@ -2583,6 +2660,18 @@ def test_context_syntax_error_constant end assert_equal [1, 7], ex.location.start_loc assert_equal [1, 11], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("CONST: class") + end + assert_equal [1, 7], ex.location.start_loc + assert_equal [1, 12], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("CONST: instance") + end + assert_equal [1, 7], ex.location.start_loc + assert_equal [1, 15], ex.location.end_loc end def test_context_syntax_error_type_alias @@ -2597,6 +2686,18 @@ def test_context_syntax_error_type_alias end assert_equal [1, 9], ex.location.start_loc assert_equal [1, 13], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("type a = class") + end + assert_equal [1, 9], ex.location.start_loc + assert_equal [1, 14], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("type a = instance") + end + assert_equal [1, 9], ex.location.start_loc + assert_equal [1, 17], ex.location.end_loc end def test_context_syntax_error_mixin @@ -2607,6 +2708,12 @@ class C include M[class] include M[instance] end + + interface _C + include _M[void] + include _M[class] + include _M[instance] + end SIG end @@ -2689,6 +2796,18 @@ module M[T < Array[void]] assert_equal [1, 12], ex.location.start_loc assert_equal [1, 16], ex.location.end_loc + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("class C[T < class] end") + end + assert_equal [1, 12], ex.location.start_loc + assert_equal [1, 17], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("class C[T < instance] end") + end + assert_equal [1, 12], ex.location.start_loc + assert_equal [1, 20], ex.location.end_loc + ex = assert_raises RBS::ParsingError do Parser.parse_signature("module M[T < void] end") end @@ -2701,6 +2820,18 @@ module M[T < Array[void]] assert_equal [1, 13], ex.location.start_loc assert_equal [1, 17], ex.location.end_loc + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("module M[T < class] end") + end + assert_equal [1, 13], ex.location.start_loc + assert_equal [1, 18], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("module M[T < instance] end") + end + assert_equal [1, 13], ex.location.start_loc + assert_equal [1, 21], ex.location.end_loc + ex = assert_raises RBS::ParsingError do Parser.parse_signature("interface _I[T < void] end") end @@ -2713,6 +2844,18 @@ module M[T < Array[void]] assert_equal [1, 17], ex.location.start_loc assert_equal [1, 21], ex.location.end_loc + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("interface _I[T < class] end") + end + assert_equal [1, 17], ex.location.start_loc + assert_equal [1, 22], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("interface _I[T < instance] end") + end + assert_equal [1, 17], ex.location.start_loc + assert_equal [1, 25], ex.location.end_loc + ex = assert_raises RBS::ParsingError do Parser.parse_signature("type a[T < void] = 1") end @@ -2724,6 +2867,18 @@ module M[T < Array[void]] end assert_equal [1, 11], ex.location.start_loc assert_equal [1, 15], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("type a[T < class] = 1") + end + assert_equal [1, 11], ex.location.start_loc + assert_equal [1, 16], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("type a[T < instance] = 1") + end + assert_equal [1, 11], ex.location.start_loc + assert_equal [1, 19], ex.location.end_loc end def test_context_syntax_error_lower_bound @@ -2751,6 +2906,22 @@ module M[T > Array[void]] assert_equal [1, 12], ex.location.start_loc assert_equal [1, 16], ex.location.end_loc + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("class C[T > class] end") + end + assert_equal [1, 12], ex.location.start_loc + assert_equal [1, 17], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("class C[T > instance] end") + end + assert_equal [1, 12], ex.location.start_loc + assert_equal [1, 20], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("class C[T > instance] end") + end + assert_equal [1, 12], ex.location.start_loc ex = assert_raises RBS::ParsingError do Parser.parse_signature("module M[T > void] end") end @@ -2763,6 +2934,22 @@ module M[T > Array[void]] assert_equal [1, 13], ex.location.start_loc assert_equal [1, 17], ex.location.end_loc + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("module M[T > class] end") + end + assert_equal [1, 13], ex.location.start_loc + assert_equal [1, 18], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("module M[T > instance] end") + end + assert_equal [1, 13], ex.location.start_loc + assert_equal [1, 21], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("module M[T > instance] end") + end + assert_equal [1, 13], ex.location.start_loc ex = assert_raises RBS::ParsingError do Parser.parse_signature("interface _I[T > void] end") end @@ -2775,6 +2962,18 @@ module M[T > Array[void]] assert_equal [1, 17], ex.location.start_loc assert_equal [1, 21], ex.location.end_loc + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("interface _I[T > class] end") + end + assert_equal [1, 17], ex.location.start_loc + assert_equal [1, 22], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("interface _I[T > instance] end") + end + assert_equal [1, 17], ex.location.start_loc + assert_equal [1, 25], ex.location.end_loc + ex = assert_raises RBS::ParsingError do Parser.parse_signature("type a[T > void] = 1") end @@ -2786,6 +2985,18 @@ module M[T > Array[void]] end assert_equal [1, 11], ex.location.start_loc assert_equal [1, 15], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("type a[T > class] = 1") + end + assert_equal [1, 11], ex.location.start_loc + assert_equal [1, 16], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("type a[T > instance] = 1") + end + assert_equal [1, 11], ex.location.start_loc + assert_equal [1, 19], ex.location.end_loc end def test_context_syntax_error_upper_and_lower_bound @@ -2858,6 +3069,18 @@ module MA[T = Array[void]] assert_equal [1, 12], ex.location.start_loc assert_equal [1, 16], ex.location.end_loc + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("class C[T = class] end") + end + assert_equal [1, 12], ex.location.start_loc + assert_equal [1, 17], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("class C[T = instance] end") + end + assert_equal [1, 12], ex.location.start_loc + assert_equal [1, 20], ex.location.end_loc + ex = assert_raises RBS::ParsingError do Parser.parse_signature("module M[T = [void]] end") end @@ -2870,6 +3093,18 @@ module MA[T = Array[void]] assert_equal [1, 13], ex.location.start_loc assert_equal [1, 17], ex.location.end_loc + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("module M[T = class] end") + end + assert_equal [1, 13], ex.location.start_loc + assert_equal [1, 18], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("module M[T = instance] end") + end + assert_equal [1, 13], ex.location.start_loc + assert_equal [1, 21], ex.location.end_loc + ex = assert_raises RBS::ParsingError do Parser.parse_signature("interface _I[T = [void]] end") end @@ -2882,6 +3117,18 @@ module MA[T = Array[void]] assert_equal [1, 17], ex.location.start_loc assert_equal [1, 21], ex.location.end_loc + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("interface _I[T = class] end") + end + assert_equal [1, 17], ex.location.start_loc + assert_equal [1, 22], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("interface _I[T = instance] end") + end + assert_equal [1, 17], ex.location.start_loc + assert_equal [1, 25], ex.location.end_loc + ex = assert_raises RBS::ParsingError do Parser.parse_signature("type a[T = [void]] = 1") end @@ -2893,5 +3140,17 @@ module MA[T = Array[void]] end assert_equal [1, 11], ex.location.start_loc assert_equal [1, 15], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("type a[T = class] = 1") + end + assert_equal [1, 11], ex.location.start_loc + assert_equal [1, 16], ex.location.end_loc + + ex = assert_raises RBS::ParsingError do + Parser.parse_signature("type a[T = instance] = 1") + end + assert_equal [1, 11], ex.location.start_loc + assert_equal [1, 19], ex.location.end_loc end end From c9b056a0bff20233e1ea591cfc5aad8c02b8e80b Mon Sep 17 00:00:00 2001 From: SamW Date: Fri, 17 Oct 2025 13:01:50 -0700 Subject: [PATCH 02/70] [Kernel] Add Kernel.trace_var and Kernel.untrace_var --- core/kernel.rbs | 43 ++++++++++++++++++++++ test/stdlib/Kernel_test.rb | 75 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/core/kernel.rbs b/core/kernel.rbs index b50e581115..513071ddf6 100644 --- a/core/kernel.rbs +++ b/core/kernel.rbs @@ -2203,6 +2203,49 @@ module Kernel : BasicObject def self?.system: (String command, *String args, ?unsetenv_others: boolish, ?pgroup: true | Integer, ?umask: Integer, ?in: redirect_fd, ?out: redirect_fd, ?err: redirect_fd, ?close_others: boolish, ?chdir: String, ?exception: bool) -> (NilClass | FalseClass | TrueClass) | (Hash[string, string?] env, String command, *String args, ?unsetenv_others: boolish, ?pgroup: true | Integer, ?umask: Integer, ?in: redirect_fd, ?out: redirect_fd, ?err: redirect_fd, ?close_others: boolish, ?chdir: String, ?exception: bool) -> (NilClass | FalseClass | TrueClass) + # An interface used with `trace_var` (and `untrace_var`) for custom command types. + interface _Tracer + # Called whenever the global variable that's being traced changes; the argument is the new value. + def call: (untyped argument) -> void + end + + # + # Controls tracing of assignments to global variables. The parameter `symbol` + # identifies the variable (as either a string name or a symbol identifier). + # *cmd* (which may be a string or a `Proc` object) or block is executed whenever + # the variable is assigned. The block or `Proc` object receives the variable's + # new value as a parameter. Also see #untrace_var. + # + # trace_var :$_, proc {|v| puts "$_ is now '#{v}'" } + # $_ = "hello" + # $_ = ' there' + # + # *produces:* + # + # $_ is now 'hello' + # $_ is now ' there' + # + def self?.trace_var: (interned name, String | _Tracer cmd) -> nil + | (interned name) { (untyped value) -> void } -> nil + | (interned name, nil) -> Array[String | _Tracer]? + + # + # Removes tracing for the specified command on the given global variable and + # returns `nil`. If no command is specified, removes all tracing for that + # variable and returns an array containing the commands actually removed. + # + def self?.untrace_var: (interned name, ?nil) -> Array[String | _Tracer] + | (interned name, String cmd) -> [String]? + | [T < _Tracer] (interned name, T cmd) -> [T]? + | (interned name, untyped cmd) -> nil + # + # For the given method names, marks the method as passing keywords through a + # normal argument splat. This should only be called on methods that accept an + # argument splat (`*args`) but not explicit keywords or a keyword splat. It + # marks the method such that if the method is called with keyword arguments, the + # final hash argument is marked with a special flag such that if it is the final + # element of a normal argument splat to another method call, and that method + # call does not include explicit keywords or a keyword splat, the final element + # is interpreted as keywords. In other words, keywords will be passed through + # the method to other methods. + # + # This should only be used for methods that delegate keywords to another method, + # and only for backwards compatibility with Ruby versions before 3.0. See + # https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyw + # ord-arguments-in-ruby-3-0/ for details on why `ruby2_keywords` exists and when + # and how to use it. + # + # This method will probably be removed at some point, as it exists only for + # backwards compatibility. As it does not exist in Ruby versions before 2.7, + # check that the module responds to this method before calling it: + # + # module Mod + # def foo(meth, *args, &block) + # send(:"do_#{meth}", *args, &block) + # end + # ruby2_keywords(:foo) if respond_to?(:ruby2_keywords, true) + # end + # + # However, be aware that if the `ruby2_keywords` method is removed, the behavior + # of the `foo` method using the above approach will change so that the method + # does not pass through keywords. + # + private def ruby2_keywords: (*interned method_name) -> nil + # + # Marks the proc as passing keywords through a normal argument splat. This + # should only be called on procs that accept an argument splat (`*args`) but not + # explicit keywords or a keyword splat. It marks the proc such that if the proc + # is called with keyword arguments, the final hash argument is marked with a + # special flag such that if it is the final element of a normal argument splat + # to another method call, and that method call does not include explicit + # keywords or a keyword splat, the final element is interpreted as keywords. In + # other words, keywords will be passed through the proc to other methods. + # + # This should only be used for procs that delegate keywords to another method, + # and only for backwards compatibility with Ruby versions before 2.7. + # + # This method will probably be removed at some point, as it exists only for + # backwards compatibility. As it does not exist in Ruby versions before 2.7, + # check that the proc responds to this method before calling it. Also, be aware + # that if this method is removed, the behavior of the proc will change so that + # it does not pass through keywords. + # + # module Mod + # foo = ->(meth, *args, &block) do + # send(:"do_#{meth}", *args, &block) + # end + # foo.ruby2_keywords if foo.respond_to?(:ruby2_keywords) + # end + # + def ruby2_keywords: () -> self + # # - def root?: (untyped name) -> bool + def key?: (K name) -> bool + + alias root? key? # # - def roots: () -> Array[untyped] + def keys: () -> Array[K] + + alias roots keys # # Whether PStore should do its best to prevent file corruptions, even when an @@ -458,7 +462,7 @@ class PStore # raises no unexpected I/O error; if such an error occurs during a write to # the store, the file may become corrupted. # - def ultra_safe: () -> untyped + def ultra_safe: () -> bool # # Whether PStore should do its best to prevent file corruptions, even when an @@ -475,7 +479,7 @@ class PStore # raises no unexpected I/O error; if such an error occurs during a write to # the store, the file may become corrupted. # - def ultra_safe=: (untyped) -> untyped + def ultra_safe=: (bool) -> bool private @@ -486,14 +490,14 @@ class PStore # - empty_marshal_checksum() # --> # - def empty_marshal_checksum: () -> untyped + def empty_marshal_checksum: () -> String # # - def empty_marshal_data: () -> untyped + def empty_marshal_data: () -> String # # Raises PStore::Error if the calling code is not in a PStore#transaction. # - def in_transaction: () -> untyped + def in_transaction: () -> void # # - def save_data_with_atomic_file_rename_strategy: (untyped data, untyped file) -> untyped + def save_data_with_atomic_file_rename_strategy: (string data, File file) -> void # # - def save_data_with_fast_strategy: (untyped data, untyped file) -> untyped -end + def save_data_with_fast_strategy: (string data, File file) -> void -PStore::EMPTY_MARSHAL_CHECKSUM: String + EMPTY_MARSHAL_CHECKSUM: String -PStore::EMPTY_MARSHAL_DATA: String + EMPTY_MARSHAL_DATA: String -PStore::EMPTY_STRING: String + EMPTY_STRING: String -PStore::RDWR_ACCESS: Hash[untyped, untyped] + RDWR_ACCESS: { mode: Integer, encoding: Encoding } -PStore::RD_ACCESS: Hash[untyped, untyped] + RD_ACCESS: { mode: Integer, encoding: Encoding } -PStore::VERSION: String + VERSION: String -PStore::WR_ACCESS: Hash[untyped, untyped] + WR_ACCESS: { mode: Integer, encoding: Encoding } +end diff --git a/stdlib/psych/0/store.rbs b/stdlib/psych/0/store.rbs index 687d8b5903..b4f1b19318 100644 --- a/stdlib/psych/0/store.rbs +++ b/stdlib/psych/0/store.rbs @@ -30,7 +30,7 @@ # greeting: # hello: world # -class Psych::Store < ::PStore +class Psych::Store < ::PStore[string, untyped] # # Calls the block with each successive line read from the stream. # - # When called from class IO (but not subclasses of IO), this method has - # potential security vulnerabilities if called with untrusted input; see - # [Command Injection](rdoc-ref:security/command_injection.rdoc). - # # The first argument must be a string that is the path to a file. # # With only argument `path` given, parses lines from the file at the given @@ -2739,10 +2727,6 @@ class IO < Object # Opens the stream, reads and returns some or all of its content, and closes the # stream; returns `nil` if no bytes were read. # - # When called from class IO (but not subclasses of IO), this method has - # potential security vulnerabilities if called with untrusted input; see - # [Command Injection](rdoc-ref:security/command_injection.rdoc). - # # The first argument must be a string that is the path to a file. # # With only argument `path` given, reads in text mode and returns the entire @@ -2782,10 +2766,6 @@ class IO < Object # --> # Returns an array of all lines read from the stream. # - # When called from class IO (but not subclasses of IO), this method has - # potential security vulnerabilities if called with untrusted input; see - # [Command Injection](rdoc-ref:security/command_injection.rdoc). - # # The first argument must be a string that is the path to a file. # # With only argument `path` given, parses lines from the file at the given @@ -3007,10 +2987,6 @@ class IO < Object # Opens the stream, writes the given `data` to it, and closes the stream; # returns the number of bytes written. # - # When called from class IO (but not subclasses of IO), this method has - # potential security vulnerabilities if called with untrusted input; see - # [Command Injection](rdoc-ref:security/command_injection.rdoc). - # # The first argument must be a string that is the path to a file. # # With only argument `path` given, writes the given `data` to the file at that diff --git a/core/kernel.rbs b/core/kernel.rbs index 037d3faee3..e70ccde806 100644 --- a/core/kernel.rbs +++ b/core/kernel.rbs @@ -1253,9 +1253,6 @@ module Kernel : BasicObject # --> # Creates an IO object connected to the given file. # - # This method has potential security vulnerabilities if called with untrusted - # input; see [Command Injection](rdoc-ref:security/command_injection.rdoc). - # # With no block given, file stream is returned: # # open('t.txt') # => # From 404d136db3846a96974dd7c3091e668ee000417e Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Wed, 21 Jan 2026 16:06:07 +0900 Subject: [PATCH 19/70] Update GitHub Actions --- .github/workflows/comments.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/comments.yml b/.github/workflows/comments.yml index 3d0b8cff63..44b871f6be 100644 --- a/.github/workflows/comments.yml +++ b/.github/workflows/comments.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v6 - uses: ruby/setup-ruby@v1 with: - ruby-version: "4.0.0" + ruby-version: "4.0.1" bundler: none - name: Install dependencies run: | From dc6b6f112b97630c5b9df72cffd34947848eb55e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 08:09:39 +0000 Subject: [PATCH 20/70] Bump actions/checkout from 4 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9434d2a911..cdbba95ac4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -28,7 +28,7 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rust tools run: | rustup update --no-self-update stable @@ -51,7 +51,7 @@ jobs: name: cargo:lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rust tools run: | rustup update --no-self-update stable From f44dd0cd160cf251c2911f5244341481c6be69de Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 20 Jan 2026 00:27:50 +0000 Subject: [PATCH 21/70] bundle update --- Gemfile.lock | 11 ++++++----- steep/Gemfile.lock | 7 ++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c24d1a7671..b89f8d65b1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -63,7 +63,8 @@ GEM bigdecimal (>= 3.1, < 5) language_server-protocol (3.17.0.5) lint_roller (1.1.0) - listen (3.9.0) + listen (3.10.0) + logger rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) @@ -84,14 +85,14 @@ GEM racc (~> 1.4) ostruct (0.6.3) parallel (1.27.0) - parser (3.3.10.0) + parser (3.3.10.1) ast (~> 2.4.1) racc power_assert (3.0.1) pp (0.6.3) prettyprint prettyprint (0.2.0) - prism (1.7.0) + prism (1.8.0) pstore (0.2.0) psych (4.0.6) stringio @@ -107,7 +108,7 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rdoc (7.0.3) + rdoc (7.1.0) erb psych (>= 4.0.0) tsort @@ -141,7 +142,7 @@ GEM rubocop-ast (1.49.0) parser (>= 3.3.7.2) prism (~> 1.7) - rubocop-on-rbs (1.8.0) + rubocop-on-rbs (1.9.0) lint_roller (~> 1.1) rbs (~> 3.5) rubocop (>= 1.72.1, < 2.0) diff --git a/steep/Gemfile.lock b/steep/Gemfile.lock index ac9b5999f9..6532921485 100644 --- a/steep/Gemfile.lock +++ b/steep/Gemfile.lock @@ -27,18 +27,19 @@ GEM concurrent-ruby (~> 1.0) json (2.18.0) language_server-protocol (3.17.0.5) - listen (3.9.0) + listen (3.10.0) + logger rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) minitest (6.0.1) prism (~> 1.5) mutex_m (0.3.0) - parser (3.3.10.0) + parser (3.3.10.1) ast (~> 2.4.1) racc power_assert (3.0.1) - prism (1.7.0) + prism (1.8.0) racc (1.8.1) rainbow (3.1.1) rb-fsevent (0.11.2) From edc474162abb06302d6f6730ad46b859cb5baace Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Wed, 21 Jan 2026 17:10:05 +0900 Subject: [PATCH 22/70] Update docs --- core/complex.rbs | 6 ++-- core/dir.rbs | 8 ++--- core/enumerable.rbs | 2 +- core/file.rbs | 4 +-- core/kernel.rbs | 2 +- core/process.rbs | 8 ++--- core/regexp.rbs | 56 ++++++++++++++++---------------- core/string.rbs | 4 +-- core/struct.rbs | 2 +- stdlib/erb/0/erb.rbs | 8 ++--- stdlib/fileutils/0/fileutils.rbs | 14 ++++---- stdlib/tsort/0/tsort.rbs | 12 +++---- 12 files changed, 63 insertions(+), 63 deletions(-) diff --git a/core/complex.rbs b/core/complex.rbs index 0a2d31045c..c1b67803d9 100644 --- a/core/complex.rbs +++ b/core/complex.rbs @@ -740,7 +740,7 @@ class Complex < Numeric # Complex.rect(1, Rational(0, 1)).to_f # => 1.0 # # Raises RangeError if `self.imag` is not exactly zero (either `Integer(0)` or - # `Rational(0, *n*)`). + # `Rational(0, _n_)`). # def to_f: () -> Float @@ -754,7 +754,7 @@ class Complex < Numeric # Complex.rect(1, Rational(0, 1)).to_i # => 1 # # Raises RangeError if `self.imag` is not exactly zero (either `Integer(0)` or - # `Rational(0, *n*)`). + # `Rational(0, _n_)`). # def to_i: () -> Integer @@ -769,7 +769,7 @@ class Complex < Numeric # Complex.rect(1, 0.0).to_r # => (1/1) # # Raises RangeError if `self.imag` is not exactly zero (either `Integer(0)` or - # `Rational(0, *n*)`) and `self.imag.to_r` is not exactly zero. + # `Rational(0, _n_)`) and `self.imag.to_r` is not exactly zero. # # Related: Complex#rationalize. # diff --git a/core/dir.rbs b/core/dir.rbs index d08aaafaee..8c5ff651a4 100644 --- a/core/dir.rbs +++ b/core/dir.rbs @@ -567,14 +567,14 @@ class Dir # # Dir.glob('io.?') # => ["io.c"] # - # * `'[*set*]'`: Matches any one character in the string *set*; behaves like a + # * `'[_set_]'`: Matches any one character in the string *set*; behaves like a # [Regexp character class](rdoc-ref:Regexp@Character+Classes), including set # negation (`'[^a-z]'`): # # Dir.glob('*.[a-z][a-z]').take(3) # # => ["CONTRIBUTING.md", "COPYING.ja", "KNOWNBUGS.rb"] # - # * `'{*abc*,*xyz*}'`: Matches either string *abc* or string *xyz*; behaves + # * `'{_abc_,_xyz_}'`: Matches either string *abc* or string *xyz*; behaves # like [Regexp alternation](rdoc-ref:Regexp@Alternation): # # Dir.glob('{LEGAL,BSDL}') # => ["LEGAL", "BSDL"] @@ -631,9 +631,9 @@ class Dir # Dir.glob('*', flags: File::FNM_DOTMATCH).take(5) # # => [".", ".appveyor.yml", ".cirrus.yml", ".dir-locals.el", ".document"] # - # * File::FNM_EXTGLOB: enables the pattern extension `'{*a*,*b*}'`, which + # * File::FNM_EXTGLOB: enables the pattern extension `'{_a_,_b_}'`, which # matches pattern *a* and pattern *b*; behaves like a [regexp - # union](rdoc-ref:Regexp.union) (e.g., `'(?:*a*|*b*)'`): + # union](rdoc-ref:Regexp.union) (e.g., `'(?:_a_|_b_)'`): # # pattern = '{LEGAL,BSDL}' # Dir.glob(pattern) # => ["LEGAL", "BSDL"] diff --git a/core/enumerable.rbs b/core/enumerable.rbs index f3a4a8b934..f3ed49ccab 100644 --- a/core/enumerable.rbs +++ b/core/enumerable.rbs @@ -2314,7 +2314,7 @@ module Enumerable[unchecked out Elem] : _Each[Elem] # Creates an enumerator for each chunked elements. The ends of chunks are # defined by *pattern* and the block. # - # If *`pattern* === *elt`* returns `true` or the block returns `true` for the + # If `_pattern_ === _elt_` returns `true` or the block returns `true` for the # element, the element is end of a chunk. # # The `===` and *block* is called from the first element to the last element of diff --git a/core/file.rbs b/core/file.rbs index c77952cf24..a154b57783 100644 --- a/core/file.rbs +++ b/core/file.rbs @@ -2239,9 +2239,9 @@ File::Separator: String # # #### File::FNM_EXTGLOB # -# Flag File::FNM_EXTGLOB enables pattern `'{*a*,*b*}'`, which matches pattern +# Flag File::FNM_EXTGLOB enables pattern `'{_a_,_b_}'`, which matches pattern # '*a*' and pattern '*b*'; behaves like a [regexp union](rdoc-ref:Regexp.union) -# (e.g., `'(?:*a*|*b*)'`): +# (e.g., `'(?:_a_|_b_)'`): # # pattern = '{LEGAL,BSDL}' # Dir.glob(pattern) # => ["LEGAL", "BSDL"] diff --git a/core/kernel.rbs b/core/kernel.rbs index e70ccde806..332aa930a5 100644 --- a/core/kernel.rbs +++ b/core/kernel.rbs @@ -102,7 +102,7 @@ # # ### Subprocesses # -# * [\`command`](rdoc-ref:Kernel#`): Returns the standard output of running +# * [`command`](rdoc-ref:Kernel#`): Returns the standard output of running # `command` in a subshell. # * #exec: Replaces current process with a new process. # * #fork: Forks the current process into two processes. diff --git a/core/process.rbs b/core/process.rbs index 70723a3ea8..029cdf3744 100644 --- a/core/process.rbs +++ b/core/process.rbs @@ -257,10 +257,10 @@ # # Use execution options to set resource limits. # -# The keys for these options are symbols of the form `:rlimit_*resource_name`*, -# where *resource_name* is the downcased form of one of the string resource -# names described at method Process.setrlimit. For example, key `:rlimit_cpu` -# corresponds to resource limit `'CPU'`. +# The keys for these options are symbols of the form +# `:rlimit_resource_name`, where *resource_name* is the downcased form of +# one of the string resource names described at method Process.setrlimit. For +# example, key `:rlimit_cpu` corresponds to resource limit `'CPU'`. # # The value for such as key is one of: # diff --git a/core/regexp.rbs b/core/regexp.rbs index 6224bd3054..2f3acfbaae 100644 --- a/core/regexp.rbs +++ b/core/regexp.rbs @@ -414,26 +414,26 @@ # # Lookahead anchors: # -# * `(?=*pat*)`: Positive lookahead assertion: ensures that the following +# * `(?=_pat_)`: Positive lookahead assertion: ensures that the following # characters match *pat*, but doesn't include those characters in the # matched substring. # -# * `(?!*pat*)`: Negative lookahead assertion: ensures that the following +# * `(?!_pat_)`: Negative lookahead assertion: ensures that the following # characters *do not* match *pat*, but doesn't include those characters in # the matched substring. # # Lookbehind anchors: # -# * `(?<=*pat*)`: Positive lookbehind assertion: ensures that the preceding +# * `(?<=_pat_)`: Positive lookbehind assertion: ensures that the preceding # characters match *pat*, but doesn't include those characters in the # matched substring. # -# * `(?`...`` tags without including the tags in the match: # # /(?<=)\w+(?=<\/b>)/.match("Fortune favors the bold.") # # => # @@ -577,7 +577,7 @@ # re.match('1943-02-04').size # => 1 # re.match('foo') # => nil # -# Adding one or more pairs of parentheses, `(*subexpression*)`, defines +# Adding one or more pairs of parentheses, `(_subexpression_)`, defines # *groups*, which may result in multiple matched substrings, called *captures*: # # re = /(\d\d\d\d)-(\d\d)-(\d\d)/ @@ -649,8 +649,8 @@ # # * For a large number of groups: # -# * The ordinary `\*n`* notation applies only for *n* in range (1..9). -# * The `MatchData[*n*]` notation applies for any non-negative *n*. +# * The ordinary `\_n_` notation applies only for *n* in range (1..9). +# * The `MatchData[_n_]` notation applies for any non-negative *n*. # # * `\0` is a special backreference, referring to the entire matched string; # it may not be used within the regexp itself, but may be used outside it @@ -662,7 +662,7 @@ # #### Named Captures # # As seen above, a capture can be referred to by its number. A capture can also -# have a name, prefixed as `?<*name*>` or `?'*name*'`, and the name (symbolized) +# have a name, prefixed as `?<_name_>` or `?'_name_'`, and the name (symbolized) # may be used as an index in `MatchData[]`: # # md = /\$(?\d+)\.(?'cents'\d+)/.match("$3.67") @@ -677,7 +677,7 @@ # /\$(?\d+)\.(\d+)/.match("$3.67") # # => # # -# A named group may be backreferenced as `\k<*name*>`: +# A named group may be backreferenced as `\k<_name_>`: # # /(?[aeiou]).\k.\k/.match('ototomy') # # => # @@ -733,9 +733,9 @@ # # #### Subexpression Calls # -# As seen above, a backreference number (`\*n`*) or name (`\k<*name*>`) gives +# As seen above, a backreference number (`\_n_`) or name (`\k<_name_>`) gives # access to a captured *substring*; the corresponding regexp *subexpression* may -# also be accessed, via the number (`\\g*n`*) or name (`\g<*name*>`): +# also be accessed, via the number (`\\gn`) or name (`\g<_name_>`): # # /\A(?\(\g*\))*\z/.match('(())') # # ^1 @@ -770,12 +770,12 @@ # # #### Conditionals # -# The conditional construct takes the form `(?(*cond*)*yes*|*no*)`, where: +# The conditional construct takes the form `(?(_cond_)_yes_|_no_)`, where: # # * *cond* may be a capture number or name. # * The match to be applied is *yes* if *cond* is captured; otherwise the # match to be applied is *no*. -# * If not needed, `|*no`* may be omitted. +# * If not needed, `|_no_` may be omitted. # # Examples: # @@ -804,7 +804,7 @@ # # #### Unicode Properties # -# The `/\p{*property_name*}/` construct (with lowercase `p`) matches characters +# The `/\p{_property_name_}/` construct (with lowercase `p`) matches characters # using a Unicode property name, much like a character class; property `Alpha` # specifies alphabetic characters: # @@ -1038,21 +1038,21 @@ # # Each of these modifiers sets a mode for the regexp: # -# * `i`: `/*pattern*/i` sets [Case-Insensitive +# * `i`: `/_pattern_/i` sets [Case-Insensitive # Mode](rdoc-ref:Regexp@Case-Insensitive+Mode). -# * `m`: `/*pattern*/m` sets [Multiline Mode](rdoc-ref:Regexp@Multiline+Mode). -# * `x`: `/*pattern*/x` sets [Extended Mode](rdoc-ref:Regexp@Extended+Mode). -# * `o`: `/*pattern*/o` sets [Interpolation +# * `m`: `/_pattern_/m` sets [Multiline Mode](rdoc-ref:Regexp@Multiline+Mode). +# * `x`: `/_pattern_/x` sets [Extended Mode](rdoc-ref:Regexp@Extended+Mode). +# * `o`: `/_pattern_/o` sets [Interpolation # Mode](rdoc-ref:Regexp@Interpolation+Mode). # # Any, all, or none of these may be applied. # # Modifiers `i`, `m`, and `x` may be applied to subexpressions: # -# * `(?*modifier*)` turns the mode "on" for ensuing subexpressions -# * `(?-*modifier*)` turns the mode "off" for ensuing subexpressions -# * `(?*modifier*:*subexp*)` turns the mode "on" for *subexp* within the group -# * `(?-*modifier*:*subexp*)` turns the mode "off" for *subexp* within the +# * `(?_modifier_)` turns the mode "on" for ensuing subexpressions +# * `(?-_modifier_)` turns the mode "off" for ensuing subexpressions +# * `(?_modifier_:_subexp_)` turns the mode "on" for *subexp* within the group +# * `(?-_modifier_:_subexp_)` turns the mode "off" for *subexp* within the # group # # Example: @@ -1168,22 +1168,22 @@ # A regular expression containing non-US-ASCII characters is assumed to use the # source encoding. This can be overridden with one of the following modifiers. # -# * `/*pat*/n`: US-ASCII if only containing US-ASCII characters, otherwise +# * `/_pat_/n`: US-ASCII if only containing US-ASCII characters, otherwise # ASCII-8BIT: # # /foo/n.encoding # => # # /foo\xff/n.encoding # => # # /foo\x7f/n.encoding # => # # -# * `/*pat*/u`: UTF-8 +# * `/_pat_/u`: UTF-8 # # /foo/u.encoding # => # # -# * `/*pat*/e`: EUC-JP +# * `/_pat_/e`: EUC-JP # # /foo/e.encoding # => # # -# * `/*pat*/s`: Windows-31J +# * `/_pat_/s`: Windows-31J # # /foo/s.encoding # => # # @@ -1926,7 +1926,7 @@ class Regexp # rdoc-file=re.c # - ~ rxp -> integer or nil # --> - # Equivalent to *`rxp* =~ $_`: + # Equivalent to `rxp =~ $_`: # # $_ = "input data" # ~ /at/ # => 7 diff --git a/core/string.rbs b/core/string.rbs index 59d3122d43..1cd435d857 100644 --- a/core/string.rbs +++ b/core/string.rbs @@ -86,7 +86,7 @@ # * `\&` and `\0` correspond to `$&`, which contains the complete matched # text. # * `\'` corresponds to `$'`, which contains the string after the match. -# * `\`` corresponds to `$``, which contains the string before the match. +# * ``` corresponds to `$``, which contains the string before the match. # * `\+` corresponds to `$+`, which contains the last capture group. # # See Regexp for details. @@ -559,7 +559,7 @@ # * `\&` and `\0` correspond to `$&`, which contains the complete matched # text. # * `\'` corresponds to `$'`, which contains the string after the match. -# * `\`` corresponds to `$``, which contains the string before the match. +# * ``` corresponds to `$``, which contains the string before the match. # * `\+` corresponds to `$+`, which contains the last capture group. # # See Regexp for details. diff --git a/core/struct.rbs b/core/struct.rbs index 65f0887114..0b3113889f 100644 --- a/core/struct.rbs +++ b/core/struct.rbs @@ -133,7 +133,7 @@ class Struct[Elem] # **Class Name** # # With string argument `class_name`, returns a new subclass of `Struct` named - # `Struct::*class_name`*: + # `Struct::class_name`: # # Foo = Struct.new('Foo', :foo, :bar) # => Struct::Foo # Foo.name # => "Struct::Foo" diff --git a/stdlib/erb/0/erb.rbs b/stdlib/erb/0/erb.rbs index 64eac1aa11..a257e0addc 100644 --- a/stdlib/erb/0/erb.rbs +++ b/stdlib/erb/0/erb.rbs @@ -181,7 +181,7 @@ # in the result, the entire tag is to be omitted. # ### Expression Tags # You can embed a Ruby expression in a template using an *expression tag*. -# Its syntax is `<%= *expression* %>`, +# Its syntax is `<%= _expression_ %>`, # where *expression* is any valid Ruby expression. # When you call method #result, # the method evaluates the expression and replaces the entire expression tag @@ -203,7 +203,7 @@ # # ### Execution Tags # You can embed Ruby executable code in template using an *execution tag*. -# Its syntax is `<% *code* %>`, +# Its syntax is `<% _code_ %>`, # where *code* is any valid Ruby code. # When you call method #result, # the method executes the code and removes the entire execution tag @@ -270,7 +270,7 @@ # #### Shorthand Format for Execution Tags # You can use keyword argument `trim_mode: '%'` to enable a shorthand format for # execution tags; -# this example uses the shorthand format `% *code`* instead of `<% *code* %>`: +# this example uses the shorthand format `% _code_` instead of `<% _code_ %>`: # template = <