orthoses 1.4.0 → 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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -1
  3. data/Gemfile.lock +25 -22
  4. data/README.md +8 -0
  5. data/examples/rack-test/Gemfile +8 -0
  6. data/examples/rack-test/Gemfile.lock +30 -0
  7. data/examples/rack-test/Rakefile +7 -0
  8. data/examples/rack-test/generate.rb +31 -0
  9. data/lib/orthoses/call_tracer/capturable.rb +2 -2
  10. data/lib/orthoses/call_tracer/lazy.rb +12 -0
  11. data/lib/orthoses/content/duplication_checker.rb +1 -1
  12. data/lib/orthoses/content/environment.rb +2 -1
  13. data/lib/orthoses/content/header_builder.rb +4 -5
  14. data/lib/orthoses/content.rb +21 -7
  15. data/lib/orthoses/sort.rb +18 -0
  16. data/lib/orthoses/trace/attribute.rb +85 -0
  17. data/lib/orthoses/trace/method.rb +202 -0
  18. data/lib/orthoses/trace/targetable.rb +17 -0
  19. data/lib/orthoses/trace.rb +21 -0
  20. data/lib/orthoses/utils/type_list.rb +73 -0
  21. data/lib/orthoses/utils.rb +23 -32
  22. data/lib/orthoses/version.rb +1 -1
  23. data/lib/orthoses.rb +4 -1
  24. data/orthoses.gemspec +1 -1
  25. data/sig/orthoses/call_tracer/lazy.rbs +2 -1
  26. data/sig/orthoses/call_tracer.rbs +1 -1
  27. data/sig/orthoses/content/header_builder.rbs +1 -0
  28. data/sig/orthoses/content.rbs +2 -0
  29. data/sig/orthoses/lazy_trace_point.rbs +1 -1
  30. data/sig/orthoses/sort.rbs +6 -0
  31. data/sig/orthoses/trace/attribute.rbs +8 -0
  32. data/sig/orthoses/trace/method/info.rbs +4 -0
  33. data/sig/orthoses/trace/method.rbs +11 -0
  34. data/sig/orthoses/trace/targetable.rbs +5 -0
  35. data/sig/orthoses/trace.rbs +6 -0
  36. data/sig/orthoses/utils/type_list.rbs +10 -0
  37. data/sig/orthoses/utils.rbs +1 -3
  38. data/sig/orthoses.rbs +1 -1
  39. metadata +22 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ee37ddf25be5bbed113698301a2aeafb3b50e7af1da412b9a1fc6afad18d8c2
4
- data.tar.gz: '075957101080441e02f4129303e766ecdfe1e7f30616177bc41a2cbf41f521f8'
3
+ metadata.gz: 0bde1745c71d6d30b7b45ce7f956a1c02f0d44353932c3743211aa1c05f58d39
4
+ data.tar.gz: bbe5b95a7665d39faab82b5f53e18cffd22962df1913f1c52698ea51c6045e2c
5
5
  SHA512:
6
- metadata.gz: 98c1cff14563ecf14713890e56bddb053d6f128aacad7ff23b6f96cc8b2c338cbea511cc668ac18079ec921cb0183b145f6b26a3c04403a98e8aa0bf734942f7
7
- data.tar.gz: 5dd573a63a6998e3a1fac1dd94f292dd80742185277124f6449ded149be662b74995815bad629157fc553cb8bef15bb31b123fdbcdccb1af3dff4d2645bc0da2
6
+ metadata.gz: 496d255f99751ac854e5b05ecac69b1e0dc7dbb5fca53bf99d6d622e2dbb154af643de7bf8de8190dca15ff1933fac35c17735da9285532a8a3246d7002836cd
7
+ data.tar.gz: 56c77cf9074eeb75547c1be3868e9843b0d5337400d2248592879f76fd6cc14f99660d8dbbf593c1d66bac1152e1dafbaa4820ea45a49f5f47945027ce45d53a
data/Gemfile CHANGED
@@ -8,4 +8,8 @@ gemspec
8
8
  gem "rbs"
9
9
  gem "rake", "~> 13.0"
10
10
  gem "rgot", "~> 1.1"
11
- gem "steep"
11
+ gem "activesupport"
12
+
13
+ group :steep do
14
+ gem "steep", "1.4.0.dev.2"
15
+ end
data/Gemfile.lock CHANGED
@@ -1,43 +1,44 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- orthoses (1.4.0)
5
- rbs (~> 2.0)
4
+ orthoses (1.6.0)
5
+ rbs (~> 3.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activesupport (7.0.4)
10
+ activesupport (7.0.4.3)
11
11
  concurrent-ruby (~> 1.0, >= 1.0.2)
12
12
  i18n (>= 1.6, < 2)
13
13
  minitest (>= 5.1)
14
14
  tzinfo (~> 2.0)
15
15
  ast (2.4.2)
16
- concurrent-ruby (1.1.10)
17
- csv (3.2.5)
16
+ concurrent-ruby (1.2.2)
17
+ csv (3.2.6)
18
18
  ffi (1.15.5)
19
- fileutils (1.6.0)
19
+ fileutils (1.7.0)
20
20
  i18n (1.12.0)
21
21
  concurrent-ruby (~> 1.0)
22
- json (2.6.2)
23
- language_server-protocol (3.17.0.1)
24
- listen (3.7.1)
22
+ json (2.6.3)
23
+ language_server-protocol (3.17.0.3)
24
+ listen (3.8.0)
25
25
  rb-fsevent (~> 0.10, >= 0.10.3)
26
26
  rb-inotify (~> 0.9, >= 0.9.10)
27
- logger (1.5.1)
28
- minitest (5.16.3)
27
+ logger (1.5.3)
28
+ minitest (5.18.0)
29
29
  parallel (1.22.1)
30
- parser (3.1.2.1)
30
+ parser (3.2.1.1)
31
31
  ast (~> 2.4.1)
32
+ pathname (0.2.1)
32
33
  rainbow (3.1.1)
33
34
  rake (13.0.6)
34
35
  rb-fsevent (0.11.2)
35
36
  rb-inotify (0.10.1)
36
37
  ffi (~> 1.0)
37
- rbs (2.7.0)
38
- rgot (1.1.0)
39
- securerandom (0.2.0)
40
- steep (1.2.1)
38
+ rbs (3.0.4)
39
+ rgot (1.3.0)
40
+ securerandom (0.2.2)
41
+ steep (1.4.0.dev.2)
41
42
  activesupport (>= 5.1)
42
43
  csv (>= 3.0.9)
43
44
  fileutils (>= 1.1.0)
@@ -47,27 +48,29 @@ GEM
47
48
  logger (>= 1.3.0)
48
49
  parallel (>= 1.0.0)
49
50
  parser (>= 3.1)
51
+ pathname (>= 0.2.1)
50
52
  rainbow (>= 2.2.2, < 4.0)
51
- rbs (>= 2.7.0)
53
+ rbs (>= 2.8.0)
52
54
  securerandom (>= 0.1)
53
55
  strscan (>= 1.0.0)
54
56
  terminal-table (>= 2, < 4)
55
- strscan (3.0.4)
57
+ strscan (3.0.6)
56
58
  terminal-table (3.0.2)
57
59
  unicode-display_width (>= 1.1.1, < 3)
58
- tzinfo (2.0.5)
60
+ tzinfo (2.0.6)
59
61
  concurrent-ruby (~> 1.0)
60
- unicode-display_width (2.3.0)
62
+ unicode-display_width (2.4.2)
61
63
 
62
64
  PLATFORMS
63
65
  ruby
64
66
 
65
67
  DEPENDENCIES
68
+ activesupport
66
69
  orthoses!
67
70
  rake (~> 13.0)
68
71
  rbs
69
72
  rgot (~> 1.1)
70
- steep
73
+ steep (= 1.4.0.dev.2)
71
74
 
72
75
  BUNDLED WITH
73
- 2.3.9
76
+ 2.4.8
data/README.md CHANGED
@@ -169,6 +169,14 @@ Run `rbs prototype runtime` command process with `patterns` option string.
169
169
 
170
170
  Force load const defined by `autoload`.
171
171
 
172
+ ### Orthoses::Sort
173
+
174
+ Sort signatures by class/module.
175
+
176
+ ## Orthoses::Trace
177
+
178
+ Trace the argument and return value objects when the method is actually called and determine the type.
179
+
172
180
  ## Development
173
181
 
174
182
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "rack"
4
+ gem "rake"
5
+ gem "cgi"
6
+ gem "minitest"
7
+ gem "minitest-global_expectations"
8
+ gem "orthoses", path: "../../"
@@ -0,0 +1,30 @@
1
+ PATH
2
+ remote: ../..
3
+ specs:
4
+ orthoses (1.4.0)
5
+ rbs (~> 2.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ cgi (0.3.6)
11
+ minitest (5.16.3)
12
+ minitest-global_expectations (1.0.1)
13
+ minitest (> 5)
14
+ rack (3.0.2)
15
+ rake (13.0.6)
16
+ rbs (2.8.1)
17
+
18
+ PLATFORMS
19
+ arm64-darwin-21
20
+
21
+ DEPENDENCIES
22
+ cgi
23
+ minitest
24
+ minitest-global_expectations
25
+ orthoses!
26
+ rack
27
+ rake
28
+
29
+ BUNDLED WITH
30
+ 2.3.16
@@ -0,0 +1,7 @@
1
+ file :src do
2
+ sh "git clone --depth 1 https://github.com/rack/rack-test.git src"
3
+ end
4
+
5
+ task :generate => :src do
6
+ load "generate.rb"
7
+ end
@@ -0,0 +1,31 @@
1
+ require 'pathname'
2
+ require 'orthoses'
3
+ require 'fileutils'
4
+
5
+ include FileUtils
6
+
7
+ out = Pathname('out')
8
+ out.rmtree rescue nil
9
+ Orthoses.logger.level = :warn
10
+ Orthoses::Builder.new do
11
+ use Orthoses::CreateFileByName,
12
+ base_dir: out.to_s
13
+ # use Orthoses::Sort
14
+ use Orthoses::Filter do |name, content|
15
+ name.start_with?("Rack::Test")
16
+ end
17
+ use Orthoses::Trace,
18
+ patterns: ['Rack::Test*']
19
+ run ->(){
20
+ cd "src" do
21
+ load "spec/all.rb"
22
+ Minitest.run
23
+ # avoid to run on at_exit
24
+ module ::Minitest
25
+ def self.run args = []
26
+ 0
27
+ end
28
+ end
29
+ end
30
+ }
31
+ end.call
@@ -25,13 +25,13 @@ module Orthoses
25
25
  else
26
26
  hash[name] = tp.binding.local_variable_get(name).then do |var|
27
27
  case var
28
- when Module, Thread::Backtrace::Location
28
+ when Module, Thread::Backtrace::Location, IO, Encoding
29
29
  var
30
30
  else
31
31
  var.dup
32
32
  end
33
33
  rescue => err
34
- warn("#dup fail (#{err.class}) #{err.message}")
34
+ warn("#{var.class}#dup failed with #{err.class}:#{err.message}")
35
35
  var
36
36
  end
37
37
  end
@@ -2,6 +2,18 @@
2
2
 
3
3
  module Orthoses
4
4
  class CallTracer
5
+ # CallTracer::Lazy is possible to perform a trace
6
+ # equivalent to CallTracer before method is defined.
7
+ # scope = CallTracerLazy.new
8
+ # scope.trace("ActiveRecord::Base#scope") do
9
+ # require 'active_record/all'
10
+ # @loader.call
11
+ # end
12
+ # scope.captures.each do |capture|
13
+ # capture.argument[:name]
14
+ # capture.argument[:body]
15
+ # capture.argument[:block]
16
+ # end
5
17
  class Lazy
6
18
  include Capturable
7
19
 
@@ -11,7 +11,7 @@ module Orthoses
11
11
  return unless @decl.respond_to?(:members)
12
12
  uniq_map = {}
13
13
  @decl.members.each do |member|
14
- if member.instance_of?(RBS::AST::Members::MethodDefinition) && member.overload
14
+ if member.instance_of?(RBS::AST::Members::MethodDefinition) && member.overloading?
15
15
  # avoid to duplicate and keep order
16
16
  uniq_map[Object.new] = member
17
17
  else
@@ -7,7 +7,8 @@ module Orthoses
7
7
  paths.each do |path|
8
8
  Orthoses.logger.debug("Load #{path}")
9
9
  buffer = RBS::Buffer.new(name: path.to_s, content: File.read(path.to_s, encoding: "UTF-8"))
10
- RBS::Parser.parse_signature(buffer).each do |decl|
10
+ _, _, decls = RBS::Parser.parse_signature(buffer)
11
+ decls.each do |decl|
11
12
  env << decl
12
13
  end
13
14
  end
@@ -3,7 +3,7 @@ module Orthoses
3
3
  class HeaderBuilder
4
4
  def initialize(env:)
5
5
  @env = env
6
- @resolver = RBS::TypeNameResolver.from_env(env)
6
+ @resolver = RBS::Resolver::TypeNameResolver.new(env)
7
7
  end
8
8
 
9
9
  def build(entry:, name_hint: nil)
@@ -66,11 +66,10 @@ module Orthoses
66
66
  "interface #{name_and_params(resolved_name.relative!, entry.decl.type_params)}"
67
67
  end
68
68
 
69
+ include RBS::Environment::ContextUtil
70
+
69
71
  def build_context(entry:)
70
- context = entry.outer.length.times.map do |i|
71
- entry.outer[0, i + 1].map(&:name).inject(:+).to_namespace.absolute!
72
- end
73
- context.push(RBS::Namespace.root)
72
+ calculate_context(entry.outer + [entry.decl])
74
73
  end
75
74
 
76
75
  def name_and_params(name, params)
@@ -60,12 +60,17 @@ module Orthoses
60
60
  end
61
61
 
62
62
  def uniq!
63
- out = ArrayIO.new
64
- writer = RBS::Writer.new(out: out)
65
- to_decl.members.each do |member|
66
- writer.write_member(member)
67
- end
68
- @body.replace(out.to_a)
63
+ lines = decl_to_lines(to_decl)
64
+ @body.replace(lines)
65
+ end
66
+
67
+ def sort!
68
+ require 'rbs/sorter'
69
+ sorter = ::RBS::Sorter.new(nil, stdout: nil)
70
+ decl = to_decl
71
+ sorter.sort_decl!(decl)
72
+ lines = decl_to_lines(decl)
73
+ @body.replace(lines)
69
74
  end
70
75
 
71
76
  def original_rbs
@@ -93,6 +98,15 @@ module Orthoses
93
98
  end
94
99
  end
95
100
 
101
+ def decl_to_lines(decl)
102
+ out = ArrayIO.new
103
+ writer = RBS::Writer.new(out: out)
104
+ decl.members.each do |member|
105
+ writer.write_member(member)
106
+ end
107
+ out.to_a
108
+ end
109
+
96
110
  def uniqed_body_string
97
111
  out = StringIO.new
98
112
  writer = RBS::Writer.new(out: out)
@@ -105,7 +119,7 @@ module Orthoses
105
119
  name: "orthoses/content.rb",
106
120
  content: original_rbs
107
121
  )
108
- parsed_decls = RBS::Parser.parse_signature(buffer)
122
+ _, _, parsed_decls = RBS::Parser.parse_signature(buffer)
109
123
  unless parsed_decls.length == 1
110
124
  raise "expect decls.length == 1, but got #{parsed_decls.length}"
111
125
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orthoses
4
+ # use Orthoses::Sort
5
+ class Sort
6
+ def initialize(loader)
7
+ @loader = loader
8
+ end
9
+
10
+ def call
11
+ @loader.call.tap do |store|
12
+ store.each do |name, content|
13
+ content.sort!
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orthoses
4
+ class Trace
5
+ class Attribute
6
+ include Targetable
7
+
8
+ def initialize(loader, patterns:)
9
+ @loader = loader
10
+ @patterns = patterns
11
+
12
+ @captured_dict = Hash.new { |h, k| h[k] = Hash.new { |hh, kk| hh[kk] = [] } }
13
+ end
14
+
15
+ def call
16
+ ::Module.prepend(Orthoses::Attribute::Hook)
17
+ store = nil
18
+ build_trace_hook.enable(target: Orthoses::Attribute::Hook.instance_method(:attr)) do
19
+ build_trace_hook.enable(target: Orthoses::Attribute::Hook.instance_method(:attr_accessor)) do
20
+ build_trace_hook.enable(target: Orthoses::Attribute::Hook.instance_method(:attr_reader)) do
21
+ build_trace_hook.enable(target: Orthoses::Attribute::Hook.instance_method(:attr_writer)) do
22
+ store = @loader.call
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ @captured_dict.each do |mod_name, captures|
29
+ captures.each do |(kind, prefix, name), types|
30
+ injected = Utils::TypeList.new(types).inject
31
+ store[mod_name] << "#{kind} #{prefix}#{name}: #{injected}"
32
+ end
33
+ end
34
+
35
+ store
36
+ end
37
+
38
+ private
39
+
40
+ def build_trace_hook
41
+ TracePoint.new(:call) do |tp1|
42
+ mod_name = Orthoses::Utils.module_name(tp1.self)
43
+ if m = tp1.self.to_s.match(/#<Class:([\w:]+)>/)
44
+ mod_name = m[1]
45
+ end
46
+ next unless mod_name
47
+ next unless target?(mod_name)
48
+ is_singleton = tp1.self.singleton_class?
49
+ prefix = is_singleton ? "self." : ""
50
+ kind = tp1.method_id
51
+
52
+ tp1.binding.local_variable_get(:names).each do |name|
53
+ attr_module = Module.new{
54
+ if [:attr_accessor, :attr_reader, :attr].include?(kind)
55
+ define_method(name) do
56
+ super()
57
+ end
58
+ end
59
+ if [:attr_accessor, :attr_writer, :attr].include?(kind)
60
+ define_method("#{name}=") do |object|
61
+ super(object)
62
+ end
63
+ end
64
+ }
65
+
66
+ tp1.self.prepend attr_module
67
+
68
+ if [:attr_accessor, :attr_reader, :attr].include?(kind)
69
+ TracePoint.new(:return) do |tp2|
70
+ type = Utils.object_to_rbs(tp2.return_value)
71
+ @captured_dict[mod_name][[kind, prefix, name]] << type
72
+ end.enable(target: attr_module.instance_method(name))
73
+ end
74
+ if [:attr_accessor, :attr_writer, :attr].include?(kind)
75
+ TracePoint.new(:call) do |tp2|
76
+ type = Utils.object_to_rbs(tp2.binding.local_variable_get(:object))
77
+ @captured_dict[mod_name][[kind, prefix, name]] << type
78
+ end.enable(target: attr_module.instance_method("#{name}="))
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orthoses
4
+ class Trace
5
+ class Method
6
+ # internal
7
+ Info = Struct.new(:key, :op_name_types, :raised, keyword_init: true)
8
+ include Targetable
9
+
10
+ def initialize(loader, patterns:)
11
+ @loader = loader
12
+ @patterns = patterns
13
+
14
+ @stack = []
15
+ @args_return_map = Hash.new { |h, k| h[k] = [] }
16
+ @alias_map = {}
17
+ end
18
+
19
+ def call
20
+ build_trace_point.enable do
21
+ @loader.call
22
+ end.tap do |store|
23
+ build_members.each do |(mod_name, member)|
24
+ out = StringIO.new
25
+ writer = ::RBS::Writer.new(out: out)
26
+ writer.write_member(member)
27
+ store[mod_name] << out.string
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def build_trace_point
35
+ TracePoint.new(:call, :return, :raise) do |tp|
36
+ case tp.event
37
+ when :call
38
+ if tp.defined_class.singleton_class?
39
+ # e.g. `Minitest::Spec::DSL#to_s`` may return `nil` with `#to_s`
40
+ m = tp.defined_class.to_s&.match(/#<Class:([\w:]+)>/) or next
41
+ mod_name = m[1] or next
42
+ kind = :singleton
43
+ else
44
+ mod_name = Utils.module_name(tp.defined_class) or next
45
+ kind = :instance
46
+ end
47
+
48
+ next unless target?(mod_name)
49
+
50
+ visibility = tp.self.private_methods.include?(tp.method_id) ? :private : nil
51
+ key = [mod_name, kind, visibility, tp.method_id]
52
+ op_name_types = tp.parameters.map do |op, name|
53
+ case name
54
+ when nil, :*, :**, :&
55
+ [op, name, 'untyped']
56
+ else
57
+ type = Utils.object_to_rbs(tp.binding.local_variable_get(name))
58
+ [op, name, type]
59
+ end
60
+ end
61
+
62
+ @alias_map[[mod_name, kind, tp.method_id]] = tp.callee_id if tp.method_id != tp.callee_id
63
+ @stack.push(Info.new(key: key, op_name_types: op_name_types, raised: false))
64
+ when :raise
65
+ @stack.last&.raised = true
66
+ when :return
67
+ info = @stack.pop
68
+ if info && !info.raised
69
+ @args_return_map[info.key] << [info.op_name_types, Utils.object_to_rbs(tp.return_value)]
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def build_members
76
+ build_method_definitions.concat(build_aliases)
77
+ end
78
+
79
+ def build_method_definitions
80
+ untyped = ::RBS::Types::Bases::Any.new(location: nil)
81
+
82
+ @args_return_map.map do |(mod_name, kind, visibility, method_id), type_samples|
83
+ type_samples.uniq!
84
+ method_types = type_samples.map do |(op_name_types, return_type)|
85
+ required_positionals = []
86
+ optional_positionals = []
87
+ rest = nil
88
+ trailing_positionals = []
89
+ required_keywords = {}
90
+ optional_keywords = {}
91
+ rest_keywords = nil
92
+
93
+ requireds = required_positionals
94
+
95
+ block = nil
96
+
97
+ op_name_types.each do |op, name, string_type|
98
+ type = ::RBS::Parser.parse_type(string_type)
99
+
100
+ case op
101
+ when :req
102
+ requireds << ::RBS::Types::Function::Param.new(name: name, type: type)
103
+ when :opt
104
+ requireds = trailing_positionals
105
+ optional_positionals << ::RBS::Types::Function::Param.new(name: name, type: type)
106
+ when :rest
107
+ requireds = trailing_positionals
108
+ name = nil if name == :* # For `def f(...) end` syntax
109
+ rest = ::RBS::Types::Function::Param.new(name: name, type: type)
110
+ when :keyreq
111
+ required_keywords[name] = ::RBS::Types::Function::Param.new(name: nil, type: type)
112
+ when :key
113
+ optional_keywords[name] = ::RBS::Types::Function::Param.new(name: nil, type: type)
114
+ when :keyrest
115
+ rest_keywords = ::RBS::Types::Function::Param.new(name: nil, type: type)
116
+ when :block
117
+ block = ::RBS::Types::Block.new(
118
+ type: ::RBS::Types::Function.empty(untyped).update(rest_positionals: ::RBS::Types::Function::Param.new(name: nil, type: untyped)),
119
+ required: false,
120
+ self_type: nil
121
+ )
122
+ end
123
+ end
124
+
125
+ return_type = if method_id == :initialize
126
+ ::RBS::Types::Bases::Void.new(location: nil)
127
+ else
128
+ ::RBS::Parser.parse_type(return_type)
129
+ end
130
+
131
+ ::RBS::MethodType.new(
132
+ location: nil,
133
+ type_params: [],
134
+ type: ::RBS::Types::Function.new(
135
+ required_positionals: required_positionals,
136
+ optional_positionals: optional_positionals,
137
+ rest_positionals: rest,
138
+ trailing_positionals: trailing_positionals,
139
+ required_keywords: required_keywords,
140
+ optional_keywords: optional_keywords,
141
+ rest_keywords: rest_keywords,
142
+ return_type: return_type,
143
+ ),
144
+ block: block
145
+ )
146
+ end
147
+
148
+ # from:
149
+ # def foo: () -> true
150
+ # | () -> false
151
+ # | (Integer) -> Integer
152
+ # to:
153
+ # def foo: () -> bool
154
+ # | (Integer) -> Integer
155
+ functions_without_return_type_map = method_types.group_by do |method_type|
156
+ method_type.type.with_return_type(untyped)
157
+ end
158
+ types = functions_without_return_type_map.map do |func, method_types_by_arg|
159
+ injected = Utils::TypeList.new(method_types_by_arg.map(&:type).map(&:return_type)).inject
160
+ method_type = method_types_by_arg.first
161
+ method_type.update(type: method_type.type.with_return_type(injected))
162
+ end
163
+
164
+ [
165
+ mod_name,
166
+ RBS::AST::Members::MethodDefinition.new(
167
+ annotations: [],
168
+ comment: nil,
169
+ kind: kind,
170
+ location: nil,
171
+ name: method_id,
172
+ overloading: false,
173
+ overloads: types.map { |type|
174
+ RBS::AST::Members::MethodDefinition::Overload.new(
175
+ annotations: [],
176
+ method_type: type
177
+ )
178
+ },
179
+ visibility: visibility
180
+ )
181
+ ]
182
+ end
183
+ end
184
+
185
+ def build_aliases
186
+ @alias_map.map do |(mod_name, kind, method_id), callee_id|
187
+ [
188
+ mod_name,
189
+ RBS::AST::Members::Alias.new(
190
+ new_name: callee_id,
191
+ old_name: method_id,
192
+ kind: kind,
193
+ annotations: [],
194
+ location: nil,
195
+ comment: nil,
196
+ )
197
+ ]
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orthoses
4
+ class Trace
5
+ module Targetable
6
+ def target?(name)
7
+ @patterns.any? do |pattern|
8
+ if pattern.end_with?("*")
9
+ (name || "").start_with?(pattern.chop)
10
+ else
11
+ name == pattern
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ module Orthoses
2
+ # Collect argument and return types during code running
3
+ # use Orthoses::Trace,
4
+ # patterns: ['Foo::Bar*']
5
+ class Trace
6
+ autoload :Attribute, 'orthoses/trace/attribute'
7
+ autoload :Method, 'orthoses/trace/method'
8
+ autoload :Targetable, 'orthoses/trace/targetable'
9
+
10
+ def initialize(loader, patterns:)
11
+ @loader = loader
12
+ @patterns = patterns
13
+ end
14
+
15
+ def call
16
+ @loader = Trace::Attribute.new(@loader, patterns: @patterns)
17
+ @loader = Trace::Method.new(@loader, patterns: @patterns)
18
+ @loader.call
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orthoses
4
+ module Utils
5
+ # internal
6
+ class TypeList
7
+ UNTYPED = ::RBS::Types::Bases::Any.new(location: nil)
8
+ NIL_TYPE = ::RBS::Types::Bases::Nil.new(location: nil)
9
+ TRUE_TYPE = ::RBS::Types::Literal.new(literal: true, location: nil)
10
+ FALSE_TYPE = ::RBS::Types::Literal.new(literal: false, location: nil)
11
+
12
+ def initialize(types)
13
+ @types = types.map do |type|
14
+ case type
15
+ when String
16
+ ::RBS::Parser.parse_type(type)
17
+ else
18
+ type
19
+ end
20
+ end
21
+ end
22
+
23
+ def inject
24
+ uniqed = @types.uniq
25
+
26
+ return UNTYPED if uniqed.find { |t| t == UNTYPED }
27
+
28
+ has_true = uniqed.delete(TRUE_TYPE)
29
+ has_false = uniqed.delete(FALSE_TYPE)
30
+ if has_true || has_false
31
+ uniqed << ::RBS::Types::Bases::Bool.new(location: nil)
32
+ end
33
+
34
+ return UNTYPED if uniqed.empty?
35
+ return uniqed.first if uniqed.length == 1
36
+
37
+ args_or_other = uniqed.group_by do |type|
38
+ type.respond_to?(:args) && !type.args.empty?
39
+ end
40
+ if args_or_other[true]
41
+ # { Array => [Array[Intger], Array[String]], Hash => [Hash[untyped, untyped]], ... }
42
+ type_by_name = args_or_other[true].group_by do |type|
43
+ type.name
44
+ end
45
+ type_by_name.each do |name, types|
46
+ if with_untyped_type = types.find { |type| type.args.include?(UNTYPED) }
47
+ types.replace([with_untyped_type])
48
+ end
49
+ end
50
+ args_injected = type_by_name.values.flatten
51
+ args_injected.concat(args_or_other[false]) if args_or_other[false]
52
+ uniqed = args_injected
53
+ end
54
+
55
+ after_optional = uniqed.delete(NIL_TYPE)
56
+ injected = uniqed.inject do |r, type|
57
+ case r
58
+ when ::RBS::Types::Union
59
+ r.tap { r.types << type }
60
+ else
61
+ ::RBS::Types::Union.new(types: [r, type], location: nil)
62
+ end
63
+ end
64
+
65
+ if after_optional
66
+ return ::RBS::Types::Optional.new(type: injected, location: nil)
67
+ end
68
+
69
+ injected
70
+ end
71
+ end
72
+ end
73
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Orthoses
4
4
  module Utils
5
+ autoload :TypeList, "orthoses/utils/type_list"
5
6
  autoload :Underscore, "orthoses/utils/underscore"
6
7
 
7
8
  def self.unautoload!
@@ -49,28 +50,6 @@ module Orthoses
49
50
  RBS::Namespace.parse(name).to_type_name
50
51
  end
51
52
 
52
- class << self
53
- # Generated scripts are not always located in the root.
54
- # By default, it follows the upper level directory from the current directory
55
- # to find the rbs_collection.yaml file and set it to `rbs_collection_pathname`.
56
- # It can be reconfigured if necessary.
57
- attr_accessor :rbs_collection_pathname
58
- end
59
- # set default
60
- self.rbs_collection_pathname = ->() {
61
- begin
62
- start = here = Dir.pwd
63
- until ok = RBS::Collection::Config::PATH.exist?
64
- Dir.chdir("..")
65
- return nil if Dir.pwd == here
66
- here = Dir.pwd
67
- end
68
- Pathname(here) + RBS::Collection::Config::PATH
69
- ensure
70
- Dir.chdir(start)
71
- end
72
- }.call
73
-
74
53
  def self.rbs_environment(library: nil, collection: true, cache: true)
75
54
  @env_cache ||= {}
76
55
  if cache && hit = @env_cache[[library, collection]]
@@ -79,9 +58,15 @@ module Orthoses
79
58
 
80
59
  loader = RBS::EnvironmentLoader.new
81
60
 
82
- if collection && rbs_collection_pathname
83
- config = RBS::Collection::Config.lockfile_of(rbs_collection_pathname) or raise
84
- loader.add_collection(config)
61
+ if collection
62
+ config_path = RBS::Collection::Config.find_config_path || RBS::Collection::Config::PATH || raise("needs rbs_collection.yaml")
63
+ lock_path = RBS::Collection::Config.to_lockfile_path(config_path)
64
+ raise "needs rbs_collection.yaml file" unless lock_path.file?
65
+ lock = RBS::Collection::Config::Lockfile.from_lockfile(
66
+ lockfile_path: lock_path,
67
+ data: YAML.load_file(lock_path.to_s)
68
+ )
69
+ loader.add_collection(lock)
85
70
  end
86
71
 
87
72
  case library
@@ -101,7 +86,7 @@ module Orthoses
101
86
  end
102
87
  end
103
88
 
104
- def self.object_to_rbs(object, strict:)
89
+ def self.object_to_rbs(object, strict: false)
105
90
  case object
106
91
  when Class, Module
107
92
  if name = module_name(object)
@@ -115,8 +100,14 @@ module Orthoses
115
100
  else
116
101
  module_name(object.class) || 'untyped'
117
102
  end
118
- when true, false, nil
119
- object.inspect
103
+ when true, false
104
+ if strict
105
+ object.inspect
106
+ else
107
+ "bool"
108
+ end
109
+ when nil
110
+ "nil"
120
111
  when Set
121
112
  if object.empty?
122
113
  "Set[untyped]"
@@ -136,7 +127,7 @@ module Orthoses
136
127
  if strict
137
128
  "[#{ary.join(', ')}]"
138
129
  else
139
- "Array[#{ary.uniq.join(' | ')}]"
130
+ "Array[#{TypeList.new(ary).inject}]"
140
131
  end
141
132
  end
142
133
  when Hash
@@ -146,9 +137,9 @@ module Orthoses
146
137
  if strict && object.keys.all? { |key| key.is_a?(Symbol) && /\A\w+\z/.match?(key) }
147
138
  "{ #{object.map { |k, v| "#{k}: #{object_to_rbs(v, strict: strict)}" }.join(', ')} }"
148
139
  else
149
- keys = object.keys.map { |k| object_to_rbs(k, strict: strict) }.uniq
150
- values = object.values.map { |v| object_to_rbs(v, strict: strict) }.uniq
151
- "Hash[#{keys.join(' | ')}, #{values.join(' | ')}]"
140
+ keys = object.keys.map { |k| object_to_rbs(k, strict: strict) }
141
+ values = object.values.map { |v| object_to_rbs(v, strict: strict) }
142
+ "Hash[#{TypeList.new(keys).inject}, #{TypeList.new(values).inject}]"
152
143
  end
153
144
  end
154
145
  when Range
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Orthoses
4
- VERSION = "1.4.0"
4
+ VERSION = "1.6.0"
5
5
  end
data/lib/orthoses.rb CHANGED
@@ -3,6 +3,8 @@
3
3
  require 'rbs'
4
4
  require 'pathname'
5
5
 
6
+ require 'orthoses/utils'
7
+
6
8
  module Orthoses
7
9
  autoload :Attribute, 'orthoses/attribute'
8
10
  autoload :Builder, 'orthoses/builder'
@@ -21,10 +23,11 @@ module Orthoses
21
23
  autoload :PP, 'orthoses/pp'
22
24
  autoload :RBSPrototypeRB, 'orthoses/rbs_prototype_rb'
23
25
  autoload :RBSPrototypeRuntime, 'orthoses/rbs_prototype_runtime'
26
+ autoload :Sort, 'orthoses/sort'
24
27
  autoload :Store, 'orthoses/store'
25
28
  autoload :Tap, 'orthoses/tap'
29
+ autoload :Trace, 'orthoses/trace'
26
30
  autoload :Autoload, 'orthoses/autoload'
27
- autoload :Utils, 'orthoses/utils'
28
31
  autoload :VERSION, 'orthoses/version'
29
32
  autoload :Walk, 'orthoses/walk'
30
33
  autoload :Writer, 'orthoses/writer'
data/orthoses.gemspec CHANGED
@@ -32,5 +32,5 @@ Gem::Specification.new do |spec|
32
32
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
33
33
  spec.require_paths = ["lib"]
34
34
 
35
- spec.add_dependency "rbs", "~> 2.0"
35
+ spec.add_dependency "rbs", "~> 3.0"
36
36
  end
@@ -2,5 +2,6 @@
2
2
 
3
3
  class Orthoses::CallTracer::Lazy
4
4
  def initialize: () -> void
5
- def trace: (untyped name) ?{ () -> untyped } -> untyped
5
+ def trace: (String name) ?{ () -> untyped } -> untyped
6
+ attr_reader captures: Array[untyped]
6
7
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  class Orthoses::CallTracer
4
4
  def initialize: () -> void
5
- def trace: [T] (Method | UnboundMethod) ?{ () -> T } -> T
5
+ def trace: (Method | UnboundMethod) ?{ () -> untyped } -> untyped
6
6
  attr_accessor captures: Array[Capture]
7
7
  end
@@ -10,4 +10,5 @@ class Orthoses::Content::HeaderBuilder
10
10
  private def build_context: (entry: untyped) -> untyped
11
11
  private def name_and_params: (untyped name, untyped params) -> ::String
12
12
  private def name_and_args: (untyped name, untyped args) -> (::String | nil)
13
+ include RBS::Environment::ContextUtil
13
14
  end
@@ -10,7 +10,9 @@ class Orthoses::Content
10
10
  def to_rbs: () -> String
11
11
  def to_decl: () -> untyped
12
12
  def uniq!: () -> untyped
13
+ def sort!: () -> untyped
13
14
  def original_rbs: () -> untyped
15
+ private def decl_to_lines: (untyped decl) -> untyped
14
16
  private def uniqed_body_string: () -> String
15
17
  private def uniqed_body_decl: () -> RBS::AST::Declarations::t
16
18
  private def auto_header: () -> (nil | untyped)
@@ -3,7 +3,7 @@
3
3
  class Orthoses::LazyTracePoint < ::TracePoint
4
4
  def method_added: (untyped id) -> untyped
5
5
  def singleton_method_added: (untyped id) -> untyped
6
- def initialize: (*untyped events) ?{ () -> untyped } -> void
6
+ def initialize: (*untyped events) { () -> untyped } -> void
7
7
  def enable: (?target: untyped?, ?target_line: untyped?, ?target_thread: untyped?) ?{ () -> untyped } -> untyped
8
8
  private def trace_instance_method: () ?{ () -> untyped } -> untyped
9
9
  private def trace_singleton_method: () ?{ () -> untyped } -> untyped
@@ -0,0 +1,6 @@
1
+ # THIS IS GENERATED CODE from `$ rake generate_self_sig`
2
+
3
+ class Orthoses::Sort
4
+ def initialize: (untyped loader) -> void
5
+ def call: () -> untyped
6
+ end
@@ -0,0 +1,8 @@
1
+ # THIS IS GENERATED CODE from `$ rake generate_self_sig`
2
+
3
+ class Orthoses::Trace::Attribute
4
+ def initialize: (untyped loader, patterns: untyped) -> void
5
+ def call: () -> untyped
6
+ private def build_trace_hook: () -> untyped
7
+ include Orthoses::Trace::Targetable
8
+ end
@@ -0,0 +1,4 @@
1
+ # THIS IS GENERATED CODE from `$ rake generate_self_sig`
2
+
3
+ class Orthoses::Trace::Method::Info < ::Struct[untyped]
4
+ end
@@ -0,0 +1,11 @@
1
+ # THIS IS GENERATED CODE from `$ rake generate_self_sig`
2
+
3
+ class Orthoses::Trace::Method
4
+ def initialize: (untyped loader, patterns: untyped) -> void
5
+ def call: () -> untyped
6
+ private def build_trace_point: () -> untyped
7
+ private def build_members: () -> untyped
8
+ private def build_method_definitions: () -> untyped
9
+ private def build_aliases: () -> untyped
10
+ include Orthoses::Trace::Targetable
11
+ end
@@ -0,0 +1,5 @@
1
+ # THIS IS GENERATED CODE from `$ rake generate_self_sig`
2
+
3
+ module Orthoses::Trace::Targetable
4
+ def target?: (untyped name) -> untyped
5
+ end
@@ -0,0 +1,6 @@
1
+ # THIS IS GENERATED CODE from `$ rake generate_self_sig`
2
+
3
+ class Orthoses::Trace
4
+ def initialize: (untyped loader, patterns: untyped) -> void
5
+ def call: () -> untyped
6
+ end
@@ -0,0 +1,10 @@
1
+ # THIS IS GENERATED CODE from `$ rake generate_self_sig`
2
+
3
+ class Orthoses::Utils::TypeList
4
+ def initialize: (untyped types) -> void
5
+ def inject: () -> untyped
6
+ FALSE_TYPE: RBS::Types::Literal
7
+ NIL_TYPE: RBS::Types::Bases::Nil
8
+ TRUE_TYPE: RBS::Types::Literal
9
+ UNTYPED: RBS::Types::Bases::Any
10
+ end
@@ -13,7 +13,7 @@ module Orthoses::Utils
13
13
 
14
14
  def self.rbs_environment: (?library: String | Array[String] | nil, ?collection: boolish, ?cache: boolish) -> RBS::Environment
15
15
 
16
- def self.object_to_rbs: (untyped object, strict: bool) -> String
16
+ def self.object_to_rbs: (untyped object, ?strict: bool) -> String
17
17
 
18
18
  def self.module_name: (Module mod) -> String?
19
19
 
@@ -24,6 +24,4 @@ module Orthoses::Utils
24
24
  def self.new_store: () -> Orthoses::store
25
25
 
26
26
  UNBOUND_NAME_METHOD: UnboundMethod
27
-
28
- attr_accessor self.rbs_collection_pathname: Pathname
29
27
  end
data/sig/orthoses.rbs CHANGED
@@ -1,7 +1,7 @@
1
1
  # THIS IS GENERATED CODE from `$ rake generate_self_sig`
2
2
 
3
3
  module Orthoses
4
- VERSION: "1.4.0"
4
+ VERSION: "1.6.0"
5
5
 
6
6
  attr_accessor self.logger: ::Logger
7
7
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: orthoses
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ksss
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-22 00:00:00.000000000 Z
11
+ date: 2023-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbs
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: '3.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.0'
26
+ version: '3.0'
27
27
  description: Build RBS by Rack base architecture
28
28
  email:
29
29
  - co000ri@gmail.com
@@ -37,6 +37,10 @@ files:
37
37
  - LICENSE.txt
38
38
  - README.md
39
39
  - examples/minitest/Rakefile
40
+ - examples/rack-test/Gemfile
41
+ - examples/rack-test/Gemfile.lock
42
+ - examples/rack-test/Rakefile
43
+ - examples/rack-test/generate.rb
40
44
  - examples/simple_middleware.rb
41
45
  - examples/simple_middleware.rbs
42
46
  - lib/orthoses.rb
@@ -66,9 +70,15 @@ files:
66
70
  - lib/orthoses/pp.rb
67
71
  - lib/orthoses/rbs_prototype_rb.rb
68
72
  - lib/orthoses/rbs_prototype_runtime.rb
73
+ - lib/orthoses/sort.rb
69
74
  - lib/orthoses/store.rb
70
75
  - lib/orthoses/tap.rb
76
+ - lib/orthoses/trace.rb
77
+ - lib/orthoses/trace/attribute.rb
78
+ - lib/orthoses/trace/method.rb
79
+ - lib/orthoses/trace/targetable.rb
71
80
  - lib/orthoses/utils.rb
81
+ - lib/orthoses/utils/type_list.rb
72
82
  - lib/orthoses/utils/underscore.rb
73
83
  - lib/orthoses/version.rb
74
84
  - lib/orthoses/walk.rb
@@ -112,9 +122,16 @@ files:
112
122
  - sig/orthoses/pp.rbs
113
123
  - sig/orthoses/rbs_prototype_rb.rbs
114
124
  - sig/orthoses/rbs_prototype_runtime.rbs
125
+ - sig/orthoses/sort.rbs
115
126
  - sig/orthoses/store.rbs
116
127
  - sig/orthoses/tap.rbs
128
+ - sig/orthoses/trace.rbs
129
+ - sig/orthoses/trace/attribute.rbs
130
+ - sig/orthoses/trace/method.rbs
131
+ - sig/orthoses/trace/method/info.rbs
132
+ - sig/orthoses/trace/targetable.rbs
117
133
  - sig/orthoses/utils.rbs
134
+ - sig/orthoses/utils/type_list.rbs
118
135
  - sig/orthoses/utils/underscore.rbs
119
136
  - sig/orthoses/walk.rbs
120
137
  - sig/orthoses/writer.rbs
@@ -139,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
156
  - !ruby/object:Gem::Version
140
157
  version: '0'
141
158
  requirements: []
142
- rubygems_version: 3.3.16
159
+ rubygems_version: 3.4.8
143
160
  signing_key:
144
161
  specification_version: 4
145
162
  summary: Framework for Generate RBS