rigortype 0.2.7 → 0.2.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
- data/README.md +1 -1
- data/docs/manual/04-diagnostics.md +7 -4
- data/docs/manual/06-baseline.md +5 -2
- data/docs/manual/15-type-protection-coverage.md +6 -4
- data/lib/rigor/analysis/baseline.rb +56 -96
- data/lib/rigor/analysis/buffer_binding.rb +13 -20
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
- data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
- data/lib/rigor/analysis/check_rules.rb +174 -274
- data/lib/rigor/analysis/dependency_recorder.rb +26 -38
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
- data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
- data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
- data/lib/rigor/analysis/diagnostic.rb +48 -75
- data/lib/rigor/analysis/erb_template_detector.rb +13 -18
- data/lib/rigor/analysis/fact_store.rb +12 -22
- data/lib/rigor/analysis/incremental.rb +47 -63
- data/lib/rigor/analysis/incremental_session.rb +74 -100
- data/lib/rigor/analysis/project_scan.rb +12 -23
- data/lib/rigor/analysis/result.rb +3 -5
- data/lib/rigor/analysis/rule_catalog.rb +67 -95
- data/lib/rigor/analysis/run_stats.rb +38 -61
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
- data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
- data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
- data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
- data/lib/rigor/analysis/runner.rb +220 -333
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
- data/lib/rigor/analysis/worker_session.rb +64 -102
- data/lib/rigor/ast/type_node.rb +6 -8
- data/lib/rigor/ast.rb +6 -10
- data/lib/rigor/bleeding_edge.rb +27 -35
- data/lib/rigor/builtins/hkt_builtins.rb +7 -14
- data/lib/rigor/builtins/imported_refinements.rb +106 -149
- data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
- data/lib/rigor/builtins/regex_refinement.rb +32 -43
- data/lib/rigor/builtins/static_return_refinements.rb +57 -81
- data/lib/rigor/cache/descriptor.rb +58 -94
- data/lib/rigor/cache/incremental_snapshot.rb +32 -48
- data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
- data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
- data/lib/rigor/cache/rbs_constant_table.rb +8 -13
- data/lib/rigor/cache/rbs_descriptor.rb +8 -13
- data/lib/rigor/cache/rbs_environment.rb +11 -19
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
- data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
- data/lib/rigor/cache/store.rb +96 -151
- data/lib/rigor/cli/annotate_command.rb +51 -88
- data/lib/rigor/cli/baseline_command.rb +10 -17
- data/lib/rigor/cli/check_command.rb +121 -199
- data/lib/rigor/cli/check_runner_factory.rb +8 -14
- data/lib/rigor/cli/ci_detector.rb +9 -14
- data/lib/rigor/cli/command.rb +8 -14
- data/lib/rigor/cli/coverage_command.rb +19 -26
- data/lib/rigor/cli/coverage_mutation.rb +14 -19
- data/lib/rigor/cli/coverage_report.rb +0 -2
- data/lib/rigor/cli/coverage_scan.rb +7 -11
- data/lib/rigor/cli/diagnostic_formats.rb +43 -62
- data/lib/rigor/cli/diff_command.rb +10 -20
- data/lib/rigor/cli/docs_command.rb +26 -40
- data/lib/rigor/cli/doctor_command.rb +8 -9
- data/lib/rigor/cli/explain_command.rb +4 -7
- data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
- data/lib/rigor/cli/fused_protection_report.rb +6 -8
- data/lib/rigor/cli/lsp_command.rb +11 -19
- data/lib/rigor/cli/mcp_command.rb +4 -6
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
- data/lib/rigor/cli/mutation_protection_report.rb +6 -9
- data/lib/rigor/cli/options.rb +11 -19
- data/lib/rigor/cli/plugin_command.rb +18 -30
- data/lib/rigor/cli/plugins_command.rb +29 -51
- data/lib/rigor/cli/plugins_renderer.rb +12 -20
- data/lib/rigor/cli/prism_colorizer.rb +13 -19
- data/lib/rigor/cli/protection_renderer.rb +5 -7
- data/lib/rigor/cli/protection_report.rb +27 -17
- data/lib/rigor/cli/renderable.rb +4 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
- data/lib/rigor/cli/sig_gen_command.rb +14 -26
- data/lib/rigor/cli/skill_command.rb +30 -47
- data/lib/rigor/cli/skill_describe.rb +40 -64
- data/lib/rigor/cli/trace_command.rb +9 -16
- data/lib/rigor/cli/trace_renderer.rb +35 -51
- data/lib/rigor/cli/triage_command.rb +6 -10
- data/lib/rigor/cli/triage_renderer.rb +1 -2
- data/lib/rigor/cli/type_of_command.rb +14 -23
- data/lib/rigor/cli/type_of_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_command.rb +6 -10
- data/lib/rigor/cli/type_scan_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_report.rb +2 -3
- data/lib/rigor/cli/upgrade_command.rb +2 -3
- data/lib/rigor/cli.rb +16 -28
- data/lib/rigor/config_audit.rb +35 -44
- data/lib/rigor/configuration/dependencies.rb +41 -70
- data/lib/rigor/configuration/severity_profile.rb +28 -42
- data/lib/rigor/configuration.rb +150 -241
- data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
- data/lib/rigor/environment/class_registry.rb +19 -26
- data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
- data/lib/rigor/environment/lockfile_resolver.rb +31 -47
- data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
- data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
- data/lib/rigor/environment/rbs_loader.rb +420 -518
- data/lib/rigor/environment/reflection.rb +28 -53
- data/lib/rigor/environment/reporters.rb +13 -24
- data/lib/rigor/environment.rb +176 -291
- data/lib/rigor/flow_contribution/conflict.rb +16 -27
- data/lib/rigor/flow_contribution/element.rb +7 -12
- data/lib/rigor/flow_contribution/fact.rb +33 -57
- data/lib/rigor/flow_contribution/merge_result.rb +6 -9
- data/lib/rigor/flow_contribution/merger.rb +32 -47
- data/lib/rigor/flow_contribution.rb +37 -55
- data/lib/rigor/inference/acceptance.rb +133 -219
- data/lib/rigor/inference/block_parameter_binder.rb +52 -82
- data/lib/rigor/inference/body_fixpoint.rb +22 -30
- data/lib/rigor/inference/budget_trace.rb +43 -63
- data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
- data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
- data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
- data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
- data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
- data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
- data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
- data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
- data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
- data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
- data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
- data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
- data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
- data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
- data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
- data/lib/rigor/inference/coverage_scanner.rb +13 -18
- data/lib/rigor/inference/def_return_typer.rb +8 -14
- data/lib/rigor/inference/dynamic_origin.rb +23 -20
- data/lib/rigor/inference/expression_typer.rb +774 -1152
- data/lib/rigor/inference/fallback.rb +8 -12
- data/lib/rigor/inference/fallback_tracer.rb +4 -10
- data/lib/rigor/inference/flow_tracer.rb +25 -36
- data/lib/rigor/inference/hkt_body.rb +45 -68
- data/lib/rigor/inference/hkt_body_parser.rb +33 -52
- data/lib/rigor/inference/hkt_reducer.rb +38 -59
- data/lib/rigor/inference/hkt_registry.rb +50 -76
- data/lib/rigor/inference/indexed_narrowing.rb +55 -81
- data/lib/rigor/inference/macro_block_self_type.rb +21 -34
- data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
- data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
- data/lib/rigor/inference/method_dispatcher.rb +378 -524
- data/lib/rigor/inference/method_parameter_binder.rb +81 -114
- data/lib/rigor/inference/multi_target_binder.rb +51 -68
- data/lib/rigor/inference/mutation_widening.rb +115 -158
- data/lib/rigor/inference/narrowing.rb +509 -727
- data/lib/rigor/inference/origin_lookup.rb +38 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
- data/lib/rigor/inference/precision_scanner.rb +26 -35
- data/lib/rigor/inference/project_patched_methods.rb +20 -32
- data/lib/rigor/inference/project_patched_scanner.rb +23 -37
- data/lib/rigor/inference/protection_scanner.rb +16 -18
- data/lib/rigor/inference/rbs_type_translator.rb +49 -66
- data/lib/rigor/inference/scope_indexer.rb +473 -817
- data/lib/rigor/inference/statement_evaluator.rb +706 -1111
- data/lib/rigor/inference/struct_fold_safety.rb +34 -48
- data/lib/rigor/inference/synthetic_method.rb +9 -16
- data/lib/rigor/inference/synthetic_method_index.rb +20 -35
- data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
- data/lib/rigor/language_server/buffer_resolution.rb +6 -9
- data/lib/rigor/language_server/buffer_table.rb +11 -18
- data/lib/rigor/language_server/completion_provider.rb +69 -116
- data/lib/rigor/language_server/debouncer.rb +16 -25
- data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
- data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
- data/lib/rigor/language_server/folding_range_provider.rb +11 -19
- data/lib/rigor/language_server/hover_provider.rb +8 -11
- data/lib/rigor/language_server/hover_renderer.rb +56 -93
- data/lib/rigor/language_server/loop.rb +14 -19
- data/lib/rigor/language_server/project_context.rb +46 -74
- data/lib/rigor/language_server/selection_range_provider.rb +9 -12
- data/lib/rigor/language_server/server.rb +55 -83
- data/lib/rigor/language_server/signature_help_provider.rb +29 -46
- data/lib/rigor/language_server/synchronized_writer.rb +4 -7
- data/lib/rigor/language_server/uri.rb +8 -13
- data/lib/rigor/language_server.rb +4 -6
- data/lib/rigor/mcp/loop.rb +2 -3
- data/lib/rigor/mcp/server.rb +4 -7
- data/lib/rigor/mcp.rb +3 -6
- data/lib/rigor/plugin/access_denied_error.rb +5 -8
- data/lib/rigor/plugin/additional_initializer.rb +21 -31
- data/lib/rigor/plugin/base.rb +335 -518
- data/lib/rigor/plugin/blueprint.rb +14 -23
- data/lib/rigor/plugin/box.rb +18 -29
- data/lib/rigor/plugin/fact_store.rb +16 -26
- data/lib/rigor/plugin/inflector.rb +37 -53
- data/lib/rigor/plugin/io_boundary.rb +33 -56
- data/lib/rigor/plugin/isolation.rb +42 -55
- data/lib/rigor/plugin/load_error.rb +10 -15
- data/lib/rigor/plugin/loader.rb +30 -49
- data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
- data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
- data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
- data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
- data/lib/rigor/plugin/macro.rb +10 -15
- data/lib/rigor/plugin/manifest.rb +85 -144
- data/lib/rigor/plugin/node_context.rb +14 -22
- data/lib/rigor/plugin/node_rule_walk.rb +49 -74
- data/lib/rigor/plugin/protocol_contract.rb +25 -39
- data/lib/rigor/plugin/registry.rb +132 -205
- data/lib/rigor/plugin/services.rb +21 -33
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
- data/lib/rigor/plugin/trust_policy.rb +24 -38
- data/lib/rigor/plugin/type_node_resolver.rb +15 -24
- data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
- data/lib/rigor/protection/mutation_scanner.rb +27 -35
- data/lib/rigor/protection/mutator.rb +50 -70
- data/lib/rigor/protection/test_suite_oracle.rb +20 -27
- data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
- data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
- data/lib/rigor/rbs_extended/reporter.rb +24 -40
- data/lib/rigor/rbs_extended.rb +107 -197
- data/lib/rigor/reflection.rb +68 -86
- data/lib/rigor/scope/discovery_index.rb +14 -19
- data/lib/rigor/scope.rb +255 -310
- data/lib/rigor/sig_gen/classification.rb +6 -10
- data/lib/rigor/sig_gen/generator.rb +197 -323
- data/lib/rigor/sig_gen/layout_index.rb +12 -20
- data/lib/rigor/sig_gen/method_candidate.rb +12 -17
- data/lib/rigor/sig_gen/observation_collector.rb +38 -70
- data/lib/rigor/sig_gen/observed_call.rb +13 -23
- data/lib/rigor/sig_gen/path_mapper.rb +17 -29
- data/lib/rigor/sig_gen/renderer.rb +7 -13
- data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
- data/lib/rigor/sig_gen/write_result.rb +8 -16
- data/lib/rigor/sig_gen/writer.rb +95 -174
- data/lib/rigor/sig_gen.rb +3 -6
- data/lib/rigor/signature_path_audit.rb +24 -30
- data/lib/rigor/source/constant_path.rb +10 -14
- data/lib/rigor/source/literals.rb +31 -45
- data/lib/rigor/source/node_locator.rb +9 -11
- data/lib/rigor/source/node_walker.rb +9 -13
- data/lib/rigor/source.rb +3 -4
- data/lib/rigor/testing.rb +16 -20
- data/lib/rigor/triage/catalogue.rb +38 -62
- data/lib/rigor/triage.rb +31 -52
- data/lib/rigor/trinary.rb +9 -13
- data/lib/rigor/type/acceptance_router.rb +4 -6
- data/lib/rigor/type/accepts_result.rb +10 -14
- data/lib/rigor/type/app.rb +19 -27
- data/lib/rigor/type/bot.rb +4 -6
- data/lib/rigor/type/bound_method.rb +10 -15
- data/lib/rigor/type/combinator.rb +165 -257
- data/lib/rigor/type/constant.rb +23 -34
- data/lib/rigor/type/data_class.rb +10 -15
- data/lib/rigor/type/data_instance.rb +14 -20
- data/lib/rigor/type/difference.rb +21 -32
- data/lib/rigor/type/dynamic.rb +3 -5
- data/lib/rigor/type/hash_shape.rb +32 -18
- data/lib/rigor/type/integer_range.rb +11 -16
- data/lib/rigor/type/intersection.rb +27 -42
- data/lib/rigor/type/nominal.rb +10 -15
- data/lib/rigor/type/plain_lattice.rb +9 -13
- data/lib/rigor/type/refined.rb +67 -114
- data/lib/rigor/type/singleton.rb +4 -6
- data/lib/rigor/type/struct_class.rb +11 -16
- data/lib/rigor/type/struct_instance.rb +15 -21
- data/lib/rigor/type/tuple.rb +14 -19
- data/lib/rigor/type/union.rb +30 -42
- data/lib/rigor/type_node/generic.rb +14 -26
- data/lib/rigor/type_node/identifier.rb +12 -19
- data/lib/rigor/type_node.rb +3 -12
- data/lib/rigor/value_semantics.rb +16 -21
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
- data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
- data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
- data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
- data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
- data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
- data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
- data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
- data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
- data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
- data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
- data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
- data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
- data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
- data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
- data/sig/rigor/scope.rbs +6 -0
- metadata +2 -1
|
@@ -2,34 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Analysis
|
|
5
|
-
# ADR-46 slice 2 — the pure set-algebra core of the incremental step,
|
|
6
|
-
#
|
|
7
|
-
# property it encodes is unit-testable without the analysis machinery.
|
|
5
|
+
# ADR-46 slice 2 — the pure set-algebra core of the incremental step, kept side-effect-free and
|
|
6
|
+
# Runner-independent so the soundness property it encodes is unit-testable without the analysis machinery.
|
|
8
7
|
#
|
|
9
|
-
# Given the files that changed since the baseline run and the
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# file. Every other file is served from the per-file diagnostic cache.
|
|
8
|
+
# Given the files that changed since the baseline run and the baseline's `dependents` index
|
|
9
|
+
# ({Runner#file_dependents}), the **affected closure** the body tier must re-analyse is the changed set
|
|
10
|
+
# plus every file that read a declaration or method body from a changed file. Every other file is served
|
|
11
|
+
# from the per-file diagnostic cache.
|
|
14
12
|
#
|
|
15
|
-
# The soundness invariant (the {Runner}-driven `--verify-incremental`
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# prevent. Structural edits (fingerprint changed) are out of this
|
|
23
|
-
# tier's scope — they widen via the negative-dependency / full fallback
|
|
24
|
-
# path (slice 3).
|
|
13
|
+
# The soundness invariant (the {Runner}-driven `--verify-incremental` gate and the spec assert it): for an
|
|
14
|
+
# edit whose declaration-structure fingerprint is unchanged (a method-body edit — no symbol created,
|
|
15
|
+
# destroyed, moved, or re-parented), the set of files whose diagnostics actually change is a SUBSET of
|
|
16
|
+
# {affected}. A file outside the closure whose diagnostics changed would be served stale — a manufactured
|
|
17
|
+
# false positive/negative, the failure mode this design exists to prevent. Structural edits (fingerprint
|
|
18
|
+
# changed) are out of this tier's scope — they widen via the negative-dependency / full fallback path
|
|
19
|
+
# (slice 3).
|
|
25
20
|
module Incremental
|
|
26
21
|
module_function
|
|
27
22
|
|
|
28
|
-
# Inverts a per-consumer source map (`consumer → enumerable of source
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
# nil (the default proc is dropped before freezing).
|
|
23
|
+
# Inverts a per-consumer source map (`consumer → enumerable of source files it read from`) into the
|
|
24
|
+
# `dependents` index (`source → Set of consumers that read from it`). The reverse edge the incremental
|
|
25
|
+
# step walks. Returns a frozen hash of frozen Sets; a missing key reads as nil (the default proc is
|
|
26
|
+
# dropped before freezing).
|
|
33
27
|
def invert(sources_by_consumer)
|
|
34
28
|
index = Hash.new { |hash, key| hash[key] = Set.new }
|
|
35
29
|
sources_by_consumer.each do |consumer, sources|
|
|
@@ -40,20 +34,18 @@ module Rigor
|
|
|
40
34
|
index.freeze
|
|
41
35
|
end
|
|
42
36
|
|
|
43
|
-
# The closure the body tier re-analyses. `changed` is any Enumerable
|
|
44
|
-
#
|
|
45
|
-
# read from it (missing key → no dependents). Returns a frozen Set.
|
|
37
|
+
# The closure the body tier re-analyses. `changed` is any Enumerable of paths; `dependents` maps a
|
|
38
|
+
# source path to the Set of files that read from it (missing key → no dependents). Returns a frozen Set.
|
|
46
39
|
def affected(changed, dependents)
|
|
47
40
|
closure = changed.to_set
|
|
48
41
|
changed.each { |file| closure.merge(dependents[file] || []) }
|
|
49
42
|
closure.freeze
|
|
50
43
|
end
|
|
51
44
|
|
|
52
|
-
# ADR-46 slice 4 — inverts a per-consumer symbol-sources map
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
# symbols that actually changed rather than all callers of the file.
|
|
45
|
+
# ADR-46 slice 4 — inverts a per-consumer symbol-sources map (`consumer → { source_path →
|
|
46
|
+
# Set<"ClassName#method"> }`) into the symbol-level dependents index: `[source_path, symbol] →
|
|
47
|
+
# Set<consumer>`. Used by {affected_with_symbols} to limit fan-out to callers of symbols that actually
|
|
48
|
+
# changed rather than all callers of the file.
|
|
57
49
|
def invert_symbols(symbol_sources_by_consumer)
|
|
58
50
|
index = Hash.new { |h, k| h[k] = Set.new }
|
|
59
51
|
symbol_sources_by_consumer.each do |consumer, sources_by_file|
|
|
@@ -66,10 +58,9 @@ module Rigor
|
|
|
66
58
|
index.freeze
|
|
67
59
|
end
|
|
68
60
|
|
|
69
|
-
# ADR-46 slice 4 — given a set of changed file paths and two per-file
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
# body-changed).
|
|
61
|
+
# ADR-46 slice 4 — given a set of changed file paths and two per-file symbol fingerprint maps (before
|
|
62
|
+
# and after), returns the frozen Set of `[path, symbol]` pairs whose fingerprints differ (added,
|
|
63
|
+
# removed, or body-changed).
|
|
73
64
|
def changed_symbol_pairs(changed_files, fingerprints_before, fingerprints_after)
|
|
74
65
|
pairs = Set.new
|
|
75
66
|
changed_files.each do |path|
|
|
@@ -89,8 +80,8 @@ module Rigor
|
|
|
89
80
|
# (b) it has an ancestry dep on a changed file (always re-checked — file-level), or
|
|
90
81
|
# (c) it has a symbol dep on a `[file, symbol]` pair that changed.
|
|
91
82
|
#
|
|
92
|
-
# Consumers that only have symbol deps on a changed file, and none of
|
|
93
|
-
#
|
|
83
|
+
# Consumers that only have symbol deps on a changed file, and none of their tracked symbols changed,
|
|
84
|
+
# are NOT included — the slice 4 precision win.
|
|
94
85
|
def affected_with_symbols(changed_files, changed_pairs, symbol_dependents, ancestry_dependents)
|
|
95
86
|
closure = changed_files.to_set
|
|
96
87
|
changed_files.each { |file| closure.merge(ancestry_dependents[file] || []) }
|
|
@@ -98,13 +89,11 @@ module Rigor
|
|
|
98
89
|
closure.freeze
|
|
99
90
|
end
|
|
100
91
|
|
|
101
|
-
# ADR-46 slice 3 — the symbol keys (`"ClassName#method"`) that are
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
# missed a name that already resolved elsewhere), so the over-report
|
|
107
|
-
# costs nothing. Returns a frozen Set of symbol-key Strings.
|
|
92
|
+
# ADR-46 slice 3 — the symbol keys (`"ClassName#method"`) that are present in a changed file's
|
|
93
|
+
# after-fingerprints but were absent from its before-fingerprints: a symbol that *appeared* in this
|
|
94
|
+
# edit. A symbol that merely moved between files still appears here for the destination file, but its
|
|
95
|
+
# negative-dependents set is empty (nobody missed a name that already resolved elsewhere), so the
|
|
96
|
+
# over-report costs nothing. Returns a frozen Set of symbol-key Strings.
|
|
108
97
|
def appeared_symbols(changed_files, fingerprints_before, fingerprints_after)
|
|
109
98
|
appeared = Set.new
|
|
110
99
|
changed_files.each do |path|
|
|
@@ -115,14 +104,12 @@ module Rigor
|
|
|
115
104
|
appeared.freeze
|
|
116
105
|
end
|
|
117
106
|
|
|
118
|
-
# ADR-46 slice 3 — the qualified class/module names *declared* in a
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
# class-name Strings. `decls_before` / `decls_after` map a path to its
|
|
125
|
-
# Set of declared class names.
|
|
107
|
+
# ADR-46 slice 3 — the qualified class/module names *declared* in a changed file's after-state that
|
|
108
|
+
# were absent from its before-state: a class that appeared in this edit. (For an added file the
|
|
109
|
+
# before-set is empty, so every class it declares appears.) A class that merely moved files still
|
|
110
|
+
# appears here, but its negative-dependents are empty, so the over-report costs nothing. Returns a
|
|
111
|
+
# frozen Set of qualified class-name Strings. `decls_before` / `decls_after` map a path to its Set of
|
|
112
|
+
# declared class names.
|
|
126
113
|
def appeared_classes(changed_files, decls_before, decls_after)
|
|
127
114
|
appeared = Set.new
|
|
128
115
|
changed_files.each do |path|
|
|
@@ -133,23 +120,20 @@ module Rigor
|
|
|
133
120
|
appeared.freeze
|
|
134
121
|
end
|
|
135
122
|
|
|
136
|
-
# ADR-46 slice 3 — the consumers to re-check because a name they
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
# maps each key to the Set of consumers that recorded the miss. Returns
|
|
141
|
-
# a frozen Set of consumer paths.
|
|
123
|
+
# ADR-46 slice 3 — the consumers to re-check because a name they looked up and *missed* (a negative
|
|
124
|
+
# dependency) now resolves. `keys` is the set of negative-dependency keys (`"toplevel:foo"` /
|
|
125
|
+
# `"method:C#m"`) the appeared symbols would satisfy; `negative_dependents` maps each key to the Set of
|
|
126
|
+
# consumers that recorded the miss. Returns a frozen Set of consumer paths.
|
|
142
127
|
def negative_closure(keys, negative_dependents)
|
|
143
128
|
closure = Set.new
|
|
144
129
|
keys.each { |key| closure.merge(negative_dependents[key] || []) }
|
|
145
130
|
closure.freeze
|
|
146
131
|
end
|
|
147
132
|
|
|
148
|
-
# The files whose per-file diagnostics differ between two runs.
|
|
149
|
-
#
|
|
150
|
-
#
|
|
151
|
-
#
|
|
152
|
-
# run and absent (zero diagnostics) in the other counts as changed.
|
|
133
|
+
# The files whose per-file diagnostics differ between two runs. Each argument maps a path to its
|
|
134
|
+
# diagnostic list; diagnostics are compared structurally via {Diagnostic#to_h} so identity / ordering
|
|
135
|
+
# of the objects themselves does not matter. A file present in one run and absent (zero diagnostics) in
|
|
136
|
+
# the other counts as changed.
|
|
153
137
|
def changed_files(before_by_file, after_by_file)
|
|
154
138
|
(before_by_file.keys | after_by_file.keys).each_with_object(Set.new) do |path, changed|
|
|
155
139
|
before = (before_by_file[path] || []).map(&:to_h)
|
|
@@ -7,38 +7,30 @@ require_relative "../inference/scope_indexer"
|
|
|
7
7
|
|
|
8
8
|
module Rigor
|
|
9
9
|
module Analysis
|
|
10
|
-
# ADR-46 slice 2 — the in-memory incremental orchestrator that composes
|
|
11
|
-
# the
|
|
12
|
-
# closure ({Incremental.affected}), and the subset-analysis hook
|
|
10
|
+
# ADR-46 slice 2 — the in-memory incremental orchestrator that composes the recorded dependency graph
|
|
11
|
+
# ({Runner#file_dependents}), the affected closure ({Incremental.affected}), and the subset-analysis hook
|
|
13
12
|
# ({Runner} `analyze_only:`) into a working incremental re-check.
|
|
14
13
|
#
|
|
15
|
-
# `#baseline` runs a full analysis with dependency recording and keeps,
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# ΔF, re-analyzes only `ΔF ∪ dependents[ΔF]`, and serves every other
|
|
20
|
-
# analyzed file from the cache — the body tier.
|
|
14
|
+
# `#baseline` runs a full analysis with dependency recording and keeps, per analyzed file, its
|
|
15
|
+
# diagnostics (the cache), its content digest, and the per-file source set (to maintain the dependents
|
|
16
|
+
# index across rounds). `#recheck` digests the files again, computes the changed set ΔF, re-analyzes
|
|
17
|
+
# only `ΔF ∪ dependents[ΔF]`, and serves every other analyzed file from the cache — the body tier.
|
|
21
18
|
#
|
|
22
|
-
# The invariant the verify harness (and the spec) assert: `#recheck`'s
|
|
23
|
-
#
|
|
24
|
-
# `--
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# an edit that adds / removes / moves a *file* is outside the analyzed
|
|
28
|
-
# set it maintains and falls to a fresh {#baseline} (the structural tier
|
|
29
|
-
# is a later slice).
|
|
19
|
+
# The invariant the verify harness (and the spec) assert: `#recheck`'s merged diagnostics are
|
|
20
|
+
# byte-identical (as a sorted set) to a full `--no-cache` re-analysis of the edited tree. This is the
|
|
21
|
+
# `--verify-incremental` acceptance gate, here without disk persistence or CLI wiring (the cache is
|
|
22
|
+
# in-process). It models the body tier only: an edit that adds / removes / moves a *file* is outside
|
|
23
|
+
# the analyzed set it maintains and falls to a fresh {#baseline} (the structural tier is a later slice).
|
|
30
24
|
class IncrementalSession
|
|
31
|
-
# The outcome of a {#recheck}: the merged diagnostics plus the file
|
|
32
|
-
#
|
|
33
|
-
# re-analyzed versus served from cache.
|
|
25
|
+
# The outcome of a {#recheck}: the merged diagnostics plus the file sets, so a caller (or the verify
|
|
26
|
+
# gate) can report what was re-analyzed versus served from cache.
|
|
34
27
|
Recheck = Data.define(:diagnostics, :changed, :affected, :reused)
|
|
35
28
|
|
|
36
|
-
# @param paths [Array<String>, nil] explicit analysis roots; nil
|
|
37
|
-
#
|
|
38
|
-
# @param environment [Rigor::Environment, nil] optional shared
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
# RBS universe for every baseline / recheck / oracle run.
|
|
29
|
+
# @param paths [Array<String>, nil] explicit analysis roots; nil (the default) uses the configuration's
|
|
30
|
+
# `paths:`.
|
|
31
|
+
# @param environment [Rigor::Environment, nil] optional shared environment to thread into each internal
|
|
32
|
+
# Runner. Long-lived callers and specs can use this to avoid rebuilding the same RBS universe for
|
|
33
|
+
# every baseline / recheck / oracle run.
|
|
42
34
|
def initialize(configuration:, paths: nil, environment: nil)
|
|
43
35
|
@configuration = configuration
|
|
44
36
|
@paths = paths
|
|
@@ -63,15 +55,14 @@ module Rigor
|
|
|
63
55
|
@class_decls = {} # path => Set<qualified class name declared in the file>
|
|
64
56
|
end
|
|
65
57
|
|
|
66
|
-
# The project files analyzed at the last baseline / recheck — the set
|
|
67
|
-
#
|
|
68
|
-
# closure from.
|
|
58
|
+
# The project files analyzed at the last baseline / recheck — the set a verify pass partitions and the
|
|
59
|
+
# merge subtracts the affected closure from.
|
|
69
60
|
def analyzed_files
|
|
70
61
|
@analyzed
|
|
71
62
|
end
|
|
72
63
|
|
|
73
|
-
# Full baseline analysis with recording. Returns the run's
|
|
74
|
-
#
|
|
64
|
+
# Full baseline analysis with recording. Returns the run's diagnostics; populates the in-process cache
|
|
65
|
+
# + dependency state.
|
|
75
66
|
def baseline
|
|
76
67
|
runner = build_runner(record_dependencies: true)
|
|
77
68
|
diagnostics = run_runner(runner).diagnostics
|
|
@@ -82,9 +73,8 @@ module Rigor
|
|
|
82
73
|
diagnostics
|
|
83
74
|
end
|
|
84
75
|
|
|
85
|
-
# Re-check after on-disk edits, including files added or removed since
|
|
86
|
-
#
|
|
87
|
-
# closure and serves the rest from cache; refreshes the cache +
|
|
76
|
+
# Re-check after on-disk edits, including files added or removed since the last run (the structural
|
|
77
|
+
# tier). Re-analyzes only the affected closure and serves the rest from cache; refreshes the cache +
|
|
88
78
|
# dependency state so a subsequent #recheck sees the new world.
|
|
89
79
|
def recheck
|
|
90
80
|
previous = @analyzed
|
|
@@ -102,13 +92,11 @@ module Rigor
|
|
|
102
92
|
Recheck.new(diagnostics: merged, changed: changed.to_set, affected: affected, reused: reused.to_set)
|
|
103
93
|
end
|
|
104
94
|
|
|
105
|
-
# The frozen set of files a #recheck must re-analyse: the
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
# the consumers of every removed file (which now miss what it provided).
|
|
111
|
-
# An added file has no before-state, so all its symbols / classes appear.
|
|
95
|
+
# The frozen set of files a #recheck must re-analyse: the symbol/ancestry-granularity closure of the
|
|
96
|
+
# changed files (slice 4), the added files themselves, the consumers of any symbol / class that
|
|
97
|
+
# *appeared* in a changed OR added file (slice 3 — a now-defined `call.unresolved-toplevel` target or
|
|
98
|
+
# `def.override-*` ancestor), and the consumers of every removed file (which now miss what it
|
|
99
|
+
# provided). An added file has no before-state, so all its symbols / classes appear.
|
|
112
100
|
def affected_closure(changed, added, removed)
|
|
113
101
|
scan = changed + added
|
|
114
102
|
new_fps = symbol_fingerprints_for(scan)
|
|
@@ -124,21 +112,18 @@ module Rigor
|
|
|
124
112
|
closure.freeze
|
|
125
113
|
end
|
|
126
114
|
|
|
127
|
-
# The current project file set (cheap directory expansion, no analysis),
|
|
128
|
-
#
|
|
115
|
+
# The current project file set (cheap directory expansion, no analysis), used to detect files added /
|
|
116
|
+
# removed since the last run.
|
|
129
117
|
def current_files
|
|
130
118
|
runner = build_runner
|
|
131
119
|
@paths ? runner.analysis_file_set(@paths) : runner.analysis_file_set
|
|
132
120
|
end
|
|
133
121
|
|
|
134
|
-
# Verification engine (the `--verify-incremental` gate): with NO
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
# known-good oracle (a full `--no-cache` run) for an arbitrary
|
|
140
|
-
# partition, without mutating session state. Returns the merged
|
|
141
|
-
# diagnostics.
|
|
122
|
+
# Verification engine (the `--verify-incremental` gate): with NO source edit, re-analyze `subset` fresh
|
|
123
|
+
# and serve every other analyzed file from the baseline cache. Because nothing on disk changed, the
|
|
124
|
+
# merged result MUST equal a full analysis — so this exercises the subset-analysis and cache-merge
|
|
125
|
+
# paths against a known-good oracle (a full `--no-cache` run) for an arbitrary partition, without
|
|
126
|
+
# mutating session state. Returns the merged diagnostics.
|
|
142
127
|
def reanalyze_subset(subset)
|
|
143
128
|
affected = subset.to_set
|
|
144
129
|
runner = build_runner(analyze_only: affected)
|
|
@@ -147,14 +132,12 @@ module Rigor
|
|
|
147
132
|
fresh + reused.flat_map { |path| @cache[path] || [] }
|
|
148
133
|
end
|
|
149
134
|
|
|
150
|
-
# Cross-process incremental run (the `--incremental` flag's engine).
|
|
151
|
-
#
|
|
152
|
-
#
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
#
|
|
156
|
-
# snapshot was restored. A nil `fingerprint` (uncomputable inputs)
|
|
157
|
-
# disables persistence: a plain full run.
|
|
135
|
+
# Cross-process incremental run (the `--incremental` flag's engine). With a disk `snapshot` whose
|
|
136
|
+
# `fingerprint` matches, restore the prior per-file state and `#recheck` (re-analyze only the changed
|
|
137
|
+
# closure, serve the rest from the restored cache); otherwise run a full `#baseline`. Either way,
|
|
138
|
+
# persist the updated snapshot for the next process. Returns `[diagnostics, warm]` — `warm` is true
|
|
139
|
+
# when a snapshot was restored. A nil `fingerprint` (uncomputable inputs) disables persistence: a
|
|
140
|
+
# plain full run.
|
|
158
141
|
def run_incremental(snapshot:, fingerprint:)
|
|
159
142
|
restored = fingerprint && snapshot.load(fingerprint: fingerprint)
|
|
160
143
|
if restored
|
|
@@ -171,26 +154,22 @@ module Rigor
|
|
|
171
154
|
|
|
172
155
|
private
|
|
173
156
|
|
|
174
|
-
# Adopt a persisted snapshot's per-file state as this session's
|
|
175
|
-
# baseline (the warm-start path).
|
|
157
|
+
# Adopt a persisted snapshot's per-file state as this session's baseline (the warm-start path).
|
|
176
158
|
def restore(payload)
|
|
177
159
|
@analyzed = payload.analyzed
|
|
178
160
|
@cache = payload.cache
|
|
179
161
|
@sources = payload.sources
|
|
180
162
|
@digests = payload.digests
|
|
181
163
|
@dependents = Incremental.invert(@sources)
|
|
182
|
-
# ADR-46 slice 4 — restore symbol-granularity state if present in the
|
|
183
|
-
#
|
|
184
|
-
# file-level dependents, which is always sound).
|
|
164
|
+
# ADR-46 slice 4 — restore symbol-granularity state if present in the payload (absent in snapshots
|
|
165
|
+
# written before slice 4 → fall back to file-level dependents, which is always sound).
|
|
185
166
|
@symbol_sources = payload.symbol_sources || {}
|
|
186
167
|
@ancestry_sources = payload.ancestry_sources || {}
|
|
187
168
|
@symbol_fingerprints = payload.symbol_fingerprints || {}
|
|
188
|
-
# ADR-46 slice 3 — restore negative edges if present (absent in
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
#
|
|
192
|
-
# fingerprint, and `--verify-incremental` backstops any residual
|
|
193
|
-
# under-capture, so it is never unsound).
|
|
169
|
+
# ADR-46 slice 3 — restore negative edges if present (absent in pre-slice-3 snapshots → empty, which
|
|
170
|
+
# only loses the appeared-symbol re-check refinement; such a snapshot is never loaded by a slice-3+
|
|
171
|
+
# engine because the engine version + schema is part of the snapshot fingerprint, and
|
|
172
|
+
# `--verify-incremental` backstops any residual under-capture, so it is never unsound).
|
|
194
173
|
@missing = payload.missing || {}
|
|
195
174
|
@class_decls = payload.class_decls || {}
|
|
196
175
|
@symbol_dependents = Incremental.invert_symbols(@symbol_sources)
|
|
@@ -207,10 +186,9 @@ module Rigor
|
|
|
207
186
|
)
|
|
208
187
|
end
|
|
209
188
|
|
|
210
|
-
# Fold a #recheck's fresh results back into the cache + graph so the
|
|
211
|
-
#
|
|
212
|
-
#
|
|
213
|
-
# from every map, and the analyzed-file list advances to `current`.
|
|
189
|
+
# Fold a #recheck's fresh results back into the cache + graph so the session is correct across
|
|
190
|
+
# multiple edits: the analyzed set gets fresh diagnostics + digests + dependency edges, removed files
|
|
191
|
+
# are evicted from every map, and the analyzed-file list advances to `current`.
|
|
214
192
|
def absorb(runner, fresh, current, analyze_set, removed)
|
|
215
193
|
removed.each { |path| forget(path) }
|
|
216
194
|
@analyzed = current
|
|
@@ -222,8 +200,8 @@ module Rigor
|
|
|
222
200
|
absorb_dependency_graph(runner)
|
|
223
201
|
end
|
|
224
202
|
|
|
225
|
-
# Evict a removed file from every per-file map so its stale diagnostics
|
|
226
|
-
#
|
|
203
|
+
# Evict a removed file from every per-file map so its stale diagnostics are never served and it drops
|
|
204
|
+
# out of the inverted dependency indexes.
|
|
227
205
|
def forget(path)
|
|
228
206
|
@cache.delete(path)
|
|
229
207
|
@digests.delete(path)
|
|
@@ -236,8 +214,8 @@ module Rigor
|
|
|
236
214
|
# pre-pass in absorb_dependency_graph, and is frozen, so no delete.
|
|
237
215
|
end
|
|
238
216
|
|
|
239
|
-
# Fold a runner's dependency recording (file-level and symbol-level) back
|
|
240
|
-
#
|
|
217
|
+
# Fold a runner's dependency recording (file-level and symbol-level) back into the session's graph
|
|
218
|
+
# state. Rebuilds all derived indexes.
|
|
241
219
|
def absorb_dependency_graph(runner)
|
|
242
220
|
runner.file_dependencies.each do |path, record|
|
|
243
221
|
@sources[path] = record.sources.dup
|
|
@@ -250,17 +228,15 @@ module Rigor
|
|
|
250
228
|
@ancestry_dependents = Incremental.invert(@ancestry_sources)
|
|
251
229
|
@negative_dependents = Incremental.invert(@missing)
|
|
252
230
|
@symbol_fingerprints.merge!(runner.symbol_fingerprints)
|
|
253
|
-
# Wholesale replace (the subset runner's pre-pass is complete): a file
|
|
254
|
-
#
|
|
255
|
-
# registers as an appearance.
|
|
231
|
+
# Wholesale replace (the subset runner's pre-pass is complete): a file that lost its last class must
|
|
232
|
+
# drop out of the map so a later re-add registers as an appearance.
|
|
256
233
|
@class_decls = runner.class_declarations
|
|
257
234
|
end
|
|
258
235
|
|
|
259
|
-
# Compute per-symbol body fingerprints for `paths` via a quick indexing
|
|
260
|
-
#
|
|
261
|
-
#
|
|
262
|
-
#
|
|
263
|
-
# changed, so only their callers are added to the affected closure.
|
|
236
|
+
# Compute per-symbol body fingerprints for `paths` via a quick indexing re-pass (Prism parse + def
|
|
237
|
+
# extraction, no type inference). Returns a hash of the form `{ path => { "ClassName#method" =>
|
|
238
|
+
# sha256_hex } }`. Used by {#recheck} to detect which symbols in a changed file actually changed, so
|
|
239
|
+
# only their callers are added to the affected closure.
|
|
264
240
|
def symbol_fingerprints_for(paths)
|
|
265
241
|
return {} if paths.empty?
|
|
266
242
|
|
|
@@ -281,11 +257,10 @@ module Rigor
|
|
|
281
257
|
result.transform_values(&:freeze).freeze
|
|
282
258
|
end
|
|
283
259
|
|
|
284
|
-
# ADR-46 slice 3 — the consumers to re-check because a symbol that
|
|
285
|
-
#
|
|
286
|
-
#
|
|
287
|
-
#
|
|
288
|
-
# then unions the recorded negative-dependents of those keys.
|
|
260
|
+
# ADR-46 slice 3 — the consumers to re-check because a symbol that appeared in a changed file resolves
|
|
261
|
+
# a prior missed lookup. Maps each appeared `"ClassName#method"` to the negative-dependency key it
|
|
262
|
+
# would satisfy (`toplevel:foo` for a top-level def, `method:C#m` otherwise), then unions the recorded
|
|
263
|
+
# negative-dependents of those keys.
|
|
289
264
|
def negative_affected(changed, new_fingerprints, new_class_decls)
|
|
290
265
|
appeared_methods = Incremental.appeared_symbols(changed, @symbol_fingerprints, new_fingerprints)
|
|
291
266
|
appeared_classes = Incremental.appeared_classes(changed, @class_decls, new_class_decls)
|
|
@@ -294,9 +269,9 @@ module Rigor
|
|
|
294
269
|
Incremental.negative_closure(keys, @negative_dependents)
|
|
295
270
|
end
|
|
296
271
|
|
|
297
|
-
# The qualified class/module names declared in `paths`, via the same
|
|
298
|
-
#
|
|
299
|
-
#
|
|
272
|
+
# The qualified class/module names declared in `paths`, via the same quick indexing re-pass
|
|
273
|
+
# {#symbol_fingerprints_for} uses (Prism parse + declaration extraction, no inference). `{ path =>
|
|
274
|
+
# Set<class name> }`.
|
|
300
275
|
def class_declarations_for(paths)
|
|
301
276
|
return {} if paths.empty?
|
|
302
277
|
|
|
@@ -320,16 +295,15 @@ module Rigor
|
|
|
320
295
|
Runner.new(configuration: @configuration, cache_store: nil, environment: @environment, **)
|
|
321
296
|
end
|
|
322
297
|
|
|
323
|
-
# Run the runner over the session's explicit paths (or, when none were
|
|
324
|
-
#
|
|
298
|
+
# Run the runner over the session's explicit paths (or, when none were given, the configuration's
|
|
299
|
+
# `paths:` via `Runner#run`'s default).
|
|
325
300
|
def run_runner(runner)
|
|
326
301
|
@paths ? runner.run(@paths) : runner.run
|
|
327
302
|
end
|
|
328
303
|
|
|
329
|
-
# Group diagnostics by their file path, keeping only those whose path
|
|
330
|
-
#
|
|
331
|
-
#
|
|
332
|
-
# and must not be served from the per-file cache.
|
|
304
|
+
# Group diagnostics by their file path, keeping only those whose path is an analyzed project file —
|
|
305
|
+
# run-level streams (the gem-RBS info diagnostic, keyed on `.rigor.yml`) are recomputed fresh every
|
|
306
|
+
# run and must not be served from the per-file cache.
|
|
333
307
|
def per_file(diagnostics)
|
|
334
308
|
diagnostics.group_by(&:path).slice(*@analyzed)
|
|
335
309
|
end
|
|
@@ -2,31 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Analysis
|
|
5
|
-
# Frozen snapshot of the project-wide state {Runner} consumes
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# index, the synthetic-method and project-patched indexes
|
|
9
|
-
# produced by the pre-pass scanners, and the diagnostics
|
|
10
|
-
# those passes emitted.
|
|
5
|
+
# Frozen snapshot of the project-wide state {Runner} consumes before per-file analysis fires: the loaded
|
|
6
|
+
# plugin registry (with `#prepare` already invoked), the dependency-source index, the synthetic-method and
|
|
7
|
+
# project-patched indexes produced by the pre-pass scanners, and the diagnostics those passes emitted.
|
|
11
8
|
#
|
|
12
|
-
# Owners (`Rigor::LanguageServer::ProjectContext`, future
|
|
13
|
-
#
|
|
14
|
-
# per
|
|
15
|
-
#
|
|
16
|
-
# `Runner.new(prebuilt: ...)` so per-buffer publishes skip
|
|
17
|
-
# the scanner walks and `#prepare` re-runs. When watched
|
|
18
|
-
# project files change, the owner discards the ProjectScan
|
|
19
|
-
# and a fresh one builds on next read.
|
|
9
|
+
# Owners (`Rigor::LanguageServer::ProjectContext`, future editor / sig-gen integrations) build a ProjectScan
|
|
10
|
+
# once per project-state generation via `Runner#prepare_project_scan` and pass it to
|
|
11
|
+
# `Runner.new(prebuilt: ...)` so per-buffer publishes skip the scanner walks and `#prepare` re-runs. When
|
|
12
|
+
# watched project files change, the owner discards the ProjectScan and a fresh one builds on next read.
|
|
20
13
|
#
|
|
21
|
-
# Editor mode v1 contract reminder: scanners observe the
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
# `workspace/didChangeWatchedFiles`. This is the same
|
|
27
|
-
# trade-off the LSP made when slice 7 cached only the
|
|
28
|
-
# `Environment`; extending the cache to the pre-pass
|
|
29
|
-
# outputs preserves the contract.
|
|
14
|
+
# Editor mode v1 contract reminder: scanners observe the bytes that were on disk at scan time, NOT the
|
|
15
|
+
# in-flight buffer. Edits to a file that itself declares synthetic methods (or `pre_eval:`-listed patches)
|
|
16
|
+
# are NOT visible until the owner invalidates the scan — typically via `workspace/didChangeWatchedFiles`.
|
|
17
|
+
# This is the same trade-off the LSP made when slice 7 cached only the `Environment`; extending the cache
|
|
18
|
+
# to the pre-pass outputs preserves the contract.
|
|
30
19
|
ProjectScan = Data.define(
|
|
31
20
|
:plugin_registry,
|
|
32
21
|
:dependency_source_index,
|
|
@@ -5,11 +5,9 @@ module Rigor
|
|
|
5
5
|
class Result
|
|
6
6
|
attr_reader :diagnostics, :stats
|
|
7
7
|
|
|
8
|
-
# @param stats [Rigor::Analysis::RunStats, nil] end-of-run
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# collection wasn't requested or wasn't applicable
|
|
12
|
-
# (early-exit paths like `validate_target_ruby` failure).
|
|
8
|
+
# @param stats [Rigor::Analysis::RunStats, nil] end-of-run telemetry (target file count, RBS class
|
|
9
|
+
# breakdown, wall + RSS) collected by the Runner. Nil when stats collection wasn't requested or wasn't
|
|
10
|
+
# applicable (early-exit paths like `validate_target_ruby` failure).
|
|
13
11
|
def initialize(diagnostics: [], stats: nil)
|
|
14
12
|
@diagnostics = diagnostics
|
|
15
13
|
@stats = stats
|