tapioca 0.8.3 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
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