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.
- checksums.yaml +4 -4
- data/README.md +588 -104
- data/lib/docscribe/cli/check_for_comments.rb +183 -0
- data/lib/docscribe/cli/config_builder.rb +180 -36
- data/lib/docscribe/cli/formatters/json.rb +294 -0
- data/lib/docscribe/cli/formatters/sarif.rb +235 -0
- data/lib/docscribe/cli/formatters/text.rb +208 -0
- data/lib/docscribe/cli/formatters.rb +26 -0
- data/lib/docscribe/cli/generate.rb +296 -125
- data/lib/docscribe/cli/init.rb +58 -14
- data/lib/docscribe/cli/options.rb +410 -133
- data/lib/docscribe/cli/rbs_gen.rb +529 -0
- data/lib/docscribe/cli/run.rb +503 -189
- data/lib/docscribe/cli/sigs.rb +366 -0
- data/lib/docscribe/cli/update_types.rb +103 -0
- data/lib/docscribe/cli.rb +35 -9
- data/lib/docscribe/config/defaults.rb +16 -12
- data/lib/docscribe/config/emit.rb +18 -0
- data/lib/docscribe/config/filtering.rb +37 -31
- data/lib/docscribe/config/loader.rb +20 -13
- data/lib/docscribe/config/plugin.rb +2 -1
- data/lib/docscribe/config/rbs.rb +68 -27
- data/lib/docscribe/config/sorbet.rb +40 -17
- data/lib/docscribe/config/sorting.rb +2 -1
- data/lib/docscribe/config/template.rb +10 -1
- data/lib/docscribe/config/utils.rb +12 -9
- data/lib/docscribe/config.rb +3 -4
- data/lib/docscribe/infer/ast_walk.rb +1 -1
- data/lib/docscribe/infer/constants.rb +15 -0
- data/lib/docscribe/infer/literals.rb +39 -26
- data/lib/docscribe/infer/names.rb +24 -16
- data/lib/docscribe/infer/params.rb +57 -13
- data/lib/docscribe/infer/raises.rb +23 -15
- data/lib/docscribe/infer/returns.rb +784 -199
- data/lib/docscribe/infer.rb +28 -28
- data/lib/docscribe/inline_rewriter/collector.rb +816 -430
- data/lib/docscribe/inline_rewriter/doc_block.rb +323 -150
- data/lib/docscribe/inline_rewriter/doc_builder.rb +1837 -648
- data/lib/docscribe/inline_rewriter/source_helpers.rb +119 -71
- data/lib/docscribe/inline_rewriter/tag_sorter.rb +165 -107
- data/lib/docscribe/inline_rewriter.rb +1144 -727
- data/lib/docscribe/parsing.rb +29 -10
- data/lib/docscribe/plugin/base/collector_plugin.rb +3 -3
- data/lib/docscribe/plugin/base/tag_plugin.rb +1 -2
- data/lib/docscribe/plugin/context.rb +28 -18
- data/lib/docscribe/plugin/registry.rb +49 -23
- data/lib/docscribe/plugin/tag.rb +9 -14
- data/lib/docscribe/plugin.rb +54 -22
- data/lib/docscribe/types/provider_chain.rb +4 -2
- data/lib/docscribe/types/rbs/collection_loader.rb +2 -3
- data/lib/docscribe/types/rbs/provider.rb +127 -62
- data/lib/docscribe/types/rbs/type_formatter.rb +286 -77
- data/lib/docscribe/types/signature.rb +22 -42
- data/lib/docscribe/types/sorbet/base_provider.rb +51 -27
- data/lib/docscribe/types/sorbet/rbi_provider.rb +3 -3
- data/lib/docscribe/types/sorbet/source_provider.rb +3 -2
- data/lib/docscribe/types/yard/formatter.rb +100 -0
- data/lib/docscribe/types/yard/parser.rb +240 -0
- data/lib/docscribe/types/yard/types.rb +52 -0
- data/lib/docscribe/version.rb +1 -1
- metadata +34 -2
|
@@ -19,41 +19,59 @@ module Docscribe
|
|
|
19
19
|
# - receiver-based containers (`def Foo.bar`, `class << Foo`)
|
|
20
20
|
# - Sorbet-aware anchoring for methods with leading `sig` declarations
|
|
21
21
|
class Collector < Parser::AST::Processor
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
# @!attribute scope
|
|
27
|
-
# @return [Symbol]
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
# @!attribute
|
|
31
|
-
# @return [
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# @!attribute
|
|
35
|
-
# @return [
|
|
36
|
-
#
|
|
37
|
-
#
|
|
22
|
+
# @!attribute [rw] node
|
|
23
|
+
# @return [Parser::AST::Node]
|
|
24
|
+
# @param [Parser::AST::Node] value
|
|
25
|
+
#
|
|
26
|
+
# @!attribute [rw] scope
|
|
27
|
+
# @return [Symbol]
|
|
28
|
+
# @param [Symbol] value
|
|
29
|
+
#
|
|
30
|
+
# @!attribute [rw] visibility
|
|
31
|
+
# @return [Symbol]
|
|
32
|
+
# @param [Symbol] value
|
|
33
|
+
#
|
|
34
|
+
# @!attribute [rw] container
|
|
35
|
+
# @return [String]
|
|
36
|
+
# @param [String] value
|
|
37
|
+
#
|
|
38
|
+
# @!attribute [rw] module_function
|
|
39
|
+
# @return [Boolean, Symbol, nil]
|
|
40
|
+
# @param [Boolean, Symbol, nil] value
|
|
41
|
+
#
|
|
42
|
+
# @!attribute [rw] included_instance_visibility
|
|
43
|
+
# @return [Symbol, nil]
|
|
44
|
+
# @param [Symbol, nil] value
|
|
45
|
+
#
|
|
46
|
+
# @!attribute [rw] anchor_node
|
|
47
|
+
# @return [Parser::AST::Node]
|
|
48
|
+
# @param [Parser::AST::Node] value
|
|
38
49
|
Insertion = Struct.new(:node, :scope, :visibility, :container, :module_function, :included_instance_visibility,
|
|
39
50
|
:anchor_node)
|
|
40
51
|
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# @!attribute
|
|
46
|
-
# @return [
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
# @!attribute visibility
|
|
50
|
-
# @return [Symbol]
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
# @!attribute
|
|
54
|
-
# @return [
|
|
55
|
-
#
|
|
56
|
-
#
|
|
52
|
+
# @!attribute [rw] node
|
|
53
|
+
# @return [Parser::AST::Node]
|
|
54
|
+
# @param [Parser::AST::Node] value
|
|
55
|
+
#
|
|
56
|
+
# @!attribute [rw] scope
|
|
57
|
+
# @return [Symbol]
|
|
58
|
+
# @param [Symbol] value
|
|
59
|
+
#
|
|
60
|
+
# @!attribute [rw] visibility
|
|
61
|
+
# @return [Symbol]
|
|
62
|
+
# @param [Symbol] value
|
|
63
|
+
#
|
|
64
|
+
# @!attribute [rw] container
|
|
65
|
+
# @return [String]
|
|
66
|
+
# @param [String] value
|
|
67
|
+
#
|
|
68
|
+
# @!attribute [rw] access
|
|
69
|
+
# @return [Symbol]
|
|
70
|
+
# @param [Symbol] value
|
|
71
|
+
#
|
|
72
|
+
# @!attribute [rw] names
|
|
73
|
+
# @return [Array<Symbol>]
|
|
74
|
+
# @param [Array<Symbol>] value
|
|
57
75
|
AttrInsertion = Struct.new(:node, :scope, :visibility, :container, :access, :names)
|
|
58
76
|
|
|
59
77
|
# Tracks visibility and container state while walking a class/module body.
|
|
@@ -66,50 +84,50 @@ module Docscribe
|
|
|
66
84
|
# - retroactive visibility updates
|
|
67
85
|
class VisibilityCtx
|
|
68
86
|
# @!attribute [rw] default_instance_vis
|
|
69
|
-
# @return [
|
|
70
|
-
# @param
|
|
87
|
+
# @return [Symbol]
|
|
88
|
+
# @param [Symbol] value
|
|
71
89
|
attr_accessor :default_instance_vis
|
|
72
90
|
|
|
73
91
|
# @!attribute [rw] default_class_vis
|
|
74
|
-
# @return [
|
|
75
|
-
# @param
|
|
92
|
+
# @return [Symbol]
|
|
93
|
+
# @param [Symbol] value
|
|
76
94
|
attr_accessor :default_class_vis
|
|
77
95
|
|
|
78
96
|
# @!attribute [rw] inside_sclass
|
|
79
|
-
# @return [
|
|
80
|
-
# @param
|
|
97
|
+
# @return [Boolean]
|
|
98
|
+
# @param [Boolean] value
|
|
81
99
|
attr_accessor :inside_sclass
|
|
82
100
|
|
|
83
101
|
# @!attribute [rw] module_function_default
|
|
84
|
-
# @return [
|
|
85
|
-
# @param
|
|
102
|
+
# @return [Boolean]
|
|
103
|
+
# @param [Boolean] value
|
|
86
104
|
attr_accessor :module_function_default
|
|
87
105
|
|
|
88
106
|
# @!attribute [rw] container_override
|
|
89
|
-
# @return [
|
|
90
|
-
# @param
|
|
107
|
+
# @return [String?]
|
|
108
|
+
# @param [String?] value
|
|
91
109
|
attr_accessor :container_override
|
|
92
110
|
|
|
93
111
|
# @!attribute [r] explicit_instance
|
|
94
|
-
# @return [
|
|
112
|
+
# @return [Hash<Symbol, Symbol>]
|
|
95
113
|
attr_reader :explicit_instance
|
|
96
114
|
|
|
97
115
|
# @!attribute [r] explicit_class
|
|
98
|
-
# @return [
|
|
116
|
+
# @return [Hash<Symbol, Symbol>]
|
|
99
117
|
attr_reader :explicit_class
|
|
100
118
|
|
|
101
119
|
# @!attribute [r] module_function_explicit
|
|
102
|
-
# @return [
|
|
120
|
+
# @return [Hash<Symbol, Boolean>]
|
|
103
121
|
attr_reader :module_function_explicit
|
|
104
122
|
|
|
105
123
|
# @!attribute [rw] container_is_module
|
|
106
|
-
# @return [
|
|
107
|
-
# @param
|
|
124
|
+
# @return [Boolean]
|
|
125
|
+
# @param [Boolean] value
|
|
108
126
|
attr_accessor :container_is_module
|
|
109
127
|
|
|
110
128
|
# @!attribute [rw] extend_self
|
|
111
|
-
# @return [
|
|
112
|
-
# @param
|
|
129
|
+
# @return [Boolean]
|
|
130
|
+
# @param [Boolean] value
|
|
113
131
|
attr_accessor :extend_self
|
|
114
132
|
|
|
115
133
|
# Create a fresh visibility context with Ruby-like defaults.
|
|
@@ -130,38 +148,65 @@ module Docscribe
|
|
|
130
148
|
|
|
131
149
|
# Duplicate the context so nested bodies can mutate state independently.
|
|
132
150
|
#
|
|
133
|
-
# @return [VisibilityCtx]
|
|
151
|
+
# @return [Docscribe::InlineRewriter::Collector::VisibilityCtx]
|
|
134
152
|
def dup
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
153
|
+
VisibilityCtx.new.tap do |ctx|
|
|
154
|
+
copy_visibility_state(ctx)
|
|
155
|
+
copy_module_function_state(ctx)
|
|
156
|
+
copy_container_state(ctx)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
private
|
|
161
|
+
|
|
162
|
+
# Copy default instance/class visibility and sclass state into a new context.
|
|
163
|
+
#
|
|
164
|
+
# @private
|
|
165
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx the target context to copy state into
|
|
166
|
+
# @return [void]
|
|
167
|
+
def copy_visibility_state(ctx)
|
|
168
|
+
ctx.default_instance_vis = default_instance_vis
|
|
169
|
+
ctx.default_class_vis = default_class_vis
|
|
170
|
+
ctx.inside_sclass = inside_sclass
|
|
139
171
|
|
|
140
|
-
|
|
141
|
-
|
|
172
|
+
ctx.explicit_instance.merge!(explicit_instance)
|
|
173
|
+
ctx.explicit_class.merge!(explicit_class)
|
|
174
|
+
end
|
|
142
175
|
|
|
143
|
-
|
|
144
|
-
|
|
176
|
+
# Copy module_function default and explicit state into a new context.
|
|
177
|
+
#
|
|
178
|
+
# @private
|
|
179
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx the target context to copy state into
|
|
180
|
+
# @return [void]
|
|
181
|
+
def copy_module_function_state(ctx)
|
|
182
|
+
ctx.module_function_default = module_function_default
|
|
183
|
+
ctx.module_function_explicit.merge!(module_function_explicit)
|
|
184
|
+
end
|
|
145
185
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
186
|
+
# Copy container override, module flag, and extend_self state into a new context.
|
|
187
|
+
#
|
|
188
|
+
# @private
|
|
189
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx the target context to copy state into
|
|
190
|
+
# @return [void]
|
|
191
|
+
def copy_container_state(ctx)
|
|
192
|
+
ctx.container_override = container_override
|
|
193
|
+
ctx.container_is_module = container_is_module
|
|
194
|
+
ctx.extend_self = extend_self
|
|
150
195
|
end
|
|
151
196
|
end
|
|
152
197
|
|
|
153
198
|
# @!attribute [r] insertions
|
|
154
|
-
#
|
|
199
|
+
# @return [Array<Docscribe::InlineRewriter::Collector::Insertion>]
|
|
155
200
|
attr_reader :insertions
|
|
156
201
|
|
|
157
202
|
# @!attribute [r] attr_insertions
|
|
158
|
-
# @return [Array<AttrInsertion>]
|
|
203
|
+
# @return [Array<Docscribe::InlineRewriter::Collector::AttrInsertion>]
|
|
159
204
|
attr_reader :attr_insertions
|
|
160
205
|
|
|
161
206
|
# Create a collector for the given source buffer.
|
|
162
207
|
#
|
|
163
208
|
# @param [Parser::Source::Buffer] buffer source buffer for anchor location lookups
|
|
164
|
-
# @return [
|
|
209
|
+
# @return [void]
|
|
165
210
|
def initialize(buffer)
|
|
166
211
|
super()
|
|
167
212
|
@buffer = buffer
|
|
@@ -180,7 +225,7 @@ module Docscribe
|
|
|
180
225
|
|
|
181
226
|
# Enter a class body and collect documentation targets from its contents.
|
|
182
227
|
#
|
|
183
|
-
# @param [Parser::AST::Node] node
|
|
228
|
+
# @param [Parser::AST::Node] node an AST node
|
|
184
229
|
# @return [Parser::AST::Node]
|
|
185
230
|
def on_class(node)
|
|
186
231
|
cname_node, super_node, body = *node
|
|
@@ -201,7 +246,7 @@ module Docscribe
|
|
|
201
246
|
# This also carries `extend self` state across reopened modules in the same
|
|
202
247
|
# file.
|
|
203
248
|
#
|
|
204
|
-
# @param [Parser::AST::Node] node
|
|
249
|
+
# @param [Parser::AST::Node] node an AST node
|
|
205
250
|
# @return [Parser::AST::Node]
|
|
206
251
|
def on_module(node)
|
|
207
252
|
cname_node, body = *node
|
|
@@ -216,11 +261,7 @@ module Docscribe
|
|
|
216
261
|
process_body(body, ctx)
|
|
217
262
|
|
|
218
263
|
# If `extend self` is active for this module, document all instance defs as module methods (M.foo).
|
|
219
|
-
|
|
220
|
-
promote_extend_self_container(container: container)
|
|
221
|
-
@module_states[container] ||= {}
|
|
222
|
-
@module_states[container][:extend_self] = true
|
|
223
|
-
end
|
|
264
|
+
persist_extend_self_state(ctx, container)
|
|
224
265
|
|
|
225
266
|
@name_stack.pop
|
|
226
267
|
node
|
|
@@ -234,7 +275,7 @@ module Docscribe
|
|
|
234
275
|
# @param [Parser::AST::Node] node a `:casgn` node
|
|
235
276
|
# @return [Parser::AST::Node] the original node
|
|
236
277
|
def on_casgn(node)
|
|
237
|
-
return node if process_struct_casgn(node)
|
|
278
|
+
return node if process_struct_casgn?(node)
|
|
238
279
|
|
|
239
280
|
node.children.each do |child|
|
|
240
281
|
process(child) if child.is_a?(Parser::AST::Node)
|
|
@@ -249,7 +290,7 @@ module Docscribe
|
|
|
249
290
|
# that +def foo+ declared outside of any class or module is still picked
|
|
250
291
|
# up by the collector.
|
|
251
292
|
#
|
|
252
|
-
# @param [Parser::AST::Node] node
|
|
293
|
+
# @param [Parser::AST::Node] node an AST node
|
|
253
294
|
# @return [Parser::AST::Node]
|
|
254
295
|
def on_def(node)
|
|
255
296
|
return node unless @name_stack.empty?
|
|
@@ -265,7 +306,7 @@ module Docscribe
|
|
|
265
306
|
# Handles the case of +def self.foo+ declared at the top level, outside
|
|
266
307
|
# of any class or module body.
|
|
267
308
|
#
|
|
268
|
-
# @param [Parser::AST::Node] node
|
|
309
|
+
# @param [Parser::AST::Node] node an AST node
|
|
269
310
|
# @return [Parser::AST::Node]
|
|
270
311
|
def on_defs(node)
|
|
271
312
|
return node unless @name_stack.empty?
|
|
@@ -278,114 +319,113 @@ module Docscribe
|
|
|
278
319
|
|
|
279
320
|
private
|
|
280
321
|
|
|
281
|
-
# Process a
|
|
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.
|
|
322
|
+
# Process a `:def` node for documentation insertion.
|
|
286
323
|
#
|
|
287
324
|
# @private
|
|
288
|
-
# @param [Parser::AST::Node
|
|
289
|
-
# @param [VisibilityCtx] ctx current visibility
|
|
290
|
-
# @param [Parser::AST::Node
|
|
325
|
+
# @param [Parser::AST::Node] node an AST node
|
|
326
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
327
|
+
# @param [Parser::AST::Node?] pending_sig_anchor Sorbet `sig` node waiting for a method
|
|
291
328
|
# @return [void]
|
|
292
|
-
def
|
|
293
|
-
|
|
329
|
+
def process_def_stmt(node, ctx, pending_sig_anchor:)
|
|
330
|
+
name, = *node
|
|
331
|
+
anchor_node = pending_sig_anchor || node
|
|
294
332
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
name, _args, _body = *node
|
|
298
|
-
anchor_node = pending_sig_anchor || node
|
|
333
|
+
return process_module_function_def(node, name, ctx, anchor_node) if module_function_applies?(ctx, name)
|
|
334
|
+
return process_extend_self_def(node, name, ctx, anchor_node) if extend_self_applies?(ctx)
|
|
299
335
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
336
|
+
scope, visibility = def_scope_visibility(ctx, name)
|
|
337
|
+
|
|
338
|
+
@insertions << Insertion.new(node, scope, visibility, container_for(ctx), nil, nil, anchor_node)
|
|
339
|
+
end
|
|
303
340
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
341
|
+
# Process a `:defs` node (singleton method) for documentation insertion.
|
|
342
|
+
#
|
|
343
|
+
# @private
|
|
344
|
+
# @param [Parser::AST::Node] node the `:defs` AST node
|
|
345
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
346
|
+
# @param [Parser::AST::Node?] pending_sig_anchor Sorbet `sig` node waiting for a method
|
|
347
|
+
# @return [void]
|
|
348
|
+
def process_defs_stmt(node, ctx, pending_sig_anchor:)
|
|
349
|
+
recv, name, _args, _body = *node
|
|
350
|
+
vis = ctx.explicit_class[name] || ctx.default_class_vis
|
|
307
351
|
|
|
308
|
-
|
|
309
|
-
|
|
352
|
+
container =
|
|
353
|
+
if const_receiver?(recv)
|
|
354
|
+
const_name(recv)
|
|
355
|
+
else
|
|
356
|
+
container_for(ctx)
|
|
310
357
|
end
|
|
311
358
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
scope = :class
|
|
315
|
-
vis = ctx.explicit_instance[name] || ctx.default_instance_vis
|
|
359
|
+
@insertions << Insertion.new(node, :class, vis, container, nil, nil, pending_sig_anchor || node)
|
|
360
|
+
end
|
|
316
361
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
362
|
+
# Process a `:sclass` node for documentation insertion.
|
|
363
|
+
#
|
|
364
|
+
# @private
|
|
365
|
+
# @param [Parser::AST::Node] node an AST node
|
|
366
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
367
|
+
# @param [Parser::AST::Node?] pending_sig_anchor pending Sorbet sig anchor
|
|
368
|
+
# @return [void]
|
|
369
|
+
def process_sclass_stmt(node, ctx, pending_sig_anchor: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
370
|
+
# `class << self` — affects default visibility for singleton methods and changes scope.
|
|
371
|
+
recv, body = *node
|
|
372
|
+
inner_ctx = ctx.dup
|
|
320
373
|
|
|
321
|
-
|
|
322
|
-
if ctx.inside_sclass
|
|
323
|
-
vis = ctx.explicit_class[name] || ctx.default_class_vis
|
|
324
|
-
scope = :class
|
|
325
|
-
else
|
|
326
|
-
vis = ctx.explicit_instance[name] || ctx.default_instance_vis
|
|
327
|
-
scope = :instance
|
|
328
|
-
end
|
|
374
|
+
configure_sclass_context(inner_ctx, recv)
|
|
329
375
|
|
|
330
|
-
|
|
376
|
+
process_body(body, inner_ctx)
|
|
377
|
+
end
|
|
331
378
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
@insertions << Insertion.new(node, :class, vis, container, nil, nil, pending_sig_anchor || node)
|
|
344
|
-
|
|
345
|
-
when :sclass
|
|
346
|
-
# `class << self` — affects default visibility for singleton methods and changes scope.
|
|
347
|
-
recv, body = *node
|
|
348
|
-
inner_ctx = ctx.dup
|
|
349
|
-
|
|
350
|
-
if self_node?(recv)
|
|
351
|
-
# class << self
|
|
352
|
-
inner_ctx.inside_sclass = true
|
|
353
|
-
inner_ctx.container_override = nil
|
|
354
|
-
elsif const_receiver?(recv)
|
|
355
|
-
# class << Foo (const receiver) — document methods under Foo
|
|
356
|
-
inner_ctx.inside_sclass = true
|
|
357
|
-
inner_ctx.container_override = const_name(recv)
|
|
358
|
-
else
|
|
359
|
-
# Unknown receiver (e.g. class << obj) — keep prior behavior
|
|
360
|
-
inner_ctx.inside_sclass = false
|
|
361
|
-
inner_ctx.container_override = nil
|
|
362
|
-
end
|
|
379
|
+
# Configure the new context with sclass receiver tracking and container override.
|
|
380
|
+
#
|
|
381
|
+
# @private
|
|
382
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx the inner context to configure
|
|
383
|
+
# @param [Parser::AST::Node] recv the receiver node of `class <<`
|
|
384
|
+
# @return [void]
|
|
385
|
+
def configure_sclass_context(ctx, recv)
|
|
386
|
+
ctx.inside_sclass = sclass_receiver?(recv)
|
|
387
|
+
ctx.container_override = sclass_container_override(recv)
|
|
388
|
+
end
|
|
363
389
|
|
|
364
|
-
|
|
365
|
-
|
|
390
|
+
# Check if the receiver is `self` or a constant reference (enables sclass semantics).
|
|
391
|
+
#
|
|
392
|
+
# @private
|
|
393
|
+
# @param [Parser::AST::Node] recv the receiver node of `class <<`
|
|
394
|
+
# @return [Boolean]
|
|
395
|
+
def sclass_receiver?(recv)
|
|
396
|
+
self_node?(recv) || const_receiver?(recv)
|
|
397
|
+
end
|
|
366
398
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
399
|
+
# Return the constant name for a non-self receiver, or nil for `class << self`.
|
|
400
|
+
#
|
|
401
|
+
# @private
|
|
402
|
+
# @param [Parser::AST::Node] recv the receiver node of `class <<`
|
|
403
|
+
# @return [String?] the container name for constant receivers, nil for `self`
|
|
404
|
+
def sclass_container_override(recv)
|
|
405
|
+
return nil if self_node?(recv)
|
|
406
|
+
return const_name(recv) if const_receiver?(recv)
|
|
373
407
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
# handled
|
|
377
|
-
elsif process_extend_self_send(node, ctx)
|
|
378
|
-
# handled
|
|
379
|
-
elsif process_module_function_send(node, ctx)
|
|
380
|
-
# handled
|
|
381
|
-
elsif process_class_method_visibility_send(node, ctx)
|
|
382
|
-
# handled
|
|
383
|
-
else
|
|
384
|
-
process_visibility_send(node, ctx, pending_sig_anchor: pending_sig_anchor)
|
|
385
|
-
end
|
|
408
|
+
nil
|
|
409
|
+
end
|
|
386
410
|
|
|
411
|
+
# Process a `:send` node for documentation insertion.
|
|
412
|
+
#
|
|
413
|
+
# @private
|
|
414
|
+
# @param [Parser::AST::Node] node an AST node
|
|
415
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
416
|
+
# @param [Parser::AST::Node?] pending_sig_anchor Sorbet `sig` node waiting for a method
|
|
417
|
+
# @return [void]
|
|
418
|
+
def process_send_stmt(node, ctx, pending_sig_anchor:)
|
|
419
|
+
if process_attr_send?(node, ctx)
|
|
420
|
+
# handled
|
|
421
|
+
elsif process_extend_self_send?(node, ctx)
|
|
422
|
+
# handled
|
|
423
|
+
elsif process_module_function_send?(node, ctx)
|
|
424
|
+
# handled
|
|
425
|
+
elsif process_class_method_visibility_send?(node, ctx)
|
|
426
|
+
# handled
|
|
387
427
|
else
|
|
388
|
-
|
|
428
|
+
process_visibility_send(node, ctx, pending_sig_anchor: pending_sig_anchor)
|
|
389
429
|
end
|
|
390
430
|
end
|
|
391
431
|
|
|
@@ -393,7 +433,7 @@ module Docscribe
|
|
|
393
433
|
#
|
|
394
434
|
# @private
|
|
395
435
|
# @param [Parser::AST::Node] node the class declaration node
|
|
396
|
-
# @param [Parser::AST::Node
|
|
436
|
+
# @param [Parser::AST::Node?] super_node the superclass expression
|
|
397
437
|
# @return [void]
|
|
398
438
|
def process_struct_class(node, super_node)
|
|
399
439
|
return unless struct_new_node?(super_node)
|
|
@@ -401,196 +441,255 @@ module Docscribe
|
|
|
401
441
|
names = extract_struct_member_names(super_node)
|
|
402
442
|
return if names.empty?
|
|
403
443
|
|
|
404
|
-
@attr_insertions << AttrInsertion.new(
|
|
405
|
-
node, # insert above the class declaration
|
|
406
|
-
:instance, # struct members are instance readers/writers
|
|
407
|
-
:public, # Struct fields are public by default
|
|
408
|
-
current_container,
|
|
409
|
-
:rw,
|
|
410
|
-
names
|
|
411
|
-
)
|
|
444
|
+
@attr_insertions << AttrInsertion.new(node, :instance, :public, current_container, :rw, names)
|
|
412
445
|
end
|
|
413
446
|
|
|
414
|
-
#
|
|
447
|
+
# Detect `attr_reader` / `attr_writer` / `attr_accessor` calls and record attribute insertions.
|
|
415
448
|
#
|
|
416
449
|
# @private
|
|
417
|
-
# @param [Parser::AST::Node] node a `:
|
|
418
|
-
# @
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
450
|
+
# @param [Parser::AST::Node] node a `:send` node
|
|
451
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
452
|
+
# @return [Boolean] true if the node was an attr_* call
|
|
453
|
+
def process_attr_send?(node, ctx)
|
|
454
|
+
recv, meth, *args = *node
|
|
422
455
|
|
|
423
|
-
|
|
456
|
+
return false unless attr_send?(recv, meth)
|
|
457
|
+
|
|
458
|
+
names = args.map { |arg| extract_name_sym(arg) }.compact
|
|
424
459
|
return true if names.empty?
|
|
425
460
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
struct_container_name(node),
|
|
431
|
-
:rw,
|
|
432
|
-
names
|
|
433
|
-
)
|
|
461
|
+
scope, visibility = attr_scope_visibility(ctx)
|
|
462
|
+
access = attr_access_type(meth)
|
|
463
|
+
|
|
464
|
+
@attr_insertions << AttrInsertion.new(node, scope, visibility, container_for(ctx), access, names)
|
|
434
465
|
|
|
435
466
|
true
|
|
436
467
|
end
|
|
437
468
|
|
|
438
|
-
#
|
|
469
|
+
# Detect `extend self` calls inside a module and persist the state.
|
|
439
470
|
#
|
|
440
471
|
# @private
|
|
441
|
-
# @param [Parser::AST::Node
|
|
442
|
-
# @
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
472
|
+
# @param [Parser::AST::Node] node a `:send` node
|
|
473
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
474
|
+
# @return [Boolean] true if `extend self` was detected
|
|
475
|
+
def process_extend_self_send?(node, ctx)
|
|
476
|
+
recv, meth, *args = *node
|
|
446
477
|
|
|
447
|
-
recv, meth,
|
|
448
|
-
return false unless meth == :new
|
|
449
|
-
return false unless recv&.type == :const
|
|
478
|
+
return false unless extend_self_send?(ctx, recv, meth, args)
|
|
450
479
|
|
|
451
|
-
|
|
452
|
-
|
|
480
|
+
persist_extend_self(ctx)
|
|
481
|
+
|
|
482
|
+
true
|
|
453
483
|
end
|
|
454
484
|
|
|
455
|
-
#
|
|
485
|
+
# Mark the context and module state as using `extend self`.
|
|
456
486
|
#
|
|
457
487
|
# @private
|
|
458
|
-
# @param [
|
|
459
|
-
# @return [
|
|
460
|
-
def
|
|
461
|
-
|
|
488
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
489
|
+
# @return [void]
|
|
490
|
+
def persist_extend_self(ctx)
|
|
491
|
+
ctx.extend_self = true
|
|
462
492
|
|
|
463
|
-
|
|
464
|
-
|
|
493
|
+
container = container_for(ctx)
|
|
494
|
+
(@module_states[container] ||= {})[:extend_self] = true
|
|
495
|
+
end
|
|
465
496
|
|
|
466
|
-
|
|
467
|
-
|
|
497
|
+
# Check if a `:send` node is an `extend self` call inside a module.
|
|
498
|
+
#
|
|
499
|
+
# @private
|
|
500
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
501
|
+
# @param [Parser::AST::Node?] recv the receiver of the send node
|
|
502
|
+
# @param [Symbol] meth the method name being called
|
|
503
|
+
# @param [Array<Parser::AST::Node>] args the arguments to the method call
|
|
504
|
+
# @return [Boolean]
|
|
505
|
+
def extend_self_send?(ctx, recv, meth, args)
|
|
506
|
+
ctx.container_is_module &&
|
|
507
|
+
recv.nil? &&
|
|
508
|
+
meth == :extend &&
|
|
509
|
+
!ctx.inside_sclass &&
|
|
510
|
+
args.any? { |arg| self_node?(arg) }
|
|
511
|
+
end
|
|
468
512
|
|
|
469
|
-
|
|
513
|
+
# Check if a node is a constant or `::` (cbase) receiver.
|
|
514
|
+
#
|
|
515
|
+
# @private
|
|
516
|
+
# @param [Parser::AST::Node?] node an AST node
|
|
517
|
+
# @return [Boolean]
|
|
518
|
+
def const_receiver?(node)
|
|
519
|
+
return false unless node.is_a?(Parser::AST::Node)
|
|
520
|
+
|
|
521
|
+
%i[const cbase].include?(node.type)
|
|
470
522
|
end
|
|
471
523
|
|
|
472
|
-
#
|
|
524
|
+
# Check if a send node is an attr_reader/attr_writer/attr_accessor call.
|
|
473
525
|
#
|
|
474
526
|
# @private
|
|
475
|
-
# @param [Parser::AST::Node]
|
|
476
|
-
# @
|
|
477
|
-
|
|
478
|
-
|
|
527
|
+
# @param [Parser::AST::Node?] recv the receiver of the send node
|
|
528
|
+
# @param [Symbol] meth the method name being called
|
|
529
|
+
# @return [Boolean]
|
|
530
|
+
def attr_send?(recv, meth)
|
|
531
|
+
recv.nil? && %i[attr_reader attr_writer attr_accessor].include?(meth)
|
|
532
|
+
end
|
|
479
533
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
534
|
+
# Determine the scope and visibility for an attribute based on sclass context.
|
|
535
|
+
#
|
|
536
|
+
# @private
|
|
537
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
538
|
+
# @return [(Symbol, Symbol)]
|
|
539
|
+
def attr_scope_visibility(ctx)
|
|
540
|
+
if ctx.inside_sclass
|
|
541
|
+
[:class, ctx.default_class_vis]
|
|
542
|
+
else
|
|
543
|
+
[:instance, ctx.default_instance_vis]
|
|
544
|
+
end
|
|
545
|
+
end
|
|
488
546
|
|
|
489
|
-
|
|
547
|
+
# Map the attr method name to an access type symbol.
|
|
548
|
+
#
|
|
549
|
+
# @private
|
|
550
|
+
# @param [Symbol] meth the method name (:attr_reader, :attr_writer, or :attr_accessor)
|
|
551
|
+
# @return [Symbol] :r for reader, :w for writer, :rw for accessor
|
|
552
|
+
def attr_access_type(meth)
|
|
553
|
+
case meth
|
|
554
|
+
when :attr_reader then :r
|
|
555
|
+
when :attr_writer then :w
|
|
556
|
+
else :rw
|
|
557
|
+
end
|
|
490
558
|
end
|
|
491
559
|
|
|
492
|
-
# Detect `
|
|
560
|
+
# Detect `module_function` calls and update the visibility context accordingly.
|
|
493
561
|
#
|
|
494
562
|
# @private
|
|
495
|
-
# @param [Parser::AST::Node] node
|
|
496
|
-
# @param [VisibilityCtx] ctx current visibility context
|
|
497
|
-
# @return [Boolean] true if
|
|
498
|
-
def
|
|
563
|
+
# @param [Parser::AST::Node] node the `:send` node
|
|
564
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
565
|
+
# @return [Boolean] true if the node was a module_function call
|
|
566
|
+
def process_module_function_send?(node, ctx)
|
|
499
567
|
recv, meth, *args = *node
|
|
500
568
|
|
|
501
|
-
return false unless
|
|
502
|
-
return
|
|
503
|
-
return false if ctx.inside_sclass
|
|
504
|
-
return false unless args.any? { |a| self_node?(a) }
|
|
569
|
+
return false unless recv.nil? && meth == :module_function
|
|
570
|
+
return true if ctx.inside_sclass
|
|
505
571
|
|
|
506
|
-
ctx
|
|
572
|
+
return enable_default_module_function?(ctx) if args.empty?
|
|
507
573
|
|
|
508
|
-
|
|
509
|
-
container = container_for(ctx)
|
|
510
|
-
@module_states[container] ||= {}
|
|
511
|
-
@module_states[container][:extend_self] = true
|
|
574
|
+
process_named_module_function(args, ctx)
|
|
512
575
|
|
|
513
576
|
true
|
|
514
577
|
end
|
|
515
578
|
|
|
516
|
-
#
|
|
579
|
+
# Enable default module_function for all subsequent method definitions in the module.
|
|
517
580
|
#
|
|
518
581
|
# @private
|
|
519
|
-
# @param [VisibilityCtx] ctx current visibility context
|
|
520
|
-
# @return [Boolean]
|
|
521
|
-
def
|
|
522
|
-
ctx.
|
|
582
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
583
|
+
# @return [Boolean] true
|
|
584
|
+
def enable_default_module_function?(ctx)
|
|
585
|
+
ctx.module_function_default = true
|
|
586
|
+
true
|
|
523
587
|
end
|
|
524
588
|
|
|
525
|
-
#
|
|
589
|
+
# Process a `module_function :foo, :bar` call with named arguments.
|
|
526
590
|
#
|
|
527
591
|
# @private
|
|
528
|
-
# @param [Parser::AST::Node
|
|
529
|
-
# @
|
|
530
|
-
|
|
531
|
-
|
|
592
|
+
# @param [Array<Parser::AST::Node>] args the named method arguments
|
|
593
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
594
|
+
# @return [void]
|
|
595
|
+
def process_named_module_function(args, ctx)
|
|
596
|
+
args.map { |arg| extract_name_sym(arg) }
|
|
597
|
+
.compact
|
|
598
|
+
.each do |sym|
|
|
599
|
+
ctx.module_function_explicit[sym] = true
|
|
532
600
|
|
|
533
|
-
|
|
601
|
+
retroactively_promote_module_function(
|
|
602
|
+
sym,
|
|
603
|
+
container: container_for(ctx)
|
|
604
|
+
)
|
|
605
|
+
end
|
|
534
606
|
end
|
|
535
607
|
|
|
536
|
-
#
|
|
608
|
+
# Retroactively promote a previously collected method to module_function (class scope).
|
|
537
609
|
#
|
|
538
610
|
# @private
|
|
539
|
-
# @param [
|
|
540
|
-
# @param [
|
|
541
|
-
# @return [
|
|
542
|
-
def
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
return true if names.empty?
|
|
548
|
-
|
|
549
|
-
scope = ctx.inside_sclass ? :class : :instance
|
|
550
|
-
visibility = ctx.inside_sclass ? ctx.default_class_vis : ctx.default_instance_vis
|
|
551
|
-
|
|
552
|
-
access =
|
|
553
|
-
case meth
|
|
554
|
-
when :attr_reader then :r
|
|
555
|
-
when :attr_writer then :w
|
|
556
|
-
else :rw
|
|
557
|
-
end
|
|
558
|
-
|
|
559
|
-
@attr_insertions << AttrInsertion.new(node, scope, visibility, container_for(ctx), access, names)
|
|
611
|
+
# @param [Symbol] name_sym the method name to promote
|
|
612
|
+
# @param [String] container the container name
|
|
613
|
+
# @return [void]
|
|
614
|
+
def retroactively_promote_module_function(name_sym, container:)
|
|
615
|
+
@insertions.reverse_each do |ins|
|
|
616
|
+
next unless ins.container == container
|
|
617
|
+
next unless ins.node.type == :def
|
|
618
|
+
next unless ins.node.children[0] == name_sym
|
|
560
619
|
|
|
561
|
-
|
|
620
|
+
ins.scope = :class
|
|
621
|
+
ins.visibility = :public
|
|
622
|
+
ins.module_function = true
|
|
623
|
+
ins.included_instance_visibility ||= :private
|
|
624
|
+
break
|
|
625
|
+
end
|
|
562
626
|
end
|
|
563
627
|
|
|
564
|
-
# Detect `private_class_method` / `protected_class_method` / `public_class_method` and update class-level
|
|
628
|
+
# Detect `private_class_method` / `protected_class_method` / `public_class_method` and update class-level
|
|
629
|
+
# visibility.
|
|
565
630
|
#
|
|
566
631
|
# @private
|
|
567
632
|
# @param [Parser::AST::Node] node a `:send` node
|
|
568
|
-
# @param [VisibilityCtx] ctx current visibility context
|
|
633
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
569
634
|
# @return [Boolean] true if the node was a class visibility modifier
|
|
570
|
-
def process_class_method_visibility_send(node, ctx)
|
|
635
|
+
def process_class_method_visibility_send?(node, ctx)
|
|
571
636
|
recv, meth, *args = *node
|
|
637
|
+
return false unless class_visibility_send?(recv, meth)
|
|
572
638
|
|
|
573
|
-
|
|
574
|
-
|
|
639
|
+
visibility = class_method_visibility(meth)
|
|
640
|
+
apply_class_method_visibility(args, ctx, visibility, container_for(ctx))
|
|
575
641
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
when :private_class_method then :private
|
|
579
|
-
when :protected_class_method then :protected
|
|
580
|
-
else :public
|
|
581
|
-
end
|
|
642
|
+
true
|
|
643
|
+
end
|
|
582
644
|
|
|
583
|
-
|
|
645
|
+
# Check if a send node is a private/protected/public_class_method call.
|
|
646
|
+
#
|
|
647
|
+
# @private
|
|
648
|
+
# @param [Parser::AST::Node?] recv the receiver of the send node
|
|
649
|
+
# @param [Symbol] meth the method name being called
|
|
650
|
+
# @return [Boolean]
|
|
651
|
+
def class_visibility_send?(recv, meth)
|
|
652
|
+
%i[
|
|
653
|
+
private_class_method
|
|
654
|
+
protected_class_method
|
|
655
|
+
public_class_method
|
|
656
|
+
].include?(meth) &&
|
|
657
|
+
(recv.nil? || self_node?(recv))
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
# Map a class method visibility modifier name to its visibility symbol.
|
|
661
|
+
#
|
|
662
|
+
# @private
|
|
663
|
+
# @param [Symbol] meth the method name (:private_class_method, etc.)
|
|
664
|
+
# @return [Symbol] :private, :protected, or :public
|
|
665
|
+
def class_method_visibility(meth)
|
|
666
|
+
case meth
|
|
667
|
+
when :private_class_method
|
|
668
|
+
:private
|
|
669
|
+
when :protected_class_method
|
|
670
|
+
:protected
|
|
671
|
+
else
|
|
672
|
+
:public
|
|
673
|
+
end
|
|
674
|
+
end
|
|
584
675
|
|
|
676
|
+
# Apply a visibility modifier to named class methods and retroactively update their visibility.
|
|
677
|
+
#
|
|
678
|
+
# @private
|
|
679
|
+
# @param [Array<Parser::AST::Node>] args the method name nodes
|
|
680
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
681
|
+
# @param [Symbol] visibility the visibility to apply (:public, :protected, :private)
|
|
682
|
+
# @param [String] container the container name
|
|
683
|
+
# @return [void]
|
|
684
|
+
def apply_class_method_visibility(args, ctx, visibility, container)
|
|
585
685
|
args.each do |arg|
|
|
586
686
|
sym = extract_name_sym(arg)
|
|
587
687
|
next unless sym
|
|
588
688
|
|
|
589
689
|
ctx.explicit_class[sym] = visibility
|
|
690
|
+
|
|
590
691
|
retroactively_set_visibility(sym, visibility, scope: :class, container: container)
|
|
591
692
|
end
|
|
592
|
-
|
|
593
|
-
true
|
|
594
693
|
end
|
|
595
694
|
|
|
596
695
|
# Detect `private` / `protected` / `public` calls and update visibility state.
|
|
@@ -601,67 +700,174 @@ module Docscribe
|
|
|
601
700
|
#
|
|
602
701
|
# @private
|
|
603
702
|
# @param [Parser::AST::Node] node a `:send` node
|
|
604
|
-
# @param [VisibilityCtx] ctx current visibility context
|
|
605
|
-
# @param [Parser::AST::Node
|
|
703
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
704
|
+
# @param [Parser::AST::Node?] pending_sig_anchor Sorbet `sig` node
|
|
606
705
|
# @return [void]
|
|
607
706
|
def process_visibility_send(node, ctx, pending_sig_anchor: nil)
|
|
608
707
|
recv, meth, *args = *node
|
|
609
|
-
return unless recv.nil? && %i[private protected public].include?(meth)
|
|
610
708
|
|
|
611
|
-
|
|
709
|
+
return unless visibility_send?(recv, meth)
|
|
710
|
+
|
|
711
|
+
process_visibility_args(args, ctx, meth, container_for(ctx), pending_sig_anchor)
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
# Check if a send node is a private/protected/public call with no receiver.
|
|
715
|
+
#
|
|
716
|
+
# @private
|
|
717
|
+
# @param [Parser::AST::Node?] recv the receiver of the send node
|
|
718
|
+
# @param [Symbol] meth the method name being called
|
|
719
|
+
# @return [Boolean]
|
|
720
|
+
def visibility_send?(recv, meth)
|
|
721
|
+
recv.nil? && %i[private protected public].include?(meth)
|
|
722
|
+
end
|
|
612
723
|
|
|
724
|
+
# Dispatch visibility modifier handling based on whether args are absent, inline defs, or named symbols.
|
|
725
|
+
#
|
|
726
|
+
# @private
|
|
727
|
+
# @param [Array<Parser::AST::Node>] args the arguments to the visibility modifier
|
|
728
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
729
|
+
# @param [Symbol] meth the visibility method (:private, :protected, :public)
|
|
730
|
+
# @param [String] container the container name
|
|
731
|
+
# @param [Parser::AST::Node?] pending_sig_anchor Sorbet `sig` node waiting for a method
|
|
732
|
+
# @return [void]
|
|
733
|
+
def process_visibility_args(args, ctx, meth, container, pending_sig_anchor)
|
|
613
734
|
if args.empty?
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
return
|
|
735
|
+
process_visibility_bare_modifier(ctx, meth)
|
|
736
|
+
elsif inline_visibility_def?(args)
|
|
737
|
+
process_visibility_inline_modifier(args.first, ctx, meth, container, pending_sig_anchor)
|
|
738
|
+
else
|
|
739
|
+
process_visibility_named_modifier(args, ctx, meth, container)
|
|
620
740
|
end
|
|
741
|
+
end
|
|
621
742
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
@insertions << Insertion.new(def_node, :class, meth, container, nil, nil, anchor_node)
|
|
645
|
-
return
|
|
646
|
-
end
|
|
743
|
+
# Check if visibility modifier args contain a single inline def/defs node.
|
|
744
|
+
#
|
|
745
|
+
# @private
|
|
746
|
+
# @param [Array<Parser::AST::Node>] args the arguments to the visibility modifier
|
|
747
|
+
# @return [Boolean]
|
|
748
|
+
def inline_visibility_def?(args)
|
|
749
|
+
args.length == 1 &&
|
|
750
|
+
args.first.is_a?(Parser::AST::Node) &&
|
|
751
|
+
%i[def defs].include?(args.first.type)
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
# Process a bare visibility modifier (no args).
|
|
755
|
+
#
|
|
756
|
+
# @private
|
|
757
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
758
|
+
# @param [Symbol] meth the visibility method (:private, :protected, :public)
|
|
759
|
+
# @return [void]
|
|
760
|
+
def process_visibility_bare_modifier(ctx, meth)
|
|
761
|
+
if ctx.inside_sclass
|
|
762
|
+
ctx.default_class_vis = meth
|
|
763
|
+
else
|
|
764
|
+
ctx.default_instance_vis = meth
|
|
647
765
|
end
|
|
766
|
+
end
|
|
648
767
|
|
|
649
|
-
|
|
768
|
+
# Process an inline visibility modifier (private def foo).
|
|
769
|
+
#
|
|
770
|
+
# @private
|
|
771
|
+
# @param [Parser::AST::Node] def_node method definition node
|
|
772
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
773
|
+
# @param [Symbol] meth the visibility method (:private, :protected, :public)
|
|
774
|
+
# @param [String] container the container name
|
|
775
|
+
# @param [Parser::AST::Node?] pending_sig_anchor Sorbet `sig` node waiting for a method
|
|
776
|
+
# @return [void]
|
|
777
|
+
def process_visibility_inline_modifier(def_node, ctx, meth, container, pending_sig_anchor)
|
|
778
|
+
anchor_node = pending_sig_anchor || def_node
|
|
779
|
+
|
|
780
|
+
case def_node.type
|
|
781
|
+
when :def
|
|
782
|
+
process_visibility_inline_def(def_node, ctx, meth, container, anchor_node)
|
|
783
|
+
when :defs
|
|
784
|
+
@insertions << Insertion.new(def_node, :class, meth, container, nil, nil, anchor_node)
|
|
785
|
+
end
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
# Process an inline def under a visibility modifier.
|
|
789
|
+
#
|
|
790
|
+
# @private
|
|
791
|
+
# @param [Parser::AST::Node] def_node method definition node
|
|
792
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
793
|
+
# @param [Symbol] meth the visibility method (:private, :protected, :public)
|
|
794
|
+
# @param [String] container the container name
|
|
795
|
+
# @param [Parser::AST::Node] anchor_node the anchor node for comment placement
|
|
796
|
+
# @return [void]
|
|
797
|
+
def process_visibility_inline_def(def_node, ctx, meth, container, anchor_node)
|
|
798
|
+
name, = *def_node
|
|
799
|
+
|
|
800
|
+
if module_function_applies?(ctx, name)
|
|
801
|
+
mod_vis = ctx.explicit_class[name] || ctx.default_class_vis
|
|
802
|
+
@insertions << Insertion.new(def_node, :class, mod_vis, container, true, meth, anchor_node)
|
|
803
|
+
elsif ctx.inside_sclass
|
|
804
|
+
@insertions << Insertion.new(def_node, :class, meth, container, nil, nil, anchor_node)
|
|
805
|
+
else
|
|
806
|
+
@insertions << Insertion.new(def_node, :instance, meth, container, nil, nil, anchor_node)
|
|
807
|
+
end
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
# Process a named visibility modifier (private :foo).
|
|
811
|
+
#
|
|
812
|
+
# @private
|
|
813
|
+
# @param [Array<Parser::AST::Node>] args the destructured arguments from Struct.new
|
|
814
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
815
|
+
# @param [Symbol] meth the visibility method (:private, :protected, :public)
|
|
816
|
+
# @param [String] container the container name
|
|
817
|
+
# @return [void]
|
|
818
|
+
def process_visibility_named_modifier(args, ctx, meth, container)
|
|
650
819
|
args.each do |arg|
|
|
651
|
-
|
|
652
|
-
|
|
820
|
+
apply_visibility_modifier_arg(arg, ctx, meth, container)
|
|
821
|
+
end
|
|
822
|
+
end
|
|
653
823
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
824
|
+
# Apply a visibility modifier to a single named method symbol, dispatching to class or instance handling.
|
|
825
|
+
#
|
|
826
|
+
# @private
|
|
827
|
+
# @param [Parser::AST::Node] arg the AST node for the method name
|
|
828
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
829
|
+
# @param [Symbol] meth the visibility method (:private, :protected, :public)
|
|
830
|
+
# @param [String] container the container name
|
|
831
|
+
# @return [void]
|
|
832
|
+
def apply_visibility_modifier_arg(arg, ctx, meth, container)
|
|
833
|
+
sym = extract_name_sym(arg)
|
|
834
|
+
return unless sym
|
|
835
|
+
|
|
836
|
+
if ctx.inside_sclass
|
|
837
|
+
apply_class_visibility_modifier(sym, ctx, meth, container)
|
|
838
|
+
else
|
|
839
|
+
apply_instance_visibility_modifier(sym, ctx, meth, container)
|
|
662
840
|
end
|
|
663
841
|
end
|
|
664
842
|
|
|
843
|
+
# Record and retroactively apply a class-scope visibility modifier for a named method.
|
|
844
|
+
#
|
|
845
|
+
# @private
|
|
846
|
+
# @param [Symbol] sym the method name
|
|
847
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
848
|
+
# @param [Symbol] meth the visibility method (:private, :protected, :public)
|
|
849
|
+
# @param [String] container the container name
|
|
850
|
+
# @return [void]
|
|
851
|
+
def apply_class_visibility_modifier(sym, ctx, meth, container)
|
|
852
|
+
ctx.explicit_class[sym] = meth
|
|
853
|
+
|
|
854
|
+
retroactively_set_visibility(sym, meth, scope: :class, container: container)
|
|
855
|
+
end
|
|
856
|
+
|
|
857
|
+
# Record and retroactively apply an instance-scope visibility modifier for a named method.
|
|
858
|
+
#
|
|
859
|
+
# @private
|
|
860
|
+
# @param [Symbol] sym the method name
|
|
861
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
862
|
+
# @param [Symbol] meth the visibility method (:private, :protected, :public)
|
|
863
|
+
# @param [String] container the container name
|
|
864
|
+
# @return [void]
|
|
865
|
+
def apply_instance_visibility_modifier(sym, ctx, meth, container)
|
|
866
|
+
ctx.explicit_instance[sym] = meth
|
|
867
|
+
retroactively_set_visibility(sym, meth, scope: :instance, container: container)
|
|
868
|
+
retroactively_set_included_instance_visibility_for_module_function(sym, meth, container: container)
|
|
869
|
+
end
|
|
870
|
+
|
|
665
871
|
# Retroactively update the included instance visibility for a module_function method.
|
|
666
872
|
#
|
|
667
873
|
# @private
|
|
@@ -681,6 +887,68 @@ module Docscribe
|
|
|
681
887
|
end
|
|
682
888
|
end
|
|
683
889
|
|
|
890
|
+
# Check if `module_function` semantics apply to a method at the current position.
|
|
891
|
+
#
|
|
892
|
+
# @private
|
|
893
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
894
|
+
# @param [Symbol] name the method name
|
|
895
|
+
# @return [Boolean]
|
|
896
|
+
def module_function_applies?(ctx, name)
|
|
897
|
+
return false if ctx.inside_sclass
|
|
898
|
+
|
|
899
|
+
ctx.module_function_default || ctx.module_function_explicit[name]
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
# Handle a def where module_function applies, recording it with class scope and module_function semantics.
|
|
903
|
+
#
|
|
904
|
+
# @private
|
|
905
|
+
# @param [Parser::AST::Node] node the `:def` AST node
|
|
906
|
+
# @param [Symbol] name the method name
|
|
907
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
908
|
+
# @param [Parser::AST::Node] anchor_node the anchor node for comment placement
|
|
909
|
+
# @return [void]
|
|
910
|
+
def process_module_function_def(node, name, ctx, anchor_node)
|
|
911
|
+
@insertions << Insertion.new(node, :class, ctx.explicit_class[name] || ctx.default_class_vis,
|
|
912
|
+
container_for(ctx), true,
|
|
913
|
+
ctx.explicit_instance[name] || :private, anchor_node)
|
|
914
|
+
end
|
|
915
|
+
|
|
916
|
+
# Check if extend self semantics should apply to the current definition.
|
|
917
|
+
#
|
|
918
|
+
# @private
|
|
919
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
920
|
+
# @return [Boolean]
|
|
921
|
+
def extend_self_applies?(ctx)
|
|
922
|
+
ctx.container_is_module && ctx.extend_self && !ctx.inside_sclass
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
# Process a def under extend self semantics, recording it as a class method.
|
|
926
|
+
#
|
|
927
|
+
# @private
|
|
928
|
+
# @param [Parser::AST::Node] node the `:def` AST node
|
|
929
|
+
# @param [Symbol] name the method name
|
|
930
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
931
|
+
# @param [Parser::AST::Node] anchor_node the anchor node for comment placement
|
|
932
|
+
# @return [void]
|
|
933
|
+
def process_extend_self_def(node, name, ctx, anchor_node)
|
|
934
|
+
@insertions << Insertion.new(node, :class, ctx.explicit_instance[name] || ctx.default_instance_vis,
|
|
935
|
+
container_for(ctx), nil, nil, anchor_node)
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
# Determine scope and visibility for a def based on sclass context and explicit visibility.
|
|
939
|
+
#
|
|
940
|
+
# @private
|
|
941
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
942
|
+
# @param [Symbol] name the method name
|
|
943
|
+
# @return [(Symbol, Symbol)]
|
|
944
|
+
def def_scope_visibility(ctx, name)
|
|
945
|
+
if ctx.inside_sclass
|
|
946
|
+
[:class, ctx.explicit_class[name] || ctx.default_class_vis]
|
|
947
|
+
else
|
|
948
|
+
[:instance, ctx.explicit_instance[name] || ctx.default_instance_vis]
|
|
949
|
+
end
|
|
950
|
+
end
|
|
951
|
+
|
|
684
952
|
# Retroactively update the visibility of a previously collected method.
|
|
685
953
|
#
|
|
686
954
|
# @private
|
|
@@ -690,104 +958,218 @@ module Docscribe
|
|
|
690
958
|
# @param [String] container the container name
|
|
691
959
|
# @return [void]
|
|
692
960
|
def retroactively_set_visibility(name_sym, visibility, scope:, container:)
|
|
693
|
-
@insertions.reverse_each do |
|
|
694
|
-
next unless
|
|
695
|
-
next unless
|
|
696
|
-
|
|
697
|
-
n = ins.node
|
|
698
|
-
method_name =
|
|
699
|
-
case n.type
|
|
700
|
-
when :def then n.children[0]
|
|
701
|
-
when :defs then n.children[1]
|
|
702
|
-
end
|
|
961
|
+
@insertions.reverse_each do |insertion|
|
|
962
|
+
next unless visibility_target?(insertion, scope, container)
|
|
963
|
+
next unless insertion_method_name(insertion.node) == name_sym
|
|
703
964
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
ins.visibility = visibility
|
|
965
|
+
insertion.visibility = visibility
|
|
707
966
|
break
|
|
708
967
|
end
|
|
709
968
|
end
|
|
710
969
|
|
|
711
|
-
# Check if
|
|
970
|
+
# Check if an Insertion matches the given scope and container for visibility updates.
|
|
712
971
|
#
|
|
713
972
|
# @private
|
|
714
|
-
# @param [
|
|
715
|
-
# @param [Symbol]
|
|
973
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the Insertion struct to check
|
|
974
|
+
# @param [Symbol] scope the scope to match (:instance or :class)
|
|
975
|
+
# @param [String] container the container name to match
|
|
716
976
|
# @return [Boolean]
|
|
717
|
-
def
|
|
718
|
-
|
|
977
|
+
def visibility_target?(insertion, scope, container)
|
|
978
|
+
insertion.container == container && insertion.scope == scope
|
|
979
|
+
end
|
|
719
980
|
|
|
720
|
-
|
|
981
|
+
# Extract the method name symbol from a def or defs AST node.
|
|
982
|
+
#
|
|
983
|
+
# @private
|
|
984
|
+
# @param [Parser::AST::Node] node the `:def` or `:defs` AST node
|
|
985
|
+
# @return [Symbol?] the method name
|
|
986
|
+
def insertion_method_name(node)
|
|
987
|
+
case node.type
|
|
988
|
+
when :def
|
|
989
|
+
node.children[0]
|
|
990
|
+
when :defs
|
|
991
|
+
node.children[1]
|
|
992
|
+
end
|
|
721
993
|
end
|
|
722
994
|
|
|
723
|
-
#
|
|
995
|
+
# Check if a node is a `self` literal.
|
|
724
996
|
#
|
|
725
997
|
# @private
|
|
726
|
-
# @param [Parser::AST::Node] node
|
|
727
|
-
# @
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
return false unless recv.nil? && meth == :module_function
|
|
732
|
-
return true if ctx.inside_sclass
|
|
998
|
+
# @param [Parser::AST::Node?] node an AST node
|
|
999
|
+
# @return [Boolean]
|
|
1000
|
+
def self_node?(node)
|
|
1001
|
+
!!(node && node.type == :self)
|
|
1002
|
+
end
|
|
733
1003
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
1004
|
+
# Process all nodes in a class/module body for documentation insertion targets.
|
|
1005
|
+
#
|
|
1006
|
+
# Handles Sorbet `sig` nodes by deferring them as pending anchors for the
|
|
1007
|
+
# next method definition.
|
|
1008
|
+
#
|
|
1009
|
+
# @private
|
|
1010
|
+
# @param [Parser::AST::Node?] body the body node
|
|
1011
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
1012
|
+
# @return [void]
|
|
1013
|
+
def process_body(body, ctx)
|
|
1014
|
+
return unless body
|
|
1015
|
+
|
|
1016
|
+
nodes = body.type == :begin ? body.children : [body]
|
|
1017
|
+
pending_sig_nodes = [] #: Array[Parser::AST::Node]
|
|
1018
|
+
|
|
1019
|
+
nodes.each do |child|
|
|
1020
|
+
process_body_child(child, ctx, pending_sig_nodes)
|
|
737
1021
|
end
|
|
1022
|
+
end
|
|
738
1023
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
1024
|
+
# Process a single child node, collecting Sorbet sigs as pending anchors and dispatching statements.
|
|
1025
|
+
#
|
|
1026
|
+
# @private
|
|
1027
|
+
# @param [Parser::AST::Node] child the child AST node to process
|
|
1028
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
1029
|
+
# @param [Array<Parser::AST::Node>] pending_sig_nodes accumulator for Sorbet sig nodes
|
|
1030
|
+
# @return [void]
|
|
1031
|
+
def process_body_child(child, ctx, pending_sig_nodes)
|
|
1032
|
+
if sorbet_sig_node?(child)
|
|
1033
|
+
pending_sig_nodes << child
|
|
1034
|
+
return
|
|
743
1035
|
end
|
|
744
1036
|
|
|
1037
|
+
process_stmt(child, ctx, pending_sig_anchor: pending_sig_nodes.first)
|
|
1038
|
+
pending_sig_nodes.clear
|
|
1039
|
+
end
|
|
1040
|
+
|
|
1041
|
+
# Process a single AST node for documentation insertion targets.
|
|
1042
|
+
#
|
|
1043
|
+
# Dispatches to specific handlers based on node type (`:def`, `:defs`,
|
|
1044
|
+
# `:sclass`, `:send` with visibility modifiers, etc.) and records
|
|
1045
|
+
# `Insertion` objects for methods that need documentation.
|
|
1046
|
+
#
|
|
1047
|
+
# @private
|
|
1048
|
+
# @param [Parser::AST::Node?] node the AST node to process
|
|
1049
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility and container context
|
|
1050
|
+
# @param [Parser::AST::Node?] pending_sig_anchor Sorbet `sig` node waiting for a method
|
|
1051
|
+
# @return [void]
|
|
1052
|
+
def process_stmt(node, ctx, pending_sig_anchor: nil)
|
|
1053
|
+
return unless node
|
|
1054
|
+
return process_casgn_stmt(node) if node.type == :casgn
|
|
1055
|
+
|
|
1056
|
+
method_name = :"process_#{node.type}_stmt"
|
|
1057
|
+
if respond_to?(method_name, true)
|
|
1058
|
+
__send__(method_name, node, ctx, pending_sig_anchor: pending_sig_anchor)
|
|
1059
|
+
else
|
|
1060
|
+
process(node)
|
|
1061
|
+
end
|
|
1062
|
+
end
|
|
1063
|
+
|
|
1064
|
+
# Process a constant assignment statement, skipping Struct.new assignments.
|
|
1065
|
+
#
|
|
1066
|
+
# @private
|
|
1067
|
+
# @param [Parser::AST::Node] node the `:casgn` AST node
|
|
1068
|
+
# @return [void]
|
|
1069
|
+
def process_casgn_stmt(node)
|
|
1070
|
+
process(node) unless process_struct_casgn?(node)
|
|
1071
|
+
end
|
|
1072
|
+
|
|
1073
|
+
# Check if a constant assignment is `Struct.new` and extract attribute insertions.
|
|
1074
|
+
#
|
|
1075
|
+
# @private
|
|
1076
|
+
# @param [Parser::AST::Node] node a `:casgn` node
|
|
1077
|
+
# @return [Boolean] true if the node was handled as a struct definition
|
|
1078
|
+
def process_struct_casgn?(node)
|
|
1079
|
+
_scope, _name, value = *node
|
|
1080
|
+
return false unless struct_new_node?(value)
|
|
1081
|
+
|
|
1082
|
+
names = extract_struct_member_names(value)
|
|
1083
|
+
return true if names.empty?
|
|
1084
|
+
|
|
1085
|
+
@attr_insertions << AttrInsertion.new(node, :instance, :public, struct_container_name(node), :rw, names)
|
|
1086
|
+
|
|
745
1087
|
true
|
|
746
1088
|
end
|
|
747
1089
|
|
|
748
|
-
#
|
|
1090
|
+
# Check if a node represents a `Struct.new` call.
|
|
749
1091
|
#
|
|
750
1092
|
# @private
|
|
751
|
-
# @param [
|
|
752
|
-
# @return [
|
|
753
|
-
def
|
|
754
|
-
|
|
1093
|
+
# @param [Parser::AST::Node?] node an AST node
|
|
1094
|
+
# @return [Boolean]
|
|
1095
|
+
def struct_new_node?(node)
|
|
1096
|
+
return false unless node.is_a?(Parser::AST::Node)
|
|
1097
|
+
return false unless node.type == :send
|
|
1098
|
+
|
|
1099
|
+
recv, meth, *_args = *node
|
|
1100
|
+
return false unless meth == :new
|
|
1101
|
+
return false unless recv&.type == :const
|
|
1102
|
+
|
|
1103
|
+
recv_name = const_name(recv)
|
|
1104
|
+
%w[Struct ::Struct].include?(recv_name)
|
|
755
1105
|
end
|
|
756
1106
|
|
|
757
|
-
#
|
|
1107
|
+
# If `extend self` is active for this module, document all instance defs as module methods (M.foo).
|
|
758
1108
|
#
|
|
759
1109
|
# @private
|
|
760
|
-
# @param [
|
|
1110
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
761
1111
|
# @param [String] container the container name
|
|
762
1112
|
# @return [void]
|
|
763
|
-
def
|
|
764
|
-
|
|
765
|
-
next unless ins.container == container
|
|
766
|
-
next unless ins.node.type == :def
|
|
767
|
-
next unless ins.node.children[0] == name_sym
|
|
1113
|
+
def persist_extend_self_state(ctx, container)
|
|
1114
|
+
return unless ctx.extend_self
|
|
768
1115
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
ins.module_function = true
|
|
772
|
-
ins.included_instance_visibility ||= :private
|
|
773
|
-
break
|
|
774
|
-
end
|
|
1116
|
+
promote_extend_self_container(container: container)
|
|
1117
|
+
(@module_states[container] ||= {})[:extend_self] = true
|
|
775
1118
|
end
|
|
776
1119
|
|
|
777
|
-
#
|
|
1120
|
+
# Extract member names from a `Struct.new` call, stripping the type string argument if present.
|
|
778
1121
|
#
|
|
779
1122
|
# @private
|
|
780
|
-
# @param [Parser::AST::Node
|
|
781
|
-
# @return [
|
|
782
|
-
def
|
|
783
|
-
|
|
1123
|
+
# @param [Parser::AST::Node?] struct_new_node a `:send` node representing `Struct.new`
|
|
1124
|
+
# @return [Array<Symbol>] extracted member names
|
|
1125
|
+
def extract_struct_member_names(struct_new_node)
|
|
1126
|
+
_recv, _meth, *args = *struct_new_node
|
|
1127
|
+
args ||= [] #: Array[Parser::AST::Node]
|
|
1128
|
+
|
|
1129
|
+
args.reject! { |arg| arg.is_a?(Parser::AST::Node) && arg.type == :hash }
|
|
1130
|
+
|
|
1131
|
+
drop_first_if_str!(args) if args.length >= 2
|
|
1132
|
+
|
|
1133
|
+
args.map { |arg| extract_name_sym(arg) }.compact
|
|
1134
|
+
end
|
|
1135
|
+
|
|
1136
|
+
# Drop the first argument if it is a string (e.g. Struct.new("Name", ...)).
|
|
1137
|
+
#
|
|
1138
|
+
# @private
|
|
1139
|
+
# @param [Array<Parser::AST::Node>] args the destructured arguments from Struct.new
|
|
1140
|
+
# @return [void]
|
|
1141
|
+
def drop_first_if_str!(args)
|
|
1142
|
+
return unless args.first.is_a?(Parser::AST::Node)
|
|
1143
|
+
return unless args.first.type == :str
|
|
1144
|
+
|
|
1145
|
+
args.shift
|
|
1146
|
+
end
|
|
1147
|
+
|
|
1148
|
+
# Build the container name for a struct constant assignment.
|
|
1149
|
+
#
|
|
1150
|
+
# @private
|
|
1151
|
+
# @param [Parser::AST::Node] node a `:casgn` node
|
|
1152
|
+
# @return [String] the fully qualified container name
|
|
1153
|
+
def struct_container_name(node)
|
|
1154
|
+
scope, name, _value = *node
|
|
1155
|
+
|
|
1156
|
+
prefix =
|
|
1157
|
+
if scope
|
|
1158
|
+
const_name(scope)
|
|
1159
|
+
elsif current_container == 'Object'
|
|
1160
|
+
nil
|
|
1161
|
+
else
|
|
1162
|
+
current_container
|
|
1163
|
+
end
|
|
1164
|
+
|
|
1165
|
+
[prefix, name.to_s].compact.reject(&:empty?).join('::')
|
|
784
1166
|
end
|
|
785
1167
|
|
|
786
1168
|
# Extract a Ruby symbol name from an AST node (`:sym` or `:str`).
|
|
787
1169
|
#
|
|
788
1170
|
# @private
|
|
789
1171
|
# @param [Parser::AST::Node] arg an AST node
|
|
790
|
-
# @return [Symbol
|
|
1172
|
+
# @return [Symbol?] the extracted name or nil
|
|
791
1173
|
def extract_name_sym(arg)
|
|
792
1174
|
case arg.type
|
|
793
1175
|
when :sym then arg.children.first
|
|
@@ -798,16 +1180,14 @@ module Docscribe
|
|
|
798
1180
|
# Build the fully qualified name for a constant node.
|
|
799
1181
|
#
|
|
800
1182
|
# @private
|
|
801
|
-
# @param [Parser::AST::Node
|
|
1183
|
+
# @param [Parser::AST::Node?] node a `:const` or `:cbase` node
|
|
802
1184
|
# @return [String] the resolved constant name
|
|
803
1185
|
def const_name(node)
|
|
804
1186
|
return 'Object' unless node
|
|
805
1187
|
|
|
806
1188
|
case node.type
|
|
807
1189
|
when :const
|
|
808
|
-
|
|
809
|
-
scope_name = scope ? const_name(scope) : nil
|
|
810
|
-
[scope_name, name].compact.join('::')
|
|
1190
|
+
qualified_const_name(node)
|
|
811
1191
|
when :cbase
|
|
812
1192
|
''
|
|
813
1193
|
else
|
|
@@ -815,6 +1195,26 @@ module Docscribe
|
|
|
815
1195
|
end
|
|
816
1196
|
end
|
|
817
1197
|
|
|
1198
|
+
# Build a qualified constant name by joining scope and constant parts.
|
|
1199
|
+
#
|
|
1200
|
+
# @private
|
|
1201
|
+
# @param [Parser::AST::Node] node the `:const` AST node
|
|
1202
|
+
# @return [String] the qualified name (e.g. "Foo::Bar")
|
|
1203
|
+
def qualified_const_name(node)
|
|
1204
|
+
scope, name = *node
|
|
1205
|
+
scope_name = scope ? const_name(scope) : nil
|
|
1206
|
+
[scope_name, name].compact.join('::')
|
|
1207
|
+
end
|
|
1208
|
+
|
|
1209
|
+
# Get the effective container name, using `container_override` when set.
|
|
1210
|
+
#
|
|
1211
|
+
# @private
|
|
1212
|
+
# @param [Docscribe::InlineRewriter::Collector::VisibilityCtx] ctx current visibility context
|
|
1213
|
+
# @return [String] the container name
|
|
1214
|
+
def container_for(ctx)
|
|
1215
|
+
ctx.container_override || current_container
|
|
1216
|
+
end
|
|
1217
|
+
|
|
818
1218
|
# Get the current container name from the name stack.
|
|
819
1219
|
#
|
|
820
1220
|
# @private
|
|
@@ -823,53 +1223,39 @@ module Docscribe
|
|
|
823
1223
|
@name_stack.empty? ? 'Object' : @name_stack.join('::')
|
|
824
1224
|
end
|
|
825
1225
|
|
|
826
|
-
#
|
|
827
|
-
#
|
|
828
|
-
# Handles Sorbet `sig` nodes by deferring them as pending anchors for the
|
|
829
|
-
# next method definition.
|
|
1226
|
+
# Check if a node is a Sorbet `sig` declaration (bare `sig` send or `sig { ... }` block).
|
|
830
1227
|
#
|
|
831
1228
|
# @private
|
|
832
|
-
# @param [Parser::AST::Node
|
|
833
|
-
# @
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
return unless body
|
|
1229
|
+
# @param [Parser::AST::Node?] node an AST node
|
|
1230
|
+
# @return [Boolean]
|
|
1231
|
+
def sorbet_sig_node?(node)
|
|
1232
|
+
return false unless node.is_a?(Parser::AST::Node)
|
|
837
1233
|
|
|
838
|
-
|
|
839
|
-
|
|
1234
|
+
sig_send_node?(node) || sig_block_node?(node)
|
|
1235
|
+
end
|
|
840
1236
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
1237
|
+
# Check if a node is a Sorbet `sig { ... }` block.
|
|
1238
|
+
#
|
|
1239
|
+
# @private
|
|
1240
|
+
# @param [Parser::AST::Node] node an AST node
|
|
1241
|
+
# @return [Boolean]
|
|
1242
|
+
def sig_block_node?(node)
|
|
1243
|
+
return false unless node.type == :block
|
|
846
1244
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
end
|
|
1245
|
+
send_node, *_rest = *node
|
|
1246
|
+
sig_send_node?(send_node)
|
|
850
1247
|
end
|
|
851
1248
|
|
|
852
|
-
# Check if a node is a Sorbet `sig`
|
|
1249
|
+
# Check if a node is a bare Sorbet `sig` send (without block).
|
|
853
1250
|
#
|
|
854
1251
|
# @private
|
|
855
|
-
# @param [Parser::AST::Node
|
|
1252
|
+
# @param [Parser::AST::Node] node an AST node
|
|
856
1253
|
# @return [Boolean]
|
|
857
|
-
def
|
|
858
|
-
return false unless node.
|
|
1254
|
+
def sig_send_node?(node)
|
|
1255
|
+
return false unless node.type == :send
|
|
859
1256
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
recv, meth, *_args = *node
|
|
863
|
-
recv.nil? && meth == :sig
|
|
864
|
-
when :block
|
|
865
|
-
send_node, *_rest = *node
|
|
866
|
-
return false unless send_node&.type == :send
|
|
867
|
-
|
|
868
|
-
recv, meth, *_args = *send_node
|
|
869
|
-
recv.nil? && meth == :sig
|
|
870
|
-
else
|
|
871
|
-
false
|
|
872
|
-
end
|
|
1257
|
+
recv, meth, *_args = *node
|
|
1258
|
+
recv.nil? && meth == :sig
|
|
873
1259
|
end
|
|
874
1260
|
|
|
875
1261
|
# Promote instance methods to class methods for a container under `extend self`.
|