ast-merge 7.1.7 → 7.1.8
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
- checksums.yaml.gz.sig +0 -0
- data/lib/ast/merge/line_range_support.rb +11 -2
- data/lib/ast/merge/rspec/README.md +45 -0
- data/lib/ast/merge/rspec/provider_snapshot.rb +497 -0
- data/lib/ast/merge/rspec.rb +3 -0
- data/lib/ast/merge/version.rb +1 -1
- data/lib/ast/merge.rb +0 -1
- data.tar.gz.sig +0 -0
- metadata +13 -13
- metadata.gz.sig +0 -0
- data/lib/ast/merge/yaml_document.rb +0 -159
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 205697b391355f6dfc618a12f26c0d567e186fe148981baa5f38f16b0bf0653f
|
|
4
|
+
data.tar.gz: 304d5c93be7f48d8f98c68229b182ae9d831baf43c3987d41ce22b2c6fcdc655
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 38c22b55d61033a0c77f2257cc65204f87ae041bcd658e00a8d5bc87b5cfb839de897679ce6e7134008972b5f19239b050d2f40d4cb6b93f0642384e30d75d2c
|
|
7
|
+
data.tar.gz: ff57c5b383ae4634dedbc89a3028a5fbe7ba44a9668a3c50c14e953323f86bd1f206177f2642d47d630d52fb2ca4e0835b11d1c649ba149b7550b4321e9edb79
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -18,7 +18,8 @@ module Ast
|
|
|
18
18
|
elsif object.respond_to?(:source_position)
|
|
19
19
|
object.source_position&.dig(:start_line)
|
|
20
20
|
elsif object.respond_to?(:start_point) && object.start_point
|
|
21
|
-
object.start_point
|
|
21
|
+
row = line_point_row(object.start_point)
|
|
22
|
+
row + 1 if row
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
|
|
@@ -30,10 +31,18 @@ module Ast
|
|
|
30
31
|
elsif object.respond_to?(:source_position)
|
|
31
32
|
object.source_position&.dig(:end_line)
|
|
32
33
|
elsif object.respond_to?(:end_point) && object.end_point
|
|
33
|
-
object.end_point
|
|
34
|
+
row = line_point_row(object.end_point)
|
|
35
|
+
row + 1 if row
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
38
|
|
|
39
|
+
def line_point_row(point)
|
|
40
|
+
return point.row if point.respond_to?(:row)
|
|
41
|
+
return point[:row] if point.respond_to?(:[])
|
|
42
|
+
|
|
43
|
+
nil
|
|
44
|
+
end
|
|
45
|
+
|
|
37
46
|
def source_line_at(analysis, line_number)
|
|
38
47
|
line = analysis.line_at(line_number)
|
|
39
48
|
line.respond_to?(:raw) ? line.raw : line
|
|
@@ -17,6 +17,7 @@ That loads:
|
|
|
17
17
|
- TreeHaver backend dependency tags
|
|
18
18
|
- Ast::Merge merge-gem dependency tags
|
|
19
19
|
- Shared conformance fixture helpers
|
|
20
|
+
- Portable provider snapshot and differential replay helpers
|
|
20
21
|
- Ast::Merge shared examples
|
|
21
22
|
|
|
22
23
|
### Split loading for `ast-merge` itself or registration-heavy suites
|
|
@@ -249,3 +250,47 @@ Use this namespace for test support shared across the merge-gem family:
|
|
|
249
250
|
- shared examples for common contracts
|
|
250
251
|
|
|
251
252
|
Format-specific parser fixtures and merge behavior specs belong in the corresponding `*-merge` gem.
|
|
253
|
+
|
|
254
|
+
## Provider snapshots
|
|
255
|
+
|
|
256
|
+
`Ast::Merge::RSpec::ProviderSnapshot` captures the versioned parse and analysis
|
|
257
|
+
projections used by the cross-runtime fixture repository. Parsing always enters
|
|
258
|
+
through an explicitly selected TreeHaver backend. The capture harness only
|
|
259
|
+
reads the normalized TreeHaver node contract; a provider suite supplies a
|
|
260
|
+
callback for parser-native evidence:
|
|
261
|
+
|
|
262
|
+
```ruby
|
|
263
|
+
snapshot = Ast::Merge::RSpec::ProviderSnapshot.new(
|
|
264
|
+
snapshot_id: "snapshot.prism.ruby",
|
|
265
|
+
provider: Prism::Merge.merge_provider,
|
|
266
|
+
source: source,
|
|
267
|
+
language: :ruby,
|
|
268
|
+
dialect: :ruby,
|
|
269
|
+
backend_id: :prism,
|
|
270
|
+
parser_contract: :prism,
|
|
271
|
+
backend_identity: {
|
|
272
|
+
id: :prism,
|
|
273
|
+
family: :native,
|
|
274
|
+
host_runtime: :ruby,
|
|
275
|
+
package: :prism,
|
|
276
|
+
parser: :Prism,
|
|
277
|
+
capabilities: %i[comments directives exact_source_spans normalized_tree],
|
|
278
|
+
},
|
|
279
|
+
extension_builder: lambda do |tree:, **|
|
|
280
|
+
{
|
|
281
|
+
schema: "structuredmerge.extension/ruby-prism/v1",
|
|
282
|
+
namespace: "ruby-prism",
|
|
283
|
+
capabilities: %w[magic-comments],
|
|
284
|
+
payload: {magic_comments: tree.magic_comments.map(&:to_s)},
|
|
285
|
+
}
|
|
286
|
+
end,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
artifacts = snapshot.capture
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The extension callback is intentionally provider-owned. The common harness
|
|
293
|
+
does not reflect over native parser objects or flatten their capabilities.
|
|
294
|
+
`#differential_replay` serializes a provider request through canonical JSON,
|
|
295
|
+
replays it, and reports whether the provider result and output bytes remain
|
|
296
|
+
identical.
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'digest'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'ast/merge'
|
|
6
|
+
require 'tree_haver'
|
|
7
|
+
|
|
8
|
+
module Ast
|
|
9
|
+
module Merge
|
|
10
|
+
module RSpec
|
|
11
|
+
# Captures the portable TreeHaver and provider projections used by the
|
|
12
|
+
# cross-runtime contract fixtures. Native facts enter only through an
|
|
13
|
+
# explicit, namespaced extension builder supplied by the provider suite.
|
|
14
|
+
# rubocop:disable Metrics/AbcSize, Metrics/ClassLength, Metrics/MethodLength, Metrics/ParameterLists -- the capture boundary mirrors the complete versioned contract
|
|
15
|
+
class ProviderSnapshot
|
|
16
|
+
PARSE_REQUEST_SCHEMA = 'structuredmerge.parse-request/v1'
|
|
17
|
+
PARSE_RESULT_SCHEMA = 'structuredmerge.parse-result/v1'
|
|
18
|
+
ANALYSIS_RESULT_SCHEMA = 'structuredmerge.analysis-result/v1'
|
|
19
|
+
EXTENSION_SCHEMA_PATTERN = %r{\Astructuredmerge\.extension/[a-z0-9-]+/v\d+\z}
|
|
20
|
+
NORMALIZED_CONTRACT = 'structuredmerge.normalized-node/v1'
|
|
21
|
+
|
|
22
|
+
class Error < StandardError; end
|
|
23
|
+
|
|
24
|
+
attr_reader :snapshot_id, :source_id, :provider, :source, :language, :dialect, :backend_id,
|
|
25
|
+
:backend_identity, :parser_contract, :profile_id, :extension_builder
|
|
26
|
+
|
|
27
|
+
def initialize(snapshot_id:, provider:, source:, language:, dialect:, backend_id:, backend_identity:,
|
|
28
|
+
source_id: nil, parser_contract: nil, profile_id: :source_preserving, extension_builder: nil)
|
|
29
|
+
@snapshot_id = required_string(snapshot_id, :snapshot_id)
|
|
30
|
+
@source_id = required_string(source_id || "#{@snapshot_id}:source", :source_id)
|
|
31
|
+
@provider = provider
|
|
32
|
+
@source = source.to_s
|
|
33
|
+
@language = required_string(language, :language)
|
|
34
|
+
@dialect = required_string(dialect, :dialect)
|
|
35
|
+
@backend_id = required_string(backend_id, :backend_id)
|
|
36
|
+
@backend_identity = normalize_backend_identity(backend_identity)
|
|
37
|
+
@parser_contract = parser_contract&.to_sym
|
|
38
|
+
@profile_id = profile_id.to_sym
|
|
39
|
+
@extension_builder = extension_builder
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def capture
|
|
43
|
+
tree = parse_tree
|
|
44
|
+
projection = TreeProjection.new(tree, source).call
|
|
45
|
+
provider_result = analyze
|
|
46
|
+
extensions = build_extensions(tree, projection, provider_result)
|
|
47
|
+
parse_request = build_parse_request
|
|
48
|
+
parse_result = build_parse_result(parse_request, projection, extensions)
|
|
49
|
+
|
|
50
|
+
{
|
|
51
|
+
parse_request: parse_request,
|
|
52
|
+
parse_result: parse_result,
|
|
53
|
+
analysis_result: build_analysis_result(parse_request, parse_result, provider_result, extensions)
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def differential_replay(operation:, request:)
|
|
58
|
+
operation = ProviderContract.normalize_operation(operation)
|
|
59
|
+
original_request = ProviderContract.validate_request!(operation, request)
|
|
60
|
+
replay_request = ProviderContract.validate_request!(operation, self.class.round_trip(original_request))
|
|
61
|
+
original_result = ProviderContract.validate_result!(
|
|
62
|
+
operation,
|
|
63
|
+
provider.public_send(operation, original_request)
|
|
64
|
+
)
|
|
65
|
+
replay_result = ProviderContract.validate_result!(operation, provider.public_send(operation, replay_request))
|
|
66
|
+
original_json = self.class.canonical_json(original_result)
|
|
67
|
+
replay_json = self.class.canonical_json(replay_result)
|
|
68
|
+
|
|
69
|
+
{
|
|
70
|
+
operation: operation.to_s,
|
|
71
|
+
equivalent: original_json == replay_json,
|
|
72
|
+
original_sha256: Digest::SHA256.hexdigest(original_json),
|
|
73
|
+
replay_sha256: Digest::SHA256.hexdigest(replay_json),
|
|
74
|
+
output_bytes_equal: output_bytes(original_result) == output_bytes(replay_result),
|
|
75
|
+
original_request: original_request,
|
|
76
|
+
transported_request: replay_request,
|
|
77
|
+
original_result: original_result,
|
|
78
|
+
transported_result: replay_result
|
|
79
|
+
}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class << self
|
|
83
|
+
def tree_sitter_extension(nodes:, **)
|
|
84
|
+
{
|
|
85
|
+
schema: 'structuredmerge.extension/tree-sitter/v1',
|
|
86
|
+
namespace: 'tree-sitter',
|
|
87
|
+
capabilities: ['grammar-node-types'],
|
|
88
|
+
payload: {
|
|
89
|
+
grammar_node_types: nodes.map { |node| node.fetch(:native_type).to_s }.uniq.sort
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def canonical_json(value)
|
|
95
|
+
JSON.generate(canonical_value(Ast::Merge.json_ready(value)))
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def round_trip(value)
|
|
99
|
+
JSON.parse(canonical_json(value), symbolize_names: true)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def extension_forwarding_digest(extension)
|
|
103
|
+
forwarded = round_trip(extension.merge('x-unknown-forwarding-probe' => { 'retained' => true }))
|
|
104
|
+
Digest::SHA256.hexdigest(canonical_json(forwarded))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def canonical_value(value)
|
|
110
|
+
case value
|
|
111
|
+
when Array
|
|
112
|
+
value.map { |item| canonical_value(item) }
|
|
113
|
+
when Hash
|
|
114
|
+
value.keys.map(&:to_s).sort.to_h do |key|
|
|
115
|
+
item = value.key?(key) ? value[key] : value[key.to_sym]
|
|
116
|
+
[key, canonical_value(item)]
|
|
117
|
+
end
|
|
118
|
+
else
|
|
119
|
+
value
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
def parse_tree
|
|
127
|
+
TreeHaver.with_backend(backend_id) do
|
|
128
|
+
options = {}
|
|
129
|
+
options[:contract] = parser_contract if parser_contract
|
|
130
|
+
TreeHaver.parser_for(language.to_sym, **options).parse(source)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def analyze
|
|
135
|
+
request = {
|
|
136
|
+
provider_id: provider.provider_id,
|
|
137
|
+
family: provider.family,
|
|
138
|
+
source: source,
|
|
139
|
+
dialect: dialect,
|
|
140
|
+
backend: backend_id,
|
|
141
|
+
profile_id: profile_id
|
|
142
|
+
}
|
|
143
|
+
ProviderContract.validate_result!(:analyze, provider.analyze(request))
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def build_parse_request
|
|
147
|
+
{
|
|
148
|
+
schema: PARSE_REQUEST_SCHEMA,
|
|
149
|
+
request_id: request_id,
|
|
150
|
+
source: source_descriptor(include_content: true),
|
|
151
|
+
language: language,
|
|
152
|
+
dialect: dialect,
|
|
153
|
+
selection: {
|
|
154
|
+
backend: backend_id,
|
|
155
|
+
preference: [],
|
|
156
|
+
required_capabilities: backend_identity.fetch(:capabilities)
|
|
157
|
+
},
|
|
158
|
+
options: {
|
|
159
|
+
comments: true,
|
|
160
|
+
diagnostics: true,
|
|
161
|
+
native_extensions: true,
|
|
162
|
+
tokens: false
|
|
163
|
+
},
|
|
164
|
+
metadata: { snapshot_id: snapshot_id }
|
|
165
|
+
}
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def build_parse_result(parse_request, projection, extensions)
|
|
169
|
+
{
|
|
170
|
+
schema: PARSE_RESULT_SCHEMA,
|
|
171
|
+
request_id: request_id,
|
|
172
|
+
ok: projection.fetch(:diagnostics).none? { |diagnostic| diagnostic.fetch(:blocking) },
|
|
173
|
+
source: source_descriptor,
|
|
174
|
+
selection: selection_report,
|
|
175
|
+
backend: backend_identity.merge(
|
|
176
|
+
language: language,
|
|
177
|
+
dialect: dialect,
|
|
178
|
+
normalized_contract: NORMALIZED_CONTRACT
|
|
179
|
+
),
|
|
180
|
+
root_id: projection.fetch(:root_id),
|
|
181
|
+
nodes: projection.fetch(:nodes),
|
|
182
|
+
comments: projection.fetch(:comments),
|
|
183
|
+
diagnostics: projection.fetch(:diagnostics),
|
|
184
|
+
extensions: extensions,
|
|
185
|
+
metadata: {
|
|
186
|
+
parse_request_sha256: Digest::SHA256.hexdigest(self.class.canonical_json(parse_request))
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def build_analysis_result(parse_request, parse_result, provider_result, extensions)
|
|
192
|
+
analysis = provider_result.fetch(:analysis, {})
|
|
193
|
+
{
|
|
194
|
+
schema: ANALYSIS_RESULT_SCHEMA,
|
|
195
|
+
request_id: request_id,
|
|
196
|
+
ok: provider_result.fetch(:ok),
|
|
197
|
+
source: source_descriptor,
|
|
198
|
+
provider: provider_result.fetch(:provider),
|
|
199
|
+
profile: provider_result.fetch(:profile),
|
|
200
|
+
parse_result_sha256: Digest::SHA256.hexdigest(self.class.canonical_json(parse_result)),
|
|
201
|
+
analysis: analysis,
|
|
202
|
+
owners: extract_owners(analysis),
|
|
203
|
+
comment_regions: Array(analysis[:comment_regions] || analysis['comment_regions']),
|
|
204
|
+
layout_gaps: Array(analysis[:layout_gaps] || analysis['layout_gaps']),
|
|
205
|
+
attachments: Array(analysis[:attachments] || analysis['attachments']),
|
|
206
|
+
ownership: Array(analysis[:ownership] || analysis['ownership']),
|
|
207
|
+
diagnostics: provider_result.fetch(:diagnostics),
|
|
208
|
+
extensions: extensions,
|
|
209
|
+
metadata: {
|
|
210
|
+
parse_request_sha256: Digest::SHA256.hexdigest(self.class.canonical_json(parse_request)),
|
|
211
|
+
provider_result_schema: provider_result.fetch(:schema)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def extract_owners(analysis)
|
|
217
|
+
%i[owners declarations entries].each do |key|
|
|
218
|
+
value = analysis[key] || analysis[key.to_s]
|
|
219
|
+
return value if value.is_a?(Array)
|
|
220
|
+
end
|
|
221
|
+
[]
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def build_extensions(tree, projection, provider_result)
|
|
225
|
+
return [] unless extension_builder
|
|
226
|
+
|
|
227
|
+
extension_value = extension_builder.call(
|
|
228
|
+
tree: tree,
|
|
229
|
+
nodes: projection.fetch(:nodes),
|
|
230
|
+
provider_result: provider_result,
|
|
231
|
+
source: source
|
|
232
|
+
)
|
|
233
|
+
extensions = extension_value.is_a?(Array) ? extension_value : [extension_value]
|
|
234
|
+
extensions.map { |extension| normalize_extension(extension) }
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def normalize_extension(extension)
|
|
238
|
+
normalized = Ast::Merge.json_ready(extension)
|
|
239
|
+
schema = normalized.fetch('schema', '').to_s
|
|
240
|
+
namespace = normalized.fetch('namespace', '').to_s
|
|
241
|
+
capabilities = Array(normalized['capabilities']).map(&:to_s).sort.uniq
|
|
242
|
+
raise Error, "Invalid extension schema: #{schema.inspect}" unless schema.match?(EXTENSION_SCHEMA_PATTERN)
|
|
243
|
+
raise Error, 'Extension namespace must not be empty' if namespace.empty?
|
|
244
|
+
raise Error, 'Extension payload is required' unless normalized.key?('payload')
|
|
245
|
+
|
|
246
|
+
normalized.merge(
|
|
247
|
+
'capabilities' => capabilities,
|
|
248
|
+
'opaque_forwarding_replay_sha256' => self.class.extension_forwarding_digest(normalized)
|
|
249
|
+
)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def selection_report
|
|
253
|
+
{
|
|
254
|
+
mode: 'explicit',
|
|
255
|
+
requested_backend: backend_id,
|
|
256
|
+
preference: [],
|
|
257
|
+
required_capabilities: backend_identity.fetch(:capabilities),
|
|
258
|
+
candidates: [
|
|
259
|
+
{
|
|
260
|
+
backend_id: backend_id,
|
|
261
|
+
available: true,
|
|
262
|
+
compatible: true,
|
|
263
|
+
selected: true,
|
|
264
|
+
reasons: []
|
|
265
|
+
}
|
|
266
|
+
],
|
|
267
|
+
selected_backend: backend_id,
|
|
268
|
+
selection_reason: 'explicit-compatible-backend',
|
|
269
|
+
registry_digest: registry_digest
|
|
270
|
+
}
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def registry_digest
|
|
274
|
+
registrations = TreeHaver.registered_languages(language.to_sym)
|
|
275
|
+
portable = registrations.keys.map(&:to_s).sort.to_h do |key|
|
|
276
|
+
config = registrations[key.to_sym] || {}
|
|
277
|
+
[
|
|
278
|
+
key,
|
|
279
|
+
config.slice(:gem_name, :contract, :symbol).transform_values do |value|
|
|
280
|
+
value.respond_to?(:name) && value.name ? value.name : value.to_s
|
|
281
|
+
end
|
|
282
|
+
]
|
|
283
|
+
end
|
|
284
|
+
Digest::SHA256.hexdigest(self.class.canonical_json(portable))
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def source_descriptor(include_content: false)
|
|
288
|
+
descriptor = {
|
|
289
|
+
source_id: source_id,
|
|
290
|
+
role: 'source',
|
|
291
|
+
byte_length: source.bytesize,
|
|
292
|
+
sha256: Digest::SHA256.hexdigest(source.b),
|
|
293
|
+
encoding: source.encoding.name.downcase,
|
|
294
|
+
bom: source.b.start_with?("\xEF\xBB\xBF".b),
|
|
295
|
+
line_endings: line_ending_profile,
|
|
296
|
+
final_newline: source.end_with?("\n", "\r")
|
|
297
|
+
}
|
|
298
|
+
descriptor[:content] = source if include_content
|
|
299
|
+
descriptor
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def line_ending_profile
|
|
303
|
+
endings = source.scan(/\r\n|\r|\n/).uniq
|
|
304
|
+
return 'none' if endings.empty?
|
|
305
|
+
return 'mixed' if endings.length > 1
|
|
306
|
+
|
|
307
|
+
{ "\n" => 'lf', "\r\n" => 'crlf', "\r" => 'cr' }.fetch(endings.first)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def normalize_backend_identity(identity)
|
|
311
|
+
normalized = Ast::Merge::ProviderContract.normalize_hash(identity)
|
|
312
|
+
required = %i[id family host_runtime package parser capabilities]
|
|
313
|
+
missing = required.reject { |key| normalized.key?(key) }
|
|
314
|
+
raise Error, "Backend identity is missing: #{missing.join(', ')}" unless missing.empty?
|
|
315
|
+
unless normalized.fetch(:id).to_s == backend_id
|
|
316
|
+
raise Error, 'Backend identity does not match selected backend'
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
normalized.merge(capabilities: Array(normalized.fetch(:capabilities)).map(&:to_s).sort.uniq)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def required_string(value, name)
|
|
323
|
+
normalized = value.to_s.strip
|
|
324
|
+
raise Error, "#{name} must not be empty" if normalized.empty?
|
|
325
|
+
|
|
326
|
+
normalized
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def request_id
|
|
330
|
+
"#{snapshot_id}:parse:#{Digest::SHA256.hexdigest(source.b)[0, 12]}"
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def output_bytes(result)
|
|
334
|
+
output = result[:output] || result['output']
|
|
335
|
+
output&.b
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Projects only the normalized TreeHaver contract. It never introspects
|
|
339
|
+
# backend-native objects; providers retain those facts in extensions.
|
|
340
|
+
class TreeProjection
|
|
341
|
+
def initialize(tree, source)
|
|
342
|
+
@tree = tree
|
|
343
|
+
@source = source
|
|
344
|
+
@nodes = []
|
|
345
|
+
@comments = []
|
|
346
|
+
@seen_comments = {}
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def call
|
|
350
|
+
root_id = project_node(@tree.root_node, nil)
|
|
351
|
+
project_tree_comments
|
|
352
|
+
{
|
|
353
|
+
root_id: root_id,
|
|
354
|
+
nodes: @nodes,
|
|
355
|
+
comments: ordered_comments,
|
|
356
|
+
diagnostics: diagnostics
|
|
357
|
+
}
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
private
|
|
361
|
+
|
|
362
|
+
def project_node(node, parent_id)
|
|
363
|
+
id = "node:#{@nodes.length}"
|
|
364
|
+
record = node_record(node, id, parent_id)
|
|
365
|
+
@nodes << record
|
|
366
|
+
children = node.children
|
|
367
|
+
record[:children] = children.each_with_index.map do |child, index|
|
|
368
|
+
{ node_id: project_node(child, id), index: index }
|
|
369
|
+
end
|
|
370
|
+
record_comment(node, id) if record.fetch(:role) == 'comment'
|
|
371
|
+
id
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def project_tree_comments
|
|
375
|
+
@tree.comments.each do |comment|
|
|
376
|
+
key = comment_key(comment)
|
|
377
|
+
next if @seen_comments[key]
|
|
378
|
+
|
|
379
|
+
id = "node:#{@nodes.length}"
|
|
380
|
+
@nodes << comment_record(comment, id)
|
|
381
|
+
record_comment(comment, id)
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def node_record(node, id, parent_id)
|
|
386
|
+
type = node.type.to_s
|
|
387
|
+
{
|
|
388
|
+
id: id,
|
|
389
|
+
type: type,
|
|
390
|
+
native_type: node.native_type.to_s,
|
|
391
|
+
role: node_role(node, type),
|
|
392
|
+
named: node.named?,
|
|
393
|
+
missing: node.missing?,
|
|
394
|
+
has_error: node.has_error?,
|
|
395
|
+
span: span(node),
|
|
396
|
+
parent_id: parent_id,
|
|
397
|
+
children: [],
|
|
398
|
+
semantic_roles: [],
|
|
399
|
+
unsupported_features: [],
|
|
400
|
+
extensions: [],
|
|
401
|
+
metadata: {}
|
|
402
|
+
}
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def comment_record(comment, id)
|
|
406
|
+
{
|
|
407
|
+
id: id,
|
|
408
|
+
type: comment.type.to_s,
|
|
409
|
+
native_type: comment.type.to_s,
|
|
410
|
+
role: 'comment',
|
|
411
|
+
named: false,
|
|
412
|
+
missing: false,
|
|
413
|
+
has_error: false,
|
|
414
|
+
span: span(comment),
|
|
415
|
+
parent_id: nil,
|
|
416
|
+
children: [],
|
|
417
|
+
semantic_roles: ['comment'],
|
|
418
|
+
unsupported_features: [],
|
|
419
|
+
extensions: [],
|
|
420
|
+
metadata: {}
|
|
421
|
+
}
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def node_role(node, type)
|
|
425
|
+
return 'error' if node.has_error? || node.missing?
|
|
426
|
+
return 'comment' if type.include?('comment')
|
|
427
|
+
|
|
428
|
+
node.named? ? 'structural' : 'token'
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def record_comment(comment, node_id)
|
|
432
|
+
key = comment_key(comment)
|
|
433
|
+
@seen_comments[key] = true
|
|
434
|
+
@comments << {
|
|
435
|
+
node_id: node_id,
|
|
436
|
+
style: optional_value(comment, :style),
|
|
437
|
+
native_kind: comment.type.to_s,
|
|
438
|
+
attachment_hint: optional_value(comment, :attachment_hint) || 'unknown'
|
|
439
|
+
}
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def comment_key(comment)
|
|
443
|
+
[comment.start_byte, comment.end_byte]
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def ordered_comments
|
|
447
|
+
nodes_by_id = @nodes.to_h { |node| [node.fetch(:id), node] }
|
|
448
|
+
@comments.sort_by do |comment|
|
|
449
|
+
range = nodes_by_id.fetch(comment.fetch(:node_id)).dig(:span, :range)
|
|
450
|
+
[range.fetch(:start_byte), range.fetch(:end_byte), comment.fetch(:node_id)]
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
def optional_value(object, method_name)
|
|
455
|
+
object.respond_to?(method_name) && object.public_send(method_name)&.to_s
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def span(node)
|
|
459
|
+
{
|
|
460
|
+
range: { start_byte: node.start_byte, end_byte: node.end_byte },
|
|
461
|
+
start_point: point(node.start_point),
|
|
462
|
+
end_point: point(node.end_point)
|
|
463
|
+
}
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
def point(value)
|
|
467
|
+
{
|
|
468
|
+
row: value.respond_to?(:row) ? value.row : value[:row],
|
|
469
|
+
column: value.respond_to?(:column) ? value.column : value[:column]
|
|
470
|
+
}
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def diagnostics
|
|
474
|
+
[
|
|
475
|
+
*@tree.errors.map { |error| diagnostic(error, 'error', true) },
|
|
476
|
+
*@tree.warnings.map { |warning| diagnostic(warning, 'warning', false) }
|
|
477
|
+
]
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def diagnostic(value, severity, blocking)
|
|
481
|
+
{
|
|
482
|
+
id: "diagnostic:#{severity}:#{value.class.name}:#{value}",
|
|
483
|
+
severity: severity,
|
|
484
|
+
category: 'parser_diagnostic',
|
|
485
|
+
code: value.respond_to?(:type) ? value.type.to_s : nil,
|
|
486
|
+
message: value.to_s,
|
|
487
|
+
source_role: 'source',
|
|
488
|
+
blocking: blocking,
|
|
489
|
+
metadata: {}
|
|
490
|
+
}
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
# rubocop:enable Metrics/AbcSize, Metrics/ClassLength, Metrics/MethodLength, Metrics/ParameterLists
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
end
|
data/lib/ast/merge/rspec.rb
CHANGED
|
@@ -34,6 +34,9 @@ require_relative 'rspec/dependency_tags'
|
|
|
34
34
|
# Load conformance fixture helpers for shared fixture harnesses
|
|
35
35
|
require_relative 'rspec/conformance_fixtures'
|
|
36
36
|
|
|
37
|
+
# Load portable provider snapshot and differential replay support
|
|
38
|
+
require_relative 'rspec/provider_snapshot'
|
|
39
|
+
|
|
37
40
|
# Load Ast::Merge shared examples (for testing *-merge implementations)
|
|
38
41
|
require_relative 'rspec/shared_examples'
|
|
39
42
|
|
data/lib/ast/merge/version.rb
CHANGED
|
@@ -5,7 +5,7 @@ module Ast
|
|
|
5
5
|
# Version namespace for this gem.
|
|
6
6
|
module Version
|
|
7
7
|
# Current gem version.
|
|
8
|
-
VERSION = '7.1.
|
|
8
|
+
VERSION = '7.1.8'
|
|
9
9
|
end
|
|
10
10
|
# Current gem version exposed at the traditional constant location.
|
|
11
11
|
VERSION = Version::VERSION # Traditional Constant Location
|
data/lib/ast/merge.rb
CHANGED
|
@@ -87,7 +87,6 @@ module Ast
|
|
|
87
87
|
autoload :ProviderContract, 'ast/merge/provider_contract'
|
|
88
88
|
autoload :ProviderRegistry, 'ast/merge/provider_registry'
|
|
89
89
|
autoload :ProviderResult, 'ast/merge/provider_result'
|
|
90
|
-
autoload :YamlDocument, 'ast/merge/yaml_document'
|
|
91
90
|
autoload :SectionTyping, 'ast/merge/section_typing'
|
|
92
91
|
autoload :SmartMergerBase, 'ast/merge/smart_merger_base'
|
|
93
92
|
autoload :SourceRegionReportSupport, 'ast/merge/source_region_report_support'
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ast-merge
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 7.1.
|
|
4
|
+
version: 7.1.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -77,14 +77,14 @@ dependencies:
|
|
|
77
77
|
requirements:
|
|
78
78
|
- - '='
|
|
79
79
|
- !ruby/object:Gem::Version
|
|
80
|
-
version: 7.1.
|
|
80
|
+
version: 7.1.8
|
|
81
81
|
type: :runtime
|
|
82
82
|
prerelease: false
|
|
83
83
|
version_requirements: !ruby/object:Gem::Requirement
|
|
84
84
|
requirements:
|
|
85
85
|
- - '='
|
|
86
86
|
- !ruby/object:Gem::Version
|
|
87
|
-
version: 7.1.
|
|
87
|
+
version: 7.1.8
|
|
88
88
|
- !ruby/object:Gem::Dependency
|
|
89
89
|
name: version_gem
|
|
90
90
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -114,7 +114,7 @@ dependencies:
|
|
|
114
114
|
version: '3.0'
|
|
115
115
|
- - ">="
|
|
116
116
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: 3.0.
|
|
117
|
+
version: 3.0.31
|
|
118
118
|
type: :development
|
|
119
119
|
prerelease: false
|
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -124,7 +124,7 @@ dependencies:
|
|
|
124
124
|
version: '3.0'
|
|
125
125
|
- - ">="
|
|
126
126
|
- !ruby/object:Gem::Version
|
|
127
|
-
version: 3.0.
|
|
127
|
+
version: 3.0.31
|
|
128
128
|
- !ruby/object:Gem::Dependency
|
|
129
129
|
name: bundler-audit
|
|
130
130
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -202,7 +202,7 @@ dependencies:
|
|
|
202
202
|
version: '3.2'
|
|
203
203
|
- - ">="
|
|
204
204
|
- !ruby/object:Gem::Version
|
|
205
|
-
version: 3.2.
|
|
205
|
+
version: 3.2.3
|
|
206
206
|
type: :development
|
|
207
207
|
prerelease: false
|
|
208
208
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -212,7 +212,7 @@ dependencies:
|
|
|
212
212
|
version: '3.2'
|
|
213
213
|
- - ">="
|
|
214
214
|
- !ruby/object:Gem::Version
|
|
215
|
-
version: 3.2.
|
|
215
|
+
version: 3.2.3
|
|
216
216
|
- !ruby/object:Gem::Dependency
|
|
217
217
|
name: kettle-test
|
|
218
218
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -242,7 +242,7 @@ dependencies:
|
|
|
242
242
|
version: '3.2'
|
|
243
243
|
- - ">="
|
|
244
244
|
- !ruby/object:Gem::Version
|
|
245
|
-
version: 3.2.
|
|
245
|
+
version: 3.2.7
|
|
246
246
|
type: :development
|
|
247
247
|
prerelease: false
|
|
248
248
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -252,7 +252,7 @@ dependencies:
|
|
|
252
252
|
version: '3.2'
|
|
253
253
|
- - ">="
|
|
254
254
|
- !ruby/object:Gem::Version
|
|
255
|
-
version: 3.2.
|
|
255
|
+
version: 3.2.7
|
|
256
256
|
- !ruby/object:Gem::Dependency
|
|
257
257
|
name: ruby-progressbar
|
|
258
258
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -400,6 +400,7 @@ files:
|
|
|
400
400
|
- lib/ast/merge/rspec/dependency_tags_helpers.rb
|
|
401
401
|
- lib/ast/merge/rspec/merge_gem_registry.rb
|
|
402
402
|
- lib/ast/merge/rspec/provider_conformance.rb
|
|
403
|
+
- lib/ast/merge/rspec/provider_snapshot.rb
|
|
403
404
|
- lib/ast/merge/rspec/setup.rb
|
|
404
405
|
- lib/ast/merge/rspec/shared_examples.rb
|
|
405
406
|
- lib/ast/merge/rspec/shared_examples/comment_attachment.rb
|
|
@@ -489,7 +490,6 @@ files:
|
|
|
489
490
|
- lib/ast/merge/unresolved_review_state.rb
|
|
490
491
|
- lib/ast/merge/unresolved_support.rb
|
|
491
492
|
- lib/ast/merge/version.rb
|
|
492
|
-
- lib/ast/merge/yaml_document.rb
|
|
493
493
|
- sig/ast/merge.rbs
|
|
494
494
|
homepage: https://github.com/structuredmerge/structuredmerge-ruby
|
|
495
495
|
licenses:
|
|
@@ -497,10 +497,10 @@ licenses:
|
|
|
497
497
|
- PolyForm-Small-Business-1.0.0
|
|
498
498
|
metadata:
|
|
499
499
|
homepage_uri: https://structuredmerge.org
|
|
500
|
-
source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.
|
|
501
|
-
changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.1.
|
|
500
|
+
source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.8
|
|
501
|
+
changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.1.8/CHANGELOG.md
|
|
502
502
|
bug_tracker_uri: https://github.com/structuredmerge/structuredmerge-ruby/issues
|
|
503
|
-
documentation_uri: https://www.rubydoc.info/gems/ast-merge/7.1.
|
|
503
|
+
documentation_uri: https://www.rubydoc.info/gems/ast-merge/7.1.8
|
|
504
504
|
funding_uri: https://github.com/sponsors/pboling
|
|
505
505
|
wiki_uri: https://github.com/structuredmerge/structuredmerge-ruby/wiki
|
|
506
506
|
news_uri: https://www.railsbling.com/tags/ast-merge
|
metadata.gz.sig
CHANGED
|
Binary file
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'json'
|
|
4
|
-
|
|
5
|
-
module Ast
|
|
6
|
-
module Merge
|
|
7
|
-
# Parser-neutral semantic analysis shared by YAML backend providers.
|
|
8
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/ModuleLength, Metrics/PerceivedComplexity -- recursive YAML validation, rendering, and ownership are one parser-neutral semantic boundary
|
|
9
|
-
module YamlDocument
|
|
10
|
-
module_function
|
|
11
|
-
|
|
12
|
-
def analyze(parsed, dialect)
|
|
13
|
-
return unsupported("Unsupported YAML dialect #{dialect}.") unless dialect == 'yaml'
|
|
14
|
-
return parse_error('YAML documents must parse to a mapping root.') unless parsed.is_a?(Hash)
|
|
15
|
-
|
|
16
|
-
validated = validate_node(parsed, '')
|
|
17
|
-
return { ok: false, diagnostics: [validated[:diagnostic]], policies: [] } unless validated[:ok]
|
|
18
|
-
|
|
19
|
-
{
|
|
20
|
-
ok: true,
|
|
21
|
-
diagnostics: [],
|
|
22
|
-
analysis: {
|
|
23
|
-
kind: 'yaml',
|
|
24
|
-
dialect: 'yaml',
|
|
25
|
-
normalized_source: canonical_yaml(validated[:value]),
|
|
26
|
-
document: validated[:value],
|
|
27
|
-
root_kind: 'mapping',
|
|
28
|
-
owners: collect_owners(validated[:value])
|
|
29
|
-
},
|
|
30
|
-
policies: []
|
|
31
|
-
}
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def match_owners(template, destination)
|
|
35
|
-
destination_paths = destination[:owners].to_h { |owner| [owner[:path], true] }
|
|
36
|
-
template_paths = template[:owners].to_h { |owner| [owner[:path], true] }
|
|
37
|
-
|
|
38
|
-
{
|
|
39
|
-
matched: template[:owners]
|
|
40
|
-
.filter { |owner| destination_paths[owner[:path]] }
|
|
41
|
-
.map { |owner| { template_path: owner[:path], destination_path: owner[:path] } },
|
|
42
|
-
unmatched_template: template[:owners].map { |owner| owner[:path] }.reject { |path| destination_paths[path] },
|
|
43
|
-
unmatched_destination: destination[:owners]
|
|
44
|
-
.map { |owner| owner[:path] }
|
|
45
|
-
.reject { |path| template_paths[path] }
|
|
46
|
-
}
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def validate_node(value, path)
|
|
50
|
-
if scalar?(value)
|
|
51
|
-
{ ok: true, value: value }
|
|
52
|
-
elsif value.is_a?(Array)
|
|
53
|
-
value.each_with_index.each_with_object({ ok: true, value: [] }) do |(item, index), memo|
|
|
54
|
-
validated = validate_node(item, "#{path}/#{index}")
|
|
55
|
-
return validated unless validated[:ok]
|
|
56
|
-
|
|
57
|
-
memo[:value] << validated[:value]
|
|
58
|
-
end
|
|
59
|
-
elsif value.is_a?(Hash)
|
|
60
|
-
value.each_with_object({ ok: true, value: {} }) do |(key, item), memo|
|
|
61
|
-
validated = validate_node(item, "#{path}/#{key}")
|
|
62
|
-
return validated unless validated[:ok]
|
|
63
|
-
|
|
64
|
-
memo[:value][key] = validated[:value]
|
|
65
|
-
end
|
|
66
|
-
else
|
|
67
|
-
unsupported("Unsupported YAML value at #{path.empty? ? '/' : path}. " \
|
|
68
|
-
'Only mappings, scalar values, and sequences are supported.')
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
private_class_method :validate_node
|
|
72
|
-
|
|
73
|
-
def scalar?(value)
|
|
74
|
-
value.nil? || value.is_a?(String) || value.is_a?(Numeric) || value == true || value == false
|
|
75
|
-
end
|
|
76
|
-
private_class_method :scalar?
|
|
77
|
-
|
|
78
|
-
def canonical_yaml(mapping)
|
|
79
|
-
"#{render_mapping(mapping).join("\n")}\n"
|
|
80
|
-
end
|
|
81
|
-
private_class_method :canonical_yaml
|
|
82
|
-
|
|
83
|
-
def render_mapping(mapping, indent = 0)
|
|
84
|
-
mapping.flat_map do |key, value|
|
|
85
|
-
prefix = ' ' * indent
|
|
86
|
-
if value.is_a?(Array)
|
|
87
|
-
["#{prefix}#{key}:"] + render_sequence(value, indent + 2)
|
|
88
|
-
elsif value.is_a?(Hash)
|
|
89
|
-
["#{prefix}#{key}:"] + render_mapping(value, indent + 2)
|
|
90
|
-
elsif value.nil?
|
|
91
|
-
["#{prefix}#{key}:"]
|
|
92
|
-
else
|
|
93
|
-
["#{prefix}#{key}: #{render_scalar(value)}"]
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
private_class_method :render_mapping
|
|
98
|
-
|
|
99
|
-
def render_sequence(sequence, indent)
|
|
100
|
-
prefix = ' ' * indent
|
|
101
|
-
sequence.flat_map do |item|
|
|
102
|
-
if scalar?(item)
|
|
103
|
-
["#{prefix}- #{render_scalar(item)}"]
|
|
104
|
-
elsif item.is_a?(Hash)
|
|
105
|
-
["#{prefix}-"] + render_mapping(item, indent + 2)
|
|
106
|
-
elsif item.is_a?(Array)
|
|
107
|
-
["#{prefix}-"] + render_sequence(item, indent + 2)
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
private_class_method :render_sequence
|
|
112
|
-
|
|
113
|
-
def render_scalar(value)
|
|
114
|
-
return '' if value.nil?
|
|
115
|
-
return value ? 'true' : 'false' if [true, false].include?(value)
|
|
116
|
-
return value.to_s unless value.is_a?(String)
|
|
117
|
-
|
|
118
|
-
value.match?(/\A[A-Za-z0-9_.-]+\z/) ? value : JSON.generate(value)
|
|
119
|
-
end
|
|
120
|
-
private_class_method :render_scalar
|
|
121
|
-
|
|
122
|
-
def collect_owners(mapping, prefix = '')
|
|
123
|
-
mapping.keys.sort.flat_map do |key|
|
|
124
|
-
path = "#{prefix}/#{key}"
|
|
125
|
-
value = mapping[key]
|
|
126
|
-
if value.is_a?(Array)
|
|
127
|
-
[{ path: path, owner_kind: 'key_value', match_key: key }] +
|
|
128
|
-
value.each_with_index.flat_map do |item, index|
|
|
129
|
-
item_path = "#{path}/#{index}"
|
|
130
|
-
nested = item.is_a?(Hash) ? collect_owners(item, item_path) : []
|
|
131
|
-
[{ path: item_path, owner_kind: 'sequence_item' }] + nested
|
|
132
|
-
end
|
|
133
|
-
elsif value.is_a?(Hash)
|
|
134
|
-
[{ path: path, owner_kind: 'mapping', match_key: key }] + collect_owners(value, path)
|
|
135
|
-
else
|
|
136
|
-
[{ path: path, owner_kind: 'key_value', match_key: key }]
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
private_class_method :collect_owners
|
|
141
|
-
|
|
142
|
-
def parse_error(message)
|
|
143
|
-
{ ok: false, diagnostics: [{ severity: 'error', category: 'parse_error', message: message }], policies: [] }
|
|
144
|
-
end
|
|
145
|
-
private_class_method :parse_error
|
|
146
|
-
|
|
147
|
-
def unsupported(message)
|
|
148
|
-
{
|
|
149
|
-
ok: false,
|
|
150
|
-
diagnostic: { severity: 'error', category: 'unsupported_feature', message: message },
|
|
151
|
-
diagnostics: [{ severity: 'error', category: 'unsupported_feature', message: message }],
|
|
152
|
-
policies: []
|
|
153
|
-
}
|
|
154
|
-
end
|
|
155
|
-
private_class_method :unsupported
|
|
156
|
-
end
|
|
157
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/ModuleLength, Metrics/PerceivedComplexity
|
|
158
|
-
end
|
|
159
|
-
end
|