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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +7 -1
  3. data/.gitignore +1 -1
  4. data/CHANGELOG.md +35 -0
  5. data/Gemfile +14 -0
  6. data/README.md +86 -47
  7. data/Rakefile +53 -21
  8. data/bin/rbs-prof +9 -0
  9. data/bin/run_in_md.rb +49 -0
  10. data/docs/stdlib.md +0 -2
  11. data/docs/syntax.md +6 -3
  12. data/goodcheck.yml +65 -0
  13. data/lib/rbs.rb +3 -0
  14. data/lib/rbs/ast/comment.rb +6 -0
  15. data/lib/rbs/ast/declarations.rb +106 -13
  16. data/lib/rbs/ast/members.rb +41 -17
  17. data/lib/rbs/cli.rb +317 -121
  18. data/lib/rbs/constant.rb +4 -4
  19. data/lib/rbs/constant_table.rb +51 -45
  20. data/lib/rbs/definition.rb +175 -59
  21. data/lib/rbs/definition_builder.rb +814 -604
  22. data/lib/rbs/environment.rb +352 -210
  23. data/lib/rbs/environment_walker.rb +14 -23
  24. data/lib/rbs/errors.rb +184 -3
  25. data/lib/rbs/factory.rb +14 -0
  26. data/lib/rbs/location.rb +15 -0
  27. data/lib/rbs/parser.y +100 -34
  28. data/lib/rbs/prototype/rb.rb +101 -113
  29. data/lib/rbs/prototype/rbi.rb +5 -3
  30. data/lib/rbs/prototype/runtime.rb +11 -7
  31. data/lib/rbs/substitution.rb +12 -1
  32. data/lib/rbs/test.rb +82 -3
  33. data/lib/rbs/test/errors.rb +5 -1
  34. data/lib/rbs/test/hook.rb +133 -259
  35. data/lib/rbs/test/observer.rb +17 -0
  36. data/lib/rbs/test/setup.rb +35 -19
  37. data/lib/rbs/test/setup_helper.rb +29 -0
  38. data/lib/rbs/test/spy.rb +0 -321
  39. data/lib/rbs/test/tester.rb +116 -0
  40. data/lib/rbs/test/type_check.rb +43 -7
  41. data/lib/rbs/type_name_resolver.rb +58 -0
  42. data/lib/rbs/types.rb +94 -2
  43. data/lib/rbs/validator.rb +55 -0
  44. data/lib/rbs/variance_calculator.rb +12 -2
  45. data/lib/rbs/version.rb +1 -1
  46. data/lib/rbs/writer.rb +127 -91
  47. data/rbs.gemspec +0 -10
  48. data/schema/decls.json +36 -10
  49. data/schema/members.json +3 -0
  50. data/stdlib/benchmark/benchmark.rbs +151 -151
  51. data/stdlib/builtin/enumerable.rbs +3 -3
  52. data/stdlib/builtin/file.rbs +0 -3
  53. data/stdlib/builtin/io.rbs +4 -4
  54. data/stdlib/builtin/proc.rbs +1 -2
  55. data/stdlib/builtin/thread.rbs +2 -2
  56. data/stdlib/csv/csv.rbs +4 -6
  57. data/stdlib/fiber/fiber.rbs +1 -1
  58. data/stdlib/json/json.rbs +7 -1
  59. data/stdlib/logger/formatter.rbs +23 -0
  60. data/stdlib/logger/log_device.rbs +39 -0
  61. data/stdlib/logger/logger.rbs +507 -0
  62. data/stdlib/logger/period.rbs +7 -0
  63. data/stdlib/logger/severity.rbs +8 -0
  64. data/stdlib/mutex_m/mutex_m.rbs +77 -0
  65. data/stdlib/pathname/pathname.rbs +6 -6
  66. data/stdlib/prime/integer-extension.rbs +1 -1
  67. data/stdlib/prime/prime.rbs +44 -44
  68. data/stdlib/pty/pty.rbs +159 -0
  69. data/stdlib/tmpdir/tmpdir.rbs +1 -1
  70. metadata +19 -130
  71. data/lib/rbs/test/test_helper.rb +0 -183
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77a83be097e5de26f4541a25fb744a31574d0f0b94d94848e11cae49d095693a
4
- data.tar.gz: 4619e6ae9f31e32ac6d2da902b11980644df037b9d254b5919fe2eff1f43bdc7
3
+ metadata.gz: cbe5937993dd712f397e9008911a67bea344277222c45f94f09d1fe6e30399d8
4
+ data.tar.gz: 914834d0f1bcb1d5e13dd8bb8446f8bfd544625ed8bf49d673fce5d47063b20c
5
5
  SHA512:
6
- metadata.gz: '095abed7b0453d72a545760d8a165bb2a3415e3f741b004926b600b8e1e1260dc752e91d86d33e0c770d3011885bea1fa728708707687c59d3a1ad9707b672c7'
7
- data.tar.gz: 94b3f4fc317d701c327b7519068ec590409ceff1208660c44f83440c9d3ed419c83ca29adf7137c10a1fb7c3d7ed7b0808e0a8a5fc5dedd66ca900981674ae90
6
+ metadata.gz: 7ab09d820f4254c7be5648c64c43fd349414393eaecc7875fea580442f34f88b6284d4af360bcc3a9c4181cf6c0c041f39cf58b49c81ce913fe2051872517719
7
+ data.tar.gz: e27fd8bd3df37c73efeb6e56c355b76c6f12aa3ff538e4b6dacf5d627a9894947decd5edcdb76715fc3bb94bff42bb6ef286f6c1cfc69aeab20ae2db3a8f7ddc
@@ -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
@@ -5,8 +5,8 @@
5
5
  /pkg/
6
6
  /spec/reports/
7
7
  /tmp/
8
- /Gemfile.lock
9
8
  /.ruby-version
10
9
  /lib/rbs/parser.rb
11
10
  /lib/rbs/parser.output
12
11
  /vendor/sigs
12
+ /Gemfile.lock
@@ -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 provides syntax and semantics definition for the `Ruby Signature` language, `.rbs` files.
4
- It consists of a parser, the syntax, and class definition interpreter, the semantics.
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
- ## Build
7
+ The following is a small example of RBS for a chat app.
7
8
 
8
- We haven't published a gem yet.
9
- You need to install the dependencies, and build its parser with `bin/setup`.
9
+ <!-- run-start:a.rbs:bundle exec rbs -I a.rbs validate -->
10
+ ```rbs
11
+ module ChatApp
12
+ VERSION: String
10
13
 
11
- ```
12
- $ bin/setup
13
- $ bundle exec exe/rbs
14
- ```
14
+ class User
15
+ attr_reader login: String
16
+ attr_reader email: String
15
17
 
16
- ## Usage
18
+ def initialize: (login: String, email: String) -> void
19
+ end
17
20
 
18
- ```
19
- $ rbs list
20
- $ rbs ancestors ::Object
21
- $ rbs methods ::Object
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
- ### rbs list [--class|--module|--interface]
26
+ def initialize: (name: String, owner: User) -> void
27
+ end
26
28
 
27
- ```
28
- $ rbs list
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
- This command lists all of the classes/modules/interfaces defined in `.rbs` files.
35
+ def initialize: (from: User | Bot, string: String) -> void
32
36
 
33
- ### rbs ancestors [--singleton|--instance] CLASS
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
- $ rbs ancestors Array # ([].class.ancestors)
37
- $ rbs ancestors --singleton Array # (Array.class.ancestors)
38
- ```
53
+ <!-- run-end -->
39
54
 
40
- This command prints the _ancestors_ of the class.
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
- ### rbs methods [--singleton|--instance] CLASS
57
+ Install the `rbs` gem. `$ gem install rbs` from the command line, or add a line in your `Gemfile`.
45
58
 
46
- ```
47
- $ rbs methods ::Integer # 1.methods
48
- $ rbs methods --singleton ::Object # Object.methods
59
+ ```rb
60
+ gem "rbs"
49
61
  ```
50
62
 
51
- This command prints all methods provided for the class.
63
+ ## CLI
52
64
 
53
- ### rbs method [--singleton|--instance] CLASS METHOD
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 method ::Integer '+' # 1+2
57
- $ rbs method --singleton ::Object tap # Object.tap { ... }
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
- This command prints type and properties of the method.
75
+ ## Library
61
76
 
62
- ### Options
77
+ There are two important concepts, _environment_ and _definition_.
63
78
 
64
- It accepts two global options, `-r` and `-I`.
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
- `-r` is for libraries. You can specify the names of libraries.
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
- `-I` is for application signatures. You can specify the name of directory.
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
- $ rbs -I sig list
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
- @env = Ruby::Signature::Environment.new
57
- Ruby::Signature::EnvironmentLoader.new.load(env: @env)
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
- class <%= klass %>Test < StdlibTest
65
- target <%= klass %>
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
- using hook.refinement
87
+ testing "singleton(::<%= klass %>)"
88
+
68
89
  <%- class_methods.each do |method_name, definition| %>
69
- def test_class_method_<%= test_name_for(method_name) %>
70
- <%- definition.method_types.each do |method_type| -%>
71
- # <%= method_type %>
72
- <%= klass %>.<%= method_name %>
73
- <%- end -%>
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
- <%- definition.method_types.each do |method_type| -%>
79
- # <%= method_type %>
80
- <%= klass %>.new.<%= method_name %>
81
- <%- end -%>
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 ||= Ruby::Signature::TypeName.new(name: klass.to_sym, namespace: Ruby::Signature::Namespace.new(path: [], absolute: true))
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 ||= Ruby::Signature::DefinitionBuilder.new(env: env).build_singleton(type_name).methods.select {|_, definition|
125
- definition.implemented_in.name.absolute! == type_name
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 ||= Ruby::Signature::DefinitionBuilder.new(env: env).build_instance(type_name).methods.select {|_, definition|
131
- definition.implemented_in.name.absolute! == type_name
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
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "stackprof"
4
+
5
+ out = ENV["RBS_STACKPROF_OUT"] || 'tmp/stackprof-cpu-rbs.dump'
6
+
7
+ StackProf.run(mode: :cpu, out: out) do
8
+ load File.join(__dir__, "../exe/rbs")
9
+ end
@@ -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