rbs 1.5.1 → 1.7.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +10 -0
  3. data/.github/workflows/ruby.yml +0 -4
  4. data/.gitignore +1 -0
  5. data/CHANGELOG.md +51 -0
  6. data/Gemfile +2 -0
  7. data/Rakefile +7 -22
  8. data/Steepfile +9 -1
  9. data/core/enumerator.rbs +1 -0
  10. data/core/io.rbs +3 -1
  11. data/core/kernel.rbs +4 -4
  12. data/core/trace_point.rbs +1 -1
  13. data/docs/collection.md +116 -0
  14. data/ext/rbs/extension/constants.c +140 -0
  15. data/ext/rbs/extension/constants.h +72 -0
  16. data/ext/rbs/extension/extconf.rb +3 -0
  17. data/ext/rbs/extension/lexer.c +1070 -0
  18. data/ext/rbs/extension/lexer.h +145 -0
  19. data/ext/rbs/extension/location.c +295 -0
  20. data/ext/rbs/extension/location.h +59 -0
  21. data/ext/rbs/extension/main.c +9 -0
  22. data/ext/rbs/extension/parser.c +2418 -0
  23. data/ext/rbs/extension/parser.h +23 -0
  24. data/ext/rbs/extension/parserstate.c +313 -0
  25. data/ext/rbs/extension/parserstate.h +141 -0
  26. data/ext/rbs/extension/rbs_extension.h +40 -0
  27. data/ext/rbs/extension/ruby_objs.c +585 -0
  28. data/ext/rbs/extension/ruby_objs.h +46 -0
  29. data/ext/rbs/extension/unescape.c +65 -0
  30. data/goodcheck.yml +1 -1
  31. data/lib/rbs/ast/comment.rb +0 -12
  32. data/lib/rbs/buffer.rb +4 -0
  33. data/lib/rbs/builtin_names.rb +1 -0
  34. data/lib/rbs/cli.rb +98 -10
  35. data/lib/rbs/collection/cleaner.rb +29 -0
  36. data/lib/rbs/collection/config/lockfile_generator.rb +95 -0
  37. data/lib/rbs/collection/config.rb +85 -0
  38. data/lib/rbs/collection/installer.rb +27 -0
  39. data/lib/rbs/collection/sources/git.rb +162 -0
  40. data/lib/rbs/collection/sources/rubygems.rb +40 -0
  41. data/lib/rbs/collection/sources/stdlib.rb +38 -0
  42. data/lib/rbs/collection/sources.rb +22 -0
  43. data/lib/rbs/collection.rb +13 -0
  44. data/lib/rbs/environment_loader.rb +12 -0
  45. data/lib/rbs/errors.rb +16 -1
  46. data/lib/rbs/location.rb +221 -217
  47. data/lib/rbs/location_aux.rb +108 -0
  48. data/lib/rbs/locator.rb +10 -7
  49. data/lib/rbs/parser_aux.rb +24 -0
  50. data/lib/rbs/repository.rb +13 -7
  51. data/lib/rbs/types.rb +2 -3
  52. data/lib/rbs/validator.rb +4 -1
  53. data/lib/rbs/version.rb +1 -1
  54. data/lib/rbs/writer.rb +4 -2
  55. data/lib/rbs.rb +4 -7
  56. data/rbs.gemspec +2 -1
  57. data/sig/ancestor_builder.rbs +2 -2
  58. data/sig/annotation.rbs +2 -2
  59. data/sig/builtin_names.rbs +1 -0
  60. data/sig/cli.rbs +5 -0
  61. data/sig/collection/cleaner.rbs +13 -0
  62. data/sig/collection/collections.rbs +112 -0
  63. data/sig/collection/config.rbs +69 -0
  64. data/sig/collection/installer.rbs +15 -0
  65. data/sig/collection.rbs +4 -0
  66. data/sig/comment.rbs +7 -7
  67. data/sig/constant_table.rbs +1 -1
  68. data/sig/declarations.rbs +9 -9
  69. data/sig/definition.rbs +1 -1
  70. data/sig/definition_builder.rbs +2 -2
  71. data/sig/environment_loader.rbs +3 -0
  72. data/sig/errors.rbs +30 -25
  73. data/sig/location.rbs +42 -79
  74. data/sig/locator.rbs +2 -2
  75. data/sig/members.rbs +7 -7
  76. data/sig/method_types.rbs +3 -3
  77. data/sig/parser.rbs +11 -21
  78. data/sig/polyfill.rbs +12 -3
  79. data/sig/repository.rbs +4 -0
  80. data/sig/types.rbs +45 -27
  81. data/sig/writer.rbs +1 -1
  82. data/stdlib/json/0/json.rbs +3 -3
  83. data/stdlib/objspace/0/objspace.rbs +406 -0
  84. data/stdlib/openssl/0/openssl.rbs +1 -1
  85. data/stdlib/tempfile/0/tempfile.rbs +270 -0
  86. data/steep/Gemfile.lock +10 -10
  87. metadata +43 -7
  88. data/lib/rbs/parser.rb +0 -3614
@@ -0,0 +1,85 @@
1
+ module RBS
2
+ module Collection
3
+
4
+ # This class represent the configration file.
5
+ class Config
6
+ class CollectionNotAvailable < StandardError
7
+ def initialize
8
+ super <<~MSG
9
+ rbs collection is not initialized.
10
+ Run `rbs collection install` to install RBSs from collection.
11
+ MSG
12
+ end
13
+ end
14
+
15
+ PATH = Pathname('rbs_collection.yaml')
16
+
17
+ # Generate a rbs lockfile from Gemfile.lock to `config_path`.
18
+ # If `with_lockfile` is true, it respects existing rbs lockfile.
19
+ def self.generate_lockfile(config_path:, gemfile_lock_path:, with_lockfile: true)
20
+ LockfileGenerator.generate(config_path: config_path, gemfile_lock_path: gemfile_lock_path, with_lockfile: with_lockfile)
21
+ end
22
+
23
+ def self.from_path(path)
24
+ new(YAML.load(path.read), config_path: path)
25
+ end
26
+
27
+ def self.lockfile_of(config_path)
28
+ lock_path = to_lockfile_path(config_path)
29
+ from_path lock_path if lock_path.exist?
30
+ end
31
+
32
+ def self.to_lockfile_path(config_path)
33
+ config_path.sub_ext('.lock' + config_path.extname)
34
+ end
35
+
36
+ def initialize(data, config_path:)
37
+ @data = data
38
+ @config_path = config_path
39
+ end
40
+
41
+ def add_gem(gem)
42
+ gems << gem
43
+ end
44
+
45
+ def gem(gem_name)
46
+ gems.find { |gem| gem['name'] == gem_name }
47
+ end
48
+
49
+ def repo_path
50
+ @config_path.dirname.join @data['path']
51
+ end
52
+
53
+ def sources
54
+ @sources ||= (
55
+ @data['sources']
56
+ .map { |c| Sources.from_config_entry(c) }
57
+ .push(Sources::Stdlib.instance)
58
+ .push(Sources::Rubygems.instance)
59
+ )
60
+ end
61
+
62
+ def dump_to(io)
63
+ YAML.dump(@data, io)
64
+ end
65
+
66
+ def gems
67
+ @data['gems'] ||= []
68
+ end
69
+
70
+ # It raises an error when there are non-available libraries
71
+ def check_rbs_availability!
72
+ raise CollectionNotAvailable unless repo_path.exist?
73
+
74
+ gems.each do |gem|
75
+ case gem['source']['type']
76
+ when 'git'
77
+ meta_path = repo_path.join(gem['name'], gem['version'], Sources::Git::METADATA_FILENAME)
78
+ raise CollectionNotAvailable unless meta_path.exist?
79
+ raise CollectionNotAvailable unless gem == YAML.load(meta_path.read)
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,27 @@
1
+ module RBS
2
+ module Collection
3
+ class Installer
4
+ attr_reader :lockfile
5
+ attr_reader :stdout
6
+
7
+ def initialize(lockfile_path:, stdout: $stdout)
8
+ @lockfile = Config.from_path(lockfile_path)
9
+ @stdout = stdout
10
+ end
11
+
12
+ def install_from_lockfile
13
+ install_to = lockfile.repo_path
14
+ lockfile.gems.each do |config_entry|
15
+ source_for(config_entry).install(dest: install_to, config_entry: config_entry, stdout: stdout)
16
+ end
17
+ stdout.puts "It's done! #{lockfile.gems.size} gems' RBSs now installed."
18
+ end
19
+
20
+ private def source_for(config_entry)
21
+ @source_for ||= {}
22
+ key = config_entry['source'] or raise
23
+ @source_for[key] ||= Sources.from_config_entry(key)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,162 @@
1
+ require 'digest/sha2'
2
+ require 'open3'
3
+ require 'find'
4
+
5
+ module RBS
6
+ module Collection
7
+ module Sources
8
+ class Git
9
+ METADATA_FILENAME = '.rbs_meta.yaml'
10
+
11
+ class CommandError < StandardError; end
12
+
13
+ attr_reader :name, :remote, :repo_dir
14
+
15
+ def initialize(name:, revision:, remote:, repo_dir:)
16
+ @name = name
17
+ @remote = remote
18
+ @repo_dir = repo_dir || 'gems'
19
+
20
+ setup!(revision: revision)
21
+ end
22
+
23
+ def has?(config_entry)
24
+ gem_name = config_entry['name']
25
+ gem_repo_dir.join(gem_name).directory?
26
+ end
27
+
28
+ def versions(config_entry)
29
+ gem_name = config_entry['name']
30
+ gem_repo_dir.join(gem_name).glob('*/').map { |path| path.basename.to_s }
31
+ end
32
+
33
+ def install(dest:, config_entry:, stdout:)
34
+ gem_name = config_entry['name']
35
+ version = config_entry['version'] or raise
36
+ gem_dir = dest.join(gem_name, version)
37
+
38
+ if gem_dir.directory?
39
+ if (prev = YAML.load_file(gem_dir.join(METADATA_FILENAME))) == config_entry
40
+ stdout.puts "Using #{format_config_entry(config_entry)}"
41
+ else
42
+ # @type var prev: RBS::Collection::Config::gem_entry
43
+ stdout.puts "Updating to #{format_config_entry(config_entry)} from #{format_config_entry(prev)}"
44
+ FileUtils.remove_entry_secure(gem_dir.to_s)
45
+ _install(dest: dest, config_entry: config_entry)
46
+ end
47
+ else
48
+ stdout.puts "Installing #{format_config_entry(config_entry)}"
49
+ _install(dest: dest, config_entry: config_entry)
50
+ end
51
+ end
52
+
53
+ private def _install(dest:, config_entry:)
54
+ gem_name = config_entry['name']
55
+ version = config_entry['version'] or raise
56
+ dest = dest.join(gem_name, version)
57
+ dest.mkpath
58
+ src = gem_repo_dir.join(gem_name, version)
59
+
60
+ cp_r(src, dest)
61
+ dest.join(METADATA_FILENAME).write(YAML.dump(config_entry))
62
+ end
63
+
64
+ private def cp_r(src, dest)
65
+ Find.find(src) do |file_src|
66
+ file_src = Pathname(file_src)
67
+
68
+ # Skip file if it starts with _, such as _test/
69
+ Find.prune if file_src.basename.to_s.start_with?('_')
70
+
71
+ file_src_relative = file_src.relative_path_from(src)
72
+ file_dest = dest.join(file_src_relative)
73
+ file_dest.dirname.mkpath
74
+ FileUtils.copy_entry(file_src, file_dest, false, true) unless file_src.directory?
75
+ end
76
+ end
77
+
78
+ def to_lockfile
79
+ {
80
+ 'type' => 'git',
81
+ 'name' => name,
82
+ 'revision' => resolved_revision,
83
+ 'remote' => remote,
84
+ 'repo_dir' => repo_dir,
85
+ }
86
+ end
87
+
88
+ private def format_config_entry(config_entry)
89
+ name = config_entry['name']
90
+ v = config_entry['version']
91
+
92
+ rev = resolved_revision[0..10]
93
+ desc = "#{name}@#{rev}"
94
+
95
+ "#{name}:#{v} (#{desc})"
96
+ end
97
+
98
+ private def setup!(revision:)
99
+ git_dir.mkpath
100
+ if git_dir.join('.git').directory?
101
+ if need_to_fetch?(revision)
102
+ git 'fetch', 'origin'
103
+ end
104
+ else
105
+ git 'clone', remote, git_dir.to_s
106
+ end
107
+
108
+ begin
109
+ git 'checkout', "origin/#{revision}" # for branch name as `revision`
110
+ rescue CommandError
111
+ git 'checkout', revision
112
+ end
113
+ end
114
+
115
+ private def need_to_fetch?(revision)
116
+ return true unless revision.match?(/\A[a-f0-9]{40}\z/)
117
+
118
+ begin
119
+ git('cat-file', '-e', revision)
120
+ false
121
+ rescue CommandError
122
+ true
123
+ end
124
+ end
125
+
126
+ private def git_dir
127
+ @git_dir ||= (
128
+ base = Pathname(ENV['XDG_CACHE_HOME'] || File.expand_path("~/.cache"))
129
+ dir = base.join('rbs', Digest::SHA256.hexdigest(remote))
130
+ dir.mkpath
131
+ dir
132
+ )
133
+ end
134
+
135
+ private def gem_repo_dir
136
+ git_dir.join @repo_dir
137
+ end
138
+
139
+ private def resolved_revision
140
+ @resolved_revision ||= resolve_revision
141
+ end
142
+
143
+ private def resolve_revision
144
+ git('rev-parse', 'HEAD').chomp
145
+ end
146
+
147
+ private def git(*cmd)
148
+ sh! 'git', *cmd
149
+ end
150
+
151
+ private def sh!(*cmd)
152
+ RBS.logger.debug "$ #{cmd.join(' ')}"
153
+ (__skip__ = Open3.capture3(*cmd, chdir: git_dir)).then do |out, err, status|
154
+ raise CommandError, "Unexpected status #{status.exitstatus}\n\n#{err}" unless status.success?
155
+
156
+ out
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,40 @@
1
+ require 'singleton'
2
+
3
+ module RBS
4
+ module Collection
5
+ module Sources
6
+ # Signatures that are inclduded in gem package as sig/ directory.
7
+ class Rubygems
8
+ include Singleton
9
+
10
+ def has?(config_entry)
11
+ gem_sig_path(config_entry)
12
+ end
13
+
14
+ def versions(config_entry)
15
+ spec, _ = gem_sig_path(config_entry)
16
+ spec or raise
17
+ [spec.version.to_s]
18
+ end
19
+
20
+ def install(dest:, config_entry:, stdout:)
21
+ # Do nothing because stdlib RBS is available by default
22
+ name = config_entry['name']
23
+ version = config_entry['version'] or raise
24
+ _, from = gem_sig_path(config_entry)
25
+ stdout.puts "Using #{name}:#{version} (#{from})"
26
+ end
27
+
28
+ def to_lockfile
29
+ {
30
+ 'type' => 'rubygems',
31
+ }
32
+ end
33
+
34
+ private def gem_sig_path(config_entry)
35
+ RBS::EnvironmentLoader.gem_sig_path(config_entry['name'], config_entry['version'])
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,38 @@
1
+ require 'singleton'
2
+
3
+ module RBS
4
+ module Collection
5
+ module Sources
6
+ # signatures that are bundled in rbs gem under the stdlib/ directory
7
+ class Stdlib
8
+ include Singleton
9
+
10
+ def has?(config_entry)
11
+ gem_dir(config_entry).exist?
12
+ end
13
+
14
+ def versions(config_entry)
15
+ gem_dir(config_entry).glob('*/').map { |path| path.basename.to_s }
16
+ end
17
+
18
+ def install(dest:, config_entry:, stdout:)
19
+ # Do nothing because stdlib RBS is available by default
20
+ name = config_entry['name']
21
+ version = config_entry['version'] or raise
22
+ from = gem_dir(config_entry) / version
23
+ stdout.puts "Using #{name}:#{version} (#{from})"
24
+ end
25
+
26
+ def to_lockfile
27
+ {
28
+ 'type' => 'stdlib',
29
+ }
30
+ end
31
+
32
+ private def gem_dir(config_entry)
33
+ Repository::DEFAULT_STDLIB_ROOT.join(config_entry['name'])
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ require_relative './sources/git'
2
+ require_relative './sources/stdlib'
3
+ require_relative './sources/rubygems'
4
+
5
+ module RBS
6
+ module Collection
7
+ module Sources
8
+ def self.from_config_entry(source_entry)
9
+ case source_entry['type']
10
+ when 'git', nil # git source by default
11
+ __skip__ = Git.new(**source_entry.slice('name', 'revision', 'remote', 'repo_dir').transform_keys(&:to_sym))
12
+ when 'stdlib'
13
+ Stdlib.instance
14
+ when 'rubygems'
15
+ Rubygems.instance
16
+ else
17
+ raise
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ require 'yaml'
2
+ require 'bundler'
3
+
4
+ require_relative './collection/sources'
5
+ require_relative './collection/config'
6
+ require_relative './collection/config/lockfile_generator'
7
+ require_relative './collection/installer'
8
+ require_relative './collection/cleaner'
9
+
10
+ module RBS
11
+ module Collection
12
+ end
13
+ end
@@ -47,6 +47,18 @@ module RBS
47
47
  end
48
48
  end
49
49
 
50
+ def add_collection(collection_config)
51
+ warn "warning: rbs collection is experimental, and the behavior may change until RBS v2.0"
52
+
53
+ collection_config.check_rbs_availability!
54
+
55
+ repository.add(collection_config.repo_path)
56
+
57
+ collection_config.gems.each do |gem|
58
+ add(library: gem['name'], version: gem['version'])
59
+ end
60
+ end
61
+
50
62
  def has_library?(library:, version:)
51
63
  if self.class.gem_sig_path(library, version) || repository.lookup(library, version)
52
64
  true
data/lib/rbs/errors.rb CHANGED
@@ -15,10 +15,23 @@ module RBS
15
15
  end
16
16
 
17
17
  class ErrorBase < StandardError; end
18
- class ParsingError < ErrorBase; end
19
18
  class LoadingError < ErrorBase; end
20
19
  class DefinitionError < ErrorBase; end
21
20
 
21
+ class ParsingError < ErrorBase
22
+ attr_reader :location
23
+ attr_reader :error_message
24
+ attr_reader :token_type
25
+
26
+ def initialize(location, error_message, token_type)
27
+ @location = location
28
+ @error_message = error_message
29
+ @token_type = token_type
30
+
31
+ super "#{Location.to_string location}: Syntax error: #{error_message}, token=`#{location.source}` (#{token_type})"
32
+ end
33
+ end
34
+
22
35
  class InvalidTypeApplicationError < DefinitionError
23
36
  attr_reader :type_name
24
37
  attr_reader :args
@@ -383,6 +396,8 @@ module RBS
383
396
  "include"
384
397
  when AST::Members::Extend
385
398
  "extend"
399
+ else
400
+ raise
386
401
  end
387
402
  end
388
403
  end