docscribe 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +296 -2
- data/lib/docscribe/cli/config_builder.rb +17 -5
- data/lib/docscribe/cli/generate.rb +309 -0
- data/lib/docscribe/cli/options.rb +8 -1
- data/lib/docscribe/cli/run.rb +52 -51
- data/lib/docscribe/cli.rb +8 -2
- data/lib/docscribe/config/defaults.rb +3 -0
- data/lib/docscribe/config/filtering.rb +2 -2
- data/lib/docscribe/config/plugin.rb +29 -0
- data/lib/docscribe/config/template.rb +17 -0
- data/lib/docscribe/config.rb +1 -0
- data/lib/docscribe/infer/returns.rb +71 -10
- data/lib/docscribe/infer.rb +7 -2
- data/lib/docscribe/inline_rewriter/collector.rb +144 -97
- data/lib/docscribe/inline_rewriter/doc_builder.rb +192 -47
- data/lib/docscribe/inline_rewriter.rb +215 -56
- data/lib/docscribe/plugin/base/collector_plugin.rb +55 -0
- data/lib/docscribe/plugin/base/tag_plugin.rb +38 -0
- data/lib/docscribe/plugin/context.rb +38 -0
- data/lib/docscribe/plugin/registry.rb +69 -0
- data/lib/docscribe/plugin/tag.rb +23 -0
- data/lib/docscribe/plugin.rb +58 -0
- data/lib/docscribe/types/rbs/collection_loader.rb +50 -0
- data/lib/docscribe/types/rbs/provider.rb +3 -0
- data/lib/docscribe/version.rb +1 -1
- metadata +13 -5
|
@@ -56,8 +56,11 @@ module Docscribe
|
|
|
56
56
|
# @param [Parser::AST::Node] node `:def` or `:defs` node
|
|
57
57
|
# @param [String] fallback_type type used when inference is uncertain
|
|
58
58
|
# @param [Boolean] nil_as_optional whether `nil` unions should be rendered as optional types
|
|
59
|
+
# @param [nil] core_rbs_provider Param documentation.
|
|
60
|
+
# @param [nil] param_types Param documentation.
|
|
59
61
|
# @return [Hash]
|
|
60
|
-
def returns_spec_from_node(node, fallback_type: FALLBACK_TYPE, nil_as_optional: true
|
|
62
|
+
def returns_spec_from_node(node, fallback_type: FALLBACK_TYPE, nil_as_optional: true, core_rbs_provider: nil,
|
|
63
|
+
param_types: nil)
|
|
61
64
|
body =
|
|
62
65
|
case node.type
|
|
63
66
|
when :def then node.children[2]
|
|
@@ -70,7 +73,8 @@ module Docscribe
|
|
|
70
73
|
if body.type == :rescue
|
|
71
74
|
main_body = body.children[0]
|
|
72
75
|
spec[:normal] =
|
|
73
|
-
last_expr_type(main_body, fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
76
|
+
last_expr_type(main_body, fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
77
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types) || FALLBACK_TYPE
|
|
74
78
|
|
|
75
79
|
body.children.each do |ch|
|
|
76
80
|
next unless ch.is_a?(Parser::AST::Node) && ch.type == :resbody
|
|
@@ -78,13 +82,15 @@ module Docscribe
|
|
|
78
82
|
exc_list, _asgn, rescue_body = *ch
|
|
79
83
|
exc_names = Raises.exception_names_from_rescue_list(exc_list)
|
|
80
84
|
rtype =
|
|
81
|
-
last_expr_type(rescue_body, fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
85
|
+
last_expr_type(rescue_body, fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
86
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types) ||
|
|
82
87
|
fallback_type
|
|
83
88
|
spec[:rescues] << [exc_names, rtype]
|
|
84
89
|
end
|
|
85
90
|
else
|
|
86
91
|
spec[:normal] =
|
|
87
|
-
last_expr_type(body, fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
92
|
+
last_expr_type(body, fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
93
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types) || FALLBACK_TYPE
|
|
88
94
|
end
|
|
89
95
|
|
|
90
96
|
spec
|
|
@@ -98,30 +104,38 @@ module Docscribe
|
|
|
98
104
|
# - `case` expressions
|
|
99
105
|
# - explicit `return`
|
|
100
106
|
# - literal-like expressions via {Literals.type_from_literal}
|
|
107
|
+
# - method calls with RBS core type lookup
|
|
101
108
|
#
|
|
102
109
|
# @note module_function: when included, also defines #last_expr_type (instance visibility: private)
|
|
103
110
|
# @param [Parser::AST::Node, nil] node expression node
|
|
104
111
|
# @param [String] fallback_type type used when inference is uncertain
|
|
105
112
|
# @param [Boolean] nil_as_optional whether `nil` unions should be rendered as optional types
|
|
113
|
+
# @param [Object, nil] core_rbs_provider optional RBS provider for core type lookup
|
|
114
|
+
# @param [Hash, nil] param_types parameter name -> type map for lvar resolution
|
|
106
115
|
# @return [String, nil]
|
|
107
|
-
def last_expr_type(node, fallback_type:, nil_as_optional:)
|
|
116
|
+
def last_expr_type(node, fallback_type:, nil_as_optional:, core_rbs_provider: nil, param_types: nil)
|
|
108
117
|
return nil unless node
|
|
109
118
|
|
|
110
119
|
case node.type
|
|
111
120
|
when :begin
|
|
112
|
-
last_expr_type(node.children.last, fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
121
|
+
last_expr_type(node.children.last, fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
122
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types)
|
|
113
123
|
|
|
114
124
|
when :if
|
|
115
|
-
t = last_expr_type(node.children[1], fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
116
|
-
|
|
125
|
+
t = last_expr_type(node.children[1], fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
126
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types)
|
|
127
|
+
e = last_expr_type(node.children[2], fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
128
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types)
|
|
117
129
|
unify_types(t, e, fallback_type: fallback_type, nil_as_optional: nil_as_optional)
|
|
118
130
|
|
|
119
131
|
when :case
|
|
120
132
|
branches = node.children[1..].compact.flat_map do |child|
|
|
121
133
|
if child.type == :when
|
|
122
|
-
last_expr_type(child.children.last, fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
134
|
+
last_expr_type(child.children.last, fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
135
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types)
|
|
123
136
|
else
|
|
124
|
-
last_expr_type(child, fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
137
|
+
last_expr_type(child, fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
138
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types)
|
|
125
139
|
end
|
|
126
140
|
end.compact
|
|
127
141
|
|
|
@@ -136,11 +150,58 @@ module Docscribe
|
|
|
136
150
|
when :return
|
|
137
151
|
Literals.type_from_literal(node.children.first, fallback_type: fallback_type)
|
|
138
152
|
|
|
153
|
+
when :send
|
|
154
|
+
recv = node.children[0]
|
|
155
|
+
meth = node.children[1]
|
|
156
|
+
|
|
157
|
+
# Try to resolve return type from RBS core for method calls
|
|
158
|
+
if core_rbs_provider && recv&.type == :send
|
|
159
|
+
# Chained call: arg.to_i.positive?
|
|
160
|
+
inner_type = last_expr_type(recv, fallback_type: nil, nil_as_optional: false,
|
|
161
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types)
|
|
162
|
+
if inner_type
|
|
163
|
+
rbs_type = resolve_rbs_return_type(inner_type, meth, core_rbs_provider)
|
|
164
|
+
return rbs_type unless rbs_type == FALLBACK_TYPE
|
|
165
|
+
end
|
|
166
|
+
elsif core_rbs_provider && recv&.type == :lvar
|
|
167
|
+
# Direct call: arg.positive?
|
|
168
|
+
lvar_name = recv.children.first
|
|
169
|
+
if lvar_name && param_types
|
|
170
|
+
recv_type = param_types[lvar_name.to_s]
|
|
171
|
+
if recv_type
|
|
172
|
+
rbs_type = resolve_rbs_return_type(recv_type, meth, core_rbs_provider)
|
|
173
|
+
return rbs_type unless rbs_type == FALLBACK_TYPE
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
Literals.type_from_literal(node, fallback_type: fallback_type)
|
|
179
|
+
|
|
139
180
|
else
|
|
140
181
|
Literals.type_from_literal(node, fallback_type: fallback_type)
|
|
141
182
|
end
|
|
142
183
|
end
|
|
143
184
|
|
|
185
|
+
# Resolve a return type from core RBS for a method call.
|
|
186
|
+
#
|
|
187
|
+
# @note module_function: when included, also defines #resolve_rbs_return_type (instance visibility: private)
|
|
188
|
+
# @private
|
|
189
|
+
# @param [String] container_type e.g. "Numeric", "String"
|
|
190
|
+
# @param [Symbol] method_name e.g. :positive?
|
|
191
|
+
# @param [Object, nil] core_rbs_provider RBS provider
|
|
192
|
+
# @return [String] FALLBACK_TYPE if lookup fails
|
|
193
|
+
def resolve_rbs_return_type(container_type, method_name, core_rbs_provider)
|
|
194
|
+
return FALLBACK_TYPE unless core_rbs_provider
|
|
195
|
+
|
|
196
|
+
sig = core_rbs_provider.signature_for(
|
|
197
|
+
container: container_type,
|
|
198
|
+
scope: :instance,
|
|
199
|
+
name: method_name
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
sig&.return_type || FALLBACK_TYPE
|
|
203
|
+
end
|
|
204
|
+
|
|
144
205
|
# Unify two inferred types into a single type string.
|
|
145
206
|
#
|
|
146
207
|
# Rules:
|
data/lib/docscribe/infer.rb
CHANGED
|
@@ -93,12 +93,17 @@ module Docscribe
|
|
|
93
93
|
# @param [Parser::AST::Node] node
|
|
94
94
|
# @param [String] fallback_type
|
|
95
95
|
# @param [Boolean] nil_as_optional
|
|
96
|
+
# @param [nil] core_rbs_provider Param documentation.
|
|
97
|
+
# @param [nil] param_types Param documentation.
|
|
96
98
|
# @return [Hash]
|
|
97
|
-
def returns_spec_from_node(node, fallback_type: FALLBACK_TYPE, nil_as_optional: true
|
|
99
|
+
def returns_spec_from_node(node, fallback_type: FALLBACK_TYPE, nil_as_optional: true, core_rbs_provider: nil,
|
|
100
|
+
param_types: nil)
|
|
98
101
|
Returns.returns_spec_from_node(
|
|
99
102
|
node,
|
|
100
103
|
fallback_type: fallback_type,
|
|
101
|
-
nil_as_optional: nil_as_optional
|
|
104
|
+
nil_as_optional: nil_as_optional,
|
|
105
|
+
core_rbs_provider: core_rbs_provider,
|
|
106
|
+
param_types: param_types
|
|
102
107
|
)
|
|
103
108
|
end
|
|
104
109
|
|
|
@@ -158,10 +158,10 @@ module Docscribe
|
|
|
158
158
|
# @return [Array<AttrInsertion>]
|
|
159
159
|
attr_reader :attr_insertions
|
|
160
160
|
|
|
161
|
-
#
|
|
161
|
+
# Create a collector for the given source buffer.
|
|
162
162
|
#
|
|
163
|
-
# @param [Parser::Source::Buffer] buffer
|
|
164
|
-
# @return [
|
|
163
|
+
# @param [Parser::Source::Buffer] buffer source buffer for anchor location lookups
|
|
164
|
+
# @return [Collector]
|
|
165
165
|
def initialize(buffer)
|
|
166
166
|
super()
|
|
167
167
|
@buffer = buffer
|
|
@@ -226,10 +226,13 @@ module Docscribe
|
|
|
226
226
|
node
|
|
227
227
|
end
|
|
228
228
|
|
|
229
|
-
#
|
|
229
|
+
# Process a constant assignment (e.g. `FOO = ...` or `Foo::BAR = ...`).
|
|
230
230
|
#
|
|
231
|
-
#
|
|
232
|
-
#
|
|
231
|
+
# If the value is a `Struct.new` call, extracts attribute insertions first.
|
|
232
|
+
# Then continues processing child nodes.
|
|
233
|
+
#
|
|
234
|
+
# @param [Parser::AST::Node] node a `:casgn` node
|
|
235
|
+
# @return [Parser::AST::Node] the original node
|
|
233
236
|
def on_casgn(node)
|
|
234
237
|
return node if process_struct_casgn(node)
|
|
235
238
|
|
|
@@ -240,15 +243,52 @@ module Docscribe
|
|
|
240
243
|
node
|
|
241
244
|
end
|
|
242
245
|
|
|
246
|
+
# Enter a top-level method definition and collect it as a documentation target.
|
|
247
|
+
#
|
|
248
|
+
# Top-level methods implicitly belong to +Object+. This handler ensures
|
|
249
|
+
# that +def foo+ declared outside of any class or module is still picked
|
|
250
|
+
# up by the collector.
|
|
251
|
+
#
|
|
252
|
+
# @param [Parser::AST::Node] node
|
|
253
|
+
# @return [Parser::AST::Node]
|
|
254
|
+
def on_def(node)
|
|
255
|
+
return node unless @name_stack.empty?
|
|
256
|
+
|
|
257
|
+
ctx = VisibilityCtx.new
|
|
258
|
+
ctx.container_is_module = false
|
|
259
|
+
process_stmt(node, ctx)
|
|
260
|
+
node
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Enter a top-level singleton method definition and collect it as a documentation target.
|
|
264
|
+
#
|
|
265
|
+
# Handles the case of +def self.foo+ declared at the top level, outside
|
|
266
|
+
# of any class or module body.
|
|
267
|
+
#
|
|
268
|
+
# @param [Parser::AST::Node] node
|
|
269
|
+
# @return [Parser::AST::Node]
|
|
270
|
+
def on_defs(node)
|
|
271
|
+
return node unless @name_stack.empty?
|
|
272
|
+
|
|
273
|
+
ctx = VisibilityCtx.new
|
|
274
|
+
ctx.container_is_module = false
|
|
275
|
+
process_stmt(node, ctx)
|
|
276
|
+
node
|
|
277
|
+
end
|
|
278
|
+
|
|
243
279
|
private
|
|
244
280
|
|
|
245
|
-
#
|
|
281
|
+
# Process a single AST node for documentation insertion targets.
|
|
282
|
+
#
|
|
283
|
+
# Dispatches to specific handlers based on node type (`:def`, `:defs`,
|
|
284
|
+
# `:sclass`, `:send` with visibility modifiers, etc.) and records
|
|
285
|
+
# `Insertion` objects for methods that need documentation.
|
|
246
286
|
#
|
|
247
287
|
# @private
|
|
248
|
-
# @param [
|
|
249
|
-
# @param [
|
|
250
|
-
# @param [nil] pending_sig_anchor
|
|
251
|
-
# @return [
|
|
288
|
+
# @param [Parser::AST::Node, nil] node the AST node to process
|
|
289
|
+
# @param [VisibilityCtx] ctx current visibility and container context
|
|
290
|
+
# @param [Parser::AST::Node, nil] pending_sig_anchor Sorbet `sig` node waiting for a method
|
|
291
|
+
# @return [void]
|
|
252
292
|
def process_stmt(node, ctx, pending_sig_anchor: nil)
|
|
253
293
|
return unless node
|
|
254
294
|
|
|
@@ -349,12 +389,12 @@ module Docscribe
|
|
|
349
389
|
end
|
|
350
390
|
end
|
|
351
391
|
|
|
352
|
-
#
|
|
392
|
+
# Check if a class inherits from Struct.new and extract attribute insertions.
|
|
353
393
|
#
|
|
354
394
|
# @private
|
|
355
|
-
# @param [
|
|
356
|
-
# @param [
|
|
357
|
-
# @return [
|
|
395
|
+
# @param [Parser::AST::Node] node the class declaration node
|
|
396
|
+
# @param [Parser::AST::Node, nil] super_node the superclass expression
|
|
397
|
+
# @return [void]
|
|
358
398
|
def process_struct_class(node, super_node)
|
|
359
399
|
return unless struct_new_node?(super_node)
|
|
360
400
|
|
|
@@ -371,11 +411,11 @@ module Docscribe
|
|
|
371
411
|
)
|
|
372
412
|
end
|
|
373
413
|
|
|
374
|
-
#
|
|
414
|
+
# Check if a constant assignment is `Struct.new` and extract attribute insertions.
|
|
375
415
|
#
|
|
376
416
|
# @private
|
|
377
|
-
# @param [
|
|
378
|
-
# @return [Boolean]
|
|
417
|
+
# @param [Parser::AST::Node] node a `:casgn` node
|
|
418
|
+
# @return [Boolean] true if the node was handled as a struct definition
|
|
379
419
|
def process_struct_casgn(node)
|
|
380
420
|
_scope, _name, value = *node
|
|
381
421
|
return false unless struct_new_node?(value)
|
|
@@ -395,11 +435,11 @@ module Docscribe
|
|
|
395
435
|
true
|
|
396
436
|
end
|
|
397
437
|
|
|
398
|
-
#
|
|
438
|
+
# Check if a node represents a `Struct.new` call.
|
|
399
439
|
#
|
|
400
440
|
# @private
|
|
401
|
-
# @param [
|
|
402
|
-
# @return [
|
|
441
|
+
# @param [Parser::AST::Node, nil] node an AST node
|
|
442
|
+
# @return [Boolean]
|
|
403
443
|
def struct_new_node?(node)
|
|
404
444
|
return false unless node.is_a?(Parser::AST::Node)
|
|
405
445
|
return false unless node.type == :send
|
|
@@ -412,11 +452,11 @@ module Docscribe
|
|
|
412
452
|
%w[Struct ::Struct].include?(recv_name)
|
|
413
453
|
end
|
|
414
454
|
|
|
415
|
-
#
|
|
455
|
+
# Extract member names from a `Struct.new` call, stripping the type string argument if present.
|
|
416
456
|
#
|
|
417
457
|
# @private
|
|
418
|
-
# @param [
|
|
419
|
-
# @return [
|
|
458
|
+
# @param [Parser::AST::Node] struct_new_node a `:send` node representing `Struct.new`
|
|
459
|
+
# @return [Array<Symbol>] extracted member names
|
|
420
460
|
def extract_struct_member_names(struct_new_node)
|
|
421
461
|
_recv, _meth, *args = *struct_new_node
|
|
422
462
|
|
|
@@ -429,11 +469,11 @@ module Docscribe
|
|
|
429
469
|
args.map { |arg| extract_name_sym(arg) }.compact
|
|
430
470
|
end
|
|
431
471
|
|
|
432
|
-
#
|
|
472
|
+
# Build the container name for a struct constant assignment.
|
|
433
473
|
#
|
|
434
474
|
# @private
|
|
435
|
-
# @param [
|
|
436
|
-
# @return [
|
|
475
|
+
# @param [Parser::AST::Node] node a `:casgn` node
|
|
476
|
+
# @return [String] the fully qualified container name
|
|
437
477
|
def struct_container_name(node)
|
|
438
478
|
scope, name, _value = *node
|
|
439
479
|
|
|
@@ -449,12 +489,12 @@ module Docscribe
|
|
|
449
489
|
[prefix, name.to_s].compact.reject(&:empty?).join('::')
|
|
450
490
|
end
|
|
451
491
|
|
|
452
|
-
#
|
|
492
|
+
# Detect `extend self` calls inside a module and persist the state.
|
|
453
493
|
#
|
|
454
494
|
# @private
|
|
455
|
-
# @param [
|
|
456
|
-
# @param [
|
|
457
|
-
# @return [Boolean]
|
|
495
|
+
# @param [Parser::AST::Node] node a `:send` node
|
|
496
|
+
# @param [VisibilityCtx] ctx current visibility context
|
|
497
|
+
# @return [Boolean] true if `extend self` was detected
|
|
458
498
|
def process_extend_self_send(node, ctx)
|
|
459
499
|
recv, meth, *args = *node
|
|
460
500
|
|
|
@@ -473,32 +513,32 @@ module Docscribe
|
|
|
473
513
|
true
|
|
474
514
|
end
|
|
475
515
|
|
|
476
|
-
#
|
|
516
|
+
# Check whether `extend self` semantics apply at the current position.
|
|
477
517
|
#
|
|
478
518
|
# @private
|
|
479
|
-
# @param [
|
|
480
|
-
# @return [
|
|
519
|
+
# @param [VisibilityCtx] ctx current visibility context
|
|
520
|
+
# @return [Boolean]
|
|
481
521
|
def extend_self_applies?(ctx)
|
|
482
522
|
ctx.container_is_module && ctx.extend_self && !ctx.inside_sclass
|
|
483
523
|
end
|
|
484
524
|
|
|
485
|
-
#
|
|
525
|
+
# Check if a node is a constant or `::` (cbase) receiver.
|
|
486
526
|
#
|
|
487
527
|
# @private
|
|
488
|
-
# @param [
|
|
489
|
-
# @return [
|
|
528
|
+
# @param [Parser::AST::Node, nil] node an AST node
|
|
529
|
+
# @return [Boolean]
|
|
490
530
|
def const_receiver?(node)
|
|
491
531
|
return false unless node.is_a?(Parser::AST::Node)
|
|
492
532
|
|
|
493
533
|
%i[const cbase].include?(node.type)
|
|
494
534
|
end
|
|
495
535
|
|
|
496
|
-
#
|
|
536
|
+
# Detect `attr_reader` / `attr_writer` / `attr_accessor` calls and record attribute insertions.
|
|
497
537
|
#
|
|
498
538
|
# @private
|
|
499
|
-
# @param [
|
|
500
|
-
# @param [
|
|
501
|
-
# @return [Boolean]
|
|
539
|
+
# @param [Parser::AST::Node] node a `:send` node
|
|
540
|
+
# @param [VisibilityCtx] ctx current visibility context
|
|
541
|
+
# @return [Boolean] true if the node was an attr_* call
|
|
502
542
|
def process_attr_send(node, ctx)
|
|
503
543
|
recv, meth, *args = *node
|
|
504
544
|
return false unless recv.nil? && %i[attr_reader attr_writer attr_accessor].include?(meth)
|
|
@@ -521,12 +561,12 @@ module Docscribe
|
|
|
521
561
|
true
|
|
522
562
|
end
|
|
523
563
|
|
|
524
|
-
#
|
|
564
|
+
# Detect `private_class_method` / `protected_class_method` / `public_class_method` and update class-level visibility.
|
|
525
565
|
#
|
|
526
566
|
# @private
|
|
527
|
-
# @param [
|
|
528
|
-
# @param [
|
|
529
|
-
# @return [Boolean]
|
|
567
|
+
# @param [Parser::AST::Node] node a `:send` node
|
|
568
|
+
# @param [VisibilityCtx] ctx current visibility context
|
|
569
|
+
# @return [Boolean] true if the node was a class visibility modifier
|
|
530
570
|
def process_class_method_visibility_send(node, ctx)
|
|
531
571
|
recv, meth, *args = *node
|
|
532
572
|
|
|
@@ -553,13 +593,17 @@ module Docscribe
|
|
|
553
593
|
true
|
|
554
594
|
end
|
|
555
595
|
|
|
556
|
-
#
|
|
596
|
+
# Detect `private` / `protected` / `public` calls and update visibility state.
|
|
597
|
+
#
|
|
598
|
+
# Handles both bare modifiers (no args) that change defaults, and named
|
|
599
|
+
# modifiers (`private :foo`) that retroactively update method visibility.
|
|
600
|
+
# Also handles inline modifiers (`private def foo`).
|
|
557
601
|
#
|
|
558
602
|
# @private
|
|
559
|
-
# @param [
|
|
560
|
-
# @param [
|
|
561
|
-
# @param [nil] pending_sig_anchor
|
|
562
|
-
# @return [
|
|
603
|
+
# @param [Parser::AST::Node] node a `:send` node
|
|
604
|
+
# @param [VisibilityCtx] ctx current visibility context
|
|
605
|
+
# @param [Parser::AST::Node, nil] pending_sig_anchor Sorbet `sig` node
|
|
606
|
+
# @return [void]
|
|
563
607
|
def process_visibility_send(node, ctx, pending_sig_anchor: nil)
|
|
564
608
|
recv, meth, *args = *node
|
|
565
609
|
return unless recv.nil? && %i[private protected public].include?(meth)
|
|
@@ -618,13 +662,13 @@ module Docscribe
|
|
|
618
662
|
end
|
|
619
663
|
end
|
|
620
664
|
|
|
621
|
-
#
|
|
665
|
+
# Retroactively update the included instance visibility for a module_function method.
|
|
622
666
|
#
|
|
623
667
|
# @private
|
|
624
|
-
# @param [
|
|
625
|
-
# @param [
|
|
626
|
-
# @param [
|
|
627
|
-
# @return [
|
|
668
|
+
# @param [Symbol] name_sym the method name
|
|
669
|
+
# @param [Symbol] visibility the new visibility (:public, :protected, :private)
|
|
670
|
+
# @param [String] container the container name
|
|
671
|
+
# @return [void]
|
|
628
672
|
def retroactively_set_included_instance_visibility_for_module_function(name_sym, visibility, container:)
|
|
629
673
|
@insertions.reverse_each do |ins|
|
|
630
674
|
next unless ins.container == container
|
|
@@ -637,14 +681,14 @@ module Docscribe
|
|
|
637
681
|
end
|
|
638
682
|
end
|
|
639
683
|
|
|
640
|
-
#
|
|
684
|
+
# Retroactively update the visibility of a previously collected method.
|
|
641
685
|
#
|
|
642
686
|
# @private
|
|
643
|
-
# @param [
|
|
644
|
-
# @param [
|
|
645
|
-
# @param [
|
|
646
|
-
# @param [
|
|
647
|
-
# @return [
|
|
687
|
+
# @param [Symbol] name_sym the method name
|
|
688
|
+
# @param [Symbol] visibility the new visibility
|
|
689
|
+
# @param [Symbol] scope the method scope (`:instance` or `:class`)
|
|
690
|
+
# @param [String] container the container name
|
|
691
|
+
# @return [void]
|
|
648
692
|
def retroactively_set_visibility(name_sym, visibility, scope:, container:)
|
|
649
693
|
@insertions.reverse_each do |ins|
|
|
650
694
|
next unless ins.container == container
|
|
@@ -664,24 +708,24 @@ module Docscribe
|
|
|
664
708
|
end
|
|
665
709
|
end
|
|
666
710
|
|
|
667
|
-
#
|
|
711
|
+
# Check if `module_function` semantics apply to a method at the current position.
|
|
668
712
|
#
|
|
669
713
|
# @private
|
|
670
|
-
# @param [
|
|
671
|
-
# @param [
|
|
672
|
-
# @return [
|
|
714
|
+
# @param [VisibilityCtx] ctx current visibility context
|
|
715
|
+
# @param [Symbol] name the method name
|
|
716
|
+
# @return [Boolean]
|
|
673
717
|
def module_function_applies?(ctx, name)
|
|
674
718
|
return false if ctx.inside_sclass
|
|
675
719
|
|
|
676
720
|
ctx.module_function_default || ctx.module_function_explicit[name]
|
|
677
721
|
end
|
|
678
722
|
|
|
679
|
-
#
|
|
723
|
+
# Detect `module_function` calls (bare or named) and update visibility state.
|
|
680
724
|
#
|
|
681
725
|
# @private
|
|
682
|
-
# @param [
|
|
683
|
-
# @param [
|
|
684
|
-
# @return [Boolean]
|
|
726
|
+
# @param [Parser::AST::Node] node a `:send` node
|
|
727
|
+
# @param [VisibilityCtx] ctx current visibility context
|
|
728
|
+
# @return [Boolean] true if the node was a `module_function` call
|
|
685
729
|
def process_module_function_send(node, ctx)
|
|
686
730
|
recv, meth, *args = *node
|
|
687
731
|
return false unless recv.nil? && meth == :module_function
|
|
@@ -701,21 +745,21 @@ module Docscribe
|
|
|
701
745
|
true
|
|
702
746
|
end
|
|
703
747
|
|
|
704
|
-
#
|
|
748
|
+
# Get the effective container name, using `container_override` when set.
|
|
705
749
|
#
|
|
706
750
|
# @private
|
|
707
|
-
# @param [
|
|
708
|
-
# @return [
|
|
751
|
+
# @param [VisibilityCtx] ctx current visibility context
|
|
752
|
+
# @return [String] the container name
|
|
709
753
|
def container_for(ctx)
|
|
710
754
|
ctx.container_override || current_container
|
|
711
755
|
end
|
|
712
756
|
|
|
713
|
-
#
|
|
757
|
+
# Retroactively promote a previously collected instance method to a class method under module_function.
|
|
714
758
|
#
|
|
715
759
|
# @private
|
|
716
|
-
# @param [
|
|
717
|
-
# @param [
|
|
718
|
-
# @return [
|
|
760
|
+
# @param [Symbol] name_sym the method name
|
|
761
|
+
# @param [String] container the container name
|
|
762
|
+
# @return [void]
|
|
719
763
|
def retroactively_promote_module_function(name_sym, container:)
|
|
720
764
|
@insertions.reverse_each do |ins|
|
|
721
765
|
next unless ins.container == container
|
|
@@ -730,20 +774,20 @@ module Docscribe
|
|
|
730
774
|
end
|
|
731
775
|
end
|
|
732
776
|
|
|
733
|
-
#
|
|
777
|
+
# Check if a node is a `self` literal.
|
|
734
778
|
#
|
|
735
779
|
# @private
|
|
736
|
-
# @param [
|
|
737
|
-
# @return [
|
|
780
|
+
# @param [Parser::AST::Node, nil] node an AST node
|
|
781
|
+
# @return [Boolean]
|
|
738
782
|
def self_node?(node)
|
|
739
783
|
node && node.type == :self
|
|
740
784
|
end
|
|
741
785
|
|
|
742
|
-
#
|
|
786
|
+
# Extract a Ruby symbol name from an AST node (`:sym` or `:str`).
|
|
743
787
|
#
|
|
744
788
|
# @private
|
|
745
|
-
# @param [
|
|
746
|
-
# @return [
|
|
789
|
+
# @param [Parser::AST::Node] arg an AST node
|
|
790
|
+
# @return [Symbol, nil] the extracted name or nil
|
|
747
791
|
def extract_name_sym(arg)
|
|
748
792
|
case arg.type
|
|
749
793
|
when :sym then arg.children.first
|
|
@@ -751,11 +795,11 @@ module Docscribe
|
|
|
751
795
|
end
|
|
752
796
|
end
|
|
753
797
|
|
|
754
|
-
#
|
|
798
|
+
# Build the fully qualified name for a constant node.
|
|
755
799
|
#
|
|
756
800
|
# @private
|
|
757
|
-
# @param [
|
|
758
|
-
# @return [
|
|
801
|
+
# @param [Parser::AST::Node, nil] node a `:const` or `:cbase` node
|
|
802
|
+
# @return [String] the resolved constant name
|
|
759
803
|
def const_name(node)
|
|
760
804
|
return 'Object' unless node
|
|
761
805
|
|
|
@@ -771,20 +815,23 @@ module Docscribe
|
|
|
771
815
|
end
|
|
772
816
|
end
|
|
773
817
|
|
|
774
|
-
#
|
|
818
|
+
# Get the current container name from the name stack.
|
|
775
819
|
#
|
|
776
820
|
# @private
|
|
777
|
-
# @return [Object
|
|
821
|
+
# @return [String] the current container (e.g. `"MyModule::MyClass"`) or `"Object"` if empty
|
|
778
822
|
def current_container
|
|
779
823
|
@name_stack.empty? ? 'Object' : @name_stack.join('::')
|
|
780
824
|
end
|
|
781
825
|
|
|
782
|
-
#
|
|
826
|
+
# Process all nodes in a class/module body for documentation insertion targets.
|
|
827
|
+
#
|
|
828
|
+
# Handles Sorbet `sig` nodes by deferring them as pending anchors for the
|
|
829
|
+
# next method definition.
|
|
783
830
|
#
|
|
784
831
|
# @private
|
|
785
|
-
# @param [
|
|
786
|
-
# @param [
|
|
787
|
-
# @return [
|
|
832
|
+
# @param [Parser::AST::Node, nil] body the body node
|
|
833
|
+
# @param [VisibilityCtx] ctx current visibility context
|
|
834
|
+
# @return [void]
|
|
788
835
|
def process_body(body, ctx)
|
|
789
836
|
return unless body
|
|
790
837
|
|
|
@@ -802,11 +849,11 @@ module Docscribe
|
|
|
802
849
|
end
|
|
803
850
|
end
|
|
804
851
|
|
|
805
|
-
#
|
|
852
|
+
# Check if a node is a Sorbet `sig` declaration (bare `sig` send or `sig { ... }` block).
|
|
806
853
|
#
|
|
807
854
|
# @private
|
|
808
|
-
# @param [
|
|
809
|
-
# @return [
|
|
855
|
+
# @param [Parser::AST::Node, nil] node an AST node
|
|
856
|
+
# @return [Boolean]
|
|
810
857
|
def sorbet_sig_node?(node)
|
|
811
858
|
return false unless node.is_a?(Parser::AST::Node)
|
|
812
859
|
|
|
@@ -825,11 +872,11 @@ module Docscribe
|
|
|
825
872
|
end
|
|
826
873
|
end
|
|
827
874
|
|
|
828
|
-
#
|
|
875
|
+
# Promote instance methods to class methods for a container under `extend self`.
|
|
829
876
|
#
|
|
830
877
|
# @private
|
|
831
|
-
# @param [
|
|
832
|
-
# @return [
|
|
878
|
+
# @param [String] container the container name
|
|
879
|
+
# @return [void]
|
|
833
880
|
def promote_extend_self_container(container:)
|
|
834
881
|
@insertions.each do |ins|
|
|
835
882
|
next unless ins.container == container
|