docscribe 1.4.1 → 1.5.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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +588 -104
  3. data/lib/docscribe/cli/check_for_comments.rb +183 -0
  4. data/lib/docscribe/cli/config_builder.rb +180 -36
  5. data/lib/docscribe/cli/formatters/json.rb +294 -0
  6. data/lib/docscribe/cli/formatters/sarif.rb +235 -0
  7. data/lib/docscribe/cli/formatters/text.rb +208 -0
  8. data/lib/docscribe/cli/formatters.rb +26 -0
  9. data/lib/docscribe/cli/generate.rb +296 -125
  10. data/lib/docscribe/cli/init.rb +58 -14
  11. data/lib/docscribe/cli/options.rb +410 -133
  12. data/lib/docscribe/cli/rbs_gen.rb +529 -0
  13. data/lib/docscribe/cli/run.rb +503 -189
  14. data/lib/docscribe/cli/sigs.rb +366 -0
  15. data/lib/docscribe/cli/update_types.rb +103 -0
  16. data/lib/docscribe/cli.rb +35 -9
  17. data/lib/docscribe/config/defaults.rb +16 -12
  18. data/lib/docscribe/config/emit.rb +18 -0
  19. data/lib/docscribe/config/filtering.rb +37 -31
  20. data/lib/docscribe/config/loader.rb +20 -13
  21. data/lib/docscribe/config/plugin.rb +2 -1
  22. data/lib/docscribe/config/rbs.rb +68 -27
  23. data/lib/docscribe/config/sorbet.rb +40 -17
  24. data/lib/docscribe/config/sorting.rb +2 -1
  25. data/lib/docscribe/config/template.rb +10 -1
  26. data/lib/docscribe/config/utils.rb +12 -9
  27. data/lib/docscribe/config.rb +3 -4
  28. data/lib/docscribe/infer/ast_walk.rb +1 -1
  29. data/lib/docscribe/infer/constants.rb +15 -0
  30. data/lib/docscribe/infer/literals.rb +39 -26
  31. data/lib/docscribe/infer/names.rb +24 -16
  32. data/lib/docscribe/infer/params.rb +57 -13
  33. data/lib/docscribe/infer/raises.rb +23 -15
  34. data/lib/docscribe/infer/returns.rb +784 -199
  35. data/lib/docscribe/infer.rb +28 -28
  36. data/lib/docscribe/inline_rewriter/collector.rb +816 -430
  37. data/lib/docscribe/inline_rewriter/doc_block.rb +323 -150
  38. data/lib/docscribe/inline_rewriter/doc_builder.rb +1837 -648
  39. data/lib/docscribe/inline_rewriter/source_helpers.rb +119 -71
  40. data/lib/docscribe/inline_rewriter/tag_sorter.rb +165 -107
  41. data/lib/docscribe/inline_rewriter.rb +1144 -727
  42. data/lib/docscribe/parsing.rb +29 -10
  43. data/lib/docscribe/plugin/base/collector_plugin.rb +3 -3
  44. data/lib/docscribe/plugin/base/tag_plugin.rb +1 -2
  45. data/lib/docscribe/plugin/context.rb +28 -18
  46. data/lib/docscribe/plugin/registry.rb +49 -23
  47. data/lib/docscribe/plugin/tag.rb +9 -14
  48. data/lib/docscribe/plugin.rb +54 -22
  49. data/lib/docscribe/types/provider_chain.rb +4 -2
  50. data/lib/docscribe/types/rbs/collection_loader.rb +2 -3
  51. data/lib/docscribe/types/rbs/provider.rb +127 -62
  52. data/lib/docscribe/types/rbs/type_formatter.rb +286 -77
  53. data/lib/docscribe/types/signature.rb +22 -42
  54. data/lib/docscribe/types/sorbet/base_provider.rb +51 -27
  55. data/lib/docscribe/types/sorbet/rbi_provider.rb +3 -3
  56. data/lib/docscribe/types/sorbet/source_provider.rb +3 -2
  57. data/lib/docscribe/types/yard/formatter.rb +100 -0
  58. data/lib/docscribe/types/yard/parser.rb +240 -0
  59. data/lib/docscribe/types/yard/types.rb +52 -0
  60. data/lib/docscribe/version.rb +1 -1
  61. metadata +34 -2
@@ -14,18 +14,19 @@ module Docscribe
14
14
  # The provider returns Docscribe's normalized signature model so the rest of
15
15
  # the pipeline can stay independent of the underlying signature source.
16
16
  class Provider
17
+ # Initialize
18
+ #
17
19
  # @param [Array<String>] sig_dirs directories containing `.rbs` files
18
20
  # @param [Array<String>] collection_dirs RBS collection directories
19
- # (loaded separately; on error they are silently dropped and only
20
- # user sig_dirs are used)
21
21
  # @param [Boolean] collapse_generics whether generic container types
22
- # should be simplified during formatting
23
- # @return [Object]
24
- def initialize(sig_dirs:, collection_dirs: [], collapse_generics: false)
22
+ # @param [Boolean] collapse_object_generics collapse Object generics flag
23
+ # @return [void]
24
+ def initialize(sig_dirs:, collection_dirs: [], collapse_generics: false, collapse_object_generics: false)
25
25
  require 'rbs'
26
26
  @sig_dirs = Array(sig_dirs).map(&:to_s)
27
27
  @collection_dirs = Array(collection_dirs).map(&:to_s)
28
28
  @collapse_generics = !!collapse_generics
29
+ @collapse_object_generics = !!collapse_object_generics
29
30
  @env = nil
30
31
  @builder = nil
31
32
  @warned = false
@@ -41,27 +42,17 @@ module Docscribe
41
42
  # @param [Symbol, String] name method name
42
43
  # @raise [::RBS::BaseError]
43
44
  # @raise [StandardError]
44
- # @return [Docscribe::Types::MethodSignature, nil]
45
+ # @return [Docscribe::Types::MethodSignature, nil] if StandardError
46
+ # @return [nil] if ::RBS::BaseError
47
+ # @return [nil] if StandardError
45
48
  def signature_for(container:, scope:, name:)
46
49
  load_env!
47
-
48
- definition = definition_for(container: container, scope: scope)
49
- method_def = definition.methods[name.to_sym]
50
- return nil unless method_def
51
-
52
- method_type = method_def.method_types.first
53
- return nil unless method_type
54
-
55
- func = method_type.type
56
- build_signature(func)
50
+ lookup_signature(container, scope, name)
57
51
  rescue ::RBS::BaseError => e
58
- warn_once("Docscribe: RBS error: #{e.class}: #{e.message}")
52
+ handle_rbs_error(e, 'RBS error')
59
53
  nil
60
54
  rescue StandardError => e
61
- warn_once(
62
- 'Docscribe: RBS integration failed (falling back to inference): ' \
63
- "#{e.class}: #{e.message}\nFeel free to open an issue on github."
64
- )
55
+ handle_rbs_error(e, 'RBS integration failed (falling back to inference)')
65
56
  nil
66
57
  end
67
58
 
@@ -75,33 +66,69 @@ module Docscribe
75
66
  # dirs are dropped and only user sig_dirs are used.
76
67
  #
77
68
  # @private
78
- # @raise [::RBS::BaseError]
79
- # @raise [StandardError]
80
69
  # @return [void]
81
70
  def load_env!
82
71
  return if @env && @builder
83
72
 
84
- @env = build_env(@sig_dirs + @collection_dirs)
73
+ @env = try_with_fallback_build_env(
74
+ @sig_dirs + @collection_dirs,
75
+ @collection_dirs
76
+ )
77
+ end
78
+
79
+ # Look up a method signature from the loaded RBS definition builder.
80
+ #
81
+ # @private
82
+ # @param [String] container fully qualified class/module name
83
+ # @param [Symbol] scope :instance or :class
84
+ # @param [Symbol, String] name method name to look up
85
+ # @return [Docscribe::Types::MethodSignature, nil]
86
+ def lookup_signature(container, scope, name)
87
+ definition = definition_for(container: container, scope: scope)
88
+ return nil unless definition
89
+
90
+ method_def = definition.methods[name.to_sym]
91
+ return nil unless method_def
92
+
93
+ method_type = method_def.method_types.first
94
+ return nil unless method_type
95
+
96
+ func = method_type.type #: ::RBS::Types::Function
97
+ build_signature(func)
98
+ end
99
+
100
+ # Try building an environment from combined dirs, falling back to
101
+ # user-only dirs on failure when collection dirs are present.
102
+ #
103
+ # @private
104
+ # @param [Array<String>] all_dirs combined sig and collection dirs
105
+ # @param [Array<String>] collection_dirs RBS collection directories
106
+ # @raise [::RBS::BaseError]
107
+ # @raise [StandardError]
108
+ # @return [RBS::Environment] if ::RBS::BaseError
109
+ # @return [Object] if ::RBS::BaseError
110
+ def try_with_fallback_build_env(all_dirs, collection_dirs)
111
+ build_env(all_dirs)
85
112
  rescue ::RBS::BaseError => e
86
- raise unless @collection_dirs.any? && !@collection_dropped
113
+ raise unless collection_dirs.any? && !@collection_dropped
87
114
 
88
115
  @collection_dropped = true
89
116
  if ENV['DOCSCRIBE_RBS_DEBUG'] == '1'
90
117
  warn "Docscribe: RBS collection error (#{e.class}), dropping collection dirs. " \
91
118
  'Set DOCSCRIBE_RBS_DEBUG=1 for details.'
92
119
  end
93
- @env = build_env(@sig_dirs)
120
+ build_env(@sig_dirs)
94
121
  end
95
122
 
96
123
  # Build an RBS environment from the given directories.
97
124
  #
98
125
  # @private
99
- # @param [Array<String>] dirs
100
- # @return [::RBS::Environment]
126
+ # @param [Array<String>] dirs directories to load RBS from
127
+ # @return [RBS::Environment]
101
128
  def build_env(dirs)
102
129
  loader = ::RBS::EnvironmentLoader.new
103
130
  # Load core types transitively
104
- loader.add(library: 'rbs')
131
+ loader.add(library: 'rbs') # steep:ignore
105
132
 
106
133
  dirs.each do |dir|
107
134
  path = Pathname(dir)
@@ -116,12 +143,12 @@ module Docscribe
116
143
  # Build the appropriate instance or singleton definition for a container.
117
144
  #
118
145
  # @private
119
- # @param [String] container
120
- # @param [Symbol] scope
121
- # @return [Object]
146
+ # @param [String] container fully qualified class/module name
147
+ # @param [Symbol] scope :instance or :class
148
+ # @return [RBS::Definition, nil]
122
149
  def definition_for(container:, scope:)
123
150
  type_name = parse_type_name(absolute_const(container))
124
- scope == :class ? @builder.build_singleton(type_name) : @builder.build_instance(type_name)
151
+ scope == :class ? @builder&.build_singleton(type_name) : @builder&.build_instance(type_name)
125
152
  end
126
153
 
127
154
  # Parse a fully-qualified constant string into an RBS TypeName.
@@ -131,10 +158,11 @@ module Docscribe
131
158
  #
132
159
  # @private
133
160
  # @param [String] string e.g. "::Irb::Autosuggestions"
134
- # @return [::RBS::TypeName]
161
+ # @return [RBS::TypeName]
135
162
  def parse_type_name(string)
136
163
  absolute = string.start_with?('::')
137
164
  *path, name = string.delete_prefix('::').split('::').map(&:to_sym)
165
+ name ||= :Object
138
166
  ::RBS::TypeName.new(
139
167
  name: name,
140
168
  namespace: ::RBS::Namespace.new(path: path, absolute: absolute)
@@ -144,7 +172,7 @@ module Docscribe
144
172
  # Normalize a container name into an absolute constant path.
145
173
  #
146
174
  # @private
147
- # @param [String] container
175
+ # @param [String] container fully qualified class/module name
148
176
  # @return [String]
149
177
  def absolute_const(container)
150
178
  s = container.to_s
@@ -155,58 +183,76 @@ module Docscribe
155
183
  # model.
156
184
  #
157
185
  # @private
158
- # @param [::RBS::Types::Function] func
186
+ # @param [RBS::Types::Function] func RBS function type to convert
159
187
  # @return [Docscribe::Types::MethodSignature]
160
188
  def build_signature(func)
189
+ param_types, positional_types = build_param_types(func)
161
190
  MethodSignature.new(
162
191
  return_type: format_type(func.return_type),
163
- param_types: build_param_types(func),
192
+ param_types: param_types,
193
+ positional_types: positional_types,
164
194
  rest_positional: build_rest_positional(func),
165
195
  rest_keywords: build_rest_keywords(func)
166
196
  )
167
197
  end
168
198
 
169
- # Build a name => type map for positional and keyword parameters.
199
+ # Build a name => type map and positional type list for all
200
+ # positional and keyword parameters.
201
+ #
202
+ # Returns [param_types (Hash), positional_types (Array)].
203
+ # positional_types includes ALL positional params in order (named
204
+ # and unnamed) so callers can fall back to positional matching when
205
+ # the RBS signature omits parameter names.
170
206
  #
171
207
  # @private
172
- # @param [::RBS::Types::Function] func
173
- # @return [Hash{String => String}]
208
+ # @param [RBS::Types::Function] func RBS function to extract params
209
+ # @return [(Hash<String, String>, Array<String>)]
174
210
  def build_param_types(func)
175
- param_types = {}
211
+ param_types = {} #: Hash[String, String]
212
+ positional_types = [] #: Array[String]
176
213
 
177
- add_positionals!(param_types, func.required_positionals)
178
- add_positionals!(param_types, func.optional_positionals)
179
- add_positionals!(param_types, func.trailing_positionals)
214
+ collect_positionals!(param_types, positional_types, func.required_positionals)
215
+ collect_positionals!(param_types, positional_types, func.optional_positionals)
216
+ collect_positionals!(param_types, positional_types, func.trailing_positionals)
180
217
 
181
- func.required_keywords.each do |kw, p|
182
- param_types[kw.to_s] = format_type(p.type)
183
- end
218
+ add_keywords!(param_types, func.required_keywords)
219
+ add_keywords!(param_types, func.optional_keywords)
184
220
 
185
- func.optional_keywords.each do |kw, p|
221
+ [param_types, positional_types]
222
+ end
223
+
224
+ # Add keyword parameters to the normalized parameter map.
225
+ #
226
+ # @private
227
+ # @param [Hash<String, String>] param_types normalized param type map
228
+ # @param [Hash<Symbol, RBS::Types::Function::Param>] keywords keyword parameter entries
229
+ # @return [void]
230
+ def add_keywords!(param_types, keywords)
231
+ keywords.each do |kw, p|
186
232
  param_types[kw.to_s] = format_type(p.type)
187
233
  end
188
-
189
- param_types
190
234
  end
191
235
 
192
- # Add named positional parameters to the normalized parameter map.
236
+ # Collect positional parameter types into both the name-keyed hash
237
+ # (when a name is available) and the ordered-position list (always).
193
238
  #
194
239
  # @private
195
- # @param [Hash{String => String}] param_types
196
- # @param [Array<Object>] list
240
+ # @param [Hash<String, String>] param_types normalized param type map
241
+ # @param [Array<String>] positional_types ordered type list
242
+ # @param [Array<RBS::Types::Function::Param>] list positional parameter objects
197
243
  # @return [void]
198
- def add_positionals!(param_types, list)
244
+ def collect_positionals!(param_types, positional_types, list)
199
245
  list.each do |p|
200
- next unless p.name
201
-
202
- param_types[p.name.to_s] = format_type(p.type)
246
+ type_str = format_type(p.type)
247
+ positional_types << type_str
248
+ param_types[p.name.to_s] = type_str if p.name
203
249
  end
204
250
  end
205
251
 
206
252
  # Build normalized `*args` metadata.
207
253
  #
208
254
  # @private
209
- # @param [::RBS::Types::Function] func
255
+ # @param [RBS::Types::Function] func RBS function for rest params
210
256
  # @return [Docscribe::Types::RestPositional, nil]
211
257
  def build_rest_positional(func)
212
258
  rp = func.rest_positionals
@@ -221,7 +267,7 @@ module Docscribe
221
267
  # Build normalized `**kwargs` metadata.
222
268
  #
223
269
  # @private
224
- # @param [::RBS::Types::Function] func
270
+ # @param [RBS::Types::Function] func RBS function for rest keywords
225
271
  # @return [Docscribe::Types::RestKeywords, nil]
226
272
  def build_rest_keywords(func)
227
273
  rk = func.rest_keywords
@@ -237,19 +283,38 @@ module Docscribe
237
283
  # generated comments.
238
284
  #
239
285
  # @private
240
- # @param [Object] type
286
+ # @param [Docscribe::Types::RBS::TypeFormatter::rbs_type] type RBS type object to format
241
287
  # @return [String]
242
288
  def format_type(type)
243
289
  Docscribe::Types::RBS::TypeFormatter.to_yard(
244
290
  type,
245
- collapse_generics: @collapse_generics
291
+ collapse_generics: @collapse_generics,
292
+ collapse_object_generics: @collapse_object_generics
246
293
  )
247
294
  end
248
295
 
296
+ # Emit a formatted RBS error warning with context-specific messaging.
297
+ #
298
+ # @private
299
+ # @param [RBS::BaseError, StandardError] error the raised exception
300
+ # @param [String] context human-readable context label
301
+ # @return [void]
302
+ def handle_rbs_error(error, context)
303
+ case error
304
+ when ::RBS::BaseError
305
+ warn_once("Docscribe: #{context}: #{error.class}: #{error.message}")
306
+ else
307
+ warn_once(
308
+ "Docscribe: #{context}: #{error.class}: #{error.message}\n" \
309
+ 'Feel free to open an issue on github.'
310
+ )
311
+ end
312
+ end
313
+
249
314
  # Print one debug warning per provider instance when debugging is enabled.
250
315
  #
251
316
  # @private
252
- # @param [String] msg
317
+ # @param [String] msg warning message text
253
318
  # @return [void]
254
319
  def warn_once(msg)
255
320
  return unless ENV['DOCSCRIBE_RBS_DEBUG'] == '1'