Skip to content

Commit 887d2f9

Browse files
authored
Merge branch 'master' into random-formatter
2 parents 0d5e501 + 3881171 commit 887d2f9

35 files changed

+983
-442
lines changed

Gemfile.lock

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ GEM
5454
strong_json (>= 1.1, < 2.2)
5555
i18n (1.14.7)
5656
concurrent-ruby (~> 1.0)
57-
json (2.13.2)
57+
json (2.15.0)
5858
json-schema (6.0.0)
5959
addressable (~> 2.8)
6060
bigdecimal (~> 3.1)
@@ -64,7 +64,7 @@ GEM
6464
rb-fsevent (~> 0.10, >= 0.10.3)
6565
rb-inotify (~> 0.9, >= 0.9.10)
6666
logger (1.7.0)
67-
marcel (1.0.4)
67+
marcel (1.1.0)
6868
memory_profiler (1.1.0)
6969
minitest (5.25.5)
7070
mutex_m (0.3.0)
@@ -73,11 +73,11 @@ GEM
7373
net-smtp (0.5.1)
7474
net-protocol
7575
nkf (0.2.0)
76-
nokogiri (1.18.9-aarch64-linux-gnu)
76+
nokogiri (1.18.10-aarch64-linux-gnu)
7777
racc (~> 1.4)
78-
nokogiri (1.18.9-arm64-darwin)
78+
nokogiri (1.18.10-arm64-darwin)
7979
racc (~> 1.4)
80-
nokogiri (1.18.9-x86_64-linux-gnu)
80+
nokogiri (1.18.10-x86_64-linux-gnu)
8181
racc (~> 1.4)
8282
ostruct (0.6.3)
8383
parallel (1.27.0)
@@ -86,7 +86,7 @@ GEM
8686
racc
8787
pathname (0.4.0)
8888
power_assert (2.0.5)
89-
prism (1.4.0)
89+
prism (1.5.1)
9090
pstore (0.2.0)
9191
psych (4.0.6)
9292
stringio
@@ -105,7 +105,7 @@ GEM
105105
rdoc (6.14.2)
106106
erb
107107
psych (>= 4.0.0)
108-
regexp_parser (2.11.2)
108+
regexp_parser (2.11.3)
109109
rspec (3.13.1)
110110
rspec-core (~> 3.13.0)
111111
rspec-expectations (~> 3.13.0)
@@ -118,19 +118,19 @@ GEM
118118
rspec-mocks (3.13.5)
119119
diff-lcs (>= 1.2.0, < 2.0)
120120
rspec-support (~> 3.13.0)
121-
rspec-support (3.13.5)
122-
rubocop (1.80.2)
121+
rspec-support (3.13.6)
122+
rubocop (1.81.1)
123123
json (~> 2.3)
124124
language_server-protocol (~> 3.17.0.2)
125125
lint_roller (~> 1.1.0)
126126
parallel (~> 1.10)
127127
parser (>= 3.3.0.2)
128128
rainbow (>= 2.2.2, < 4.0)
129129
regexp_parser (>= 2.9.3, < 3.0)
130-
rubocop-ast (>= 1.46.0, < 2.0)
130+
rubocop-ast (>= 1.47.1, < 2.0)
131131
ruby-progressbar (~> 1.7)
132132
unicode-display_width (>= 2.4.0, < 4.0)
133-
rubocop-ast (1.46.0)
133+
rubocop-ast (1.47.1)
134134
parser (>= 3.3.7.2)
135135
prism (~> 1.4)
136136
rubocop-on-rbs (1.8.0)
@@ -175,9 +175,9 @@ GEM
175175
tsort (0.2.0)
176176
tzinfo (2.0.6)
177177
concurrent-ruby (~> 1.0)
178-
unicode-display_width (3.1.5)
179-
unicode-emoji (~> 4.0, >= 4.0.4)
180-
unicode-emoji (4.0.4)
178+
unicode-display_width (3.2.0)
179+
unicode-emoji (~> 4.1)
180+
unicode-emoji (4.1.0)
181181
uri (1.0.3)
182182
zlib (3.2.1)
183183

Rakefile

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,12 @@ test_config = lambda do |t|
2525
t.test_files = FileList["test/**/*_test.rb"].reject do |path|
2626
path =~ %r{test/stdlib/}
2727
end
28-
t.verbose = true
29-
t.options = '-v'
28+
if defined?(RubyMemcheck)
29+
if t.is_a?(RubyMemcheck::TestTask)
30+
t.verbose = true
31+
t.options = '-v'
32+
end
33+
end
3034
end
3135

3236
Rake::TestTask.new(test: :compile, &test_config)
@@ -537,3 +541,17 @@ task :compile_c99 do
537541
ensure
538542
ENV.delete("TEST_NO_C23")
539543
end
544+
545+
task :prepare_bench do
546+
ENV.delete("DEBUG")
547+
Rake::Task[:"clobber"].invoke
548+
Rake::Task[:"templates"].invoke
549+
Rake::Task[:"compile"].invoke
550+
end
551+
552+
task :prepare_profiling do
553+
ENV["DEBUG"] = "1"
554+
Rake::Task[:"clobber"].invoke
555+
Rake::Task[:"templates"].invoke
556+
Rake::Task[:"compile"].invoke
557+
end

bin/benchmark-parse-each.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
require "rbs"
2+
require "benchmark/ips"
3+
require "csv"
4+
5+
if ARGV.empty?
6+
STDERR.puts "Usage: #{$0} FILE..."
7+
STDERR.puts ""
8+
STDERR.puts "Benchmark parsing each file separately and output the result as CSV."
9+
exit 1
10+
end
11+
12+
results = []
13+
14+
total = ARGV.size
15+
ARGV.each_with_index do |file, index|
16+
GC.start
17+
18+
STDERR.puts "#{index}/#{total}\tBenchmarking with #{file}..."
19+
content = File.read(file)
20+
21+
benchmark = Benchmark.ips do |x|
22+
x.report(file) do
23+
RBS::Parser.parse_signature(content)
24+
end
25+
26+
x.quiet = true
27+
end
28+
29+
results << {
30+
file: file,
31+
size: content.bytesize,
32+
ips: benchmark.entries[0].ips,
33+
sd: benchmark.entries[0].ips_sd
34+
}
35+
end
36+
37+
puts CSV.generate {|csv|
38+
csv << ["File", "Size", "IPS", "SD"]
39+
results.each do |result|
40+
csv << [result[:file], result[:size], result[:ips], result[:sd]]
41+
end
42+
}

bin/benchmark-parse.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require "rbs"
2+
require "benchmark/ips"
3+
require "csv"
4+
require "pathname"
5+
6+
files = {}
7+
ARGV.each do |file|
8+
content = File.read(file)
9+
files[file] = RBS::Buffer.new(content: content, name: Pathname(file))
10+
end
11+
12+
puts "Benchmarking parsing #{files.size} files..."
13+
14+
result = Benchmark.ips do |x|
15+
x.report("parsing") do
16+
files.each do |file, content|
17+
RBS::Parser.parse_signature(content)
18+
end
19+
end
20+
21+
x.quiet = true
22+
end
23+
24+
entry = result.entries[0]
25+
puts "✅ #{"%0.3f" % entry.ips} i/s (±#{"%0.3f" % entry.error_percentage}%)"

bin/profile-parse.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
require 'rbs'
2+
require "optparse"
3+
4+
wait = false
5+
duration = 3
6+
7+
args = ARGV.dup
8+
9+
OptionParser.new do |opts|
10+
opts.banner = "Usage: profile-parse.rb [options] FILE"
11+
12+
opts.on("--wait", "Wait for enter before starting") do
13+
wait = true
14+
end
15+
opts.on("--duration=NUMBER", "Repeat parsing for <NUMBER> seconds") do |number|
16+
duration = number.to_i
17+
end
18+
end.parse!(args)
19+
20+
if wait
21+
puts "⏯️ Waiting for enter to continue at #{Process.pid}..."
22+
STDIN.gets
23+
end
24+
25+
file = args.shift or raise "No file path is given"
26+
sig = File.read(file)
27+
28+
puts "Parsing #{file} -- #{sig.bytesize} bytes"
29+
30+
started_at = Time.now
31+
count = 0
32+
33+
loop do
34+
count += 1
35+
RBS::Parser.parse_signature(sig)
36+
break if (Time.now - started_at) > duration
37+
end
38+
39+
puts "✅ Done #{count} loop(s)"

core/process.rbs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,6 +1861,7 @@ class Process::Status < Object
18611861
#
18621862
# ArgumentError is raised if `mask` is negative.
18631863
#
1864+
%a{deprecated}
18641865
def &: (Integer num) -> Integer
18651866

18661867
# <!--
@@ -1893,6 +1894,7 @@ class Process::Status < Object
18931894
#
18941895
# ArgumentError is raised if `places` is negative.
18951896
#
1897+
%a{deprecated}
18961898
def >>: (Integer num) -> Integer
18971899

18981900
# <!--

core/string.rbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,6 +1547,7 @@ class String
15471547
# hashing algorithms, install the string-crypt gem and `require 'string/crypt'`
15481548
# to continue using it.
15491549
#
1550+
%a{deprecated}
15501551
def crypt: (string salt_str) -> String
15511552

15521553
# <!-- rdoc-file=string.c -->

docs/aliases.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Aliases
2+
3+
This document explains module/class aliases and type aliases.
4+
5+
## Module/class alias
6+
7+
Module/class aliases give another name to a module/class.
8+
This is useful for some syntaxes that has lexical constraints.
9+
10+
```rbs
11+
class C
12+
end
13+
14+
class D = C # ::D is an alias for ::C
15+
16+
class E < D # ::E inherits from ::D, which is actually ::C
17+
end
18+
```
19+
20+
Note that module/class aliases cannot be recursive.
21+
22+
So, we can define a *normalization* of aliased module/class names.
23+
Normalization follows the chain of alias definitions and resolves them to the original module/class defined with `module`/`class` syntax.
24+
25+
```rbs
26+
class C
27+
end
28+
29+
class D = C
30+
class E = D
31+
```
32+
33+
`::E` is defined as an alias, and it can be normalized to `::C`.
34+
35+
## Type alias
36+
37+
The biggest difference from module/class alias is that type alias can be recursive.
38+
39+
```rbs
40+
# cons_cell type is defined recursively
41+
type cons_cell = nil
42+
| [Integer, cons_cell]
43+
```
44+
45+
This means type aliases *cannot be* normalized generally.
46+
So, we provide another operation for type alias, `DefinitionBuilder#expand_alias` and its family.
47+
It substitutes with the immediate right hand side of a type alias.
48+
49+
```
50+
cons_cell ===> nil | [Integer, cons_cell] (expand 1 step)
51+
===> nil | [Integer, nil | [Integer, cons_cell]] (expand 2 steps)
52+
===> ... (expand will go infinitely)
53+
```
54+
55+
Note that the namespace of a type alias *can be* normalized, because they are module names.
56+
57+
```rbs
58+
module M
59+
type t = String
60+
end
61+
62+
module N = M
63+
```
64+
65+
With the type definition above, a type `::N::t` can be normalized to `::M::t`.
66+
And then it can be expanded to `::String`.
67+
68+
> [!NOTE]
69+
> This is something like an *unfold* operation in type theory.
70+
71+
## Type name resolution
72+
73+
Type name resolution in RBS usually rewrites *relative* type names to *absolute* type names.
74+
`Environment#resolve_type_names` converts all type names in the RBS type definitions, and returns a new `Environment` object.
75+
76+
It also *normalizes* modules names in type names.
77+
78+
- If the type name can be resolved and normalized successfully, the AST has *absolute* type names.
79+
- If the type name resolution/normalization fails, the AST has *relative* type names.

ext/rbs_extension/ast_translation.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
#include "rbs_string_bridging.h"
1212
#include "legacy_location.h"
1313

14+
VALUE EMPTY_ARRAY;
15+
VALUE EMPTY_HASH;
16+
1417
#define RBS_LOC_CHILDREN_SIZE(cap) (sizeof(rbs_loc_children) + sizeof(rbs_loc_entry) * ((cap) - 1))
1518

1619
rbs_translation_context_t rbs_translation_context_create(rbs_constant_pool_t *constant_pool, VALUE buffer, rb_encoding *ruby_encoding) {
@@ -32,6 +35,10 @@ VALUE rbs_node_list_to_ruby_array(rbs_translation_context_t ctx, rbs_node_list_t
3235
}
3336

3437
VALUE rbs_hash_to_ruby_hash(rbs_translation_context_t ctx, rbs_hash_t *rbs_hash) {
38+
if (!rbs_hash->head) {
39+
return EMPTY_HASH;
40+
}
41+
3542
VALUE ruby_hash = rb_hash_new();
3643

3744
for (rbs_hash_node_t *n = rbs_hash->head; n != NULL; n = n->next) {
@@ -60,12 +67,12 @@ VALUE rbs_loc_to_ruby_location(rbs_translation_context_t ctx, rbs_location_t *so
6067
}
6168

6269
VALUE rbs_location_list_to_ruby_array(rbs_translation_context_t ctx, rbs_location_list_t *list) {
63-
VALUE ruby_array = rb_ary_new();
64-
6570
if (list == NULL) {
66-
return ruby_array;
71+
return EMPTY_ARRAY;
6772
}
6873

74+
VALUE ruby_array = rb_ary_new();
75+
6976
for (rbs_location_list_node_t *n = list->head; n != NULL; n = n->next) {
7077
rb_ary_push(ruby_array, rbs_loc_to_ruby_location(ctx, n->loc));
7178
}

ext/rbs_extension/ast_translation.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,7 @@ VALUE rbs_node_list_to_ruby_array(rbs_translation_context_t, rbs_node_list_t *li
3131
VALUE rbs_hash_to_ruby_hash(rbs_translation_context_t, rbs_hash_t *hash);
3232
VALUE rbs_struct_to_ruby_value(rbs_translation_context_t, rbs_node_t *instance);
3333

34+
extern VALUE EMPTY_ARRAY;
35+
extern VALUE EMPTY_HASH;
36+
3437
#endif

0 commit comments

Comments
 (0)