rbs 0.4.0 → 0.9.0
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/.github/workflows/ruby.yml +7 -1
- data/.gitignore +1 -1
- data/CHANGELOG.md +35 -0
- data/Gemfile +14 -0
- data/README.md +86 -47
- data/Rakefile +53 -21
- data/bin/rbs-prof +9 -0
- data/bin/run_in_md.rb +49 -0
- data/docs/stdlib.md +0 -2
- data/docs/syntax.md +6 -3
- data/goodcheck.yml +65 -0
- data/lib/rbs.rb +3 -0
- data/lib/rbs/ast/comment.rb +6 -0
- data/lib/rbs/ast/declarations.rb +106 -13
- data/lib/rbs/ast/members.rb +41 -17
- data/lib/rbs/cli.rb +317 -121
- data/lib/rbs/constant.rb +4 -4
- data/lib/rbs/constant_table.rb +51 -45
- data/lib/rbs/definition.rb +175 -59
- data/lib/rbs/definition_builder.rb +814 -604
- data/lib/rbs/environment.rb +352 -210
- data/lib/rbs/environment_walker.rb +14 -23
- data/lib/rbs/errors.rb +184 -3
- data/lib/rbs/factory.rb +14 -0
- data/lib/rbs/location.rb +15 -0
- data/lib/rbs/parser.y +100 -34
- data/lib/rbs/prototype/rb.rb +101 -113
- data/lib/rbs/prototype/rbi.rb +5 -3
- data/lib/rbs/prototype/runtime.rb +11 -7
- data/lib/rbs/substitution.rb +12 -1
- data/lib/rbs/test.rb +82 -3
- data/lib/rbs/test/errors.rb +5 -1
- data/lib/rbs/test/hook.rb +133 -259
- data/lib/rbs/test/observer.rb +17 -0
- data/lib/rbs/test/setup.rb +35 -19
- data/lib/rbs/test/setup_helper.rb +29 -0
- data/lib/rbs/test/spy.rb +0 -321
- data/lib/rbs/test/tester.rb +116 -0
- data/lib/rbs/test/type_check.rb +43 -7
- data/lib/rbs/type_name_resolver.rb +58 -0
- data/lib/rbs/types.rb +94 -2
- data/lib/rbs/validator.rb +55 -0
- data/lib/rbs/variance_calculator.rb +12 -2
- data/lib/rbs/version.rb +1 -1
- data/lib/rbs/writer.rb +127 -91
- data/rbs.gemspec +0 -10
- data/schema/decls.json +36 -10
- data/schema/members.json +3 -0
- data/stdlib/benchmark/benchmark.rbs +151 -151
- data/stdlib/builtin/enumerable.rbs +3 -3
- data/stdlib/builtin/file.rbs +0 -3
- data/stdlib/builtin/io.rbs +4 -4
- data/stdlib/builtin/proc.rbs +1 -2
- data/stdlib/builtin/thread.rbs +2 -2
- data/stdlib/csv/csv.rbs +4 -6
- data/stdlib/fiber/fiber.rbs +1 -1
- data/stdlib/json/json.rbs +7 -1
- data/stdlib/logger/formatter.rbs +23 -0
- data/stdlib/logger/log_device.rbs +39 -0
- data/stdlib/logger/logger.rbs +507 -0
- data/stdlib/logger/period.rbs +7 -0
- data/stdlib/logger/severity.rbs +8 -0
- data/stdlib/mutex_m/mutex_m.rbs +77 -0
- data/stdlib/pathname/pathname.rbs +6 -6
- data/stdlib/prime/integer-extension.rbs +1 -1
- data/stdlib/prime/prime.rbs +44 -44
- data/stdlib/pty/pty.rbs +159 -0
- data/stdlib/tmpdir/tmpdir.rbs +1 -1
- metadata +19 -130
- data/lib/rbs/test/test_helper.rb +0 -183
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbe5937993dd712f397e9008911a67bea344277222c45f94f09d1fe6e30399d8
|
4
|
+
data.tar.gz: 914834d0f1bcb1d5e13dd8bb8446f8bfd544625ed8bf49d673fce5d47063b20c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ab09d820f4254c7be5648c64c43fd349414393eaecc7875fea580442f34f88b6284d4af360bcc3a9c4181cf6c0c041f39cf58b49c81ce913fe2051872517719
|
7
|
+
data.tar.gz: e27fd8bd3df37c73efeb6e56c355b76c6f12aa3ff538e4b6dacf5d627a9894947decd5edcdb76715fc3bb94bff42bb6ef286f6c1cfc69aeab20ae2db3a8f7ddc
|
data/.github/workflows/ruby.yml
CHANGED
@@ -24,5 +24,11 @@ jobs:
|
|
24
24
|
ruby -v
|
25
25
|
gem install bundler
|
26
26
|
bin/setup
|
27
|
-
bundle exec rake
|
27
|
+
bundle exec rake test
|
28
|
+
bundle exec rake stdlib_test
|
29
|
+
bundle exec rake rubocop
|
30
|
+
bundle exec rake validate
|
31
|
+
bundle exec rake test_doc
|
28
32
|
bundle exec rake build
|
33
|
+
RBS_GENERATE_TEST_PATH=/tmp/Array_test.rb bundle exec rake 'generate:stdlib_test[Array]'
|
34
|
+
ruby -c /tmp/Array_test.rb
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,41 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.9.0 (2020-08-03)
|
6
|
+
|
7
|
+
* Fix signature validation [#351](https://github.com/ruby/rbs/pull/351), [#352](https://github.com/ruby/rbs/pull/352)
|
8
|
+
* Parsing performance improvement [#350](https://github.com/ruby/rbs/pull/350)
|
9
|
+
|
10
|
+
## 0.8.0 (2020-08-01)
|
11
|
+
|
12
|
+
* Signature updates for `Enumerator` and `PTY`
|
13
|
+
* Fix prototype rb/rbi error handling [#349](https://github.com/ruby/rbs/pull/349)
|
14
|
+
* Runtime test improvements [#344](https://github.com/ruby/rbs/pull/344), [#343](https://github.com/ruby/rbs/pull/343)
|
15
|
+
* Add `...` syntax [#342](https://github.com/ruby/rbs/pull/342)
|
16
|
+
|
17
|
+
## 0.7.0 (2020-07-20)
|
18
|
+
|
19
|
+
* Add `DefinitionBuilder#one_instance_ancestors` and `DefinitionBuilder#one_singleton_ancestors` [#341](https://github.com/ruby/rbs/pull/341)
|
20
|
+
* Bug fix in ConstantTable [#340](https://github.com/ruby/rbs/pull/340)
|
21
|
+
* Make `rbs validate` faster [#338](https://github.com/ruby/rbs/pull/338)
|
22
|
+
* Dedup methods generated by `rbs prototype rb` [#334](https://github.com/ruby/rbs/pull/334)
|
23
|
+
|
24
|
+
## 0.6.0 (2020-07-12)
|
25
|
+
|
26
|
+
* Signature update for `Logger`.
|
27
|
+
* Clean `Environment#inspect`. [#331](https://github.com/ruby/rbs/pull/331)
|
28
|
+
* Module self type syntax update. [#329](https://github.com/ruby/rbs/pull/329)
|
29
|
+
* Better validation. [#328](https://github.com/ruby/rbs/pull/328)
|
30
|
+
* Parser performance improvement. [#327](https://github.com/ruby/rbs/pull/327)
|
31
|
+
* Runtime type checking performance improvements with sampling [#323](https://github.com/ruby/rbs/pull/323)
|
32
|
+
|
33
|
+
## 0.5.0 (2020-07-04)
|
34
|
+
|
35
|
+
* Signature updates for `Mutex_m`, `IO`, and `Enumerable`.
|
36
|
+
* Syntax update. [#307](https://github.com/ruby/rbs/pull/307)
|
37
|
+
* AST command prints _absolute_ type names with file name filtering. [#312](https://github.com/ruby/rbs/pull/312)
|
38
|
+
* Improve CLI message. [#309](https://github.com/ruby/rbs/pull/309)
|
39
|
+
|
5
40
|
## 0.4.0 (2020-06-15)
|
6
41
|
|
7
42
|
* Signature update for `Fiber`, `Encoding`, and `Enumerator`.
|
data/Gemfile
CHANGED
@@ -3,4 +3,18 @@ source "https://rubygems.org"
|
|
3
3
|
# Specify your gem's dependencies in rbs.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
+
# Development dependencies
|
7
|
+
gem "rake"
|
8
|
+
gem "minitest"
|
9
|
+
gem "racc"
|
10
|
+
gem "rubocop"
|
11
|
+
gem "rubocop-rubycw"
|
12
|
+
gem "minitest-reporters"
|
13
|
+
gem "json"
|
14
|
+
gem "json-schema"
|
15
|
+
gem 'stackprof'
|
16
|
+
gem "goodcheck"
|
17
|
+
|
18
|
+
# Test gems
|
6
19
|
gem "rbs-amber", path: "test/assets/test-gem"
|
20
|
+
|
data/README.md
CHANGED
@@ -1,79 +1,118 @@
|
|
1
1
|
# RBS
|
2
2
|
|
3
|
-
RBS
|
4
|
-
|
3
|
+
RBS is a language to describe the structure of Ruby programs.
|
4
|
+
You can write down the definition of a class or module: methods defined in the class, instance variables and their types, and inheritance/mix-in relations.
|
5
|
+
It also allows declaring constants and global variables.
|
5
6
|
|
6
|
-
|
7
|
+
The following is a small example of RBS for a chat app.
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
<!-- run-start:a.rbs:bundle exec rbs -I a.rbs validate -->
|
10
|
+
```rbs
|
11
|
+
module ChatApp
|
12
|
+
VERSION: String
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
```
|
14
|
+
class User
|
15
|
+
attr_reader login: String
|
16
|
+
attr_reader email: String
|
15
17
|
|
16
|
-
|
18
|
+
def initialize: (login: String, email: String) -> void
|
19
|
+
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
$ rbs method ::Object tap
|
23
|
-
```
|
21
|
+
class Bot
|
22
|
+
attr_reader name: String
|
23
|
+
attr_reader email: String
|
24
|
+
attr_reader owner: User
|
24
25
|
|
25
|
-
|
26
|
+
def initialize: (name: String, owner: User) -> void
|
27
|
+
end
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
29
|
+
class Message
|
30
|
+
attr_reader id: String
|
31
|
+
attr_reader string: String
|
32
|
+
attr_reader from: User | Bot # `|` means union types: `#from` can be `User` or `Bot`
|
33
|
+
attr_reader reply_to: Message? # `?` means optional type: `#reply_to` can be `nil`
|
30
34
|
|
31
|
-
|
35
|
+
def initialize: (from: User | Bot, string: String) -> void
|
32
36
|
|
33
|
-
|
37
|
+
def reply: (from: User | Bot, string: String) -> Message
|
38
|
+
end
|
34
39
|
|
40
|
+
class Channel
|
41
|
+
attr_reader name: String
|
42
|
+
attr_reader messages: Array[Message]
|
43
|
+
attr_reader users: Array[User]
|
44
|
+
attr_reader bots: Array[Bot]
|
45
|
+
|
46
|
+
def initialize: (name: String) -> void
|
47
|
+
|
48
|
+
def each_member: () { (User | Bot) -> void } -> void # `{` and `}` means block.
|
49
|
+
| () -> Enumerable[User | Bot, void] # Method can be overloaded.
|
50
|
+
end
|
51
|
+
end
|
35
52
|
```
|
36
|
-
|
37
|
-
$ rbs ancestors --singleton Array # (Array.class.ancestors)
|
38
|
-
```
|
53
|
+
<!-- run-end -->
|
39
54
|
|
40
|
-
|
41
|
-
The name of the command is borrowed from `Class#ancestors`, but the semantics is a bit different.
|
42
|
-
The `ancestors` command is more precise (I believe).
|
55
|
+
## Installation
|
43
56
|
|
44
|
-
|
57
|
+
Install the `rbs` gem. `$ gem install rbs` from the command line, or add a line in your `Gemfile`.
|
45
58
|
|
46
|
-
```
|
47
|
-
|
48
|
-
$ rbs methods --singleton ::Object # Object.methods
|
59
|
+
```rb
|
60
|
+
gem "rbs"
|
49
61
|
```
|
50
62
|
|
51
|
-
|
63
|
+
## CLI
|
52
64
|
|
53
|
-
|
65
|
+
The gem ships with the `rbs` command line tool to demonstrate what it can do and help develop RBS.
|
54
66
|
|
55
|
-
```
|
56
|
-
$ rbs
|
57
|
-
$ rbs
|
67
|
+
```bash
|
68
|
+
$ rbs version
|
69
|
+
$ rbs list
|
70
|
+
$ rbs ancestors ::Object
|
71
|
+
$ rbs methods ::Object
|
72
|
+
$ rbs method Object then
|
58
73
|
```
|
59
74
|
|
60
|
-
|
75
|
+
## Library
|
61
76
|
|
62
|
-
|
77
|
+
There are two important concepts, _environment_ and _definition_.
|
63
78
|
|
64
|
-
|
79
|
+
An _environment_ is a dictionary that keeps track of all declarations. What is the declaration associated with `String` class? An _environment_ will give you the answer.
|
65
80
|
|
66
|
-
|
81
|
+
A _definition_ gives you the detail of the class. What is the type of the return value of `gsub` method of the `String` class? The _definition_ for `String` class knows the list of methods it provides and their types.
|
67
82
|
|
68
|
-
|
69
|
-
$ rbs -r set list
|
70
|
-
```
|
83
|
+
The following is a small code to retrieve the definition of the `String#gsub` method.
|
71
84
|
|
72
|
-
|
85
|
+
<!-- run-start:a.rb:bundle exec ruby a.rb -->
|
86
|
+
```rb
|
87
|
+
require "rbs"
|
73
88
|
|
89
|
+
loader = RBS::EnvironmentLoader.new()
|
90
|
+
|
91
|
+
# loader.add(path: Pathname("sig")) # Load .rbs files from `sig` directory
|
92
|
+
# loader.add(library: "pathname") # Load pathname library
|
93
|
+
|
94
|
+
environment = RBS::Environment.from_loader(loader).resolve_type_names
|
95
|
+
|
96
|
+
# ::String
|
97
|
+
string = RBS::TypeName.new(name: :String, namespace: RBS::Namespace.root)
|
98
|
+
|
99
|
+
# Class declaration for ::String
|
100
|
+
decl = environment.class_decls[string]
|
101
|
+
|
102
|
+
# Builder provides the translation from `declaration` to `definition`
|
103
|
+
builder = RBS::DefinitionBuilder.new(env: environment)
|
104
|
+
|
105
|
+
# Definition of instance of String
|
106
|
+
instance = builder.build_instance(string)
|
107
|
+
# Print the types of `gsub` method
|
108
|
+
puts instance.methods[:gsub].method_types.join("\n")
|
109
|
+
|
110
|
+
# Definition of singleton of String
|
111
|
+
singleton = builder.build_singleton(string)
|
112
|
+
# No `gsub` method for String singleton
|
113
|
+
puts singleton.methods[:gsub]
|
74
114
|
```
|
75
|
-
|
76
|
-
```
|
115
|
+
<!-- run-end -->
|
77
116
|
|
78
117
|
## Guides
|
79
118
|
|
data/Rakefile
CHANGED
@@ -9,10 +9,23 @@ Rake::TestTask.new(:test) do |t|
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
multitask :default => [:test, :stdlib_test, :rubocop, :validate]
|
12
|
+
multitask :default => [:test, :stdlib_test, :rubocop, :validate, :test_doc]
|
13
|
+
|
14
|
+
task :test_doc => :parser do
|
15
|
+
files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
16
|
+
`git ls-files -z`.split("\x0").select do |file| Pathname(file).extname == ".md" end
|
17
|
+
end
|
18
|
+
|
19
|
+
sh "ruby bin/run_in_md.rb #{files.join(" ")}"
|
20
|
+
end
|
13
21
|
|
14
22
|
task :validate => :parser do
|
15
23
|
sh "rbs validate"
|
24
|
+
|
25
|
+
FileList["stdlib/*"].each do |path|
|
26
|
+
next if path =~ %r{stdlib/builtin}
|
27
|
+
sh "rbs -r#{File.basename(path)} validate"
|
28
|
+
end
|
16
29
|
end
|
17
30
|
|
18
31
|
FileList["test/stdlib/**/*_test.rb"].each do |test|
|
@@ -41,7 +54,7 @@ namespace :generate do
|
|
41
54
|
raise "Class name is necessary. e.g. rake 'generate:stdlib_test[String]'"
|
42
55
|
end
|
43
56
|
|
44
|
-
path = Pathname("test/stdlib/#{klass}_test.rb")
|
57
|
+
path = Pathname(ENV["RBS_GENERATE_TEST_PATH"] || "test/stdlib/#{klass}_test.rb")
|
45
58
|
raise "#{path} already exists!" if path.exist?
|
46
59
|
|
47
60
|
require "erb"
|
@@ -53,35 +66,54 @@ namespace :generate do
|
|
53
66
|
def initialize(klass)
|
54
67
|
@klass = klass
|
55
68
|
|
56
|
-
|
57
|
-
|
69
|
+
loader = RBS::EnvironmentLoader.new
|
70
|
+
Dir['stdlib/*'].each do |lib|
|
71
|
+
next if lib.end_with?('builtin')
|
72
|
+
|
73
|
+
loader.add(library: File.basename(lib))
|
74
|
+
end
|
75
|
+
@env = RBS::Environment.from_loader(loader).resolve_type_names
|
58
76
|
end
|
59
77
|
|
60
78
|
def call
|
61
79
|
ERB.new(<<~ERB, trim_mode: "-").result(binding)
|
62
80
|
require_relative "test_helper"
|
63
81
|
|
64
|
-
|
65
|
-
|
82
|
+
<%- unless class_methods.empty? -%>
|
83
|
+
class <%= klass %>SingletonTest < Minitest::Test
|
84
|
+
include TypeAssertions
|
85
|
+
|
66
86
|
# library "pathname", "set", "securerandom" # Declare library signatures to load
|
67
|
-
|
87
|
+
testing "singleton(::<%= klass %>)"
|
88
|
+
|
68
89
|
<%- class_methods.each do |method_name, definition| %>
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
90
|
+
def test_<%= test_name_for(method_name) %>
|
91
|
+
<%- definition.method_types.each do |method_type| -%>
|
92
|
+
assert_send_type "<%= method_type %>",
|
93
|
+
<%= klass %>, :<%= method_name %>
|
94
|
+
<%- end -%>
|
74
95
|
end
|
75
96
|
<%- end -%>
|
97
|
+
end
|
98
|
+
<%- end -%>
|
99
|
+
|
100
|
+
<%- unless instance_methods.empty? -%>
|
101
|
+
class <%= klass %>Test < Minitest::Test
|
102
|
+
include TypeAssertions
|
103
|
+
|
104
|
+
# library "pathname", "set", "securerandom" # Declare library signatures to load
|
105
|
+
testing "::<%= klass %>"
|
106
|
+
|
76
107
|
<%- instance_methods.each do |method_name, definition| %>
|
77
108
|
def test_<%= test_name_for(method_name) %>
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
109
|
+
<%- definition.method_types.each do |method_type| -%>
|
110
|
+
assert_send_type "<%= method_type %>",
|
111
|
+
<%= klass %>.new, :<%= method_name %>
|
112
|
+
<%- end -%>
|
82
113
|
end
|
83
114
|
<%- end -%>
|
84
115
|
end
|
116
|
+
<%- end -%>
|
85
117
|
ERB
|
86
118
|
end
|
87
119
|
|
@@ -117,18 +149,18 @@ namespace :generate do
|
|
117
149
|
end
|
118
150
|
|
119
151
|
def type_name
|
120
|
-
@type_name ||=
|
152
|
+
@type_name ||= RBS::TypeName.new(name: klass.to_sym, namespace: RBS::Namespace.new(path: [], absolute: true))
|
121
153
|
end
|
122
154
|
|
123
155
|
def class_methods
|
124
|
-
@class_methods ||=
|
125
|
-
definition.implemented_in
|
156
|
+
@class_methods ||= RBS::DefinitionBuilder.new(env: env).build_singleton(type_name).methods.select {|_, definition|
|
157
|
+
definition.implemented_in == type_name
|
126
158
|
}
|
127
159
|
end
|
128
160
|
|
129
161
|
def instance_methods
|
130
|
-
@instance_methods ||=
|
131
|
-
definition.implemented_in
|
162
|
+
@instance_methods ||= RBS::DefinitionBuilder.new(env: env).build_instance(type_name).methods.select {|_, definition|
|
163
|
+
definition.implemented_in == type_name
|
132
164
|
}
|
133
165
|
end
|
134
166
|
end
|
data/bin/rbs-prof
ADDED
data/bin/run_in_md.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require "tmpdir"
|
2
|
+
require "shellwords"
|
3
|
+
|
4
|
+
ARGV.each do |path|
|
5
|
+
puts "~~~~~~~ Checking #{path} ~~~~~~"
|
6
|
+
content = File.read(path)
|
7
|
+
|
8
|
+
snippets = []
|
9
|
+
lines = []
|
10
|
+
content.lines.each.with_index do |line, index|
|
11
|
+
case line
|
12
|
+
when /run-start:/
|
13
|
+
lines = [[line, index+1]]
|
14
|
+
when /run-end/
|
15
|
+
lines << [line, index+1]
|
16
|
+
snippets << lines
|
17
|
+
lines = []
|
18
|
+
else
|
19
|
+
lines << [line, index+1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
snippets.each do |lines|
|
24
|
+
puts ">>> Code detected: "
|
25
|
+
hd, _, *code_lines, _, _ = lines
|
26
|
+
|
27
|
+
head = hd[0]
|
28
|
+
head.gsub!(/^<!-- +/, "").gsub!(/ +-->$/, "")
|
29
|
+
_,name,command = head.split(/:/)
|
30
|
+
|
31
|
+
puts "# command=#{Shellwords.split(command).inspect}"
|
32
|
+
puts "# name=#{name}"
|
33
|
+
puts code_lines.map {|line, i| "#{"%4d" % i} #{line}" }.join
|
34
|
+
|
35
|
+
code = code_lines.map(&:first).join
|
36
|
+
|
37
|
+
puts ">>> Running..."
|
38
|
+
Dir.mktmpdir do |dir|
|
39
|
+
File.write(File.join(dir, name), code)
|
40
|
+
pid = spawn({ "BUNDLE_GEMFILE" => File.join(__dir__, "../Gemfile") },
|
41
|
+
*Shellwords.split(command),
|
42
|
+
chdir: dir)
|
43
|
+
|
44
|
+
_, status = Process.waitpid2(pid)
|
45
|
+
|
46
|
+
status.success? or raise "Failed to execute code: #{code_lines.join}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|