tapioca 0.18.0 → 0.19.1

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/lib/tapioca/cli.rb +1 -1
  4. data/lib/tapioca/commands/abstract_dsl.rb +1 -1
  5. data/lib/tapioca/commands/check_shims.rb +3 -0
  6. data/lib/tapioca/commands/configure.rb +1 -0
  7. data/lib/tapioca/commands/todo.rb +8 -2
  8. data/lib/tapioca/dsl/compiler.rb +18 -13
  9. data/lib/tapioca/dsl/compilers/aasm.rb +5 -2
  10. data/lib/tapioca/dsl/compilers/action_controller_helpers.rb +2 -2
  11. data/lib/tapioca/dsl/compilers/action_mailer.rb +1 -1
  12. data/lib/tapioca/dsl/compilers/action_text.rb +1 -1
  13. data/lib/tapioca/dsl/compilers/active_job.rb +1 -1
  14. data/lib/tapioca/dsl/compilers/active_model_attributes.rb +1 -1
  15. data/lib/tapioca/dsl/compilers/active_model_secure_password.rb +1 -1
  16. data/lib/tapioca/dsl/compilers/active_model_validations_confirmation.rb +1 -1
  17. data/lib/tapioca/dsl/compilers/active_record_associations.rb +1 -1
  18. data/lib/tapioca/dsl/compilers/active_record_columns.rb +1 -1
  19. data/lib/tapioca/dsl/compilers/active_record_delegated_types.rb +1 -1
  20. data/lib/tapioca/dsl/compilers/active_record_enum.rb +1 -1
  21. data/lib/tapioca/dsl/compilers/active_record_fixtures.rb +1 -1
  22. data/lib/tapioca/dsl/compilers/active_record_relations.rb +1 -1
  23. data/lib/tapioca/dsl/compilers/active_record_scope.rb +1 -1
  24. data/lib/tapioca/dsl/compilers/active_record_secure_token.rb +1 -1
  25. data/lib/tapioca/dsl/compilers/active_record_store.rb +1 -1
  26. data/lib/tapioca/dsl/compilers/active_record_typed_store.rb +1 -1
  27. data/lib/tapioca/dsl/compilers/active_resource.rb +1 -1
  28. data/lib/tapioca/dsl/compilers/active_storage.rb +2 -2
  29. data/lib/tapioca/dsl/compilers/active_support_concern.rb +6 -6
  30. data/lib/tapioca/dsl/compilers/active_support_current_attributes.rb +1 -1
  31. data/lib/tapioca/dsl/compilers/active_support_environment_inquirer.rb +1 -1
  32. data/lib/tapioca/dsl/compilers/active_support_time_ext.rb +1 -1
  33. data/lib/tapioca/dsl/compilers/config.rb +2 -2
  34. data/lib/tapioca/dsl/compilers/frozen_record.rb +1 -1
  35. data/lib/tapioca/dsl/compilers/graphql_input_object.rb +1 -1
  36. data/lib/tapioca/dsl/compilers/graphql_mutation.rb +1 -1
  37. data/lib/tapioca/dsl/compilers/identity_cache.rb +2 -2
  38. data/lib/tapioca/dsl/compilers/json_api_client_resource.rb +1 -1
  39. data/lib/tapioca/dsl/compilers/kredis.rb +1 -1
  40. data/lib/tapioca/dsl/compilers/mixed_in_class_attributes.rb +2 -2
  41. data/lib/tapioca/dsl/compilers/protobuf.rb +1 -1
  42. data/lib/tapioca/dsl/compilers/rails_generators.rb +1 -1
  43. data/lib/tapioca/dsl/compilers/sidekiq_worker.rb +1 -1
  44. data/lib/tapioca/dsl/compilers/smart_properties.rb +1 -1
  45. data/lib/tapioca/dsl/compilers/state_machines.rb +2 -2
  46. data/lib/tapioca/dsl/compilers/url_helpers.rb +7 -7
  47. data/lib/tapioca/dsl/helpers/graphql_type_helper.rb +1 -1
  48. data/lib/tapioca/dsl/pipeline.rb +10 -10
  49. data/lib/tapioca/gem/events.rb +7 -7
  50. data/lib/tapioca/gem/listeners/documentation.rb +94 -0
  51. data/lib/tapioca/gem/listeners/methods.rb +8 -8
  52. data/lib/tapioca/gem/listeners/mixins.rb +3 -3
  53. data/lib/tapioca/gem/listeners/sorbet_signatures.rb +5 -7
  54. data/lib/tapioca/gem/listeners/sorbet_type_variables.rb +1 -1
  55. data/lib/tapioca/gem/listeners.rb +1 -1
  56. data/lib/tapioca/gem/pipeline.rb +20 -19
  57. data/lib/tapioca/gemfile.rb +0 -16
  58. data/lib/tapioca/helpers/rbi_helper.rb +7 -0
  59. data/lib/tapioca/helpers/sorbet_helper.rb +19 -0
  60. data/lib/tapioca/helpers/test/dsl_compiler.rb +2 -9
  61. data/lib/tapioca/internal.rb +2 -1
  62. data/lib/tapioca/rbi_ext/model.rb +6 -2
  63. data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +6 -6
  64. data/lib/tapioca/runtime/generic_type_registry.rb +8 -8
  65. data/lib/tapioca/runtime/reflection.rb +28 -21
  66. data/lib/tapioca/runtime/trackers/constant_definition.rb +3 -3
  67. data/lib/tapioca/runtime/trackers/method_definition.rb +4 -4
  68. data/lib/tapioca/runtime/trackers/mixin.rb +5 -5
  69. data/lib/tapioca/runtime/trackers/required_ancestor.rb +2 -2
  70. data/lib/tapioca/runtime/trackers/tracker.rb +1 -1
  71. data/lib/tapioca/sorbet_ext/generic_name_patch.rb +1 -1
  72. data/lib/tapioca/sorbet_ext/void_patch.rb +29 -0
  73. data/lib/tapioca/static/symbol_loader.rb +32 -6
  74. data/lib/tapioca/version.rb +1 -1
  75. metadata +13 -12
  76. data/lib/tapioca/gem/listeners/yard_doc.rb +0 -110
@@ -109,7 +109,7 @@ module Tapioca
109
109
  # end
110
110
  # end
111
111
  # ~~~
112
- #: [ConstantType = (T::Module[top] & ::StateMachines::ClassMethods)]
112
+ #: [ConstantType = (Module[top] & ::StateMachines::ClassMethods)]
113
113
  class StateMachines < Compiler
114
114
  # @override
115
115
  #: -> void
@@ -154,7 +154,7 @@ module Tapioca
154
154
 
155
155
  class << self
156
156
  # @override
157
- #: -> Enumerable[T::Module[top]]
157
+ #: -> Enumerable[Module[top]]
158
158
  def gather_constants
159
159
  all_classes.select { |mod| ::StateMachines::InstanceMethods > mod }
160
160
  end
@@ -78,7 +78,7 @@ module Tapioca
78
78
  # include GeneratedUrlHelpersModule
79
79
  # end
80
80
  # ~~~
81
- #: [ConstantType = T::Module[top]]
81
+ #: [ConstantType = Module[top]]
82
82
  class UrlHelpers < Compiler
83
83
  # @override
84
84
  #: -> void
@@ -96,7 +96,7 @@ module Tapioca
96
96
 
97
97
  class << self
98
98
  # @override
99
- #: -> Enumerable[T::Module[top]]
99
+ #: -> Enumerable[Module[top]]
100
100
  def gather_constants
101
101
  return [] unless defined?(Rails.application) && Rails.application
102
102
 
@@ -128,7 +128,7 @@ module Tapioca
128
128
  constants.concat(NON_DISCOVERABLE_INCLUDERS).push(GeneratedUrlHelpersModule, GeneratedPathHelpersModule)
129
129
  end
130
130
 
131
- #: -> Array[T::Module[top]]
131
+ #: -> Array[Module[top]]
132
132
  def gather_non_discoverable_includers
133
133
  [].tap do |includers|
134
134
  if defined?(ActionController::TemplateAssertions) && defined?(ActionDispatch::IntegrationTest)
@@ -143,7 +143,7 @@ module Tapioca
143
143
 
144
144
  # Returns `true` if `mod` "directly" includes `helper`.
145
145
  # For classes, this method will return false if the `helper` is included only by a superclass
146
- #: (T::Module[top] mod, T::Module[top] helper) -> bool
146
+ #: (Module[top] mod, Module[top] helper) -> bool
147
147
  private def includes_helper?(mod, helper)
148
148
  ancestors = ancestors_of(mod)
149
149
 
@@ -158,11 +158,11 @@ module Tapioca
158
158
  end
159
159
  end
160
160
 
161
- NON_DISCOVERABLE_INCLUDERS = gather_non_discoverable_includers #: Array[T::Module[top]]
161
+ NON_DISCOVERABLE_INCLUDERS = gather_non_discoverable_includers #: Array[Module[top]]
162
162
 
163
163
  private
164
164
 
165
- #: (RBI::Tree root, T::Module[top] constant) -> void
165
+ #: (RBI::Tree root, Module[top] constant) -> void
166
166
  def generate_module_for(root, constant)
167
167
  root.create_module(T.must(constant.name)) do |mod|
168
168
  mod.create_include("::ActionDispatch::Routing::UrlFor")
@@ -178,7 +178,7 @@ module Tapioca
178
178
  end
179
179
  end
180
180
 
181
- #: (RBI::Scope mod, T::Module[top] helper_module) -> void
181
+ #: (RBI::Scope mod, Module[top] helper_module) -> void
182
182
  def create_mixins_for(mod, helper_module)
183
183
  include_helper = constant.ancestors.include?(helper_module) || NON_DISCOVERABLE_INCLUDERS.include?(constant)
184
184
  extend_helper = constant.singleton_class.ancestors.include?(helper_module)
@@ -110,7 +110,7 @@ module Tapioca
110
110
 
111
111
  private
112
112
 
113
- #: (T::Module[top] constant) -> String
113
+ #: (Module[top] constant) -> String
114
114
  def type_for_constant(constant)
115
115
  if constant.instance_methods.include?(:prepare)
116
116
  prepare_method = constant.instance_method(:prepare)
@@ -7,13 +7,13 @@ module Tapioca
7
7
  #: Enumerable[singleton(Compiler)]
8
8
  attr_reader :active_compilers
9
9
 
10
- #: Array[T::Module[top]]
10
+ #: Array[Module[top]]
11
11
  attr_reader :requested_constants
12
12
 
13
13
  #: Array[Pathname]
14
14
  attr_reader :requested_paths
15
15
 
16
- #: Array[T::Module[top]]
16
+ #: Array[Module[top]]
17
17
  attr_reader :skipped_constants
18
18
 
19
19
  #: ^(String error) -> void
@@ -23,12 +23,12 @@ module Tapioca
23
23
  attr_reader :errors
24
24
 
25
25
  #: (
26
- #| requested_constants: Array[T::Module[top]],
26
+ #| requested_constants: Array[Module[top]],
27
27
  #| ?requested_paths: Array[Pathname],
28
28
  #| ?requested_compilers: Array[singleton(Compiler)],
29
29
  #| ?excluded_compilers: Array[singleton(Compiler)],
30
30
  #| ?error_handler: ^(String error) -> void,
31
- #| ?skipped_constants: Array[T::Module[top]],
31
+ #| ?skipped_constants: Array[Module[top]],
32
32
  #| ?number_of_workers: Integer?,
33
33
  #| ?compiler_options: Hash[String, untyped],
34
34
  #| ?lsp_addon: bool
@@ -56,7 +56,7 @@ module Tapioca
56
56
  @errors = [] #: Array[String]
57
57
  end
58
58
 
59
- #: [R] { (T::Module[top] constant, RBI::File rbi) -> R } -> Array[R]
59
+ #: [R] { (Module[top] constant, RBI::File rbi) -> R } -> Array[R]
60
60
  def run(&blk)
61
61
  constants_to_process = gather_constants(requested_constants, requested_paths, skipped_constants)
62
62
  .select { |c| Module === c } # Filter value constants out
@@ -128,10 +128,10 @@ module Tapioca
128
128
  end
129
129
 
130
130
  #: (
131
- #| Array[T::Module[top]] requested_constants,
131
+ #| Array[Module[top]] requested_constants,
132
132
  #| Array[Pathname] requested_paths,
133
- #| Array[T::Module[top]] skipped_constants
134
- #| ) -> Set[T::Module[top]]
133
+ #| Array[Module[top]] skipped_constants
134
+ #| ) -> Set[Module[top]]
135
135
  def gather_constants(requested_constants, requested_paths, skipped_constants)
136
136
  Compiler.requested_constants = requested_constants
137
137
  constants = Set.new.compare_by_identity
@@ -153,7 +153,7 @@ module Tapioca
153
153
  constants
154
154
  end
155
155
 
156
- #: (Set[T::Module[top]] constants) -> Set[T::Module[top]]
156
+ #: (Set[Module[top]] constants) -> Set[Module[top]]
157
157
  def filter_anonymous_and_reloaded_constants(constants)
158
158
  # Group constants by their names
159
159
  constants_by_name = constants
@@ -182,7 +182,7 @@ module Tapioca
182
182
  Set.new.compare_by_identity.merge(filtered_constants)
183
183
  end
184
184
 
185
- #: (T::Module[top] constant) -> RBI::File?
185
+ #: (Module[top] constant) -> RBI::File?
186
186
  def rbi_for_constant(constant)
187
187
  file = RBI::File.new(strictness: "true")
188
188
 
@@ -37,12 +37,12 @@ module Tapioca
37
37
 
38
38
  class ForeignConstantFound < ConstantFound
39
39
  # @override
40
- #: -> T::Module[top]
40
+ #: -> Module[top]
41
41
  def constant
42
42
  T.cast(@constant, T::Module[T.anything])
43
43
  end
44
44
 
45
- #: (String symbol, T::Module[top] constant) -> void
45
+ #: (String symbol, Module[top] constant) -> void
46
46
  def initialize(symbol, constant)
47
47
  super
48
48
  end
@@ -53,10 +53,10 @@ module Tapioca
53
53
  #: String
54
54
  attr_reader :symbol
55
55
 
56
- #: T::Module[top]
56
+ #: Module[top]
57
57
  attr_reader :constant
58
58
 
59
- #: (String symbol, T::Module[top] constant) -> void
59
+ #: (String symbol, Module[top] constant) -> void
60
60
  def initialize(symbol, constant)
61
61
  super()
62
62
  @symbol = symbol
@@ -68,7 +68,7 @@ module Tapioca
68
68
  #: RBI::Const
69
69
  attr_reader :node
70
70
 
71
- #: (String symbol, T::Module[top] constant, RBI::Const node) -> void
71
+ #: (String symbol, Module[top] constant, RBI::Const node) -> void
72
72
  def initialize(symbol, constant, node)
73
73
  super(symbol, constant)
74
74
  @node = node
@@ -79,7 +79,7 @@ module Tapioca
79
79
  #: RBI::Scope
80
80
  attr_reader :node
81
81
 
82
- #: (String symbol, T::Module[top] constant, RBI::Scope node) -> void
82
+ #: (String symbol, Module[top] constant, RBI::Scope node) -> void
83
83
  def initialize(symbol, constant, node)
84
84
  super(symbol, constant)
85
85
  @node = node
@@ -103,7 +103,7 @@ module Tapioca
103
103
 
104
104
  #: (
105
105
  #| String symbol,
106
- #| T::Module[top] constant,
106
+ #| Module[top] constant,
107
107
  #| UnboundMethod method,
108
108
  #| RBI::Method node,
109
109
  #| untyped signature,
@@ -0,0 +1,94 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Tapioca
5
+ module Gem
6
+ module Listeners
7
+ class Documentation < Base
8
+ IGNORED_COMMENTS = [
9
+ ":doc:",
10
+ ":nodoc:",
11
+ "typed:",
12
+ "frozen_string_literal:",
13
+ "encoding:",
14
+ "warn_indent:",
15
+ "shareable_constant_value:",
16
+ "rubocop:",
17
+ "@requires_ancestor:",
18
+ ] #: Array[String]
19
+
20
+ #: (Pipeline pipeline, Rubydex::Graph gem_graph) -> void
21
+ def initialize(pipeline, gem_graph)
22
+ super(pipeline)
23
+
24
+ @gem_graph = gem_graph
25
+ end
26
+
27
+ private
28
+
29
+ #: (String line) -> bool
30
+ def rbs_comment?(line)
31
+ line.start_with?(": ", "| ")
32
+ end
33
+
34
+ # @override
35
+ #: (ConstNodeAdded event) -> void
36
+ def on_const(event)
37
+ event.node.comments = documentation_comments(event.symbol)
38
+ end
39
+
40
+ # @override
41
+ #: (ScopeNodeAdded event) -> void
42
+ def on_scope(event)
43
+ event.node.comments = documentation_comments(event.symbol)
44
+ end
45
+
46
+ # @override
47
+ #: (MethodNodeAdded event) -> void
48
+ def on_method(event)
49
+ name = if event.constant.singleton_class?
50
+ "#{event.symbol}::<#{event.symbol.split("::").last}>##{event.node.name}()"
51
+ else
52
+ "#{event.symbol}##{event.node.name}()"
53
+ end
54
+ event.node.comments = documentation_comments(name, sigs: event.node.sigs)
55
+ end
56
+
57
+ #: (String name, ?sigs: Array[RBI::Sig]) -> Array[RBI::Comment]
58
+ def documentation_comments(name, sigs: [])
59
+ declaration = @gem_graph[name]
60
+ # For attr_writer methods (name ending in =), fall back to reader docs
61
+ if declaration.nil? && name.end_with?("=()")
62
+ declaration = @gem_graph[name.delete_suffix("=()") + "()"]
63
+ end
64
+ # For singleton methods (Class::<Class>#method()), fall back to instance method docs.
65
+ # This handles module_function and extend self methods which Rubydex indexes
66
+ # only under the instance method name.
67
+ if declaration.nil? && name.include?("::<")
68
+ declaration = @gem_graph[name.sub(/::<[^>]+>#/, "#")]
69
+ end
70
+ return [] unless declaration
71
+
72
+ comments = declaration.definitions.flat_map(&:comments)
73
+ comments.uniq!
74
+ return [] if comments.empty?
75
+
76
+ lines = comments
77
+ .map { |comment| comment.string.gsub(/^#+ ?/, "") }
78
+ .reject { |line| IGNORED_COMMENTS.any? { |comment| line.include?(comment) } || rbs_comment?(line) }
79
+
80
+ # Strip leading and trailing blank lines, matching YARD's behavior
81
+ lines = lines.reverse_each.drop_while(&:empty?).reverse_each.drop_while(&:empty?)
82
+
83
+ lines.map! { |line| RBI::Comment.new(line) }
84
+ end
85
+
86
+ # @override
87
+ #: (NodeAdded event) -> bool
88
+ def ignore?(event)
89
+ event.is_a?(Tapioca::Gem::ForeignScopeNodeAdded)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -25,9 +25,9 @@ module Tapioca
25
25
  #: (
26
26
  #| RBI::Tree tree,
27
27
  #| String module_name,
28
- #| T::Module[top] mod,
28
+ #| Module[top] mod,
29
29
  #| ?Array[Symbol] for_visibility,
30
- #| ?attached_class: T::Module[top]?
30
+ #| ?attached_class: Module[top]?
31
31
  #| ) -> void
32
32
  def compile_directly_owned_methods(
33
33
  tree,
@@ -59,7 +59,7 @@ module Tapioca
59
59
  #: (
60
60
  #| RBI::Tree tree,
61
61
  #| String symbol_name,
62
- #| T::Module[top] constant,
62
+ #| Module[top] constant,
63
63
  #| UnboundMethod? method,
64
64
  #| ?RBI::Visibility visibility
65
65
  #| ) -> void
@@ -169,7 +169,7 @@ module Tapioca
169
169
  # This method implements a better way of checking whether a constant defines a method.
170
170
  # It walks up the ancestor tree via the `super_method` method; if any of the super
171
171
  # methods are owned by the constant, it means that the constant declares the method.
172
- #: (UnboundMethod method, T::Module[top] constant) -> bool
172
+ #: (UnboundMethod method, Module[top] constant) -> bool
173
173
  def method_owned_by_constant?(method, constant)
174
174
  # Widen the type of `method` to be nilable
175
175
  method = method #: UnboundMethod?
@@ -183,7 +183,7 @@ module Tapioca
183
183
  false
184
184
  end
185
185
 
186
- #: (T::Module[top] mod) -> Hash[Symbol, Array[Symbol]]
186
+ #: (Module[top] mod) -> Hash[Symbol, Array[Symbol]]
187
187
  def method_names_by_visibility(mod)
188
188
  {
189
189
  public: public_instance_methods_of(mod),
@@ -192,7 +192,7 @@ module Tapioca
192
192
  }
193
193
  end
194
194
 
195
- #: (T::Module[top] constant, String method_name) -> bool
195
+ #: (Module[top] constant, String method_name) -> bool
196
196
  def struct_method?(constant, method_name)
197
197
  return false unless T::Props::ClassMethods === constant
198
198
 
@@ -202,7 +202,7 @@ module Tapioca
202
202
  .include?(method_name.gsub(/=$/, "").to_sym)
203
203
  end
204
204
 
205
- #: (T::Module[top]? attached_class, Symbol method_name) -> bool?
205
+ #: (Module[top]? attached_class, Symbol method_name) -> bool?
206
206
  def method_new_in_abstract_class?(attached_class, method_name)
207
207
  attached_class &&
208
208
  method_name == :new &&
@@ -210,7 +210,7 @@ module Tapioca
210
210
  Class === attached_class.singleton_class
211
211
  end
212
212
 
213
- #: (T::Module[top] constant) -> UnboundMethod?
213
+ #: (Module[top] constant) -> UnboundMethod?
214
214
  def initialize_method_for(constant)
215
215
  constant.instance_method(:initialize)
216
216
  rescue
@@ -30,7 +30,7 @@ module Tapioca
30
30
  add_mixins(node, constant, extends.reverse, Runtime::Trackers::Mixin::Type::Extend)
31
31
  end
32
32
 
33
- #: (RBI::Tree tree, T::Module[top] constant, Array[T::Module[top]] mods, Runtime::Trackers::Mixin::Type mixin_type) -> void
33
+ #: (RBI::Tree tree, Module[top] constant, Array[Module[top]] mods, Runtime::Trackers::Mixin::Type mixin_type) -> void
34
34
  def add_mixins(tree, constant, mods, mixin_type)
35
35
  mods
36
36
  .select do |mod|
@@ -57,7 +57,7 @@ module Tapioca
57
57
  end
58
58
  end
59
59
 
60
- #: (T::Module[top] constant, T::Module[top] mixin, Runtime::Trackers::Mixin::Type mixin_type) -> bool
60
+ #: (Module[top] constant, Module[top] mixin, Runtime::Trackers::Mixin::Type mixin_type) -> bool
61
61
  def mixed_in_by_gem?(constant, mixin, mixin_type)
62
62
  mixin_location = Runtime::Trackers::Mixin.mixin_location(mixin, mixin_type, constant)
63
63
 
@@ -73,7 +73,7 @@ module Tapioca
73
73
  mixin_name.start_with?("T::") && !mixin_name.start_with?("T::Props")
74
74
  end
75
75
 
76
- #: (T::Module[top] constant) -> Array[T::Module[top]]
76
+ #: (Module[top] constant) -> Array[Module[top]]
77
77
  def interesting_ancestors_of(constant)
78
78
  inherited_ancestors = Set.new.compare_by_identity.merge(inherited_ancestors_of(constant))
79
79
 
@@ -8,8 +8,6 @@ module Tapioca
8
8
  include Runtime::Reflection
9
9
  include RBIHelper
10
10
 
11
- TYPE_PARAMETER_MATCHER = /T\.type_parameter\(:?([[:word:]]+)\)/
12
-
13
11
  private
14
12
 
15
13
  # @override
@@ -25,8 +23,10 @@ module Tapioca
25
23
  def compile_signature(signature, parameters)
26
24
  parameter_types = signature.arg_types.to_h #: Hash[Symbol, T::Types::Base]
27
25
  parameter_types.merge!(signature.kwarg_types)
28
- parameter_types[signature.rest_name] = signature.rest_type if signature.has_rest
29
- parameter_types[signature.keyrest_name] = signature.keyrest_type if signature.has_keyrest
26
+ rest_type = signature.rest_type
27
+ parameter_types[signature.rest_name] = rest_type if rest_type
28
+ keyrest_type = signature.keyrest_type
29
+ parameter_types[signature.keyrest_name] = keyrest_type if keyrest_type
30
30
  parameter_types[signature.block_name] = signature.block_type if signature.block_name
31
31
 
32
32
  sig = RBI::Sig.new
@@ -42,9 +42,7 @@ module Tapioca
42
42
  sig.return_type = return_type
43
43
  @pipeline.push_symbol(return_type)
44
44
 
45
- parameter_types.values.join(", ").scan(TYPE_PARAMETER_MATCHER).flatten.uniq.each do |k, _|
46
- sig.type_params << k
47
- end
45
+ sig.type_params.concat(extract_type_parameters(parameter_types.values.map(&:to_s).append(return_type)))
48
46
 
49
47
  case signature.mode
50
48
  when "abstract"
@@ -22,7 +22,7 @@ module Tapioca
22
22
  node << sclass if sclass.nodes.length > 1
23
23
  end
24
24
 
25
- #: (RBI::Tree tree, T::Module[top] constant) -> void
25
+ #: (RBI::Tree tree, Module[top] constant) -> void
26
26
  def compile_type_variable_declarations(tree, constant)
27
27
  # Try to find the type variables defined on this constant, bail if we can't
28
28
  type_variables = Runtime::GenericTypeRegistry.lookup_type_variables(constant)
@@ -14,5 +14,5 @@ require "tapioca/gem/listeners/sorbet_signatures"
14
14
  require "tapioca/gem/listeners/sorbet_type_variables"
15
15
  require "tapioca/gem/listeners/subconstants"
16
16
  require "tapioca/gem/listeners/foreign_constants"
17
- require "tapioca/gem/listeners/yard_doc"
17
+ require "tapioca/gem/listeners/documentation"
18
18
  require "tapioca/gem/listeners/source_location"
@@ -32,6 +32,7 @@ module Tapioca
32
32
 
33
33
  @payload_symbols = Static::SymbolLoader.payload_symbols #: Set[String]
34
34
  @bootstrap_symbols = load_bootstrap_symbols(@gem) #: Set[String]
35
+ gem_graph = Static::SymbolLoader.graph_from_paths(@gem.files) if include_doc
35
36
 
36
37
  @bootstrap_symbols.each { |symbol| push_symbol(symbol) }
37
38
 
@@ -46,7 +47,7 @@ module Tapioca
46
47
  @node_listeners << Gem::Listeners::SorbetRequiredAncestors.new(self)
47
48
  @node_listeners << Gem::Listeners::SorbetSignatures.new(self)
48
49
  @node_listeners << Gem::Listeners::Subconstants.new(self)
49
- @node_listeners << Gem::Listeners::YardDoc.new(self) if include_doc
50
+ @node_listeners << Gem::Listeners::Documentation.new(self, gem_graph) if include_doc
50
51
  @node_listeners << Gem::Listeners::ForeignConstants.new(self)
51
52
  @node_listeners << Gem::Listeners::SourceLocation.new(self) if include_loc
52
53
  @node_listeners << Gem::Listeners::RemoveEmptyPayloadScopes.new(self)
@@ -71,29 +72,29 @@ module Tapioca
71
72
  @events << Gem::ConstantFound.new(symbol, constant)
72
73
  end
73
74
 
74
- #: (String symbol, T::Module[top] constant) -> void
75
+ #: (String symbol, Module[top] constant) -> void
75
76
  def push_foreign_constant(symbol, constant)
76
77
  @events << Gem::ForeignConstantFound.new(symbol, constant)
77
78
  end
78
79
 
79
- #: (String symbol, T::Module[top] constant, RBI::Const node) -> void
80
+ #: (String symbol, Module[top] constant, RBI::Const node) -> void
80
81
  def push_const(symbol, constant, node)
81
82
  @events << Gem::ConstNodeAdded.new(symbol, constant, node)
82
83
  end
83
84
 
84
- #: (String symbol, T::Module[top] constant, RBI::Scope node) -> void
85
+ #: (String symbol, Module[top] constant, RBI::Scope node) -> void
85
86
  def push_scope(symbol, constant, node)
86
87
  @events << Gem::ScopeNodeAdded.new(symbol, constant, node)
87
88
  end
88
89
 
89
- #: (String symbol, T::Module[top] constant, RBI::Scope node) -> void
90
+ #: (String symbol, Module[top] constant, RBI::Scope node) -> void
90
91
  def push_foreign_scope(symbol, constant, node)
91
92
  @events << Gem::ForeignScopeNodeAdded.new(symbol, constant, node)
92
93
  end
93
94
 
94
95
  #: (
95
96
  #| String symbol,
96
- #| T::Module[top] constant,
97
+ #| Module[top] constant,
97
98
  #| UnboundMethod method,
98
99
  #| RBI::Method node,
99
100
  #| untyped signature,
@@ -149,7 +150,7 @@ module Tapioca
149
150
  end
150
151
  end
151
152
 
152
- #: (Symbol method_name, T::Module[top] owner) -> MethodDefinitionLookupResult
153
+ #: (Symbol method_name, Module[top] owner) -> MethodDefinitionLookupResult
153
154
  def method_definition_in_gem(method_name, owner)
154
155
  definitions = Tapioca::Runtime::Trackers::MethodDefinition.method_definitions_for(method_name, owner)
155
156
 
@@ -175,7 +176,7 @@ module Tapioca
175
176
 
176
177
  # Helpers
177
178
 
178
- #: (T::Module[top] constant) -> String?
179
+ #: (Module[top] constant) -> String?
179
180
  def name_of(constant)
180
181
  name = name_of_proxy_target(constant, super(class_of(constant)))
181
182
  return name if name
@@ -247,7 +248,7 @@ module Tapioca
247
248
 
248
249
  # Compiling
249
250
 
250
- #: (String symbol, T::Module[top] constant) -> void
251
+ #: (String symbol, Module[top] constant) -> void
251
252
  def compile_foreign_constant(symbol, constant)
252
253
  return if skip_foreign_constant?(symbol, constant)
253
254
  return if seen?(symbol)
@@ -273,7 +274,7 @@ module Tapioca
273
274
  end
274
275
  end
275
276
 
276
- #: (String name, T::Module[top] constant) -> void
277
+ #: (String name, Module[top] constant) -> void
277
278
  def compile_alias(name, constant)
278
279
  return if seen?(name)
279
280
 
@@ -328,7 +329,7 @@ module Tapioca
328
329
  @root << node
329
330
  end
330
331
 
331
- #: (String name, T::Module[top] constant) -> void
332
+ #: (String name, Module[top] constant) -> void
332
333
  def compile_module(name, constant)
333
334
  return if skip_module?(name, constant)
334
335
  return if seen?(name)
@@ -339,7 +340,7 @@ module Tapioca
339
340
  push_scope(name, constant, scope)
340
341
  end
341
342
 
342
- #: (String name, T::Module[top] constant) -> RBI::Scope
343
+ #: (String name, Module[top] constant) -> RBI::Scope
343
344
  def compile_scope(name, constant)
344
345
  scope = if constant.is_a?(Class)
345
346
  superclass = compile_superclass(constant)
@@ -422,7 +423,7 @@ module Tapioca
422
423
  false
423
424
  end
424
425
 
425
- #: (String name, T::Module[top] constant) -> bool
426
+ #: (String name, Module[top] constant) -> bool
426
427
  def skip_alias?(name, constant)
427
428
  return true if symbol_in_payload?(name)
428
429
  return true unless constant_in_gem?(name)
@@ -440,12 +441,12 @@ module Tapioca
440
441
  false
441
442
  end
442
443
 
443
- #: (String name, T::Module[top] constant) -> bool
444
+ #: (String name, Module[top] constant) -> bool
444
445
  def skip_foreign_constant?(name, constant)
445
446
  Tapioca::TypeVariableModule === constant
446
447
  end
447
448
 
448
- #: (String name, T::Module[top] constant) -> bool
449
+ #: (String name, Module[top] constant) -> bool
449
450
  def skip_module?(name, constant)
450
451
  return true unless defined_in_gem?(constant, strict: false)
451
452
  return true if Tapioca::TypeVariableModule === constant
@@ -453,7 +454,7 @@ module Tapioca
453
454
  false
454
455
  end
455
456
 
456
- #: (T::Module[top] constant, ?strict: bool) -> bool
457
+ #: (Module[top] constant, ?strict: bool) -> bool
457
458
  def defined_in_gem?(constant, strict: true)
458
459
  files = get_file_candidates(constant)
459
460
  .merge(Runtime::Trackers::ConstantDefinition.files_for(constant))
@@ -465,7 +466,7 @@ module Tapioca
465
466
  end
466
467
  end
467
468
 
468
- #: (T::Module[top] constant) -> Set[String]
469
+ #: (Module[top] constant) -> Set[String]
469
470
  def get_file_candidates(constant)
470
471
  file_candidates_for(constant)
471
472
  rescue ArgumentError, NameError
@@ -496,7 +497,7 @@ module Tapioca
496
497
 
497
498
  # Helpers
498
499
 
499
- #: ((T::Module[top] & T::Generic) constant) -> String
500
+ #: ((Module[top] & T::Generic) constant) -> String
500
501
  def generic_name_of(constant)
501
502
  type_name = T.must(constant.name)
502
503
  return type_name if type_name =~ /\[.*\]$/
@@ -512,7 +513,7 @@ module Tapioca
512
513
  "#{type_name}[#{type_variable_names}]"
513
514
  end
514
515
 
515
- #: (T::Module[top] constant, String? class_name) -> String?
516
+ #: (Module[top] constant, String? class_name) -> String?
516
517
  def name_of_proxy_target(constant, class_name)
517
518
  return unless class_name == "ActiveSupport::Deprecation::DeprecatedConstantProxy"
518
519
 
@@ -177,22 +177,6 @@ module Tapioca
177
177
  end
178
178
  end
179
179
 
180
- #: -> void
181
- def parse_yard_docs
182
- files.each do |path|
183
- YARD.parse(path.to_s, [], Logger::Severity::FATAL)
184
- rescue RangeError
185
- # In some circumstances, YARD will raise an error when parsing a file
186
- # that is actually valid Ruby. We don't want tapioca to halt in these
187
- # cases, so we'll rescue the error, pretend like there was no
188
- # documentation, and move on.
189
- #
190
- # This can be removed when https://github.com/lsegal/yard/issues/1536
191
- # is resolved and released.
192
- []
193
- end
194
- end
195
-
196
180
  #: -> Array[String]
197
181
  def exported_rbi_files
198
182
  @exported_rbi_files ||= Dir.glob("#{full_gem_path}/rbi/**/*.rbi").sort
@@ -94,6 +94,13 @@ module Tapioca
94
94
  end
95
95
  end
96
96
 
97
+ TYPE_PARAMETER_MATCHER = /T\.type_parameter\(:?([[:word:]]+)\)/
98
+
99
+ #: (Array[String] type_strings) -> Array[String]
100
+ def extract_type_parameters(type_strings)
101
+ type_strings.join(", ").scan(TYPE_PARAMETER_MATCHER).flatten.uniq
102
+ end
103
+
97
104
  #: (String name) -> bool
98
105
  def valid_method_name?(name)
99
106
  Prism.parse_success?("def self.#{name}(a); end")
@@ -22,6 +22,25 @@ module Tapioca
22
22
  SPOOM_CONTEXT.srb(sorbet_args.join(" "), sorbet_bin: sorbet_path)
23
23
  end
24
24
 
25
+ #: (String, rbi_mode: bool) { (String stderr) -> void } -> void
26
+ def sorbet_syntax_check!(source, rbi_mode:, &on_failure)
27
+ quoted_source = "\"#{source}\""
28
+
29
+ result = if rbi_mode
30
+ # --e-rbi cannot be used on its own, so we pass a dummy value like `-e ""`
31
+ sorbet("--no-config", "--stop-after=parser", "-e", '""', "--e-rbi", quoted_source)
32
+ else
33
+ sorbet("--no-config", "--stop-after=parser", "-e", quoted_source)
34
+ end
35
+
36
+ unless result.status
37
+ stderr = result.err #: as !nil
38
+ on_failure.call(stderr)
39
+ end
40
+
41
+ nil
42
+ end
43
+
25
44
  #: -> String
26
45
  def sorbet_path
27
46
  sorbet_path = ENV.fetch(SORBET_EXE_PATH_ENV_VAR, SORBET_BIN)