docscribe 1.2.1 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +8 -2
- data/lib/docscribe/config/filtering.rb +2 -2
- data/lib/docscribe/config/plugin.rb +29 -0
- data/lib/docscribe/config/rbs.rb +38 -1
- data/lib/docscribe/config/template.rb +54 -130
- data/lib/docscribe/config.rb +1 -0
- data/lib/docscribe/infer/returns.rb +151 -12
- data/lib/docscribe/infer.rb +7 -2
- data/lib/docscribe/inline_rewriter/collector.rb +144 -97
- data/lib/docscribe/inline_rewriter/doc_block.rb +10 -1
- data/lib/docscribe/inline_rewriter/doc_builder.rb +256 -54
- data/lib/docscribe/inline_rewriter.rb +216 -56
- data/lib/docscribe/plugin/base/collector_plugin.rb +53 -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
data/lib/docscribe/config/rbs.rb
CHANGED
|
@@ -11,6 +11,7 @@ module Docscribe
|
|
|
11
11
|
# @return [Docscribe::Types::RBS::Provider, nil]
|
|
12
12
|
def rbs_provider
|
|
13
13
|
return nil unless rbs_enabled?
|
|
14
|
+
return nil unless ruby_supports_rbs?
|
|
14
15
|
|
|
15
16
|
@rbs_provider ||= begin
|
|
16
17
|
require 'docscribe/types/rbs/provider'
|
|
@@ -30,8 +31,43 @@ module Docscribe
|
|
|
30
31
|
fetch_bool(%w[rbs enabled], false)
|
|
31
32
|
end
|
|
32
33
|
|
|
34
|
+
# Method documentation.
|
|
35
|
+
#
|
|
36
|
+
# @raise [LoadError]
|
|
37
|
+
# @return [Object]
|
|
38
|
+
def core_rbs_provider
|
|
39
|
+
return nil unless ruby_supports_rbs?
|
|
40
|
+
|
|
41
|
+
@core_rbs_provider ||= begin
|
|
42
|
+
require 'docscribe/types/rbs/provider'
|
|
43
|
+
Docscribe::Types::RBS::Provider.new(
|
|
44
|
+
sig_dirs: [],
|
|
45
|
+
collapse_generics: false
|
|
46
|
+
)
|
|
47
|
+
rescue LoadError
|
|
48
|
+
nil
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# Method documentation.
|
|
55
|
+
#
|
|
56
|
+
# @private
|
|
57
|
+
# @return [Boolean]
|
|
58
|
+
def ruby_supports_rbs?
|
|
59
|
+
return true if RUBY_VERSION >= '3.0'
|
|
60
|
+
|
|
61
|
+
@rbs_warning_emitted ||= begin
|
|
62
|
+
warn 'Docscribe: RBS requires Ruby 3.0+. Falling back to inference.'
|
|
63
|
+
true
|
|
64
|
+
end
|
|
65
|
+
false
|
|
66
|
+
end
|
|
67
|
+
|
|
33
68
|
# Signature directories used by the RBS provider.
|
|
34
69
|
#
|
|
70
|
+
# @private
|
|
35
71
|
# @return [Array<String>]
|
|
36
72
|
def rbs_sig_dirs
|
|
37
73
|
Array(raw.dig('rbs', 'sig_dirs') || DEFAULT.dig('rbs', 'sig_dirs')).map(&:to_s)
|
|
@@ -43,7 +79,8 @@ module Docscribe
|
|
|
43
79
|
# - `Hash<Symbol, String>` => `Hash`
|
|
44
80
|
# - `Array<Integer>` => `Array`
|
|
45
81
|
#
|
|
46
|
-
# @
|
|
82
|
+
# @private
|
|
83
|
+
# @return [Object]
|
|
47
84
|
def rbs_collapse_generics?
|
|
48
85
|
fetch_bool(%w[rbs collapse_generics], false)
|
|
49
86
|
end
|
|
@@ -14,170 +14,94 @@ module Docscribe
|
|
|
14
14
|
---
|
|
15
15
|
# Docscribe configuration file
|
|
16
16
|
#
|
|
17
|
-
#
|
|
18
|
-
# bundle exec docscribe lib
|
|
19
|
-
#
|
|
20
|
-
# Apply safe doc updates:
|
|
21
|
-
# bundle exec docscribe -a lib
|
|
22
|
-
#
|
|
23
|
-
# Apply aggressive doc updates (rebuild existing doc blocks):
|
|
24
|
-
# bundle exec docscribe -A lib
|
|
17
|
+
# Docscribe works without this file — create it only for customization.
|
|
25
18
|
#
|
|
19
|
+
# Quick start:
|
|
20
|
+
# bundle exec docscribe lib # check what would change
|
|
21
|
+
# bundle exec docscribe -a lib # apply safe updates
|
|
22
|
+
# bundle exec docscribe -A lib # rebuild all doc blocks
|
|
26
23
|
|
|
27
24
|
emit:
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
include_param_documentation: true
|
|
40
|
-
|
|
41
|
-
# Emit @param tags.
|
|
42
|
-
param_tags: true
|
|
43
|
-
|
|
44
|
-
# Emit @return tag (can be overridden per scope/visibility under methods:).
|
|
45
|
-
return_tag: true
|
|
46
|
-
|
|
47
|
-
# Emit @private / @protected tags based on Ruby visibility context.
|
|
48
|
-
visibility_tags: true
|
|
49
|
-
|
|
50
|
-
# Emit @raise tags inferred from rescue clauses / raise/fail calls.
|
|
51
|
-
raise_tags: true
|
|
52
|
-
|
|
53
|
-
# Emit conditional rescue return tags:
|
|
54
|
-
#
|
|
55
|
-
# @return [String] if FooError, BarError
|
|
56
|
-
rescue_conditional_returns: true
|
|
57
|
-
|
|
58
|
-
# Generate @!attribute docs for attr_reader/attr_writer/attr_accessor.
|
|
59
|
-
attributes: false
|
|
25
|
+
# What to include in generated documentation
|
|
26
|
+
header: false # +MyClass#foo+ -> ReturnType
|
|
27
|
+
param_tags: true # @param tags
|
|
28
|
+
return_tag: true # @return tag
|
|
29
|
+
visibility_tags: true # @private / @protected
|
|
30
|
+
raise_tags: true # @raise tags
|
|
31
|
+
rescue_conditional_returns: true # @return [Type] if Error
|
|
32
|
+
attributes: false # @!attribute for attr_*
|
|
33
|
+
|
|
34
|
+
# Placeholder text for generated docs
|
|
35
|
+
include_default_message: true # "Method documentation."
|
|
36
|
+
include_param_documentation: true # "Param documentation."
|
|
60
37
|
|
|
61
38
|
doc:
|
|
62
|
-
# Default text
|
|
39
|
+
# Default text and formatting
|
|
63
40
|
default_message: "Method documentation."
|
|
64
|
-
|
|
65
|
-
# Default text appended to generated @param tags.
|
|
66
41
|
param_documentation: "Param documentation."
|
|
42
|
+
param_tag_style: "type_name" # "type_name" or "name_type"
|
|
43
|
+
sort_tags: true
|
|
44
|
+
tag_order: ["todo", "note", "api", "private", "protected", "param", "option", "yieldparam", "raise", "return"]
|
|
67
45
|
|
|
68
|
-
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
|
|
46
|
+
inference:
|
|
47
|
+
# Type inference behavior
|
|
48
|
+
fallback_type: "Object" # when uncertain
|
|
49
|
+
nil_as_optional: true # String | nil => String?
|
|
50
|
+
treat_options_keyword_as_hash: true # options: keyword => Hash
|
|
72
51
|
|
|
73
|
-
|
|
74
|
-
|
|
52
|
+
filter:
|
|
53
|
+
# Which methods and files to process
|
|
54
|
+
# Method format: "Container#method" (instance) or "Container.method" (class)
|
|
55
|
+
# Supports globs ("*#initialize") and regex ("/^MyApp::.*$/")
|
|
56
|
+
include: []
|
|
57
|
+
exclude: []
|
|
58
|
+
visibilities: ["public", "protected", "private"]
|
|
59
|
+
scopes: ["instance", "class"]
|
|
75
60
|
|
|
76
|
-
|
|
77
|
-
|
|
61
|
+
files:
|
|
62
|
+
# File paths relative to project root (globs or /regex/)
|
|
63
|
+
include: []
|
|
64
|
+
exclude: ["spec"]
|
|
78
65
|
|
|
79
66
|
methods:
|
|
80
|
-
#
|
|
67
|
+
# Override defaults per scope and visibility.
|
|
68
|
+
# Empty {} means "use values from `doc` section".
|
|
81
69
|
#
|
|
82
70
|
# Example:
|
|
83
|
-
# methods:
|
|
84
71
|
# instance:
|
|
85
72
|
# public:
|
|
86
73
|
# default_message: "Public API."
|
|
87
|
-
#
|
|
74
|
+
# private:
|
|
75
|
+
# return_tag: false
|
|
88
76
|
instance:
|
|
89
77
|
public: {}
|
|
90
78
|
protected: {}
|
|
91
79
|
private: {}
|
|
92
|
-
|
|
93
80
|
class:
|
|
94
81
|
public: {}
|
|
95
82
|
protected: {}
|
|
96
83
|
private: {}
|
|
97
84
|
|
|
98
|
-
inference:
|
|
99
|
-
# Type used when inference is uncertain.
|
|
100
|
-
fallback_type: "Object"
|
|
101
|
-
|
|
102
|
-
# Whether nil unions become optional types (for example String | nil => String?).
|
|
103
|
-
nil_as_optional: true
|
|
104
|
-
|
|
105
|
-
# Special-case: treat keyword arg named options/options: as a Hash.
|
|
106
|
-
treat_options_keyword_as_hash: true
|
|
107
|
-
|
|
108
|
-
filter:
|
|
109
|
-
# Filter which methods Docscribe touches.
|
|
110
|
-
#
|
|
111
|
-
# Method id format:
|
|
112
|
-
# instance: "MyModule::MyClass#instance_method"
|
|
113
|
-
# class: "MyModule::MyClass.class_method"
|
|
114
|
-
#
|
|
115
|
-
# Patterns:
|
|
116
|
-
# - glob: "*#initialize", "MyApp::*#*"
|
|
117
|
-
# - regex: "/^MyApp::.*#(foo|bar)$/"
|
|
118
|
-
#
|
|
119
|
-
# Semantics:
|
|
120
|
-
# - scopes / visibilities act as allow-lists
|
|
121
|
-
# - exclude wins
|
|
122
|
-
# - if include is empty => include everything (subject to allow-lists)
|
|
123
|
-
visibilities: ["public", "protected", "private"]
|
|
124
|
-
scopes: ["instance", "class"]
|
|
125
|
-
include: []
|
|
126
|
-
exclude: []
|
|
127
|
-
|
|
128
|
-
files:
|
|
129
|
-
# Filter which files Docscribe processes (paths are matched relative
|
|
130
|
-
# to the project root).
|
|
131
|
-
#
|
|
132
|
-
# Tips:
|
|
133
|
-
# - Use directory shorthand to exclude a whole directory:
|
|
134
|
-
# exclude: ["spec"]
|
|
135
|
-
# - Or use globs:
|
|
136
|
-
# exclude: ["spec/**/*.rb", "vendor/**/*.rb"]
|
|
137
|
-
include: []
|
|
138
|
-
exclude: ["spec"]
|
|
139
|
-
|
|
140
85
|
rbs:
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
# CLI equivalent:
|
|
144
|
-
# bundle exec docscribe -a --rbs --sig-dir sig lib
|
|
145
|
-
#
|
|
146
|
-
# Under Bundler, you may need `gem "rbs"` in your Gemfile (or a
|
|
147
|
-
# Gemfile that includes it), otherwise `require "rbs"` may fail and
|
|
148
|
-
# Docscribe will fall back to inference.
|
|
86
|
+
# Use RBS signatures for better types (requires `gem "rbs"`)
|
|
149
87
|
enabled: false
|
|
150
|
-
|
|
151
|
-
# Signature directories (repeatable via --sig-dir).
|
|
152
88
|
sig_dirs: ["sig"]
|
|
153
|
-
|
|
154
|
-
#
|
|
155
|
-
# - Hash<Symbol, String> => Hash
|
|
156
|
-
# - Array<Integer> => Array
|
|
157
|
-
collapse_generics: false
|
|
89
|
+
collapse_generics: false # Hash<Symbol, String> => Hash
|
|
90
|
+
collection: false # auto-discover from rbs_collection.lock.yaml
|
|
158
91
|
|
|
159
92
|
sorbet:
|
|
160
|
-
#
|
|
161
|
-
# RBI files to improve @param / @return types.
|
|
162
|
-
#
|
|
163
|
-
# CLI equivalent:
|
|
164
|
-
# bundle exec docscribe -a --sorbet --rbi-dir sorbet/rbi lib
|
|
165
|
-
#
|
|
166
|
-
# Sorbet resolution order is:
|
|
167
|
-
# 1. inline `sig` in the current source file
|
|
168
|
-
# 2. RBI files
|
|
169
|
-
# 3. RBS
|
|
170
|
-
# 4. AST inference
|
|
93
|
+
# Use Sorbet inline sigs and RBI files for better types
|
|
171
94
|
enabled: false
|
|
172
|
-
|
|
173
|
-
# RBI directories scanned recursively for `.rbi` files
|
|
174
|
-
# (repeatable via --rbi-dir).
|
|
175
95
|
rbi_dirs: ["sorbet/rbi", "rbi"]
|
|
176
|
-
|
|
177
|
-
# If true, simplify generic types:
|
|
178
|
-
# - Hash<Symbol, String> => Hash
|
|
179
|
-
# - Array<Integer> => Array
|
|
180
96
|
collapse_generics: false
|
|
97
|
+
|
|
98
|
+
plugins:
|
|
99
|
+
# Load custom plugins
|
|
100
|
+
# Example:
|
|
101
|
+
# require:
|
|
102
|
+
# - ./docscribe_plugins
|
|
103
|
+
# - docscribe-rails-associations
|
|
104
|
+
require: []
|
|
181
105
|
YAML
|
|
182
106
|
end
|
|
183
107
|
end
|
data/lib/docscribe/config.rb
CHANGED
|
@@ -24,7 +24,9 @@ module Docscribe
|
|
|
24
24
|
return FALLBACK_TYPE unless root && %i[def defs].include?(root.type)
|
|
25
25
|
|
|
26
26
|
body = root.children.last
|
|
27
|
-
|
|
27
|
+
local_var_types = build_local_variable_types(body)
|
|
28
|
+
last_expr_type(body, fallback_type: FALLBACK_TYPE, nil_as_optional: true,
|
|
29
|
+
local_var_types: local_var_types) || FALLBACK_TYPE
|
|
28
30
|
rescue Parser::SyntaxError
|
|
29
31
|
FALLBACK_TYPE
|
|
30
32
|
end
|
|
@@ -43,7 +45,9 @@ module Docscribe
|
|
|
43
45
|
|
|
44
46
|
return FALLBACK_TYPE unless body
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
local_var_types = build_local_variable_types(body)
|
|
49
|
+
last_expr_type(body, fallback_type: FALLBACK_TYPE, nil_as_optional: true,
|
|
50
|
+
local_var_types: local_var_types) || FALLBACK_TYPE
|
|
47
51
|
end
|
|
48
52
|
|
|
49
53
|
# Return a structured return-type spec for a method node.
|
|
@@ -56,8 +60,11 @@ module Docscribe
|
|
|
56
60
|
# @param [Parser::AST::Node] node `:def` or `:defs` node
|
|
57
61
|
# @param [String] fallback_type type used when inference is uncertain
|
|
58
62
|
# @param [Boolean] nil_as_optional whether `nil` unions should be rendered as optional types
|
|
63
|
+
# @param [nil] core_rbs_provider Param documentation.
|
|
64
|
+
# @param [nil] param_types Param documentation.
|
|
59
65
|
# @return [Hash]
|
|
60
|
-
def returns_spec_from_node(node, fallback_type: FALLBACK_TYPE, nil_as_optional: true
|
|
66
|
+
def returns_spec_from_node(node, fallback_type: FALLBACK_TYPE, nil_as_optional: true, core_rbs_provider: nil,
|
|
67
|
+
param_types: nil)
|
|
61
68
|
body =
|
|
62
69
|
case node.type
|
|
63
70
|
when :def then node.children[2]
|
|
@@ -67,10 +74,16 @@ module Docscribe
|
|
|
67
74
|
spec = { normal: FALLBACK_TYPE, rescues: [] }
|
|
68
75
|
return spec unless body
|
|
69
76
|
|
|
77
|
+
local_var_types = build_local_variable_types(body)
|
|
78
|
+
|
|
70
79
|
if body.type == :rescue
|
|
71
80
|
main_body = body.children[0]
|
|
81
|
+
rescue_local_var_types = build_local_variable_types(body)
|
|
82
|
+
all_local_var_types = rescue_local_var_types || local_var_types
|
|
72
83
|
spec[:normal] =
|
|
73
|
-
last_expr_type(main_body, fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
84
|
+
last_expr_type(main_body, fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
85
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
86
|
+
local_var_types: all_local_var_types) || FALLBACK_TYPE
|
|
74
87
|
|
|
75
88
|
body.children.each do |ch|
|
|
76
89
|
next unless ch.is_a?(Parser::AST::Node) && ch.type == :resbody
|
|
@@ -78,18 +91,51 @@ module Docscribe
|
|
|
78
91
|
exc_list, _asgn, rescue_body = *ch
|
|
79
92
|
exc_names = Raises.exception_names_from_rescue_list(exc_list)
|
|
80
93
|
rtype =
|
|
81
|
-
last_expr_type(rescue_body, fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
94
|
+
last_expr_type(rescue_body, fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
95
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
96
|
+
local_var_types: all_local_var_types) ||
|
|
82
97
|
fallback_type
|
|
83
98
|
spec[:rescues] << [exc_names, rtype]
|
|
84
99
|
end
|
|
85
100
|
else
|
|
86
101
|
spec[:normal] =
|
|
87
|
-
last_expr_type(body, fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
102
|
+
last_expr_type(body, fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
103
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
104
|
+
local_var_types: local_var_types) || FALLBACK_TYPE
|
|
88
105
|
end
|
|
89
106
|
|
|
90
107
|
spec
|
|
91
108
|
end
|
|
92
109
|
|
|
110
|
+
# Resolve a return type from core RBS for a method call.
|
|
111
|
+
#
|
|
112
|
+
# @note module_function: when included, also defines #resolve_rbs_return_type (instance visibility: private)
|
|
113
|
+
# @private
|
|
114
|
+
# @param [Object] node Param documentation.
|
|
115
|
+
# @return [String] FALLBACK_TYPE if lookup fails
|
|
116
|
+
def build_local_variable_types(node)
|
|
117
|
+
types = {}
|
|
118
|
+
ASTWalk.walk(node) do |n|
|
|
119
|
+
case n.type
|
|
120
|
+
when :lvasgn, :gvasgn, :ivasgn
|
|
121
|
+
name = n.children[0].to_s
|
|
122
|
+
value = n.children[1]
|
|
123
|
+
if value
|
|
124
|
+
inferred = Literals.type_from_literal(value, fallback_type: FALLBACK_TYPE)
|
|
125
|
+
types[name] = inferred if inferred && inferred != FALLBACK_TYPE
|
|
126
|
+
end
|
|
127
|
+
when :casgn
|
|
128
|
+
name = n.children[0].to_s
|
|
129
|
+
value = n.children[2]
|
|
130
|
+
if value
|
|
131
|
+
inferred = Literals.type_from_literal(value, fallback_type: FALLBACK_TYPE)
|
|
132
|
+
types[name] = inferred if inferred && inferred != FALLBACK_TYPE
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
types.empty? ? nil : types
|
|
137
|
+
end
|
|
138
|
+
|
|
93
139
|
# Infer the type of the last expression in a node.
|
|
94
140
|
#
|
|
95
141
|
# Supports:
|
|
@@ -98,30 +144,45 @@ module Docscribe
|
|
|
98
144
|
# - `case` expressions
|
|
99
145
|
# - explicit `return`
|
|
100
146
|
# - literal-like expressions via {Literals.type_from_literal}
|
|
147
|
+
# - method calls with RBS core type lookup
|
|
101
148
|
#
|
|
102
149
|
# @note module_function: when included, also defines #last_expr_type (instance visibility: private)
|
|
103
150
|
# @param [Parser::AST::Node, nil] node expression node
|
|
104
151
|
# @param [String] fallback_type type used when inference is uncertain
|
|
105
152
|
# @param [Boolean] nil_as_optional whether `nil` unions should be rendered as optional types
|
|
153
|
+
# @param [Object, nil] core_rbs_provider optional RBS provider for core type lookup
|
|
154
|
+
# @param [Hash, nil] param_types parameter name -> type map for lvar resolution
|
|
155
|
+
# @param [nil] local_var_types Param documentation.
|
|
106
156
|
# @return [String, nil]
|
|
107
|
-
def last_expr_type(node, fallback_type:, nil_as_optional:
|
|
157
|
+
def last_expr_type(node, fallback_type:, nil_as_optional:, core_rbs_provider: nil, param_types: nil,
|
|
158
|
+
local_var_types: nil)
|
|
108
159
|
return nil unless node
|
|
109
160
|
|
|
110
161
|
case node.type
|
|
111
162
|
when :begin
|
|
112
|
-
last_expr_type(node.children.last, fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
163
|
+
last_expr_type(node.children.last, fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
164
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
165
|
+
local_var_types: local_var_types)
|
|
113
166
|
|
|
114
167
|
when :if
|
|
115
|
-
t = last_expr_type(node.children[1], fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
116
|
-
|
|
168
|
+
t = last_expr_type(node.children[1], fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
169
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
170
|
+
local_var_types: local_var_types)
|
|
171
|
+
e = last_expr_type(node.children[2], fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
172
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
173
|
+
local_var_types: local_var_types)
|
|
117
174
|
unify_types(t, e, fallback_type: fallback_type, nil_as_optional: nil_as_optional)
|
|
118
175
|
|
|
119
176
|
when :case
|
|
120
177
|
branches = node.children[1..].compact.flat_map do |child|
|
|
121
178
|
if child.type == :when
|
|
122
|
-
last_expr_type(child.children.last, fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
179
|
+
last_expr_type(child.children.last, fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
180
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
181
|
+
local_var_types: local_var_types)
|
|
123
182
|
else
|
|
124
|
-
last_expr_type(child, fallback_type: fallback_type, nil_as_optional: nil_as_optional
|
|
183
|
+
last_expr_type(child, fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
184
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
185
|
+
local_var_types: local_var_types)
|
|
125
186
|
end
|
|
126
187
|
end.compact
|
|
127
188
|
|
|
@@ -136,11 +197,89 @@ module Docscribe
|
|
|
136
197
|
when :return
|
|
137
198
|
Literals.type_from_literal(node.children.first, fallback_type: fallback_type)
|
|
138
199
|
|
|
200
|
+
when :block
|
|
201
|
+
send_node = node.children[0]
|
|
202
|
+
if send_node&.type == :send
|
|
203
|
+
recv = send_node.children[0]
|
|
204
|
+
meth = send_node.children[1]
|
|
205
|
+
|
|
206
|
+
if core_rbs_provider && recv&.type == :lvar
|
|
207
|
+
lvar_name = recv.children.first
|
|
208
|
+
recv_type = nil
|
|
209
|
+
recv_type = local_var_types[lvar_name.to_s] if local_var_types && lvar_name
|
|
210
|
+
recv_type = param_types[lvar_name.to_s] if !recv_type && param_types && lvar_name
|
|
211
|
+
if recv_type
|
|
212
|
+
rbs_type = resolve_rbs_return_type(recv_type, meth, core_rbs_provider)
|
|
213
|
+
return rbs_type unless rbs_type == FALLBACK_TYPE
|
|
214
|
+
end
|
|
215
|
+
elsif core_rbs_provider && recv&.type == :send
|
|
216
|
+
inner_type = last_expr_type(recv, fallback_type: nil, nil_as_optional: false,
|
|
217
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
218
|
+
local_var_types: local_var_types)
|
|
219
|
+
if inner_type
|
|
220
|
+
rbs_type = resolve_rbs_return_type(inner_type, meth, core_rbs_provider)
|
|
221
|
+
return rbs_type unless rbs_type == FALLBACK_TYPE
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
last_expr_type(node.children[2], fallback_type: fallback_type, nil_as_optional: nil_as_optional,
|
|
227
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
228
|
+
local_var_types: local_var_types)
|
|
229
|
+
|
|
230
|
+
when :send
|
|
231
|
+
recv = node.children[0]
|
|
232
|
+
meth = node.children[1]
|
|
233
|
+
|
|
234
|
+
# Try to resolve return type from RBS core for method calls
|
|
235
|
+
if core_rbs_provider && recv&.type == :send
|
|
236
|
+
# Chained call: arg.to_i.positive?
|
|
237
|
+
inner_type = last_expr_type(recv, fallback_type: nil, nil_as_optional: false,
|
|
238
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
239
|
+
local_var_types: local_var_types)
|
|
240
|
+
if inner_type
|
|
241
|
+
rbs_type = resolve_rbs_return_type(inner_type, meth, core_rbs_provider)
|
|
242
|
+
return rbs_type unless rbs_type == FALLBACK_TYPE
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
elsif core_rbs_provider && recv&.type == :lvar
|
|
246
|
+
# Direct call on local variable: p1.positive? or admins.any?
|
|
247
|
+
lvar_name = recv.children.first
|
|
248
|
+
recv_type = nil
|
|
249
|
+
recv_type = local_var_types[lvar_name.to_s] if local_var_types && lvar_name
|
|
250
|
+
recv_type = param_types[lvar_name.to_s] if !recv_type && param_types && lvar_name
|
|
251
|
+
if recv_type
|
|
252
|
+
rbs_type = resolve_rbs_return_type(recv_type, meth, core_rbs_provider)
|
|
253
|
+
return rbs_type unless rbs_type == FALLBACK_TYPE
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
Literals.type_from_literal(node, fallback_type: fallback_type)
|
|
258
|
+
|
|
139
259
|
else
|
|
140
260
|
Literals.type_from_literal(node, fallback_type: fallback_type)
|
|
141
261
|
end
|
|
142
262
|
end
|
|
143
263
|
|
|
264
|
+
# Method documentation.
|
|
265
|
+
#
|
|
266
|
+
# @note module_function: when included, also defines #resolve_rbs_return_type (instance visibility: private)
|
|
267
|
+
# @param [Object] container_type Param documentation.
|
|
268
|
+
# @param [Object] method_name Param documentation.
|
|
269
|
+
# @param [Object] core_rbs_provider Param documentation.
|
|
270
|
+
# @return [Object]
|
|
271
|
+
def resolve_rbs_return_type(container_type, method_name, core_rbs_provider)
|
|
272
|
+
return FALLBACK_TYPE unless core_rbs_provider
|
|
273
|
+
|
|
274
|
+
sig = core_rbs_provider.signature_for(
|
|
275
|
+
container: container_type,
|
|
276
|
+
scope: :instance,
|
|
277
|
+
name: method_name
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
sig&.return_type || FALLBACK_TYPE
|
|
281
|
+
end
|
|
282
|
+
|
|
144
283
|
# Unify two inferred types into a single type string.
|
|
145
284
|
#
|
|
146
285
|
# 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
|
|