rbs 1.3.3 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -319,9 +319,16 @@ module RBS
319
319
  const_to_name(node.children[0])
320
320
  end
321
321
 
322
+ value_node = node.children.last
323
+ type = if value_node.nil?
324
+ # Give up type prediction when node is MASGN.
325
+ Types::Bases::Any.new(location: nil)
326
+ else
327
+ node_type(value_node)
328
+ end
322
329
  decls << AST::Declarations::Constant.new(
323
330
  name: const_name,
324
- type: node_type(node.children.last),
331
+ type: type,
325
332
  location: nil,
326
333
  comment: comments[node.first_lineno - 1]
327
334
  )
@@ -87,7 +87,7 @@ module RBS
87
87
  unless const_name(mix)
88
88
  RBS.logger.warn("Skipping anonymous module #{mix} included in #{mod}")
89
89
  else
90
- module_name = module_full_name = to_type_name(const_name(mix))
90
+ module_name = module_full_name = to_type_name(const_name(mix), full_name: true)
91
91
  if module_full_name.namespace == type_name.namespace
92
92
  module_name = TypeName.new(name: module_full_name.name, namespace: Namespace.empty)
93
93
  end
@@ -55,13 +55,8 @@ module RBS
55
55
  end
56
56
 
57
57
  def find_best_version(version)
58
- return latest_version unless version
59
-
60
- if v = version_names.reverse.bsearch {|v| v <= version ? true : false }
61
- versions[v]
62
- else
63
- oldest_version
64
- end
58
+ best_version = Repository.find_best_version(version, version_names)
59
+ versions[best_version]
65
60
  end
66
61
 
67
62
  def empty?
@@ -89,6 +84,17 @@ module RBS
89
84
  end
90
85
  end
91
86
 
87
+ def self.find_best_version(version, candidates)
88
+ candidates = candidates.sort
89
+ return candidates.last || raise unless version
90
+
91
+ if v = candidates.reverse.bsearch {|v| v <= version ? true : false }
92
+ v
93
+ else
94
+ candidates.first or raise
95
+ end
96
+ end
97
+
92
98
  def add(dir)
93
99
  dirs << dir
94
100
 
@@ -0,0 +1,88 @@
1
+ module RBS
2
+ class TypeAliasDependency
3
+ attr_reader :env
4
+
5
+ # Direct dependencies corresponds to a directed graph
6
+ # with vertices as types and directions based on assignment of types
7
+ attr_reader :direct_dependencies
8
+ # A hash which stores the transitive closure
9
+ # of the directed graph
10
+ attr_reader :dependencies
11
+
12
+ def initialize(env:)
13
+ @env = env
14
+ end
15
+
16
+ # Check if an alias type definition is circular & prohibited
17
+ def circular_definition?(alias_name)
18
+ # Construct transitive closure, if not constructed already
19
+ transitive_closure() unless @dependencies
20
+
21
+ # Check for recursive type alias
22
+ @dependencies[alias_name][alias_name]
23
+ end
24
+
25
+ def build_dependencies
26
+ return if @direct_dependencies
27
+
28
+ # Initialize hash(a directed graph)
29
+ @direct_dependencies = {}
30
+ # Initialize dependencies as an empty hash
31
+ @dependencies = {}
32
+ # Iterate over alias declarations inserted into environment
33
+ env.alias_decls.each do |name, entry|
34
+ # Construct a directed graph by recursively extracting type aliases
35
+ @direct_dependencies[name] = direct_dependency(entry.decl.type)
36
+ # Initialize dependencies with an empty hash
37
+ @dependencies[name] = {}
38
+ end
39
+ end
40
+
41
+ def transitive_closure
42
+ # Construct a graph of direct dependencies
43
+ build_dependencies()
44
+ # Construct transitive closure by using DFS(recursive technique)
45
+ @direct_dependencies.each_key do |name|
46
+ dependency(name, name)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ # Constructs directed graph recursively
53
+ def direct_dependency(type, result = Set[])
54
+ case type
55
+ when RBS::Types::Union, RBS::Types::Intersection, RBS::Types::Optional
56
+ # Iterate over nested types & extract type aliases recursively
57
+ type.each_type do |nested_type|
58
+ direct_dependency(nested_type, result)
59
+ end
60
+ when RBS::Types::Alias
61
+ # Append type name if the type is an alias
62
+ result << type.name
63
+ end
64
+
65
+ result
66
+ end
67
+
68
+ # Recursive function to construct transitive closure
69
+ def dependency(start, vertex, nested = nil)
70
+ if (start == vertex)
71
+ if (@direct_dependencies[start].include?(vertex) || nested)
72
+ # Mark a vertex as connected to itself
73
+ # if it is connected as an edge || a path(traverse multiple edges)
74
+ @dependencies[start][vertex] = true
75
+ end
76
+ else
77
+ # Mark a pair of vertices as connected while recursively performing DFS
78
+ @dependencies[start][vertex] = true
79
+ end
80
+
81
+ # Iterate over the direct dependencies of the vertex
82
+ @direct_dependencies[vertex]&.each do |type_name|
83
+ # Invoke the function unless it is already checked
84
+ dependency(start, type_name, start == type_name) unless @dependencies[start][type_name]
85
+ end
86
+ end
87
+ end
88
+ end
data/lib/rbs/validator.rb CHANGED
@@ -53,5 +53,13 @@ module RBS
53
53
  validate_type(type, context: context)
54
54
  end
55
55
  end
56
+
57
+ def validate_type_alias(entry:)
58
+ @type_alias_dependency ||= TypeAliasDependency.new(env: env)
59
+ if @type_alias_dependency.circular_definition?(entry.decl.name)
60
+ location = entry.decl.location or raise
61
+ raise RecursiveTypeAliasError.new(alias_names: [entry.decl.name], location: location)
62
+ end
63
+ end
56
64
  end
57
65
  end
data/lib/rbs/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RBS
2
- VERSION = "1.3.3"
2
+ VERSION = "1.6.0"
3
3
  end
data/lib/rbs.rb CHANGED
@@ -44,6 +44,8 @@ require "rbs/factory"
44
44
  require "rbs/repository"
45
45
  require "rbs/ancestor_graph"
46
46
  require "rbs/locator"
47
+ require "rbs/type_alias_dependency"
48
+ require "rbs/collection"
47
49
 
48
50
  begin
49
51
  require "rbs/parser"
@@ -37,5 +37,6 @@ module RBS
37
37
  Regexp: Name
38
38
  TrueClass: Name
39
39
  FalseClass: Name
40
+ Numeric: Name
40
41
  end
41
42
  end
data/sig/cli.rbs CHANGED
@@ -2,6 +2,7 @@ module RBS
2
2
  class CLI
3
3
  class LibraryOptions
4
4
  attr_accessor core_root: Pathname?
5
+ attr_accessor config_path: Pathname?
5
6
 
6
7
  attr_reader libs: Array[String]
7
8
  attr_reader dirs: Array[String]
@@ -63,6 +64,10 @@ module RBS
63
64
 
64
65
  def run_test: (Array[String], LibraryOptions) -> void
65
66
 
67
+ def run_collection: (Array[String], LibraryOptions) -> void
68
+
66
69
  def test_opt: (LibraryOptions) -> String?
70
+
71
+ def collection_options: (Array[String]) -> OptionParser
67
72
  end
68
73
  end
@@ -0,0 +1,13 @@
1
+ module RBS
2
+ module Collection
3
+ class Cleaner
4
+ attr_reader lock: Config
5
+
6
+ def initialize: (lockfile_path: Pathname) -> void
7
+
8
+ def clean: () -> void
9
+
10
+ def needed?: (String gem_name, String version) -> bool
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,112 @@
1
+ module RBS
2
+ module Collection
3
+ module Sources
4
+ def self.from_config_entry: (source_entry) -> _Source
5
+
6
+ interface _Source
7
+ def has?: (Config::gem_entry) -> boolish
8
+ def versions: (Config::gem_entry) -> Array[String]
9
+ def install: (dest: Pathname, config_entry: Config::gem_entry, stdout: CLI::_IO) -> void
10
+ def to_lockfile: () -> source_entry
11
+ end
12
+
13
+ type source_entry = Git::source_entry
14
+ | Stdlib::source_entry
15
+ | Rubygems::source_entry
16
+
17
+ class Git
18
+ METADATA_FILENAME: String
19
+
20
+ type source_entry = {
21
+ 'type' => 'git',
22
+ 'name' => String,
23
+ 'remote' => String,
24
+ 'revision' => String,
25
+ 'repo_dir' => String?,
26
+ }
27
+
28
+ class CommandError < StandardError
29
+ end
30
+
31
+ attr_reader name: String
32
+ attr_reader remote: String
33
+ attr_reader repo_dir: String
34
+
35
+ def initialize: (name: String, revision: String, remote: String, repo_dir: String?) -> untyped
36
+
37
+ def has?: (Config::gem_entry) -> bool
38
+
39
+ def versions: (Config::gem_entry) -> Array[String]
40
+
41
+ def install: (dest: Pathname, config_entry: Config::gem_entry, stdout: CLI::_IO) -> void
42
+
43
+ def to_lockfile: () -> source_entry
44
+
45
+ private
46
+
47
+ def _install: (dest: Pathname , config_entry: Config::gem_entry) -> void
48
+
49
+ def setup!: (revision: String) -> void
50
+
51
+ def need_to_fetch?: (String revision ) -> bool
52
+
53
+ def git_dir: () -> Pathname
54
+
55
+ def gem_repo_dir: () -> Pathname
56
+
57
+ def with_revision: [T] () { () -> T } -> T
58
+
59
+ def resolved_revision: () -> String
60
+
61
+ def resolve_revision: () -> String
62
+
63
+ def git: (*String cmd) -> String
64
+
65
+ def sh!: (*String cmd) -> String
66
+
67
+ def format_config_entry: (Config::gem_entry) -> String
68
+ end
69
+
70
+ # signatures that are bundled in rbs gem under the stdlib/ directory
71
+ class Stdlib
72
+ type source_entry = {
73
+ 'type' => 'stdlib',
74
+ }
75
+
76
+ # polyfill of singleton module
77
+ def self.instance: () -> instance
78
+
79
+ def has?: (Config::gem_entry) -> bool
80
+
81
+ def versions: (Config::gem_entry) -> Array[String]
82
+
83
+ def install: (dest: Pathname, config_entry: Config::gem_entry, stdout: CLI::_IO) -> void
84
+
85
+ def to_lockfile: () -> source_entry
86
+
87
+ private
88
+
89
+ def gem_dir: (Config::gem_entry) -> Pathname
90
+ end
91
+
92
+ # sig/ directory
93
+ class Rubygems
94
+ type source_entry = {
95
+ 'type' => 'rubygems',
96
+ }
97
+
98
+ # polyfill of singleton module
99
+ def self.instance: () -> instance
100
+
101
+ def has?: (Config::gem_entry) -> boolish
102
+ def versions: (Config::gem_entry) -> Array[String]
103
+ def install: (dest: Pathname, config_entry: Config::gem_entry, stdout: CLI::_IO) -> void
104
+ def to_lockfile: () -> source_entry
105
+
106
+ private
107
+
108
+ def gem_sig_path: (Config::gem_entry) -> [Gem::Specification, Pathname]?
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,69 @@
1
+ module RBS
2
+ module Collection
3
+ # This class represent the configration file.
4
+ class Config
5
+ class LockfileGenerator
6
+ attr_reader config: Config
7
+ attr_reader lock: Config?
8
+ attr_reader lock_path: Pathname
9
+ attr_reader gemfile_lock: Bundler::LockfileParser
10
+
11
+ def self.generate: (config_path: Pathname, gemfile_lock_path: Pathname, ?with_lockfile: boolish) -> Config
12
+
13
+ def initialize: (config_path: Pathname, gemfile_lock_path: Pathname, with_lockfile: boolish) -> void
14
+
15
+ def generate: () -> Config
16
+
17
+ private
18
+
19
+ def assign_gem: (gem_name: String, version: String?) -> void
20
+
21
+ def upsert_gem: (gem_entry? old, gem_entry new) -> void
22
+
23
+ def gemfile_lock_gems: () { (untyped) -> void } -> void
24
+
25
+ def remove_ignored_gems!: () -> void
26
+
27
+ def find_source: (gem_name: String) -> untyped
28
+
29
+ def find_best_version: (version: String?, versions: Array[String]) -> Gem::Version
30
+ end
31
+
32
+ PATH: Pathname
33
+
34
+ type gem_entry = {
35
+ 'name' => String,
36
+ 'version' => String?,
37
+ 'ignore' => boolish,
38
+ 'source' => Sources::source_entry?
39
+ }
40
+
41
+ @config_path: Pathname
42
+
43
+ def self.generate_lockfile: (config_path: Pathname, gemfile_lock_path: Pathname, ?with_lockfile: boolish) -> Config
44
+
45
+ def self.from_path: (Pathname path) -> Config
46
+
47
+ def self.lockfile_of: (Pathname config_path) -> Config?
48
+
49
+ def self.to_lockfile_path: (Pathname config_path) -> Pathname
50
+
51
+ # config_path is necessary to resolve relative repo_path
52
+ def initialize: (untyped data, config_path: Pathname) -> void
53
+
54
+ def add_gem: (untyped gem) -> untyped
55
+
56
+ def gem: (String gem_name) -> untyped
57
+
58
+ def repo_path: () -> Pathname
59
+
60
+ def sources: () -> Array[Sources::_Source]
61
+
62
+ def dump_to: (Pathname) -> void
63
+
64
+ def gems: () -> Array[untyped]
65
+
66
+ def check_rbs_availability!: () -> void
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,15 @@
1
+ module RBS
2
+ module Collection
3
+ class Installer
4
+ attr_reader lockfile: Config
5
+ attr_reader stdout: CLI::_IO
6
+
7
+ def initialize: (lockfile_path: Pathname, ?stdout: CLI::_IO) -> void
8
+ def install_from_lockfile: () -> void
9
+
10
+ private
11
+
12
+ def source_for: (Config::gem_entry) -> Sources::_Source
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ module RBS
2
+ module Collection
3
+ end
4
+ end
@@ -78,6 +78,9 @@ module RBS
78
78
  def add: (path: Pathname) -> void
79
79
  | (library: String, version: String?) -> void
80
80
 
81
+ # Add repository path and libraries via rbs_collection.lock.yaml.
82
+ def add_collection: (Collection::Config collection_config) -> void
83
+
81
84
  # This is helper function to test if RBS for a library is available or not.
82
85
  #
83
86
  def has_library?: (library: String, version: String?) -> bool
data/sig/errors.rbs CHANGED
@@ -196,4 +196,13 @@ module RBS
196
196
 
197
197
  def mixin_name: () -> String
198
198
  end
199
+
200
+ class RecursiveTypeAliasError < LoadingError
201
+ attr_reader alias_names: Array[TypeName]
202
+ attr_reader location: Location
203
+
204
+ def initialize: (alias_names: Array[TypeName], location: Location) -> void
205
+
206
+ def name: () -> String
207
+ end
199
208
  end