rbs 1.5.1 → 1.7.0.beta.1

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 (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,46 @@
1
+ #ifndef RBS__RUBY_OBJS_H
2
+ #define RBS__RUBY_OBJS_H
3
+
4
+ #include "ruby.h"
5
+
6
+ VALUE rbs_alias(VALUE typename, VALUE location);
7
+ VALUE rbs_ast_annotation(VALUE string, VALUE location);
8
+ VALUE rbs_ast_comment(VALUE string, VALUE location);
9
+ VALUE rbs_ast_decl_alias(VALUE name, VALUE type, VALUE annotations, VALUE location, VALUE comment);
10
+ VALUE rbs_ast_decl_class_super(VALUE name, VALUE args, VALUE location);
11
+ VALUE rbs_ast_decl_class(VALUE name, VALUE type_params, VALUE super_class, VALUE members, VALUE annotations, VALUE location, VALUE comment);
12
+ VALUE rbs_ast_decl_constant(VALUE name, VALUE type, VALUE location, VALUE comment);
13
+ VALUE rbs_ast_decl_global(VALUE name, VALUE type, VALUE location, VALUE comment);
14
+ VALUE rbs_ast_decl_interface(VALUE name, VALUE type_params, VALUE members, VALUE annotations, VALUE location, VALUE comment);
15
+ VALUE rbs_ast_decl_module_self(VALUE name, VALUE args, VALUE location);
16
+ VALUE rbs_ast_decl_module_type_params_param(VALUE name, VALUE variance, VALUE skip_validation, VALUE location);
17
+ VALUE rbs_ast_decl_module_type_params();
18
+ VALUE rbs_ast_decl_module(VALUE name, VALUE type_params, VALUE self_types, VALUE members, VALUE annotations, VALUE location, VALUE comment);
19
+ VALUE rbs_ast_members_alias(VALUE new_name, VALUE old_name, VALUE kind, VALUE annotations, VALUE location, VALUE comment);
20
+ VALUE rbs_ast_members_attribute(VALUE klass, VALUE name, VALUE type, VALUE ivar_name, VALUE kind, VALUE annotations, VALUE location, VALUE comment);
21
+ VALUE rbs_ast_members_method_definition(VALUE name, VALUE kind, VALUE types, VALUE annotations, VALUE location, VALUE comment, VALUE overload);
22
+ VALUE rbs_ast_members_mixin(VALUE klass, VALUE name, VALUE args, VALUE annotations, VALUE location, VALUE comment);
23
+ VALUE rbs_ast_members_variable(VALUE klass, VALUE name, VALUE type, VALUE location, VALUE comment);
24
+ VALUE rbs_ast_members_visibility(VALUE klass, VALUE location);
25
+ VALUE rbs_base_type(VALUE klass, VALUE location);
26
+ VALUE rbs_block(VALUE type, VALUE required);
27
+ VALUE rbs_class_instance(VALUE typename, VALUE type_args, VALUE location);
28
+ VALUE rbs_class_singleton(VALUE typename, VALUE location);
29
+ VALUE rbs_function_param(VALUE type, VALUE name, VALUE location);
30
+ VALUE rbs_function(VALUE required_positional_params, VALUE optional_positional_params, VALUE rest_positional_params, VALUE trailing_positional_params, VALUE required_keywords, VALUE optional_keywords, VALUE rest_keywords, VALUE return_type);
31
+ VALUE rbs_interface(VALUE typename, VALUE type_args, VALUE location);
32
+ VALUE rbs_intersection(VALUE types, VALUE location);
33
+ VALUE rbs_literal(VALUE literal, VALUE location);
34
+ VALUE rbs_method_type(VALUE type_params, VALUE type, VALUE block, VALUE location);
35
+ VALUE rbs_namespace(VALUE path, VALUE absolute);
36
+ VALUE rbs_optional(VALUE type, VALUE location);
37
+ VALUE rbs_proc(VALUE function, VALUE block, VALUE location);
38
+ VALUE rbs_record(VALUE fields, VALUE location);
39
+ VALUE rbs_tuple(VALUE types, VALUE location);
40
+ VALUE rbs_type_name(VALUE namespace, VALUE name);
41
+ VALUE rbs_union(VALUE types, VALUE location);
42
+ VALUE rbs_variable(VALUE name, VALUE location);
43
+
44
+ void pp(VALUE object);
45
+
46
+ #endif
@@ -0,0 +1,65 @@
1
+ #include "rbs_extension.h"
2
+
3
+ static VALUE REGEXP = 0;
4
+ static VALUE HASH = 0;
5
+
6
+ static const char *regexp_str = "\\\\[abefnrstv\"]";
7
+
8
+ static ID gsub = 0;
9
+
10
+ void rbs_unescape_string(VALUE string) {
11
+ if (!REGEXP) {
12
+ REGEXP = rb_reg_new(regexp_str, strlen(regexp_str), 0);
13
+ rb_global_variable(&REGEXP);
14
+ }
15
+
16
+ if (!gsub) {
17
+ gsub = rb_intern("gsub!");
18
+ }
19
+
20
+ if (!HASH) {
21
+ HASH = rb_hash_new();
22
+ rb_hash_aset(HASH, rb_str_new_literal("\\a"), rb_str_new_literal("\a"));
23
+ rb_hash_aset(HASH, rb_str_new_literal("\\b"), rb_str_new_literal("\b"));
24
+ rb_hash_aset(HASH, rb_str_new_literal("\\e"), rb_str_new_literal("\e"));
25
+ rb_hash_aset(HASH, rb_str_new_literal("\\f"), rb_str_new_literal("\f"));
26
+ rb_hash_aset(HASH, rb_str_new_literal("\\n"), rb_str_new_literal("\n"));
27
+ rb_hash_aset(HASH, rb_str_new_literal("\\r"), rb_str_new_literal("\r"));
28
+ rb_hash_aset(HASH, rb_str_new_literal("\\s"), rb_str_new_literal(" "));
29
+ rb_hash_aset(HASH, rb_str_new_literal("\\t"), rb_str_new_literal("\t"));
30
+ rb_hash_aset(HASH, rb_str_new_literal("\\v"), rb_str_new_literal("\v"));
31
+ rb_hash_aset(HASH, rb_str_new_literal("\\\""), rb_str_new_literal("\""));
32
+ rb_global_variable(&HASH);
33
+ }
34
+
35
+ rb_funcall(string, gsub, 2, REGEXP, HASH);
36
+ }
37
+
38
+ VALUE rbs_unquote_string(parserstate *state, range rg, int offset_bytes) {
39
+ VALUE string = state->lexstate->string;
40
+ rb_encoding *enc = rb_enc_get(string);
41
+
42
+ unsigned int first_char = rb_enc_mbc_to_codepoint(
43
+ RSTRING_PTR(string) + rg.start.byte_pos + offset_bytes,
44
+ RSTRING_END(string),
45
+ enc
46
+ );
47
+
48
+ int byte_length = rg.end.byte_pos - rg.start.byte_pos - offset_bytes;
49
+
50
+ if (first_char == '"' || first_char == '\'' || first_char == '`') {
51
+ int bs = rb_enc_codelen(first_char, enc);
52
+ offset_bytes += bs;
53
+ byte_length -= 2 * bs;
54
+ }
55
+
56
+ char *buffer = RSTRING_PTR(state->lexstate->string) + rg.start.byte_pos + offset_bytes;
57
+ VALUE str = rb_enc_str_new(buffer, byte_length, enc);
58
+
59
+ if (first_char == '\"') {
60
+ rbs_unescape_string(str);
61
+ }
62
+
63
+ return str;
64
+ }
65
+
data/goodcheck.yml CHANGED
@@ -40,7 +40,7 @@ rules:
40
40
  glob:
41
41
  - "**/*.rbs"
42
42
  justification:
43
- - When you strictly want `true | false`.
43
+ - When you strictly want `true | false`. Use `bool` in this case.
44
44
  pass:
45
45
  - "(arg: boolish)"
46
46
  - "{ () -> boolish }"
@@ -22,18 +22,6 @@ module RBS
22
22
  def to_json(state = _ = nil)
23
23
  { string: string, location: location }.to_json(state)
24
24
  end
25
-
26
- def concat(string:, location:)
27
- @string.concat string
28
-
29
- if loc = @location
30
- loc.concat location
31
- else
32
- @location = location
33
- end
34
-
35
- self
36
- end
37
25
  end
38
26
  end
39
27
  end
data/lib/rbs/buffer.rb CHANGED
@@ -46,5 +46,9 @@ module RBS
46
46
  def last_position
47
47
  content.size
48
48
  end
49
+
50
+ def inspect
51
+ "#<RBS::Buffer:#{__id__} @name=#{name}, @content=#{content.bytesize} bytes, @lines=#{lines.size} lines,>"
52
+ end
49
53
  end
50
54
  end
@@ -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|
@@ -737,14 +749,11 @@ Examples:
737
749
  args.each do |path|
738
750
  path = Pathname(path)
739
751
  loader.each_file(path, skip_hidden: false, immediate: true) do |file_path|
740
- Parser.parse_signature(file_path.read)
741
- rescue RBS::Parser::SyntaxError => ex
742
- loc = ex.error_value.location
743
- stdout.puts "#{file_path}:#{loc.start_line}:#{loc.start_column}: parse error on value: (#{ex.token_str})"
744
- syntax_error = true
745
- rescue RBS::Parser::SemanticsError => ex
746
- loc = ex.location
747
- stdout.puts "#{file_path}:#{loc.start_line}:#{loc.start_column}: #{ex.original_message}"
752
+ RBS.logger.info "Parsing #{file_path}..."
753
+ buffer = Buffer.new(content: file_path.read, name: file_path)
754
+ Parser.parse_signature(buffer)
755
+ rescue RBS::ParsingError => ex
756
+ stdout.puts ex.message
748
757
  syntax_error = true
749
758
  end
750
759
  end
@@ -819,11 +828,90 @@ EOB
819
828
 
820
829
  # @type var out: String
821
830
  # @type var err: String
822
- out, err, status = Open3.capture3(env_hash, *args)
831
+ out, err, status = __skip__ = Open3.capture3(env_hash, *args)
823
832
  stdout.print(out)
824
833
  stderr.print(err)
825
834
 
826
835
  status
827
836
  end
837
+
838
+ def run_collection(args, options)
839
+ warn "warning: rbs collection is experimental, and the behavior may change until RBS v2.0"
840
+
841
+ opts = collection_options(args)
842
+ params = {}
843
+ opts.order args.drop(1), into: params
844
+ config_path = options.config_path or raise
845
+ lock_path = Collection::Config.to_lockfile_path(config_path)
846
+
847
+ case args[0]
848
+ when 'install'
849
+ unless params[:frozen]
850
+ Collection::Config.generate_lockfile(config_path: config_path, gemfile_lock_path: Pathname('./Gemfile.lock'))
851
+ end
852
+ Collection::Installer.new(lockfile_path: lock_path, stdout: stdout).install_from_lockfile
853
+ when 'update'
854
+ # TODO: Be aware of argv to update only specified gem
855
+ Collection::Config.generate_lockfile(config_path: config_path, gemfile_lock_path: Pathname('./Gemfile.lock'), with_lockfile: false)
856
+ Collection::Installer.new(lockfile_path: lock_path, stdout: stdout).install_from_lockfile
857
+ when 'init'
858
+ if config_path.exist?
859
+ puts "#{config_path} already exists"
860
+ exit 1
861
+ end
862
+
863
+ config_path.write(<<~'YAML')
864
+ # Download sources
865
+ sources:
866
+ - name: ruby/gem_rbs_collection
867
+ remote: https://github.com/ruby/gem_rbs_collection.git
868
+ revision: main
869
+ repo_dir: gems
870
+
871
+ # A directory to install the downloaded RBSs
872
+ path: .gem_rbs_collection
873
+
874
+ gems:
875
+ # Skip loading rbs gem's RBS.
876
+ # It's unnecessary if you don't use rbs as a library.
877
+ - name: rbs
878
+ ignore: true
879
+ YAML
880
+ stdout.puts "created: #{config_path}"
881
+ when 'clean'
882
+ unless lock_path.exist?
883
+ puts "#{lock_path} should exist to clean"
884
+ exit 1
885
+ end
886
+ Collection::Cleaner.new(lockfile_path: lock_path)
887
+ when 'help'
888
+ puts opts.help
889
+ else
890
+ puts opts.help
891
+ exit 1
892
+ end
893
+ end
894
+
895
+ def collection_options(args)
896
+ OptionParser.new do |opts|
897
+ opts.banner = <<~HELP
898
+ Usage: rbs collection [install|update|init|clean|help]
899
+
900
+ Manage RBS collection, which contains third party RBS.
901
+
902
+ Examples:
903
+
904
+ # Initialize the configration file
905
+ $ rbs collection init
906
+
907
+ # Generate the lock file and install RBSs from the lock file
908
+ $ rbs collection install
909
+
910
+ # Update the RBSs
911
+ $ rbs collection update
912
+ HELP
913
+ opts.on('--frozen') if args[0] == 'install'
914
+ end
915
+ end
828
916
  end
829
917
  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