tapioca 0.7.0 → 0.8.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.
- 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