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,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