rigortype 0.1.19 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -23
- data/lib/rigor/analysis/check_rules/rule_walk.rb +3 -21
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +24 -15
- data/lib/rigor/analysis/check_rules.rb +492 -71
- data/lib/rigor/analysis/dependency_source_inference/index.rb +4 -7
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -18
- data/lib/rigor/analysis/dependency_source_inference.rb +3 -12
- data/lib/rigor/analysis/fact_store.rb +5 -4
- data/lib/rigor/analysis/rule_catalog.rb +153 -6
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +17 -17
- data/lib/rigor/analysis/runner/project_pre_passes.rb +9 -8
- data/lib/rigor/analysis/runner.rb +17 -6
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +3 -4
- data/lib/rigor/analysis/worker_session.rb +10 -14
- data/lib/rigor/builtins/predefined_constant_refinements.rb +151 -0
- data/lib/rigor/cache/store.rb +5 -3
- data/lib/rigor/cli/annotate_command.rb +28 -7
- data/lib/rigor/cli/baseline_command.rb +4 -3
- data/lib/rigor/cli/check_command.rb +115 -16
- data/lib/rigor/cli/coverage_command.rb +148 -16
- data/lib/rigor/cli/coverage_scan.rb +57 -0
- data/lib/rigor/cli/explain_command.rb +2 -0
- data/lib/rigor/cli/lsp_command.rb +3 -7
- data/lib/rigor/cli/mutation_protection_renderer.rb +63 -0
- data/lib/rigor/cli/mutation_protection_report.rb +73 -0
- data/lib/rigor/cli/options.rb +9 -0
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/protection_renderer.rb +63 -0
- data/lib/rigor/cli/protection_report.rb +68 -0
- data/lib/rigor/cli/sig_gen_command.rb +2 -1
- data/lib/rigor/cli/trace_command.rb +2 -1
- data/lib/rigor/cli/triage_command.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +1 -1
- data/lib/rigor/cli/type_scan_command.rb +2 -1
- data/lib/rigor/cli.rb +3 -2
- data/lib/rigor/configuration/dependencies.rb +2 -4
- data/lib/rigor/configuration.rb +45 -7
- data/lib/rigor/environment/bundle_sig_discovery.rb +61 -13
- data/lib/rigor/environment/class_registry.rb +4 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +43 -0
- data/lib/rigor/environment/lockfile_resolver.rb +1 -1
- data/lib/rigor/environment/rbs_collection_discovery.rb +1 -2
- data/lib/rigor/environment/rbs_coverage_report.rb +2 -1
- data/lib/rigor/environment/rbs_loader.rb +49 -5
- data/lib/rigor/environment.rb +17 -7
- data/lib/rigor/flow_contribution/fact.rb +1 -1
- data/lib/rigor/flow_contribution.rb +3 -5
- data/lib/rigor/inference/acceptance.rb +17 -9
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/builtins/comparable_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/method_catalog.rb +19 -0
- data/lib/rigor/inference/builtins/string_catalog.rb +9 -1
- data/lib/rigor/inference/expression_typer.rb +20 -28
- data/lib/rigor/inference/hkt_body.rb +8 -11
- data/lib/rigor/inference/hkt_body_parser.rb +10 -12
- data/lib/rigor/inference/hkt_registry.rb +10 -11
- data/lib/rigor/inference/method_dispatcher/call_context.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +156 -21
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +9 -73
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +6 -7
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +10 -16
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +25 -13
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +93 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +1 -3
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +24 -22
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +90 -15
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +303 -0
- data/lib/rigor/inference/method_dispatcher.rb +40 -48
- data/lib/rigor/inference/mutation_widening.rb +5 -11
- data/lib/rigor/inference/narrowing.rb +14 -16
- data/lib/rigor/inference/parameter_inference_collector.rb +367 -0
- data/lib/rigor/inference/project_patched_methods.rb +4 -7
- data/lib/rigor/inference/project_patched_scanner.rb +2 -13
- data/lib/rigor/inference/protection_scanner.rb +86 -0
- data/lib/rigor/inference/scope_indexer.rb +129 -55
- data/lib/rigor/inference/statement_evaluator.rb +244 -114
- data/lib/rigor/inference/struct_fold_safety.rb +181 -0
- data/lib/rigor/inference/synthetic_method.rb +7 -7
- data/lib/rigor/language_server/completion_provider.rb +6 -12
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +3 -3
- data/lib/rigor/language_server/hover_provider.rb +2 -3
- data/lib/rigor/language_server/hover_renderer.rb +2 -11
- data/lib/rigor/language_server/server.rb +9 -17
- data/lib/rigor/language_server.rb +4 -5
- data/lib/rigor/plugin/base.rb +10 -8
- data/lib/rigor/plugin/macro/block_as_method.rb +3 -4
- data/lib/rigor/plugin/macro/heredoc_template.rb +4 -7
- data/lib/rigor/plugin/macro/trait_registry.rb +3 -6
- data/lib/rigor/plugin/macro.rb +4 -5
- data/lib/rigor/plugin/manifest.rb +45 -66
- data/lib/rigor/plugin/registry.rb +6 -7
- data/lib/rigor/plugin/type_node_resolver.rb +6 -8
- data/lib/rigor/protection/mutation_scanner.rb +120 -0
- data/lib/rigor/protection/mutator.rb +246 -0
- data/lib/rigor/rbs_extended.rb +24 -36
- data/lib/rigor/reflection.rb +4 -7
- data/lib/rigor/scope/discovery_index.rb +14 -2
- data/lib/rigor/scope.rb +54 -11
- data/lib/rigor/sig_gen/observed_call.rb +3 -3
- data/lib/rigor/sig_gen/writer.rb +40 -2
- data/lib/rigor/source/constant_path.rb +62 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/type/bound_method.rb +2 -11
- data/lib/rigor/type/combinator.rb +16 -3
- data/lib/rigor/type/constant.rb +2 -11
- data/lib/rigor/type/data_class.rb +2 -11
- data/lib/rigor/type/data_instance.rb +2 -11
- data/lib/rigor/type/hash_shape.rb +2 -11
- data/lib/rigor/type/integer_range.rb +2 -11
- data/lib/rigor/type/intersection.rb +2 -11
- data/lib/rigor/type/nominal.rb +2 -11
- data/lib/rigor/type/plain_lattice.rb +37 -0
- data/lib/rigor/type/refined.rb +72 -13
- data/lib/rigor/type/singleton.rb +2 -11
- data/lib/rigor/type/struct_class.rb +75 -0
- data/lib/rigor/type/struct_instance.rb +93 -0
- data/lib/rigor/type/tuple.rb +5 -15
- data/lib/rigor/type.rb +2 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +3 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +3 -3
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +5 -13
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +11 -17
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +7 -10
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +4 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +6 -8
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +5 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -2
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +9 -11
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +8 -9
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +13 -12
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +3 -4
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +8 -8
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +9 -11
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +7 -8
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +7 -9
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +12 -13
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +15 -23
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +3 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +3 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +2 -4
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +27 -11
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +4 -6
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +12 -18
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +5 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +3 -4
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +1 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +19 -14
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +5 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +2 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +7 -11
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +6 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +5 -15
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +28 -41
- data/sig/rigor/scope.rbs +9 -1
- data/sig/rigor/type.rbs +36 -1
- metadata +19 -1
|
@@ -4,9 +4,11 @@ require "prism"
|
|
|
4
4
|
|
|
5
5
|
require_relative "../scope"
|
|
6
6
|
require_relative "../type"
|
|
7
|
+
require_relative "../source/constant_path"
|
|
7
8
|
require_relative "mutation_widening"
|
|
8
9
|
require_relative "narrowing"
|
|
9
10
|
require_relative "statement_evaluator"
|
|
11
|
+
require_relative "struct_fold_safety"
|
|
10
12
|
|
|
11
13
|
module Rigor
|
|
12
14
|
module Inference
|
|
@@ -114,12 +116,8 @@ module Rigor
|
|
|
114
116
|
# recognised `define_method` calls and records the
|
|
115
117
|
# introduced method names. `rigor check` consults the
|
|
116
118
|
# table to suppress false positives for methods the
|
|
117
|
-
# user has defined but no RBS sig describes.
|
|
118
|
-
#
|
|
119
|
-
# / include tables below) so a method `def`/`attr_reader`-
|
|
120
|
-
# declared in one file suppresses a false `undefined-method`
|
|
121
|
-
# for a call in another — `rigor check` seeds the project-wide
|
|
122
|
-
# table via `Runner#seed_project_scope`.
|
|
119
|
+
# user has defined but no RBS sig describes. Merged
|
|
120
|
+
# UNDER the cross-file pre-pass seed; details: merge_project_method_indexes.
|
|
123
121
|
discovered_methods = deep_merge_class_methods(
|
|
124
122
|
default_scope.discovered_methods, build_discovered_methods(root)
|
|
125
123
|
)
|
|
@@ -150,6 +148,10 @@ module Rigor
|
|
|
150
148
|
# `table[node]` to type predicates; the second pass's
|
|
151
149
|
# entry is the one that reflects all flow-derived
|
|
152
150
|
# rebinds, so it MUST overwrite the first.
|
|
151
|
+
# ADR-48 Struct slice 3 — install the top-level fold-safe-local set so
|
|
152
|
+
# a member read off a mutation-free top-level struct binding folds.
|
|
153
|
+
seeded_scope = seed_struct_fold_safe(seeded_scope, root)
|
|
154
|
+
|
|
153
155
|
on_enter = ->(node, scope) { table[node] = scope }
|
|
154
156
|
StatementEvaluator.new(scope: seeded_scope, on_enter: on_enter,
|
|
155
157
|
converged_loop_recording: converged_loop_recording).evaluate(root)
|
|
@@ -158,6 +160,17 @@ module Rigor
|
|
|
158
160
|
table
|
|
159
161
|
end
|
|
160
162
|
|
|
163
|
+
# ADR-48 Struct slice 3 — installs the top-level fold-safe-local set
|
|
164
|
+
# ({Inference::StructFoldSafety}). Struct member layouts of constant
|
|
165
|
+
# receivers are resolved through the side-table the seeded scope carries.
|
|
166
|
+
def seed_struct_fold_safe(seeded_scope, root)
|
|
167
|
+
seeded_scope.with_struct_fold_safe(
|
|
168
|
+
StructFoldSafety.fold_safe_locals(
|
|
169
|
+
root, ->(name) { seeded_scope.struct_member_layout(name)&.[](:members) }
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
end
|
|
173
|
+
|
|
161
174
|
# v0.0.2 #5 + ADR-24 slice 2 — seeds the three
|
|
162
175
|
# project-method indexes onto `seeded_scope`: the
|
|
163
176
|
# per-instance-method def-node table, the class ->
|
|
@@ -185,11 +198,9 @@ module Rigor
|
|
|
185
198
|
method_visibilities = default_scope.discovered_method_visibilities.merge(
|
|
186
199
|
build_discovered_method_visibilities(root)
|
|
187
200
|
) { |_class, cross_file, per_file| cross_file.merge(per_file) }
|
|
188
|
-
# ADR-48 — per-file Data member layouts merged OVER the
|
|
189
|
-
# seed (same-file declaration is authoritative
|
|
190
|
-
data_member_layouts = default_scope
|
|
191
|
-
build_data_member_layouts(root)
|
|
192
|
-
)
|
|
201
|
+
# ADR-48 — per-file Data + Struct member layouts merged OVER the
|
|
202
|
+
# cross-file seed (same-file declaration is authoritative).
|
|
203
|
+
data_member_layouts, struct_member_layouts = merge_member_layouts(default_scope, root)
|
|
193
204
|
|
|
194
205
|
seeded_scope.with_discovery(
|
|
195
206
|
seeded_scope.discovery.with(
|
|
@@ -198,11 +209,23 @@ module Rigor
|
|
|
198
209
|
discovered_superclasses: superclasses,
|
|
199
210
|
discovered_includes: includes,
|
|
200
211
|
discovered_method_visibilities: method_visibilities,
|
|
201
|
-
data_member_layouts: data_member_layouts
|
|
212
|
+
data_member_layouts: data_member_layouts,
|
|
213
|
+
struct_member_layouts: struct_member_layouts
|
|
202
214
|
)
|
|
203
215
|
)
|
|
204
216
|
end
|
|
205
217
|
|
|
218
|
+
# ADR-48 — the per-file Data + Struct member-layout tables, each merged
|
|
219
|
+
# OVER the cross-file seed so a same-file declaration wins for its own
|
|
220
|
+
# classes. Returned as a pair to keep {#merge_project_method_indexes}
|
|
221
|
+
# under the method-size budget.
|
|
222
|
+
def merge_member_layouts(default_scope, root)
|
|
223
|
+
[
|
|
224
|
+
default_scope.data_member_layouts.merge(build_data_member_layouts(root)),
|
|
225
|
+
default_scope.struct_member_layouts.merge(build_struct_member_layouts(root))
|
|
226
|
+
]
|
|
227
|
+
end
|
|
228
|
+
|
|
206
229
|
# Slice 7 phase 2. Builds the class-level ivar accumulator
|
|
207
230
|
# by walking every `Prism::ClassNode` / `Prism::ModuleNode`
|
|
208
231
|
# body, descending into each nested `Prism::DefNode`, and
|
|
@@ -357,7 +380,7 @@ module Rigor
|
|
|
357
380
|
|
|
358
381
|
case node
|
|
359
382
|
when Prism::ClassNode, Prism::ModuleNode
|
|
360
|
-
name =
|
|
383
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
361
384
|
if name
|
|
362
385
|
child_prefix = qualified_prefix + [name]
|
|
363
386
|
if node.body
|
|
@@ -841,7 +864,7 @@ module Rigor
|
|
|
841
864
|
|
|
842
865
|
case root
|
|
843
866
|
when Prism::ClassNode, Prism::ModuleNode
|
|
844
|
-
name =
|
|
867
|
+
name = Source::ConstantPath.qualified_name(root.constant_path)
|
|
845
868
|
if name && root.body
|
|
846
869
|
child = prefix + [name]
|
|
847
870
|
collect_class_method_defs(root.body, child, acc)
|
|
@@ -1251,7 +1274,7 @@ module Rigor
|
|
|
1251
1274
|
|
|
1252
1275
|
case node
|
|
1253
1276
|
when Prism::ClassNode, Prism::ModuleNode
|
|
1254
|
-
name =
|
|
1277
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1255
1278
|
if name
|
|
1256
1279
|
child_prefix = qualified_prefix + [name]
|
|
1257
1280
|
walk_class_cvars(node.body, child_prefix, default_scope, accumulator) if node.body
|
|
@@ -1337,7 +1360,7 @@ module Rigor
|
|
|
1337
1360
|
|
|
1338
1361
|
case node
|
|
1339
1362
|
when Prism::ClassNode, Prism::ModuleNode
|
|
1340
|
-
name =
|
|
1363
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1341
1364
|
if name
|
|
1342
1365
|
child_prefix = qualified_prefix + [name]
|
|
1343
1366
|
walk_constant_writes(node.body, child_prefix, default_scope, accumulator) if node.body
|
|
@@ -1347,7 +1370,7 @@ module Rigor
|
|
|
1347
1370
|
record_constant_write(node, qualified_prefix, default_scope, accumulator, node.name.to_s)
|
|
1348
1371
|
return
|
|
1349
1372
|
when Prism::ConstantPathWriteNode
|
|
1350
|
-
full =
|
|
1373
|
+
full = Source::ConstantPath.qualified_name(node.target)
|
|
1351
1374
|
record_constant_write(node, [], default_scope, accumulator, full) if full
|
|
1352
1375
|
return
|
|
1353
1376
|
end
|
|
@@ -1413,7 +1436,7 @@ module Rigor
|
|
|
1413
1436
|
|
|
1414
1437
|
case node
|
|
1415
1438
|
when Prism::ClassNode, Prism::ModuleNode
|
|
1416
|
-
name =
|
|
1439
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1417
1440
|
if name
|
|
1418
1441
|
child_prefix = qualified_prefix + [name]
|
|
1419
1442
|
record_meta_superclass_members(node, child_prefix, accumulator) if node.is_a?(Prism::ClassNode)
|
|
@@ -1466,7 +1489,7 @@ module Rigor
|
|
|
1466
1489
|
when Prism::SelfNode
|
|
1467
1490
|
qualified_prefix
|
|
1468
1491
|
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
|
1469
|
-
rendered =
|
|
1492
|
+
rendered = Source::ConstantPath.qualified_name(node.expression)
|
|
1470
1493
|
return nil unless rendered
|
|
1471
1494
|
|
|
1472
1495
|
if !qualified_prefix.empty? && qualified_prefix.last == rendered
|
|
@@ -1573,7 +1596,7 @@ module Rigor
|
|
|
1573
1596
|
when Prism::ConstantReadNode
|
|
1574
1597
|
receiver.name.to_s == qualified_prefix.last
|
|
1575
1598
|
when Prism::ConstantPathNode
|
|
1576
|
-
rendered =
|
|
1599
|
+
rendered = Source::ConstantPath.render(receiver)
|
|
1577
1600
|
return false unless rendered
|
|
1578
1601
|
|
|
1579
1602
|
path = rendered.split("::")
|
|
@@ -1605,7 +1628,7 @@ module Rigor
|
|
|
1605
1628
|
|
|
1606
1629
|
case node
|
|
1607
1630
|
when Prism::ClassNode, Prism::ModuleNode
|
|
1608
|
-
name =
|
|
1631
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1609
1632
|
if name
|
|
1610
1633
|
child_prefix = qualified_prefix + [name]
|
|
1611
1634
|
walk_def_nodes(node.body, child_prefix, false, accumulator) if node.body
|
|
@@ -1681,7 +1704,7 @@ module Rigor
|
|
|
1681
1704
|
|
|
1682
1705
|
case node
|
|
1683
1706
|
when Prism::ClassNode, Prism::ModuleNode
|
|
1684
|
-
name =
|
|
1707
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1685
1708
|
if name
|
|
1686
1709
|
walk_singleton_body(node.body, qualified_prefix + [name], false, accumulator) if node.body
|
|
1687
1710
|
return
|
|
@@ -1810,16 +1833,16 @@ module Rigor
|
|
|
1810
1833
|
|
|
1811
1834
|
case node
|
|
1812
1835
|
when Prism::ClassNode
|
|
1813
|
-
name =
|
|
1836
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1814
1837
|
if name
|
|
1815
1838
|
full = (qualified_prefix + [name]).join("::")
|
|
1816
|
-
superclass = node.superclass &&
|
|
1839
|
+
superclass = node.superclass && Source::ConstantPath.qualified_name(node.superclass)
|
|
1817
1840
|
accumulator[full] = superclass if superclass
|
|
1818
1841
|
walk_class_superclasses(node.body, qualified_prefix + [name], accumulator) if node.body
|
|
1819
1842
|
return
|
|
1820
1843
|
end
|
|
1821
1844
|
when Prism::ModuleNode
|
|
1822
|
-
name =
|
|
1845
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1823
1846
|
if name
|
|
1824
1847
|
walk_class_superclasses(node.body, qualified_prefix + [name], accumulator) if node.body
|
|
1825
1848
|
return
|
|
@@ -1851,14 +1874,14 @@ module Rigor
|
|
|
1851
1874
|
|
|
1852
1875
|
case node
|
|
1853
1876
|
when Prism::ClassNode
|
|
1854
|
-
name =
|
|
1877
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1855
1878
|
if name
|
|
1856
1879
|
record_data_member_layout(accumulator, qualified_prefix + [name], node.superclass)
|
|
1857
1880
|
walk_data_member_layouts(node.body, qualified_prefix + [name], accumulator) if node.body
|
|
1858
1881
|
return
|
|
1859
1882
|
end
|
|
1860
1883
|
when Prism::ModuleNode
|
|
1861
|
-
name =
|
|
1884
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1862
1885
|
if name
|
|
1863
1886
|
walk_data_member_layouts(node.body, qualified_prefix + [name], accumulator) if node.body
|
|
1864
1887
|
return
|
|
@@ -1883,6 +1906,74 @@ module Rigor
|
|
|
1883
1906
|
accumulator[qualified_parts.join("::")] = members.freeze
|
|
1884
1907
|
end
|
|
1885
1908
|
|
|
1909
|
+
# ADR-48 Struct follow-up — the `Struct.new(...)` sibling of
|
|
1910
|
+
# {#build_data_member_layouts}. A separate, additive table so the
|
|
1911
|
+
# existing `Data.define` value-shape contract (a bare `[Symbol]`) is
|
|
1912
|
+
# untouched: a Struct entry carries `{ members:, keyword_init: }`
|
|
1913
|
+
# because the dispatcher needs the flag to fold the matching `.new`
|
|
1914
|
+
# call form (positional vs keyword) without manufacturing a wrong map.
|
|
1915
|
+
def build_struct_member_layouts(root)
|
|
1916
|
+
accumulator = {}
|
|
1917
|
+
walk_struct_member_layouts(root, [], accumulator)
|
|
1918
|
+
accumulator.freeze
|
|
1919
|
+
end
|
|
1920
|
+
|
|
1921
|
+
def walk_struct_member_layouts(node, qualified_prefix, accumulator)
|
|
1922
|
+
return unless node.is_a?(Prism::Node)
|
|
1923
|
+
|
|
1924
|
+
case node
|
|
1925
|
+
when Prism::ClassNode
|
|
1926
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1927
|
+
if name
|
|
1928
|
+
record_struct_member_layout(accumulator, qualified_prefix + [name], node.superclass)
|
|
1929
|
+
walk_struct_member_layouts(node.body, qualified_prefix + [name], accumulator) if node.body
|
|
1930
|
+
return
|
|
1931
|
+
end
|
|
1932
|
+
when Prism::ModuleNode
|
|
1933
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1934
|
+
if name
|
|
1935
|
+
walk_struct_member_layouts(node.body, qualified_prefix + [name], accumulator) if node.body
|
|
1936
|
+
return
|
|
1937
|
+
end
|
|
1938
|
+
when Prism::ConstantWriteNode
|
|
1939
|
+
record_struct_member_layout(accumulator, qualified_prefix + [node.name.to_s], node.value)
|
|
1940
|
+
end
|
|
1941
|
+
|
|
1942
|
+
node.compact_child_nodes.each do |child|
|
|
1943
|
+
walk_struct_member_layouts(child, qualified_prefix, accumulator)
|
|
1944
|
+
end
|
|
1945
|
+
end
|
|
1946
|
+
|
|
1947
|
+
# Records `qualified -> { members:, keyword_init: }` when `expr` is a
|
|
1948
|
+
# `Struct.new(*Symbol [, keyword_init: <bool>])` call with at least one
|
|
1949
|
+
# literal-Symbol member.
|
|
1950
|
+
def record_struct_member_layout(accumulator, qualified_parts, expr)
|
|
1951
|
+
return unless struct_new_call?(expr)
|
|
1952
|
+
|
|
1953
|
+
members = meta_member_names(expr)
|
|
1954
|
+
return if members.empty?
|
|
1955
|
+
|
|
1956
|
+
accumulator[qualified_parts.join("::")] = {
|
|
1957
|
+
members: members.freeze,
|
|
1958
|
+
keyword_init: struct_new_keyword_init?(expr)
|
|
1959
|
+
}.freeze
|
|
1960
|
+
end
|
|
1961
|
+
|
|
1962
|
+
# True when a `Struct.new` call carries `keyword_init: true` as a
|
|
1963
|
+
# literal in its trailing keyword hash. A non-literal value (or its
|
|
1964
|
+
# absence) reads as `false` — the conservative positional default.
|
|
1965
|
+
def struct_new_keyword_init?(call_node)
|
|
1966
|
+
args = call_node.arguments&.arguments || []
|
|
1967
|
+
last = args.last
|
|
1968
|
+
return false unless last.is_a?(Prism::KeywordHashNode)
|
|
1969
|
+
|
|
1970
|
+
last.elements.any? do |assoc|
|
|
1971
|
+
assoc.is_a?(Prism::AssocNode) &&
|
|
1972
|
+
assoc.key.is_a?(Prism::SymbolNode) && assoc.key.unescaped == "keyword_init" &&
|
|
1973
|
+
assoc.value.is_a?(Prism::TrueNode)
|
|
1974
|
+
end
|
|
1975
|
+
end
|
|
1976
|
+
|
|
1886
1977
|
MIXIN_CALL_NAMES = %i[include prepend].freeze
|
|
1887
1978
|
|
|
1888
1979
|
# ADR-24 slice 2 — per-class/module table mapping a fully
|
|
@@ -1906,7 +1997,7 @@ module Rigor
|
|
|
1906
1997
|
|
|
1907
1998
|
case node
|
|
1908
1999
|
when Prism::ClassNode, Prism::ModuleNode
|
|
1909
|
-
name =
|
|
2000
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1910
2001
|
if name
|
|
1911
2002
|
full = (qualified_prefix + [name]).join("::")
|
|
1912
2003
|
walk_class_includes(node.body, qualified_prefix + [name], full, accumulator) if node.body
|
|
@@ -1926,7 +2017,7 @@ module Rigor
|
|
|
1926
2017
|
return unless MIXIN_CALL_NAMES.include?(node.name)
|
|
1927
2018
|
|
|
1928
2019
|
node.arguments&.arguments&.each do |arg|
|
|
1929
|
-
mod =
|
|
2020
|
+
mod = Source::ConstantPath.qualified_name(arg)
|
|
1930
2021
|
(accumulator[current_class] ||= []) << mod if mod
|
|
1931
2022
|
end
|
|
1932
2023
|
end
|
|
@@ -1966,7 +2057,7 @@ module Rigor
|
|
|
1966
2057
|
|
|
1967
2058
|
case node
|
|
1968
2059
|
when Prism::ClassNode, Prism::ModuleNode
|
|
1969
|
-
name =
|
|
2060
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
1970
2061
|
if name
|
|
1971
2062
|
child_prefix = qualified_prefix + [name]
|
|
1972
2063
|
walk_method_visibilities(node.body, child_prefix, false, :public, accumulator) if node.body
|
|
@@ -2104,7 +2195,7 @@ module Rigor
|
|
|
2104
2195
|
|
|
2105
2196
|
case node
|
|
2106
2197
|
when Prism::ClassNode, Prism::ModuleNode
|
|
2107
|
-
name =
|
|
2198
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
2108
2199
|
if name
|
|
2109
2200
|
collect_class_alias_map(node.body, qualified_prefix + [name], accumulator) if node.body
|
|
2110
2201
|
return accumulator
|
|
@@ -2252,7 +2343,8 @@ module Rigor
|
|
|
2252
2343
|
# `{ def_nodes:, def_sources:, superclasses:, includes:, class_sources: }`
|
|
2253
2344
|
def discovered_def_index_for_paths(paths, buffer: nil)
|
|
2254
2345
|
acc = { def_nodes: {}, singleton_def_nodes: {}, def_sources: {}, superclasses: {}, includes: {},
|
|
2255
|
-
method_visibilities: {}, methods: {}, class_sources: {}, data_member_layouts: {}
|
|
2346
|
+
method_visibilities: {}, methods: {}, class_sources: {}, data_member_layouts: {},
|
|
2347
|
+
struct_member_layouts: {} }
|
|
2256
2348
|
paths.each do |path|
|
|
2257
2349
|
physical = buffer ? buffer.resolve(path) : path
|
|
2258
2350
|
root = Prism.parse(File.read(physical), filepath: path).value
|
|
@@ -2306,6 +2398,7 @@ module Rigor
|
|
|
2306
2398
|
record_class_sources(acc[:class_sources], path, root, superclasses, includes)
|
|
2307
2399
|
merge_class_keyed_index_tables(acc, root)
|
|
2308
2400
|
acc[:data_member_layouts].merge!(build_data_member_layouts(root))
|
|
2401
|
+
acc[:struct_member_layouts].merge!(build_struct_member_layouts(root))
|
|
2309
2402
|
end
|
|
2310
2403
|
|
|
2311
2404
|
# Folds the per-class method-visibility and method-existence tables of
|
|
@@ -2364,14 +2457,14 @@ module Rigor
|
|
|
2364
2457
|
|
|
2365
2458
|
case node
|
|
2366
2459
|
when Prism::ClassNode
|
|
2367
|
-
name =
|
|
2460
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
2368
2461
|
if name
|
|
2369
2462
|
full = (qualified_prefix + [name]).join("::")
|
|
2370
2463
|
accumulator[full] = Type::Combinator.singleton_of(full)
|
|
2371
2464
|
return collect_class_decls(node.body, qualified_prefix + [name], accumulator) if node.body
|
|
2372
2465
|
end
|
|
2373
2466
|
when Prism::ModuleNode
|
|
2374
|
-
name =
|
|
2467
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
2375
2468
|
return collect_class_decls(node.body, qualified_prefix + [name], accumulator) if name && node.body
|
|
2376
2469
|
when Prism::ConstantWriteNode
|
|
2377
2470
|
record_class_new_constant_decl(node, qualified_prefix, accumulator)
|
|
@@ -2417,7 +2510,7 @@ module Rigor
|
|
|
2417
2510
|
arg = call_node.arguments&.arguments&.first
|
|
2418
2511
|
return nil if arg.nil?
|
|
2419
2512
|
|
|
2420
|
-
raw =
|
|
2513
|
+
raw = Source::ConstantPath.qualified_name(arg)
|
|
2421
2514
|
return nil if raw.nil?
|
|
2422
2515
|
|
|
2423
2516
|
prefix = qualified_prefix.dup
|
|
@@ -2462,7 +2555,7 @@ module Rigor
|
|
|
2462
2555
|
end
|
|
2463
2556
|
|
|
2464
2557
|
def record_class_or_module?(node, qualified_prefix, identity_table, discovered)
|
|
2465
|
-
name =
|
|
2558
|
+
name = Source::ConstantPath.qualified_name(node.constant_path)
|
|
2466
2559
|
return false unless name
|
|
2467
2560
|
|
|
2468
2561
|
full = (qualified_prefix + [name]).join("::")
|
|
@@ -2574,25 +2667,6 @@ module Rigor
|
|
|
2574
2667
|
end
|
|
2575
2668
|
end
|
|
2576
2669
|
|
|
2577
|
-
def qualified_name_for(constant_path_node)
|
|
2578
|
-
case constant_path_node
|
|
2579
|
-
when Prism::ConstantReadNode
|
|
2580
|
-
constant_path_node.name.to_s
|
|
2581
|
-
when Prism::ConstantPathNode
|
|
2582
|
-
render_constant_path(constant_path_node)
|
|
2583
|
-
end
|
|
2584
|
-
end
|
|
2585
|
-
|
|
2586
|
-
def render_constant_path(node)
|
|
2587
|
-
prefix =
|
|
2588
|
-
case node.parent
|
|
2589
|
-
when Prism::ConstantReadNode then "#{node.parent.name}::"
|
|
2590
|
-
when Prism::ConstantPathNode then "#{render_constant_path(node.parent)}::"
|
|
2591
|
-
else ""
|
|
2592
|
-
end
|
|
2593
|
-
"#{prefix}#{node.name}"
|
|
2594
|
-
end
|
|
2595
|
-
|
|
2596
2670
|
# Walks `node`'s subtree DFS and fills in scope entries for every
|
|
2597
2671
|
# Prism node the StatementEvaluator did not visit (i.e. expression-
|
|
2598
2672
|
# interior nodes like the receiver/args of a CallNode). Those
|