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
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Analysis
|
|
7
|
+
module CheckRules
|
|
8
|
+
# Issue #1367 (ADR-117 Decision point 1, WD2) — what the interpreter's setter for each special global accepts:
|
|
9
|
+
# the envelope `global.write-type-mismatch` and `global.readonly-write` judge a write against.
|
|
10
|
+
#
|
|
11
|
+
# The setter is the authority, not the RBS declaration of the global. Where the two differ, the setter wins:
|
|
12
|
+
# `$;` is declared `Regexp | String | nil`, but its setter also takes any object with `to_str`, and `$stdout` is
|
|
13
|
+
# declared `IO`, but its setter takes anything that responds to `write`. Each entry names the CRuby function
|
|
14
|
+
# that enforces it and was confirmed on Ruby 4.0.5, whose error the comment quotes.
|
|
15
|
+
#
|
|
16
|
+
# A special whose setter accepts every value is absent, so a write to it is never checked: `$stdin`
|
|
17
|
+
# (ADR-117 WD2), `$_`, `$VERBOSE` / `$-v` / `$-w`, `$DEBUG` / `$-d` and `$=`. So is `$@`, whose setter raises
|
|
18
|
+
# `ArgumentError` or `TypeError` depending on whether `$!` is set when it runs. `$&`, `` $` ``, `$'`, `$+`
|
|
19
|
+
# and `$1`..`$9` cannot be assigned at all: that is a syntax error, not a write.
|
|
20
|
+
module SpecialGlobalSetters
|
|
21
|
+
# A setter that raises `TypeError` unless the value is an instance of one of `classes` (a subclass counts,
|
|
22
|
+
# since the setter tests the object's built-in type) or, when `conversion` names a method, responds to it:
|
|
23
|
+
# the implicit conversion the setter calls (`to_str`, `to_int`) or the method it asks `respond_to?` about
|
|
24
|
+
# (`write`). `accepts` is the phrase the diagnostic quotes.
|
|
25
|
+
Contract = Data.define(:classes, :conversion, :accepts) do
|
|
26
|
+
# Whether an instance of the core class `class_name` is one the setter takes by type. Both sides are core
|
|
27
|
+
# classes, whose hierarchy is Ruby's own, so the running interpreter answers it.
|
|
28
|
+
def accepts_class?(class_name)
|
|
29
|
+
klass = ::Object.const_get(class_name)
|
|
30
|
+
classes.any? { |accepted| klass <= ::Object.const_get(accepted) }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# `rb_str_setter` (string.c), reached through `rb_deprecated_str_setter` and io.c's `deprecated_rs_setter`,
|
|
35
|
+
# tests `T_STRING` and calls no conversion: `$/ = 1` raises "value of $/ must be String", and so do a
|
|
36
|
+
# `Regexp` and an object with `to_str`.
|
|
37
|
+
STRING_OR_NIL = Contract.new(classes: %w[String NilClass].freeze, conversion: nil, accepts: "a String or nil")
|
|
38
|
+
# `rb_fs_setter` (string.c): nil, a String, a Regexp, or an object with `to_str` (`rb_fs_check`).
|
|
39
|
+
# `$; = 1` raises "value of $; must be String or Regexp".
|
|
40
|
+
FIELD_SEPARATOR = Contract.new(
|
|
41
|
+
classes: %w[String Regexp NilClass].freeze, conversion: :to_str,
|
|
42
|
+
accepts: "a String, a Regexp, nil, or an object with `to_str'"
|
|
43
|
+
)
|
|
44
|
+
# `match_setter` (re.c): nil or a MatchData (`Check_Type(val, T_MATCH)`). `$~ = 1` raises
|
|
45
|
+
# "wrong argument type Integer (expected MatchData)".
|
|
46
|
+
MATCH_DATA = Contract.new(classes: %w[MatchData NilClass].freeze, conversion: nil,
|
|
47
|
+
accepts: "a MatchData or nil")
|
|
48
|
+
# `set_arg0` (ruby.c) calls `StringValueCStr`: a String or an object with `to_str`, and not nil. `$0 = 1`
|
|
49
|
+
# raises "no implicit conversion of Integer into String".
|
|
50
|
+
PROGRAM_NAME = Contract.new(
|
|
51
|
+
classes: %w[String].freeze, conversion: :to_str, accepts: "a String or an object with `to_str'"
|
|
52
|
+
)
|
|
53
|
+
# `argf_lineno_setter` (io.c) calls `NUM2INT`: an Integer, a Float, or an object with `to_int`, and not nil.
|
|
54
|
+
# `$. = "1"` raises "no implicit conversion of String into Integer".
|
|
55
|
+
LINE_NUMBER = Contract.new(
|
|
56
|
+
classes: %w[Integer
|
|
57
|
+
Float].freeze, conversion: :to_int, accepts: "an Integer, a Float, or an object with `to_int'"
|
|
58
|
+
)
|
|
59
|
+
# `opt_i_set` (io.c) through `argf_inplace_mode_set`: nil or false turns in-place mode off; anything else
|
|
60
|
+
# goes through `StringValueCStr`. `$-i = 1` raises "no implicit conversion of Integer into String".
|
|
61
|
+
INPLACE_MODE = Contract.new(
|
|
62
|
+
classes: %w[String NilClass FalseClass].freeze, conversion: :to_str,
|
|
63
|
+
accepts: "a String, nil, false, or an object with `to_str'"
|
|
64
|
+
)
|
|
65
|
+
# `stdout_setter` / `stderr_setter` (io.c) call `must_respond_to(id_write, …)`, which asks `respond_to?` and so
|
|
66
|
+
# honours `respond_to_missing?`. `$stdout = 1` raises "$stdout must have write method, Integer given".
|
|
67
|
+
# ADR-117 WD2: this `_Writer` requirement is the only contract a stream write is checked against.
|
|
68
|
+
WRITER = Contract.new(classes: [].freeze, conversion: :write, accepts: "an object that responds to `write'")
|
|
69
|
+
|
|
70
|
+
CONTRACTS = {
|
|
71
|
+
"$/": STRING_OR_NIL,
|
|
72
|
+
"$-0": STRING_OR_NIL,
|
|
73
|
+
"$,": STRING_OR_NIL,
|
|
74
|
+
"$\\": STRING_OR_NIL,
|
|
75
|
+
"$;": FIELD_SEPARATOR,
|
|
76
|
+
"$-F": FIELD_SEPARATOR,
|
|
77
|
+
"$~": MATCH_DATA,
|
|
78
|
+
"$0": PROGRAM_NAME,
|
|
79
|
+
"$PROGRAM_NAME": PROGRAM_NAME,
|
|
80
|
+
"$.": LINE_NUMBER,
|
|
81
|
+
"$-i": INPLACE_MODE,
|
|
82
|
+
"$stdout": WRITER,
|
|
83
|
+
"$>": WRITER,
|
|
84
|
+
"$stderr": WRITER
|
|
85
|
+
}.freeze
|
|
86
|
+
|
|
87
|
+
# The specials whose setter is `rb_gvar_readonly_setter` — declared with `rb_define_readonly_variable`, as a
|
|
88
|
+
# virtual variable without a setter, or with that setter by name. Any write raises `NameError`
|
|
89
|
+
# ("$! is a read-only variable"), whatever the value.
|
|
90
|
+
#
|
|
91
|
+
# - `$!` (eval.c); `$$` and `$?` (process.c).
|
|
92
|
+
# - `$<`, `$FILENAME` and `$*` (io.c, ARGF).
|
|
93
|
+
# - `$:` / `$LOAD_PATH` / `$-I`, `$"` / `$LOADED_FEATURES` (load.c).
|
|
94
|
+
# - `$-W`, and the `-p` / `-l` / `-a` switches `$-p`, `$-l` and `$-a` (ruby.c).
|
|
95
|
+
READ_ONLY = Set[
|
|
96
|
+
:$!, :$$, :$?, :$<, :$FILENAME, :$*, :$:, :$LOAD_PATH, :$-I, :$", :$LOADED_FEATURES,
|
|
97
|
+
:$-W, :$-p, :$-l, :$-a
|
|
98
|
+
].freeze
|
|
99
|
+
|
|
100
|
+
# The literal nodes `global.write-type-mismatch` judges, by the core class of the object each evaluates to.
|
|
101
|
+
# An interpolated String, Symbol or Regexp is still a new instance of that class.
|
|
102
|
+
LITERAL_CLASSES = {
|
|
103
|
+
Prism::IntegerNode => "Integer", Prism::FloatNode => "Float", Prism::RationalNode => "Rational",
|
|
104
|
+
Prism::ImaginaryNode => "Complex", Prism::StringNode => "String", Prism::InterpolatedStringNode => "String",
|
|
105
|
+
Prism::SymbolNode => "Symbol", Prism::InterpolatedSymbolNode => "Symbol", Prism::ArrayNode => "Array",
|
|
106
|
+
Prism::HashNode => "Hash", Prism::RegularExpressionNode => "Regexp",
|
|
107
|
+
Prism::InterpolatedRegularExpressionNode => "Regexp", Prism::NilNode => "NilClass",
|
|
108
|
+
Prism::TrueNode => "TrueClass", Prism::FalseNode => "FalseClass"
|
|
109
|
+
}.freeze
|
|
110
|
+
|
|
111
|
+
# How the diagnostic names a literal of each class.
|
|
112
|
+
LITERAL_DESCRIPTIONS = { "NilClass" => "nil", "TrueClass" => "true", "FalseClass" => "false" }.freeze
|
|
113
|
+
|
|
114
|
+
module_function
|
|
115
|
+
|
|
116
|
+
# The setter contract a write of `name` is checked against, or nil when its setter accepts every value or the
|
|
117
|
+
# global is no special.
|
|
118
|
+
def contract_for(name)
|
|
119
|
+
CONTRACTS[name]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# The core class of the object `node` evaluates to when it is a literal ({LITERAL_CLASSES}), looking
|
|
123
|
+
# through parentheses around a single expression, or nil for any other expression.
|
|
124
|
+
def literal_class(node)
|
|
125
|
+
while node.is_a?(Prism::ParenthesesNode)
|
|
126
|
+
body = node.body
|
|
127
|
+
return nil unless body.is_a?(Prism::StatementsNode) && body.body.size == 1
|
|
128
|
+
|
|
129
|
+
node = body.body.first
|
|
130
|
+
end
|
|
131
|
+
LITERAL_CLASSES[node.class]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def literal_description(class_name)
|
|
135
|
+
LITERAL_DESCRIPTIONS.fetch(class_name) do
|
|
136
|
+
"#{class_name.start_with?('A', 'E', 'I', 'O', 'U') ? 'an' : 'a'} #{class_name} literal"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def read_only?(name)
|
|
141
|
+
READ_ONLY.include?(name)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "prism"
|
|
4
4
|
|
|
5
5
|
require_relative "../../source/node_children"
|
|
6
|
+
require_relative "../../inference/narrowing"
|
|
6
7
|
require_relative "inferred_param_guard"
|
|
7
8
|
|
|
8
9
|
module Rigor
|
|
@@ -29,6 +30,12 @@ module Rigor
|
|
|
29
30
|
# The false-positive envelope mirrors `flow.always-truthy-condition`: clauses inside loops / blocks are
|
|
30
31
|
# skipped (mutation tracking through those is incomplete), and the rule reads the engine's own
|
|
31
32
|
# narrowing rather than recomputing it, so the diagnostic and the body typing can never diverge.
|
|
33
|
+
#
|
|
34
|
+
# Issue #1429 — a `:disjoint` clause is not reported when its `Bot` rests on a `Nominal` subject member whose
|
|
35
|
+
# class is disjoint from a condition's ({Inference::Narrowing.disjoint_nominal_guard?}): the `when C` or `in C`
|
|
36
|
+
# is code evidence that the subject can hold such an object (`io = STDOUT; case io when StringIO`), and Rigor's
|
|
37
|
+
# reading of the subject's type is what the evidence contradicts. The body scope stays `Bot`, so no call on the
|
|
38
|
+
# subject in it is checked. A subject that is a literal carrier (`x = 1; case x when String`) is still reported.
|
|
32
39
|
class UnreachableClauseCollector
|
|
33
40
|
LOOP_OR_BLOCK_NODE_CLASSES = [
|
|
34
41
|
Prism::WhileNode, Prism::UntilNode, Prism::ForNode, Prism::BlockNode
|
|
@@ -139,13 +146,30 @@ module Rigor
|
|
|
139
146
|
return if scope.nil?
|
|
140
147
|
return unless scope.local(subject_name).is_a?(Type::Bot)
|
|
141
148
|
|
|
149
|
+
kind = when_clause_kind(clause, subject_name)
|
|
150
|
+
return if kind == :disjoint &&
|
|
151
|
+
disjoint_nominal_subject?(@scope_index[clause.conditions.first], subject_name, clause.conditions)
|
|
152
|
+
|
|
142
153
|
@results << Result.new(
|
|
143
154
|
clause: clause, body: clause.statements, subject_name: subject_name,
|
|
144
155
|
condition_source: clause.conditions.map(&:slice).join(", "),
|
|
145
|
-
kind:
|
|
156
|
+
kind: kind, keyword: "when"
|
|
146
157
|
)
|
|
147
158
|
end
|
|
148
159
|
|
|
160
|
+
# True when the subject, as `entry` binds it, holds a non-literal `Nominal` whose class the class one of
|
|
161
|
+
# `patterns` names is disjoint from (issue #1429, see the class comment).
|
|
162
|
+
def disjoint_nominal_subject?(entry, subject_name, patterns)
|
|
163
|
+
subject_type = entry&.local(subject_name)
|
|
164
|
+
return false if subject_type.nil?
|
|
165
|
+
|
|
166
|
+
patterns.any? do |condition|
|
|
167
|
+
class_name = Inference::Narrowing.lexical_class_name(condition, entry)
|
|
168
|
+
class_name &&
|
|
169
|
+
Inference::Narrowing.disjoint_nominal_guard?(subject_type, class_name, environment: entry.environment)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
149
173
|
# A dead `when` is `:prior_exhaustion` when the subject was already narrowed to `bot` BEFORE this
|
|
150
174
|
# clause (an earlier clause covered it) — read from the entry scope the engine recorded on the
|
|
151
175
|
# clause's first condition node (ADR-47 WD2). Otherwise the subject was still concrete entering the
|
|
@@ -170,13 +194,23 @@ module Rigor
|
|
|
170
194
|
return if scope.nil?
|
|
171
195
|
return unless scope.local(subject_name).is_a?(Type::Bot)
|
|
172
196
|
|
|
197
|
+
kind = in_clause_kind(clause, subject_name)
|
|
198
|
+
return if kind == :disjoint && disjoint_nominal_pattern?(clause, subject_name)
|
|
199
|
+
|
|
173
200
|
@results << Result.new(
|
|
174
201
|
clause: clause, body: clause.statements, subject_name: subject_name,
|
|
175
202
|
condition_source: clause.pattern.slice,
|
|
176
|
-
kind:
|
|
203
|
+
kind: kind, keyword: "in"
|
|
177
204
|
)
|
|
178
205
|
end
|
|
179
206
|
|
|
207
|
+
# {#disjoint_nominal_subject?} for a bare class pattern, `in C` or `in C => x`.
|
|
208
|
+
def disjoint_nominal_pattern?(clause, subject_name)
|
|
209
|
+
pattern = clause.pattern
|
|
210
|
+
pattern = pattern.value if pattern.is_a?(Prism::CapturePatternNode)
|
|
211
|
+
disjoint_nominal_subject?(@scope_index[clause.pattern], subject_name, [pattern])
|
|
212
|
+
end
|
|
213
|
+
|
|
180
214
|
def in_clause_kind(clause, subject_name)
|
|
181
215
|
entry = @scope_index[clause.pattern]
|
|
182
216
|
return :prior_exhaustion if entry && entry.local(subject_name).is_a?(Type::Bot)
|