rigortype 0.3.8 → 0.3.9
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/data/capability_roles/capability_roles.rbs +36 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +37 -4
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +14 -1
- data/docs/handbook/11-sig-gen.md +72 -1
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +32 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +11 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +6 -1
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/plugins/rigor-actionpack.md +18 -0
- data/docs/manual/plugins/rigor-activerecord.md +28 -0
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
- data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
- data/lib/rigor/analysis/baseline.rb +2 -2
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +200 -32
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +6 -1
- data/lib/rigor/analysis/incremental.rb +16 -0
- data/lib/rigor/analysis/incremental_session.rb +81 -22
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +4 -1
- data/lib/rigor/analysis/run_cache_key.rb +1 -1
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +177 -26
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
- data/lib/rigor/analysis/runner/pool_coordinator.rb +256 -88
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +133 -35
- data/lib/rigor/analysis/worker_session.rb +9 -10
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +155 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +4 -4
- data/lib/rigor/builtins/static_return_refinements.rb +5 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +58 -20
- data/lib/rigor/cache/engine_source.rb +4 -4
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +40 -6
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +79 -11
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +77 -32
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +3 -3
- data/lib/rigor/cli/docs_command.rb +2 -2
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +1 -1
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +2 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +2 -2
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +36 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +10 -6
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +68 -6
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +6 -6
- data/lib/rigor/configuration.rb +43 -4
- data/lib/rigor/effects/attribution.rb +1 -3
- data/lib/rigor/effects/config_envelopes.rb +6 -8
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +0 -1
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +8 -8
- data/lib/rigor/effects/envelope_index.rb +5 -8
- data/lib/rigor/effects/framework_units.rb +4 -6
- data/lib/rigor/effects/identity.rb +4 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/method_key.rb +1 -1
- data/lib/rigor/effects/plugin_facts.rb +4 -6
- data/lib/rigor/effects/propagator.rb +2 -3
- data/lib/rigor/effects/scanner.rb +1 -1
- data/lib/rigor/effects/signature_sources.rb +3 -5
- data/lib/rigor/effects/snapshot.rb +10 -10
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/unit_scan.rb +10 -10
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +2 -2
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +51 -4
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +388 -59
- data/lib/rigor/environment.rb +105 -20
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/inference/acceptance.rb +153 -8
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/body_fixpoint.rb +5 -5
- data/lib/rigor/inference/budget_trace.rb +1 -5
- data/lib/rigor/inference/captured_locals.rb +2 -3
- data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +168 -0
- data/lib/rigor/inference/expression_typer.rb +763 -143
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +4 -7
- data/lib/rigor/inference/index_write_widening.rb +1 -5
- data/lib/rigor/inference/macro_block_self_type.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +12 -13
- data/lib/rigor/inference/method_parameter_binder.rb +4 -6
- data/lib/rigor/inference/multi_target_binder.rb +1 -3
- data/lib/rigor/inference/mutation_rejoin.rb +168 -0
- data/lib/rigor/inference/mutation_widening.rb +100 -51
- data/lib/rigor/inference/narrowing.rb +299 -46
- data/lib/rigor/inference/optimistic_origin.rb +1 -7
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_patched_methods.rb +2 -2
- data/lib/rigor/inference/project_patched_scanner.rb +3 -3
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +3 -3
- data/lib/rigor/inference/refinement_mutation.rb +72 -0
- data/lib/rigor/inference/scope_indexer.rb +688 -151
- data/lib/rigor/inference/statement_evaluator.rb +221 -50
- data/lib/rigor/inference/string_mutation.rb +60 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +1 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +2 -2
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +2 -5
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/plugin/base.rb +90 -21
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +7 -7
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +31 -12
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +23 -1
- data/lib/rigor/plugin/registry.rb +42 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +11 -11
- data/lib/rigor/protection/kill_signature.rb +2 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +6 -9
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +61 -21
- data/lib/rigor/reflection/constant_path.rb +126 -0
- data/lib/rigor/reflection.rb +26 -10
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +30 -0
- data/lib/rigor/scope.rb +203 -4
- data/lib/rigor/sig_gen/generator.rb +115 -22
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/observation_collector.rb +17 -15
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +4 -5
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/writer.rb +23 -6
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +2 -5
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +68 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +5 -5
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +3 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/environment.rbs +5 -0
- data/sig/rigor/inference.rbs +3 -10
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/manifest.rbs +2 -1
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +24 -3
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
- data/sig/rigor/type.rbs +27 -1
- data/sig/rigor.rbs +1 -0
- data/skills/rigor-ask/SKILL.md +8 -6
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
- data/skills/rigor-project-init/SKILL.md +6 -3
- data/skills/rigor-project-init/references/02-configure.md +10 -6
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-type-oracle/SKILL.md +228 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
- metadata +26 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 857b76366640c2aa188649c7fe6af483d599bc05e5f185c4d8285b093c092fdd
|
|
4
|
+
data.tar.gz: 8e281f8668221704dd74c0b8d942830f6e58b252135db54142a70543bcfdf839
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aaea4bc6f1073c80307375365f111ba58c198cef0852a63abe5476c1ffa937754b0e45a506b69a052e685724d24fa44689f4f8bfb375f902b7e53521eea7ae4d
|
|
7
|
+
data.tar.gz: 846d488914e0046afce19772fa8295ab99b068acf8e94a04d57590f9feb7718ebca29858ca78f47435a1542e7c1c8cbf000ca983619b3ebdb684650f8c8fc7aa
|
data/README.md
CHANGED
|
@@ -231,7 +231,7 @@ rigor docs --list # list every bundled page
|
|
|
231
231
|
|
|
232
232
|
## Status
|
|
233
233
|
|
|
234
|
-
Current release: **`v0.3.
|
|
234
|
+
Current release: **`v0.3.9`** (2026-09-12) — on the
|
|
235
235
|
`0.3.x` line, continuing the evaluation era opened by `v0.2.0`, the first
|
|
236
236
|
publicly-announced (general / evaluation) release. The line publishes
|
|
237
237
|
an enumerated [compatibility surface](docs/compatibility.md) as a
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Rigor's capability-role catalog (ADR-1 § "Capability roles",
|
|
2
|
+
# docs/type-specification/structural-interfaces-and-object-shapes.md).
|
|
3
|
+
#
|
|
4
|
+
# These are the roles Rigor itself ships, as opposed to the stdlib interfaces (`_Reader`, `_ToStr`, …)
|
|
5
|
+
# it reuses from upstream RBS. They exist so a `%a{rigor:v1:conforms-to _ClosableStream}` directive
|
|
6
|
+
# written by following the specification resolves in a default run instead of failing soft.
|
|
7
|
+
#
|
|
8
|
+
# Parameter and return types are deliberately the loosest honest reading: a role says which members a
|
|
9
|
+
# value answers, and narrowing them here would reject conforming classes rather than describe them.
|
|
10
|
+
|
|
11
|
+
# The specification's running example: the one-member lifetime contract.
|
|
12
|
+
interface _Closable
|
|
13
|
+
def close: () -> untyped
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
interface _RewindableStream
|
|
17
|
+
def read: (*untyped) -> String?
|
|
18
|
+
|
|
19
|
+
def rewind: () -> untyped
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
interface _ClosableStream
|
|
23
|
+
def close: () -> untyped
|
|
24
|
+
|
|
25
|
+
def closed?: () -> bool
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
interface _FileDescriptorBacked
|
|
29
|
+
def fileno: () -> Integer
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Not generic: `_Callable[**A, R]` as ADR-1 first drafted it is not RBS grammar (`**A` has no meaning
|
|
33
|
+
# in a type-parameter list). A generic form is a future extension, gated on RBS gaining the syntax.
|
|
34
|
+
interface _Callable
|
|
35
|
+
def call: (*untyped) -> untyped
|
|
36
|
+
end
|
|
@@ -68,6 +68,31 @@ class Object
|
|
|
68
68
|
|
|
69
69
|
# `core_ext/object/inclusion` — `x.in?([a, b])` / `x.in?(1..10)`.
|
|
70
70
|
def in?: (untyped) -> bool
|
|
71
|
+
|
|
72
|
+
# Issue #673 — `core_ext/object/to_query`, `core_ext/object/duplicable` and
|
|
73
|
+
# `core_ext/object/instance_variables` all hang off `Object`, so every
|
|
74
|
+
# RBS-known receiver reaches them; declaring them only on `Hash` (as
|
|
75
|
+
# `to_param` / `to_query` were) or only on `NilClass` (as `duplicable?` /
|
|
76
|
+
# `instance_values` were) left `"x".to_param`, `1.duplicable?` and friends a
|
|
77
|
+
# `call.undefined-method` on ordinary Rails code.
|
|
78
|
+
#
|
|
79
|
+
# `to_param` is `to_s`; `to_query(key)` interpolates two `CGI.escape` results.
|
|
80
|
+
# `key` is `untyped` because the body only calls `to_param` on it, and both
|
|
81
|
+
# `:name` and `"user[name]"` are ordinary call sites.
|
|
82
|
+
def to_param: () -> String
|
|
83
|
+
def to_query: (untyped key) -> String
|
|
84
|
+
|
|
85
|
+
# `bool` and not `true`: `Object#duplicable?` answers true, but the
|
|
86
|
+
# `Singleton` (and, on a Ruby where methods are not duplicable, `Method` /
|
|
87
|
+
# `UnboundMethod`) overrides answer false. `-> true` on the universal
|
|
88
|
+
# receiver would make every `if x.duplicable?` an always-truthy branch —
|
|
89
|
+
# a false positive on the only shape anybody writes.
|
|
90
|
+
def duplicable?: () -> bool
|
|
91
|
+
|
|
92
|
+
# Keys without the leading `@` for `instance_values`, with it for
|
|
93
|
+
# `instance_variable_names`.
|
|
94
|
+
def instance_values: () -> Hash[String, untyped]
|
|
95
|
+
def instance_variable_names: () -> Array[String]
|
|
71
96
|
end
|
|
72
97
|
|
|
73
98
|
# `nil.blank?` / `nil.present?` / `nil.try` are the most frequent
|
|
@@ -80,16 +105,24 @@ class NilClass
|
|
|
80
105
|
def try!: (*untyped) -> nil
|
|
81
106
|
def duplicable?: () -> true
|
|
82
107
|
def instance_values: () -> Hash[String, untyped]
|
|
108
|
+
# `core_ext/object/to_query` overrides `to_param` on the three singleton
|
|
109
|
+
# values to return `self` rather than `Object#to_param`'s `to_s`, so the
|
|
110
|
+
# `Object` row above would carry a wrong return here. `NilClass#to_query`
|
|
111
|
+
# is its own method (`CGI.escape(key.to_param)`), still a String.
|
|
112
|
+
def to_param: () -> nil
|
|
113
|
+
def to_query: (untyped key) -> String
|
|
83
114
|
end
|
|
84
115
|
|
|
85
116
|
class TrueClass
|
|
86
117
|
def blank?: () -> false
|
|
87
118
|
def present?: () -> true
|
|
119
|
+
def to_param: () -> true
|
|
88
120
|
end
|
|
89
121
|
|
|
90
122
|
class FalseClass
|
|
91
123
|
def blank?: () -> true
|
|
92
124
|
def present?: () -> false
|
|
125
|
+
def to_param: () -> false
|
|
93
126
|
end
|
|
94
127
|
|
|
95
128
|
# ---------------------------------------------------------------
|
|
@@ -263,6 +296,16 @@ end
|
|
|
263
296
|
# `spec/rigor/environment/activesupport_overlay_parity_spec.rb`); every method not listed here stays
|
|
264
297
|
# undeclared and silently `Dynamic` rather than guessed.
|
|
265
298
|
module ActiveSupport
|
|
299
|
+
# Issue #534 item 7 — the `ActiveSupport::Concern` twin of the plugin's declaration. The plugin file
|
|
300
|
+
# carries the reasoning; the short version is that naming the module makes `extend ActiveSupport::Concern`
|
|
301
|
+
# resolve, and the three members have to come with it so that `included do … end` does not become a false
|
|
302
|
+
# `call.undefined-method` on a now-RBS-known module.
|
|
303
|
+
module Concern
|
|
304
|
+
def included: (?untyped base) ?{ (?) -> untyped } -> untyped
|
|
305
|
+
def prepended: (?untyped base) ?{ (?) -> untyped } -> untyped
|
|
306
|
+
def class_methods: () { (?) -> untyped } -> untyped
|
|
307
|
+
end
|
|
308
|
+
|
|
266
309
|
class Duration
|
|
267
310
|
# The `ago` family (issue #659). `since(time = ::Time.current)` and `ago(time = ::Time.current)` are
|
|
268
311
|
# the only two real methods; `from_now` / `after` alias the first and `until` / `before` the second,
|
|
@@ -272,23 +315,25 @@ module ActiveSupport
|
|
|
272
315
|
# `GEM_OVERLAY_OPEN_RECEIVERS` cannot suppress since it protects a Duration receiver, not a `Time`
|
|
273
316
|
# one. #658 declared that surface and #670 the `Date` / `DateTime` twins, so the chains resolve now.
|
|
274
317
|
#
|
|
275
|
-
#
|
|
276
|
-
# `
|
|
277
|
-
#
|
|
278
|
-
#
|
|
279
|
-
#
|
|
280
|
-
#
|
|
281
|
-
|
|
318
|
+
# Since #673 the no-argument form answers `ActiveSupport::TimeWithZone`, the class these really
|
|
319
|
+
# build off `::Time.current` under a zone, declared below as a `::Time` SUBCLASS. That keeps every
|
|
320
|
+
# `Time`-typed chain and parameter working while `1.hour.ago.time_zone` stops being a false
|
|
321
|
+
# `call.undefined-method`. A `Time | ActiveSupport::TimeWithZone` union was measured and rejected —
|
|
322
|
+
# it types every downstream call `Dynamic[top]`. The with-an-argument form stays `untyped` because
|
|
323
|
+
# the runtime return depends on the argument's class AND on whether the duration carries a
|
|
324
|
+
# date-scale part (`(1.month).ago(Date.today)` answers a `Date`; `(30.minutes).ago(Date.today)` a
|
|
325
|
+
# `TimeWithZone`).
|
|
326
|
+
def ago: () -> TimeWithZone
|
|
282
327
|
| (untyped time) -> untyped
|
|
283
|
-
def until: () ->
|
|
328
|
+
def until: () -> TimeWithZone
|
|
284
329
|
| (untyped time) -> untyped
|
|
285
|
-
def before: () ->
|
|
330
|
+
def before: () -> TimeWithZone
|
|
286
331
|
| (untyped time) -> untyped
|
|
287
|
-
def since: () ->
|
|
332
|
+
def since: () -> TimeWithZone
|
|
288
333
|
| (untyped time) -> untyped
|
|
289
|
-
def from_now: () ->
|
|
334
|
+
def from_now: () -> TimeWithZone
|
|
290
335
|
| (untyped time) -> untyped
|
|
291
|
-
def after: () ->
|
|
336
|
+
def after: () -> TimeWithZone
|
|
292
337
|
| (untyped time) -> untyped
|
|
293
338
|
|
|
294
339
|
# `to_i` / its `in_seconds` alias return `@value.to_i` — always Integer.
|
|
@@ -315,6 +360,54 @@ module ActiveSupport
|
|
|
315
360
|
# Float` is what stays honest without costing the common `parts[:days] * 2` chain anything.
|
|
316
361
|
def parts: () -> Hash[Symbol, Integer | Float]
|
|
317
362
|
end
|
|
363
|
+
|
|
364
|
+
# ---------------------------------------------------------------
|
|
365
|
+
# ActiveSupport::TimeWithZone — issue #673
|
|
366
|
+
# ---------------------------------------------------------------
|
|
367
|
+
#
|
|
368
|
+
# `Time.current` and the `Duration#ago` family really answer a `TimeWithZone`
|
|
369
|
+
# under a zone, and this class was not modelled at all, so the four readers a
|
|
370
|
+
# TWZ has and a `Time` does not — `time_zone`, `time`, `period`,
|
|
371
|
+
# `comparable_time` — were `call.undefined-method` on every `Time.current`
|
|
372
|
+
# receiver. Declaring them on `Time` would have been wrong: a plain `Time`
|
|
373
|
+
# genuinely lacks them.
|
|
374
|
+
#
|
|
375
|
+
# `< ::Time` rather than a `Time | TimeWithZone` union at the producers. The
|
|
376
|
+
# union was measured during #632 and rejected — it types every downstream
|
|
377
|
+
# call `Dynamic[top]`. Inheritance is the cheaper shape AND the honest one
|
|
378
|
+
# here: Rails overrides `TimeWithZone#is_a?` to answer true for `::Time`, so
|
|
379
|
+
# `Time` is the class Rails itself tells callers a TWZ is, and TWZ's
|
|
380
|
+
# `method_missing` forwards every name it does not define to the wrapped
|
|
381
|
+
# `::Time` — which is exactly what inheriting `Time`'s surface models. A TWZ
|
|
382
|
+
# therefore also satisfies every `Time`-typed parameter, and a chain through
|
|
383
|
+
# `Time.current` keeps resolving instead of collapsing.
|
|
384
|
+
#
|
|
385
|
+
# The cost, taken deliberately: `Time.current` answers a plain `Time` when
|
|
386
|
+
# `Time.zone` is nil, so `Time.current.time_zone` in a zone-less
|
|
387
|
+
# activesupport-only project is a real `NoMethodError` this now types. Rails
|
|
388
|
+
# sets `config.time_zone` ("UTC") by default, so the shape is rare, and the
|
|
389
|
+
# false positive it replaces fired on correct Rails code — which AGENTS.md
|
|
390
|
+
# ranks above the worst-case static reading.
|
|
391
|
+
class TimeWithZone < ::Time
|
|
392
|
+
# `Time`'s own `initialize` takes the `(year, month, …)` parts; inheriting
|
|
393
|
+
# it would make `TimeWithZone.new(utc, zone)` — how Rails constructs one —
|
|
394
|
+
# a `call.argument-type-mismatch`. `untyped` because the zone and period
|
|
395
|
+
# carriers (`ActiveSupport::TimeZone`, `TZInfo::TimezonePeriod`) are not
|
|
396
|
+
# declared anywhere in this overlay.
|
|
397
|
+
def initialize: (untyped utc_time, untyped time_zone, ?untyped local_time, ?untyped period) -> void
|
|
398
|
+
|
|
399
|
+
# The `attr_reader`; an `ActiveSupport::TimeZone`, undeclared here.
|
|
400
|
+
def time_zone: () -> untyped
|
|
401
|
+
|
|
402
|
+
# The wall-clock `::Time` in the zone. `utc` / `getutc` / `getgm` /
|
|
403
|
+
# `gmtime` are `Time`'s own names and stay inherited; `comparable_time` is
|
|
404
|
+
# the alias of `utc` that `Time` does not carry.
|
|
405
|
+
def time: () -> ::Time
|
|
406
|
+
def comparable_time: () -> ::Time
|
|
407
|
+
|
|
408
|
+
# A `TZInfo::TimezonePeriod`, undeclared here.
|
|
409
|
+
def period: () -> untyped
|
|
410
|
+
end
|
|
318
411
|
end
|
|
319
412
|
|
|
320
413
|
# ---------------------------------------------------------------
|
|
@@ -322,7 +415,10 @@ end
|
|
|
322
415
|
# ---------------------------------------------------------------
|
|
323
416
|
|
|
324
417
|
class Time
|
|
325
|
-
|
|
418
|
+
# `Time.zone ? Time.zone.now : Time.now` — an `ActiveSupport::TimeWithZone` under a zone, which
|
|
419
|
+
# since #673 is declared as a `::Time` subclass so the four TWZ-only readers resolve without the
|
|
420
|
+
# rest of the chain losing its `Time` typing. See the class's own note for the zone-less cost.
|
|
421
|
+
def self.current: () -> ActiveSupport::TimeWithZone
|
|
326
422
|
def self.zone: () -> untyped # ActiveSupport::TimeZone | nil
|
|
327
423
|
def self.zone=: (String | Symbol | untyped) -> untyped
|
|
328
424
|
|
|
@@ -25,7 +25,7 @@ n = ARGV.size
|
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
A vanilla checker says: `n: Integer`. Rigor says:
|
|
28
|
-
`n:
|
|
28
|
+
`n: Integer[0..]` (a non-negative integer — `Array#size` cannot
|
|
29
29
|
return a negative count).
|
|
30
30
|
|
|
31
31
|
The reason this matters: most diagnostics Rigor wants to fire
|
|
@@ -40,7 +40,7 @@ or narrow (`Constant<3>`, `non-empty-string`). The rest of
|
|
|
40
40
|
this chapter is the carrier zoo.
|
|
41
41
|
|
|
42
42
|
One note on notation before the zoo: angle brackets hold a
|
|
43
|
-
concrete value or bound — `Constant<3>`, `
|
|
43
|
+
concrete value or bound — `Constant<3>`, `Integer[0..]` —
|
|
44
44
|
while square brackets hold type parameters, exactly as in RBS
|
|
45
45
|
— `Nominal[String]`, `Hash[K, V]`, `Dynamic[top]`. A type
|
|
46
46
|
parameter names the type of a *part*: `Hash[K, V]` is a Hash
|
|
@@ -124,27 +124,30 @@ nominal carrier or `Dynamic[top]`.
|
|
|
124
124
|
|
|
125
125
|
Some integer-valued expressions produce a known range without
|
|
126
126
|
producing a single literal value. Rigor describes those with
|
|
127
|
-
`Type::IntegerRange`, displayed as `
|
|
127
|
+
`Type::IntegerRange`, displayed as `Integer[…]` around the Ruby
|
|
128
|
+
range literal that covers the values, `Integer[1..10]`:
|
|
128
129
|
|
|
129
130
|
```ruby
|
|
130
|
-
n = ARGV.size #=> dump_type:
|
|
131
|
-
m = n + 1 #=> dump_type:
|
|
132
|
-
double = n * 2 #=> dump_type:
|
|
131
|
+
n = ARGV.size #=> dump_type: Integer[0..]
|
|
132
|
+
m = n + 1 #=> dump_type: Integer[1..]
|
|
133
|
+
double = n * 2 #=> dump_type: Integer[0..]
|
|
133
134
|
```
|
|
134
135
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
The literal means what it means in Ruby: `Integer[0..]` is
|
|
137
|
+
every integer `(0..).cover?` accepts, an endless range is
|
|
138
|
+
unbounded above, a beginless one (`Integer[..-1]`) is unbounded
|
|
139
|
+
below, and `Integer[1...10]` is written back as `Integer[1..9]`
|
|
140
|
+
because for integers the two are the same set. Multiplication
|
|
141
|
+
preserves the floor, so `n * 2` stays `Integer[0..]`.
|
|
139
142
|
|
|
140
143
|
A handful of common ranges have shorter names:
|
|
141
144
|
|
|
142
145
|
| Spelling | Meaning |
|
|
143
146
|
| --- | --- |
|
|
144
|
-
| `positive-int` | `
|
|
145
|
-
| `non-negative-int` | `
|
|
146
|
-
| `negative-int` | `
|
|
147
|
-
| `non-positive-int` | `
|
|
147
|
+
| `positive-int` | `Integer[1..]` |
|
|
148
|
+
| `non-negative-int` | `Integer[0..]` |
|
|
149
|
+
| `negative-int` | `Integer[..-1]` |
|
|
150
|
+
| `non-positive-int` | `Integer[..0]` |
|
|
148
151
|
|
|
149
152
|
`Array#size`, `Array#length`, `Hash#size`, `String#size`, …
|
|
150
153
|
all carry `non-negative-int`. `Array#count` does too. Adding
|
|
@@ -107,7 +107,7 @@ matched member:
|
|
|
107
107
|
```ruby
|
|
108
108
|
case n
|
|
109
109
|
when 0 then :zero # Constant<0>
|
|
110
|
-
when 1..9 then :small #
|
|
110
|
+
when 1..9 then :small # Integer[1..9]
|
|
111
111
|
when 10 then :ten # Constant<10>
|
|
112
112
|
else :large # everything else
|
|
113
113
|
end
|
|
@@ -153,7 +153,7 @@ end
|
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
The `is_a?` narrowed `x` to `Integer`, then the integer
|
|
156
|
-
comparison narrowed it further to `
|
|
156
|
+
comparison narrowed it further to `Integer[1..]`.
|
|
157
157
|
|
|
158
158
|
## Integer comparisons
|
|
159
159
|
|
|
@@ -165,7 +165,7 @@ integer ranges:
|
|
|
165
165
|
def safe_index(arr, n)
|
|
166
166
|
return :empty if arr.empty?
|
|
167
167
|
return :out_of_range if n < 0 || n >= arr.size
|
|
168
|
-
# n:
|
|
168
|
+
# n: Integer[0..arr.size - 1] (in practice: Integer[0..]
|
|
169
169
|
# tightened against `n >= arr.size`)
|
|
170
170
|
arr.fetch(n)
|
|
171
171
|
end
|
|
@@ -176,10 +176,43 @@ Range comparisons compose with literals:
|
|
|
176
176
|
```ruby
|
|
177
177
|
n = some_input
|
|
178
178
|
if n.between?(1, 9)
|
|
179
|
-
# n:
|
|
179
|
+
# n: Integer[1..9]
|
|
180
180
|
end
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
+
## Float comparisons
|
|
184
|
+
|
|
185
|
+
A `Float` narrows on the *true* edge only. `x > 0.0` is false for
|
|
186
|
+
`NaN`, so the values that pass are a range, `Float[0.0..]`, but the
|
|
187
|
+
values that fail are everything else *plus* `NaN`, which no range
|
|
188
|
+
spells, so the else branch keeps `Float`:
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
x = Float(input)
|
|
192
|
+
if x > 0.0
|
|
193
|
+
# x: Float[0.0..]
|
|
194
|
+
else
|
|
195
|
+
# x: Float
|
|
196
|
+
end
|
|
197
|
+
if x < 1.0
|
|
198
|
+
# x: Float[...1.0] (Ruby's exclusive end)
|
|
199
|
+
end
|
|
200
|
+
if x.between?(0.0, 1.0)
|
|
201
|
+
# x: Float[0.0..1.0]
|
|
202
|
+
end
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`x.nan?` narrows its *false* edge to `non-nan-float`, and
|
|
206
|
+
`x.finite?` its *true* edge to `finite-float`. The range is the
|
|
207
|
+
one Ruby's `Range#cover?` would accept, so `Float[0.0..]` contains
|
|
208
|
+
`Float::INFINITY` and never `Float::NAN`.
|
|
209
|
+
|
|
210
|
+
Some calls produce a bounded Float on their own: `rand(0.0...1.0)`
|
|
211
|
+
and `Random.rand(1.0..2.0)` are the range they name, `Math.sqrt(n)` of a `non-negative-int` is
|
|
212
|
+
`Float[0.0..]`, and `x.clamp(0.0, 1.0)` or `x.clamp(0.0..1.0)` on a
|
|
213
|
+
bounded `x` is the bracket. A bounded Float knows it is not `NaN`,
|
|
214
|
+
so `u.nan?` folds to `false` and `u.round` to an integer range.
|
|
215
|
+
|
|
183
216
|
## Predicate methods on refinements
|
|
184
217
|
|
|
185
218
|
Rigor recognises a small set of "type-carrier predicate
|
|
@@ -21,7 +21,7 @@ first one that produces a result:
|
|
|
21
21
|
2. **Shape dispatch.** The receiver carries a `Tuple` /
|
|
22
22
|
`HashShape` / `IntegerRange` / refinement and the method
|
|
23
23
|
has a per-shape rule. `Tuple[A, B, C].size` →
|
|
24
|
-
`Constant<3>`; `
|
|
24
|
+
`Constant<3>`; `Integer[0..].zero?` → `Constant<true> |
|
|
25
25
|
Constant<false>`.
|
|
26
26
|
3. **RBS dispatch.** The class has an RBS sig for the method.
|
|
27
27
|
Argument types are checked against the parameter contract
|
data/docs/handbook/06-classes.md
CHANGED
|
@@ -113,11 +113,11 @@ class Counter
|
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
def bump
|
|
116
|
-
@count += 1 # @count rebound to
|
|
116
|
+
@count += 1 # @count rebound to Integer[1..]
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
def value
|
|
120
|
-
@count #
|
|
120
|
+
@count # Integer[0..] (union of seen writes)
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
123
|
```
|
|
@@ -128,7 +128,8 @@ A short reference:
|
|
|
128
128
|
| Family | Names |
|
|
129
129
|
| --- | --- |
|
|
130
130
|
| Empty / non-empty | `non-empty-string`, `non-empty-array[T]`, `non-empty-hash[K, V]` |
|
|
131
|
-
| Integer ranges | `positive-int`, `non-negative-int`, `negative-int`, `non-positive-int`, `non-zero-int`, `
|
|
131
|
+
| Integer ranges | `positive-int`, `non-negative-int`, `negative-int`, `non-positive-int`, `non-zero-int`, `Integer[min..max]` |
|
|
132
|
+
| Float ranges | `Float[0.0..1.0]`, `Float[0.0...1.0]`, `Float[0.0..]`, `non-nan-float`, `finite-float` |
|
|
132
133
|
| String predicates | `lowercase-string`, `uppercase-string`, `numeric-string`, `decimal-int-string`, `octal-int-string`, `hex-int-string`, `literal-string` |
|
|
133
134
|
| Paired complements | `non-lowercase-string`, `non-uppercase-string`, `non-numeric-string` |
|
|
134
135
|
| Composed | `non-empty-lowercase-string`, `non-empty-uppercase-string`, `non-empty-literal-string` |
|
|
@@ -465,6 +466,18 @@ Notes:
|
|
|
465
466
|
`source-rbs-synthesis-failed` `:info` diagnostic; the file
|
|
466
467
|
falls back to no inline-RBS contribution and analysis
|
|
467
468
|
continues.
|
|
469
|
+
- When a method is declared **both** in `sig/` and by an
|
|
470
|
+
inline annotation, the `.rbs` wins for that one method and
|
|
471
|
+
the inline signature is dropped — reported as a
|
|
472
|
+
`source-rbs-annotation-not-honoured` `:info` naming both
|
|
473
|
+
files. Only the overlapping member stands down; every other
|
|
474
|
+
annotation in the file still binds, and the class keeps its
|
|
475
|
+
method surface. This matters while migrating in either
|
|
476
|
+
direction: rbs and Steep merge the two sources without
|
|
477
|
+
ranking them, so left to collide they fail the class's
|
|
478
|
+
definition build and every call on it — real methods and
|
|
479
|
+
typos alike — reads `Dynamic[top]`. Delete one of the two
|
|
480
|
+
declarations to make the inline one take effect.
|
|
468
481
|
|
|
469
482
|
Full plugin documentation, configuration options (including
|
|
470
483
|
the `require_magic_comment: false` host-context override the
|
data/docs/handbook/11-sig-gen.md
CHANGED
|
@@ -90,7 +90,7 @@ states:
|
|
|
90
90
|
| `new-file` | No RBS file declares the receiver class at all. |
|
|
91
91
|
| `new-method` | RBS file declares the class but not this method. |
|
|
92
92
|
| `tighter-return` | RBS file declares the method, but the inferred return is a strict subtype of the declared return. |
|
|
93
|
-
| `equivalent` |
|
|
93
|
+
| `equivalent` | Nothing for `sig-gen` to propose: the inferred return is identical, wider or unrelated, or it is a narrowing the generator declines (a literal under a wider declaration, anything under a declared `void`). Silently skipped. |
|
|
94
94
|
| `skipped` | Disqualified for one of the reasons below. |
|
|
95
95
|
|
|
96
96
|
The `sig.skipped.*` reasons are:
|
|
@@ -129,6 +129,77 @@ its reason next to the rows that did emit. In text mode a
|
|
|
129
129
|
one-line stderr summary counts the skipped methods per
|
|
130
130
|
reason instead; stdout stays paste-clean.
|
|
131
131
|
|
|
132
|
+
## Recording a gap the generator cannot close
|
|
133
|
+
|
|
134
|
+
`sig-gen --diff` answers "is this declaration what the
|
|
135
|
+
implementation proves?" for every method it can type. The
|
|
136
|
+
answers it cannot give are the interesting ones: a
|
|
137
|
+
`tighter-return` you decided not to apply, a method it
|
|
138
|
+
skipped, a declaration with no `def` behind it at all. Each
|
|
139
|
+
of those is a hand-written type, and a hand-written type
|
|
140
|
+
that nobody wrote down a reason for is indistinguishable
|
|
141
|
+
from one nobody has looked at since.
|
|
142
|
+
|
|
143
|
+
The convention Rigor uses on its own `sig/` is a line in the
|
|
144
|
+
member's RBS comment:
|
|
145
|
+
|
|
146
|
+
```rbs
|
|
147
|
+
class Registry
|
|
148
|
+
# sig-gen gap: #1234 — sig-gen types the body `untyped`
|
|
149
|
+
# (the ivar has no inferred field type yet), so this
|
|
150
|
+
# return is hand-written until it can prove one.
|
|
151
|
+
def resolve: (String name) -> Entry
|
|
152
|
+
end
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
A `void` return needs no marker. `sig-gen` never proposes a
|
|
156
|
+
value for one: `void` says the return is not part of the
|
|
157
|
+
contract, and no inference synthesizes it, so a `void`
|
|
158
|
+
declaration is authored intent the way a parameter type is
|
|
159
|
+
([ADR-14](../adr/14-rbs-sig-generation.md) § "The
|
|
160
|
+
inference-vs-RBS contradiction rule").
|
|
161
|
+
|
|
162
|
+
Neither does a declaration the body proves as a literal.
|
|
163
|
+
`sig-gen` will not propose `"bot"` for a `def describe: ()
|
|
164
|
+
-> String` whose body is the string `"bot"`: the declared
|
|
165
|
+
type is your abstraction over that body, and pinning the
|
|
166
|
+
literal would rewrite a contract every sibling class shares.
|
|
167
|
+
A method no `.rbs` declares is unaffected — the literal is
|
|
168
|
+
still the strictest thing the body proves, and that is what
|
|
169
|
+
gets written.
|
|
170
|
+
|
|
171
|
+
A comment rather than a `%a{…}` annotation, for three
|
|
172
|
+
reasons: it stays out of the `rigor:v1:` directive namespace
|
|
173
|
+
(`docs/type-specification/rbs-extended.md`), no engine path
|
|
174
|
+
reads it, and `RBS::Parser` binds it to the member, so a
|
|
175
|
+
check can find it on the AST instead of scanning lines. The
|
|
176
|
+
issue number is the load-bearing half — it points at the
|
|
177
|
+
engine work that would let the generator answer, which is
|
|
178
|
+
the [ADR-14](../adr/14-rbs-sig-generation.md) rule that a
|
|
179
|
+
gap pushing you toward hand-written RBS is the more valuable
|
|
180
|
+
signal.
|
|
181
|
+
|
|
182
|
+
Nothing in the CLI requires this. It is a convention you can
|
|
183
|
+
gate in your own suite: parse `sig/**/*.rbs`, run the
|
|
184
|
+
generator, and fail on a declaration that is neither
|
|
185
|
+
generated-equivalent nor marked. Rigor's own gate is
|
|
186
|
+
`spec/rigor/sig_gen/provenance_spec.rb`
|
|
187
|
+
([ADR-107](../adr/107-checked-types-and-typeless-comments.md)
|
|
188
|
+
G3).
|
|
189
|
+
|
|
190
|
+
One case in that list is not a marker case at all. A
|
|
191
|
+
declaration with no `def` behind it may simply describe a
|
|
192
|
+
method Ruby generates — an `attr_*`, a `Data` member, one a
|
|
193
|
+
class macro defines at load — or it may be left over from a
|
|
194
|
+
`def` that was deleted or renamed, which nothing catches,
|
|
195
|
+
because `rigor check` and Steep both ask whether the
|
|
196
|
+
implementation matches `sig/` and never the converse.
|
|
197
|
+
Rigor's gate separates the two by asking its own
|
|
198
|
+
project index and then the loaded tree
|
|
199
|
+
([#839](https://github.com/rigortype/rigor/issues/839)).
|
|
200
|
+
That check requires the code under `sig/`, so it stays a
|
|
201
|
+
repository gate: `sig-gen` in your project is unchanged.
|
|
202
|
+
|
|
132
203
|
## What method shapes the generator covers
|
|
133
204
|
|
|
134
205
|
Slice-by-slice (each shipped via a CHANGELOG entry — this
|
data/docs/handbook/README.md
CHANGED
|
@@ -105,7 +105,7 @@ choices.
|
|
|
105
105
|
conditional / mapped types do not have a Rigor analogue.
|
|
106
106
|
- [**Coming from PHPStan**](appendix-phpstan.md) — the
|
|
107
107
|
closest peer in spirit. Identical refinement vocabulary
|
|
108
|
-
(`non-empty-string`, `
|
|
108
|
+
(`non-empty-string`, `Integer[min..max]`, `numeric-string`,
|
|
109
109
|
`literal-string`), `@phpstan-assert*` ↔ `RBS::Extended`,
|
|
110
110
|
Type-Specifying Extensions ↔ plugins, baseline diffing.
|
|
111
111
|
- [**Coming from mypy / Pyright**](appendix-mypy.md) — gradual
|
|
@@ -224,7 +224,7 @@ end
|
|
|
224
224
|
| --- | --- | --- |
|
|
225
225
|
| `positive-int` | `when n > 0` | Rigor names and carries the result. |
|
|
226
226
|
| `non-empty-string` | `when s != ""` / `byte_size(s) > 0` | Rigor produces it from `unless s.empty?`. |
|
|
227
|
-
| `
|
|
227
|
+
| `Integer[1..9]` | `when x in 1..9` | Rigor's range carrier handles arbitrary bounds. |
|
|
228
228
|
| `non-empty-array[T]` | `when xs != []` | Rigor produces it from `unless arr.empty?`. |
|
|
229
229
|
| `numeric-string` | `Integer.parse/1` + match | No direct Elixir analogue. |
|
|
230
230
|
|
|
@@ -283,7 +283,7 @@ The other direction:
|
|
|
283
283
|
and Elixir's types do not fold call results to singleton
|
|
284
284
|
types this way.
|
|
285
285
|
- **Named refinement carriers.** `non-empty-string`,
|
|
286
|
-
`positive-int`, `
|
|
286
|
+
`positive-int`, `Integer[1..9]`, `numeric-string` — first-class,
|
|
287
287
|
named, and flowed onward from a guard.
|
|
288
288
|
- **Inferred object shapes and capability roles.** Beyond
|
|
289
289
|
behaviours and protocols, Rigor infers anonymous structural
|
|
@@ -243,7 +243,7 @@ carriers that encode an invariant on the value.
|
|
|
243
243
|
| --- | --- | --- |
|
|
244
244
|
| `non-empty-string` | runtime `if len(s) == 0` check | Rigor produces it from `unless s.empty?`. |
|
|
245
245
|
| `positive-int` | runtime `if n <= 0` check | Rigor narrows from `n > 0`. |
|
|
246
|
-
| `
|
|
246
|
+
| `Integer[1..9]` | runtime range check | Rigor's range carrier handles arbitrary bounds. |
|
|
247
247
|
| `numeric-string` | `strconv.Atoi` + error check | No type-level analogue in Go. |
|
|
248
248
|
| `non-empty-array[T]` | runtime `len(xs) == 0` check | Rigor produces it from `unless arr.empty?`. |
|
|
249
249
|
|
|
@@ -311,7 +311,7 @@ is deliberately minimal:
|
|
|
311
311
|
- **Constant folding through method calls.** `"foo".upcase` is
|
|
312
312
|
`Constant<"FOO">`, not `string`.
|
|
313
313
|
- **Refinements.** `non-empty-string`, `positive-int`,
|
|
314
|
-
`
|
|
314
|
+
`Integer[1..9]` — invariants on the value, no runtime check needed
|
|
315
315
|
to know them statically.
|
|
316
316
|
- **Inferred shapes beyond interfaces.** Rigor infers anonymous
|
|
317
317
|
object shapes and capability roles, not just satisfaction of a
|
|
@@ -292,7 +292,7 @@ class refinement carriers, produced automatically by narrowing.
|
|
|
292
292
|
| --- | --- | --- |
|
|
293
293
|
| `non-empty-string` | a `NonEmptyString` value class wrapping validation | Rigor produces it from `unless s.empty?`, no wrapper type. |
|
|
294
294
|
| `positive-int` | a `PositiveInt` value object, or a runtime guard | Rigor narrows from `n > 0`. |
|
|
295
|
-
| `
|
|
295
|
+
| `Integer[1..9]` | an `enum` of nine constants, or a range check | Rigor's range carrier handles arbitrary bounds without enumerating them. |
|
|
296
296
|
| `numeric-string` | `string` + `int.TryParse` discipline | No type-level analogue in either language. |
|
|
297
297
|
| `non-empty-array[T]` | a non-empty-collection value class | Rigor produces it from `unless arr.empty?`. |
|
|
298
298
|
|
|
@@ -379,7 +379,7 @@ longer than you might expect, because neither has literal types:
|
|
|
379
379
|
`Constant<"FOO">`, not `String`. Rigor catalogues which
|
|
380
380
|
built-in methods are pure and folds through them.
|
|
381
381
|
- **First-class refinements.** `non-empty-string`, `positive-int`,
|
|
382
|
-
`
|
|
382
|
+
`Integer[1..9]`, `numeric-string` — invariants on ordinary types,
|
|
383
383
|
no value-class wrapper.
|
|
384
384
|
- **Structural facets without a declaration.** A Ruby object that
|
|
385
385
|
has the right methods satisfies an RBS `interface` (a
|
|
@@ -71,7 +71,7 @@ features one at a time (`Literal`, `LiteralString`, `TypeIs`,
|
|
|
71
71
|
| `non-empty-string` | (no built-in; PEP 675's `LiteralString` is closest in spirit but different in semantics) |
|
|
72
72
|
| `literal-string` | `LiteralString` (PEP 675) — provably built from source-code literals. **Direct match.** |
|
|
73
73
|
| `positive-int` | (no built-in; convention is `Annotated[int, Gt(0)]` with third-party validators) |
|
|
74
|
-
| `
|
|
74
|
+
| `Integer[min..max]` | (no built-in; same `Annotated[int, Range(...)]` convention) |
|
|
75
75
|
| `numeric-string` | (no built-in) |
|
|
76
76
|
| `non-empty-array[T]` | (no built-in; some libraries use `tuple[T, *tuple[T, ...]]`) |
|
|
77
77
|
| `Constant<42>` | `Literal[42]` |
|
|
@@ -286,7 +286,7 @@ draws the distinction in full.
|
|
|
286
286
|
`Hash`.
|
|
287
287
|
- **First-class refinement carriers with narrowing.**
|
|
288
288
|
`non-empty-string`, `positive-int`, `numeric-string`,
|
|
289
|
-
`
|
|
289
|
+
`Integer[min..max]` — values restricted by predicate, narrowed
|
|
290
290
|
by the corresponding Ruby predicate methods.
|
|
291
291
|
- **No-false-positives stance.** mypy will warn about dynamic
|
|
292
292
|
code unless `--no-warn-unused-ignores` or `--ignore-missing-imports`
|
|
@@ -53,7 +53,7 @@ is the closest match of any peer.
|
|
|
53
53
|
| `numeric-string` | `numeric-string` | Identical. |
|
|
54
54
|
| `lowercase-string` | `lowercase-string` | Identical. |
|
|
55
55
|
| `class-string` | `Singleton[T]` | Equivalent shape. |
|
|
56
|
-
| `int<1, 9>` | `
|
|
56
|
+
| `int<1, 9>` | `Integer[1..9]` | Same carrier; Rigor spells the bound as the Ruby range literal `Range#cover?` reads (`Integer[1..]`, `Integer[..9]`). |
|
|
57
57
|
| `positive-int` | `positive-int` | Identical. |
|
|
58
58
|
| `negative-int` | `negative-int` | Identical. |
|
|
59
59
|
| `non-zero-int` | `non-zero-int` | Identical. |
|
|
@@ -274,7 +274,7 @@ by narrowing.
|
|
|
274
274
|
| --- | --- | --- |
|
|
275
275
|
| `non-empty-string` | `struct NonEmptyString(String)` newtype | Rigor produces it from `unless s.empty?`, no wrapper. |
|
|
276
276
|
| `positive-int` | `struct PositiveInt(u32)` newtype | Rigor narrows from `n > 0`. |
|
|
277
|
-
| `
|
|
277
|
+
| `Integer[1..9]` | newtype + range check, or const generics gymnastics | Rigor's range carrier handles arbitrary bounds directly. |
|
|
278
278
|
| `numeric-string` | newtype wrapping validated parse | No type-level analogue. |
|
|
279
279
|
| `non-empty-array[T]` | newtype over `Vec<T>` | Rigor produces it from `unless arr.empty?`. |
|
|
280
280
|
|