docscribe 1.3.0 → 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/lib/docscribe/config/defaults.rb +5 -2
- data/lib/docscribe/config/rbs.rb +38 -1
- data/lib/docscribe/config/template.rb +54 -147
- data/lib/docscribe/infer/returns.rb +103 -25
- data/lib/docscribe/inline_rewriter/doc_block.rb +10 -1
- data/lib/docscribe/inline_rewriter/doc_builder.rb +66 -9
- data/lib/docscribe/inline_rewriter.rb +20 -19
- data/lib/docscribe/plugin/base/collector_plugin.rb +0 -2
- data/lib/docscribe/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 911fd61018509316b42bf5a799232d24be36cb653d5997c4e170dab5db028884
|
|
4
|
+
data.tar.gz: 4ce0e7f0cc78c6b94ce366cb464726e083a6050a42f835e35995c378072507e6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 107a67dd5c484b840ba5ab85918ec25d53b0ce4adda13e4ef612d3bfa99d4201e82b474cd636f79a60e5e40e5cbaeb8e15c899939b5e74df14f397f4188b4168
|
|
7
|
+
data.tar.gz: 9cac2bb4b0c37b29707f0d6beec99987ab8d0f3d9327b3f44e9684816694e74fced394d88fc493ee6f5dc19ce07b82fc295e38f9c0273faad09976d125103a9e
|
|
@@ -14,7 +14,9 @@ module Docscribe
|
|
|
14
14
|
# - optional Sorbet integration
|
|
15
15
|
DEFAULT = {
|
|
16
16
|
'emit' => {
|
|
17
|
-
'header' =>
|
|
17
|
+
'header' => false,
|
|
18
|
+
'include_default_message' => true,
|
|
19
|
+
'include_param_documentation' => true,
|
|
18
20
|
'param_tags' => true,
|
|
19
21
|
'return_tag' => true,
|
|
20
22
|
'visibility_tags' => true,
|
|
@@ -53,11 +55,12 @@ module Docscribe
|
|
|
53
55
|
'exclude' => [],
|
|
54
56
|
'files' => {
|
|
55
57
|
'include' => [],
|
|
56
|
-
'exclude' => []
|
|
58
|
+
'exclude' => ['spec']
|
|
57
59
|
}
|
|
58
60
|
},
|
|
59
61
|
'rbs' => {
|
|
60
62
|
'enabled' => false,
|
|
63
|
+
'collection' => false,
|
|
61
64
|
'sig_dirs' => ['sig'],
|
|
62
65
|
'collapse_generics' => false
|
|
63
66
|
},
|
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,187 +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
|
-
plugins:
|
|
141
|
-
# Load custom plugins by path or gem name.
|
|
142
|
-
#
|
|
143
|
-
# Each entry is passed to `require`. Registration happens inside
|
|
144
|
-
# the required file via Docscribe::Plugin::Registry.register.
|
|
145
|
-
#
|
|
146
|
-
# Example:
|
|
147
|
-
# require:
|
|
148
|
-
# - ./docscribe_plugins
|
|
149
|
-
# - docscribe-rails-associations
|
|
150
|
-
require: []
|
|
151
|
-
|
|
152
85
|
rbs:
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
# CLI equivalent:
|
|
156
|
-
# bundle exec docscribe -a --rbs --sig-dir sig lib
|
|
157
|
-
#
|
|
158
|
-
# Under Bundler, you may need `gem "rbs"` in your Gemfile (or a
|
|
159
|
-
# Gemfile that includes it), otherwise `require "rbs"` may fail and
|
|
160
|
-
# Docscribe will fall back to inference.
|
|
86
|
+
# Use RBS signatures for better types (requires `gem "rbs"`)
|
|
161
87
|
enabled: false
|
|
162
|
-
|
|
163
|
-
# Signature directories (repeatable via --sig-dir).
|
|
164
88
|
sig_dirs: ["sig"]
|
|
165
|
-
|
|
166
|
-
#
|
|
167
|
-
# - Hash<Symbol, String> => Hash
|
|
168
|
-
# - Array<Integer> => Array
|
|
169
|
-
collapse_generics: false
|
|
170
|
-
# Auto-discover RBS collection from rbs_collection.lock.yaml.
|
|
171
|
-
# Equivalent to --rbs-collection CLI flag.
|
|
172
|
-
# Requires `bundle exec rbs collection install` to have been run.
|
|
173
|
-
#
|
|
174
|
-
collection: false
|
|
89
|
+
collapse_generics: false # Hash<Symbol, String> => Hash
|
|
90
|
+
collection: false # auto-discover from rbs_collection.lock.yaml
|
|
175
91
|
|
|
176
92
|
sorbet:
|
|
177
|
-
#
|
|
178
|
-
# RBI files to improve @param / @return types.
|
|
179
|
-
#
|
|
180
|
-
# CLI equivalent:
|
|
181
|
-
# bundle exec docscribe -a --sorbet --rbi-dir sorbet/rbi lib
|
|
182
|
-
#
|
|
183
|
-
# Sorbet resolution order is:
|
|
184
|
-
# 1. inline `sig` in the current source file
|
|
185
|
-
# 2. RBI files
|
|
186
|
-
# 3. RBS
|
|
187
|
-
# 4. AST inference
|
|
93
|
+
# Use Sorbet inline sigs and RBI files for better types
|
|
188
94
|
enabled: false
|
|
189
|
-
|
|
190
|
-
# RBI directories scanned recursively for `.rbi` files
|
|
191
|
-
# (repeatable via --rbi-dir).
|
|
192
95
|
rbi_dirs: ["sorbet/rbi", "rbi"]
|
|
193
|
-
|
|
194
|
-
# If true, simplify generic types:
|
|
195
|
-
# - Hash<Symbol, String> => Hash
|
|
196
|
-
# - Array<Integer> => Array
|
|
197
96
|
collapse_generics: false
|
|
97
|
+
|
|
98
|
+
plugins:
|
|
99
|
+
# Load custom plugins
|
|
100
|
+
# Example:
|
|
101
|
+
# require:
|
|
102
|
+
# - ./docscribe_plugins
|
|
103
|
+
# - docscribe-rails-associations
|
|
104
|
+
require: []
|
|
198
105
|
YAML
|
|
199
106
|
end
|
|
200
107
|
end
|
|
@@ -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.
|
|
@@ -70,11 +74,16 @@ module Docscribe
|
|
|
70
74
|
spec = { normal: FALLBACK_TYPE, rescues: [] }
|
|
71
75
|
return spec unless body
|
|
72
76
|
|
|
77
|
+
local_var_types = build_local_variable_types(body)
|
|
78
|
+
|
|
73
79
|
if body.type == :rescue
|
|
74
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
|
|
75
83
|
spec[:normal] =
|
|
76
84
|
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
|
|
85
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
86
|
+
local_var_types: all_local_var_types) || FALLBACK_TYPE
|
|
78
87
|
|
|
79
88
|
body.children.each do |ch|
|
|
80
89
|
next unless ch.is_a?(Parser::AST::Node) && ch.type == :resbody
|
|
@@ -83,19 +92,50 @@ module Docscribe
|
|
|
83
92
|
exc_names = Raises.exception_names_from_rescue_list(exc_list)
|
|
84
93
|
rtype =
|
|
85
94
|
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
|
|
95
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
96
|
+
local_var_types: all_local_var_types) ||
|
|
87
97
|
fallback_type
|
|
88
98
|
spec[:rescues] << [exc_names, rtype]
|
|
89
99
|
end
|
|
90
100
|
else
|
|
91
101
|
spec[:normal] =
|
|
92
102
|
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
|
|
103
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
104
|
+
local_var_types: local_var_types) || FALLBACK_TYPE
|
|
94
105
|
end
|
|
95
106
|
|
|
96
107
|
spec
|
|
97
108
|
end
|
|
98
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
|
+
|
|
99
139
|
# Infer the type of the last expression in a node.
|
|
100
140
|
#
|
|
101
141
|
# Supports:
|
|
@@ -112,30 +152,37 @@ module Docscribe
|
|
|
112
152
|
# @param [Boolean] nil_as_optional whether `nil` unions should be rendered as optional types
|
|
113
153
|
# @param [Object, nil] core_rbs_provider optional RBS provider for core type lookup
|
|
114
154
|
# @param [Hash, nil] param_types parameter name -> type map for lvar resolution
|
|
155
|
+
# @param [nil] local_var_types Param documentation.
|
|
115
156
|
# @return [String, nil]
|
|
116
|
-
def last_expr_type(node, fallback_type:, nil_as_optional:, core_rbs_provider: nil, param_types: nil
|
|
157
|
+
def last_expr_type(node, fallback_type:, nil_as_optional:, core_rbs_provider: nil, param_types: nil,
|
|
158
|
+
local_var_types: nil)
|
|
117
159
|
return nil unless node
|
|
118
160
|
|
|
119
161
|
case node.type
|
|
120
162
|
when :begin
|
|
121
163
|
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
|
|
164
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
165
|
+
local_var_types: local_var_types)
|
|
123
166
|
|
|
124
167
|
when :if
|
|
125
168
|
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
|
|
169
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
170
|
+
local_var_types: local_var_types)
|
|
127
171
|
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
|
|
172
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
173
|
+
local_var_types: local_var_types)
|
|
129
174
|
unify_types(t, e, fallback_type: fallback_type, nil_as_optional: nil_as_optional)
|
|
130
175
|
|
|
131
176
|
when :case
|
|
132
177
|
branches = node.children[1..].compact.flat_map do |child|
|
|
133
178
|
if child.type == :when
|
|
134
179
|
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
|
|
180
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
181
|
+
local_var_types: local_var_types)
|
|
136
182
|
else
|
|
137
183
|
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
|
|
184
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
185
|
+
local_var_types: local_var_types)
|
|
139
186
|
end
|
|
140
187
|
end.compact
|
|
141
188
|
|
|
@@ -150,6 +197,36 @@ module Docscribe
|
|
|
150
197
|
when :return
|
|
151
198
|
Literals.type_from_literal(node.children.first, fallback_type: fallback_type)
|
|
152
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
|
+
|
|
153
230
|
when :send
|
|
154
231
|
recv = node.children[0]
|
|
155
232
|
meth = node.children[1]
|
|
@@ -158,20 +235,22 @@ module Docscribe
|
|
|
158
235
|
if core_rbs_provider && recv&.type == :send
|
|
159
236
|
# Chained call: arg.to_i.positive?
|
|
160
237
|
inner_type = last_expr_type(recv, fallback_type: nil, nil_as_optional: false,
|
|
161
|
-
core_rbs_provider: core_rbs_provider, param_types: param_types
|
|
238
|
+
core_rbs_provider: core_rbs_provider, param_types: param_types,
|
|
239
|
+
local_var_types: local_var_types)
|
|
162
240
|
if inner_type
|
|
163
241
|
rbs_type = resolve_rbs_return_type(inner_type, meth, core_rbs_provider)
|
|
164
242
|
return rbs_type unless rbs_type == FALLBACK_TYPE
|
|
165
243
|
end
|
|
244
|
+
|
|
166
245
|
elsif core_rbs_provider && recv&.type == :lvar
|
|
167
|
-
# Direct call:
|
|
246
|
+
# Direct call on local variable: p1.positive? or admins.any?
|
|
168
247
|
lvar_name = recv.children.first
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
|
175
254
|
end
|
|
176
255
|
end
|
|
177
256
|
|
|
@@ -182,14 +261,13 @@ module Docscribe
|
|
|
182
261
|
end
|
|
183
262
|
end
|
|
184
263
|
|
|
185
|
-
#
|
|
264
|
+
# Method documentation.
|
|
186
265
|
#
|
|
187
266
|
# @note module_function: when included, also defines #resolve_rbs_return_type (instance visibility: private)
|
|
188
|
-
# @
|
|
189
|
-
# @param [
|
|
190
|
-
# @param [
|
|
191
|
-
# @
|
|
192
|
-
# @return [String] FALLBACK_TYPE if lookup fails
|
|
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]
|
|
193
271
|
def resolve_rbs_return_type(container_type, method_name, core_rbs_provider)
|
|
194
272
|
return FALLBACK_TYPE unless core_rbs_provider
|
|
195
273
|
|
|
@@ -65,11 +65,20 @@ module Docscribe
|
|
|
65
65
|
# @param [Array<String>] missing_lines generated tag lines to add
|
|
66
66
|
# @param [Boolean] sort_tags whether sortable tags should be reordered
|
|
67
67
|
# @param [Array<String>] tag_order configured sortable tag order
|
|
68
|
+
# @param [Hash] filter_existing Param documentation.
|
|
68
69
|
# @return [Array<String>]
|
|
69
|
-
def merge(existing_lines, missing_lines:, sort_tags:, tag_order:)
|
|
70
|
+
def merge(existing_lines, missing_lines:, sort_tags:, tag_order:, filter_existing: {})
|
|
70
71
|
existing_entries = parse(existing_lines, tag_order: tag_order)
|
|
71
72
|
missing_entries = parse_generated(missing_lines, tag_order: tag_order)
|
|
72
73
|
|
|
74
|
+
filter_param_names = filter_existing[:param_names] || []
|
|
75
|
+
filter_return = !!filter_existing[:return]
|
|
76
|
+
|
|
77
|
+
existing_entries = existing_entries.reject do |e|
|
|
78
|
+
(e.kind == :tag && e.tag == 'param' && filter_param_names.include?(e.subject)) ||
|
|
79
|
+
(e.kind == :tag && e.tag == 'return' && filter_return)
|
|
80
|
+
end
|
|
81
|
+
|
|
73
82
|
entries = existing_entries + missing_entries
|
|
74
83
|
entries = sort(entries, tag_order: tag_order) if sort_tags
|
|
75
84
|
|
|
@@ -228,10 +228,11 @@ module Docscribe
|
|
|
228
228
|
# @param [Object, nil] signature_provider
|
|
229
229
|
# @param [nil] core_rbs_provider Param documentation.
|
|
230
230
|
# @param [nil] param_types Param documentation.
|
|
231
|
+
# @param [nil] strategy Param documentation.
|
|
231
232
|
# @raise [StandardError]
|
|
232
233
|
# @return [Hash]
|
|
233
234
|
def build_missing_merge_result(insertion, existing_lines:, config:, signature_provider: nil,
|
|
234
|
-
core_rbs_provider: nil, param_types: nil)
|
|
235
|
+
core_rbs_provider: nil, param_types: nil, strategy: nil)
|
|
235
236
|
node = insertion.node
|
|
236
237
|
name = SourceHelpers.node_name(node)
|
|
237
238
|
return { lines: [], reasons: [] } unless name
|
|
@@ -283,10 +284,22 @@ module Docscribe
|
|
|
283
284
|
|
|
284
285
|
all_params&.each do |pl|
|
|
285
286
|
pname = extract_param_name_from_param_line(pl)
|
|
286
|
-
next
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
287
|
+
next unless pname
|
|
288
|
+
|
|
289
|
+
if !info[:param_names].include?(pname)
|
|
290
|
+
lines << "#{pl}\n"
|
|
291
|
+
reasons << { type: :missing_param, message: "missing @param #{pname}", extra: { param: pname } }
|
|
292
|
+
elsif info[:param_types][pname] && strategy != :safe
|
|
293
|
+
new_type = extract_param_type_from_param_line(pl)
|
|
294
|
+
if new_type && info[:param_types][pname] != new_type
|
|
295
|
+
lines << "#{pl}\n"
|
|
296
|
+
reasons << {
|
|
297
|
+
type: :updated_param,
|
|
298
|
+
message: "updated @param #{pname} from #{info[:param_types][pname]} to #{new_type}",
|
|
299
|
+
extra: { param: pname }
|
|
300
|
+
}
|
|
301
|
+
end
|
|
302
|
+
end
|
|
290
303
|
end
|
|
291
304
|
end
|
|
292
305
|
|
|
@@ -301,9 +314,17 @@ module Docscribe
|
|
|
301
314
|
end
|
|
302
315
|
end
|
|
303
316
|
|
|
304
|
-
if config.emit_return_tag?(scope, visibility)
|
|
305
|
-
|
|
306
|
-
|
|
317
|
+
if config.emit_return_tag?(scope, visibility)
|
|
318
|
+
if !info[:has_return]
|
|
319
|
+
lines << "#{indent}# @return [#{normal_type}]\n"
|
|
320
|
+
reasons << { type: :missing_return, message: 'missing @return' }
|
|
321
|
+
elsif info[:return_type] && info[:return_type] != normal_type && strategy != :safe
|
|
322
|
+
lines << "#{indent}# @return [#{normal_type}]\n"
|
|
323
|
+
reasons << {
|
|
324
|
+
type: :updated_return,
|
|
325
|
+
message: "updated @return from #{info[:return_type]} to #{normal_type}"
|
|
326
|
+
}
|
|
327
|
+
end
|
|
307
328
|
end
|
|
308
329
|
|
|
309
330
|
if config.emit_rescue_conditional_returns? && !info[:has_return]
|
|
@@ -341,7 +362,9 @@ module Docscribe
|
|
|
341
362
|
# @return [Hash] parsed tag info
|
|
342
363
|
def parse_existing_doc_tags(lines)
|
|
343
364
|
param_names = {}
|
|
365
|
+
param_types = {}
|
|
344
366
|
has_return = false
|
|
367
|
+
return_type = nil
|
|
345
368
|
has_private = false
|
|
346
369
|
has_protected = false
|
|
347
370
|
has_module_function_note = false
|
|
@@ -354,9 +377,17 @@ module Docscribe
|
|
|
354
377
|
end
|
|
355
378
|
if (pname = extract_param_name_from_param_line(line))
|
|
356
379
|
param_names[pname] = true
|
|
380
|
+
if (type_match = line.match(/@param\s+\[([^\]]+)\]\s+\S+/) || line.match(/@param\s+\S+\s+\[([^\]]+)\]/))
|
|
381
|
+
param_types[pname] = type_match[1]
|
|
382
|
+
end
|
|
357
383
|
end
|
|
358
384
|
|
|
359
|
-
|
|
385
|
+
if line.match?(/^\s*#\s*@return\b/)
|
|
386
|
+
has_return = true
|
|
387
|
+
if (m = line.match(/@return\s+\[([^\]]+)\]/))
|
|
388
|
+
return_type = m[1]
|
|
389
|
+
end
|
|
390
|
+
end
|
|
360
391
|
has_private ||= line.match?(/^\s*#\s*@private\b/)
|
|
361
392
|
has_protected ||= line.match?(/^\s*#\s*@protected\b/)
|
|
362
393
|
has_module_function_note ||= line.match?(/^\s*#\s*@note\s+module_function:/)
|
|
@@ -366,7 +397,9 @@ module Docscribe
|
|
|
366
397
|
|
|
367
398
|
{
|
|
368
399
|
param_names: param_names,
|
|
400
|
+
param_types: param_types,
|
|
369
401
|
has_return: has_return,
|
|
402
|
+
return_type: return_type,
|
|
370
403
|
raise_types: raise_types,
|
|
371
404
|
has_private: has_private,
|
|
372
405
|
has_protected: has_protected,
|
|
@@ -462,6 +495,19 @@ module Docscribe
|
|
|
462
495
|
treat_options_keyword_as_hash: config.treat_options_keyword_as_hash?
|
|
463
496
|
)
|
|
464
497
|
param_types[pname] = ty
|
|
498
|
+
|
|
499
|
+
when :kwoptarg
|
|
500
|
+
pname, default = *a
|
|
501
|
+
pname = pname.to_s
|
|
502
|
+
default_src = default&.loc&.expression&.source
|
|
503
|
+
ty = external_sig&.param_types&.[](pname) ||
|
|
504
|
+
Infer.infer_param_type(
|
|
505
|
+
"#{pname}:",
|
|
506
|
+
default_src,
|
|
507
|
+
fallback_type: config.fallback_type,
|
|
508
|
+
treat_options_keyword_as_hash: config.treat_options_keyword_as_hash?
|
|
509
|
+
)
|
|
510
|
+
param_types[pname] = ty
|
|
465
511
|
end
|
|
466
512
|
end
|
|
467
513
|
|
|
@@ -672,6 +718,17 @@ module Docscribe
|
|
|
672
718
|
nil
|
|
673
719
|
end
|
|
674
720
|
|
|
721
|
+
# Method documentation.
|
|
722
|
+
#
|
|
723
|
+
# @note module_function: when included, also defines #extract_param_type_from_param_line (instance visibility: private)
|
|
724
|
+
# @param [Object] line Param documentation.
|
|
725
|
+
# @return [Object]
|
|
726
|
+
def extract_param_type_from_param_line(line)
|
|
727
|
+
if (m = line.match(/@param\s+\[([^\]]+)\]\s+\S+/) || line.match(/@param\s+\S+\s+\[([^\]]+)\]/))
|
|
728
|
+
m[1]
|
|
729
|
+
end
|
|
730
|
+
end
|
|
731
|
+
|
|
675
732
|
# Build a Plugin::Context from a collected insertion.
|
|
676
733
|
#
|
|
677
734
|
# @note module_function
|
|
@@ -83,20 +83,11 @@ module Docscribe
|
|
|
83
83
|
|
|
84
84
|
config ||= Docscribe::Config.load
|
|
85
85
|
signature_provider = build_signature_provider(config, code, file.to_s)
|
|
86
|
-
|
|
87
|
-
if config.respond_to?(:core_rbs_provider)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
core_rbs_provider = nil
|
|
92
|
-
end
|
|
93
|
-
elsif config.respond_to?(:rbs_provider)
|
|
94
|
-
begin
|
|
95
|
-
core_rbs_provider = config.rbs_provider
|
|
96
|
-
rescue StandardError
|
|
97
|
-
core_rbs_provider = nil
|
|
98
|
-
end
|
|
99
|
-
end
|
|
86
|
+
begin
|
|
87
|
+
core_rbs_provider ||= config.core_rbs_provider if config.respond_to?(:core_rbs_provider)
|
|
88
|
+
rescue StandardError => e
|
|
89
|
+
warn "Docscribe: failed to load core RBS provider: #{e.message}" if ENV['DOCSCRIBE_DEBUG']
|
|
90
|
+
core_rbs_provider = nil
|
|
100
91
|
end
|
|
101
92
|
|
|
102
93
|
collector = Docscribe::InlineRewriter::Collector.new(buffer)
|
|
@@ -345,8 +336,14 @@ module Docscribe
|
|
|
345
336
|
rewriter.remove(range)
|
|
346
337
|
end
|
|
347
338
|
|
|
339
|
+
effective_param_types = external_sig&.param_types || DocBuilder.build_param_types_from_node(
|
|
340
|
+
insertion.node,
|
|
341
|
+
external_sig: external_sig,
|
|
342
|
+
config: config
|
|
343
|
+
)
|
|
344
|
+
|
|
348
345
|
doc = build_method_doc(insertion, config: config, signature_provider: signature_provider,
|
|
349
|
-
core_rbs_provider: core_rbs_provider, param_types:
|
|
346
|
+
core_rbs_provider: core_rbs_provider, param_types: effective_param_types)
|
|
350
347
|
return if doc.nil? || doc.empty?
|
|
351
348
|
|
|
352
349
|
rewriter.insert_before(anchor_bol_range, doc)
|
|
@@ -369,7 +366,8 @@ module Docscribe
|
|
|
369
366
|
config: config,
|
|
370
367
|
signature_provider: signature_provider,
|
|
371
368
|
core_rbs_provider: core_rbs_provider,
|
|
372
|
-
param_types: external_sig&.param_types
|
|
369
|
+
param_types: external_sig&.param_types,
|
|
370
|
+
strategy: strategy
|
|
373
371
|
)
|
|
374
372
|
|
|
375
373
|
missing_lines = merge_result[:lines]
|
|
@@ -423,7 +421,8 @@ module Docscribe
|
|
|
423
421
|
end
|
|
424
422
|
|
|
425
423
|
doc = build_method_doc(insertion, config: config, signature_provider: signature_provider,
|
|
426
|
-
core_rbs_provider: core_rbs_provider,
|
|
424
|
+
core_rbs_provider: core_rbs_provider,
|
|
425
|
+
param_types: external_sig&.param_types)
|
|
427
426
|
return if doc.nil? || doc.empty?
|
|
428
427
|
|
|
429
428
|
rewriter.insert_before(anchor_bol_range, doc)
|
|
@@ -790,16 +789,18 @@ module Docscribe
|
|
|
790
789
|
# @param [Object, nil] signature_provider external signature provider
|
|
791
790
|
# @param [Object, nil] core_rbs_provider RBS core type provider
|
|
792
791
|
# @param [Hash, nil] param_types parameter name -> type map
|
|
792
|
+
# @param [Object] strategy Param documentation.
|
|
793
793
|
# @return [Hash] result with `:lines` and `:reasons` keys
|
|
794
794
|
def build_missing_method_merge_result(insertion, existing_lines:, config:, signature_provider:,
|
|
795
|
-
core_rbs_provider:, param_types:)
|
|
795
|
+
core_rbs_provider:, param_types:, strategy:)
|
|
796
796
|
DocBuilder.build_missing_merge_result(
|
|
797
797
|
insertion,
|
|
798
798
|
existing_lines: existing_lines,
|
|
799
799
|
config: config,
|
|
800
800
|
signature_provider: signature_provider,
|
|
801
801
|
core_rbs_provider: core_rbs_provider,
|
|
802
|
-
param_types: param_types
|
|
802
|
+
param_types: param_types,
|
|
803
|
+
strategy: strategy
|
|
803
804
|
)
|
|
804
805
|
end
|
|
805
806
|
|
|
@@ -41,8 +41,6 @@ module Docscribe
|
|
|
41
41
|
# - :anchor_node => Parser::AST::Node — node above which to insert doc
|
|
42
42
|
# - :doc => String — complete doc block including newlines
|
|
43
43
|
#
|
|
44
|
-
# @param [Parser::AST::Node] ast root AST node of the file
|
|
45
|
-
# @param [Parser::Source::Buffer] buffer source buffer
|
|
46
44
|
# @param [Object] _ast Param documentation.
|
|
47
45
|
# @param [Object] _buffer Param documentation.
|
|
48
46
|
# @return [Array<Hash>]
|
data/lib/docscribe/version.rb
CHANGED