docscribe 1.6.1 → 1.6.2
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 +76 -193
- data/exe/docscribe-client +26 -7
- data/lib/docscribe/cli/config_builder.rb +37 -2
- data/lib/docscribe/cli/coverage.rb +5 -5
- data/lib/docscribe/cli/formatters/json.rb +74 -29
- data/lib/docscribe/cli/formatters/sarif.rb +20 -3
- data/lib/docscribe/cli/options.rb +17 -2
- data/lib/docscribe/cli/rbs_gen.rb +4 -4
- data/lib/docscribe/cli/run.rb +107 -24
- data/lib/docscribe/cli/update_types.rb +61 -17
- data/lib/docscribe/cli.rb +19 -13
- data/lib/docscribe/config/defaults.rb +1 -0
- data/lib/docscribe/config/rbs.rb +22 -1
- data/lib/docscribe/config/template.rb +3 -0
- data/lib/docscribe/config/validation.rb +19 -0
- data/lib/docscribe/config.rb +1 -0
- data/lib/docscribe/infer/behavior.rb +13 -13
- data/lib/docscribe/infer/params.rb +2 -2
- data/lib/docscribe/infer/raises.rb +5 -6
- data/lib/docscribe/infer/returns.rb +1612 -151
- data/lib/docscribe/infer.rb +7 -7
- data/lib/docscribe/inline_rewriter/doc_builder.rb +485 -102
- data/lib/docscribe/inline_rewriter.rb +263 -97
- data/lib/docscribe/plugin/registry.rb +1 -0
- data/lib/docscribe/server/base.rb +46 -15
- data/lib/docscribe/server/client.rb +20 -11
- data/lib/docscribe/server/daemon.rb +200 -23
- data/lib/docscribe/server/protocol.rb +4 -4
- data/lib/docscribe/types/primitive.rb +160 -0
- data/lib/docscribe/types/sorbet/base_provider.rb +33 -1
- data/lib/docscribe/types/yard/formatter.rb +35 -6
- data/lib/docscribe/types/yard/parser.rb +25 -20
- data/lib/docscribe/types/yard/validator.rb +131 -0
- data/lib/docscribe/validator/generic_compatibility.rb +698 -0
- data/lib/docscribe/validator/type_mismatch_validator.rb +287 -0
- data/lib/docscribe/version.rb +1 -1
- metadata +8 -3
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'docscribe/types/yard/validator'
|
|
4
|
+
require 'docscribe/infer/constants'
|
|
5
|
+
require 'docscribe/validator/generic_compatibility'
|
|
6
|
+
|
|
7
|
+
module Docscribe
|
|
8
|
+
module Validator
|
|
9
|
+
# Compares YARD-documented types against inferred / external types.
|
|
10
|
+
#
|
|
11
|
+
# Lightweight, in-process checks only:
|
|
12
|
+
# - last expression / literal inference (already in `Infer`)
|
|
13
|
+
# - `core_rbs_provider` for stdlib sends (`String#to_i` etc.)
|
|
14
|
+
# - `external_sig` (RBS/Sorbet) when available and `--validate-types` is on
|
|
15
|
+
#
|
|
16
|
+
# Heavy inter-procedural `send("foo")` graph is out of scope for this MVP.
|
|
17
|
+
# When `expected` is `Object` (fallback) we silence to avoid false positives.
|
|
18
|
+
class TypeMismatchValidator
|
|
19
|
+
# @!attribute [rw] type
|
|
20
|
+
# @return [Symbol]
|
|
21
|
+
# @param [Symbol] value
|
|
22
|
+
#
|
|
23
|
+
# @!attribute [rw] yard_type
|
|
24
|
+
# @return [String?]
|
|
25
|
+
# @param [String?] value
|
|
26
|
+
#
|
|
27
|
+
# @!attribute [rw] expected_type
|
|
28
|
+
# @return [String?]
|
|
29
|
+
# @param [String?] value
|
|
30
|
+
#
|
|
31
|
+
# @!attribute [rw] message
|
|
32
|
+
# @return [String]
|
|
33
|
+
# @param [String] value
|
|
34
|
+
#
|
|
35
|
+
# @!attribute [rw] source
|
|
36
|
+
# @return [String?]
|
|
37
|
+
# @param [String?] value
|
|
38
|
+
Result = Struct.new(:type, :yard_type, :expected_type, :message, :source, keyword_init: true)
|
|
39
|
+
|
|
40
|
+
# @param [String] fallback_type value of `inference.fallback_type` (default `Object`)
|
|
41
|
+
# @return [void]
|
|
42
|
+
def initialize(fallback_type: Infer::FALLBACK_TYPE)
|
|
43
|
+
@fallback_type = fallback_type.to_s
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Whether a documented param type mismatches the expected one.
|
|
47
|
+
#
|
|
48
|
+
# @param [String, nil] yard_type type from `@param [...]`
|
|
49
|
+
# @param [String, nil] expected_type type from `external_sig` or `Infer`
|
|
50
|
+
# @param [String, Symbol, nil] method_name method name for void compatibility
|
|
51
|
+
# @return [Boolean]
|
|
52
|
+
def mismatched_param?(yard_type, expected_type, method_name: nil)
|
|
53
|
+
mismatched_return?(yard_type, expected_type, method_name: method_name)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Whether a documented return type mismatches the expected one.
|
|
57
|
+
#
|
|
58
|
+
# @param [String, nil] yard_type type from `@return [...]`
|
|
59
|
+
# @param [String, nil] expected_type inferred or external `normal_type`
|
|
60
|
+
# @param [String, Symbol, nil] method_name method name for void compatibility
|
|
61
|
+
# @return [Boolean]
|
|
62
|
+
def mismatched_return?(yard_type, expected_type, method_name: nil)
|
|
63
|
+
return false if blank_type?(yard_type)
|
|
64
|
+
return false if blank_type?(expected_type)
|
|
65
|
+
return false if expected_suppressed?(expected_type)
|
|
66
|
+
return false if yard_compatible?(yard_type, expected_type, method_name: method_name)
|
|
67
|
+
return false if types_normalized_equal?(yard_type, expected_type)
|
|
68
|
+
|
|
69
|
+
!normalized_equal?(yard_type, expected_type)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Whether YARD type is generic compatible with expected (e.g. Hash vs Hash<Symbol, String>).
|
|
73
|
+
# Delegates to GenericCompatibility service for dynamic, map-dispatched checks.
|
|
74
|
+
#
|
|
75
|
+
# @param [String, nil] yard_type
|
|
76
|
+
# @param [String, nil] expected_type
|
|
77
|
+
# @param [String, Symbol, nil] method_name method name for void compatibility threading
|
|
78
|
+
# @return [Boolean]
|
|
79
|
+
def generic_compatible?(yard_type, expected_type, method_name: nil)
|
|
80
|
+
GenericCompatibility.compatible?(yard_type, expected_type, fallback_type: @fallback_type, method_name: method_name)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Whether yard type is included in expected union.
|
|
84
|
+
#
|
|
85
|
+
# @param [String, nil] yard_type
|
|
86
|
+
# @param [String, nil] expected_type
|
|
87
|
+
# @return [Boolean]
|
|
88
|
+
def yard_in_expected_union?(yard_type, expected_type)
|
|
89
|
+
return false if yard_type.nil? || expected_type.nil?
|
|
90
|
+
|
|
91
|
+
normalized_yard = normalize(yard_type)
|
|
92
|
+
expected_type.split(',').any? { |part| normalize(part) == normalized_yard }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Whether void YARD type is compatible with fallback union or initialize/setup dynamic.
|
|
96
|
+
#
|
|
97
|
+
# @param [String, nil] yard_type
|
|
98
|
+
# @param [String, nil] expected_type
|
|
99
|
+
# @param [String, Symbol, nil] method_name method name for dynamic check
|
|
100
|
+
# @return [Boolean]
|
|
101
|
+
def void_compatible?(yard_type, expected_type, method_name: nil)
|
|
102
|
+
GenericCompatibility.void_compatible?(yard_type, expected_type, @fallback_type, method_name: method_name)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Whether a type string is a union of only fallback types (with optional `?`).
|
|
106
|
+
#
|
|
107
|
+
# @param [String, nil] type_str
|
|
108
|
+
# @return [Boolean]
|
|
109
|
+
def fallback_union?(type_str)
|
|
110
|
+
return false if type_str.nil? || type_str.strip.empty?
|
|
111
|
+
|
|
112
|
+
fallback_norm = normalize(@fallback_type)
|
|
113
|
+
parts = type_str.to_s.split(',').map { |p| normalize(p.strip.delete_suffix('?').strip) }
|
|
114
|
+
parts.all? { |p| p == fallback_norm || p.empty? }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Whether a YARD type string has invalid syntax (e.g. `Sym bol` leftover).
|
|
118
|
+
#
|
|
119
|
+
# @param [String, nil] yard_type
|
|
120
|
+
# @return [Boolean]
|
|
121
|
+
def invalid_syntax?(yard_type) # rubocop:disable SortedMethodsByCall/Waterfall
|
|
122
|
+
return false if yard_type.nil? || yard_type.strip.empty?
|
|
123
|
+
|
|
124
|
+
!Types::Yard::Validator.valid?(yard_type)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Build a Result for a return mismatch, or nil if no mismatch.
|
|
128
|
+
#
|
|
129
|
+
# @param [String, nil] yard_type
|
|
130
|
+
# @param [String, nil] expected_type
|
|
131
|
+
# @param [String] source source of expected type: "rbs" or "infer"
|
|
132
|
+
# @param [String, Symbol, nil] method_name method name for void compatibility
|
|
133
|
+
# @return [Docscribe::Validator::TypeMismatchValidator::Result, nil]
|
|
134
|
+
def check_return(yard_type, expected_type, source: 'infer', method_name: nil)
|
|
135
|
+
return invalid_return_result(yard_type, expected_type) if invalid_syntax?(yard_type)
|
|
136
|
+
return unless mismatched_return?(yard_type, expected_type, method_name: method_name)
|
|
137
|
+
|
|
138
|
+
mismatch_return_result(yard_type, expected_type, source: source)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Build a Result for a param mismatch, or nil if no mismatch.
|
|
142
|
+
#
|
|
143
|
+
# @param [String] param_name
|
|
144
|
+
# @param [String, nil] yard_type
|
|
145
|
+
# @param [String, nil] expected_type
|
|
146
|
+
# @param [String] source source of expected type: "rbs" or "infer"
|
|
147
|
+
# @param [String, Symbol, nil] method_name method name for void compatibility
|
|
148
|
+
# @return [Docscribe::Validator::TypeMismatchValidator::Result, nil]
|
|
149
|
+
def check_param(param_name, yard_type, expected_type, source: 'infer', method_name: nil)
|
|
150
|
+
return invalid_param_result(param_name, yard_type, expected_type) if invalid_syntax?(yard_type)
|
|
151
|
+
return unless mismatched_param?(yard_type, expected_type, method_name: method_name)
|
|
152
|
+
|
|
153
|
+
mismatch_param_result(param_name, yard_type, expected_type, source: source)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# @param [String, nil] yard_type
|
|
157
|
+
# @param [String, nil] expected_type
|
|
158
|
+
# @return [Docscribe::Validator::TypeMismatchValidator::Result]
|
|
159
|
+
def invalid_return_result(yard_type, expected_type)
|
|
160
|
+
Result.new(
|
|
161
|
+
type: :invalid_syntax,
|
|
162
|
+
yard_type: yard_type,
|
|
163
|
+
expected_type: expected_type,
|
|
164
|
+
message: "invalid YARD type [#{yard_type}]#{" expected [#{expected_type}]" if expected_type && expected_type != @fallback_type}",
|
|
165
|
+
source: 'syntax'
|
|
166
|
+
)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# @param [String, nil] yard_type
|
|
170
|
+
# @param [String, nil] expected_type
|
|
171
|
+
# @param [String] source
|
|
172
|
+
# @return [Docscribe::Validator::TypeMismatchValidator::Result]
|
|
173
|
+
def mismatch_return_result(yard_type, expected_type, source: 'infer')
|
|
174
|
+
Result.new(
|
|
175
|
+
type: :type_mismatch_return,
|
|
176
|
+
yard_type: yard_type,
|
|
177
|
+
expected_type: expected_type,
|
|
178
|
+
message: "updated @return from #{yard_type} to #{expected_type}",
|
|
179
|
+
source: source
|
|
180
|
+
)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# @param [String] param_name
|
|
184
|
+
# @param [String, nil] yard_type
|
|
185
|
+
# @param [String, nil] expected_type
|
|
186
|
+
# @return [Docscribe::Validator::TypeMismatchValidator::Result]
|
|
187
|
+
def invalid_param_result(param_name, yard_type, expected_type)
|
|
188
|
+
Result.new(
|
|
189
|
+
type: :invalid_syntax,
|
|
190
|
+
yard_type: yard_type,
|
|
191
|
+
expected_type: expected_type,
|
|
192
|
+
message: "invalid YARD type [#{yard_type}] for @param #{param_name}#{" expected [#{expected_type}]" if expected_type && expected_type != @fallback_type}",
|
|
193
|
+
source: 'syntax'
|
|
194
|
+
)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# @param [String] param_name
|
|
198
|
+
# @param [String, nil] yard_type
|
|
199
|
+
# @param [String, nil] expected_type
|
|
200
|
+
# @param [String] source
|
|
201
|
+
# @return [Docscribe::Validator::TypeMismatchValidator::Result]
|
|
202
|
+
def mismatch_param_result(param_name, yard_type, expected_type, source: 'infer')
|
|
203
|
+
Result.new(
|
|
204
|
+
type: :type_mismatch_param,
|
|
205
|
+
yard_type: yard_type,
|
|
206
|
+
expected_type: expected_type,
|
|
207
|
+
message: "updated @param #{param_name} from #{yard_type} to #{expected_type}",
|
|
208
|
+
source: source
|
|
209
|
+
)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
private
|
|
213
|
+
|
|
214
|
+
# Whether type string blank (nil or whitespace).
|
|
215
|
+
#
|
|
216
|
+
# @private
|
|
217
|
+
# @param [String, nil] type_str
|
|
218
|
+
# @return [Boolean]
|
|
219
|
+
def blank_type?(type_str)
|
|
220
|
+
type_str.nil? || type_str.strip.empty?
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Whether expected suppressed as fallback (uncertain).
|
|
224
|
+
#
|
|
225
|
+
# @private
|
|
226
|
+
# @param [String, nil] expected_type
|
|
227
|
+
# @return [Boolean]
|
|
228
|
+
def expected_suppressed?(expected_type)
|
|
229
|
+
normalize(expected_type) == @fallback_type || fallback_union?(expected_type)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Whether yard compatible with expected via void/union/generic.
|
|
233
|
+
#
|
|
234
|
+
# @private
|
|
235
|
+
# @param [String, nil] yard_type
|
|
236
|
+
# @param [String, nil] expected_type
|
|
237
|
+
# @param [String, Symbol, nil] method_name method name for void compatibility
|
|
238
|
+
# @return [Boolean]
|
|
239
|
+
def yard_compatible?(yard_type, expected_type, method_name: nil)
|
|
240
|
+
void_compatible?(yard_type, expected_type, method_name: method_name) ||
|
|
241
|
+
yard_in_expected_union?(yard_type, expected_type) ||
|
|
242
|
+
generic_compatible?(yard_type, expected_type, method_name: method_name)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Whether types equal after normalization (including optional "?").
|
|
246
|
+
#
|
|
247
|
+
# @private
|
|
248
|
+
# @param [String, nil] yard_type
|
|
249
|
+
# @param [String, nil] expected_type
|
|
250
|
+
# @return [Boolean]
|
|
251
|
+
def types_normalized_equal?(yard_type, expected_type)
|
|
252
|
+
normalized_equal?(yard_type, expected_type) || optional_normalized_equal?(yard_type, expected_type)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Whether normalized types equal.
|
|
256
|
+
#
|
|
257
|
+
# @private
|
|
258
|
+
# @param [String, nil] yard_type
|
|
259
|
+
# @param [String, nil] expected_type
|
|
260
|
+
# @return [Boolean]
|
|
261
|
+
def normalized_equal?(yard_type, expected_type)
|
|
262
|
+
normalize(yard_type) == normalize(expected_type)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Whether optional-normalized types equal.
|
|
266
|
+
#
|
|
267
|
+
# @private
|
|
268
|
+
# @param [String, nil] yard_type
|
|
269
|
+
# @param [String, nil] expected_type
|
|
270
|
+
# @return [Boolean]
|
|
271
|
+
def optional_normalized_equal?(yard_type, expected_type)
|
|
272
|
+
normalize(yard_type).delete_suffix('?') == normalize(expected_type).delete_suffix('?')
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Normalize a type string for comparison (strip, squeeze spaces, unify RBS/YARD syntax).
|
|
276
|
+
#
|
|
277
|
+
# @private
|
|
278
|
+
# @param [String, nil] type_str
|
|
279
|
+
# @return [String]
|
|
280
|
+
def normalize(type_str)
|
|
281
|
+
s = type_str.to_s
|
|
282
|
+
s = s.sub(/#.*\z/m, '').strip unless s.lstrip.start_with?('#')
|
|
283
|
+
s.strip.squeeze(' ').gsub('[', '<').gsub(']', '>').gsub(/\buntyped\b/, 'Object').gsub(/\bFALLBACK_TYPE\b/, 'Object')
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
data/lib/docscribe/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: docscribe
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.6.
|
|
4
|
+
version: 1.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- unurgunite
|
|
@@ -188,6 +188,7 @@ files:
|
|
|
188
188
|
- lib/docscribe/config/sorting.rb
|
|
189
189
|
- lib/docscribe/config/template.rb
|
|
190
190
|
- lib/docscribe/config/utils.rb
|
|
191
|
+
- lib/docscribe/config/validation.rb
|
|
191
192
|
- lib/docscribe/infer.rb
|
|
192
193
|
- lib/docscribe/infer/ast_walk.rb
|
|
193
194
|
- lib/docscribe/infer/behavior.rb
|
|
@@ -217,6 +218,7 @@ files:
|
|
|
217
218
|
- lib/docscribe/server/daemon.rb
|
|
218
219
|
- lib/docscribe/server/protocol.rb
|
|
219
220
|
- lib/docscribe/types/overload_selector.rb
|
|
221
|
+
- lib/docscribe/types/primitive.rb
|
|
220
222
|
- lib/docscribe/types/provider_chain.rb
|
|
221
223
|
- lib/docscribe/types/rbs/collection_loader.rb
|
|
222
224
|
- lib/docscribe/types/rbs/provider.rb
|
|
@@ -228,6 +230,9 @@ files:
|
|
|
228
230
|
- lib/docscribe/types/yard/formatter.rb
|
|
229
231
|
- lib/docscribe/types/yard/parser.rb
|
|
230
232
|
- lib/docscribe/types/yard/types.rb
|
|
233
|
+
- lib/docscribe/types/yard/validator.rb
|
|
234
|
+
- lib/docscribe/validator/generic_compatibility.rb
|
|
235
|
+
- lib/docscribe/validator/type_mismatch_validator.rb
|
|
231
236
|
- lib/docscribe/version.rb
|
|
232
237
|
homepage: https://github.com/unurgunite/docscribe
|
|
233
238
|
licenses:
|
|
@@ -238,7 +243,7 @@ metadata:
|
|
|
238
243
|
changelog_uri: https://github.com/unurgunite/docscribe/blob/master/CHANGELOG.md
|
|
239
244
|
rubygems_mfa_required: 'true'
|
|
240
245
|
post_install_message: |
|
|
241
|
-
You installed docscribe 1.6.
|
|
246
|
+
You installed docscribe 1.6.2. Your future self (and your team) thank you.
|
|
242
247
|
|
|
243
248
|
$ docscribe --help
|
|
244
249
|
|
|
@@ -258,7 +263,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
258
263
|
- !ruby/object:Gem::Version
|
|
259
264
|
version: '0'
|
|
260
265
|
requirements: []
|
|
261
|
-
rubygems_version: 4.0.
|
|
266
|
+
rubygems_version: 4.0.21
|
|
262
267
|
specification_version: 4
|
|
263
268
|
summary: Auto-generate inline YARD documentation for Ruby by analyzing code AST. Supports
|
|
264
269
|
RBS and Sorbet type signatures.
|