rigortype 0.3.9 → 0.4.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/README.md +2 -2
- data/data/core_overlay/enumerable.rbs +51 -0
- data/data/core_overlay/enumerator.rbs +84 -0
- data/data/core_overlay/hash_rbs3.rbs +41 -0
- data/data/core_overlay/process.rbs +40 -0
- data/data/core_overlay/string_io.rbs +33 -0
- data/data/effects/core.yml +3 -3
- data/data/gem_overlay/activesupport/core_ext.rbs +127 -4
- data/docs/handbook/03-narrowing.md +95 -10
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- data/docs/handbook/07-rbs-and-extended.md +16 -11
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +454 -13
- data/docs/manual/02-cli-reference.md +63 -2
- data/docs/manual/03-configuration.md +7 -0
- data/docs/manual/04-diagnostics.md +4 -0
- data/docs/manual/07-plugins.md +1 -1
- data/docs/manual/10-mcp-server.md +3 -2
- data/docs/manual/16-rbs-extended-annotations.md +40 -7
- data/docs/manual/19-effect-labels.md +10 -0
- data/docs/manual/plugins/README.md +7 -0
- data/docs/manual/plugins/rigor-actioncable.md +8 -1
- data/docs/manual/plugins/rigor-actionmailer.md +7 -0
- data/docs/manual/plugins/rigor-actionpack.md +243 -0
- data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
- data/docs/manual/plugins/rigor-activejob.md +7 -0
- data/docs/manual/plugins/rigor-activerecord.md +110 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +11 -5
- data/docs/manual/plugins/rigor-grape.md +106 -0
- data/docs/manual/plugins/rigor-graphql.md +23 -2
- data/docs/manual/plugins/rigor-pundit.md +8 -1
- data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
- data/docs/manual/plugins/rigor-rails-routes.md +7 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +212 -25
- data/docs/manual/plugins/rigor-sidekiq.md +8 -1
- data/docs/manual/plugins/rigor-sorbet.md +21 -5
- data/exe/rigor +19 -4
- data/lib/rigor/analysis/baseline.rb +1 -1
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -3
- data/lib/rigor/analysis/check_rules/rule_ids.rb +20 -5
- data/lib/rigor/analysis/check_rules/rule_walk.rb +2 -1
- data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +36 -2
- data/lib/rigor/analysis/check_rules.rb +520 -49
- data/lib/rigor/analysis/dependency_recorder.rb +23 -0
- data/lib/rigor/analysis/fact_store.rb +9 -0
- data/lib/rigor/analysis/incremental.rb +26 -0
- data/lib/rigor/analysis/incremental_session.rb +36 -4
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/plugin_roots.rb +0 -1
- data/lib/rigor/analysis/reachability/scan_cache.rb +1 -0
- data/lib/rigor/analysis/rule_catalog.rb +127 -0
- data/lib/rigor/analysis/run_cache_key.rb +20 -11
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +167 -11
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -4
- data/lib/rigor/analysis/runner/pool_coordinator.rb +107 -15
- data/lib/rigor/analysis/runner/project_pre_passes.rb +26 -7
- data/lib/rigor/analysis/runner.rb +278 -20
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +91 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +399 -0
- data/lib/rigor/analysis/worker_session.rb +53 -10
- data/lib/rigor/bleeding_edge.rb +0 -2
- data/lib/rigor/builtins/hkt_builtins.rb +1 -0
- data/lib/rigor/builtins/imported_refinements.rb +4 -0
- data/lib/rigor/builtins/regex_refinement.rb +17 -9
- data/lib/rigor/builtins/static_return_refinements.rb +2 -0
- data/lib/rigor/cache/descriptor.rb +6 -1
- data/lib/rigor/cache/engine_source.rb +29 -1
- data/lib/rigor/cache/incremental_snapshot.rb +33 -1
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- data/lib/rigor/cache/rbs_descriptor.rb +37 -7
- data/lib/rigor/cache/store.rb +0 -1
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/doc_links.rb +1 -1
- data/lib/rigor/cli/docs_command.rb +4 -4
- data/lib/rigor/cli/plugin_command.rb +3 -3
- data/lib/rigor/cli/prism_colorizer.rb +0 -1
- data/lib/rigor/cli/sig_gen_command.rb +210 -29
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +0 -1
- data/lib/rigor/cli/type_of_command.rb +19 -5
- data/lib/rigor/cli/type_of_renderer.rb +20 -9
- data/lib/rigor/cli/type_of_template_probe.rb +189 -0
- data/lib/rigor/cli.rb +21 -3
- data/lib/rigor/configuration/severity_profile.rb +19 -3
- data/lib/rigor/configuration.rb +66 -3
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +11 -2
- data/lib/rigor/effects/callee_rule.rb +368 -0
- data/lib/rigor/effects/catalog.rb +7 -4
- data/lib/rigor/effects/collector.rb +11 -5
- data/lib/rigor/effects/config_envelopes.rb +9 -2
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/effect_table.rb +11 -3
- data/lib/rigor/effects/envelope_check.rb +1 -1
- data/lib/rigor/effects/envelope_index.rb +15 -0
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/identity.rb +16 -0
- data/lib/rigor/effects/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +21 -0
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +43 -31
- data/lib/rigor/effects/propagator.rb +295 -16
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +121 -72
- data/lib/rigor/effects/signature_sources.rb +1 -1
- data/lib/rigor/effects/snapshot.rb +2 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +385 -30
- data/lib/rigor/effects/visibility.rb +101 -0
- data/lib/rigor/environment/lockfile_resolver.rb +17 -0
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/rbs_loader.rb +399 -133
- data/lib/rigor/environment.rb +103 -38
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +139 -9
- data/lib/rigor/inference/block_auto_splat.rb +216 -0
- data/lib/rigor/inference/block_call_timing.rb +338 -0
- data/lib/rigor/inference/block_parameter_binder.rb +73 -27
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +2 -1
- data/lib/rigor/inference/budget_trace.rb +2 -1
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +387 -15
- data/lib/rigor/inference/closure_escape_analyzer.rb +157 -13
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/def_return_typer.rb +11 -7
- data/lib/rigor/inference/define_method_block_self.rb +64 -0
- data/lib/rigor/inference/element_read_widening.rb +22 -10
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +1532 -496
- data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
- data/lib/rigor/inference/global_write_census.rb +239 -0
- data/lib/rigor/inference/guard_rebinding.rb +447 -0
- data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
- data/lib/rigor/inference/index_write_widening.rb +16 -3
- data/lib/rigor/inference/indexed_narrowing.rb +61 -9
- data/lib/rigor/inference/jump_targets.rb +82 -0
- data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
- data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
- data/lib/rigor/inference/last_line.rb +340 -0
- data/lib/rigor/inference/last_status.rb +144 -0
- data/lib/rigor/inference/macro_block_self_type.rb +167 -17
- data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
- data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
- data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
- data/lib/rigor/inference/match_rebinding.rb +392 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +85 -24
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +4 -0
- data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +113 -73
- data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +881 -38
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +128 -53
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +214 -13
- data/lib/rigor/inference/method_parameter_binder.rb +8 -3
- data/lib/rigor/inference/multi_target_binder.rb +340 -52
- data/lib/rigor/inference/mutation_rejoin.rb +4 -1
- data/lib/rigor/inference/mutation_widening.rb +70 -48
- data/lib/rigor/inference/narrowing.rb +540 -120
- data/lib/rigor/inference/operand_effects.rb +167 -0
- data/lib/rigor/inference/operand_walk.rb +88 -0
- data/lib/rigor/inference/optimistic_origin.rb +152 -9
- data/lib/rigor/inference/parameter_inference_collector.rb +3 -2
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +7 -2
- data/lib/rigor/inference/project_patched_scanner.rb +7 -3
- data/lib/rigor/inference/receiver_alias.rb +90 -1
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +15 -11
- data/lib/rigor/inference/repeated_or_writes.rb +463 -0
- data/lib/rigor/inference/return_barrier.rb +54 -0
- data/lib/rigor/inference/rewrite_mutation.rb +120 -0
- data/lib/rigor/inference/scope_indexer.rb +4615 -551
- data/lib/rigor/inference/statement_evaluator.rb +3176 -490
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +44 -7
- data/lib/rigor/inference/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- data/lib/rigor/language_server/debouncer.rb +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +3 -2
- data/lib/rigor/language_server/hover_renderer.rb +3 -3
- data/lib/rigor/language_server/project_context.rb +5 -3
- data/lib/rigor/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +168 -5
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +1 -1
- data/lib/rigor/plugin/effect_attribution.rb +58 -4
- data/lib/rigor/plugin/loader.rb +2 -1
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- data/lib/rigor/plugin/manifest.rb +71 -10
- data/lib/rigor/plugin/registry.rb +35 -1
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +9 -3
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/protection/discovery_seed.rb +3 -1
- data/lib/rigor/protection/kill_signature.rb +0 -1
- data/lib/rigor/protection/mutation_cache.rb +1 -2
- data/lib/rigor/rbs_extended.rb +27 -0
- data/lib/rigor/reflection/constant_ancestors.rb +97 -0
- data/lib/rigor/reflection/constant_path.rb +19 -6
- data/lib/rigor/reflection.rb +72 -105
- data/lib/rigor/scope/discovery_index.rb +135 -2
- data/lib/rigor/scope.rb +859 -49
- data/lib/rigor/sig_gen/alias_index.rb +289 -0
- data/lib/rigor/sig_gen/classification.rb +22 -5
- data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
- data/lib/rigor/sig_gen/generator.rb +558 -31
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- data/lib/rigor/sig_gen/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +1 -1
- data/lib/rigor/sig_gen/renderer.rb +124 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +44 -1
- data/lib/rigor/sig_gen/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +166 -35
- data/lib/rigor/sig_gen.rb +3 -0
- data/lib/rigor/signature_path_audit.rb +1 -1
- data/lib/rigor/source/node_walker.rb +0 -3
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/type/combinator.rb +88 -12
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/hash_shape.rb +1 -1
- data/lib/rigor/type/refined.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +9 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +13 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +239 -4
- data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
- data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
- data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +9 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +380 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +281 -35
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +25 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +171 -5
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +2 -0
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
- data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
- data/plugins/rigor-grape/sig/grape.rbs +263 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +72 -2
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +11 -7
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +35 -37
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +9 -17
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +217 -36
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +9 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +0 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +46 -1
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/sig/rigor/analysis/baseline.rbs +69 -7
- data/sig/rigor/analysis/fact_store.rbs +1 -1
- data/sig/rigor/analysis/project_scan.rbs +74 -0
- data/sig/rigor/effects/config_envelopes.rbs +100 -0
- data/sig/rigor/effects/effect_table.rbs +60 -0
- data/sig/rigor/effects/envelope.rbs +97 -0
- data/sig/rigor/effects/envelope_index.rbs +33 -0
- data/sig/rigor/effects/file_collection.rbs +81 -0
- data/sig/rigor/effects/label.rbs +26 -0
- data/sig/rigor/effects/label_set.rbs +46 -0
- data/sig/rigor/effects/method_key.rbs +24 -0
- data/sig/rigor/effects/origin.rbs +50 -0
- data/sig/rigor/effects/plugin_facts.rbs +141 -0
- data/sig/rigor/effects/registry.rbs +68 -0
- data/sig/rigor/effects/summary.rbs +50 -0
- data/sig/rigor/effects/taint_cause.rbs +12 -0
- data/sig/rigor/environment.rbs +12 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +6 -4
- data/sig/rigor/plugin/additional_initializer.rbs +36 -0
- data/sig/rigor/plugin/base.rbs +30 -7
- data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
- data/sig/rigor/plugin/effect_attribution.rbs +66 -0
- data/sig/rigor/plugin/effect_edge.rbs +33 -0
- data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
- data/sig/rigor/plugin/io_boundary.rbs +1 -1
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +50 -11
- data/sig/rigor/plugin/protocol_contract.rbs +68 -0
- data/sig/rigor/plugin/registry.rbs +62 -1
- data/sig/rigor/plugin.rbs +1 -1
- data/sig/rigor/rbs_extended.rbs +1 -1
- data/sig/rigor/reflection.rbs +7 -6
- data/sig/rigor/scope.rbs +135 -20
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -7
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +6 -0
- data/sig/rigor.rbs +38 -20
- data/skills/rigor-ask/SKILL.md +8 -5
- data/skills/rigor-baseline-reduce/SKILL.md +4 -6
- data/skills/rigor-ci-setup/SKILL.md +17 -21
- data/skills/rigor-doctor/SKILL.md +24 -22
- data/skills/rigor-doctor/references/01-checks.md +97 -33
- data/skills/rigor-editor-setup/SKILL.md +6 -4
- data/skills/rigor-mcp-setup/SKILL.md +5 -4
- data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
- data/skills/rigor-next-steps/SKILL.md +4 -2
- data/skills/rigor-plugin-author/SKILL.md +19 -23
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +12 -25
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
- data/skills/rigor-plugin-review/SKILL.md +6 -4
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
- data/skills/rigor-plugin-tune/SKILL.md +4 -2
- data/skills/rigor-project-init/SKILL.md +9 -7
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +33 -8
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
- data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
- data/skills/rigor-protection-uplift/SKILL.md +4 -6
- data/skills/rigor-rbs-setup/SKILL.md +4 -2
- data/skills/rigor-type-oracle/SKILL.md +4 -6
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +12 -8
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +0 -3
- data/skills/rigor-unused-adjudicate/SKILL.md +4 -2
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +108 -1
|
@@ -126,6 +126,29 @@ module Rigor
|
|
|
126
126
|
[result, capture.snapshot]
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
+
# Issue #992 — runs the block with its reads DETACHED from the current consumer (and from any enclosing
|
|
130
|
+
# capture) and returns `[block_result, read_set]`, so a caller that learns only afterwards whether its
|
|
131
|
+
# answer depended on those reads can {replay} them or drop them. `call.wrong-arity` reads a class's whole
|
|
132
|
+
# envelope bucket to decide a call that turns out to fit, and recording that as a file-level ancestry
|
|
133
|
+
# edge would make every correct call on a project class re-check on any edit to the class's files.
|
|
134
|
+
# Returns a nil read set when no recording is active.
|
|
135
|
+
def withhold
|
|
136
|
+
previous = Thread.current[KEY]
|
|
137
|
+
return [yield, nil] if previous.nil?
|
|
138
|
+
|
|
139
|
+
previous_captures = Thread.current[CAPTURE_KEY]
|
|
140
|
+
capture = Capture.new
|
|
141
|
+
Thread.current[KEY] = Accumulator.new(previous.consumer)
|
|
142
|
+
Thread.current[CAPTURE_KEY] = [capture]
|
|
143
|
+
begin
|
|
144
|
+
result = yield
|
|
145
|
+
ensure
|
|
146
|
+
Thread.current[KEY] = previous
|
|
147
|
+
Thread.current[CAPTURE_KEY] = previous_captures
|
|
148
|
+
end
|
|
149
|
+
[result, capture.snapshot]
|
|
150
|
+
end
|
|
151
|
+
|
|
129
152
|
# ADR-84 WD2 — replays a {ReadSet} into the current consumer's accumulator as if each read happened
|
|
130
153
|
# here, applying the same per-consumer self-read filter as {read_site}, and tees into every active
|
|
131
154
|
# capture so an enclosing capture window stays transitive when a memo hit substitutes for a body walk.
|
|
@@ -21,8 +21,17 @@ module Rigor
|
|
|
21
21
|
# #775 — a target is an immutable value keyed by its name, and the local target is built on every
|
|
22
22
|
# binding (`Scope#with_local` invalidates through it) and every local-fact read: ~350k times on
|
|
23
23
|
# the lib self-check for a few thousand distinct names. One shared instance per name.
|
|
24
|
+
#
|
|
25
|
+
# #1055 — the table is a class ivar holding an unshareable Hash, and a non-main Ractor may neither
|
|
26
|
+
# write one nor read one. A pool worker therefore pays the allocation instead of sharing the
|
|
27
|
+
# instance: the interning is a per-process ALLOCATION saving, never an identity contract (`Target`
|
|
28
|
+
# is a `Data` value and compares by value), so declining it changes nothing a caller can observe.
|
|
29
|
+
# The guard is a C-level predicate on the hot path, which is what keeps the sequential run's
|
|
30
|
+
# saving intact.
|
|
24
31
|
def self.local(name)
|
|
25
32
|
name = name.to_sym
|
|
33
|
+
return new(kind: :local, name: name) unless Ractor.main?
|
|
34
|
+
|
|
26
35
|
@local_targets[name] ||= new(kind: :local, name: name)
|
|
27
36
|
end
|
|
28
37
|
@local_targets = {}
|
|
@@ -120,6 +120,32 @@ module Rigor
|
|
|
120
120
|
appeared.freeze
|
|
121
121
|
end
|
|
122
122
|
|
|
123
|
+
# Issue #1120 — the method names whose refinements moved in this edit: a `[refined class, method, refining
|
|
124
|
+
# module]` entry that appeared in, or vanished from, any file in `paths`. A consumer whose
|
|
125
|
+
# `call.undefined-method` answer consulted the refinement table recorded a `refinement:<method>` edge
|
|
126
|
+
# whichever way it answered, because the answer is a function of the whole project's refinements of that
|
|
127
|
+
# name. `paths` MUST include removed files, whose whole before-table vanished. `before` / `after` map a
|
|
128
|
+
# path to that file's `{refined class => {method => [modules]}}` table (nil when it refines nothing).
|
|
129
|
+
def changed_refinement_names(paths, before, after)
|
|
130
|
+
moved = Set.new
|
|
131
|
+
paths.each do |path|
|
|
132
|
+
entries_before = refinement_entries(before[path])
|
|
133
|
+
entries_after = refinement_entries(after[path])
|
|
134
|
+
(entries_before ^ entries_after).each { |(_refined, method_name, _module)| moved << method_name }
|
|
135
|
+
end
|
|
136
|
+
moved.freeze
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def refinement_entries(table)
|
|
140
|
+
entries = Set.new
|
|
141
|
+
table&.each do |refined, methods|
|
|
142
|
+
methods.each do |method_name, modules|
|
|
143
|
+
modules.each { |refining| entries << [refined, method_name, refining] }
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
entries
|
|
147
|
+
end
|
|
148
|
+
|
|
123
149
|
# Issue #639 — the SYMMETRIC reading of {appeared_classes}: the names whose declaration in a file
|
|
124
150
|
# appeared OR VANISHED. Appearance alone answered the miss side, which was the only side recording a
|
|
125
151
|
# `class:` edge; a bare reference that RESOLVED now records one too, and what invalidates it is the
|
|
@@ -162,7 +162,11 @@ module Rigor
|
|
|
162
162
|
runner = build_runner(record_dependencies: true)
|
|
163
163
|
diagnostics = run_runner(runner).diagnostics
|
|
164
164
|
@last_runner = runner # ADR-88 WD1 — the post-hoc fact-surface fingerprint reads this prepared registry.
|
|
165
|
-
|
|
165
|
+
# #392 — the template units are deliberately NOT in the analysed set. `Runner#target_files`
|
|
166
|
+
# re-analyses every unit on every run, so a unit must never be reused from the per-file cache and
|
|
167
|
+
# must never read as a file that VANISHED from the project on the next recheck (which is what
|
|
168
|
+
# `previous - current` would say, `current_files` being a `.rb` expansion).
|
|
169
|
+
@analyzed = runner.analyzed_files - runner.template_unit_paths
|
|
166
170
|
@seed_bundles = runner.seed_bundles # ADR-85 WD2 — the freshly built bundle set for the next run.
|
|
167
171
|
absorb_dependency_graph(runner)
|
|
168
172
|
@return_summaries = runner.return_summaries # ADR-89 WD2 — the full-run behavioural surface.
|
|
@@ -283,12 +287,24 @@ module Rigor
|
|
|
283
287
|
symbol_pairs = behaviourally_unstable_pairs(changed_pairs, unstable, scan_index) | param_pairs
|
|
284
288
|
base = dependents_base(unstable, symbol_pairs)
|
|
285
289
|
closure = base | changed.to_set | added.to_set |
|
|
286
|
-
negative_affected(scan, removed, new_fps, new_class_decls, new_constant_decls)
|
|
290
|
+
negative_affected(scan, removed, new_fps, new_class_decls, new_constant_decls, summary)
|
|
287
291
|
closure = param_seed_closure(closure, param_files)
|
|
288
292
|
removed.each { |path| closure |= @dependents[path] || Set.new }
|
|
289
293
|
closure.freeze
|
|
290
294
|
end
|
|
291
295
|
|
|
296
|
+
# Issue #1367 — every file whose last result holds a `global.*` diagnostic re-checks on any recheck. What
|
|
297
|
+
# exempts such a write is a fact of the whole program — an alias, or a `write` / `to_str` / `to_int` / hatch
|
|
298
|
+
# defined in any spelling on any receiver, a rewritten ancestor, a mixin — which no name-keyed edge can
|
|
299
|
+
# follow, so a stale report would outlive the edit that exempts it. Such files are rare, so this costs little.
|
|
300
|
+
# A file the census silenced is not re-checked when the exemption goes away: that stale answer misses a
|
|
301
|
+
# report, it never makes one.
|
|
302
|
+
def global_write_reporters
|
|
303
|
+
@cache.each_with_object(Set.new) do |(path, diagnostics), reporters|
|
|
304
|
+
reporters << path if diagnostics.any? { |diagnostic| diagnostic.rule.to_s.start_with?("global.") }
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
292
308
|
# ADR-67 WD6c lift — the seed-invalidated callees' own contribution to the closure: the files
|
|
293
309
|
# themselves, plus — on a pre-slice-4 snapshot with no symbol edges, where the pairs' symbol fan-out
|
|
294
310
|
# found nothing — their file-level dependents (wider, always sound).
|
|
@@ -828,7 +844,9 @@ module Rigor
|
|
|
828
844
|
# a prior missed lookup, and (issue #639) because a class DECLARATION appeared or vanished. Maps each
|
|
829
845
|
# appeared `"ClassName#method"` to the negative-dependency key it would satisfy (`toplevel:foo` for a
|
|
830
846
|
# top-level def, `method:C#m` otherwise), then unions the recorded negative-dependents of those keys.
|
|
831
|
-
|
|
847
|
+
# Issue #1120 — plus the consumers of any refinement name the edit moved ({#refinement_affected}). Issue #1367 —
|
|
848
|
+
# and every file whose last result holds a `global.*` diagnostic ({#global_write_reporters}).
|
|
849
|
+
def negative_affected(changed, removed, new_fingerprints, new_class_decls, new_constant_decls, summary = nil)
|
|
832
850
|
appeared_methods = Incremental.appeared_symbols(changed, @symbol_fingerprints, new_fingerprints)
|
|
833
851
|
# Issue #639 — a class DISAPPEARING satisfies the `class:` kind too, now that a resolved bare
|
|
834
852
|
# reference records the edge and not only a failed one; removed files join the diff for it.
|
|
@@ -844,7 +862,21 @@ module Rigor
|
|
|
844
862
|
keys = appeared_methods.map { |symbol| negative_key_for(symbol) }
|
|
845
863
|
keys.concat(moved_classes.map { |klass| "class:#{klass.split('::').last}" })
|
|
846
864
|
keys.concat(moved_constants.map { |name| "constant:#{name.split('::').last}" })
|
|
847
|
-
Incremental.negative_closure(keys, @negative_dependents)
|
|
865
|
+
Incremental.negative_closure(keys, @negative_dependents) | refinement_affected(changed + removed, summary) |
|
|
866
|
+
global_write_reporters
|
|
867
|
+
end
|
|
868
|
+
|
|
869
|
+
# Issue #1120 — the consumers whose `call.undefined-method` answer read the refinement table for a method
|
|
870
|
+
# name whose refinements moved in this edit. The before-state is each file's seed bundle (the per-file
|
|
871
|
+
# table the last run recorded), the after-state the scan's per-file tables. A refine body is not a class's
|
|
872
|
+
# method, so neither the symbol fingerprints nor the appeared-symbol diff above sees it.
|
|
873
|
+
def refinement_affected(paths, summary)
|
|
874
|
+
after = (summary && summary[:refinements]) || {}
|
|
875
|
+
before = paths.to_h { |path| [path, @seed_bundles.dig(path, :refinements)] }
|
|
876
|
+
names = Incremental.changed_refinement_names(paths, before, after)
|
|
877
|
+
return Set.new if names.empty?
|
|
878
|
+
|
|
879
|
+
Incremental.negative_closure(names.map { |name| "refinement:#{name}" }, @negative_dependents)
|
|
848
880
|
end
|
|
849
881
|
|
|
850
882
|
# The qualified class/module names declared in the pre-parsed `index` (shared with
|
|
@@ -16,13 +16,23 @@ module Rigor
|
|
|
16
16
|
# are NOT visible until the owner invalidates the scan — typically via `workspace/didChangeWatchedFiles`.
|
|
17
17
|
# This is the same trade-off the LSP made when slice 7 cached only the `Environment`; extending the cache
|
|
18
18
|
# to the pre-pass outputs preserves the contract.
|
|
19
|
+
#
|
|
20
|
+
# `template_units` (#1038) is the one slot a consumer does not adopt verbatim. The rest of the snapshot
|
|
21
|
+
# is reused as it stands until the owner invalidates it; the template index is handed to
|
|
22
|
+
# `TemplateUnits.collect(previous:)`, which re-expands the claimed globs and revalidates each template
|
|
23
|
+
# against the filesystem, so a template edited, added or deleted on disk is seen on the next publish
|
|
24
|
+
# even though the snapshot itself did not move. What the carry saves is the plugin transform — for ERB
|
|
25
|
+
# (#393) an Erubi compile of every view in the project, otherwise paid per keystroke. The index is
|
|
26
|
+
# always built WITHOUT a buffer binding, so the shared snapshot never holds a unit compiled from an
|
|
27
|
+
# editor's in-flight bytes.
|
|
19
28
|
ProjectScan = Data.define(
|
|
20
29
|
:plugin_registry,
|
|
21
30
|
:dependency_source_index,
|
|
22
31
|
:synthetic_method_index,
|
|
23
32
|
:project_patched_methods,
|
|
24
33
|
:plugin_prepare_diagnostics,
|
|
25
|
-
:pre_eval_diagnostics
|
|
34
|
+
:pre_eval_diagnostics,
|
|
35
|
+
:template_units
|
|
26
36
|
)
|
|
27
37
|
end
|
|
28
38
|
end
|
|
@@ -83,7 +83,6 @@ module Rigor
|
|
|
83
83
|
# Loads the project's configured plugins, runs every `#prepare`, and returns the union of every
|
|
84
84
|
# published `:reachability_roots` and `:reachability_references` fact.
|
|
85
85
|
#
|
|
86
|
-
# @param configuration — the loaded project configuration.
|
|
87
86
|
# @param plugin_requirer — how a plugin gem is brought into the process, called with the gem name. The
|
|
88
87
|
# same seam `Analysis::Runner` exposes, so a spec can register a plugin class without publishing a
|
|
89
88
|
# gem.
|
|
@@ -36,6 +36,7 @@ module Rigor
|
|
|
36
36
|
File.expand_path("scan.rb", __dir__),
|
|
37
37
|
File.expand_path(__FILE__)
|
|
38
38
|
].freeze
|
|
39
|
+
Ractor.make_shareable(SOURCE_FILES)
|
|
39
40
|
|
|
40
41
|
# A file modified within this window of the recording instant is computed but never
|
|
41
42
|
# recorded — the FileDigest racy-write guard, one tier simpler because a refused signature
|
|
@@ -656,6 +656,90 @@ module Rigor
|
|
|
656
656
|
since: "0.1.2"
|
|
657
657
|
),
|
|
658
658
|
|
|
659
|
+
CheckRules::RULE_GLOBAL_WRITE_TYPE_MISMATCH => Entry.new(
|
|
660
|
+
id: CheckRules::RULE_GLOBAL_WRITE_TYPE_MISMATCH,
|
|
661
|
+
summary: "A special global is assigned a literal its setter rejects (TypeError at runtime).",
|
|
662
|
+
fires_when: [
|
|
663
|
+
"The write is `$g = literal` to a special whose setter checks the value: `$/` / `$-0`, `$,` and " \
|
|
664
|
+
"`$\\` (a String or nil), `$;` / `$-F` (a String, a Regexp, nil, or an object with `to_str`), `$~` " \
|
|
665
|
+
"(a MatchData or nil), `$0` / `$PROGRAM_NAME` (a String or an object with `to_str`), `$.` (an " \
|
|
666
|
+
"Integer, a Float, or an object with `to_int`), `$-i` (a String, nil, false, or an object with " \
|
|
667
|
+
"`to_str`), and `$stdout` / `$>` / `$stderr` (an object that responds to `write`, ADR-117 WD2).",
|
|
668
|
+
"The value is a literal node — an Integer, Float, Rational or imaginary literal, a String, Symbol " \
|
|
669
|
+
"or Regexp literal (interpolated or not), an Array or Hash literal, or `nil` / `true` / `false`, " \
|
|
670
|
+
"parenthesised or not — whose class the setter does not take: `$/ = 1`, `$/ = /x/`, `$~ = \"x\"`, " \
|
|
671
|
+
"`$0 = nil`, `$. = \"3\"`, `$stdout = 1`.",
|
|
672
|
+
"Where the setter also converts (`to_str`, `to_int`) or asks for `write`, the literal's object " \
|
|
673
|
+
"cannot answer that method: RBS gives its class neither the method nor a `method_missing` / " \
|
|
674
|
+
"`respond_to_missing?` / `respond_to?` of its own, and the program defines none of them anywhere.",
|
|
675
|
+
"The envelope is the interpreter's setter, not the global's RBS declaration: `$/ = /x/` fires " \
|
|
676
|
+
"although `$;` takes a Regexp."
|
|
677
|
+
],
|
|
678
|
+
does_not_fire_when: [
|
|
679
|
+
"The special's setter accepts every value: `$stdin` (never checked, ADR-117 WD2), `$_`, `$VERBOSE`, " \
|
|
680
|
+
"`$DEBUG`, `$=`; nor `$@`, whose setter depends on whether `$!` is set.",
|
|
681
|
+
"The value is not a literal — a variable, a method call, a constant, a conditional — whatever its " \
|
|
682
|
+
"inferred type: a class RBS declares without `write` or `to_str` does not prove the object lacks it.",
|
|
683
|
+
"RBS gives the literal's class the method or a hatch of its own (a `sig/` reopening or `include` " \
|
|
684
|
+
"counts).",
|
|
685
|
+
"The program — any project or `pre_eval:` file — defines the method or a `method_missing` / " \
|
|
686
|
+
"`respond_to_missing?` / `respond_to?` anywhere, in any spelling (`def`, `define_method`, " \
|
|
687
|
+
"`define_singleton_method`, `alias`, `alias_method`, `attr_*`, a delegation macro, a string `eval`, " \
|
|
688
|
+
"the same through `send`) and on any receiver, or defines a method whose name no literal spells (a " \
|
|
689
|
+
"computed `define_method` or `send` name, a string `eval` whose code is not a plain literal, a name " \
|
|
690
|
+
"whose bytes are not valid in its encoding). " \
|
|
691
|
+
"This is blanket: no census of where a definition lands can be complete, so the literal of every " \
|
|
692
|
+
"class declines, whatever the definition's owner.",
|
|
693
|
+
"An ancestor's surface is rewritten from outside (`Integer.include(M)`, `Object.include(M)`, a string " \
|
|
694
|
+
"`class_eval`), or a top-level `include` / `prepend` / `extend`, or an ancestor's `include`, names a " \
|
|
695
|
+
"module RBS does not rule out.",
|
|
696
|
+
"For `$stdout` / `$>` / `$stderr`, a refinement that may add `write`, to any class, and a `using` in " \
|
|
697
|
+
"effect at the write. Which class it refines is not followed. The `to_str` / `to_int` conversions, and a " \
|
|
698
|
+
"refined `respond_to_missing?` / `respond_to?`, ignore refinements, so those still fire.",
|
|
699
|
+
"Any project or `pre_eval:` file aliases the special (`alias $stdout $out`, on either side).",
|
|
700
|
+
"The write is `$g op= v`, `$g ||= v`, `$g &&= v`, a multiple-assignment target, a `for` index, or a " \
|
|
701
|
+
"`rescue => $g` reference: their value is not type-checked.",
|
|
702
|
+
"The write sits in the dead arm of a decidable version guard. Other code that never runs (`if " \
|
|
703
|
+
"false`, after `return`) is not recognised, and a write there still fires."
|
|
704
|
+
],
|
|
705
|
+
suppression: "`# rigor:disable global.write-type-mismatch` on the write.",
|
|
706
|
+
severity_authored: :error,
|
|
707
|
+
severity_by_profile: { lenient: :warning, balanced: :error, strict: :error },
|
|
708
|
+
# A literal's class is exact, the setter's envelope is the running interpreter's, and every way a program can
|
|
709
|
+
# give the object the method the setter asks for declines; the write then raises every time it runs.
|
|
710
|
+
evidence_tier: :high,
|
|
711
|
+
since: "0.4.0"
|
|
712
|
+
),
|
|
713
|
+
|
|
714
|
+
CheckRules::RULE_GLOBAL_READONLY_WRITE => Entry.new(
|
|
715
|
+
id: CheckRules::RULE_GLOBAL_READONLY_WRITE,
|
|
716
|
+
summary: "A read-only special global is written (NameError at runtime).",
|
|
717
|
+
fires_when: [
|
|
718
|
+
"The write is `$g = value`, `$g op= value`, or a target of a multiple assignment (`$g, x = ...`, " \
|
|
719
|
+
"`*$g`) — forms that always write — to a special Ruby defines read-only: `$!`, `$$`, `$?`, `$<`, " \
|
|
720
|
+
"`$FILENAME`, `$*`, `$:` / `$LOAD_PATH` / `$-I`, `$\"` / `$LOADED_FEATURES`, `$-W`, `$-p`, `$-l`, " \
|
|
721
|
+
"`$-a`.",
|
|
722
|
+
"Whatever the value: the setter raises `NameError` (`$! is a read-only variable`) before it " \
|
|
723
|
+
"looks at it."
|
|
724
|
+
],
|
|
725
|
+
does_not_fire_when: [
|
|
726
|
+
"The write is `$g ||= v` or `$g &&= v`, which writes only when the current value is falsy " \
|
|
727
|
+
"(truthy): `$LOAD_PATH ||= []` never writes.",
|
|
728
|
+
"The write is a `for` index or a `rescue => $g` reference.",
|
|
729
|
+
"Any project or `pre_eval:` file aliases the special (`alias $! $err`, on either side), which can " \
|
|
730
|
+
"make the name another variable.",
|
|
731
|
+
"Mutating the value is not a write: `$LOAD_PATH << dir` and `$LOADED_FEATURES.delete(f)` stay silent.",
|
|
732
|
+
"The write sits in the dead arm of a decidable version guard. Other code that never runs is not " \
|
|
733
|
+
"recognised, and a write there still fires."
|
|
734
|
+
],
|
|
735
|
+
suppression: "`# rigor:disable global.readonly-write` on the write.",
|
|
736
|
+
severity_authored: :error,
|
|
737
|
+
severity_by_profile: { lenient: :error, balanced: :error, strict: :error },
|
|
738
|
+
# Syntactic: the name alone decides it, and the write raises every time it runs.
|
|
739
|
+
evidence_tier: :high,
|
|
740
|
+
since: "0.4.0"
|
|
741
|
+
),
|
|
742
|
+
|
|
659
743
|
CheckRules::RULE_SUPPRESSION_UNKNOWN_RULE => Entry.new(
|
|
660
744
|
id: CheckRules::RULE_SUPPRESSION_UNKNOWN_RULE,
|
|
661
745
|
summary: "A `# rigor:disable[-file]` comment names a rule that does not exist.",
|
|
@@ -896,6 +980,48 @@ module Rigor
|
|
|
896
980
|
since: "0.3.4"
|
|
897
981
|
),
|
|
898
982
|
|
|
983
|
+
CheckRules::RULE_CONTRADICTING_SIGNATURE => Entry.new(
|
|
984
|
+
id: CheckRules::RULE_CONTRADICTING_SIGNATURE,
|
|
985
|
+
summary: "Two declarations of one method provably contradict: no value or call satisfies both.",
|
|
986
|
+
fires_when: [
|
|
987
|
+
"A method is declared both in the project's `sig/` and by an inline `# @rbs` / `#:` " \
|
|
988
|
+
"annotation, and the return or a parameter every call must pass holds two proven-disjoint " \
|
|
989
|
+
"types — distinct literals, a literal outside such a class, or two classes written absolutely " \
|
|
990
|
+
"whose RBS comes from Ruby core or the stdlib, neither an RBS ancestor of the other " \
|
|
991
|
+
"(`::String` against `::Integer`) — or the two keyword-free declarations accept " \
|
|
992
|
+
"positional counts that cannot meet, or one requires a keyword the other cannot take in any " \
|
|
993
|
+
"form. Several overloads are paired by correspondence, never by order. Positioned at the " \
|
|
994
|
+
"`.rbs` member; Rigor reads that declaration.",
|
|
995
|
+
"A member-level `%a{rigor:v1:return: …}` or `%a{rigor:v1:param: …}` refinement is proven " \
|
|
996
|
+
"disjoint from the type its own member declares, in every overload it applies to " \
|
|
997
|
+
"(`rigor:v1:return: positive-int` on `-> String`)."
|
|
998
|
+
],
|
|
999
|
+
does_not_fire_when: [
|
|
1000
|
+
"One declaration refines the other (`String` against `non-empty-string`, `Symbol` against " \
|
|
1001
|
+
"`:asc | :desc`, `bool` against `TrueClass`): the two merge to the more precise one, silently.",
|
|
1002
|
+
"Either side is `untyped`, `void` or `top` in that position — consistent with everything.",
|
|
1003
|
+
"Rigor cannot prove the two contradict: a module or interface on either side, a class the " \
|
|
1004
|
+
"project declares (in `sig/`, inline or only in Ruby), a gem's class, any relative class name, " \
|
|
1005
|
+
"two element types of one generic class, a type alias, `self`, a type variable, a proc, an " \
|
|
1006
|
+
"optional or rest position, any block position, overloads that do not pair one to one, or " \
|
|
1007
|
+
"differently shaped parameter lists that still overlap. The " \
|
|
1008
|
+
"inline signature is then dropped and reported as `source-rbs-annotation-not-honoured` (info).",
|
|
1009
|
+
"A refinement is written on one overload rather than on the member: call sites do not honour it.",
|
|
1010
|
+
"Two `.rbs` files declare the same method (that fails the class's definition build, " \
|
|
1011
|
+
"`rbs.coverage.definition-build-failed`), or a project `.rbs` collides with bundled RBS " \
|
|
1012
|
+
"(the file is quarantined, `rbs.coverage.quarantined-signature`)."
|
|
1013
|
+
],
|
|
1014
|
+
suppression: "`disable: [\"rbs.contradicting-signature\"]` in `.rigor.yml`, or a baseline entry. A " \
|
|
1015
|
+
"`# rigor:disable` comment does not reach a row positioned in a `.rbs` file. The real " \
|
|
1016
|
+
"fix is to regenerate the stale signature or correct the annotation.",
|
|
1017
|
+
severity_authored: :error,
|
|
1018
|
+
severity_by_profile: { lenient: :error, balanced: :error, strict: :error },
|
|
1019
|
+
# Both sides are authored declarations, and a firing needs a proof that no value or call satisfies
|
|
1020
|
+
# both — the ADR-35 both-sides-authored construction, with every doubt routed to the `:info` row.
|
|
1021
|
+
evidence_tier: :high,
|
|
1022
|
+
since: "0.4.0"
|
|
1023
|
+
),
|
|
1024
|
+
|
|
899
1025
|
CheckRules::RULE_SUPPRESSION_UNKNOWN_MARKER => Entry.new(
|
|
900
1026
|
id: CheckRules::RULE_SUPPRESSION_UNKNOWN_MARKER,
|
|
901
1027
|
summary: "A comment uses a suppression marker Rigor does not recognise " \
|
|
@@ -925,6 +1051,7 @@ module Rigor
|
|
|
925
1051
|
since: "0.3.0"
|
|
926
1052
|
)
|
|
927
1053
|
}.freeze
|
|
1054
|
+
Ractor.make_shareable(ENTRIES)
|
|
928
1055
|
|
|
929
1056
|
module_function
|
|
930
1057
|
|
|
@@ -82,15 +82,23 @@ module Rigor
|
|
|
82
82
|
|
|
83
83
|
# @param rbs_config_entries — the RBS-derived config slots
|
|
84
84
|
# (`rbs.libraries` [+ `rbs.virtual_rbs`]). nil on any failure so a malformed key disables the cache.
|
|
85
|
-
|
|
85
|
+
# @param template_units_digest — #392 — the run's template-unit identity
|
|
86
|
+
# ({Analysis::TemplateUnits#digest}: every unit's source bytes + transform id + synthesis version).
|
|
87
|
+
# nil for a project whose plugins declare no `template_globs:`, which is every project today, and a
|
|
88
|
+
# nil contributes NO slot — so no existing key moves. A project that DOES have units produces a key
|
|
89
|
+
# the ADR-87 boot-slim probe (which loads no plugin and so passes nil) cannot reconstruct: the probe
|
|
90
|
+
# simply misses and the full path takes over, the same forgone-fast-lane trade the `rbs.virtual_rbs`
|
|
91
|
+
# slot already makes, and never a wrong hit.
|
|
92
|
+
def descriptor(configuration:, files:, explain:, rbs_config_entries:, template_units_digest: nil)
|
|
86
93
|
Cache::Descriptor.new(
|
|
87
94
|
gems: [Cache::RbsDescriptor.rbs_gem_entry],
|
|
88
|
-
configs: rbs_config_entries + engine_source_entries + lockfile_entries(configuration) +
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
configs: rbs_config_entries + engine_source_entries + lockfile_entries(configuration) +
|
|
96
|
+
template_unit_entries(template_units_digest) + [
|
|
97
|
+
config_entry("configuration", Marshal.dump(configuration.to_h)),
|
|
98
|
+
config_entry("engine",
|
|
99
|
+
"#{Rigor::VERSION}:#{Cache::Descriptor::SCHEMA_VERSION}:#{explain}"),
|
|
100
|
+
config_entry("paths", files.sort.join("\n"))
|
|
101
|
+
]
|
|
94
102
|
)
|
|
95
103
|
rescue StandardError
|
|
96
104
|
nil
|
|
@@ -104,11 +112,12 @@ module Rigor
|
|
|
104
112
|
#
|
|
105
113
|
# {Cache::EngineSource::Unavailable} is left to propagate into `descriptor`'s rescue, which disables
|
|
106
114
|
# the cache for the run: an engine we cannot identify must not be keyed by its version alone.
|
|
107
|
-
def
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
def template_unit_entries(digest)
|
|
116
|
+
digest.nil? ? [] : [config_entry("template-units", digest)]
|
|
117
|
+
end
|
|
110
118
|
|
|
111
|
-
|
|
119
|
+
def engine_source_entries
|
|
120
|
+
Cache::EngineSource.key_config_entries
|
|
112
121
|
end
|
|
113
122
|
|
|
114
123
|
# Issue #564 — the two dependency lockfiles, BY CONTENT. Both are inputs to the run's diagnostics and
|