tapioca 0.8.3 → 0.9.2

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -2
  3. data/README.md +188 -36
  4. data/lib/tapioca/cli.rb +130 -66
  5. data/lib/tapioca/commands/annotations.rb +167 -34
  6. data/lib/tapioca/commands/check_shims.rb +101 -0
  7. data/lib/tapioca/commands/{init.rb → configure.rb} +1 -1
  8. data/lib/tapioca/commands/dsl.rb +1 -1
  9. data/lib/tapioca/commands/gem.rb +15 -10
  10. data/lib/tapioca/commands.rb +2 -1
  11. data/lib/tapioca/dsl/compiler.rb +1 -13
  12. data/lib/tapioca/dsl/compilers/active_model_attributes.rb +1 -1
  13. data/lib/tapioca/dsl/compilers/active_record_relations.rb +17 -0
  14. data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +5 -4
  15. data/lib/tapioca/dsl/compilers/frozen_record.rb +2 -2
  16. data/lib/tapioca/dsl/compilers/protobuf.rb +6 -0
  17. data/lib/tapioca/dsl/compilers.rb +0 -4
  18. data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +21 -3
  19. data/lib/tapioca/dsl/pipeline.rb +0 -2
  20. data/lib/tapioca/dsl.rb +8 -0
  21. data/lib/tapioca/executor.rb +0 -3
  22. data/lib/tapioca/gem/events.rb +22 -3
  23. data/lib/tapioca/gem/listeners/base.rb +11 -0
  24. data/lib/tapioca/gem/listeners/dynamic_mixins.rb +5 -0
  25. data/lib/tapioca/gem/listeners/foreign_constants.rb +65 -0
  26. data/lib/tapioca/gem/listeners/methods.rb +7 -18
  27. data/lib/tapioca/gem/listeners/mixins.rb +31 -10
  28. data/lib/tapioca/gem/listeners/remove_empty_payload_scopes.rb +5 -0
  29. data/lib/tapioca/gem/listeners/sorbet_enums.rb +5 -0
  30. data/lib/tapioca/gem/listeners/sorbet_helpers.rb +5 -0
  31. data/lib/tapioca/gem/listeners/sorbet_props.rb +5 -0
  32. data/lib/tapioca/gem/listeners/sorbet_required_ancestors.rb +5 -0
  33. data/lib/tapioca/gem/listeners/sorbet_signatures.rb +6 -1
  34. data/lib/tapioca/gem/listeners/sorbet_type_variables.rb +5 -0
  35. data/lib/tapioca/gem/listeners/source_location.rb +67 -0
  36. data/lib/tapioca/gem/listeners/subconstants.rb +5 -0
  37. data/lib/tapioca/gem/listeners/yard_doc.rb +5 -0
  38. data/lib/tapioca/gem/listeners.rb +2 -0
  39. data/lib/tapioca/gem/pipeline.rb +64 -19
  40. data/lib/tapioca/gem.rb +6 -0
  41. data/lib/tapioca/gemfile.rb +7 -6
  42. data/lib/tapioca/helpers/cli_helper.rb +8 -2
  43. data/lib/tapioca/helpers/config_helper.rb +0 -2
  44. data/lib/tapioca/helpers/env_helper.rb +16 -0
  45. data/lib/tapioca/helpers/rbi_files_helper.rb +255 -0
  46. data/lib/tapioca/helpers/rbi_helper.rb +98 -94
  47. data/lib/tapioca/helpers/sorbet_helper.rb +2 -3
  48. data/lib/tapioca/helpers/test/content.rb +0 -2
  49. data/lib/tapioca/helpers/test/template.rb +0 -2
  50. data/lib/tapioca/internal.rb +36 -12
  51. data/lib/tapioca/rbi_ext/model.rb +2 -15
  52. data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +18 -16
  53. data/lib/tapioca/runtime/reflection.rb +26 -0
  54. data/lib/tapioca/runtime/trackers/constant_definition.rb +44 -16
  55. data/lib/tapioca/runtime/trackers/mixin.rb +49 -14
  56. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +1 -4
  57. data/lib/tapioca/sorbet_ext/name_patch.rb +15 -5
  58. data/lib/tapioca/sorbet_ext/proc_bind_patch.rb +40 -0
  59. data/lib/tapioca/static/requires_compiler.rb +0 -2
  60. data/lib/tapioca/static/symbol_loader.rb +26 -30
  61. data/lib/tapioca/static/symbol_table_parser.rb +0 -3
  62. data/lib/tapioca/version.rb +1 -1
  63. data/lib/tapioca.rb +3 -0
  64. metadata +24 -7
  65. data/lib/tapioca/dsl/helpers/param_helper.rb +0 -55
  66. data/lib/tapioca/helpers/shims_helper.rb +0 -115
  67. data/lib/tapioca/helpers/signatures_helper.rb +0 -17
  68. data/lib/tapioca/helpers/type_variable_helper.rb +0 -43
@@ -5,7 +5,7 @@ module Tapioca
5
5
  module Commands
6
6
  class Gem < Command
7
7
  include SorbetHelper
8
- include RBIHelper
8
+ include RBIFilesHelper
9
9
 
10
10
  sig do
11
11
  params(
@@ -16,7 +16,8 @@ module Tapioca
16
16
  typed_overrides: T::Hash[String, String],
17
17
  outpath: Pathname,
18
18
  file_header: T::Boolean,
19
- doc: T::Boolean,
19
+ include_doc: T::Boolean,
20
+ include_loc: T::Boolean,
20
21
  include_exported_rbis: T::Boolean,
21
22
  number_of_workers: T.nilable(Integer),
22
23
  auto_strictness: T::Boolean,
@@ -32,7 +33,8 @@ module Tapioca
32
33
  typed_overrides:,
33
34
  outpath:,
34
35
  file_header:,
35
- doc:,
36
+ include_doc:,
37
+ include_loc:,
36
38
  include_exported_rbis:,
37
39
  number_of_workers: nil,
38
40
  auto_strictness: true,
@@ -57,7 +59,8 @@ module Tapioca
57
59
  @bundle = T.let(nil, T.nilable(Gemfile))
58
60
  @existing_rbis = T.let(nil, T.nilable(T::Hash[String, String]))
59
61
  @expected_rbis = T.let(nil, T.nilable(T::Hash[String, String]))
60
- @doc = T.let(doc, T::Boolean)
62
+ @include_doc = T.let(include_doc, T::Boolean)
63
+ @include_loc = T.let(include_loc, T::Boolean)
61
64
  @include_exported_rbis = include_exported_rbis
62
65
  end
63
66
 
@@ -94,12 +97,12 @@ module Tapioca
94
97
  end
95
98
  end
96
99
 
97
- sig { params(should_verify: T::Boolean).void }
98
- def sync(should_verify: false)
100
+ sig { params(should_verify: T::Boolean, exclude: T::Array[String]).void }
101
+ def sync(should_verify: false, exclude: [])
99
102
  if should_verify
100
103
  say("Checking for out-of-date RBIs...")
101
104
  say("")
102
- perform_sync_verification
105
+ perform_sync_verification(exclude: exclude)
103
106
  return
104
107
  end
105
108
 
@@ -182,7 +185,7 @@ module Tapioca
182
185
  default_command(:gem, gem.name),
183
186
  reason: "types exported from the `#{gem.name}` gem",) if @file_header
184
187
 
185
- rbi.root = Tapioca::Gem::Pipeline.new(gem, include_doc: @doc).compile
188
+ rbi.root = Tapioca::Gem::Pipeline.new(gem, include_doc: @include_doc, include_loc: @include_loc).compile
186
189
 
187
190
  merge_with_exported_rbi(gem, rbi) if @include_exported_rbis
188
191
 
@@ -201,11 +204,13 @@ module Tapioca
201
204
  end
202
205
  end
203
206
 
204
- sig { void }
205
- def perform_sync_verification
207
+ sig { params(exclude: T::Array[String]).void }
208
+ def perform_sync_verification(exclude: [])
206
209
  diff = {}
207
210
 
208
211
  removed_rbis.each do |gem_name|
212
+ next if exclude.include?(gem_name)
213
+
209
214
  filename = existing_rbi(gem_name)
210
215
  diff[filename] = :removed
211
216
  end
@@ -5,8 +5,9 @@ module Tapioca
5
5
  module Commands
6
6
  autoload :Command, "tapioca/commands/command"
7
7
  autoload :Annotations, "tapioca/commands/annotations"
8
+ autoload :CheckShims, "tapioca/commands/check_shims"
8
9
  autoload :Dsl, "tapioca/commands/dsl"
9
- autoload :Init, "tapioca/commands/init"
10
+ autoload :Configure, "tapioca/commands/configure"
10
11
  autoload :Gem, "tapioca/commands/gem"
11
12
  autoload :Require, "tapioca/commands/require"
12
13
  autoload :Todo, "tapioca/commands/todo"
@@ -1,12 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "rbi"
5
- require "tapioca/rbi_ext/model"
6
- require "tapioca/rbi_formatter"
7
- require "tapioca/dsl/helpers/param_helper"
8
- require "tapioca/dsl/pipeline"
9
-
10
4
  module Tapioca
11
5
  module Dsl
12
6
  class Compiler
@@ -14,6 +8,7 @@ module Tapioca
14
8
  extend T::Helpers
15
9
  extend T::Generic
16
10
 
11
+ include RBIHelper
17
12
  include Runtime::Reflection
18
13
  extend Runtime::Reflection
19
14
 
@@ -119,8 +114,6 @@ module Tapioca
119
114
  )
120
115
  end
121
116
 
122
- include Helpers::ParamHelper
123
-
124
117
  sig { params(method_def: T.any(Method, UnboundMethod)).returns(T::Array[RBI::TypedParam]) }
125
118
  def compile_method_parameters_to_rbi(method_def)
126
119
  signature = signature_of(method_def)
@@ -175,11 +168,6 @@ module Tapioca
175
168
  "T.nilable(#{type})"
176
169
  end
177
170
  end
178
-
179
- sig { params(name: String).returns(T::Boolean) }
180
- def valid_parameter_name?(name)
181
- name.match?(/^[[[:alnum:]]_]+$/)
182
- end
183
171
  end
184
172
  end
185
173
  end
@@ -103,7 +103,7 @@ module Tapioca
103
103
  when ActiveModel::Type::Date
104
104
  "::Date"
105
105
  when ActiveModel::Type::DateTime, ActiveModel::Type::Time
106
- "::DateTime"
106
+ "::Time"
107
107
  when ActiveModel::Type::Decimal
108
108
  "::BigDecimal"
109
109
  when ActiveModel::Type::Float
@@ -543,6 +543,23 @@ module Tapioca
543
543
  ],
544
544
  return_type: constant_name
545
545
  )
546
+ when :find_sole_by
547
+ create_common_method(
548
+ "find_sole_by",
549
+ common_relation_methods_module,
550
+ parameters: [
551
+ create_param("arg", type: "T.untyped"),
552
+ create_rest_param("args", type: "T.untyped"),
553
+ ],
554
+ return_type: constant_name
555
+ )
556
+ when :sole
557
+ create_common_method(
558
+ "sole",
559
+ common_relation_methods_module,
560
+ parameters: [],
561
+ return_type: constant_name
562
+ )
546
563
  when :first, :last, :take
547
564
  create_common_method(
548
565
  method_name,
@@ -95,17 +95,18 @@ module Tapioca
95
95
  sig { override.void }
96
96
  def decorate
97
97
  stores = constant.typed_stores
98
- return if stores.values.flat_map(&:accessors).empty?
98
+ return if stores.values.all? { |store| store.accessors.empty? }
99
99
 
100
100
  root.create_path(constant) do |model|
101
101
  stores.values.each do |store_data|
102
- store_data.accessors.each do |accessor|
103
- field = store_data.fields[accessor]
102
+ store_data.accessors.each do |accessor, name|
103
+ field = store_data.fields.fetch(accessor)
104
104
  type = type_for(field.type_sym)
105
105
  type = as_nilable_type(type) if field.null
106
+ name ||= field.name # support < 1.5.0
106
107
 
107
108
  store_accessors_module = model.create_module("StoreAccessors")
108
- generate_methods(store_accessors_module, field.name.to_s, type)
109
+ generate_methods(store_accessors_module, name.to_s, type)
109
110
  model.create_include("StoreAccessors")
110
111
  end
111
112
  end
@@ -65,7 +65,7 @@ module Tapioca
65
65
  class FrozenRecord < Compiler
66
66
  extend T::Sig
67
67
 
68
- ConstantType = type_member { { fixed: T.class_of(::FrozenRecord::Base) } }
68
+ ConstantType = type_member { { fixed: T.all(T.class_of(::FrozenRecord::Base), Extensions::FrozenRecord) } }
69
69
 
70
70
  sig { override.void }
71
71
  def decorate
@@ -97,7 +97,7 @@ module Tapioca
97
97
 
98
98
  sig { params(record: RBI::Scope).void }
99
99
  def decorate_scopes(record)
100
- scopes = T.unsafe(constant).__tapioca_scope_names
100
+ scopes = constant.__tapioca_scope_names
101
101
  return if scopes.nil?
102
102
 
103
103
  module_name = "GeneratedRelationMethods"
@@ -146,6 +146,11 @@ module Tapioca
146
146
  end
147
147
  end
148
148
 
149
+ sig { params(descriptor: Google::Protobuf::FieldDescriptor).returns(T::Boolean) }
150
+ def nilable_descriptor?(descriptor)
151
+ descriptor.label == :optional && descriptor.type == :message
152
+ end
153
+
149
154
  sig { params(descriptor: Google::Protobuf::FieldDescriptor).returns(Field) }
150
155
  def field_of(descriptor)
151
156
  if descriptor.label == :repeated
@@ -185,6 +190,7 @@ module Tapioca
185
190
  end
186
191
  else
187
192
  type = type_of(descriptor)
193
+ type = as_nilable_type(type) if nilable_descriptor?(descriptor)
188
194
 
189
195
  Field.new(
190
196
  name: descriptor.name,
@@ -1,10 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "tapioca/rbi_ext/model"
5
- require "tapioca/dsl/helpers/param_helper"
6
- require "tapioca/dsl/pipeline"
7
-
8
4
  module Tapioca
9
5
  module Dsl
10
6
  module Compilers
@@ -35,15 +35,23 @@ module Tapioca
35
35
  when ActiveRecord::Type::Boolean
36
36
  "T::Boolean"
37
37
  when ActiveRecord::Type::DateTime, ActiveRecord::Type::Time
38
- "::DateTime"
38
+ "::Time"
39
39
  when ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter
40
40
  "::ActiveSupport::TimeWithZone"
41
+ when ActiveRecord::Enum::EnumType
42
+ "::String"
41
43
  else
42
44
  handle_unknown_type(column_type)
43
45
  end
44
46
 
45
47
  column = @constant.columns_hash[column_name]
46
- setter_type = getter_type
48
+ setter_type =
49
+ case column_type
50
+ when ActiveRecord::Enum::EnumType
51
+ enum_setter_type(column_type)
52
+ else
53
+ getter_type
54
+ end
47
55
 
48
56
  if column&.null
49
57
  return [as_nilable_type(getter_type), as_nilable_type(setter_type)]
@@ -75,7 +83,7 @@ module Tapioca
75
83
  end
76
84
  end
77
85
 
78
- sig { params(column_type: Object).returns(String) }
86
+ sig { params(column_type: BasicObject).returns(String) }
79
87
  def handle_unknown_type(column_type)
80
88
  return "T.untyped" unless ActiveModel::Type::Value === column_type
81
89
  return "T.untyped" if Runtime::GenericTypeRegistry.generic_type_instance?(column_type)
@@ -108,6 +116,16 @@ module Tapioca
108
116
 
109
117
  first_argument_type.to_s
110
118
  end
119
+
120
+ sig { params(column_type: ActiveRecord::Enum::EnumType).returns(String) }
121
+ def enum_setter_type(column_type)
122
+ case column_type.subtype
123
+ when ActiveRecord::Type::Integer
124
+ "T.any(::String, ::Symbol, ::Integer)"
125
+ else
126
+ "T.any(::String, ::Symbol)"
127
+ end
128
+ end
111
129
  end
112
130
  end
113
131
  end
@@ -1,8 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "tapioca/dsl/compilers"
5
-
6
4
  module Tapioca
7
5
  module Dsl
8
6
  class Pipeline
data/lib/tapioca/dsl.rb CHANGED
@@ -1,6 +1,14 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "rbi"
5
+
4
6
  require "tapioca"
5
7
  require "tapioca/runtime/reflection"
8
+ require "tapioca/helpers/sorbet_helper"
9
+ require "tapioca/helpers/rbi_helper"
10
+ require "tapioca/rbi_ext/model"
11
+ require "tapioca/rbi_formatter"
12
+ require "tapioca/dsl/compilers"
13
+ require "tapioca/dsl/pipeline"
6
14
  require "tapioca/dsl/compiler"
@@ -1,9 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "etc"
5
- require "parallel"
6
-
7
4
  module Tapioca
8
5
  class Executor
9
6
  extend T::Sig
@@ -1,8 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "pathname"
5
-
6
4
  module Tapioca
7
5
  module Gem
8
6
  class Event
@@ -42,6 +40,20 @@ module Tapioca
42
40
  end
43
41
  end
44
42
 
43
+ class ForeignConstantFound < ConstantFound
44
+ extend T::Sig
45
+
46
+ sig { override.returns(Module) }
47
+ def constant
48
+ T.cast(@constant, Module)
49
+ end
50
+
51
+ sig { params(symbol: String, constant: Module).void }
52
+ def initialize(symbol, constant)
53
+ super
54
+ end
55
+ end
56
+
45
57
  class NodeAdded < Event
46
58
  extend T::Helpers
47
59
  extend T::Sig
@@ -88,9 +100,14 @@ module Tapioca
88
100
  end
89
101
  end
90
102
 
103
+ class ForeignScopeNodeAdded < ScopeNodeAdded; end
104
+
91
105
  class MethodNodeAdded < NodeAdded
92
106
  extend T::Sig
93
107
 
108
+ sig { returns(UnboundMethod) }
109
+ attr_reader :method
110
+
94
111
  sig { returns(RBI::Method) }
95
112
  attr_reader :node
96
113
 
@@ -104,14 +121,16 @@ module Tapioca
104
121
  params(
105
122
  symbol: String,
106
123
  constant: Module,
124
+ method: UnboundMethod,
107
125
  node: RBI::Method,
108
126
  signature: T.untyped,
109
127
  parameters: T::Array[[Symbol, String]]
110
128
  ).void.checked(:never)
111
129
  end
112
- def initialize(symbol, constant, node, signature, parameters)
130
+ def initialize(symbol, constant, method, node, signature, parameters) # rubocop:disable Metrics/ParameterLists
113
131
  super(symbol, constant)
114
132
  @node = node
133
+ @method = method
115
134
  @signature = signature
116
135
  @parameters = parameters
117
136
  end
@@ -17,6 +17,8 @@ module Tapioca
17
17
 
18
18
  sig { params(event: NodeAdded).void }
19
19
  def dispatch(event)
20
+ return if ignore?(event)
21
+
20
22
  case event
21
23
  when ConstNodeAdded
22
24
  on_const(event)
@@ -42,6 +44,15 @@ module Tapioca
42
44
  sig { params(event: MethodNodeAdded).void }
43
45
  def on_method(event)
44
46
  end
47
+
48
+ sig { params(event: NodeAdded).returns(T::Boolean) }
49
+ def ignore?(event)
50
+ # Some listeners do not have to take any action on certain events. For example,
51
+ # almost every listener should skip ForeignScopeNodeAdded events in order not to generate
52
+ # unnecessary RBIs for foreign constants. This method should be overridden by listener
53
+ # subclasses to skip any events that aren't relevant to them.
54
+ false
55
+ end
45
56
  end
46
57
  end
47
58
  end
@@ -26,6 +26,11 @@ module Tapioca
26
26
  @pipeline.push_symbol(name) if name
27
27
  end
28
28
  end
29
+
30
+ sig { override.params(event: NodeAdded).returns(T::Boolean) }
31
+ def ignore?(event)
32
+ event.is_a?(Tapioca::Gem::ForeignScopeNodeAdded)
33
+ end
29
34
  end
30
35
  end
31
36
  end
@@ -0,0 +1,65 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Tapioca
5
+ module Gem
6
+ module Listeners
7
+ class ForeignConstants < Base
8
+ extend T::Sig
9
+
10
+ include Runtime::Reflection
11
+
12
+ private
13
+
14
+ sig { override.params(event: ScopeNodeAdded).void }
15
+ def on_scope(event)
16
+ mixin = event.constant
17
+ return if Class === mixin # Classes can't be mixed into other constants
18
+
19
+ # There are cases where we want to process constants not declared by the current
20
+ # gem, i.e. "foreign constant". These are constants defined in another gem to which
21
+ # this gem is applying a mix-in. This pattern is especially common for gems that add
22
+ # behavior to Ruby standard library classes by mixing in modules to them.
23
+ #
24
+ # The way we identify these "foreign constants" is by asking the mixin tracker which
25
+ # constants have mixed in the current module that we are handling. We add all the
26
+ # constants that we discover to the pipeline to be processed.
27
+ Runtime::Trackers::Mixin.constants_with_mixin(mixin).each_value do |location_info|
28
+ location_info.each do |constant, location|
29
+ next unless mixed_in_by_gem?(location)
30
+
31
+ name = @pipeline.name_of(constant)
32
+
33
+ # Calling Tapioca::Gem::Pipeline#name_of on a singleton class returns `nil`.
34
+ # To handle this case, use string parsing to get the name of the singleton class's
35
+ # base constant. Then, generate RBIs as if the base constant is extending the mixin,
36
+ # which is functionally equivalent to including or prepending to the singleton class.
37
+ if !name && constant.singleton_class?
38
+ name = constant_name_from_singleton_class(constant)
39
+ next unless name
40
+
41
+ constant = T.cast(constantize(name), Module)
42
+ end
43
+
44
+ @pipeline.push_foreign_constant(name, constant) if name
45
+ end
46
+ end
47
+ end
48
+
49
+ sig do
50
+ params(
51
+ location: String,
52
+ ).returns(T::Boolean)
53
+ end
54
+ def mixed_in_by_gem?(location)
55
+ @pipeline.gem.contains_path?(location)
56
+ end
57
+
58
+ sig { override.params(event: NodeAdded).returns(T::Boolean) }
59
+ def ignore?(event)
60
+ event.is_a?(Tapioca::Gem::ForeignScopeNodeAdded)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -7,13 +7,9 @@ module Tapioca
7
7
  class Methods < Base
8
8
  extend T::Sig
9
9
 
10
+ include RBIHelper
10
11
  include Runtime::Reflection
11
12
 
12
- SPECIAL_METHOD_NAMES = T.let([
13
- "!", "~", "+@", "**", "-@", "*", "/", "%", "+", "-", "<<", ">>", "&", "|", "^",
14
- "<", "<=", "=>", ">", ">=", "==", "===", "!=", "=~", "!~", "<=>", "[]", "[]=", "`",
15
- ], T::Array[String])
16
-
17
13
  private
18
14
 
19
15
  sig { override.params(event: ScopeNodeAdded).void }
@@ -138,7 +134,7 @@ module Tapioca
138
134
  end
139
135
  end
140
136
 
141
- @pipeline.push_method(symbol_name, constant, rbi_method, signature, sanitized_parameters)
137
+ @pipeline.push_method(symbol_name, constant, method, rbi_method, signature, sanitized_parameters)
142
138
  tree << rbi_method
143
139
  end
144
140
 
@@ -184,24 +180,17 @@ module Tapioca
184
180
  .include?(method_name.gsub(/=$/, "").to_sym)
185
181
  end
186
182
 
187
- sig { params(name: String).returns(T::Boolean) }
188
- def valid_method_name?(name)
189
- return true if SPECIAL_METHOD_NAMES.include?(name)
190
-
191
- !!name.match(/^[[:word:]]+[?!=]?$/)
192
- end
193
-
194
- sig { params(name: String).returns(T::Boolean) }
195
- def valid_parameter_name?(name)
196
- name.match?(/^[[[:alnum:]]_]+$/)
197
- end
198
-
199
183
  sig { params(constant: Module).returns(T.nilable(UnboundMethod)) }
200
184
  def initialize_method_for(constant)
201
185
  constant.instance_method(:initialize)
202
186
  rescue
203
187
  nil
204
188
  end
189
+
190
+ sig { override.params(event: NodeAdded).returns(T::Boolean) }
191
+ def ignore?(event)
192
+ event.is_a?(Tapioca::Gem::ForeignScopeNodeAdded)
193
+ end
205
194
  end
206
195
  end
207
196
  end
@@ -26,19 +26,20 @@ module Tapioca
26
26
  end
27
27
 
28
28
  node = event.node
29
- add_mixins(node, prepends.reverse, Runtime::Trackers::Mixin::Type::Prepend)
30
- add_mixins(node, includes.reverse, Runtime::Trackers::Mixin::Type::Include)
31
- add_mixins(node, extends.reverse, Runtime::Trackers::Mixin::Type::Extend)
29
+ add_mixins(node, constant, prepends.reverse, Runtime::Trackers::Mixin::Type::Prepend)
30
+ add_mixins(node, constant, includes.reverse, Runtime::Trackers::Mixin::Type::Include)
31
+ add_mixins(node, constant, extends.reverse, Runtime::Trackers::Mixin::Type::Extend)
32
32
  end
33
33
 
34
34
  sig do
35
35
  params(
36
36
  tree: RBI::Tree,
37
+ constant: Module,
37
38
  mods: T::Array[Module],
38
39
  mixin_type: Runtime::Trackers::Mixin::Type
39
40
  ).void
40
41
  end
41
- def add_mixins(tree, mods, mixin_type)
42
+ def add_mixins(tree, constant, mods, mixin_type)
42
43
  mods
43
44
  .select do |mod|
44
45
  name = @pipeline.name_of(mod)
@@ -46,6 +47,8 @@ module Tapioca
46
47
  name && !filtered_mixin?(name)
47
48
  end
48
49
  .map do |mod|
50
+ next unless mixed_in_by_gem?(constant, mod, mixin_type)
51
+
49
52
  name = @pipeline.name_of(mod)
50
53
  @pipeline.push_symbol(name) if name
51
54
 
@@ -62,6 +65,25 @@ module Tapioca
62
65
  end
63
66
  end
64
67
 
68
+ sig do
69
+ params(
70
+ constant: Module,
71
+ mixin: Module,
72
+ mixin_type: Runtime::Trackers::Mixin::Type
73
+ ).returns(T::Boolean)
74
+ end
75
+ def mixed_in_by_gem?(constant, mixin, mixin_type)
76
+ mixin_location =
77
+ T.cast(
78
+ Runtime::Trackers::Mixin.constants_with_mixin(mixin).dig(mixin_type, constant),
79
+ T.nilable(String)
80
+ )
81
+
82
+ return true if mixin_location.nil?
83
+
84
+ @pipeline.gem.contains_path?(mixin_location)
85
+ end
86
+
65
87
  sig { params(mixin_name: String).returns(T::Boolean) }
66
88
  def filtered_mixin?(mixin_name)
67
89
  # filter T:: namespace mixins that aren't T::Props
@@ -71,9 +93,8 @@ module Tapioca
71
93
 
72
94
  sig { params(constant: Module).returns(T::Array[Module]) }
73
95
  def interesting_ancestors_of(constant)
74
- inherited_ancestors_ids = Set.new(
75
- inherited_ancestors_of(constant).map { |mod| object_id_of(mod) }
76
- )
96
+ inherited_ancestors = Set.new.compare_by_identity.merge(inherited_ancestors_of(constant))
97
+
77
98
  # TODO: There is actually a bug here where this will drop modules that
78
99
  # may be included twice. For example:
79
100
  #
@@ -91,9 +112,9 @@ module Tapioca
91
112
  #
92
113
  # Instead, we should only drop the tail matches of the ancestors and
93
114
  # inherited ancestors, past the location of the constant itself.
94
- constant.ancestors.reject do |mod|
95
- inherited_ancestors_ids.include?(object_id_of(mod))
96
- end
115
+ ancestors = Set.new.compare_by_identity.merge(ancestors_of(constant))
116
+
117
+ (ancestors - inherited_ancestors).to_a
97
118
  end
98
119
  end
99
120
  end
@@ -15,6 +15,11 @@ module Tapioca
15
15
  def on_scope(event)
16
16
  event.node.detach if @pipeline.symbol_in_payload?(event.symbol) && event.node.empty?
17
17
  end
18
+
19
+ sig { override.params(event: NodeAdded).returns(T::Boolean) }
20
+ def ignore?(event)
21
+ event.is_a?(Tapioca::Gem::ForeignScopeNodeAdded)
22
+ end
18
23
  end
19
24
  end
20
25
  end
@@ -20,6 +20,11 @@ module Tapioca
20
20
 
21
21
  event.node << RBI::TEnumBlock.new(enums)
22
22
  end
23
+
24
+ sig { override.params(event: NodeAdded).returns(T::Boolean) }
25
+ def ignore?(event)
26
+ event.is_a?(Tapioca::Gem::ForeignScopeNodeAdded)
27
+ end
23
28
  end
24
29
  end
25
30
  end
@@ -23,6 +23,11 @@ module Tapioca
23
23
  node << RBI::Helper.new("final") if T::Private::Final.final_module?(constant)
24
24
  node << RBI::Helper.new("sealed") if T::Private::Sealed.sealed_module?(constant)
25
25
  end
26
+
27
+ sig { override.params(event: NodeAdded).returns(T::Boolean) }
28
+ def ignore?(event)
29
+ event.is_a?(Tapioca::Gem::ForeignScopeNodeAdded)
30
+ end
26
31
  end
27
32
  end
28
33
  end