rbs 4.1.3-java → 4.2.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/bundle-update.yml +2 -2
  4. data/.github/workflows/c-check.yml +2 -2
  5. data/.github/workflows/changelog.yml +121 -0
  6. data/.github/workflows/comments.yml +2 -2
  7. data/.github/workflows/dependabot.yml +1 -1
  8. data/.github/workflows/jruby.yml +3 -3
  9. data/.github/workflows/release-gems.yml +6 -7
  10. data/.github/workflows/ruby.yml +6 -6
  11. data/.github/workflows/rust.yml +12 -12
  12. data/.github/workflows/truffleruby.yml +2 -2
  13. data/.github/workflows/typecheck.yml +2 -2
  14. data/.github/workflows/wasm.yml +3 -3
  15. data/.github/workflows/windows.yml +2 -2
  16. data/.rubocop.yml +0 -1
  17. data/CHANGELOG.md +50 -0
  18. data/Rakefile +50 -21
  19. data/config.yml +5 -0
  20. data/core/io.rbs +2 -1
  21. data/docs/CONTRIBUTING.md +1 -1
  22. data/docs/release.md +57 -1
  23. data/docs/stdlib.md +8 -0
  24. data/docs/syntax.md +12 -0
  25. data/ext/rbs_extension/ast_translation.c +15 -0
  26. data/ext/rbs_extension/class_constants.c +2 -0
  27. data/ext/rbs_extension/class_constants.h +1 -0
  28. data/ext/rbs_extension/main.c +55 -19
  29. data/include/rbs/ast.h +21 -13
  30. data/include/rbs/defines.h +2 -2
  31. data/include/rbs/lexer.h +13 -12
  32. data/include/rbs/parser.h +37 -3
  33. data/lib/rbs/ast/declarations.rb +1 -1
  34. data/lib/rbs/ast/type_param.rb +1 -1
  35. data/lib/rbs/definition_builder/ancestor_builder.rb +8 -5
  36. data/lib/rbs/definition_builder/method_builder.rb +4 -4
  37. data/lib/rbs/definition_builder.rb +5 -2
  38. data/lib/rbs/environment/class_entry.rb +12 -0
  39. data/lib/rbs/environment/module_entry.rb +26 -1
  40. data/lib/rbs/parser_aux.rb +2 -2
  41. data/lib/rbs/prototype/helpers.rb +19 -52
  42. data/lib/rbs/prototype/runtime/value_object_generator.rb +0 -1
  43. data/lib/rbs/prototype/runtime.rb +1 -1
  44. data/lib/rbs/types.rb +44 -2
  45. data/lib/rbs/version.rb +1 -1
  46. data/lib/rbs/wasm/parser.rb +62 -25
  47. data/lib/rbs/wasm/rbs_parser.wasm +0 -0
  48. data/lib/rbs/wasm/runtime.rb +19 -4
  49. data/lib/rbs/wasm/serialization_schema.rb +3 -2
  50. data/rbs.gemspec +1 -5
  51. data/schema/function.json +12 -1
  52. data/sig/environment/class_entry.rbs +6 -0
  53. data/sig/environment/module_entry.rbs +15 -0
  54. data/sig/parser.rbs +4 -4
  55. data/sig/types.rbs +11 -1
  56. data/src/ast.c +17 -1
  57. data/src/lexer.c +1562 -1560
  58. data/src/lexer.re +50 -18
  59. data/src/lexstate.c +2 -1
  60. data/src/parser.c +130 -70
  61. data/src/serialize.c +20 -13
  62. data/src/util/rbs_encoding.c +195 -49
  63. data/stdlib/erb/0/erb.rbs +4 -4
  64. data/stdlib/monitor/0/monitor.rbs +4 -4
  65. data/stdlib/singleton/0/singleton.rbs +3 -0
  66. data/stdlib/stringio/0/stringio.rbs +2 -1
  67. data/stdlib/zlib/0/gzip_file.rbs +1 -1
  68. data/stdlib/zlib/0/gzip_writer.rbs +2 -1
  69. data/wasm/README.md +20 -4
  70. data/wasm/rbs_wasm.c +93 -37
  71. metadata +5 -3
@@ -124,10 +124,13 @@ module RBS
124
124
  end
125
125
 
126
126
  entry = env.class_decls[type_name] or raise "Unknown name for build_instance: #{type_name}"
127
- args = entry.type_params.map {|param| Types::Variable.new(name: param.name, location: param.location) }
128
127
 
129
128
  entry.each_decl do |decl|
130
- subst_ = subst + Substitution.build(decl.type_params.each.map(&:name), args)
129
+ if align_params = entry.align_params(decl)
130
+ subst_ = subst + align_params
131
+ else
132
+ subst_ = subst
133
+ end
131
134
 
132
135
  decl.members.each do |member|
133
136
  case member
@@ -64,6 +64,18 @@ module RBS
64
64
  end
65
65
  end
66
66
  end
67
+
68
+ def align_params(decl)
69
+ entry_params = type_params
70
+ decl_param_names = decl.type_params.map(&:name)
71
+
72
+ return nil if decl_param_names == entry_params.map(&:name)
73
+
74
+ Substitution.build(
75
+ decl_param_names,
76
+ entry_params.map {|param| Types::Variable.new(name: param.name, location: param.location) }
77
+ )
78
+ end
67
79
  end
68
80
  end
69
81
  end
@@ -42,10 +42,35 @@ module RBS
42
42
 
43
43
  def self_types
44
44
  each_decl.flat_map do |decl|
45
- decl.self_types
45
+ self_types = decl.self_types
46
+ subst = align_params(decl)
47
+
48
+ if self_types.empty? || subst.nil?
49
+ self_types
50
+ else
51
+ self_types.map do |self_type|
52
+ AST::Declarations::Module::Self.new(
53
+ name: self_type.name,
54
+ args: self_type.args.map {|type| type.sub(subst) },
55
+ location: self_type.location
56
+ )
57
+ end
58
+ end
46
59
  end.uniq
47
60
  end
48
61
 
62
+ def align_params(decl)
63
+ entry_params = type_params
64
+ decl_param_names = decl.type_params.map(&:name)
65
+
66
+ return nil if decl_param_names == entry_params.map(&:name)
67
+
68
+ Substitution.build(
69
+ decl_param_names,
70
+ entry_params.map {|param| Types::Variable.new(name: param.name, location: param.location) }
71
+ )
72
+ end
73
+
49
74
  def validate_type_params
50
75
  unless context_decls.empty?
51
76
  first_decl, *rest_decls = each_decl.to_a
@@ -14,7 +14,7 @@ module RBS
14
14
  def self.parse_method_type(source, range: nil, byte_range: 0..., variables: [], require_eof: false)
15
15
  buf = buffer(source)
16
16
  byte_range = byte_range(range, buf.content) if range
17
- _parse_method_type(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables, require_eof)
17
+ _parse_method_type(buf, byte_range.begin || 0, byte_range.end || buf.content.bytesize, variables, require_eof, false)
18
18
  end
19
19
 
20
20
  def self.parse_signature(source)
@@ -28,7 +28,7 @@ module RBS
28
28
  0
29
29
  end
30
30
  content = buf.content
31
- dirs, decls = _parse_signature(buf, start_pos, content.bytesize)
31
+ dirs, decls = _parse_signature(buf, start_pos, content.bytesize, false)
32
32
 
33
33
  if resolved
34
34
  dirs = dirs.dup if dirs.frozen?
@@ -5,58 +5,25 @@ module RBS
5
5
  module Helpers
6
6
  private
7
7
 
8
- # Prism can't parse Ruby 3.2 code
9
- if RUBY_VERSION >= "3.3"
10
- def parse_comments(string, include_trailing:)
11
- Prism.parse_comments(string, version: "current").yield_self do |prism_comments| # steep:ignore UnexpectedKeywordArgument
12
- prism_comments.each_with_object({}) do |comment, hash| #$ Hash[Integer, AST::Comment]
13
- # Skip EmbDoc comments
14
- next unless comment.is_a?(Prism::InlineComment)
15
- # skip like `module Foo # :nodoc:`
16
- next if comment.trailing? && !include_trailing
17
-
18
- line = comment.location.start_line
19
- body = "#{comment.location.slice}\n"
20
- body = body[2..-1] or raise
21
- body = "\n" if body.empty?
22
-
23
- comment = AST::Comment.new(string: body, location: nil)
24
- if prev_comment = hash.delete(line - 1)
25
- hash[line] = AST::Comment.new(string: prev_comment.string + comment.string,
26
- location: nil)
27
- else
28
- hash[line] = comment
29
- end
30
- end
31
- end
32
- end
33
- else
34
- require "ripper"
35
- def parse_comments(string, include_trailing:)
36
- Ripper.lex(string).yield_self do |tokens|
37
- code_lines = {} #: Hash[Integer, bool]
38
- tokens.each.with_object({}) do |token, hash| #$ Hash[Integer, AST::Comment]
39
- case token[1]
40
- when :on_sp, :on_ignored_nl
41
- # skip
42
- when :on_comment
43
- line = token[0][0]
44
- # skip like `module Foo # :nodoc:`
45
- next if code_lines[line] && !include_trailing
46
- body = token[2][2..-1] or raise
47
-
48
- body = "\n" if body.empty?
49
-
50
- comment = AST::Comment.new(string: body, location: nil)
51
- if prev_comment = hash.delete(line - 1)
52
- hash[line] = AST::Comment.new(string: prev_comment.string + comment.string,
53
- location: nil)
54
- else
55
- hash[line] = comment
56
- end
57
- else
58
- code_lines[token[0][0]] = true
59
- end
8
+ def parse_comments(string, include_trailing:)
9
+ Prism.parse_comments(string, version: "current").yield_self do |prism_comments| # steep:ignore UnexpectedKeywordArgument
10
+ prism_comments.each_with_object({}) do |comment, hash| #$ Hash[Integer, AST::Comment]
11
+ # Skip EmbDoc comments
12
+ next unless comment.is_a?(Prism::InlineComment)
13
+ # skip like `module Foo # :nodoc:`
14
+ next if comment.trailing? && !include_trailing
15
+
16
+ line = comment.location.start_line
17
+ body = "#{comment.location.slice}\n"
18
+ body = body[2..-1] or raise
19
+ body = "\n" if body.empty?
20
+
21
+ comment = AST::Comment.new(string: body, location: nil)
22
+ if prev_comment = hash.delete(line - 1)
23
+ hash[line] = AST::Comment.new(string: prev_comment.string + comment.string,
24
+ location: nil)
25
+ else
26
+ hash[line] = comment
60
27
  end
61
28
  end
62
29
  end
@@ -212,7 +212,6 @@ module RBS
212
212
 
213
213
  class DataGenerator < ValueObjectBase
214
214
  def self.generatable?(target)
215
- return false unless RUBY_VERSION >= '3.2'
216
215
  return false unless target < Data
217
216
  # Avoid direct inherited class like `class Option < Data`
218
217
  return false unless target.respond_to?(:members)
@@ -527,7 +527,7 @@ module RBS
527
527
 
528
528
  generate_mixin(mod, decl, type_name, type_name_absolute)
529
529
 
530
- unless mod < Struct || (RUBY_VERSION >= '3.2' && mod < Data)
530
+ unless mod < Struct || mod < Data
531
531
  generate_methods(mod, type_name, decl.members) unless outline
532
532
  end
533
533
 
data/lib/rbs/types.rb CHANGED
@@ -959,6 +959,33 @@ module RBS
959
959
  end
960
960
  end
961
961
 
962
+ class ForwardingParam
963
+ attr_reader :location
964
+
965
+ def initialize(location:)
966
+ @location = location
967
+ end
968
+
969
+ def ==(other)
970
+ other.is_a?(ForwardingParam)
971
+ end
972
+
973
+ alias eql? ==
974
+
975
+ def hash
976
+ self.class.hash
977
+ end
978
+
979
+ def to_json(state = nil)
980
+ json = {} #: Hash[Symbol, untyped]
981
+ json.to_json(state)
982
+ end
983
+
984
+ def to_s
985
+ "..."
986
+ end
987
+ end
988
+
962
989
  attr_reader :required_positionals
963
990
  attr_reader :optional_positionals
964
991
  attr_reader :rest_positionals
@@ -966,9 +993,10 @@ module RBS
966
993
  attr_reader :required_keywords
967
994
  attr_reader :optional_keywords
968
995
  attr_reader :rest_keywords
996
+ attr_reader :forwarding
969
997
  attr_reader :return_type
970
998
 
971
- def initialize(required_positionals:, optional_positionals:, rest_positionals:, trailing_positionals:, required_keywords:, optional_keywords:, rest_keywords:, return_type:)
999
+ def initialize(required_positionals:, optional_positionals:, rest_positionals:, trailing_positionals:, required_keywords:, optional_keywords:, rest_keywords:, return_type:, forwarding: nil)
972
1000
  @return_type = return_type
973
1001
  @required_positionals = required_positionals
974
1002
  @optional_positionals = optional_positionals
@@ -977,6 +1005,7 @@ module RBS
977
1005
  @required_keywords = required_keywords
978
1006
  @optional_keywords = optional_keywords
979
1007
  @rest_keywords = rest_keywords
1008
+ @forwarding = forwarding
980
1009
  end
981
1010
 
982
1011
  def ==(other)
@@ -988,6 +1017,7 @@ module RBS
988
1017
  other.required_keywords == required_keywords &&
989
1018
  other.optional_keywords == optional_keywords &&
990
1019
  other.rest_keywords == rest_keywords &&
1020
+ other.forwarding == forwarding &&
991
1021
  other.return_type == return_type
992
1022
  end
993
1023
 
@@ -1002,6 +1032,7 @@ module RBS
1002
1032
  required_keywords.hash ^
1003
1033
  optional_keywords.hash ^
1004
1034
  rest_keywords.hash ^
1035
+ forwarding.hash ^
1005
1036
  return_type.hash
1006
1037
  end
1007
1038
 
@@ -1043,6 +1074,7 @@ module RBS
1043
1074
  required_keywords: hmapv(required_keywords) {|param| param.map_type(&block) },
1044
1075
  optional_keywords: hmapv(optional_keywords) {|param| param.map_type(&block) },
1045
1076
  rest_keywords: rest_keywords&.yield_self {|param| param.map_type(&block) },
1077
+ forwarding: forwarding,
1046
1078
  return_type: yield(return_type)
1047
1079
  )
1048
1080
  else
@@ -1110,6 +1142,7 @@ module RBS
1110
1142
  required_keywords: required_keywords,
1111
1143
  optional_keywords: optional_keywords,
1112
1144
  rest_keywords: rest_keywords,
1145
+ forwarding: forwarding,
1113
1146
  return_type: return_type
1114
1147
  }.to_json(state)
1115
1148
  end
@@ -1129,6 +1162,7 @@ module RBS
1129
1162
  required_keywords: {},
1130
1163
  optional_keywords: {},
1131
1164
  rest_keywords: nil,
1165
+ forwarding: nil,
1132
1166
  return_type: return_type
1133
1167
  )
1134
1168
  end
@@ -1142,12 +1176,13 @@ module RBS
1142
1176
  required_keywords: required_keywords,
1143
1177
  optional_keywords: optional_keywords,
1144
1178
  rest_keywords: rest_keywords,
1179
+ forwarding: forwarding,
1145
1180
  return_type: type
1146
1181
  )
1147
1182
  end
1148
1183
 
1149
1184
  def update(required_positionals: self.required_positionals, optional_positionals: self.optional_positionals, rest_positionals: self.rest_positionals, trailing_positionals: self.trailing_positionals,
1150
- required_keywords: self.required_keywords, optional_keywords: self.optional_keywords, rest_keywords: self.rest_keywords, return_type: self.return_type)
1185
+ required_keywords: self.required_keywords, optional_keywords: self.optional_keywords, rest_keywords: self.rest_keywords, forwarding: self.forwarding, return_type: self.return_type)
1151
1186
  Function.new(
1152
1187
  required_positionals: required_positionals,
1153
1188
  optional_positionals: optional_positionals,
@@ -1156,6 +1191,7 @@ module RBS
1156
1191
  required_keywords: required_keywords,
1157
1192
  optional_keywords: optional_keywords,
1158
1193
  rest_keywords: rest_keywords,
1194
+ forwarding: forwarding,
1159
1195
  return_type: return_type
1160
1196
  )
1161
1197
  end
@@ -1167,6 +1203,7 @@ module RBS
1167
1203
  trailing_positionals.empty? &&
1168
1204
  required_keywords.empty? &&
1169
1205
  optional_keywords.empty? &&
1206
+ !forwarding? &&
1170
1207
  !rest_keywords
1171
1208
  end
1172
1209
 
@@ -1175,6 +1212,7 @@ module RBS
1175
1212
  params = []
1176
1213
 
1177
1214
  params.push(*required_positionals.map(&:to_s))
1215
+ params.push(forwarding.to_s) if forwarding
1178
1216
  params.push(*optional_positionals.map {|p| "?#{p}"})
1179
1217
  params.push("*#{rest_positionals}") if rest_positionals
1180
1218
  params.push(*trailing_positionals.map(&:to_s))
@@ -1185,6 +1223,10 @@ module RBS
1185
1223
  params.join(", ")
1186
1224
  end
1187
1225
 
1226
+ def forwarding?
1227
+ !forwarding.nil?
1228
+ end
1229
+
1188
1230
  def return_to_s
1189
1231
  return_type.to_s(1)
1190
1232
  end
data/lib/rbs/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RBS
4
- VERSION = "4.1.3"
4
+ VERSION = "4.2.0"
5
5
  end
@@ -12,83 +12,102 @@ module RBS
12
12
  # RBS::Parser API on top, exactly as it does for the C extension.
13
13
  class Parser
14
14
  class << self
15
- def _parse_signature(buffer, start_pos, end_pos)
16
- validate_position_range(start_pos, end_pos)
15
+ def _parse_signature(buffer, start_pos, end_pos, enable_forwarding_params)
16
+ validate_position_range(buffer, start_pos, end_pos)
17
+ validate_parser_options(enable_forwarding_params)
17
18
  encoding = buffer.content.encoding.name
18
- success, bytes = WASM::Runtime.instance.parse_signature(buffer.content, encoding, start_pos, end_pos)
19
- raise_parsing_error(buffer, bytes) unless success
19
+ status, bytes = WASM::Runtime.instance.parse_signature(buffer.content, encoding, start_pos, end_pos)
20
+ raise_parse_failure(buffer, status, bytes, start_pos, end_pos) unless status == WASM::Runtime::OK
20
21
 
21
22
  WASM::Deserializer.deserialize(bytes, buffer)
22
23
  end
23
24
 
24
25
  def _parse_type(buffer, start_pos, end_pos, variables, require_eof, void_allowed, self_allowed, classish_allowed)
25
- validate_position_range(start_pos, end_pos)
26
+ validate_position_range(buffer, start_pos, end_pos)
26
27
  validate_variables(variables)
27
28
  encoding = buffer.content.encoding.name
28
- success, bytes = WASM::Runtime.instance.parse_type(buffer.content, encoding, start_pos, end_pos, variables, require_eof, void_allowed, self_allowed, classish_allowed)
29
- raise_parsing_error(buffer, bytes) unless success
29
+ status, bytes = WASM::Runtime.instance.parse_type(buffer.content, encoding, start_pos, end_pos, variables, require_eof, void_allowed, self_allowed, classish_allowed)
30
+ raise_parse_failure(buffer, status, bytes, start_pos, end_pos) unless status == WASM::Runtime::OK
30
31
 
31
32
  deserialize_or_nil(bytes, buffer)
32
33
  end
33
34
 
34
- def _parse_method_type(buffer, start_pos, end_pos, variables, require_eof)
35
- validate_position_range(start_pos, end_pos)
35
+ def _parse_method_type(buffer, start_pos, end_pos, variables, require_eof, enable_forwarding_params)
36
+ validate_position_range(buffer, start_pos, end_pos)
36
37
  validate_variables(variables)
38
+ validate_parser_options(enable_forwarding_params)
37
39
  encoding = buffer.content.encoding.name
38
- success, bytes = WASM::Runtime.instance.parse_method_type(buffer.content, encoding, start_pos, end_pos, variables, require_eof)
39
- raise_parsing_error(buffer, bytes) unless success
40
+ status, bytes = WASM::Runtime.instance.parse_method_type(buffer.content, encoding, start_pos, end_pos, variables, require_eof)
41
+ raise_parse_failure(buffer, status, bytes, start_pos, end_pos) unless status == WASM::Runtime::OK
40
42
 
41
43
  deserialize_or_nil(bytes, buffer)
42
44
  end
43
45
 
44
46
  def _parse_type_params(buffer, start_pos, end_pos, module_type_params)
45
- validate_position_range(start_pos, end_pos)
47
+ validate_position_range(buffer, start_pos, end_pos)
46
48
  encoding = buffer.content.encoding.name
47
- success, bytes = WASM::Runtime.instance.parse_type_params(buffer.content, encoding, start_pos, end_pos, module_type_params)
48
- raise_parsing_error(buffer, bytes) unless success
49
+ status, bytes = WASM::Runtime.instance.parse_type_params(buffer.content, encoding, start_pos, end_pos, module_type_params)
50
+ raise_parse_failure(buffer, status, bytes, start_pos, end_pos) unless status == WASM::Runtime::OK
49
51
 
50
52
  bytes.empty? ? nil : WASM::Deserializer.deserialize_node_list(bytes, buffer)
51
53
  end
52
54
 
53
55
  def _lex(buffer, end_pos)
54
56
  encoding = buffer.content.encoding.name
55
- _success, bytes = WASM::Runtime.instance.lex(buffer.content, encoding, end_pos)
57
+ _status, bytes = WASM::Runtime.instance.lex(buffer.content, encoding, end_pos)
56
58
 
57
59
  WASM::Deserializer.deserialize_tokens(bytes, buffer)
58
60
  end
59
61
 
60
62
  def _parse_inline_leading_annotation(buffer, start_pos, end_pos, variables)
61
- validate_position_range(start_pos, end_pos)
63
+ validate_position_range(buffer, start_pos, end_pos)
62
64
  validate_variables(variables)
63
65
  encoding = buffer.content.encoding.name
64
- success, bytes = WASM::Runtime.instance.parse_inline_leading_annotation(buffer.content, encoding, start_pos, end_pos, variables)
65
- raise_parsing_error(buffer, bytes) unless success
66
+ status, bytes = WASM::Runtime.instance.parse_inline_leading_annotation(buffer.content, encoding, start_pos, end_pos, variables)
67
+ raise_parse_failure(buffer, status, bytes, start_pos, end_pos) unless status == WASM::Runtime::OK
66
68
 
67
69
  deserialize_or_nil(bytes, buffer)
68
70
  end
69
71
 
70
72
  def _parse_inline_trailing_annotation(buffer, start_pos, end_pos, variables)
71
- validate_position_range(start_pos, end_pos)
73
+ validate_position_range(buffer, start_pos, end_pos)
72
74
  validate_variables(variables)
73
75
  encoding = buffer.content.encoding.name
74
- success, bytes = WASM::Runtime.instance.parse_inline_trailing_annotation(buffer.content, encoding, start_pos, end_pos, variables)
75
- raise_parsing_error(buffer, bytes) unless success
76
+ status, bytes = WASM::Runtime.instance.parse_inline_trailing_annotation(buffer.content, encoding, start_pos, end_pos, variables)
77
+ raise_parse_failure(buffer, status, bytes, start_pos, end_pos) unless status == WASM::Runtime::OK
76
78
 
77
79
  deserialize_or_nil(bytes, buffer)
78
80
  end
79
81
 
80
82
  private
81
83
 
82
- # Reject negative or reversed ranges before handing them to the parser,
83
- # matching validate_position_range in the C extension (main.c). A reversed
84
- # range would otherwise make the lexer loop forever inside WebAssembly.
85
- def validate_position_range(start_pos, end_pos)
84
+ # Reject the position ranges the parser cannot take, matching
85
+ # validate_position_range in the C extension (main.c).
86
+ #
87
+ # `end_pos` past the end of the buffer is fine: clamping with a large
88
+ # number instead of measuring the buffer is ordinary, and the lexer stops
89
+ # at the end of the input on its own.
90
+ def validate_position_range(buffer, start_pos, end_pos)
86
91
  if start_pos < 0 || end_pos < 0
87
92
  raise ArgumentError, "negative position range: #{start_pos}...#{end_pos}"
88
93
  end
89
94
  if start_pos > end_pos
90
95
  raise ArgumentError, "invalid position range: #{start_pos}...#{end_pos}"
91
96
  end
97
+
98
+ size = buffer.content.bytesize
99
+ if start_pos > size
100
+ raise ArgumentError, "position range starts past the end of the buffer: #{start_pos}...#{end_pos}, buffer is #{size} bytes"
101
+ end
102
+ end
103
+
104
+ # The WebAssembly entry points (rbs_wasm.c) build their parsers with the
105
+ # default options, so the optional syntax the C extension can enable is
106
+ # not reachable here. The public RBS::Parser API never enables it.
107
+ def validate_parser_options(enable_forwarding_params)
108
+ if enable_forwarding_params
109
+ raise NotImplementedError, "forwarding parameter syntax is not supported by the WebAssembly parser"
110
+ end
92
111
  end
93
112
 
94
113
  # Reject anything that is not nil or an Array of Symbols, matching
@@ -112,6 +131,24 @@ module RBS
112
131
  bytes.empty? ? nil : WASM::Deserializer.deserialize(bytes, buffer)
113
132
  end
114
133
 
134
+ # Raise for a status other than OK (see rbs_wasm.c).
135
+ #
136
+ # A negative status is about the range rather than the source text, so it
137
+ # comes with an empty result and an ArgumentError, as in the C extension
138
+ # (main.c). Starting past the end of the buffer is plain from the
139
+ # buffer's size and rejected above, so a start position that comes back
140
+ # rejected can only be one inside a character.
141
+ def raise_parse_failure(buffer, status, bytes, start_pos, end_pos)
142
+ case status
143
+ when WASM::Runtime::INVALID_START_POS
144
+ raise ArgumentError, "position range starts inside a character: #{start_pos}...#{end_pos}"
145
+ when WASM::Runtime::INVALID_RANGE
146
+ raise ArgumentError, "invalid position range: #{start_pos}...#{end_pos}"
147
+ else
148
+ raise_parsing_error(buffer, bytes)
149
+ end
150
+ end
151
+
115
152
  # Decodes the error blob written by set_error_result (rbs_wasm.c) and raises
116
153
  # the same error the C extension would (see raise_error in main.c).
117
154
  def raise_parsing_error(buffer, blob)
Binary file
@@ -16,6 +16,14 @@ module RBS
16
16
  class Runtime
17
17
  include MonitorMixin
18
18
 
19
+ # Statuses the parse entry points return (see rbs_wasm.c). A negative one
20
+ # is about the range the caller asked for rather than the source text,
21
+ # and comes with an empty result.
22
+ INVALID_START_POS = -2
23
+ INVALID_RANGE = -1
24
+ PARSE_ERROR = 0
25
+ OK = 1
26
+
19
27
  class << self
20
28
  def instance
21
29
  @instance ||= new
@@ -48,9 +56,9 @@ module RBS
48
56
  end
49
57
 
50
58
  # `content` is the whole buffer; `start_pos`/`end_pos` are the character
51
- # range within it to parse. Each method returns [success, bytes]: on success
52
- # `bytes` is the serialized AST, otherwise it is the error blob (see
53
- # set_error_result in rbs_wasm.c).
59
+ # range within it to parse. Each method returns [status, bytes]: with OK
60
+ # `bytes` is the serialized AST, with PARSE_ERROR it is the error blob (see
61
+ # set_error_result in rbs_wasm.c), and with a negative status it is empty.
54
62
 
55
63
  def parse_signature(content, encoding, start_pos, end_pos)
56
64
  run(content, encoding) { |ptr, len, enc_ptr, enc_len| @parse_signature.apply(ptr, len, enc_ptr, enc_len, start_pos, end_pos)[0] }
@@ -118,7 +126,7 @@ module RBS
118
126
  @memory.write(source_ptr, bytes.to_java_bytes)
119
127
  @memory.write(name_ptr, name.to_java_bytes) unless name_length.zero?
120
128
  status = yield(source_ptr, length, name_ptr, name_length)
121
- [status == 1, read_result]
129
+ [i32(status), read_result]
122
130
  ensure
123
131
  @free.apply(source_ptr)
124
132
  @free.apply(name_ptr)
@@ -156,6 +164,13 @@ module RBS
156
164
  end
157
165
  end
158
166
 
167
+ # A WebAssembly i32 comes back in a JVM long, so read the low 32 bits as
168
+ # signed: the negative statuses have to stay negative on this side.
169
+ def i32(value)
170
+ value &= 0xFFFF_FFFF
171
+ value >= 0x8000_0000 ? value - 0x1_0000_0000 : value
172
+ end
173
+
159
174
  def bool(value)
160
175
  value ? 1 : 0
161
176
  end
@@ -23,7 +23,7 @@ module RBS
23
23
  # :bool, :location_range, :location_range_list, :attr_ivar_name, or
24
24
  # [:enum, [value_or_nil, ...]].
25
25
  module SerializationSchema
26
- SYMBOL_TAG = 78
26
+ SYMBOL_TAG = 79
27
27
 
28
28
  SCHEMA = [
29
29
  nil, # tag 0 is reserved for NULL
@@ -91,7 +91,8 @@ module RBS
91
91
  [:node, "RBS::Types::Block", true, nil, [[:type, :node], [:required, :bool], [:self_type, :node]], false],
92
92
  [:node, "RBS::Types::ClassInstance", true, [[:name, true], [:args, false]], [[:name, :node], [:args, :node_list]], false],
93
93
  [:node, "RBS::Types::ClassSingleton", true, [[:name, true], [:args, false]], [[:name, :node], [:args, :node_list]], false],
94
- [:node, "RBS::Types::Function", false, nil, [[:required_positionals, :node_list], [:optional_positionals, :node_list], [:rest_positionals, :node], [:trailing_positionals, :node_list], [:required_keywords, :hash], [:optional_keywords, :hash], [:rest_keywords, :node], [:return_type, :node]], false],
94
+ [:node, "RBS::Types::Function", false, nil, [[:required_positionals, :node_list], [:optional_positionals, :node_list], [:rest_positionals, :node], [:trailing_positionals, :node_list], [:required_keywords, :hash], [:optional_keywords, :hash], [:rest_keywords, :node], [:forwarding, :node], [:return_type, :node]], false],
95
+ [:node, "RBS::Types::Function::ForwardingParam", true, nil, nil, false],
95
96
  [:node, "RBS::Types::Function::Param", true, [[:name, false]], [[:type, :node], [:name, :node]], false],
96
97
  [:node, "RBS::Types::Interface", true, [[:name, true], [:args, false]], [[:name, :node], [:args, :node_list]], false],
97
98
  [:node, "RBS::Types::Intersection", true, nil, [[:types, :node_list]], false],
data/rbs.gemspec CHANGED
@@ -67,14 +67,10 @@ Gem::Specification.new do |spec|
67
67
  spec.extensions = %w{ext/rbs_extension/extconf.rb}
68
68
  end
69
69
 
70
- if false
71
- spec.required_ruby_version = ">= 3.4"
72
- end
73
-
74
70
  spec.bindir = "exe"
75
71
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
76
72
  spec.require_paths = ["lib"]
77
- spec.required_ruby_version = ">= 3.2"
73
+ spec.required_ruby_version = ">= 3.3"
78
74
  spec.add_dependency "logger"
79
75
  spec.add_dependency "prism", ">= 1.6.0"
80
76
  spec.add_dependency "tsort"
data/schema/function.json CHANGED
@@ -78,10 +78,21 @@
78
78
  }
79
79
  ]
80
80
  },
81
+ "forwarding": {
82
+ "title": "Forwarding parameter",
83
+ "oneOf": [
84
+ {
85
+ "type": "object"
86
+ },
87
+ {
88
+ "type": "null"
89
+ }
90
+ ]
91
+ },
81
92
  "return_type": {
82
93
  "title": "Return type of a function",
83
94
  "$ref": "types.json"
84
95
  }
85
96
  },
86
- "required": ["required_positionals", "optional_positionals", "rest_positionals", "trailing_positionals", "required_keywords", "optional_keywords", "rest_keywords", "return_type"]
97
+ "required": ["required_positionals", "optional_positionals", "rest_positionals", "trailing_positionals", "required_keywords", "optional_keywords", "rest_keywords", "forwarding", "return_type"]
87
98
  }
@@ -45,6 +45,12 @@ module RBS
45
45
  # * Raises `GenericParameterMismatchError` if incompatible declaration is detected.
46
46
  #
47
47
  def validate_type_params: () -> void
48
+
49
+ # Returns a substitution that renames the type parameters of the declaration to the entry's type parameters (`#type_params`)
50
+ #
51
+ # Returns `nil` if the declaration uses the same type parameter names as `#type_params`.
52
+ #
53
+ def align_params: (declaration | ModuleEntry::declaration) -> Substitution?
48
54
  end
49
55
  end
50
56
  end
@@ -44,7 +44,22 @@ module RBS
44
44
  #
45
45
  def validate_type_params: () -> void
46
46
 
47
+ # Returns the self types of the declarations
48
+ #
49
+ # The type variables in the self types are aligned to `#type_params`,
50
+ # so that the self types from declarations with different type parameter
51
+ # names can be compared and used with `#type_params`.
52
+ #
53
+ # Note that the returned objects may be different from the ones in the
54
+ # declarations, but `#location` points to the original declaration.
55
+ #
47
56
  def self_types: () -> Array[AST::Declarations::Module::Self]
57
+
58
+ # Returns a substitution that renames the type parameters of the declaration to the entry's type parameters (`#type_params`)
59
+ #
60
+ # Returns `nil` if the declaration uses the same type parameter names as `#type_params`.
61
+ #
62
+ def align_params: (declaration | ClassEntry::declaration) -> Substitution?
48
63
  end
49
64
  end
50
65
  end