rigortype 0.3.9 → 0.4.1
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 +5 -4
- 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/08-understanding-errors.md +3 -2
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +454 -13
- data/docs/install.md +87 -15
- data/docs/manual/02-cli-reference.md +65 -4
- data/docs/manual/03-configuration.md +12 -0
- data/docs/manual/04-diagnostics.md +4 -0
- data/docs/manual/07-plugins.md +1 -1
- data/docs/manual/08-skills.md +4 -3
- data/docs/manual/10-mcp-server.md +3 -2
- data/docs/manual/12-caching.md +43 -4
- data/docs/manual/14-rails-quickstart.md +19 -11
- 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 +427 -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 +638 -136
- data/lib/rigor/analysis/dependency_recorder.rb +222 -17
- data/lib/rigor/analysis/fact_store.rb +9 -0
- data/lib/rigor/analysis/incremental.rb +26 -0
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +266 -0
- data/lib/rigor/analysis/incremental_run_slot.rb +171 -0
- data/lib/rigor/analysis/incremental_session.rb +309 -53
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +24 -13
- 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 +3 -1
- data/lib/rigor/analysis/rule_catalog.rb +127 -0
- data/lib/rigor/analysis/run_cache_key.rb +46 -18
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +168 -12
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -4
- data/lib/rigor/analysis/runner/pool_coordinator.rb +109 -17
- data/lib/rigor/analysis/runner/project_pre_passes.rb +44 -17
- data/lib/rigor/analysis/runner.rb +603 -54
- data/lib/rigor/analysis/shadow_harness.rb +27 -0
- data/lib/rigor/analysis/source_rbs_gate.rb +157 -0
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +93 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +409 -0
- data/lib/rigor/analysis/worker_session.rb +57 -13
- 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 +45 -1
- data/lib/rigor/cache/engine_source.rb +29 -1
- data/lib/rigor/cache/file_digest.rb +28 -0
- data/lib/rigor/cache/incremental_snapshot.rb +102 -8
- 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 +63 -4
- data/lib/rigor/cache/value_digest.rb +325 -0
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/check_command.rb +83 -13
- data/lib/rigor/cli/doc_links.rb +1 -1
- data/lib/rigor/cli/docs_command.rb +4 -4
- data/lib/rigor/cli/doctor_command.rb +1 -1
- data/lib/rigor/cli/effects_report.rb +2 -2
- 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/unused_command.rb +3 -3
- data/lib/rigor/cli.rb +21 -3
- data/lib/rigor/config_audit.rb +3 -2
- data/lib/rigor/config_path.rb +29 -0
- data/lib/rigor/configuration/severity_profile.rb +24 -3
- data/lib/rigor/configuration.rb +71 -7
- 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 +49 -37
- 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/bundle_sig_discovery.rb +5 -3
- data/lib/rigor/environment/lockfile_resolver.rb +27 -1
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +10 -1
- data/lib/rigor/environment/rbs_loader.rb +400 -134
- data/lib/rigor/environment/source_rbs_synthesis.rb +147 -0
- data/lib/rigor/environment.rb +104 -96
- 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 +88 -26
- 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 +393 -15
- data/lib/rigor/inference/closure_escape_analyzer.rb +157 -13
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/declaration_walk/context.rb +195 -0
- data/lib/rigor/inference/declaration_walk/errors.rb +18 -0
- data/lib/rigor/inference/declaration_walk/shadow.rb +147 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +441 -0
- data/lib/rigor/inference/declaration_walk.rb +7 -0
- data/lib/rigor/inference/def_node_resolver.rb +3 -2
- 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/definer_resolution.rb +266 -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 +1665 -538
- data/lib/rigor/inference/external_ancestor_resolution.rb +264 -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 +488 -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 +179 -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 +901 -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/module_function_state.rb +213 -0
- data/lib/rigor/inference/multi_target_binder.rb +345 -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 +674 -124
- 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 +13 -11
- 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/certainty.rb +176 -0
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +35 -0
- data/lib/rigor/inference/scope_indexer/def_nestings_collector.rb +47 -0
- data/lib/rigor/inference/scope_indexer/layered_def_nestings.rb +36 -0
- data/lib/rigor/inference/scope_indexer/member_layouts_collector.rb +61 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +130 -0
- data/lib/rigor/inference/scope_indexer/superclasses_collector.rb +64 -0
- data/lib/rigor/inference/scope_indexer.rb +5913 -701
- data/lib/rigor/inference/singleton_hook_decline.rb +253 -0
- data/lib/rigor/inference/statement_evaluator.rb +3249 -497
- 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 +196 -13
- 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/io_boundary.rb +82 -4
- data/lib/rigor/plugin/isolation.rb +1 -1
- 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/trust_policy.rb +3 -3
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/protection/discovery_seed.rb +20 -9
- data/lib/rigor/protection/kill_signature.rb +0 -1
- data/lib/rigor/protection/mutation_cache.rb +4 -5
- data/lib/rigor/rbs_extended.rb +27 -0
- data/lib/rigor/reflection/constant_ancestors.rb +146 -0
- data/lib/rigor/reflection/constant_path.rb +41 -13
- data/lib/rigor/reflection.rb +73 -107
- data/lib/rigor/scope/discovery_index.rb +351 -2
- data/lib/rigor/scope/resolution_chain/relevance.rb +165 -0
- data/lib/rigor/scope/resolution_chain.rb +884 -0
- data/lib/rigor/scope.rb +981 -198
- 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 +580 -47
- 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 +112 -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-ffi/lib/rigor/plugin/ffi.rb +10 -0
- 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 +14 -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 +2 -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 +177 -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 +46 -18
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +40 -10
- 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 +132 -1
|
@@ -36,21 +36,27 @@ path, no vendoring, no `signature_paths:` wiring.
|
|
|
36
36
|
|
|
37
37
|
Roughly the top ~40 selectors plus their close neighbours, across:
|
|
38
38
|
|
|
39
|
-
- **Object (universal)** — `#blank?`, `#present?`, `#presence`,
|
|
40
|
-
`#try!`, `#acts_like
|
|
39
|
+
- **Object (universal)** — `#blank?`, `#present?`, `#presence`,
|
|
40
|
+
`#presence_in`, `#try`, `#try!`, `#acts_like?`, `#deep_dup`, `#with`,
|
|
41
|
+
`#with_options`, `#html_safe?`, `Kernel#class_eval` (+ `NilClass` /
|
|
42
|
+
`TrueClass` / `FalseClass`).
|
|
41
43
|
- **Integer / Float** — Duration multipliers (`#days`, `#hours`,
|
|
42
44
|
`#minutes`, …) and Bytes multipliers (`#megabytes`, `#gigabytes`, …).
|
|
43
45
|
- **String** — inflections (`#underscore`, `#camelize`, `#classify`,
|
|
44
46
|
`#constantize`, `#pluralize`, …), filters (`#squish`, `#truncate`),
|
|
45
|
-
`#html_safe`, `#starts_with?` / `#ends_with
|
|
47
|
+
`#html_safe`, `#starts_with?` / `#ends_with?` (also on **Symbol**),
|
|
48
|
+
conversions and `#in_time_zone`.
|
|
46
49
|
- **Time / Date / DateTime** — `.current`, `.zone`, `#yesterday`,
|
|
47
50
|
`#tomorrow`, `#beginning_of_*` / `#end_of_*`, `#ago`, `#since`. `Time`
|
|
48
51
|
additionally carries its **whole** Rails instance surface (see below);
|
|
49
52
|
`Date` and `DateTime` carry the same subset they always did.
|
|
50
53
|
- **Array** — `.wrap`, `#to_sentence`, `#in_groups_of`, `#second` …
|
|
51
54
|
`#fifth`, `#compact_blank`, `#exclude?`.
|
|
52
|
-
- **Hash** — `#symbolize_keys` / `#stringify_keys` (+ deep / bang)
|
|
53
|
-
`#deep_merge`, `#
|
|
55
|
+
- **Hash** — `#symbolize_keys` / `#stringify_keys` (+ deep / bang) and
|
|
56
|
+
their `#to_options` alias, `#deep_merge`, `#reverse_merge` /
|
|
57
|
+
`#with_defaults` (+ bang), `#with_indifferent_access`, `#except!`,
|
|
58
|
+
`#extract!`.
|
|
59
|
+
- **Range** — `#overlaps?`, `#to_fs` / `#to_formatted_s`.
|
|
54
60
|
- **Enumerable** — `#index_by`, `#index_with`, `#pluck`, `#exclude?`.
|
|
55
61
|
|
|
56
62
|
```ruby
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# rigor-grape
|
|
2
|
+
|
|
3
|
+
Types the [Grape](https://github.com/ruby-grape/grape)
|
|
4
|
+
endpoint-declaration DSL so calls inside `class API < Grape::API`
|
|
5
|
+
bodies — and inside `Grape::Entity` subclasses from
|
|
6
|
+
[grape-entity](https://github.com/ruby-grape/grape-entity) — resolve to
|
|
7
|
+
real carriers instead of `Dynamic[top]`. It reads source only, with no
|
|
8
|
+
`grape` runtime dependency.
|
|
9
|
+
|
|
10
|
+
It ships bundled in `rigortype`. Activate it under `plugins:` (or let
|
|
11
|
+
bundler auto-detection pick it up when `grape` or `grape-entity` is in
|
|
12
|
+
`Gemfile.lock`):
|
|
13
|
+
|
|
14
|
+
```yaml
|
|
15
|
+
plugins:
|
|
16
|
+
- rigor-grape
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## What it types
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
class ThingsAPI < Grape::API
|
|
23
|
+
version "v1", using: :path
|
|
24
|
+
format :json
|
|
25
|
+
|
|
26
|
+
desc "List things" # Object?
|
|
27
|
+
route_setting :swagger, tags: %w[things] # Object?
|
|
28
|
+
|
|
29
|
+
params do # → Grape::Validations::ParamsScope
|
|
30
|
+
requires :id, type: Integer # Object?
|
|
31
|
+
optional :q, type: String # Object?
|
|
32
|
+
requires :filter, type: Hash do # nested scope re-enters ParamsScope
|
|
33
|
+
requires :state, type: String
|
|
34
|
+
end
|
|
35
|
+
mutually_exclusive :q, :filter
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
namespace :things do # self: the API::Instance class object
|
|
39
|
+
get "/:id" do # self: Grape::Endpoint
|
|
40
|
+
params # Hash[untyped, untyped]
|
|
41
|
+
error!("nope", 404) # bot
|
|
42
|
+
present Thing.first, with: Entities::Thing
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class Entities::Thing < Grape::Entity
|
|
48
|
+
expose :id # Array[untyped]
|
|
49
|
+
expose :name do # `self` stays the class object
|
|
50
|
+
expose :first
|
|
51
|
+
end
|
|
52
|
+
format_with(:iso) { |d| d.to_s } # Object
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Recognised surfaces:
|
|
57
|
+
|
|
58
|
+
- **Class-body declarations** on `Grape::API` subclasses (including
|
|
59
|
+
through intermediate source base classes): `params`, `namespace` /
|
|
60
|
+
`group` / `resource` / `resources` / `segment` / `route_param` /
|
|
61
|
+
`version` / `given` / `mounted`, `get`/`put`/`post`/`delete`/`head`/
|
|
62
|
+
`patch`/`options`/`route`, `desc`, `route_setting`, `helpers`, `use`,
|
|
63
|
+
`mount`, `rescue_from`, the format/error-format setters, callbacks
|
|
64
|
+
(`before`/`after`/`before_validation`/`after_validation`), `prefix`,
|
|
65
|
+
`scope`, `contract`, and friends.
|
|
66
|
+
- **`params` block bodies** bind `self` to
|
|
67
|
+
`Grape::Validations::ParamsScope`, so `requires`, `optional`, `given`,
|
|
68
|
+
`with`, `use`, and the grouping macros (`mutually_exclusive`,
|
|
69
|
+
`exactly_one_of`, `at_least_one_of`, `all_or_none_of`) resolve —
|
|
70
|
+
including nested `requires :x, type: Hash do ... end` bodies.
|
|
71
|
+
- **Namespace-family bodies** (`namespace`, `route_param`, `version`,
|
|
72
|
+
`given`, `mounted`) bind `self` to the `Grape::API::Instance` class
|
|
73
|
+
object, matching Grape's `instance_eval`-on-class semantics, so nested
|
|
74
|
+
declarations resolve the same surface.
|
|
75
|
+
- **`desc 'x' do ... end` bodies** bind `self` to
|
|
76
|
+
`Grape::DSL::Desc::ConfigContext` — the closed `ROUTE_ATTRIBUTES`
|
|
77
|
+
setter surface — so `detail`, `success`, `failure`, `tags`,
|
|
78
|
+
`entity`, `hidden`, `is_array`, `consumes`, `headers`, `summary`,
|
|
79
|
+
`deprecated`, `named`, `nickname`, `produces`, `security`,
|
|
80
|
+
`http_codes`, `body_name`, `default`, `description`, and `params`
|
|
81
|
+
(the documentation setter, not `ParamsScope`) resolve.
|
|
82
|
+
- **Verb bodies** bind `self` to `Grape::Endpoint`, so `params`,
|
|
83
|
+
`headers`, `cookies`, `env`, `declared`, `present`, `error!`,
|
|
84
|
+
`status`, `redirect`, `body`, `content_type`, `route`,
|
|
85
|
+
`route_setting`, `stream`, `sendfile` resolve.
|
|
86
|
+
- **`Grape::Entity` class bodies**: `expose`, `unexpose`,
|
|
87
|
+
`with_options`, `documentation`, `format_with`, `root`,
|
|
88
|
+
`root_element`, `represent`, `present_collection`, `root_exposures`.
|
|
89
|
+
Nested `expose` bodies keep `self` on the class object, so they
|
|
90
|
+
resolve the same way.
|
|
91
|
+
|
|
92
|
+
## Deferred
|
|
93
|
+
|
|
94
|
+
- `helpers do ... end` bodies (an anonymous module is `self` —
|
|
95
|
+
unnameable).
|
|
96
|
+
- Named `params :name` scopes inside `helpers` and `contract` schema
|
|
97
|
+
blocks.
|
|
98
|
+
- Value-level typing: `present`/`declared` results against the declared
|
|
99
|
+
entity or params, entity `represent` result typing.
|
|
100
|
+
- `Grape::Middleware` and custom-middleware DSL.
|
|
101
|
+
|
|
102
|
+
Calls on classes outside `Grape::API` / `Grape::Entity` ancestry keep
|
|
103
|
+
the `Dynamic` fallback, and DSL names the bundled signature does not
|
|
104
|
+
declare stay opaque rather than diagnosed — the declared classes are
|
|
105
|
+
registered as open receivers because Grape's runtime surface is
|
|
106
|
+
generated (`override_all_methods!`) and user base classes extend it.
|
|
@@ -58,11 +58,25 @@ name. `null:` extracts to `nullable:` (defaulting to `true`, mirroring
|
|
|
58
58
|
graphql-ruby); `required:` defaults to `false`. The single-element
|
|
59
59
|
`[String]` list form is recognised.
|
|
60
60
|
|
|
61
|
+
## Typed DSL surface
|
|
62
|
+
|
|
63
|
+
Beyond the fact tables, the plugin bundles the graphql-ruby class-level
|
|
64
|
+
DSL signature, so calls inside a recognised subclass type as their real
|
|
65
|
+
carriers instead of staying opaque: `field` returns
|
|
66
|
+
`GraphQL::Schema::Field`, `argument` returns `GraphQL::Schema::Argument`,
|
|
67
|
+
`Enum.value` returns `GraphQL::Schema::EnumValue`, `description` /
|
|
68
|
+
`graphql_name` return their configured strings, and the `Schema`
|
|
69
|
+
registration macros (`query`, `mutation`, `use`, `orphan_types`, …)
|
|
70
|
+
return what they registered. A block parameter on `field`/`argument`
|
|
71
|
+
(`field(:comments) { |f| … }`) types as `GraphQL::Schema::Field` /
|
|
72
|
+
`GraphQL::Schema::Argument`.
|
|
73
|
+
|
|
61
74
|
## No diagnostics, no config
|
|
62
75
|
|
|
63
76
|
The plugin emits no diagnostics and has no configuration knobs — it
|
|
64
|
-
contributes the type tables above for
|
|
65
|
-
walks every `paths:` entry's `.rb` files
|
|
77
|
+
contributes the type tables and signature above for the engine and
|
|
78
|
+
other plugins to consume. It walks every `paths:` entry's `.rb` files
|
|
79
|
+
for the schema-class shapes.
|
|
66
80
|
|
|
67
81
|
## Limitations
|
|
68
82
|
|
|
@@ -72,6 +86,13 @@ walks every `paths:` entry's `.rb` files for the schema-class shapes.
|
|
|
72
86
|
- **No `Schema.execute(...)` result typing.** Typing
|
|
73
87
|
`Schema.execute(query).to_h` against the queried fields is a future
|
|
74
88
|
plugin.
|
|
89
|
+
- **Interface DSL stays opaque.** `include GraphQL::Schema::Interface`
|
|
90
|
+
wires the DSL onto the includer through an include-time `extend`
|
|
91
|
+
hook RBS cannot express, so `field` inside an interface module is
|
|
92
|
+
not yet typed.
|
|
93
|
+
- **Zero-arity definition blocks keep an opaque `self`.**
|
|
94
|
+
`field :x do ... end` `instance_eval`s on the new `Field` at
|
|
95
|
+
runtime; only the explicit-parameter form (`{ |f| … }`) is typed.
|
|
75
96
|
- **Constant-form types only.** The string form (`field :foo, "User"`)
|
|
76
97
|
and the `<Type>.array` / `<Type>!` sugar chains are not recognised
|
|
77
98
|
(the `[String]` bracket form is). Multi-element and empty list
|
|
@@ -33,7 +33,7 @@ authorize(Comment, :edit) # error: no policy class CommentPolicy (did you mean
|
|
|
33
33
|
| `plugin.pundit.policy-call` | info | an `authorize` / `policy` / `policy_scope` call resolved to a discovered policy |
|
|
34
34
|
| `plugin.pundit.unknown-policy-class` | error | the record maps to a `<Type>Policy` with no entry in the index (with a did-you-mean) |
|
|
35
35
|
| `plugin.pundit.unknown-policy-method` | error | the policy exists but the `:action` has no `<action>?` predicate (lists known predicates + a did-you-mean) |
|
|
36
|
-
| `plugin.pundit.load-error` | warning | policy discovery failed (parse/read error) — once per
|
|
36
|
+
| `plugin.pundit.load-error` | warning | policy discovery failed (parse/read error) — once per run, on `.rigor.yml` |
|
|
37
37
|
|
|
38
38
|
The record maps to a policy by constant name or inferred
|
|
39
39
|
`Nominal[T]` (`Post` → `PostPolicy`); `:update` normalises to
|
|
@@ -88,6 +88,13 @@ candidate rather than being guessed at.
|
|
|
88
88
|
not validated when `local` has no inferred `Nominal[T]`.
|
|
89
89
|
- **`Scope` policies** are validated for class existence, not for
|
|
90
90
|
`Scope#resolve`.
|
|
91
|
+
- **The "failed to discover policies" warning is run-scoped.** It is
|
|
92
|
+
a fact about your configuration, not about any one source file, so
|
|
93
|
+
it is reported once per run on `.rigor.yml` rather than repeated on
|
|
94
|
+
every analysed file. It is `:warning`, so if you baselined it at its
|
|
95
|
+
old position that entry no longer matches and the row fails a
|
|
96
|
+
`--fail-on=warning` run — regenerate with `rigor baseline
|
|
97
|
+
regenerate`.
|
|
91
98
|
|
|
92
99
|
## Plugin internals
|
|
93
100
|
|
|
@@ -88,11 +88,16 @@ view templates containing lazy `t('.key')` calls.
|
|
|
88
88
|
may come from controller instance variables not visible in the
|
|
89
89
|
template source. Configure `view_search_paths:` to override the
|
|
90
90
|
default `["app/views"]`.
|
|
91
|
-
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
- **`load-error` rows are reported on `.rigor.yml`** — a locale
|
|
92
|
+
file that does not parse, or a locale or view scan that fails,
|
|
93
|
+
is reported once per run at `.rigor.yml:1:1`. They are
|
|
94
|
+
`:warning`, so if you baselined one at its old position on a
|
|
95
|
+
Ruby file, that entry no longer matches and the row fails a
|
|
96
|
+
`--fail-on=warning` run — regenerate with `rigor baseline
|
|
97
|
+
regenerate`. View diagnostics keep their view file position and
|
|
98
|
+
are reported once per run whatever `--workers` is set to
|
|
99
|
+
([#1060](https://github.com/rigortype/rigor/issues/1060)); a
|
|
100
|
+
baseline entry recorded against a view still matches.
|
|
96
101
|
- **Pluralization is recognised but not validated** — `count:` is
|
|
97
102
|
treated as a reserved option; whether the locale defines
|
|
98
103
|
`:zero` / `:one` / `:other` is not checked.
|
|
@@ -129,6 +129,13 @@ routed. Helper-name recognition for all three is unaffected.
|
|
|
129
129
|
doesn't model) may not register, which can surface a false
|
|
130
130
|
`unknown-helper`. Record those in a baseline, or
|
|
131
131
|
`# rigor:disable` the line.
|
|
132
|
+
- **The "routes file did not load" warning is run-scoped.** It is a
|
|
133
|
+
fact about your configuration, not about any one source file, so
|
|
134
|
+
it is reported once per run on `.rigor.yml` rather than on the
|
|
135
|
+
first analysed file. It is `:warning`, so if you baselined it at
|
|
136
|
+
its old position that entry no longer matches and the row fails a
|
|
137
|
+
`--fail-on=warning` run — regenerate with `rigor baseline
|
|
138
|
+
regenerate`.
|
|
132
139
|
- **Project-custom inflections** declared in
|
|
133
140
|
`config/initializers/inflections.rb` are not yet fully ingested
|
|
134
141
|
(ADR-39 slice 3); the standard ActiveSupport inflections are
|
|
@@ -45,40 +45,97 @@ plugin id/version + config), so an unchanged second run skips the parse.
|
|
|
45
45
|
| Rule | Severity | Fires when |
|
|
46
46
|
| --- | --- | --- |
|
|
47
47
|
| `plugin.rbs-inline.source-rbs-synthesis-failed` | info | rbs-inline could not parse a file; analysis falls back to no inline-RBS contribution and the diagnostic carries the upstream error |
|
|
48
|
-
| `plugin.rbs-inline.source-rbs-annotation-not-honoured` | info | an annotation parsed successfully but contributed nothing — the file's other annotations still apply.
|
|
48
|
+
| `plugin.rbs-inline.source-rbs-annotation-not-honoured` | info | an annotation parsed successfully but contributed nothing — the file's other annotations still apply. Six causes: a member your `sig/` also declares where Rigor cannot tell which of the two is more precise (see [Precedence](#precedence)), the `# @rbs module-self: Foo` spelling (see below), a `#:` line whose type does not parse (see [Unparseable `#:` types](#unparseable--types)), a same-line `# @rbs %a{…}` whose method type does not parse (see [Same-line annotations](#same-line-annotations)), a `# @rbs name: T` parameter type that does not parse (see [Unparseable `# @rbs name:` types](#unparseable--rbs-name-types)), and an `@rbs`-prefixed tag the gem does not recognise (see [An unrecognised tag after `@rbs`](#an-unrecognised-tag-after-rbs)) |
|
|
49
|
+
| `rbs.contradicting-signature` | error | an inline declaration contradicts the `sig/` declaration of the same method, or an inline `%a{rigor:v1:…}` refinement is outside its own declared type (see [Precedence](#precedence)). A core rule rather than the plugin's own, so it carries no `plugin.rbs-inline.` prefix |
|
|
49
50
|
|
|
50
51
|
## Precedence
|
|
51
52
|
|
|
52
53
|
When a method is declared **both** in `sig/` and by an inline
|
|
53
|
-
annotation, **the
|
|
54
|
-
|
|
55
|
-
binds
|
|
54
|
+
annotation, Rigor **compares the two**, position by position — each
|
|
55
|
+
parameter, the return type, a block's parameters and return — and
|
|
56
|
+
exactly one of them binds. Every other annotation in the file still
|
|
57
|
+
binds either way, and the class keeps its method surface.
|
|
56
58
|
|
|
57
59
|
```ruby
|
|
58
|
-
# lib/demo.rb
|
|
59
|
-
class Demo
|
|
60
|
-
# @rbs
|
|
61
|
-
def
|
|
62
|
-
|
|
63
|
-
# @rbs (Integer) ->
|
|
64
|
-
def
|
|
60
|
+
# lib/demo.rb # sig/demo.rbs
|
|
61
|
+
class Demo # class Demo
|
|
62
|
+
# @rbs dir: :asc | :desc # def order: (Symbol dir) -> void
|
|
63
|
+
def order(dir) = nil # def shared: (String) -> Integer
|
|
64
|
+
# end
|
|
65
|
+
# @rbs (Integer) -> String
|
|
66
|
+
def shared(v) = v.to_s
|
|
65
67
|
end
|
|
66
68
|
```
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
`
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
70
|
+
- **One refines the other: the more precise one binds, silently.**
|
|
71
|
+
`:asc | :desc` is a subtype of `Symbol`, so `Demo#order` takes the
|
|
72
|
+
inline contract and `order(:up)` is an argument-type error, where
|
|
73
|
+
the `.rbs` alone let it pass. The merge takes the narrower type in
|
|
74
|
+
every position, parameters included, on purpose: both declarations
|
|
75
|
+
are yours, and the narrower one is what you stated. `String` in
|
|
76
|
+
`sig/` beside `non-empty-string` inline reads as `non-empty-string`.
|
|
77
|
+
`untyped`, `void` and `top` are consistent with anything and say the
|
|
78
|
+
least, so `sig/`'s `-> untyped` beside an inline `-> void` is quiet.
|
|
79
|
+
Identical declarations — what `rigor sig-gen` writes for an annotated
|
|
80
|
+
method — are quiet too. The inline side binds only if the `.rbs`
|
|
81
|
+
member loses nothing by it: the same visibility, and every
|
|
82
|
+
annotation it carries (a predicate, an assertion, an effect
|
|
83
|
+
envelope) also on the inline one.
|
|
84
|
+
- **They contradict: an error, and the `.rbs` binds.** If `Demo#shared`
|
|
85
|
+
took a `::String` in `sig/` and an `::Integer` inline, no value would
|
|
86
|
+
be both, so the run would report
|
|
87
|
+
[`rbs.contradicting-signature`](../04-diagnostics.md#rule-rbs-contradicting-signature)
|
|
88
|
+
at the `sig/` line, naming the annotated file. Spelled `String` and
|
|
89
|
+
`Integer`, as in the example, the pair is undecided instead (below). Positional counts that
|
|
90
|
+
cannot meet, or a keyword one side requires and the other cannot take
|
|
91
|
+
in any form, contradict the same way. The error needs a proof, and
|
|
92
|
+
only Ruby core or stdlib classes written absolutely (`::String`,
|
|
93
|
+
`::Integer`) give one, read from their RBS hierarchy. A module such
|
|
94
|
+
as `Comparable` never counts, since any class may include it; nor
|
|
95
|
+
does a class of your own (your `sig/` may omit the superclass Ruby
|
|
96
|
+
gives it), a gem's class, a relative name such as plain `String`
|
|
97
|
+
(it may be your own `App::String`), an optional or rest parameter
|
|
98
|
+
(a call may leave it out), or a block's parameters (the body may
|
|
99
|
+
never yield). A stale generated signature is the usual cause:
|
|
100
|
+
regenerate it, or fix the annotation.
|
|
101
|
+
- **Rigor cannot tell: the `.rbs` binds, with an `:info`.** When a
|
|
102
|
+
position names a type alias, an interface, `self`, a type variable,
|
|
103
|
+
or a relative class name your project also declares, when Rigor
|
|
104
|
+
cannot prove two types disjoint (a subclass relation such as
|
|
105
|
+
`Integer` against `Numeric` included), when the parameter lists are shaped
|
|
106
|
+
differently but overlap, when the overloads do not pair one to one
|
|
107
|
+
(they are paired by what they declare, not by their order), or when
|
|
108
|
+
each side is more precise somewhere, the inline signature is dropped
|
|
109
|
+
and reported as
|
|
110
|
+
`plugin.rbs-inline.source-rbs-annotation-not-honoured`, naming the
|
|
111
|
+
member and the `.rbs` that bound. Make one a refinement of the
|
|
112
|
+
other, or delete one, to settle it.
|
|
113
|
+
|
|
114
|
+
`rigor sig-gen --write` produces this overlap on purpose: by default it
|
|
115
|
+
copies each inline declaration into `sig/`, so the generated signature
|
|
116
|
+
is the complete contract a gem ships. An identical copy is quiet, as
|
|
117
|
+
above. When an inline annotation later disagrees with its copy,
|
|
118
|
+
`rigor sig-gen --write` and `--check` refuse the method and exit `1`
|
|
119
|
+
until you make the two agree or pass `--overwrite`, which replaces the
|
|
120
|
+
`sig/` member with the inline declaration. A project whose Steep reads the same
|
|
121
|
+
annotations sets `sig_gen.inline_declared: skip` instead
|
|
122
|
+
([handbook chapter 11](../../handbook/11-sig-gen.md#methods-declared-inline)).
|
|
123
|
+
|
|
124
|
+
A `%a{rigor:v1:return: …}` or `%a{rigor:v1:param: …}` refinement on an
|
|
125
|
+
inline annotation must also share values with its own declared type:
|
|
126
|
+
`# @rbs %a{rigor:v1:return: positive-int} () -> ::String` is reported as
|
|
127
|
+
`rbs.contradicting-signature` at the annotated file (the same proof rule
|
|
128
|
+
applies, so `-> String` without `::` is left alone).
|
|
129
|
+
|
|
130
|
+
There is no upstream rule to defer to: rbs merges an inline `.rb`
|
|
131
|
+
declaration and a `.rbs` one into a single class entry and ranks
|
|
132
|
+
neither, so Steep reports the same overlap as a signature error and
|
|
133
|
+
the class fails to build. Rigor keeps the reporting and drops the
|
|
134
|
+
degradation ([ADR-112](../../adr/112-extrbs-comment-channel.md) WD5,
|
|
135
|
+
which replaced [ADR-32](../../adr/32-rbs-inline-comment-ingestion.md)
|
|
136
|
+
WD13's "the `.rbs` always wins") — left to collide, one duplicated
|
|
137
|
+
method costs the class every other method, and each call on it, real
|
|
138
|
+
methods and typos alike, reads `Dynamic[top]`.
|
|
82
139
|
|
|
83
140
|
Two overlaps this does **not** cover: a `.rbs` that collides with
|
|
84
141
|
*bundled* RBS (Ruby core, stdlib, a gem's signatures) is quarantined
|
|
@@ -109,6 +166,136 @@ in silence. Constructs the gem supports and the built-in parser does not —
|
|
|
109
166
|
`@rbs generic T`, `@rbs!` embedded RBS blocks, `@rbs inherits`, method
|
|
110
167
|
visibility — all work here.
|
|
111
168
|
|
|
169
|
+
## Same-line annotations
|
|
170
|
+
|
|
171
|
+
An `%a{…}` annotation can sit on the same line as a method type:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
class Reader
|
|
175
|
+
# @rbs %a{rigor:v1:return: non-empty-string} () -> String
|
|
176
|
+
def title = "x"
|
|
177
|
+
|
|
178
|
+
#: %a{pure} () -> String
|
|
179
|
+
def label = "x"
|
|
180
|
+
end
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
This is the spelling the built-in parser and Steep's inline mode accept. The
|
|
184
|
+
gem itself does not: it keeps the annotation and drops the method type in the
|
|
185
|
+
`@rbs` form, and drops the whole `#:` line. Rigor splits the line back into the
|
|
186
|
+
annotation and the method type before the gem's writer runs, so both apply, as
|
|
187
|
+
they do when the annotation has a line of its own
|
|
188
|
+
([ADR-32](../../adr/32-rbs-inline-comment-ingestion.md) WD11). The gem's own
|
|
189
|
+
`rbs-inline --output` is unchanged and still drops them.
|
|
190
|
+
|
|
191
|
+
When the method type after the annotations does not parse, nothing is split:
|
|
192
|
+
the method types as if no signature had been written, and Rigor reports it — a
|
|
193
|
+
`#:` line under [Unparseable `#:` types](#unparseable--types), and an `@rbs`
|
|
194
|
+
line as `plugin.rbs-inline.source-rbs-annotation-not-honoured` naming the line
|
|
195
|
+
and the text it could not read.
|
|
196
|
+
|
|
197
|
+
## Unparseable `#:` types
|
|
198
|
+
|
|
199
|
+
A `#:` line whose type does not parse as RBS is dropped — the signature never
|
|
200
|
+
applies, and the method types as if the line had never been written, not
|
|
201
|
+
merely as if its type were wrong:
|
|
202
|
+
|
|
203
|
+
```ruby
|
|
204
|
+
class BadRefProbe
|
|
205
|
+
#: (finite-float) -> String
|
|
206
|
+
def show(f)
|
|
207
|
+
f.to_s
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
`finite-float` is a [Rigor refinement](../16-rbs-extended-annotations.md)
|
|
213
|
+
name, not an RBS type, and does not belong in an ordinary type position.
|
|
214
|
+
Rigor reports the drop as
|
|
215
|
+
`plugin.rbs-inline.source-rbs-annotation-not-honoured`, naming the line and
|
|
216
|
+
the text that failed to parse, rather than leaving `show` silently `untyped`
|
|
217
|
+
with no diagnostic anywhere. The `# @rbs name: TYPE` tag form of the same
|
|
218
|
+
mistake is a different failure shape — see the next section.
|
|
219
|
+
|
|
220
|
+
## An unresolvable type name in `# @rbs`
|
|
221
|
+
|
|
222
|
+
Naming a Rigor refinement (or any other unresolvable name) where an RBS type
|
|
223
|
+
belongs in the `# @rbs name: TYPE` tag form does not drop silently the way
|
|
224
|
+
`#:` does — it takes the whole class down:
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
class ProbeZZ
|
|
228
|
+
# @rbs g: finite-float
|
|
229
|
+
def probe(g)
|
|
230
|
+
g.to_s
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Upstream's own type parser truncates `finite-float` to `finite` (a hyphen
|
|
236
|
+
cannot continue an RBS type name) before this ever reaches Rigor, so `finite`
|
|
237
|
+
is the only token that reaches `RBS::DefinitionBuilder` — and it names no
|
|
238
|
+
loaded type, so the build for the WHOLE class fails
|
|
239
|
+
(`RBS::NoTypeFoundError`). `probe`, and every other real method on `ProbeZZ`,
|
|
240
|
+
reads `Dynamic[top]`. This surfaces as
|
|
241
|
+
`rbs.coverage.definition-build-failed`, naming the token and, when it is the
|
|
242
|
+
truncated head of a registered refinement name, the `%a{rigor:v1:…}` spelling
|
|
243
|
+
that IS valid today (see
|
|
244
|
+
[RBS::Extended annotations](../16-rbs-extended-annotations.md)).
|
|
245
|
+
|
|
246
|
+
## Unparseable `# @rbs name:` types
|
|
247
|
+
|
|
248
|
+
A bounded or parameterised refinement — `Integer[1..10]`, `non-empty-array
|
|
249
|
+
[Integer]` — does not truncate at a hyphen the way `finite-float` does, so it
|
|
250
|
+
does not take the whole class down. In a `# @rbs name: TYPE` position it
|
|
251
|
+
leaves the parameter untyped instead, silently:
|
|
252
|
+
|
|
253
|
+
```ruby
|
|
254
|
+
class BoundedProbe
|
|
255
|
+
# @rbs n: Integer[1..10]
|
|
256
|
+
def probe(n)
|
|
257
|
+
n
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The gem's grammar makes the colon-and-type optional on this annotation, so a
|
|
263
|
+
`TYPE` it cannot parse leaves the parameter's name recorded and its type
|
|
264
|
+
unset rather than raising — indistinguishable downstream from a parameter
|
|
265
|
+
nobody annotated at all. Rigor reports the drop as
|
|
266
|
+
`plugin.rbs-inline.source-rbs-annotation-not-honoured`, naming the line and
|
|
267
|
+
the text that failed to parse, and points at the `%a{rigor:v1:param:}`
|
|
268
|
+
spelling that carries a refinement like `Integer[1..10]` correctly (see
|
|
269
|
+
[RBS::Extended annotations](../16-rbs-extended-annotations.md)) — the same
|
|
270
|
+
advice the `%a{rigor:v1:…}` pointer above gives for the tag form.
|
|
271
|
+
|
|
272
|
+
## An unrecognised tag after `@rbs`
|
|
273
|
+
|
|
274
|
+
The gem recognises a comment as an `@rbs` annotation attempt as soon as it
|
|
275
|
+
sees the word boundary right after `@rbs` — which matches a hyphen, not only
|
|
276
|
+
whitespace or end-of-line:
|
|
277
|
+
|
|
278
|
+
```ruby
|
|
279
|
+
class TagProbe
|
|
280
|
+
# @rbs-ext return: non-empty-string
|
|
281
|
+
# @rbs return: String
|
|
282
|
+
def name
|
|
283
|
+
"x"
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
`# @rbs-ext …` is inside that net, but nothing in the gem's grammar
|
|
289
|
+
recognises `-ext`, so the gem gives up on the whole paragraph and folds it
|
|
290
|
+
back into an ordinary comment — the neighbouring `# @rbs return: String`
|
|
291
|
+
line still binds, but the `@rbs-ext` line contributes nothing and, before
|
|
292
|
+
this, said nothing either. Rigor reports the drop as
|
|
293
|
+
`plugin.rbs-inline.source-rbs-annotation-not-honoured`, naming the line and
|
|
294
|
+
the comment text — without implying `@rbs-ext` is a recognised tag of any
|
|
295
|
+
kind. A comment that merely mentions `@rbs` in prose, or one that opens with
|
|
296
|
+
a tag not starting with `@rbs` (`# @extrbs …`), is outside the gem's
|
|
297
|
+
detector and stays silent.
|
|
298
|
+
|
|
112
299
|
## Configuration
|
|
113
300
|
|
|
114
301
|
```yaml
|
|
@@ -40,7 +40,7 @@ arguments.
|
|
|
40
40
|
| `plugin.sidekiq.worker-call` | info | a `Worker.perform_*` call matched a discovered worker's `#perform` arity |
|
|
41
41
|
| `plugin.sidekiq.wrong-arity` | error | the forwarded argument count falls outside `#perform`'s arity envelope (message names the schedule carve-out for `perform_in` / `perform_at`) |
|
|
42
42
|
| `plugin.sidekiq.missing-schedule` | error | `perform_in()` / `perform_at()` called with zero arguments (the schedule is required even when `#perform` takes none) |
|
|
43
|
-
| `plugin.sidekiq.load-error` | warning | worker discovery failed (parse/read error) — once per
|
|
43
|
+
| `plugin.sidekiq.load-error` | warning | worker discovery failed (parse/read error) — once per run, on `.rigor.yml` |
|
|
44
44
|
|
|
45
45
|
## What it types
|
|
46
46
|
|
|
@@ -144,6 +144,13 @@ added it is 100.
|
|
|
144
144
|
alternative `klass:` spelling, and a schedule built in Ruby with
|
|
145
145
|
`Sidekiq::Cron::Job.load_from_hash!`, supply no roots — the worker
|
|
146
146
|
stays a `rigor unused` candidate rather than being guessed at.
|
|
147
|
+
- **The "failed to discover workers" warning is run-scoped.** It is
|
|
148
|
+
a fact about your configuration, not about any one source file, so
|
|
149
|
+
it is reported once per run on `.rigor.yml` rather than repeated on
|
|
150
|
+
every analysed file. It is `:warning`, so if you baselined it at its
|
|
151
|
+
old position that entry no longer matches and the row fails a
|
|
152
|
+
`--fail-on=warning` run — regenerate with `rigor baseline
|
|
153
|
+
regenerate`.
|
|
147
154
|
|
|
148
155
|
## Plugin internals
|
|
149
156
|
|
|
@@ -47,11 +47,27 @@ plugins:
|
|
|
47
47
|
The plugin is **input-side only**: it translates Sorbet's syntax
|
|
48
48
|
into Rigor's type model. It does **not** run Sorbet's checker,
|
|
49
49
|
ship `sorbet-runtime`, or enforce Sorbet's runtime guarantees.
|
|
50
|
-
When an RBS sig and a Sorbet sig disagree,
|
|
51
|
-
sig
|
|
52
|
-
|
|
53
|
-
`T
|
|
54
|
-
documents the full vocabulary and
|
|
50
|
+
When an RBS sig and a Sorbet sig disagree, call sites get the
|
|
51
|
+
Sorbet sig's return type, and the method's body is checked
|
|
52
|
+
against the RBS return. Forms outside the
|
|
53
|
+
translation table (`T.proc`, `T.self_type`, …) degrade to
|
|
54
|
+
`Dynamic[top]`. Chapter 10 documents the full vocabulary and
|
|
55
|
+
these edges.
|
|
56
|
+
|
|
57
|
+
The plugin also types the annotation DSL's own expressions
|
|
58
|
+
(`sig`, `params`, `returns`, `void`, `abstract`, `override`,
|
|
59
|
+
`type_member`, `T::Array[...]`, `T::Helpers`/`T::Generic` macros,
|
|
60
|
+
`T::Struct`/`T::Enum` class bodies — `sig` on a plain
|
|
61
|
+
`T::Struct` subclass still needs the subclass's own `extend T::Sig`,
|
|
62
|
+
matching the runtime) through a bundled
|
|
63
|
+
`sorbet-runtime` RBS surface — so the sig DSL no longer reads as
|
|
64
|
+
`Dynamic[top]` on a Sorbet codebase. What it does not model:
|
|
65
|
+
`T::Struct` does not synthesise `prop`-declared readers (a
|
|
66
|
+
`Doc#name` accessor still reads opaque), `include T::Props` does
|
|
67
|
+
not confer the `ClassMethods` surface (only the `< T::Struct`
|
|
68
|
+
subclass path does), and the deeper type-member machinery
|
|
69
|
+
(`T.attached_class` variance, `type_member` bounds) is not
|
|
70
|
+
propagated into call sites.
|
|
55
71
|
|
|
56
72
|
## Plugin internals
|
|
57
73
|
|
data/exe/rigor
CHANGED
|
@@ -11,13 +11,28 @@
|
|
|
11
11
|
# context across the exec. The `none` and `process` (default) strategies
|
|
12
12
|
# need no flag, so this is a no-op for them — behaviour is unchanged
|
|
13
13
|
# unless a project opts into `ruby_box`.
|
|
14
|
+
#
|
|
15
|
+
# Before re-exec'ing, a child Ruby probes whether this Ruby can run
|
|
16
|
+
# Rigor under `RUBY_BOX=1` at all (Rigor::Plugin::BoxProbe — released
|
|
17
|
+
# Rubies through 4.0.7 segfault on Ruby Bug #22260). When it cannot,
|
|
18
|
+
# the request is dropped with a warning and the run proceeds under the
|
|
19
|
+
# configured strategy (`process` by default) instead of crashing.
|
|
14
20
|
rigor_isolation = ENV["RIGOR_PLUGIN_ISOLATION"].to_s
|
|
15
21
|
rigor_wants_box = rigor_isolation == "ruby_box" || !ENV["RIGOR_BOX"].to_s.empty?
|
|
16
22
|
if rigor_wants_box && ENV["RUBY_BOX"].to_s.empty?
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
require_relative "../lib/rigor/plugin/box_probe"
|
|
24
|
+
if (rigor_box_unsupported = Rigor::Plugin::BoxProbe.unsupported_reason)
|
|
25
|
+
rigor_box_warning = "rigor: ruby_box plugin isolation is unavailable: #{rigor_box_unsupported}. " \
|
|
26
|
+
"Falling back to the configured plugins_isolation strategy (default: process)."
|
|
27
|
+
# `$stderr.puts`, not `warn`: `-W0` silences `warn`, and this fallback must never be silent.
|
|
28
|
+
$stderr.puts rigor_box_warning # rubocop:disable Style/StderrPuts
|
|
29
|
+
ENV.delete("RIGOR_PLUGIN_ISOLATION") if rigor_isolation == "ruby_box"
|
|
30
|
+
ENV.delete("RIGOR_BOX")
|
|
31
|
+
else
|
|
32
|
+
ENV["RUBY_BOX"] = "1"
|
|
33
|
+
ENV["RIGOR_PLUGIN_ISOLATION"] = "ruby_box" if rigor_isolation.empty?
|
|
34
|
+
exec(RbConfig.ruby, __FILE__, *ARGV)
|
|
35
|
+
end
|
|
21
36
|
end
|
|
22
37
|
|
|
23
38
|
lib = File.expand_path("../lib", __dir__)
|