t-ruby 0.0.35 → 0.0.36
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.
- checksums.yaml +4 -4
- data/README.md +5 -4
- data/lib/t_ruby/benchmark.rb +16 -16
- data/lib/t_ruby/bundler_integration.rb +33 -35
- data/lib/t_ruby/cache.rb +46 -43
- data/lib/t_ruby/cli.rb +3 -3
- data/lib/t_ruby/compiler.rb +25 -27
- data/lib/t_ruby/config.rb +15 -11
- data/lib/t_ruby/constraint_checker.rb +14 -8
- data/lib/t_ruby/declaration_generator.rb +8 -8
- data/lib/t_ruby/doc_generator.rb +33 -33
- data/lib/t_ruby/docs_badge_generator.rb +8 -8
- data/lib/t_ruby/docs_example_verifier.rb +5 -6
- data/lib/t_ruby/error_handler.rb +21 -22
- data/lib/t_ruby/generic_type_parser.rb +3 -3
- data/lib/t_ruby/intersection_type_parser.rb +2 -2
- data/lib/t_ruby/ir.rb +21 -24
- data/lib/t_ruby/lsp_server.rb +128 -138
- data/lib/t_ruby/package_manager.rb +16 -18
- data/lib/t_ruby/parser.rb +7 -7
- data/lib/t_ruby/parser_combinator.rb +22 -22
- data/lib/t_ruby/rbs_generator.rb +2 -4
- data/lib/t_ruby/runtime_validator.rb +41 -37
- data/lib/t_ruby/smt_solver.rb +31 -29
- data/lib/t_ruby/type_alias_registry.rb +1 -0
- data/lib/t_ruby/type_checker.rb +64 -63
- data/lib/t_ruby/type_erasure.rb +2 -4
- data/lib/t_ruby/type_inferencer.rb +22 -26
- data/lib/t_ruby/union_type_parser.rb +3 -3
- data/lib/t_ruby/version.rb +1 -1
- data/lib/t_ruby/watcher.rb +18 -14
- metadata +4 -3
data/lib/t_ruby/config.rb
CHANGED
|
@@ -14,13 +14,13 @@ module TRuby
|
|
|
14
14
|
"source" => {
|
|
15
15
|
"include" => ["src"],
|
|
16
16
|
"exclude" => [],
|
|
17
|
-
"extensions" => [".trb"]
|
|
17
|
+
"extensions" => [".trb"],
|
|
18
18
|
},
|
|
19
19
|
"output" => {
|
|
20
20
|
"ruby_dir" => "build",
|
|
21
21
|
"rbs_dir" => nil,
|
|
22
22
|
"preserve_structure" => true,
|
|
23
|
-
"clean_before_build" => false
|
|
23
|
+
"clean_before_build" => false,
|
|
24
24
|
},
|
|
25
25
|
"compiler" => {
|
|
26
26
|
"strictness" => "standard",
|
|
@@ -30,15 +30,15 @@ module TRuby
|
|
|
30
30
|
"checks" => {
|
|
31
31
|
"no_implicit_any" => false,
|
|
32
32
|
"no_unused_vars" => false,
|
|
33
|
-
"strict_nil" => false
|
|
34
|
-
}
|
|
33
|
+
"strict_nil" => false,
|
|
34
|
+
},
|
|
35
35
|
},
|
|
36
36
|
"watch" => {
|
|
37
37
|
"paths" => [],
|
|
38
38
|
"debounce" => 100,
|
|
39
39
|
"clear_screen" => false,
|
|
40
|
-
"on_success" => nil
|
|
41
|
-
}
|
|
40
|
+
"on_success" => nil,
|
|
41
|
+
},
|
|
42
42
|
}.freeze
|
|
43
43
|
|
|
44
44
|
# Legacy keys for migration detection
|
|
@@ -249,7 +249,7 @@ module TRuby
|
|
|
249
249
|
value = strictness
|
|
250
250
|
return if VALID_STRICTNESS.include?(value)
|
|
251
251
|
|
|
252
|
-
raise ConfigError, "Invalid compiler.strictness: '#{value}'. Must be one of: #{VALID_STRICTNESS.join(
|
|
252
|
+
raise ConfigError, "Invalid compiler.strictness: '#{value}'. Must be one of: #{VALID_STRICTNESS.join(", ")}"
|
|
253
253
|
end
|
|
254
254
|
|
|
255
255
|
def load_raw_config(config_path)
|
|
@@ -308,8 +308,8 @@ module TRuby
|
|
|
308
308
|
result = deep_dup(DEFAULT_CONFIG)
|
|
309
309
|
|
|
310
310
|
# Migrate emit -> compiler.generate_rbs
|
|
311
|
-
if raw_config["emit"]
|
|
312
|
-
result["compiler"]["generate_rbs"] = raw_config["emit"]["rbs"]
|
|
311
|
+
if raw_config["emit"]&.key?("rbs")
|
|
312
|
+
result["compiler"]["generate_rbs"] = raw_config["emit"]["rbs"]
|
|
313
313
|
end
|
|
314
314
|
|
|
315
315
|
# Migrate paths -> source.include and output.ruby_dir
|
|
@@ -344,8 +344,12 @@ module TRuby
|
|
|
344
344
|
end
|
|
345
345
|
|
|
346
346
|
def deep_dup(hash)
|
|
347
|
-
hash.
|
|
348
|
-
|
|
347
|
+
hash.transform_values do |value|
|
|
348
|
+
if value.is_a?(Hash)
|
|
349
|
+
deep_dup(value)
|
|
350
|
+
else
|
|
351
|
+
(value.is_a?(Array) ? value.dup : value)
|
|
352
|
+
end
|
|
349
353
|
end
|
|
350
354
|
end
|
|
351
355
|
|
|
@@ -42,7 +42,7 @@ module TRuby
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def satisfied?(value_type)
|
|
45
|
-
@left_type
|
|
45
|
+
[@left_type, @right_type].include?(value_type)
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -62,6 +62,7 @@ module TRuby
|
|
|
62
62
|
return false unless value.is_a?(Numeric)
|
|
63
63
|
return false if @min && value < @min
|
|
64
64
|
return false if @max && value > @max
|
|
65
|
+
|
|
65
66
|
true
|
|
66
67
|
end
|
|
67
68
|
|
|
@@ -78,6 +79,7 @@ module TRuby
|
|
|
78
79
|
return "#{@min}..#{@max}" if @min && @max
|
|
79
80
|
return ">= #{@min}" if @min
|
|
80
81
|
return "<= #{@max}" if @max
|
|
82
|
+
|
|
81
83
|
""
|
|
82
84
|
end
|
|
83
85
|
end
|
|
@@ -94,6 +96,7 @@ module TRuby
|
|
|
94
96
|
|
|
95
97
|
def satisfied?(value)
|
|
96
98
|
return false unless value.is_a?(String)
|
|
99
|
+
|
|
97
100
|
@pattern.match?(value)
|
|
98
101
|
end
|
|
99
102
|
|
|
@@ -145,10 +148,12 @@ module TRuby
|
|
|
145
148
|
|
|
146
149
|
def satisfied?(value)
|
|
147
150
|
return false unless value.respond_to?(:length)
|
|
151
|
+
|
|
148
152
|
len = value.length
|
|
149
153
|
return len == @exact_length if @exact_length
|
|
150
154
|
return false if @min_length && len < @min_length
|
|
151
155
|
return false if @max_length && len > @max_length
|
|
156
|
+
|
|
152
157
|
true
|
|
153
158
|
end
|
|
154
159
|
|
|
@@ -167,6 +172,7 @@ module TRuby
|
|
|
167
172
|
|
|
168
173
|
def build_condition
|
|
169
174
|
return "length == #{@exact_length}" if @exact_length
|
|
175
|
+
|
|
170
176
|
parts = []
|
|
171
177
|
parts << "length >= #{@min_length}" if @min_length
|
|
172
178
|
parts << "length <= #{@max_length}" if @max_length
|
|
@@ -187,7 +193,7 @@ module TRuby
|
|
|
187
193
|
def register(name, base_type:, constraints: [])
|
|
188
194
|
@constraints[name] = {
|
|
189
195
|
base_type: base_type,
|
|
190
|
-
constraints: constraints
|
|
196
|
+
constraints: constraints,
|
|
191
197
|
}
|
|
192
198
|
end
|
|
193
199
|
|
|
@@ -213,7 +219,7 @@ module TRuby
|
|
|
213
219
|
return {
|
|
214
220
|
name: name,
|
|
215
221
|
base_type: base_type,
|
|
216
|
-
constraints: [BoundsConstraint.new(subtype: name, supertype: base_type)]
|
|
222
|
+
constraints: [BoundsConstraint.new(subtype: name, supertype: base_type)],
|
|
217
223
|
}
|
|
218
224
|
end
|
|
219
225
|
|
|
@@ -325,8 +331,8 @@ module TRuby
|
|
|
325
331
|
end
|
|
326
332
|
|
|
327
333
|
# Pattern: /regex/
|
|
328
|
-
if condition.match?(
|
|
329
|
-
match = condition.match(
|
|
334
|
+
if condition.match?(%r{^/(.+)/$})
|
|
335
|
+
match = condition.match(%r{^/(.+)/$})
|
|
330
336
|
constraints << PatternConstraint.new(base_type: base_type, pattern: match[1])
|
|
331
337
|
end
|
|
332
338
|
|
|
@@ -353,7 +359,7 @@ module TRuby
|
|
|
353
359
|
# Predicate: positive?, negative?, empty?, etc.
|
|
354
360
|
if condition.match?(/^(\w+)\?$/)
|
|
355
361
|
match = condition.match(/^(\w+)\?$/)
|
|
356
|
-
constraints << PredicateConstraint.new(base_type: base_type, predicate: "#{match[1]}?"
|
|
362
|
+
constraints << PredicateConstraint.new(base_type: base_type, predicate: :"#{match[1]}?")
|
|
357
363
|
end
|
|
358
364
|
|
|
359
365
|
constraints
|
|
@@ -367,7 +373,7 @@ module TRuby
|
|
|
367
373
|
when "Numeric" then value.is_a?(Numeric)
|
|
368
374
|
when "Array" then value.is_a?(Array)
|
|
369
375
|
when "Hash" then value.is_a?(Hash)
|
|
370
|
-
when "Boolean" then
|
|
376
|
+
when "Boolean" then [true, false].include?(value)
|
|
371
377
|
when "Symbol" then value.is_a?(Symbol)
|
|
372
378
|
else true # Unknown types pass through
|
|
373
379
|
end
|
|
@@ -388,7 +394,7 @@ module TRuby
|
|
|
388
394
|
@types[name] = {
|
|
389
395
|
base_type: base_type,
|
|
390
396
|
constraints: constraints,
|
|
391
|
-
defined_at: caller_locations(1, 1).first
|
|
397
|
+
defined_at: caller_locations(1, 1).first,
|
|
392
398
|
}
|
|
393
399
|
@checker.register(name, base_type: base_type, constraints: constraints)
|
|
394
400
|
end
|
|
@@ -195,7 +195,7 @@ module TRuby
|
|
|
195
195
|
{
|
|
196
196
|
type_aliases: @type_aliases,
|
|
197
197
|
interfaces: @interfaces,
|
|
198
|
-
functions: @functions
|
|
198
|
+
functions: @functions,
|
|
199
199
|
}
|
|
200
200
|
end
|
|
201
201
|
|
|
@@ -230,13 +230,13 @@ module TRuby
|
|
|
230
230
|
|
|
231
231
|
@search_paths.each do |path|
|
|
232
232
|
full_path = File.join(path, file_name)
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
233
|
+
next unless File.exist?(full_path) && !@loaded_files.include?(full_path)
|
|
234
|
+
|
|
235
|
+
parser = DeclarationParser.new
|
|
236
|
+
parser.parse_file(full_path)
|
|
237
|
+
@loaded_declarations.merge(parser)
|
|
238
|
+
@loaded_files.add(full_path)
|
|
239
|
+
return true
|
|
240
240
|
end
|
|
241
241
|
|
|
242
242
|
false
|
data/lib/t_ruby/doc_generator.rb
CHANGED
|
@@ -14,7 +14,7 @@ module TRuby
|
|
|
14
14
|
types: {},
|
|
15
15
|
interfaces: {},
|
|
16
16
|
functions: {},
|
|
17
|
-
modules: {}
|
|
17
|
+
modules: {},
|
|
18
18
|
}
|
|
19
19
|
@parser = nil
|
|
20
20
|
end
|
|
@@ -84,7 +84,7 @@ module TRuby
|
|
|
84
84
|
md << ""
|
|
85
85
|
|
|
86
86
|
if info[:type_params]&.any?
|
|
87
|
-
md << "**Type Parameters:** `<#{info[:type_params].join(
|
|
87
|
+
md << "**Type Parameters:** `<#{info[:type_params].join(", ")}>`"
|
|
88
88
|
md << ""
|
|
89
89
|
end
|
|
90
90
|
|
|
@@ -94,7 +94,7 @@ module TRuby
|
|
|
94
94
|
md << "| Name | Type | Description |"
|
|
95
95
|
md << "|------|------|-------------|"
|
|
96
96
|
info[:members].each do |member|
|
|
97
|
-
md << "| `#{member[:name]}` | `#{member[:type]}` | #{member[:description] ||
|
|
97
|
+
md << "| `#{member[:name]}` | `#{member[:type]}` | #{member[:description] || "-"} |"
|
|
98
98
|
end
|
|
99
99
|
md << ""
|
|
100
100
|
end
|
|
@@ -114,9 +114,9 @@ module TRuby
|
|
|
114
114
|
|
|
115
115
|
# Signature
|
|
116
116
|
params = info[:params]&.map { |p| "#{p[:name]}: #{p[:type]}" }&.join(", ") || ""
|
|
117
|
-
type_params = info[:type_params]&.any? ? "<#{info[:type_params].join(
|
|
117
|
+
type_params = info[:type_params]&.any? ? "<#{info[:type_params].join(", ")}>" : ""
|
|
118
118
|
md << "```ruby"
|
|
119
|
-
md << "def #{name}#{type_params}(#{params}): #{info[:return_type] ||
|
|
119
|
+
md << "def #{name}#{type_params}(#{params}): #{info[:return_type] || "void"}"
|
|
120
120
|
md << "```"
|
|
121
121
|
md << ""
|
|
122
122
|
|
|
@@ -126,7 +126,7 @@ module TRuby
|
|
|
126
126
|
md << "| Name | Type | Description |"
|
|
127
127
|
md << "|------|------|-------------|"
|
|
128
128
|
info[:params].each do |param|
|
|
129
|
-
md << "| `#{param[:name]}` | `#{param[:type]}` | #{param[:description] ||
|
|
129
|
+
md << "| `#{param[:name]}` | `#{param[:type]}` | #{param[:description] || "-"} |"
|
|
130
130
|
end
|
|
131
131
|
md << ""
|
|
132
132
|
end
|
|
@@ -147,13 +147,13 @@ module TRuby
|
|
|
147
147
|
files.each { |file| parse_file(file) }
|
|
148
148
|
|
|
149
149
|
File.write(output_path, JSON.pretty_generate({
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
150
|
+
generated_at: Time.now.iso8601,
|
|
151
|
+
version: TRuby::VERSION,
|
|
152
|
+
types: @docs[:types],
|
|
153
|
+
interfaces: @docs[:interfaces],
|
|
154
|
+
functions: @docs[:functions],
|
|
155
|
+
modules: @docs[:modules],
|
|
156
|
+
}))
|
|
157
157
|
|
|
158
158
|
puts "JSON documentation generated: #{output_path}"
|
|
159
159
|
end
|
|
@@ -191,7 +191,7 @@ module TRuby
|
|
|
191
191
|
type_params: type_params&.split(/\s*,\s*/),
|
|
192
192
|
definition: definition.strip,
|
|
193
193
|
description: doc_comments["type:#{name}"],
|
|
194
|
-
source: relative_path
|
|
194
|
+
source: relative_path,
|
|
195
195
|
}
|
|
196
196
|
end
|
|
197
197
|
|
|
@@ -240,11 +240,11 @@ module TRuby
|
|
|
240
240
|
content.scan(/interface\s+(\w+)(?:<([^>]+)>)?\s*\n((?:(?!^end).)*?)^end/m) do |name, type_params, body|
|
|
241
241
|
members = []
|
|
242
242
|
|
|
243
|
-
body.scan(/^\s*(\w+[
|
|
243
|
+
body.scan(/^\s*(\w+[?!]?)\s*:\s*(.+)$/) do |member_name, member_type|
|
|
244
244
|
members << {
|
|
245
245
|
name: member_name,
|
|
246
246
|
type: member_type.strip,
|
|
247
|
-
description: doc_comments["member:#{name}.#{member_name}"]
|
|
247
|
+
description: doc_comments["member:#{name}.#{member_name}"],
|
|
248
248
|
}
|
|
249
249
|
end
|
|
250
250
|
|
|
@@ -253,21 +253,21 @@ module TRuby
|
|
|
253
253
|
type_params: type_params&.split(/\s*,\s*/),
|
|
254
254
|
members: members,
|
|
255
255
|
description: doc_comments["interface:#{name}"],
|
|
256
|
-
source: source
|
|
256
|
+
source: source,
|
|
257
257
|
}
|
|
258
258
|
end
|
|
259
259
|
end
|
|
260
260
|
|
|
261
261
|
def parse_functions(content, source, doc_comments)
|
|
262
262
|
# Match function definitions
|
|
263
|
-
content.scan(/def\s+(\w+[
|
|
263
|
+
content.scan(/def\s+(\w+[?!]?)(?:<([^>]+)>)?\s*\(([^)]*)\)(?:\s*:\s*(.+?))?(?:\n|$)/) do |name, type_params, params_str, return_type|
|
|
264
264
|
params = []
|
|
265
265
|
|
|
266
266
|
params_str.scan(/(\w+)\s*:\s*([^,]+)/) do |param_name, param_type|
|
|
267
267
|
params << {
|
|
268
268
|
name: param_name,
|
|
269
269
|
type: param_type.strip,
|
|
270
|
-
description: doc_comments["param:#{name}.#{param_name}"]
|
|
270
|
+
description: doc_comments["param:#{name}.#{param_name}"],
|
|
271
271
|
}
|
|
272
272
|
end
|
|
273
273
|
|
|
@@ -277,7 +277,7 @@ module TRuby
|
|
|
277
277
|
params: params,
|
|
278
278
|
return_type: return_type&.strip,
|
|
279
279
|
description: doc_comments["def:#{name}"],
|
|
280
|
-
source: source
|
|
280
|
+
source: source,
|
|
281
281
|
}
|
|
282
282
|
end
|
|
283
283
|
end
|
|
@@ -360,15 +360,15 @@ module TRuby
|
|
|
360
360
|
def generate_search_index(output_dir)
|
|
361
361
|
search_data = []
|
|
362
362
|
|
|
363
|
-
@docs[:types].
|
|
363
|
+
@docs[:types].each_key do |name|
|
|
364
364
|
search_data << { type: "type", name: name, url: "types/#{name}.html" }
|
|
365
365
|
end
|
|
366
366
|
|
|
367
|
-
@docs[:interfaces].
|
|
367
|
+
@docs[:interfaces].each_key do |name|
|
|
368
368
|
search_data << { type: "interface", name: name, url: "interfaces/#{name}.html" }
|
|
369
369
|
end
|
|
370
370
|
|
|
371
|
-
@docs[:functions].
|
|
371
|
+
@docs[:functions].each_key do |name|
|
|
372
372
|
search_data << { type: "function", name: name, url: "functions/#{name}.html" }
|
|
373
373
|
end
|
|
374
374
|
|
|
@@ -392,8 +392,8 @@ module TRuby
|
|
|
392
392
|
<body>
|
|
393
393
|
<a href="../index.html">← Back to Index</a>
|
|
394
394
|
<h1>type #{name}</h1>
|
|
395
|
-
#{
|
|
396
|
-
<pre>type #{name}#{
|
|
395
|
+
#{"<p>#{info[:description]}</p>" if info[:description]}
|
|
396
|
+
<pre>type #{name}#{"<#{info[:type_params].join(", ")}>" if info[:type_params]} = #{info[:definition]}</pre>
|
|
397
397
|
<p class="meta">Source: <code>#{info[:source]}</code></p>
|
|
398
398
|
</body>
|
|
399
399
|
</html>
|
|
@@ -402,7 +402,7 @@ module TRuby
|
|
|
402
402
|
|
|
403
403
|
def generate_interface_html(name, info)
|
|
404
404
|
members_html = info[:members]&.map do |m|
|
|
405
|
-
"<tr><td><code>#{m[:name]}</code></td><td><code>#{m[:type]}</code></td><td>#{m[:description] ||
|
|
405
|
+
"<tr><td><code>#{m[:name]}</code></td><td><code>#{m[:type]}</code></td><td>#{m[:description] || "-"}</td></tr>"
|
|
406
406
|
end&.join || ""
|
|
407
407
|
|
|
408
408
|
<<~HTML
|
|
@@ -423,8 +423,8 @@ module TRuby
|
|
|
423
423
|
</head>
|
|
424
424
|
<body>
|
|
425
425
|
<a href="../index.html">← Back to Index</a>
|
|
426
|
-
<h1>interface #{name}#{
|
|
427
|
-
#{
|
|
426
|
+
<h1>interface #{name}#{"<#{info[:type_params].join(", ")}>" if info[:type_params]}</h1>
|
|
427
|
+
#{"<p>#{info[:description]}</p>" if info[:description]}
|
|
428
428
|
#{"<h2>Members</h2><table><tr><th>Name</th><th>Type</th><th>Description</th></tr>#{members_html}</table>" unless members_html.empty?}
|
|
429
429
|
<p class="meta">Source: <code>#{info[:source]}</code></p>
|
|
430
430
|
</body>
|
|
@@ -434,11 +434,11 @@ module TRuby
|
|
|
434
434
|
|
|
435
435
|
def generate_function_html(name, info)
|
|
436
436
|
params_html = info[:params]&.map do |p|
|
|
437
|
-
"<tr><td><code>#{p[:name]}</code></td><td><code>#{p[:type]}</code></td><td>#{p[:description] ||
|
|
437
|
+
"<tr><td><code>#{p[:name]}</code></td><td><code>#{p[:type]}</code></td><td>#{p[:description] || "-"}</td></tr>"
|
|
438
438
|
end&.join || ""
|
|
439
439
|
|
|
440
440
|
params_sig = info[:params]&.map { |p| "#{p[:name]}: #{p[:type]}" }&.join(", ") || ""
|
|
441
|
-
type_params = info[:type_params]&.any? ? "<#{info[:type_params].join(
|
|
441
|
+
type_params = info[:type_params]&.any? ? "<#{info[:type_params].join(", ")}>" : ""
|
|
442
442
|
|
|
443
443
|
<<~HTML
|
|
444
444
|
<!DOCTYPE html>
|
|
@@ -459,12 +459,12 @@ module TRuby
|
|
|
459
459
|
<body>
|
|
460
460
|
<a href="../index.html">← Back to Index</a>
|
|
461
461
|
<h1>#{name}</h1>
|
|
462
|
-
#{
|
|
462
|
+
#{"<p>#{info[:description]}</p>" if info[:description]}
|
|
463
463
|
<h2>Signature</h2>
|
|
464
|
-
<pre>def #{name}#{type_params}(#{params_sig}): #{info[:return_type] ||
|
|
464
|
+
<pre>def #{name}#{type_params}(#{params_sig}): #{info[:return_type] || "void"}</pre>
|
|
465
465
|
#{"<h2>Parameters</h2><table><tr><th>Name</th><th>Type</th><th>Description</th></tr>#{params_html}</table>" unless params_html.empty?}
|
|
466
466
|
<h2>Returns</h2>
|
|
467
|
-
<p><code>#{info[:return_type] ||
|
|
467
|
+
<p><code>#{info[:return_type] || "void"}</code></p>
|
|
468
468
|
<p class="meta">Source: <code>#{info[:source]}</code></p>
|
|
469
469
|
</body>
|
|
470
470
|
</html>
|
|
@@ -148,16 +148,16 @@ module TRuby
|
|
|
148
148
|
markdown += "Pass rate: #{file_summary[:pass_rate]}% (#{file_summary[:passed]}/#{file_summary[:total]})\n\n"
|
|
149
149
|
|
|
150
150
|
failed_results = file_results.select(&:fail?)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
151
|
+
next unless failed_results.any?
|
|
152
|
+
|
|
153
|
+
markdown += "**Failed examples:**\n\n"
|
|
154
|
+
failed_results.each do |result|
|
|
155
|
+
markdown += "- Line #{result.line_number}:\n"
|
|
156
|
+
result.errors.each do |error|
|
|
157
|
+
markdown += " - #{error}\n"
|
|
158
158
|
end
|
|
159
|
-
markdown += "\n"
|
|
160
159
|
end
|
|
160
|
+
markdown += "\n"
|
|
161
161
|
end
|
|
162
162
|
|
|
163
163
|
File.write(output_path, markdown)
|
|
@@ -173,12 +173,11 @@ module TRuby
|
|
|
173
173
|
|
|
174
174
|
def verify_ruby_example(example)
|
|
175
175
|
# For Ruby examples, just validate syntax
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
end
|
|
176
|
+
|
|
177
|
+
RubyVM::InstructionSequence.compile(example.code)
|
|
178
|
+
pass_result(example)
|
|
179
|
+
rescue SyntaxError => e
|
|
180
|
+
fail_result(example, ["Ruby syntax error: #{e.message}"])
|
|
182
181
|
end
|
|
183
182
|
|
|
184
183
|
def verify_rbs_example(example)
|
data/lib/t_ruby/error_handler.rb
CHANGED
|
@@ -69,7 +69,7 @@ module TRuby
|
|
|
69
69
|
next unless line.match?(/^\s*def\s+/)
|
|
70
70
|
|
|
71
71
|
# Check for unclosed parenthesis
|
|
72
|
-
if line.match?(/def\s+\w+\([^)]*$/) &&
|
|
72
|
+
if line.match?(/def\s+\w+\([^)]*$/) && @lines[(idx + 1)..].none? { |l| l.match?(/\)/) }
|
|
73
73
|
@errors << "Line #{idx + 1}: Potential unclosed parenthesis in function definition"
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -84,6 +84,7 @@ module TRuby
|
|
|
84
84
|
def check_method_signature_errors
|
|
85
85
|
@lines.each_with_index do |line, idx|
|
|
86
86
|
next unless line.match?(/^\s*def\s+/)
|
|
87
|
+
|
|
87
88
|
check_single_method_signature(line, idx)
|
|
88
89
|
end
|
|
89
90
|
end
|
|
@@ -96,7 +97,7 @@ module TRuby
|
|
|
96
97
|
end
|
|
97
98
|
|
|
98
99
|
# Pattern 2: Check for text after closing paren without colon (e.g., "def test() something")
|
|
99
|
-
if match = line.match(/def\s+\w+\s*\([^)]*\)\s*([^:\s].+?)\s*$/)
|
|
100
|
+
if (match = line.match(/def\s+\w+\s*\([^)]*\)\s*([^:\s].+?)\s*$/))
|
|
100
101
|
trailing = match[1].strip
|
|
101
102
|
# Allow if it's just end-of-line content or a valid Ruby block start
|
|
102
103
|
unless trailing.empty? || trailing.start_with?("#") || trailing == "end"
|
|
@@ -112,13 +113,13 @@ module TRuby
|
|
|
112
113
|
end
|
|
113
114
|
|
|
114
115
|
# Pattern 4: Extract and validate return type
|
|
115
|
-
if match = line.match(/def\s+\w+\s*\([^)]*\)\s*:\s*(.+?)\s*$/)
|
|
116
|
+
if (match = line.match(/def\s+\w+\s*\([^)]*\)\s*:\s*(.+?)\s*$/))
|
|
116
117
|
return_type_str = match[1].strip
|
|
117
118
|
validate_type_expression(return_type_str, idx, "return type")
|
|
118
119
|
end
|
|
119
120
|
|
|
120
121
|
# Pattern 5: Extract and validate parameter types
|
|
121
|
-
if match = line.match(/def\s+\w+\s*\(([^)]+)\)/)
|
|
122
|
+
if (match = line.match(/def\s+\w+\s*\(([^)]+)\)/))
|
|
122
123
|
params_str = match[1]
|
|
123
124
|
validate_parameter_types_expression(params_str, idx)
|
|
124
125
|
end
|
|
@@ -189,7 +190,7 @@ module TRuby
|
|
|
189
190
|
# Use TypeParser to validate
|
|
190
191
|
result = @type_parser.parse(type_str)
|
|
191
192
|
if result[:success]
|
|
192
|
-
remaining = type_str[result[:position] || 0..]&.strip
|
|
193
|
+
remaining = type_str[(result[:position] || 0)..]&.strip
|
|
193
194
|
if remaining && !remaining.empty? && result[:remaining] && !result[:remaining].strip.empty?
|
|
194
195
|
@errors << "Line #{line_idx + 1}: Unexpected token after #{context} '#{type_str}'"
|
|
195
196
|
end
|
|
@@ -207,17 +208,17 @@ module TRuby
|
|
|
207
208
|
next if param.empty?
|
|
208
209
|
|
|
209
210
|
# Check for param: Type pattern
|
|
210
|
-
|
|
211
|
-
param_name = match[1]
|
|
212
|
-
type_str = match[2].strip
|
|
211
|
+
next unless (match = param.match(/^(\w+)\s*:\s*(.+)$/))
|
|
213
212
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
next
|
|
217
|
-
end
|
|
213
|
+
param_name = match[1]
|
|
214
|
+
type_str = match[2].strip
|
|
218
215
|
|
|
219
|
-
|
|
216
|
+
if type_str.empty?
|
|
217
|
+
@errors << "Line #{line_idx + 1}: Expected type after colon for parameter '#{param_name}'"
|
|
218
|
+
next
|
|
220
219
|
end
|
|
220
|
+
|
|
221
|
+
validate_type_expression(type_str, line_idx, "parameter type for '#{param_name}'")
|
|
221
222
|
end
|
|
222
223
|
end
|
|
223
224
|
|
|
@@ -235,7 +236,7 @@ module TRuby
|
|
|
235
236
|
depth -= 1
|
|
236
237
|
current += char
|
|
237
238
|
when ","
|
|
238
|
-
if depth
|
|
239
|
+
if depth.zero?
|
|
239
240
|
result << current.strip
|
|
240
241
|
current = ""
|
|
241
242
|
else
|
|
@@ -262,10 +263,8 @@ module TRuby
|
|
|
262
263
|
return_type = match[2]&.strip
|
|
263
264
|
|
|
264
265
|
# Check return type if it's a simple type name
|
|
265
|
-
if return_type && return_type.
|
|
266
|
-
|
|
267
|
-
@errors << "Line #{idx + 1}: Unknown return type '#{return_type}'"
|
|
268
|
-
end
|
|
266
|
+
if return_type&.match?(/^\w+$/) && !(VALID_TYPES.include?(return_type) || @type_aliases.key?(return_type))
|
|
267
|
+
@errors << "Line #{idx + 1}: Unknown return type '#{return_type}'"
|
|
269
268
|
end
|
|
270
269
|
|
|
271
270
|
# Check parameter types
|
|
@@ -286,10 +285,10 @@ module TRuby
|
|
|
286
285
|
next unless param_type
|
|
287
286
|
|
|
288
287
|
# Only check simple type names against VALID_TYPES
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
288
|
+
next unless param_type.match?(/^\w+$/)
|
|
289
|
+
next if VALID_TYPES.include?(param_type) || @type_aliases.key?(param_type)
|
|
290
|
+
|
|
291
|
+
@errors << "Line #{line_idx + 1}: Unknown parameter type '#{param_type}'"
|
|
293
292
|
end
|
|
294
293
|
end
|
|
295
294
|
|
|
@@ -30,7 +30,7 @@ module TRuby
|
|
|
30
30
|
{
|
|
31
31
|
type: :generic,
|
|
32
32
|
base: base_name,
|
|
33
|
-
params: params
|
|
33
|
+
params: params,
|
|
34
34
|
}
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -50,7 +50,7 @@ module TRuby
|
|
|
50
50
|
depth -= 1
|
|
51
51
|
current += char
|
|
52
52
|
when ","
|
|
53
|
-
if depth
|
|
53
|
+
if depth.zero?
|
|
54
54
|
params << current.strip
|
|
55
55
|
current = ""
|
|
56
56
|
else
|
|
@@ -61,7 +61,7 @@ module TRuby
|
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
params << current.strip if current.length
|
|
64
|
+
params << current.strip if current.length.positive?
|
|
65
65
|
params
|
|
66
66
|
end
|
|
67
67
|
end
|
|
@@ -17,13 +17,13 @@ module TRuby
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
19
|
def parse_intersection
|
|
20
|
-
members = @type_string.split("&").map
|
|
20
|
+
members = @type_string.split("&").map(&:strip).compact
|
|
21
21
|
|
|
22
22
|
{
|
|
23
23
|
type: :intersection,
|
|
24
24
|
members: members,
|
|
25
25
|
has_duplicates: members.length != members.uniq.length,
|
|
26
|
-
unique_members: members.uniq
|
|
26
|
+
unique_members: members.uniq,
|
|
27
27
|
}
|
|
28
28
|
end
|
|
29
29
|
end
|