tapioca 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/README.md +491 -73
- data/lib/tapioca/cli.rb +40 -3
- data/lib/tapioca/commands/annotations.rb +154 -0
- data/lib/tapioca/commands/dsl.rb +20 -1
- data/lib/tapioca/commands/gem.rb +17 -57
- data/lib/tapioca/commands/init.rb +1 -0
- data/lib/tapioca/commands/todo.rb +4 -2
- data/lib/tapioca/commands.rb +1 -0
- data/lib/tapioca/dsl/compiler.rb +2 -2
- data/lib/tapioca/dsl/compilers/aasm.rb +1 -1
- data/lib/tapioca/dsl/compilers/action_controller_helpers.rb +1 -1
- data/lib/tapioca/dsl/compilers/action_mailer.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_job.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_model_attributes.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_model_secure_password.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_record_associations.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_record_columns.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_record_enum.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_record_fixtures.rb +3 -1
- data/lib/tapioca/dsl/compilers/active_record_relations.rb +8 -8
- data/lib/tapioca/dsl/compilers/active_record_scope.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_resource.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_storage.rb +6 -2
- data/lib/tapioca/dsl/compilers/active_support_concern.rb +1 -1
- data/lib/tapioca/dsl/compilers/active_support_current_attributes.rb +1 -1
- data/lib/tapioca/dsl/compilers/config.rb +2 -2
- data/lib/tapioca/dsl/compilers/frozen_record.rb +1 -1
- data/lib/tapioca/dsl/compilers/identity_cache.rb +1 -1
- data/lib/tapioca/dsl/compilers/mixed_in_class_attributes.rb +1 -1
- data/lib/tapioca/dsl/compilers/protobuf.rb +27 -3
- data/lib/tapioca/dsl/compilers/rails_generators.rb +1 -1
- data/lib/tapioca/dsl/compilers/sidekiq_worker.rb +1 -1
- data/lib/tapioca/dsl/compilers/smart_properties.rb +1 -1
- data/lib/tapioca/dsl/compilers/state_machines.rb +1 -1
- data/lib/tapioca/dsl/compilers/url_helpers.rb +5 -2
- data/lib/tapioca/dsl/helpers/param_helper.rb +4 -1
- data/lib/tapioca/dsl/pipeline.rb +32 -1
- data/lib/tapioca/dsl.rb +6 -0
- data/lib/tapioca/executor.rb +4 -46
- data/lib/tapioca/gem/listeners/methods.rb +26 -1
- data/lib/tapioca/gem/listeners/sorbet_props.rb +1 -1
- data/lib/tapioca/gem/listeners/sorbet_required_ancestors.rb +1 -0
- data/lib/tapioca/gem/listeners/sorbet_signatures.rb +1 -1
- data/lib/tapioca/gem/pipeline.rb +5 -1
- data/lib/tapioca/gemfile.rb +50 -3
- data/lib/tapioca/helpers/config_helper.rb +13 -0
- data/lib/tapioca/helpers/rbi_helper.rb +114 -7
- data/lib/tapioca/helpers/shims_helper.rb +36 -8
- data/lib/tapioca/helpers/signatures_helper.rb +17 -0
- data/lib/tapioca/helpers/sorbet_helper.rb +5 -11
- data/lib/tapioca/helpers/test/content.rb +1 -0
- data/lib/tapioca/helpers/test/dsl_compiler.rb +1 -0
- data/lib/tapioca/helpers/test/template.rb +1 -0
- data/lib/tapioca/helpers/type_variable_helper.rb +43 -0
- data/lib/tapioca/internal.rb +4 -1
- data/lib/tapioca/rbi_ext/model.rb +14 -2
- data/lib/tapioca/repo_index.rb +41 -0
- data/lib/tapioca/runtime/generic_type_registry.rb +4 -2
- data/lib/tapioca/runtime/loader.rb +3 -0
- data/lib/tapioca/runtime/reflection.rb +17 -13
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +38 -21
- data/lib/tapioca/static/symbol_table_parser.rb +2 -0
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +5 -0
- metadata +26 -21
@@ -4,14 +4,121 @@
|
|
4
4
|
module Tapioca
|
5
5
|
module RBIHelper
|
6
6
|
extend T::Sig
|
7
|
+
extend T::Helpers
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
requires_ancestor { Thor::Shell }
|
10
|
+
requires_ancestor { SorbetHelper }
|
11
|
+
|
12
|
+
sig do
|
13
|
+
params(
|
14
|
+
command: String,
|
15
|
+
gem_dir: String,
|
16
|
+
dsl_dir: String,
|
17
|
+
auto_strictness: T::Boolean,
|
18
|
+
gems: T::Array[Gemfile::GemSpec],
|
19
|
+
compilers: T::Enumerable[Class]
|
20
|
+
).void
|
21
|
+
end
|
22
|
+
def validate_rbi_files(command:, gem_dir:, dsl_dir:, auto_strictness:, gems: [], compilers: [])
|
23
|
+
error_url_base = Spoom::Sorbet::Errors::DEFAULT_ERROR_URL_BASE
|
24
|
+
|
25
|
+
say("Checking generated RBI files... ")
|
26
|
+
res = sorbet(
|
27
|
+
"--no-config",
|
28
|
+
"--error-url-base=#{error_url_base}",
|
29
|
+
"--stop-after namer",
|
30
|
+
dsl_dir,
|
31
|
+
gem_dir
|
32
|
+
)
|
33
|
+
say(" Done", :green)
|
34
|
+
|
35
|
+
errors = Spoom::Sorbet::Errors::Parser.parse_string(res.err)
|
36
|
+
|
37
|
+
if errors.empty?
|
38
|
+
say(" No errors found\n\n", [:green, :bold])
|
39
|
+
return
|
40
|
+
end
|
41
|
+
|
42
|
+
parse_errors = errors.select { |error| error.code < 4000 }
|
43
|
+
|
44
|
+
if parse_errors.any?
|
45
|
+
say_error(<<~ERR, :red)
|
46
|
+
|
47
|
+
##### INTERNAL ERROR #####
|
48
|
+
|
49
|
+
There are parse errors in the generated RBI files.
|
50
|
+
|
51
|
+
This seems related to a bug in Tapioca.
|
52
|
+
Please open an issue at https://github.com/Shopify/tapioca/issues/new with the following information:
|
53
|
+
|
54
|
+
Tapioca v#{Tapioca::VERSION}
|
55
|
+
|
56
|
+
Command:
|
57
|
+
#{command}
|
58
|
+
|
59
|
+
ERR
|
60
|
+
|
61
|
+
say_error(<<~ERR, :red) if gems.any?
|
62
|
+
Gems:
|
63
|
+
#{gems.map { |gem| " #{gem.name} (#{gem.version})" }.join("\n")}
|
64
|
+
|
65
|
+
ERR
|
66
|
+
|
67
|
+
say_error(<<~ERR, :red) if compilers.any?
|
68
|
+
Compilers:
|
69
|
+
#{compilers.map { |compiler| " #{compiler.name}" }.join("\n")}
|
70
|
+
|
71
|
+
ERR
|
72
|
+
|
73
|
+
say_error(<<~ERR, :red)
|
74
|
+
Errors:
|
75
|
+
#{parse_errors.map { |error| " #{error}" }.join("\n")}
|
76
|
+
|
77
|
+
##########################
|
78
|
+
|
79
|
+
ERR
|
80
|
+
end
|
81
|
+
|
82
|
+
if auto_strictness
|
83
|
+
redef_errors = errors.select { |error| error.code == 4010 }
|
84
|
+
update_gem_rbis_strictnesses(redef_errors, gem_dir)
|
85
|
+
end
|
86
|
+
|
87
|
+
Kernel.exit(1) if parse_errors.any?
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
sig { params(errors: T::Array[Spoom::Sorbet::Errors::Error], gem_dir: String).void }
|
93
|
+
def update_gem_rbis_strictnesses(errors, gem_dir)
|
94
|
+
files = []
|
95
|
+
|
96
|
+
errors.each do |error|
|
97
|
+
# Collect the file with error
|
98
|
+
files << error.file
|
99
|
+
error.more.each do |line|
|
100
|
+
# Also collect the conflicting definition file paths
|
101
|
+
next unless line.include?("Previous definition")
|
102
|
+
|
103
|
+
files << line.split(":").first&.strip
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
files
|
108
|
+
.uniq
|
109
|
+
.sort
|
110
|
+
.select { |file| file.start_with?(gem_dir) }
|
111
|
+
.each do |file|
|
112
|
+
Spoom::Sorbet::Sigils.change_sigil_in_file(file, "false")
|
113
|
+
say("\n Changed strictness of #{file} to `typed: false` (conflicting with DSL files)", [:yellow, :bold])
|
114
|
+
end
|
115
|
+
|
116
|
+
say("\n")
|
117
|
+
end
|
118
|
+
|
119
|
+
sig { params(path: String).returns(String) }
|
120
|
+
def gem_name_from_rbi_path(path)
|
121
|
+
T.must(File.basename(path, ".rbi").split("@").first)
|
15
122
|
end
|
16
123
|
end
|
17
124
|
end
|
@@ -8,20 +8,25 @@ module Tapioca
|
|
8
8
|
|
9
9
|
requires_ancestor { Thor::Shell }
|
10
10
|
|
11
|
+
SORBET_PAYLOAD_URL = "https://github.com/sorbet/sorbet/tree/master/rbi"
|
12
|
+
|
13
|
+
sig { params(index: RBI::Index, dir: String).void }
|
14
|
+
def index_payload(index, dir)
|
15
|
+
return unless Dir.exist?(dir)
|
16
|
+
|
17
|
+
say("Loading Sorbet payload... ")
|
18
|
+
files = Dir.glob("#{dir}/**/*.rbi").sort
|
19
|
+
parse_and_index_files(index, files)
|
20
|
+
say(" Done", :green)
|
21
|
+
end
|
22
|
+
|
11
23
|
sig { params(index: RBI::Index, kind: String, dir: String).void }
|
12
24
|
def index_rbis(index, kind, dir)
|
13
25
|
return unless Dir.exist?(dir) && !Dir.empty?(dir)
|
14
26
|
|
15
27
|
say("Loading #{kind} RBIs from #{dir}... ")
|
16
28
|
files = Dir.glob("#{dir}/**/*.rbi").sort
|
17
|
-
|
18
|
-
trees = files.map do |file|
|
19
|
-
RBI::Parser.parse_file(file)
|
20
|
-
rescue RBI::ParseError => e
|
21
|
-
say_error("\nWarning: #{e} (#{e.location})", :yellow)
|
22
|
-
end.compact
|
23
|
-
|
24
|
-
index.visit_all(trees)
|
29
|
+
parse_and_index_files(index, files)
|
25
30
|
say(" Done", :green)
|
26
31
|
end
|
27
32
|
|
@@ -32,14 +37,37 @@ module Tapioca
|
|
32
37
|
index.keys.each do |key|
|
33
38
|
nodes = index[key]
|
34
39
|
next unless shims_have_duplicates?(nodes, shim_rbi_dir)
|
40
|
+
|
35
41
|
duplicates[key] = nodes
|
36
42
|
end
|
37
43
|
say(" Done", :green)
|
38
44
|
duplicates
|
39
45
|
end
|
40
46
|
|
47
|
+
sig { params(loc: RBI::Loc, path_prefix: T.nilable(String)).returns(String) }
|
48
|
+
def location_to_payload_url(loc, path_prefix:)
|
49
|
+
return loc.to_s unless path_prefix
|
50
|
+
|
51
|
+
url = loc.file || ""
|
52
|
+
return loc.to_s unless url.start_with?(path_prefix)
|
53
|
+
|
54
|
+
url = url.sub(path_prefix, SORBET_PAYLOAD_URL)
|
55
|
+
url = "#{url}#L#{loc.begin_line}"
|
56
|
+
url
|
57
|
+
end
|
58
|
+
|
41
59
|
private
|
42
60
|
|
61
|
+
sig { params(index: RBI::Index, files: T::Array[String]).void }
|
62
|
+
def parse_and_index_files(index, files)
|
63
|
+
trees = files.map do |file|
|
64
|
+
RBI::Parser.parse_file(file)
|
65
|
+
rescue RBI::ParseError => e
|
66
|
+
say_error("\nWarning: #{e} (#{e.location})", :yellow)
|
67
|
+
end.compact
|
68
|
+
index.visit_all(trees)
|
69
|
+
end
|
70
|
+
|
43
71
|
sig { params(nodes: T::Array[RBI::Node], shim_rbi_dir: String).returns(T::Boolean) }
|
44
72
|
def shims_have_duplicates?(nodes, shim_rbi_dir)
|
45
73
|
return false if nodes.size == 1
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Tapioca
|
5
|
+
module SignaturesHelper
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
sig { params(sig_string: String).returns(String) }
|
9
|
+
def sanitize_signature_types(sig_string)
|
10
|
+
sig_string
|
11
|
+
.gsub(".returns(<VOID>)", ".void")
|
12
|
+
.gsub("<VOID>", "void")
|
13
|
+
.gsub("<NOT-TYPED>", "T.untyped")
|
14
|
+
.gsub(".params()", "")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -21,20 +21,14 @@ module Tapioca
|
|
21
21
|
SORBET_EXE_PATH_ENV_VAR = "TAPIOCA_SORBET_EXE"
|
22
22
|
|
23
23
|
FEATURE_REQUIREMENTS = T.let({
|
24
|
-
|
25
|
-
|
24
|
+
to_ary_nil_support: ::Gem::Requirement.new(">= 0.5.9220"), # https://github.com/sorbet/sorbet/pull/4706
|
25
|
+
print_payload_sources: ::Gem::Requirement.new(">= 0.5.9818"), # https://github.com/sorbet/sorbet/pull/5504
|
26
|
+
type_variable_block_syntax: ::Gem::Requirement.new(">= 0.5.9892"), # https://github.com/sorbet/sorbet/pull/5639
|
26
27
|
}.freeze, T::Hash[Symbol, ::Gem::Requirement])
|
27
28
|
|
28
|
-
|
29
|
-
const :out, String
|
30
|
-
const :err, String
|
31
|
-
const :status, T::Boolean
|
32
|
-
end
|
33
|
-
|
34
|
-
sig { params(sorbet_args: String).returns(CmdResult) }
|
29
|
+
sig { params(sorbet_args: String).returns(Spoom::ExecResult) }
|
35
30
|
def sorbet(*sorbet_args)
|
36
|
-
|
37
|
-
CmdResult.new(out: out, err: err, status: status.success? || false)
|
31
|
+
Spoom::Sorbet.srb(sorbet_args.join(" "), sorbet_bin: sorbet_path, capture_err: true)
|
38
32
|
end
|
39
33
|
|
40
34
|
sig { returns(String) }
|
@@ -41,6 +41,7 @@ module Tapioca
|
|
41
41
|
def add_content_file(name, content)
|
42
42
|
file_name = tmp_path("lib/#{name}")
|
43
43
|
raise ArgumentError, "a file named '#{name}' was already added; cannot overwrite." if File.exist?(file_name)
|
44
|
+
|
44
45
|
FileUtils.mkdir_p(File.dirname(file_name))
|
45
46
|
File.write(file_name, content)
|
46
47
|
file_name
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Tapioca
|
5
|
+
module TypeVariableHelper
|
6
|
+
extend T::Sig
|
7
|
+
extend SorbetHelper
|
8
|
+
|
9
|
+
sig do
|
10
|
+
params(
|
11
|
+
type: String,
|
12
|
+
variance: Symbol,
|
13
|
+
fixed: T.nilable(String),
|
14
|
+
upper: T.nilable(String),
|
15
|
+
lower: T.nilable(String)
|
16
|
+
).returns(String)
|
17
|
+
end
|
18
|
+
def self.serialize_type_variable(type, variance, fixed, upper, lower)
|
19
|
+
variance = nil if variance == :invariant
|
20
|
+
|
21
|
+
bounds = []
|
22
|
+
bounds << "fixed: #{fixed}" if fixed
|
23
|
+
bounds << "lower: #{lower}" if lower
|
24
|
+
bounds << "upper: #{upper}" if upper
|
25
|
+
|
26
|
+
parameters = []
|
27
|
+
block = []
|
28
|
+
|
29
|
+
parameters << ":#{variance}" if variance
|
30
|
+
|
31
|
+
if sorbet_supports?(:type_variable_block_syntax)
|
32
|
+
block = bounds
|
33
|
+
else
|
34
|
+
parameters.concat(bounds)
|
35
|
+
end
|
36
|
+
|
37
|
+
serialized = type.dup
|
38
|
+
serialized << "(#{parameters.join(", ")})" unless parameters.empty?
|
39
|
+
serialized << " { { #{block.join(", ")} } }" unless block.empty?
|
40
|
+
serialized
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/tapioca/internal.rb
CHANGED
@@ -6,14 +6,17 @@ require "tapioca/runtime/reflection"
|
|
6
6
|
require "tapioca/runtime/trackers"
|
7
7
|
require "tapioca/runtime/dynamic_mixin_compiler"
|
8
8
|
require "tapioca/runtime/loader"
|
9
|
+
require "tapioca/helpers/sorbet_helper"
|
10
|
+
require "tapioca/helpers/type_variable_helper"
|
9
11
|
require "tapioca/sorbet_ext/generic_name_patch"
|
10
12
|
require "tapioca/sorbet_ext/fixed_hash_patch"
|
11
13
|
require "tapioca/runtime/generic_type_registry"
|
12
14
|
require "tapioca/helpers/cli_helper"
|
13
15
|
require "tapioca/helpers/config_helper"
|
16
|
+
require "tapioca/helpers/signatures_helper"
|
14
17
|
require "tapioca/helpers/rbi_helper"
|
15
18
|
require "tapioca/helpers/shims_helper"
|
16
|
-
require "tapioca/
|
19
|
+
require "tapioca/repo_index"
|
17
20
|
require "tapioca/commands"
|
18
21
|
require "tapioca/cli"
|
19
22
|
require "tapioca/gemfile"
|
@@ -61,8 +61,18 @@ module RBI
|
|
61
61
|
create_node(RBI::MixesInClassMethods.new(name))
|
62
62
|
end
|
63
63
|
|
64
|
-
sig
|
65
|
-
|
64
|
+
sig do
|
65
|
+
params(
|
66
|
+
name: String,
|
67
|
+
type: String,
|
68
|
+
variance: Symbol,
|
69
|
+
fixed: T.nilable(String),
|
70
|
+
upper: T.nilable(String),
|
71
|
+
lower: T.nilable(String)
|
72
|
+
).void
|
73
|
+
end
|
74
|
+
def create_type_variable(name, type:, variance: :invariant, fixed: nil, upper: nil, lower: nil)
|
75
|
+
value = Tapioca::TypeVariableHelper.serialize_type_variable(type, variance, fixed, upper, lower)
|
66
76
|
create_node(RBI::TypeMember.new(name, value))
|
67
77
|
end
|
68
78
|
|
@@ -98,6 +108,7 @@ module RBI
|
|
98
108
|
sig { params(name: String).returns(T::Boolean) }
|
99
109
|
def valid_method_name?(name)
|
100
110
|
return true if SPECIAL_METHOD_NAMES.include?(name)
|
111
|
+
|
101
112
|
!!name.match(/^[a-zA-Z_][[:word:]]*[?!=]?$/)
|
102
113
|
end
|
103
114
|
|
@@ -110,6 +121,7 @@ module RBI
|
|
110
121
|
def create_node(node)
|
111
122
|
cached = nodes_cache[node.to_s]
|
112
123
|
return cached if cached
|
124
|
+
|
113
125
|
nodes_cache[node.to_s] = node
|
114
126
|
self << node
|
115
127
|
node
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Tapioca
|
5
|
+
class RepoIndex
|
6
|
+
extend T::Sig
|
7
|
+
extend T::Generic
|
8
|
+
|
9
|
+
sig { params(json: String).returns(RepoIndex) }
|
10
|
+
def self.from_json(json)
|
11
|
+
RepoIndex.from_hash(JSON.parse(json))
|
12
|
+
end
|
13
|
+
|
14
|
+
sig { params(hash: T::Hash[String, T::Hash[T.untyped, T.untyped]]).returns(RepoIndex) }
|
15
|
+
def self.from_hash(hash)
|
16
|
+
hash.each_with_object(RepoIndex.new) do |(name, _), index|
|
17
|
+
index << name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
sig { void }
|
22
|
+
def initialize
|
23
|
+
@entries = T.let(Set.new, T::Set[String])
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { params(gem_name: String).void }
|
27
|
+
def <<(gem_name)
|
28
|
+
@entries.add(gem_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
sig { returns(T::Enumerable[String]) }
|
32
|
+
def gems
|
33
|
+
@entries.sort
|
34
|
+
end
|
35
|
+
|
36
|
+
sig { params(gem_name: String).returns(T::Boolean) }
|
37
|
+
def has_gem?(gem_name)
|
38
|
+
@entries.include?(gem_name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -111,8 +111,10 @@ module Tapioca
|
|
111
111
|
constant.clone
|
112
112
|
end
|
113
113
|
|
114
|
-
# Let's set the `name`
|
115
|
-
|
114
|
+
# Let's set the `name` and `to_s` methods to return the proper generic name
|
115
|
+
name_proc = -> { name }
|
116
|
+
generic_type.define_singleton_method(:name, name_proc)
|
117
|
+
generic_type.define_singleton_method(:to_s, name_proc)
|
116
118
|
|
117
119
|
# We need to define a `<=` method on the cloned constant, so that Sorbet
|
118
120
|
# can do covariance/contravariance checks on the type variables.
|
@@ -41,6 +41,7 @@ module Tapioca
|
|
41
41
|
sig { params(file: T.nilable(String)).void }
|
42
42
|
def require_helper(file)
|
43
43
|
return unless file
|
44
|
+
|
44
45
|
file = File.absolute_path(file)
|
45
46
|
return unless File.exist?(file)
|
46
47
|
|
@@ -51,6 +52,8 @@ module Tapioca
|
|
51
52
|
def rails_engines
|
52
53
|
return [] unless Object.const_defined?("Rails::Engine")
|
53
54
|
|
55
|
+
safe_require("active_support/core_ext/class/subclasses")
|
56
|
+
|
54
57
|
# We can use `Class#descendants` here, since we know Rails is loaded
|
55
58
|
Object.const_get("Rails::Engine").descendants.reject(&:abstract_railtie?)
|
56
59
|
end
|
@@ -35,58 +35,58 @@ module Tapioca
|
|
35
35
|
|
36
36
|
sig { params(object: BasicObject).returns(Class).checked(:never) }
|
37
37
|
def class_of(object)
|
38
|
-
CLASS_METHOD.
|
38
|
+
CLASS_METHOD.bind_call(object)
|
39
39
|
end
|
40
40
|
|
41
41
|
sig { params(constant: Module).returns(T::Array[Symbol]) }
|
42
42
|
def constants_of(constant)
|
43
|
-
CONSTANTS_METHOD.
|
43
|
+
CONSTANTS_METHOD.bind_call(constant, false)
|
44
44
|
end
|
45
45
|
|
46
46
|
sig { params(constant: Module).returns(T.nilable(String)) }
|
47
47
|
def name_of(constant)
|
48
|
-
name = NAME_METHOD.
|
48
|
+
name = NAME_METHOD.bind_call(constant)
|
49
49
|
name&.start_with?("#<") ? nil : name
|
50
50
|
end
|
51
51
|
|
52
52
|
sig { params(constant: Module).returns(Class) }
|
53
53
|
def singleton_class_of(constant)
|
54
|
-
SINGLETON_CLASS_METHOD.
|
54
|
+
SINGLETON_CLASS_METHOD.bind_call(constant)
|
55
55
|
end
|
56
56
|
|
57
57
|
sig { params(constant: Module).returns(T::Array[Module]) }
|
58
58
|
def ancestors_of(constant)
|
59
|
-
ANCESTORS_METHOD.
|
59
|
+
ANCESTORS_METHOD.bind_call(constant)
|
60
60
|
end
|
61
61
|
|
62
62
|
sig { params(constant: Class).returns(T.nilable(Class)) }
|
63
63
|
def superclass_of(constant)
|
64
|
-
SUPERCLASS_METHOD.
|
64
|
+
SUPERCLASS_METHOD.bind_call(constant)
|
65
65
|
end
|
66
66
|
|
67
67
|
sig { params(object: BasicObject).returns(Integer).checked(:never) }
|
68
68
|
def object_id_of(object)
|
69
|
-
OBJECT_ID_METHOD.
|
69
|
+
OBJECT_ID_METHOD.bind_call(object)
|
70
70
|
end
|
71
71
|
|
72
72
|
sig { params(object: BasicObject, other: BasicObject).returns(T::Boolean).checked(:never) }
|
73
73
|
def are_equal?(object, other)
|
74
|
-
EQUAL_METHOD.
|
74
|
+
EQUAL_METHOD.bind_call(object, other)
|
75
75
|
end
|
76
76
|
|
77
77
|
sig { params(constant: Module).returns(T::Array[Symbol]) }
|
78
78
|
def public_instance_methods_of(constant)
|
79
|
-
PUBLIC_INSTANCE_METHODS_METHOD.
|
79
|
+
PUBLIC_INSTANCE_METHODS_METHOD.bind_call(constant)
|
80
80
|
end
|
81
81
|
|
82
82
|
sig { params(constant: Module).returns(T::Array[Symbol]) }
|
83
83
|
def protected_instance_methods_of(constant)
|
84
|
-
PROTECTED_INSTANCE_METHODS_METHOD.
|
84
|
+
PROTECTED_INSTANCE_METHODS_METHOD.bind_call(constant)
|
85
85
|
end
|
86
86
|
|
87
87
|
sig { params(constant: Module).returns(T::Array[Symbol]) }
|
88
88
|
def private_instance_methods_of(constant)
|
89
|
-
PRIVATE_INSTANCE_METHODS_METHOD.
|
89
|
+
PRIVATE_INSTANCE_METHODS_METHOD.bind_call(constant)
|
90
90
|
end
|
91
91
|
|
92
92
|
sig { params(constant: Module).returns(T::Array[Module]) }
|
@@ -124,7 +124,7 @@ module Tapioca
|
|
124
124
|
|
125
125
|
sig { params(constant: Module, method: Symbol).returns(Method) }
|
126
126
|
def method_of(constant, method)
|
127
|
-
METHOD_METHOD.
|
127
|
+
METHOD_METHOD.bind_call(constant, method)
|
128
128
|
end
|
129
129
|
|
130
130
|
# Returns an array with all classes that are < than the supplied class.
|
@@ -140,7 +140,11 @@ module Tapioca
|
|
140
140
|
#
|
141
141
|
# class D < C; end
|
142
142
|
# descendants_of(C) # => [B, A, D]
|
143
|
-
sig
|
143
|
+
sig do
|
144
|
+
type_parameters(:U)
|
145
|
+
.params(klass: T.all(Class, T.type_parameter(:U)))
|
146
|
+
.returns(T::Array[T.type_parameter(:U)])
|
147
|
+
end
|
144
148
|
def descendants_of(klass)
|
145
149
|
result = ObjectSpace.each_object(klass.singleton_class).reject do |k|
|
146
150
|
T.cast(k, Module).singleton_class? || T.unsafe(k) == klass
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "tapioca/sorbet_ext/name_patch"
|
5
|
+
require "tapioca/helpers/sorbet_helper"
|
5
6
|
|
6
7
|
module T
|
7
8
|
module Generic
|
@@ -20,31 +21,47 @@ module T
|
|
20
21
|
Tapioca::Runtime::GenericTypeRegistry.register_type(constant, types)
|
21
22
|
end
|
22
23
|
|
23
|
-
def type_member(variance = :invariant, fixed: nil, lower:
|
24
|
+
def type_member(variance = :invariant, fixed: nil, lower: nil, upper: nil, &blk)
|
24
25
|
# `T::Generic#type_member` just instantiates a `T::Type::TypeMember` instance and returns it.
|
25
26
|
# We use that when registering the type member and then later return it from this method.
|
27
|
+
hash = if blk
|
28
|
+
blk.call
|
29
|
+
else
|
30
|
+
{
|
31
|
+
fixed: fixed,
|
32
|
+
lower: lower,
|
33
|
+
upper: upper,
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
26
37
|
Tapioca::TypeVariableModule.new(
|
27
38
|
T.cast(self, Module),
|
28
39
|
Tapioca::TypeVariableModule::Type::Member,
|
29
40
|
variance,
|
30
|
-
|
31
|
-
lower,
|
32
|
-
upper
|
41
|
+
**hash,
|
33
42
|
).tap do |type_variable|
|
34
43
|
Tapioca::Runtime::GenericTypeRegistry.register_type_variable(self, type_variable)
|
35
44
|
end
|
36
45
|
end
|
37
46
|
|
38
|
-
def type_template(variance = :invariant, fixed: nil, lower:
|
47
|
+
def type_template(variance = :invariant, fixed: nil, lower: nil, upper: nil, &blk)
|
39
48
|
# `T::Generic#type_template` just instantiates a `T::Type::TypeTemplate` instance and returns it.
|
40
49
|
# We use that when registering the type template and then later return it from this method.
|
50
|
+
hash = if blk
|
51
|
+
blk.call
|
52
|
+
else
|
53
|
+
{
|
54
|
+
fixed: fixed,
|
55
|
+
lower: lower,
|
56
|
+
upper: upper,
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
41
60
|
Tapioca::TypeVariableModule.new(
|
42
61
|
T.cast(self, Module),
|
43
62
|
Tapioca::TypeVariableModule::Type::Template,
|
44
63
|
variance,
|
45
|
-
|
46
|
-
lower,
|
47
|
-
upper
|
64
|
+
**hash,
|
48
65
|
).tap do |type_variable|
|
49
66
|
Tapioca::Runtime::GenericTypeRegistry.register_type_variable(self, type_variable)
|
50
67
|
end
|
@@ -122,7 +139,7 @@ module Tapioca
|
|
122
139
|
sig do
|
123
140
|
params(context: Module, type: Type, variance: Symbol, fixed: T.untyped, lower: T.untyped, upper: T.untyped).void
|
124
141
|
end
|
125
|
-
def initialize(context, type, variance, fixed, lower, upper
|
142
|
+
def initialize(context, type, variance, fixed: nil, lower: nil, upper: nil)
|
126
143
|
@context = context
|
127
144
|
@type = type
|
128
145
|
@variance = variance
|
@@ -137,7 +154,7 @@ module Tapioca
|
|
137
154
|
constant_name = super
|
138
155
|
|
139
156
|
# This is a hack to work around modules under anonymous modules not having
|
140
|
-
# names in 2.
|
157
|
+
# names in 2.7: https://bugs.ruby-lang.org/issues/14895
|
141
158
|
#
|
142
159
|
# This happens when a type variable is declared under `class << self`, for
|
143
160
|
# example.
|
@@ -153,17 +170,17 @@ module Tapioca
|
|
153
170
|
|
154
171
|
sig { returns(String) }
|
155
172
|
def serialize
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
173
|
+
fixed = @fixed.to_s if @fixed
|
174
|
+
upper = @upper.to_s if @upper
|
175
|
+
lower = @lower.to_s if @lower
|
176
|
+
|
177
|
+
TypeVariableHelper.serialize_type_variable(
|
178
|
+
@type.serialize,
|
179
|
+
@variance,
|
180
|
+
fixed,
|
181
|
+
upper,
|
182
|
+
lower
|
183
|
+
)
|
167
184
|
end
|
168
185
|
|
169
186
|
sig { returns(Tapioca::TypeVariable) }
|
data/lib/tapioca/version.rb
CHANGED