rbs 1.3.3 → 1.6.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/dependabot.yml +10 -0
  3. data/CHANGELOG.md +69 -0
  4. data/Gemfile +2 -0
  5. data/Rakefile +4 -0
  6. data/Steepfile +9 -1
  7. data/core/array.rbs +8 -7
  8. data/core/builtin.rbs +1 -1
  9. data/core/enumerable.rbs +11 -10
  10. data/core/enumerator.rbs +2 -2
  11. data/core/exception.rbs +1 -0
  12. data/core/false_class.rbs +4 -4
  13. data/core/file.rbs +3 -1
  14. data/core/float.rbs +1 -1
  15. data/core/global_variables.rbs +180 -0
  16. data/core/hash.rbs +7 -7
  17. data/core/integer.rbs +1 -2
  18. data/core/io/wait.rbs +37 -0
  19. data/core/io.rbs +11 -5
  20. data/core/kernel.rbs +25 -2
  21. data/core/object.rbs +1 -1
  22. data/core/ractor.rbs +779 -0
  23. data/core/range.rbs +11 -9
  24. data/core/string_io.rbs +3 -5
  25. data/core/true_class.rbs +4 -4
  26. data/docs/collection.md +116 -0
  27. data/lib/rbs/builtin_names.rb +1 -0
  28. data/lib/rbs/cli.rb +94 -2
  29. data/lib/rbs/collection/cleaner.rb +29 -0
  30. data/lib/rbs/collection/config/lockfile_generator.rb +95 -0
  31. data/lib/rbs/collection/config.rb +85 -0
  32. data/lib/rbs/collection/installer.rb +27 -0
  33. data/lib/rbs/collection/sources/git.rb +147 -0
  34. data/lib/rbs/collection/sources/rubygems.rb +40 -0
  35. data/lib/rbs/collection/sources/stdlib.rb +38 -0
  36. data/lib/rbs/collection/sources.rb +22 -0
  37. data/lib/rbs/collection.rb +13 -0
  38. data/lib/rbs/environment_loader.rb +12 -0
  39. data/lib/rbs/errors.rb +18 -0
  40. data/lib/rbs/parser.rb +1 -1
  41. data/lib/rbs/parser.y +1 -1
  42. data/lib/rbs/prototype/rb.rb +8 -1
  43. data/lib/rbs/prototype/runtime.rb +1 -1
  44. data/lib/rbs/repository.rb +13 -7
  45. data/lib/rbs/type_alias_dependency.rb +88 -0
  46. data/lib/rbs/validator.rb +8 -0
  47. data/lib/rbs/version.rb +1 -1
  48. data/lib/rbs.rb +2 -0
  49. data/sig/builtin_names.rbs +1 -0
  50. data/sig/cli.rbs +5 -0
  51. data/sig/collection/cleaner.rbs +13 -0
  52. data/sig/collection/collections.rbs +112 -0
  53. data/sig/collection/config.rbs +69 -0
  54. data/sig/collection/installer.rbs +15 -0
  55. data/sig/collection.rbs +4 -0
  56. data/sig/environment_loader.rbs +3 -0
  57. data/sig/errors.rbs +9 -0
  58. data/sig/polyfill.rbs +12 -3
  59. data/sig/repository.rbs +4 -0
  60. data/sig/type_alias_dependency.rbs +22 -0
  61. data/sig/validator.rbs +2 -0
  62. data/stdlib/digest/0/digest.rbs +418 -0
  63. data/stdlib/objspace/0/objspace.rbs +406 -0
  64. data/stdlib/openssl/0/openssl.rbs +3711 -0
  65. data/stdlib/pathname/0/pathname.rbs +2 -2
  66. data/stdlib/rubygems/0/rubygems.rbs +1 -1
  67. data/stdlib/securerandom/0/securerandom.rbs +3 -1
  68. data/stdlib/tempfile/0/tempfile.rbs +270 -0
  69. data/stdlib/uri/0/generic.rbs +3 -3
  70. data/steep/Gemfile.lock +10 -10
  71. metadata +28 -3
data/core/range.rbs CHANGED
@@ -101,7 +101,9 @@ class Range[out Elem] < Object
101
101
  # ```
102
102
  def begin: () -> Elem # Begin-less ranges have type of Range[Integer?]
103
103
 
104
- def bsearch: [U] () { (Elem) -> boolish } -> U?
104
+ def bsearch: () -> ::Enumerator[Elem, Elem?]
105
+ | () { (Elem) -> (true | false) } -> Elem?
106
+ | () { (Elem) -> ::Integer } -> Elem?
105
107
 
106
108
  def cover?: (untyped obj) -> bool
107
109
 
@@ -132,7 +134,7 @@ class Range[out Elem] < Object
132
134
  # (10..20).first(3) #=> [10, 11, 12]
133
135
  # ```
134
136
  def first: () -> Elem
135
- | (?Integer n) -> ::Array[Elem]
137
+ | (Integer n) -> ::Array[Elem]
136
138
 
137
139
  # Compute a hash-code for this range. Two ranges with equal begin and end
138
140
  # points (using `eql?` ), and the same
@@ -165,7 +167,7 @@ class Range[out Elem] < Object
165
167
  # (10...20).last(3) #=> [17, 18, 19]
166
168
  # ```
167
169
  def last: () -> Elem
168
- | (?Integer n) -> ::Array[Elem]
170
+ | (Integer n) -> ::Array[Elem]
169
171
 
170
172
  # Returns the maximum value in the range. Returns `nil` if the begin value
171
173
  # of the range larger than the end value. Returns `nil` if the begin value
@@ -178,9 +180,9 @@ class Range[out Elem] < Object
178
180
  # (10..20).max #=> 20
179
181
  # ```
180
182
  def max: () -> Elem
181
- | () { (Elem arg0, Elem arg1) -> Integer } -> Elem
182
- | (?Integer n) -> ::Array[Elem]
183
- | (?Integer n) { (Elem arg0, Elem arg1) -> Integer } -> ::Array[Elem]
183
+ | () { (Elem a, Elem b) -> Integer } -> Elem
184
+ | (Integer n) -> ::Array[Elem]
185
+ | (Integer n) { (Elem a, Elem b) -> Integer } -> ::Array[Elem]
184
186
 
185
187
  # Returns the minimum value in the range. Returns `nil` if the begin value
186
188
  # of the range is larger than the end value. Returns `nil` if the begin
@@ -193,9 +195,9 @@ class Range[out Elem] < Object
193
195
  # (10..20).min #=> 10
194
196
  # ```
195
197
  def min: () -> Elem
196
- | () { (Elem arg0, Elem arg1) -> Integer } -> Elem
197
- | (?Integer n) -> ::Array[Elem]
198
- | (?Integer n) { (Elem arg0, Elem arg1) -> Integer } -> ::Array[Elem]
198
+ | () { (Elem a, Elem b) -> Integer } -> Elem
199
+ | (Integer n) -> ::Array[Elem]
200
+ | (Integer n) { (Elem a, Elem b) -> Integer } -> ::Array[Elem]
199
201
 
200
202
  # Returns the number of elements in the range. Both the begin and the end
201
203
  # of the [Range](Range.downloaded.ruby_doc) must be
data/core/string_io.rbs CHANGED
@@ -164,10 +164,9 @@ class StringIO
164
164
 
165
165
  # See IO#read.
166
166
  #
167
- def read: (?Integer length, ?String outbuf) -> String?
167
+ def read: (?int? length, ?string outbuf) -> String?
168
168
 
169
- def read_nonblock: (Integer len) -> String
170
- | (Integer len, ?String buf) -> String
169
+ def read_nonblock: (int len, ?string buf) -> String
171
170
 
172
171
  def readbyte: () -> Integer
173
172
 
@@ -179,8 +178,7 @@ class StringIO
179
178
  #
180
179
  def readlines: (?String sep, ?Integer limit, ?chomp: boolish) -> ::Array[String]
181
180
 
182
- def readpartial: (Integer maxlen) -> String
183
- | (Integer maxlen, ?String outbuf) -> String
181
+ def readpartial: (int maxlen, ?string outbuf) -> String
184
182
 
185
183
  # Reinitializes the stream with the given *other_StrIO* or *string* and *mode*
186
184
  # (see StringIO#new).
data/core/true_class.rbs CHANGED
@@ -5,13 +5,13 @@
5
5
  class TrueClass
6
6
  public
7
7
 
8
- def !: () -> bool
8
+ def !: () -> false
9
9
 
10
10
  # And---Returns `false` if *obj* is `nil` or `false`, `true` otherwise.
11
11
  #
12
12
  def &: (nil) -> false
13
13
  | (false) -> false
14
- | (untyped obj) -> bool
14
+ | (untyped obj) -> true
15
15
 
16
16
  # Case Equality -- For class Object, effectively the same as calling `#==`, but
17
17
  # typically overridden by descendants to provide meaningful semantics in `case`
@@ -24,7 +24,7 @@ class TrueClass
24
24
  #
25
25
  def ^: (nil) -> true
26
26
  | (false) -> true
27
- | (untyped obj) -> bool
27
+ | (untyped obj) -> false
28
28
 
29
29
  alias inspect to_s
30
30
 
@@ -42,5 +42,5 @@ class TrueClass
42
42
  #
43
43
  # or
44
44
  #
45
- def |: (boolish obj) -> bool
45
+ def |: (untyped obj) -> true
46
46
  end
@@ -0,0 +1,116 @@
1
+ # RBS Collection manager
2
+
3
+ `rbs collection` sub command manages third party gems' RBS. In short, it is `bundler` for RBS.
4
+
5
+ ## Requirements
6
+
7
+ * `git(1)`
8
+ * `Gemfile.lock`
9
+
10
+
11
+ ## Usage
12
+
13
+ ### Setup
14
+
15
+ First, generate the configuration file, `rbs_collection.yaml`, with `rbs collection init`.
16
+
17
+ ```console
18
+ $ rbs collection init
19
+ created: rbs_collection.yaml
20
+
21
+ $ cat rbs_collection.yaml
22
+ # Download sources
23
+ sources:
24
+ - name: ruby/gem_rbs_collection
25
+ remote: https://github.com/ruby/gem_rbs_collection.git
26
+ revision: main
27
+ repo_dir: gems
28
+
29
+ # A directory to install the downloaded RBSs
30
+ path: .gem_rbs_collection
31
+
32
+ gems:
33
+ # Skip loading rbs gem's RBS.
34
+ # It's unnecessary if you don't use rbs as a library.
35
+ - name: rbs
36
+ ignore: true
37
+ ```
38
+
39
+ I also recommend updating `.gitignore`.
40
+
41
+ ```console
42
+ $ echo /.gem_rbs_collection/ >> .gitignore
43
+ ```
44
+
45
+ ### Install dependencies
46
+
47
+ Then, install gems' RBS with `rbs collection install`! It copies RBS from [the gem RBS repository](https://github.com/ruby/gem_rbs_collection) to `.gem_rbs_collection/` directory by default.
48
+ I recommend to ignore `.gem_rbs_collection/` from version control system, such as Git.
49
+
50
+ ```console
51
+ $ rbs collection install
52
+ Installing ast:2.4 (ruby/gem_rbs_collection@4b1a2a2f64c)
53
+ ...
54
+ It's done! 42 gems's RBSs now installed.
55
+ ```
56
+
57
+ Finally the third party RBSs are available! `rbs` commands, such as `rbs validate`, automatically load the third party RBSs.
58
+
59
+ ### Other commands
60
+
61
+ `rbs collection` has two more commands.
62
+
63
+ * `rbs collection update` updates `rbs_collection.lock.yaml`.
64
+ * `rbs collection clean` removes unnecessary rbs from `.gem_rbs_collection` directory.
65
+
66
+ ## Configuration
67
+
68
+ Configure `rbs collection` with editing `rbs_collection.yaml`.
69
+
70
+ ```yaml
71
+ # Download sources.
72
+ # You can add own collection git repository.
73
+ sources:
74
+ - name: ruby/gem_rbs_collection
75
+ remote: https://github.com/ruby/gem_rbs_collection.git
76
+ revision: main
77
+ repo_dir: gems
78
+
79
+ # A directory to install the downloaded RBSs
80
+ path: .gem_rbs_collection
81
+
82
+ gems:
83
+ # If the Gemfile.lock doesn't contain csv gem but you use csv gem,
84
+ # you can write the gem name explicitly to install RBS of the gem.
85
+ - name: csv
86
+
87
+ # If the Gemfile.lock contains nokogiri gem but you don't want to use the RBS,
88
+ # you can ignore the gem.
89
+ # `rbs collection` avoids to install nokogiri gem's RBS by this change.
90
+ # It is useful if the nokogiri RBS has a problem, such as compatibility issue with other RBS.
91
+ - name: nokogiri
92
+ ignore: true
93
+ ```
94
+
95
+ ## Files / Directories
96
+
97
+ * `rbs_collection.yaml`
98
+ * The configuration file.
99
+ * You need to edit it if:
100
+ * You don't want to ignore gem's RBS.
101
+ * You want to add gem's RBS explicitly.
102
+ * You can change the file path with `--collection` option. e.g. `rbs --collection another_conf.yaml collection install`.
103
+ * `rbs_collection.lock.yaml`
104
+ * RBS installs and loads RBS files with this file.
105
+ * It is auto-generated file. Do not edit this file.
106
+ * I recommend to manage it with VCS such as git.
107
+ * `.gem_rbs_collection/`
108
+ * RBS installs third party RBS files to the directory.
109
+ * I recommend to ignore it from VCS.
110
+ * You can change the path with `path` option of `rbs_collection.yaml` file.
111
+
112
+
113
+ ## How it works
114
+
115
+ `rbs collection` is integrated with Bundler.
116
+ `rbs collection install` command generates `gem_rbs_collection.lock.yaml` from `gem_rbs_collection.yaml` and `Gemfile.lock`. It uses `Gemfile.lock` to detects dependencies.
@@ -51,5 +51,6 @@ module RBS
51
51
  Regexp = Name.define(:Regexp)
52
52
  TrueClass = Name.define(:TrueClass)
53
53
  FalseClass = Name.define(:FalseClass)
54
+ Numeric = Name.define(:Numeric)
54
55
  end
55
56
  end
data/lib/rbs/cli.rb CHANGED
@@ -6,6 +6,7 @@ module RBS
6
6
  class CLI
7
7
  class LibraryOptions
8
8
  attr_accessor :core_root
9
+ attr_accessor :config_path
9
10
  attr_reader :repos
10
11
  attr_reader :libs
11
12
  attr_reader :dirs
@@ -16,6 +17,7 @@ module RBS
16
17
 
17
18
  @libs = []
18
19
  @dirs = []
20
+ @config_path = Collection::Config::PATH
19
21
  end
20
22
 
21
23
  def loader
@@ -25,6 +27,8 @@ module RBS
25
27
  end
26
28
 
27
29
  loader = EnvironmentLoader.new(core_root: core_root, repository: repository)
30
+ lock = config_path&.then { |p| Collection::Config.lockfile_of(p) }
31
+ loader.add_collection(lock) if lock
28
32
 
29
33
  dirs.each do |dir|
30
34
  loader.add(path: Pathname(dir))
@@ -52,6 +56,14 @@ module RBS
52
56
  self.core_root = nil
53
57
  end
54
58
 
59
+ opts.on('--collection PATH', "File path of collection configration (default: #{Collection::Config::PATH})") do |path|
60
+ self.config_path = Pathname(path).expand_path
61
+ end
62
+
63
+ opts.on('--no-collection', 'Ignore collection configration') do
64
+ self.config_path = nil
65
+ end
66
+
55
67
  opts.on("--repo DIR", "Add RBS repository") do |dir|
56
68
  repos << dir
57
69
  end
@@ -68,7 +80,7 @@ module RBS
68
80
  @stderr = stderr
69
81
  end
70
82
 
71
- COMMANDS = [:ast, :list, :ancestors, :methods, :method, :validate, :constant, :paths, :prototype, :vendor, :parse, :test]
83
+ COMMANDS = [:ast, :list, :ancestors, :methods, :method, :validate, :constant, :paths, :prototype, :vendor, :parse, :test, :collection]
72
84
 
73
85
  def parse_logging_options(opts)
74
86
  opts.on("--log-level LEVEL", "Specify log level (defaults to `warn`)") do |level|
@@ -451,6 +463,7 @@ EOU
451
463
  builder.expand_alias(name).tap do |type|
452
464
  validator.validate_type type, context: [Namespace.root]
453
465
  end
466
+ validator.validate_type_alias(entry: decl)
454
467
  end
455
468
  end
456
469
 
@@ -818,11 +831,90 @@ EOB
818
831
 
819
832
  # @type var out: String
820
833
  # @type var err: String
821
- out, err, status = Open3.capture3(env_hash, *args)
834
+ out, err, status = __skip__ = Open3.capture3(env_hash, *args)
822
835
  stdout.print(out)
823
836
  stderr.print(err)
824
837
 
825
838
  status
826
839
  end
840
+
841
+ def run_collection(args, options)
842
+ warn "warning: rbs collection is experimental, and the behavior may change until RBS v2.0"
843
+
844
+ opts = collection_options(args)
845
+ params = {}
846
+ opts.order args.drop(1), into: params
847
+ config_path = options.config_path or raise
848
+ lock_path = Collection::Config.to_lockfile_path(config_path)
849
+
850
+ case args[0]
851
+ when 'install'
852
+ unless params[:frozen]
853
+ Collection::Config.generate_lockfile(config_path: config_path, gemfile_lock_path: Pathname('./Gemfile.lock'))
854
+ end
855
+ Collection::Installer.new(lockfile_path: lock_path, stdout: stdout).install_from_lockfile
856
+ when 'update'
857
+ # TODO: Be aware of argv to update only specified gem
858
+ Collection::Config.generate_lockfile(config_path: config_path, gemfile_lock_path: Pathname('./Gemfile.lock'), with_lockfile: false)
859
+ Collection::Installer.new(lockfile_path: lock_path, stdout: stdout).install_from_lockfile
860
+ when 'init'
861
+ if config_path.exist?
862
+ puts "#{config_path} already exists"
863
+ exit 1
864
+ end
865
+
866
+ config_path.write(<<~'YAML')
867
+ # Download sources
868
+ sources:
869
+ - name: ruby/gem_rbs_collection
870
+ remote: https://github.com/ruby/gem_rbs_collection.git
871
+ revision: main
872
+ repo_dir: gems
873
+
874
+ # A directory to install the downloaded RBSs
875
+ path: .gem_rbs_collection
876
+
877
+ gems:
878
+ # Skip loading rbs gem's RBS.
879
+ # It's unnecessary if you don't use rbs as a library.
880
+ - name: rbs
881
+ ignore: true
882
+ YAML
883
+ stdout.puts "created: #{config_path}"
884
+ when 'clean'
885
+ unless lock_path.exist?
886
+ puts "#{lock_path} should exist to clean"
887
+ exit 1
888
+ end
889
+ Collection::Cleaner.new(lockfile_path: lock_path)
890
+ when 'help'
891
+ puts opts.help
892
+ else
893
+ puts opts.help
894
+ exit 1
895
+ end
896
+ end
897
+
898
+ def collection_options(args)
899
+ OptionParser.new do |opts|
900
+ opts.banner = <<~HELP
901
+ Usage: rbs collection [install|update|init|clean|help]
902
+
903
+ Manage RBS collection, which contains third party RBS.
904
+
905
+ Examples:
906
+
907
+ # Initialize the configration file
908
+ $ rbs collection init
909
+
910
+ # Generate the lock file and install RBSs from the lock file
911
+ $ rbs collection install
912
+
913
+ # Update the RBSs
914
+ $ rbs collection update
915
+ HELP
916
+ opts.on('--frozen') if args[0] == 'install'
917
+ end
918
+ end
827
919
  end
828
920
  end
@@ -0,0 +1,29 @@
1
+ module RBS
2
+ module Collection
3
+ class Cleaner
4
+ attr_reader :lock
5
+
6
+ def initialize(lockfile_path:)
7
+ @lock = Config.from_path(lockfile_path)
8
+ end
9
+
10
+ def clean
11
+ lock.repo_path.glob('*/*') do |dir|
12
+ *_, gem_name, version = dir.to_s.split('/')
13
+ gem_name or raise
14
+ version or raise
15
+ next if needed? gem_name, version
16
+
17
+ FileUtils.remove_entry_secure(dir.to_s)
18
+ end
19
+ end
20
+
21
+ def needed?(gem_name, version)
22
+ gem = lock.gem(gem_name)
23
+ return false unless gem
24
+
25
+ gem['version'] == version
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,95 @@
1
+ module RBS
2
+ module Collection
3
+
4
+ # This class represent the configration file.
5
+ class Config
6
+ class LockfileGenerator
7
+ attr_reader :config, :lock, :gemfile_lock, :lock_path
8
+
9
+ def self.generate(config_path:, gemfile_lock_path:, with_lockfile: true)
10
+ new(config_path: config_path, gemfile_lock_path: gemfile_lock_path, with_lockfile: with_lockfile).generate
11
+ end
12
+
13
+ def initialize(config_path:, gemfile_lock_path:, with_lockfile:)
14
+ @config = Config.from_path config_path
15
+ @lock_path = Config.to_lockfile_path(config_path)
16
+ @lock = Config.from_path(lock_path) if lock_path.exist? && with_lockfile
17
+ @gemfile_lock = Bundler::LockfileParser.new(gemfile_lock_path.read)
18
+ end
19
+
20
+ def generate
21
+ config.gems.each do |gem|
22
+ assign_gem(gem_name: gem['name'], version: gem['version'])
23
+ end
24
+
25
+ gemfile_lock_gems do |spec|
26
+ assign_gem(gem_name: spec.name, version: spec.version)
27
+ end
28
+ remove_ignored_gems!
29
+
30
+ config.dump_to(lock_path)
31
+ config
32
+ end
33
+
34
+ private def assign_gem(gem_name:, version:)
35
+ locked = lock&.gem(gem_name)
36
+ specified = config.gem(gem_name)
37
+
38
+ return if specified&.dig('ignore')
39
+ return if specified&.dig('source') # skip if the source is already filled
40
+
41
+ if locked
42
+ # If rbs_collection.lock.yaml contain the gem, use it.
43
+ upsert_gem specified, locked
44
+ else
45
+ # Find the gem from gem_collection.
46
+ source = find_source(gem_name: gem_name)
47
+ return unless source
48
+
49
+ installed_version = version
50
+ best_version = find_best_version(version: installed_version, versions: source.versions({ 'name' => gem_name }))
51
+ # @type var new_content: RBS::Collection::Config::gem_entry
52
+ new_content = {
53
+ 'name' => gem_name,
54
+ 'version' => best_version.to_s,
55
+ 'source' => source.to_lockfile,
56
+ }
57
+ upsert_gem specified, new_content
58
+ end
59
+ end
60
+
61
+ private def upsert_gem(old, new)
62
+ if old
63
+ old.merge! new
64
+ else
65
+ config.add_gem new
66
+ end
67
+ end
68
+
69
+ private def remove_ignored_gems!
70
+ config.gems.reject! { |gem| gem['ignore'] }
71
+ end
72
+
73
+ private def gemfile_lock_gems(&block)
74
+ gemfile_lock.specs.each do |spec|
75
+ yield spec
76
+ end
77
+ end
78
+
79
+ private def find_source(gem_name:)
80
+ sources = config.sources
81
+
82
+ sources.find { |c| c.has?({ 'name' => gem_name, 'revision' => nil } ) }
83
+ end
84
+
85
+ private def find_best_version(version:, versions:)
86
+ candidates = versions.map { |v| Gem::Version.create(v) or raise }
87
+ return candidates.max || raise unless version
88
+
89
+ v = Gem::Version.create(version) or raise
90
+ Repository.find_best_version(v, candidates)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end