rigortype 0.3.9 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/core_overlay/enumerable.rbs +51 -0
- data/data/core_overlay/enumerator.rbs +84 -0
- data/data/core_overlay/hash_rbs3.rbs +41 -0
- data/data/core_overlay/process.rbs +40 -0
- data/data/core_overlay/string_io.rbs +33 -0
- data/data/effects/core.yml +3 -3
- data/data/gem_overlay/activesupport/core_ext.rbs +127 -4
- data/docs/handbook/03-narrowing.md +95 -10
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- data/docs/handbook/07-rbs-and-extended.md +16 -11
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +454 -13
- data/docs/manual/02-cli-reference.md +63 -2
- data/docs/manual/03-configuration.md +7 -0
- data/docs/manual/04-diagnostics.md +4 -0
- data/docs/manual/07-plugins.md +1 -1
- data/docs/manual/10-mcp-server.md +3 -2
- data/docs/manual/16-rbs-extended-annotations.md +40 -7
- data/docs/manual/19-effect-labels.md +10 -0
- data/docs/manual/plugins/README.md +7 -0
- data/docs/manual/plugins/rigor-actioncable.md +8 -1
- data/docs/manual/plugins/rigor-actionmailer.md +7 -0
- data/docs/manual/plugins/rigor-actionpack.md +243 -0
- data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
- data/docs/manual/plugins/rigor-activejob.md +7 -0
- data/docs/manual/plugins/rigor-activerecord.md +110 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +11 -5
- data/docs/manual/plugins/rigor-grape.md +106 -0
- data/docs/manual/plugins/rigor-graphql.md +23 -2
- data/docs/manual/plugins/rigor-pundit.md +8 -1
- data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
- data/docs/manual/plugins/rigor-rails-routes.md +7 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +212 -25
- data/docs/manual/plugins/rigor-sidekiq.md +8 -1
- data/docs/manual/plugins/rigor-sorbet.md +21 -5
- data/exe/rigor +19 -4
- data/lib/rigor/analysis/baseline.rb +1 -1
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -3
- data/lib/rigor/analysis/check_rules/rule_ids.rb +20 -5
- data/lib/rigor/analysis/check_rules/rule_walk.rb +2 -1
- data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +36 -2
- data/lib/rigor/analysis/check_rules.rb +520 -49
- data/lib/rigor/analysis/dependency_recorder.rb +23 -0
- data/lib/rigor/analysis/fact_store.rb +9 -0
- data/lib/rigor/analysis/incremental.rb +26 -0
- data/lib/rigor/analysis/incremental_session.rb +36 -4
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/plugin_roots.rb +0 -1
- data/lib/rigor/analysis/reachability/scan_cache.rb +1 -0
- data/lib/rigor/analysis/rule_catalog.rb +127 -0
- data/lib/rigor/analysis/run_cache_key.rb +20 -11
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +167 -11
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -4
- data/lib/rigor/analysis/runner/pool_coordinator.rb +107 -15
- data/lib/rigor/analysis/runner/project_pre_passes.rb +26 -7
- data/lib/rigor/analysis/runner.rb +278 -20
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +91 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +399 -0
- data/lib/rigor/analysis/worker_session.rb +53 -10
- data/lib/rigor/bleeding_edge.rb +0 -2
- data/lib/rigor/builtins/hkt_builtins.rb +1 -0
- data/lib/rigor/builtins/imported_refinements.rb +4 -0
- data/lib/rigor/builtins/regex_refinement.rb +17 -9
- data/lib/rigor/builtins/static_return_refinements.rb +2 -0
- data/lib/rigor/cache/descriptor.rb +6 -1
- data/lib/rigor/cache/engine_source.rb +29 -1
- data/lib/rigor/cache/incremental_snapshot.rb +33 -1
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- data/lib/rigor/cache/rbs_descriptor.rb +37 -7
- data/lib/rigor/cache/store.rb +0 -1
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/doc_links.rb +1 -1
- data/lib/rigor/cli/docs_command.rb +4 -4
- data/lib/rigor/cli/plugin_command.rb +3 -3
- data/lib/rigor/cli/prism_colorizer.rb +0 -1
- data/lib/rigor/cli/sig_gen_command.rb +210 -29
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +0 -1
- data/lib/rigor/cli/type_of_command.rb +19 -5
- data/lib/rigor/cli/type_of_renderer.rb +20 -9
- data/lib/rigor/cli/type_of_template_probe.rb +189 -0
- data/lib/rigor/cli.rb +21 -3
- data/lib/rigor/configuration/severity_profile.rb +19 -3
- data/lib/rigor/configuration.rb +66 -3
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +11 -2
- data/lib/rigor/effects/callee_rule.rb +368 -0
- data/lib/rigor/effects/catalog.rb +7 -4
- data/lib/rigor/effects/collector.rb +11 -5
- data/lib/rigor/effects/config_envelopes.rb +9 -2
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/effect_table.rb +11 -3
- data/lib/rigor/effects/envelope_check.rb +1 -1
- data/lib/rigor/effects/envelope_index.rb +15 -0
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/identity.rb +16 -0
- data/lib/rigor/effects/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +21 -0
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +43 -31
- data/lib/rigor/effects/propagator.rb +295 -16
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +121 -72
- data/lib/rigor/effects/signature_sources.rb +1 -1
- data/lib/rigor/effects/snapshot.rb +2 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +385 -30
- data/lib/rigor/effects/visibility.rb +101 -0
- data/lib/rigor/environment/lockfile_resolver.rb +17 -0
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/rbs_loader.rb +399 -133
- data/lib/rigor/environment.rb +103 -38
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +139 -9
- data/lib/rigor/inference/block_auto_splat.rb +216 -0
- data/lib/rigor/inference/block_call_timing.rb +338 -0
- data/lib/rigor/inference/block_parameter_binder.rb +73 -27
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +2 -1
- data/lib/rigor/inference/budget_trace.rb +2 -1
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +387 -15
- data/lib/rigor/inference/closure_escape_analyzer.rb +157 -13
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/def_return_typer.rb +11 -7
- data/lib/rigor/inference/define_method_block_self.rb +64 -0
- data/lib/rigor/inference/element_read_widening.rb +22 -10
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +1532 -496
- data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
- data/lib/rigor/inference/global_write_census.rb +239 -0
- data/lib/rigor/inference/guard_rebinding.rb +447 -0
- data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
- data/lib/rigor/inference/index_write_widening.rb +16 -3
- data/lib/rigor/inference/indexed_narrowing.rb +61 -9
- data/lib/rigor/inference/jump_targets.rb +82 -0
- data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
- data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
- data/lib/rigor/inference/last_line.rb +340 -0
- data/lib/rigor/inference/last_status.rb +144 -0
- data/lib/rigor/inference/macro_block_self_type.rb +167 -17
- data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
- data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
- data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
- data/lib/rigor/inference/match_rebinding.rb +392 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +85 -24
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +4 -0
- data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +113 -73
- data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +881 -38
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +128 -53
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +214 -13
- data/lib/rigor/inference/method_parameter_binder.rb +8 -3
- data/lib/rigor/inference/multi_target_binder.rb +340 -52
- data/lib/rigor/inference/mutation_rejoin.rb +4 -1
- data/lib/rigor/inference/mutation_widening.rb +70 -48
- data/lib/rigor/inference/narrowing.rb +540 -120
- data/lib/rigor/inference/operand_effects.rb +167 -0
- data/lib/rigor/inference/operand_walk.rb +88 -0
- data/lib/rigor/inference/optimistic_origin.rb +152 -9
- data/lib/rigor/inference/parameter_inference_collector.rb +3 -2
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +7 -2
- data/lib/rigor/inference/project_patched_scanner.rb +7 -3
- data/lib/rigor/inference/receiver_alias.rb +90 -1
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +15 -11
- data/lib/rigor/inference/repeated_or_writes.rb +463 -0
- data/lib/rigor/inference/return_barrier.rb +54 -0
- data/lib/rigor/inference/rewrite_mutation.rb +120 -0
- data/lib/rigor/inference/scope_indexer.rb +4615 -551
- data/lib/rigor/inference/statement_evaluator.rb +3176 -490
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +44 -7
- data/lib/rigor/inference/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- data/lib/rigor/language_server/debouncer.rb +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +3 -2
- data/lib/rigor/language_server/hover_renderer.rb +3 -3
- data/lib/rigor/language_server/project_context.rb +5 -3
- data/lib/rigor/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +168 -5
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +1 -1
- data/lib/rigor/plugin/effect_attribution.rb +58 -4
- data/lib/rigor/plugin/loader.rb +2 -1
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- data/lib/rigor/plugin/manifest.rb +71 -10
- data/lib/rigor/plugin/registry.rb +35 -1
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +9 -3
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/protection/discovery_seed.rb +3 -1
- data/lib/rigor/protection/kill_signature.rb +0 -1
- data/lib/rigor/protection/mutation_cache.rb +1 -2
- data/lib/rigor/rbs_extended.rb +27 -0
- data/lib/rigor/reflection/constant_ancestors.rb +97 -0
- data/lib/rigor/reflection/constant_path.rb +19 -6
- data/lib/rigor/reflection.rb +72 -105
- data/lib/rigor/scope/discovery_index.rb +135 -2
- data/lib/rigor/scope.rb +859 -49
- data/lib/rigor/sig_gen/alias_index.rb +289 -0
- data/lib/rigor/sig_gen/classification.rb +22 -5
- data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
- data/lib/rigor/sig_gen/generator.rb +558 -31
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- data/lib/rigor/sig_gen/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +1 -1
- data/lib/rigor/sig_gen/renderer.rb +124 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +44 -1
- data/lib/rigor/sig_gen/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +166 -35
- data/lib/rigor/sig_gen.rb +3 -0
- data/lib/rigor/signature_path_audit.rb +1 -1
- data/lib/rigor/source/node_walker.rb +0 -3
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/type/combinator.rb +88 -12
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/hash_shape.rb +1 -1
- data/lib/rigor/type/refined.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +9 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +13 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +239 -4
- data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
- data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
- data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +9 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +380 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +281 -35
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +25 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +171 -5
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +2 -0
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
- data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
- data/plugins/rigor-grape/sig/grape.rbs +263 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +72 -2
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +11 -7
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +35 -37
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +9 -17
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +217 -36
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +9 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +0 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +46 -1
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/sig/rigor/analysis/baseline.rbs +69 -7
- data/sig/rigor/analysis/fact_store.rbs +1 -1
- data/sig/rigor/analysis/project_scan.rbs +74 -0
- data/sig/rigor/effects/config_envelopes.rbs +100 -0
- data/sig/rigor/effects/effect_table.rbs +60 -0
- data/sig/rigor/effects/envelope.rbs +97 -0
- data/sig/rigor/effects/envelope_index.rbs +33 -0
- data/sig/rigor/effects/file_collection.rbs +81 -0
- data/sig/rigor/effects/label.rbs +26 -0
- data/sig/rigor/effects/label_set.rbs +46 -0
- data/sig/rigor/effects/method_key.rbs +24 -0
- data/sig/rigor/effects/origin.rbs +50 -0
- data/sig/rigor/effects/plugin_facts.rbs +141 -0
- data/sig/rigor/effects/registry.rbs +68 -0
- data/sig/rigor/effects/summary.rbs +50 -0
- data/sig/rigor/effects/taint_cause.rbs +12 -0
- data/sig/rigor/environment.rbs +12 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +6 -4
- data/sig/rigor/plugin/additional_initializer.rbs +36 -0
- data/sig/rigor/plugin/base.rbs +30 -7
- data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
- data/sig/rigor/plugin/effect_attribution.rbs +66 -0
- data/sig/rigor/plugin/effect_edge.rbs +33 -0
- data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
- data/sig/rigor/plugin/io_boundary.rbs +1 -1
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +50 -11
- data/sig/rigor/plugin/protocol_contract.rbs +68 -0
- data/sig/rigor/plugin/registry.rbs +62 -1
- data/sig/rigor/plugin.rbs +1 -1
- data/sig/rigor/rbs_extended.rbs +1 -1
- data/sig/rigor/reflection.rbs +7 -6
- data/sig/rigor/scope.rbs +135 -20
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -7
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +6 -0
- data/sig/rigor.rbs +38 -20
- data/skills/rigor-ask/SKILL.md +8 -5
- data/skills/rigor-baseline-reduce/SKILL.md +4 -6
- data/skills/rigor-ci-setup/SKILL.md +17 -21
- data/skills/rigor-doctor/SKILL.md +24 -22
- data/skills/rigor-doctor/references/01-checks.md +97 -33
- data/skills/rigor-editor-setup/SKILL.md +6 -4
- data/skills/rigor-mcp-setup/SKILL.md +5 -4
- data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
- data/skills/rigor-next-steps/SKILL.md +4 -2
- data/skills/rigor-plugin-author/SKILL.md +19 -23
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +12 -25
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
- data/skills/rigor-plugin-review/SKILL.md +6 -4
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
- data/skills/rigor-plugin-tune/SKILL.md +4 -2
- data/skills/rigor-project-init/SKILL.md +9 -7
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +33 -8
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
- data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
- data/skills/rigor-protection-uplift/SKILL.md +4 -6
- data/skills/rigor-rbs-setup/SKILL.md +4 -2
- data/skills/rigor-type-oracle/SKILL.md +4 -6
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +12 -8
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +0 -3
- data/skills/rigor-unused-adjudicate/SKILL.md +4 -2
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +108 -1
|
@@ -138,6 +138,28 @@ truncation explicit. `--trace` records fail-soft fallbacks,
|
|
|
138
138
|
after the rows of a line table in text output. The editor-mode
|
|
139
139
|
`--tmp-file` / `--instead-of` pair is accepted as on `check`.
|
|
140
140
|
|
|
141
|
+
A template a plugin compiles into Ruby (an ERB view under
|
|
142
|
+
`rigor-actionpack`) is probed the way `rigor check` analyses
|
|
143
|
+
it: the compiled Ruby is typed under the view's declared
|
|
144
|
+
`self`, locals and instance variables, and `LINE:COL` are the
|
|
145
|
+
template's own. A column answers only when it lies in Ruby the
|
|
146
|
+
compiler copied verbatim — the `@user.name` in
|
|
147
|
+
`<%= @user.name %>` — and denotes exactly one compiled
|
|
148
|
+
expression. A column in HTML text, in code the plugin rewrote
|
|
149
|
+
(a layout's `<%= yield %>`), or in bytes copied to more than
|
|
150
|
+
one place prints `no expression found at …` with the reason
|
|
151
|
+
and exits `1`; the command never answers about a nearby
|
|
152
|
+
node instead. Two tags that name the same thing (`<%= v %>`
|
|
153
|
+
beside `<% if "a" <= v %>`, or `<%= v %> <%= v.to_s %>`)
|
|
154
|
+
decline for that reason as well; a name repeated inside ONE
|
|
155
|
+
tag still answers. A bare `FILE:LINE` lists only the expressions
|
|
156
|
+
that map back to a template column. `--trace` fallbacks are
|
|
157
|
+
reported at the template line, and a JSON fallback carries a
|
|
158
|
+
`column` only when its position maps back to one. A template
|
|
159
|
+
the plugin declined to compile prints
|
|
160
|
+
`plugin declined the template; probing its bytes as Ruby`
|
|
161
|
+
and is probed as plain Ruby, parse error included.
|
|
162
|
+
|
|
141
163
|
The four probe commands — `type-of`, `type-scan`, `trace` and
|
|
142
164
|
`annotate` — build their environment fresh on every invocation
|
|
143
165
|
and never read or write the persistent cache. That is why none
|
|
@@ -477,13 +499,52 @@ rigor sig-gen [paths]
|
|
|
477
499
|
| `--print` | Write RBS to stdout. Default. |
|
|
478
500
|
| `--diff` | Write a unified diff against existing RBS. |
|
|
479
501
|
| `--write` | Write RBS to `sig/<path>.rbs` files. |
|
|
480
|
-
| `--
|
|
502
|
+
| `--check` | Write nothing; print what `--write` with the same options would change, and exit `1` if anything would. The CI freshness gate. |
|
|
503
|
+
| `--overwrite` | Allow tighter-return updates, and inline declarations that disagree with `sig/`, to replace user-authored RBS. |
|
|
481
504
|
| `--include-private` | Emit private and protected methods too. |
|
|
482
505
|
| `--params=untyped\|observed\|observed-strict` | Parameter-typing policy. Default `untyped`. |
|
|
483
|
-
| `--observe=PATH` | Scan `PATH` for call-site observations. Repeatable. |
|
|
506
|
+
| `--observe=PATH` | Scan `PATH` for call-site observations. Repeatable. Default: the configured `test_paths:` (unset: whichever of `spec/` and `test/` exist). |
|
|
484
507
|
| `--new-files` / `--new-methods` / `--tighter-returns` | Emit only that classification. |
|
|
508
|
+
| `--effect-envelopes` | Also emit `%a{rigor:v1:effect …}` for effectful methods. Needs the `effects:` opt-in. |
|
|
509
|
+
| `--no-cache` | Do not read or write the analysis cache. Only effect collection uses it. |
|
|
485
510
|
| `--format=text\|json` | Output format. |
|
|
486
511
|
|
|
512
|
+
`--print`, `--diff`, `--write` and `--check` are mutually exclusive.
|
|
513
|
+
`--check` fails exactly when `--write` would create, change or
|
|
514
|
+
refuse a file, so a tighter return `--write` declines without
|
|
515
|
+
`--overwrite` does not fail it; `--check --overwrite` counts one.
|
|
516
|
+
See [handbook chapter 11](../handbook/11-sig-gen.md#keeping-sig-current-in-ci).
|
|
517
|
+
|
|
518
|
+
A method declared inline with `# @rbs` / `#:` is written as that
|
|
519
|
+
declaration, not as what its body infers; a parameter-only
|
|
520
|
+
annotation keeps its parameters and takes the return from the body,
|
|
521
|
+
and `initialize` is always `-> void`. When `sig/` already declares
|
|
522
|
+
the method (a `def` or an `attr_*`) and the two disagree as types —
|
|
523
|
+
parameter names and union spelling do not count, overload order
|
|
524
|
+
does — sig-gen changes neither: the method is refused
|
|
525
|
+
(`sig.skipped.inline-differs`, listed under `refused` in `--format=json`),
|
|
526
|
+
and `--write` / `--check` exit `1` until you make them agree or pass
|
|
527
|
+
`--overwrite`, which replaces the whole `sig/` member with the inline
|
|
528
|
+
declaration. For a parameter-only annotation only the parameters are
|
|
529
|
+
compared; the return follows the ordinary proposal rules. A class
|
|
530
|
+
made generic inline is not written unless `sig/` declares it with the
|
|
531
|
+
same type parameters. A project whose Steep reads the same
|
|
532
|
+
annotations sets `sig_gen.inline_declared: skip` in `.rigor.yml` to
|
|
533
|
+
keep those methods out of `sig/`. See
|
|
534
|
+
[handbook chapter 11](../handbook/11-sig-gen.md#methods-declared-inline).
|
|
535
|
+
|
|
536
|
+
When `.rigor.yml` carries an `effects:` block, sig-gen also writes
|
|
537
|
+
`%a{pure}` above a method whose effect summary is **exhaustive**
|
|
538
|
+
(every call it reaches was resolved), **undischarged** (nothing in
|
|
539
|
+
its footprint is only invisible because `effects.tolerated:` says
|
|
540
|
+
so), **claimed** (every callee is described by a catalogue row, a
|
|
541
|
+
plugin, an envelope or a project definition), carries no authored
|
|
542
|
+
bound of its own, and is free of surviving labels in the `≤` lane. Nothing else is annotated, and
|
|
543
|
+
`--effect-envelopes` adds Rigor's own labelled spelling for methods
|
|
544
|
+
that do have a footprint. With no `effects:` block the output is
|
|
545
|
+
byte-for-byte what it was before. See
|
|
546
|
+
[handbook chapter 11](../handbook/11-sig-gen.md#emitting-effect-annotations).
|
|
547
|
+
|
|
487
548
|
Every signature is parsed before it is emitted. A method whose
|
|
488
549
|
generated RBS does not parse is **skipped** (`sig.skipped.unrenderable-rbs`)
|
|
489
550
|
and reported on stderr rather than written — an unparseable
|
|
@@ -60,6 +60,7 @@ cache:
|
|
|
60
60
|
| `target_ruby` | String | `"4.0"` | The Ruby version *your* project runs — `"X.Y"`, `"X.Y.Z"`, or `"latest"`. Independent of the Ruby Rigor itself runs on. |
|
|
61
61
|
| `paths` | Array | `["lib"]` | Directories or files to analyse. |
|
|
62
62
|
| `exclude` | Array | `[]` | Glob patterns to skip. `vendor/bundle`, `.bundle`, and `node_modules` are always excluded. |
|
|
63
|
+
| `test_paths` | Array | `nil` | The project's test roots: the directories (or files) holding its tests. Relative entries resolve against the config file's directory. Unset auto-detects whichever of `spec/` and `test/` exist; `[]` declares none. `rigor sig-gen --params=observed` reads call sites there to type parameters, and names on stderr a declared root that does not exist. Test roots are not analysed unless `paths:` also lists them, and changing them invalidates no cache. |
|
|
63
64
|
| `includes` | Array | `[]` | Other config files to layer underneath this one. |
|
|
64
65
|
| `fold_platform_specific_paths` | Boolean | `false` | Resolve Ruby-version-conditional load paths when discovering sources. |
|
|
65
66
|
| `parameter_inference` | Boolean | `false` | Opt-in call-site parameter type inference on the `check` walk ([ADR-67](../adr/67-parameter-type-inference.md) WD6). When `true`, an undeclared `def` / `initialize` / setter parameter is typed to the union of its resolved call-site argument types, sharpening downstream ivar reads, folds, and protection coverage. Precision-additive only — the negative rules never fire against an inferred parameter. Cannot be combined with `--incremental`. |
|
|
@@ -73,6 +74,12 @@ cache:
|
|
|
73
74
|
| `pre_eval` | Array | `[]` | Files (or globs) walked before per-file analysis, to register project monkey-patches and publish their top-level constants project-wide. |
|
|
74
75
|
| `plugins` | Array | `[]` | Plugins to activate — see [Using plugins](07-plugins.md). |
|
|
75
76
|
|
|
77
|
+
### Signature generation
|
|
78
|
+
|
|
79
|
+
| Key | Type | Default | Meaning |
|
|
80
|
+
| --- | --- | --- | --- |
|
|
81
|
+
| `sig_gen.inline_declared` | String | `"write"` | What `rigor sig-gen` does with a method already declared inline by `# @rbs` / `#:`. `write` copies the inline declaration into `sig/`, so the generated signature is the complete contract a gem ships, and refuses (exit 1) a `sig/` declaration that later disagrees with the inline one until the two agree or `--overwrite` replaces it. `skip` leaves every method the inline reader declares out of `sig/` (`sig.skipped.inline-declared`): set it when Steep reads the same annotations (`inline: true` beside `signature "sig"`), where a copy would declare each method twice (`DuplicatedMethodDefinition`). The cost of `skip`: a consumer reading only your shipped `sig/` never sees those methods, and an annotation on one — a Rigor refinement included — takes effect only where the source itself is analysed. Any other value, or any other key under `sig_gen:`, is a load error. Only `rigor sig-gen` reads this key, and changing it invalidates no cache. See [handbook chapter 11](../handbook/11-sig-gen.md#methods-declared-inline). |
|
|
82
|
+
|
|
76
83
|
### Config validation warnings
|
|
77
84
|
|
|
78
85
|
`rigor check` warns on STDERR when a configured value silently resolves to
|
|
@@ -16,6 +16,7 @@ Every rule has a two-segment `family.rule` identifier:
|
|
|
16
16
|
| `call` | Call sites — undefined methods, arity, argument types, nil receivers. |
|
|
17
17
|
| `flow` | Control-flow proofs — always-raises, dead branches, constant conditions. |
|
|
18
18
|
| `def` | Method definitions — return types, ivar writes, visibility. |
|
|
19
|
+
| `global` | Writes to special globals — a value the setter rejects, a read-only variable. |
|
|
19
20
|
| `assert` | `assert_type` checks. |
|
|
20
21
|
| `dump` | `dump_type` notices. |
|
|
21
22
|
|
|
@@ -70,11 +71,14 @@ carries no `documentation_url`.
|
|
|
70
71
|
| <a id="rule-def-override-visibility-reduced"></a>`def.override-visibility-reduced` | An override reduces the visibility it inherits from a project-defined ancestor. | high |
|
|
71
72
|
| <a id="rule-def-override-return-widened"></a>`def.override-return-widened` | An override's declared return type widens the inherited return (covariance). | high |
|
|
72
73
|
| <a id="rule-def-override-param-narrowed"></a>`def.override-param-narrowed` | An override narrows an inherited parameter type (contravariance). | high |
|
|
74
|
+
| <a id="rule-global-write-type-mismatch"></a>`global.write-type-mismatch` | A special global is assigned a literal its setter rejects, so the write raises `TypeError` every time it runs. `$/`, `$-0`, `$,` and `$\` take only a String or nil (`$/ = 1` and `$/ = /x/` report). `$;` and `$-F` take a String, a Regexp, nil, or an object with `to_str`. `$~` takes a MatchData or nil. `$0` and `$PROGRAM_NAME` take a String or an object with `to_str`, so `$0 = nil` and `$0 = :name` report. `$.` takes an Integer, a Float, or an object with `to_int`, so `$. = "3"` reports and `$. = 3r` does not. `$-i` takes a String, nil, false, or an object with `to_str`. `$stdout`, `$>` and `$stderr` take anything that responds to `write`: `$stdout = 1` reports, `$stdout = StringIO.new` does not. Ruby's setter decides, not the global's RBS type. Only a value written as a literal is judged — a number, a string, a symbol, an array, a hash or a regexp literal (interpolated or not), or `nil`, `true` or `false` — so a variable, a method call or a constant never reports, whatever its type. A literal stays silent when your program defines the method the setter asks for (`write`, `to_str`, `to_int`) or a `method_missing` / `respond_to_missing?` / `respond_to?` anywhere, in any spelling and on any class — Rigor cannot always tell which objects such a definition reaches, so it does not try — or when a top-level `include` / `extend` or an ancestor's `include` names a module Rigor has no RBS for. For `$stdout`, `$>` and `$stderr` it also stays silent where a `using` is in effect for a refinement that may add `write`. `$/`, `$-0`, `$,`, `$\` and `$~` accept only their classes, so none of that silences them. A write to `$stdin` is never checked, and a special any file aliases (`alias $stdout $out`, including a `pre_eval:` file) is exempt. `warning` under `lenient`. | high |
|
|
75
|
+
| <a id="rule-global-readonly-write"></a>`global.readonly-write` | A read-only special global is written — `$!`, `$$`, `$?`, `$<`, `$FILENAME`, `$*`, `$:` / `$LOAD_PATH` / `$-I`, `$"` / `$LOADED_FEATURES`, `$-W`, `$-p`, `$-l` or `$-a` — which raises `NameError` whatever the value. Covers `$g = value`, `$g += value` and a multiple-assignment target. `$LOAD_PATH ||= []` never writes and `$LOAD_PATH << dir` is a method call, so neither fires. A special any file aliases (`alias $! $err`) is exempt. An error in every profile. | high |
|
|
73
76
|
| <a id="rule-static-value-use-void"></a>`static.value-use.void` | A value recovered from an author-declared `-> void` return is used in value context (an assignment right-hand side, a call receiver, or a call argument). Off by default; reaches a run only through the `use-of-void-value` bleeding-edge feature (ADR-100). A bare-statement `void` call and a legitimate `top` value both stay silent. | high |
|
|
74
77
|
| <a id="rule-effect-envelope-exceeded"></a>`effect.envelope-exceeded` | A method performs an effect its declared envelope does not admit — its proven effect labels (its own body plus everything it calls) are not covered by the `%a{pure}` or `%a{rigor:v1:effect …}` bound written on it or on its class. Opt-in twice over: it needs an `effects:` block in `.rigor.yml` and an envelope you wrote. Positioned at the Ruby `def`. Unproven ("and possibly more") effects never fire, and `mutate.local` is tolerated by every envelope. | high |
|
|
75
78
|
| <a id="rule-effect-liskov-widened"></a>`effect.liskov-widened` | An override escapes the envelope written on the method it overrides. A `PgRepo` is usable wherever a `Repo` is, so a `%a{rigor:v1:effect io.db}` on `Repo#find` binds `PgRepo#find` too: an implementation may be purer than the bound it inherits, never less pure. Either what the override *does* exceeds the inherited bound, or the envelope the override *declares for itself* is wider than it. Both sides must be authored — nothing fires unless someone wrote an envelope on the ancestor — and only subclassing counts, not `include`. Positioned at the override's `def`. Needs an `effects:` block. | high |
|
|
76
79
|
| <a id="rule-effect-unknown-label"></a>`effect.unknown-label` | An effect declaration names a label the registry does not know — a typo in an envelope (`%a{rigor:v1:effect io.bd}`), or a member of `effects.tolerated:`. The whole tag then reads as unbounded, so the declaration quietly stops doing anything; this says so. Positioned at the declaration: the `.rbs` line, the `.rb` line for an rbs-inline annotation, or `.rigor.yml` for a config value. `# rigor:disable` comments are not read out of `.rbs` or `.rigor.yml`, so use `disable:` or the baseline there. Only fires where the spelling is evidently meant to be a label (close to a known one, next to a known one, dotted, or retired) — a word nothing resembles stays silent, because you may be opening your own vocabulary. Needs an `effects:` block. | high |
|
|
77
80
|
| <a id="rule-effect-annotations-unchecked"></a>`effect.annotations-unchecked` | Your signatures carry `%a{pure}` / `%a{rigor:v1:effect …}` but `.rigor.yml` has no `effects:` block, so nothing checks them. One `:info` per run, positioned at the first annotation. An annotation never turns effect collection on by itself — that would make one line in one file more expensive for every run — so this is how it tells you instead. Add `effects: {}` to opt in, or `disable:` it to keep the annotations documentary. Reads both annotation lanes — `sig/*.rbs` and rbs-inline comments — on every run, a warm `--incremental` with nothing changed included. | — |
|
|
81
|
+
| <a id="rule-rbs-contradicting-signature"></a>`rbs.contradicting-signature` | A method is declared both in your `sig/` and by an inline `# @rbs` / `#:` annotation, and the two provably contradict — no value or call satisfies both: in the return or a parameter every call must pass, the two types share no value (`::String` against `::Integer`). Only absolutely written Ruby core or stdlib classes count: a module such as `Comparable`, a class your project declares, a gem's class, or a relative name like plain `String` never does, or two keyword-free declarations accept positional counts that cannot meet, or one requires a keyword the other cannot take in any form. Overloads are paired by correspondence, not order. Also fires when a member-level `%a{rigor:v1:return: …}` / `%a{rigor:v1:param: …}` refinement shares no value with the type its own member declares. Positioned at the `.rbs` member; Rigor reads that declaration. A stale generated signature is the usual cause: regenerate it, or fix the annotation. Two declarations where one refines the other merge to the more precise one without a word, and a pair Rigor cannot rank drops the inline side with a `source-rbs-annotation-not-honoured` `:info` instead ([Precedence](plugins/rigor-rbs-inline.md#precedence)). An error in every profile. | high |
|
|
78
82
|
| <a id="rule-suppression-unknown-rule"></a>`suppression.unknown-rule` | A `# rigor:disable[-file]` comment names a rule that does not exist (typically a typo), so the suppression silently does nothing. `plugin.`-prefixed tokens are never flagged. | high |
|
|
79
83
|
| <a id="rule-suppression-empty"></a>`suppression.empty` | A `# rigor:disable[-file]` comment lists no rules, so it suppresses nothing. | high |
|
|
80
84
|
| <a id="rule-suppression-unknown-marker"></a>`suppression.unknown-marker` | A comment uses a suppression marker Rigor does not recognise — typically the RuboCop reflex `# rigor:disable-next-line <rule>` or `# rigor:enable <rule>`. Rigor's only markers are `# rigor:disable <rules>` (suppresses on its own line) and `# rigor:disable-file <rules>`, so the comment suppresses nothing. | high |
|
data/docs/manual/07-plugins.md
CHANGED
|
@@ -95,7 +95,7 @@ with the `plugins_isolation:` configuration key or the
|
|
|
95
95
|
| --- | --- |
|
|
96
96
|
| `process` (default) | Run the call in a forked, crash-contained worker, so the target library's monkey-patches and any crash never contaminate Rigor. Falls back to `none` where `fork` is unavailable (Windows / JRuby). |
|
|
97
97
|
| `none` | Load the library into Rigor's own process and call it directly. |
|
|
98
|
-
| `ruby_box` | Run inside an experimental `Ruby::Box` sandbox. This needs the `RUBY_BOX=1` start flag, so the `rigor` launcher re-execs itself with it set when you select this strategy. **Environment variable only** — the configuration file is read long after Ruby has booted, so `plugins_isolation: ruby_box` is reported as a configuration error instead. |
|
|
98
|
+
| `ruby_box` | Run inside an experimental `Ruby::Box` sandbox. This needs the `RUBY_BOX=1` start flag, so the `rigor` launcher re-execs itself with it set when you select this strategy. It also needs a Ruby that fixes [Ruby Bug #22260](https://bugs.ruby-lang.org/issues/22260), which no release up to 4.0.7 does. The launcher checks for the fix first, and without it prints a warning and uses the configured strategy instead. **Environment variable only** — the configuration file is read long after Ruby has booted, so `plugins_isolation: ruby_box` is reported as a configuration error instead. |
|
|
99
99
|
|
|
100
100
|
The environment variable wins over `plugins_isolation:`, so you can
|
|
101
101
|
override a project's committed choice for one invocation. The legacy
|
|
@@ -358,8 +358,9 @@ Generate RBS skeleton signatures inferred from Ruby source files.
|
|
|
358
358
|
| `params` | `"untyped"` \| `"observed"` | no | `"untyped"` |
|
|
359
359
|
| `config` | `string` | no | session default |
|
|
360
360
|
|
|
361
|
-
`params: "observed"` harvests call-site argument types from
|
|
362
|
-
|
|
361
|
+
`params: "observed"` harvests call-site argument types from the
|
|
362
|
+
project's test roots: the configured `test_paths:`, or whichever of
|
|
363
|
+
`spec/` and `test/` exist.
|
|
363
364
|
|
|
364
365
|
**Returns:** JSON — the same as `rigor sig-gen --print --format json`.
|
|
365
366
|
|
|
@@ -22,25 +22,56 @@ The plain `() -> String` stays the compatibility contract; the
|
|
|
22
22
|
annotation tells Rigor the return is a non-empty string.
|
|
23
23
|
|
|
24
24
|
You may also write any of them **in a `.rb` file**, as an
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
inline-RBS comment — `%a{}` is RBS's own annotation grammar, and
|
|
26
|
+
the annotation reaches Rigor on the same path the generated
|
|
27
|
+
signature does. Rigor reads three spellings:
|
|
28
28
|
|
|
29
29
|
```rb
|
|
30
30
|
# rbs_inline: enabled
|
|
31
31
|
|
|
32
32
|
class Reader
|
|
33
|
+
# Own line: the annotation, then the type on its own tag.
|
|
33
34
|
# @rbs %a{rigor:v1:return: non-empty-string}
|
|
34
35
|
# @rbs return: String
|
|
35
36
|
def read_name = "x"
|
|
37
|
+
|
|
38
|
+
# Same line, `@rbs` method type.
|
|
39
|
+
# @rbs %a{rigor:v1:return: non-empty-string} () -> String
|
|
40
|
+
def title = "x"
|
|
41
|
+
|
|
42
|
+
# Same line, `#:` method type.
|
|
43
|
+
#: %a{rigor:v1:return: non-empty-string} () -> String
|
|
44
|
+
def label = "x"
|
|
36
45
|
end
|
|
37
46
|
```
|
|
38
47
|
|
|
48
|
+
Several annotations may stand before the method type
|
|
49
|
+
(`#: %a{pure} %a{rigor:v1:return: non-empty-string} () -> String`).
|
|
50
|
+
The other inline-RBS readers do not accept all three:
|
|
51
|
+
|
|
52
|
+
| spelling | rbs's built-in inline parser, Steep with `inline: true` | the `rbs-inline` gem's own `--output` |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| own line | syntax error (`expected a token pARROW`), annotation lost | annotation kept |
|
|
55
|
+
| same line | annotation and method type kept | annotation kept, method type **dropped** |
|
|
56
|
+
|
|
57
|
+
Rigor keeps both halves of every row. If you also run Steep in
|
|
58
|
+
inline mode, use the same-line spelling. The measurement is in
|
|
59
|
+
[ADR-111](../adr/111-inline-refinement-carrier.md). If the method type after a same-line annotation
|
|
60
|
+
does not parse, the method is left untyped and Rigor reports it as
|
|
61
|
+
[`plugin.rbs-inline.source-rbs-annotation-not-honoured`](plugins/rigor-rbs-inline.md#same-line-annotations).
|
|
62
|
+
|
|
39
63
|
This needs the `rbs-inline` library installed; Rigor ingests
|
|
40
64
|
inline annotations by default when it is
|
|
41
|
-
([ADR-93](../adr/93-default-rbs-inline-ingestion.md)).
|
|
42
|
-
|
|
43
|
-
|
|
65
|
+
([ADR-93](../adr/93-default-rbs-inline-ingestion.md)). `# rigor:`
|
|
66
|
+
comments remain suppression-only.
|
|
67
|
+
|
|
68
|
+
A dedicated `# @extrbs` comment for what RBS cannot spell is
|
|
69
|
+
accepted in [ADR-112](../adr/112-extrbs-comment-channel.md) but
|
|
70
|
+
not implemented yet ([#1073](https://github.com/rigortype/rigor/issues/1073)).
|
|
71
|
+
Until it ships, the `%a{}` forms above are the inline route. A type
|
|
72
|
+
plain RBS can spell, such as `:asc | :desc`, belongs in `# @rbs` or
|
|
73
|
+
`#:` either way.
|
|
74
|
+
|
|
44
75
|
This page is the *operational* reference — the directives you can
|
|
45
76
|
write and their syntax. For the normative rules (conflict
|
|
46
77
|
handling, merging, provenance) see
|
|
@@ -183,7 +214,9 @@ class UserRepository
|
|
|
183
214
|
end
|
|
184
215
|
```
|
|
185
216
|
|
|
186
|
-
The same two work as rbs-inline comments in a `.rb` file
|
|
217
|
+
The same two work as rbs-inline comments in a `.rb` file, in any of
|
|
218
|
+
the three spellings shown above — the own-line
|
|
219
|
+
one here:
|
|
187
220
|
|
|
188
221
|
```rb
|
|
189
222
|
# rbs_inline: enabled
|
|
@@ -214,6 +214,16 @@ rigor effects --pure
|
|
|
214
214
|
436 methods on Redmine, and they are your `%a{pure}` candidates — the on-ramp to
|
|
215
215
|
the last section of this chapter.
|
|
216
216
|
|
|
217
|
+
You do not have to write them all out by hand. With the `effects:` block in
|
|
218
|
+
place, `rigor sig-gen` annotates the methods in this set whose signature it is
|
|
219
|
+
proposing anyway: `%a{pure}` above every method that is exhaustive, undischarged,
|
|
220
|
+
free of surviving `≤` labels, not already carrying a bound you wrote, and calling
|
|
221
|
+
nothing the analyzer had no description for; and, under `--effect-envelopes`, the labelled spelling for the methods that
|
|
222
|
+
do have a footprint. A method that is clean only because `effects.tolerated:`
|
|
223
|
+
says so is deliberately left bare — the annotation would travel to readers who
|
|
224
|
+
do not share your tolerated list. See
|
|
225
|
+
[handbook chapter 11](../handbook/11-sig-gen.md#emitting-effect-annotations).
|
|
226
|
+
|
|
217
227
|
### Asking it a question
|
|
218
228
|
|
|
219
229
|
**By label.** The question this chapter opens with, in one command:
|
|
@@ -54,6 +54,9 @@ The full catalogue, with a one-line scope for every plugin, is
|
|
|
54
54
|
`Result` / `Option` unwrap types and synthesises `Enum` variants.
|
|
55
55
|
- [rigor-pundit](rigor-pundit.md) — policy-class existence and
|
|
56
56
|
`authorize(record, :action)` predicate validation.
|
|
57
|
+
- [rigor-active-model-serializers](rigor-active-model-serializers.md) —
|
|
58
|
+
types the `object` reader inside a serializer as the serializer's
|
|
59
|
+
model (no diagnostics).
|
|
57
60
|
- [rigor-sidekiq](rigor-sidekiq.md) — Sidekiq `Worker.perform_*`
|
|
58
61
|
argument arity against the discovered `#perform`.
|
|
59
62
|
- [rigor-actioncable](rigor-actioncable.md) — `broadcast_to` channel
|
|
@@ -62,6 +65,10 @@ The full catalogue, with a one-line scope for every plugin, is
|
|
|
62
65
|
through Minitest / Test::Unit assertions and spec matchers.
|
|
63
66
|
- [rigor-graphql](rigor-graphql.md) — GraphQL-Ruby type / enum / input
|
|
64
67
|
/ mutation table publication (cross-plugin facts, no diagnostics).
|
|
68
|
+
- [rigor-grape](rigor-grape.md) — types the `Grape::API` endpoint DSL
|
|
69
|
+
(`params`, `namespace`, verb macros, `desc`, `route_setting`) and
|
|
70
|
+
`Grape::Entity` `expose` declarations, including the `instance_eval`'d
|
|
71
|
+
block `self` bindings.
|
|
65
72
|
- [rigor-rspec-rails](rigor-rspec-rails.md) — `have_http_status`
|
|
66
73
|
argument validation (out-of-range codes, unknown status symbols).
|
|
67
74
|
- [rigor-shoulda-matchers](rigor-shoulda-matchers.md) — shoulda matcher
|
|
@@ -36,7 +36,7 @@ ActionCable.server.broadcast("chat_room_42", body: "hi") # warning: no such s
|
|
|
36
36
|
| `plugin.actioncable.broadcast-stream` | info | `ActionCable.server.broadcast("...", ...)` matched a registered `stream_from` literal |
|
|
37
37
|
| `plugin.actioncable.unknown-channel` | error | the receiver ends in `Channel` but is not in the index (with a did-you-mean) |
|
|
38
38
|
| `plugin.actioncable.unknown-stream` | warning | the literal stream name matched no `stream_from` registration (with a did-you-mean) |
|
|
39
|
-
| `plugin.actioncable.load-error` | warning | channel discovery failed (parse/read error) — once per
|
|
39
|
+
| `plugin.actioncable.load-error` | warning | channel discovery failed (parse/read error) — once per run, on `.rigor.yml` |
|
|
40
40
|
|
|
41
41
|
The `unknown-stream` check is **suppressed** when any discovered
|
|
42
42
|
channel registers a dynamic stream (`stream_from interpolated_string`
|
|
@@ -100,6 +100,13 @@ default.
|
|
|
100
100
|
- **The `#receive` contract is path-scoped, not class-scoped** (ADR-28):
|
|
101
101
|
any `def receive(data)` defined anywhere under `channel_search_paths`
|
|
102
102
|
is typed, even on a class that isn't an ActionCable channel.
|
|
103
|
+
- **The "failed to discover channels" warning is run-scoped.** It is
|
|
104
|
+
a fact about your configuration, not about any one source file, so
|
|
105
|
+
it is reported once per run on `.rigor.yml` rather than repeated on
|
|
106
|
+
every analysed file. It is `:warning`, so if you baselined it at its
|
|
107
|
+
old position that entry no longer matches and the row fails a
|
|
108
|
+
`--fail-on=warning` run — regenerate with `rigor baseline
|
|
109
|
+
regenerate`.
|
|
103
110
|
|
|
104
111
|
## Plugin internals
|
|
105
112
|
|
|
@@ -69,6 +69,13 @@ plugins:
|
|
|
69
69
|
which the report already records. Rooting every class under
|
|
70
70
|
`app/mailers` instead would mark a mailer nothing sends reachable
|
|
71
71
|
forever on no evidence.
|
|
72
|
+
- **The "failed to discover mailers" warning is run-scoped.** It is
|
|
73
|
+
a fact about your configuration, not about any one source file, so
|
|
74
|
+
it is reported once per run on `.rigor.yml` rather than repeated on
|
|
75
|
+
every analysed file. It is `:warning`, so if you baselined it at its
|
|
76
|
+
old position that entry no longer matches and the row fails a
|
|
77
|
+
`--fail-on=warning` run — regenerate with `rigor baseline
|
|
78
|
+
regenerate`.
|
|
72
79
|
|
|
73
80
|
## Plugin internals
|
|
74
81
|
|
|
@@ -54,6 +54,7 @@ plugins:
|
|
|
54
54
|
config:
|
|
55
55
|
controller_search_paths: ["app/controllers"] # default
|
|
56
56
|
view_search_paths: ["app/views"] # default
|
|
57
|
+
view_type_checks: false # default
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
## What it types
|
|
@@ -111,8 +112,250 @@ reason `ActionController::Parameters` and the `ActionDispatch` readers
|
|
|
111
112
|
above stay undeclared; their leniency is what makes the `params`
|
|
112
113
|
typing safe.
|
|
113
114
|
|
|
115
|
+
## ERB templates as effect units
|
|
116
|
+
|
|
117
|
+
Every `app/views/**/*.erb` is compiled to Ruby and analysed as one
|
|
118
|
+
**effect unit**, keyed `view:users/show.html` — Rails' own logical
|
|
119
|
+
name with the handler dropped, so an ERB → Haml rewrite is not a
|
|
120
|
+
rename. Nothing has to be enabled: activating the plugin is what
|
|
121
|
+
claims the templates.
|
|
122
|
+
|
|
123
|
+
What that buys is the answer to *"what does this request actually
|
|
124
|
+
do"* past the `render` line. A partial that calls `@user.update`
|
|
125
|
+
reports `io.db.write` at `app/views/users/_card.html.erb`, a
|
|
126
|
+
`Time.now` in a layout fragment reports `nondet.time`, a leftover
|
|
127
|
+
`puts` reports `io.output.stdout` — all of it in `rigor effects` and
|
|
128
|
+
in the snapshot, so a template that starts writing shows up in a
|
|
129
|
+
diff.
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
$ rigor effects
|
|
133
|
+
view:users/_card.html: [mutate.local, nondet.time] ≤ [io.db.write] …?
|
|
134
|
+
view:users/show.html: [mutate.local] ≤ [io.db.read] …?
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**The compiler** is Erubi when it resolves in your project's bundle
|
|
138
|
+
— Rails' own — and stdlib `ERB` otherwise. Erubi is never added to
|
|
139
|
+
your Gemfile and is not a Rigor dependency
|
|
140
|
+
([ADR-90](../../adr/90-target-library-resolution-from-project-bundle.md)).
|
|
141
|
+
Either way the line map is measured rather than assumed, so a
|
|
142
|
+
finding names the template's own line; the column is always 1,
|
|
143
|
+
because a compiler rewrites the text of each line and a column of
|
|
144
|
+
the compiled Ruby would name nothing you wrote.
|
|
145
|
+
|
|
146
|
+
**What `self` is.** `ActionView::Base`, declared so the name
|
|
147
|
+
resolves and **open** so its method surface stays lenient. That is
|
|
148
|
+
what keeps `link_to`, `form_with`, `t`, `content_for`, your own
|
|
149
|
+
`ApplicationHelper` methods and every route helper from drawing a
|
|
150
|
+
finding per line.
|
|
151
|
+
|
|
152
|
+
**What is in scope.** `@ivars` are seeded from the controller
|
|
153
|
+
actions that render the template — the implicit render
|
|
154
|
+
(`UsersController#show` → `users/show`) and explicit
|
|
155
|
+
`render :edit` / `render "admin/form"`. Two restrictions keep a seed
|
|
156
|
+
from claiming a type the template will not find. Only assignments
|
|
157
|
+
whose right-hand side is one record and cannot be `nil` contribute:
|
|
158
|
+
`User.find(id)` and `Model.new` do; `find_by` never does, nor a call
|
|
159
|
+
written to return several records — `User.find(a, b)`,
|
|
160
|
+
`User.find([1, 2])`, `User.find(*ids)`, `User.create([…])` or a
|
|
161
|
+
`find` with a block. A single argument that only holds an Array at
|
|
162
|
+
runtime (`User.find(ids)`) still seeds the model. And only
|
|
163
|
+
assignments the action reaches on **every** path contribute — not
|
|
164
|
+
one inside an `if`, a `case`, a `rescue`, a loop or a block, and
|
|
165
|
+
nothing from a `before_action` carrying `if:` / `unless:`. Anything
|
|
166
|
+
else leaves the ivar unseeded, which reads as `Dynamic` and is
|
|
167
|
+
silent. A partial inherits the assigns of its own directory, because
|
|
168
|
+
an ivar is not a local. Locals come from the Rails 7.1 strict-locals
|
|
169
|
+
comment:
|
|
170
|
+
|
|
171
|
+
```erb
|
|
172
|
+
<%# locals: (user:, admin: false) %>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**`call.*` findings are off inside templates by default**, while the
|
|
176
|
+
synthesised receivers are still coarse — measured on redmine and
|
|
177
|
+
mastodon, the feature adds **zero** new findings to either
|
|
178
|
+
([the measurement note](../../notes/20260917-erb-template-units.md)).
|
|
179
|
+
Set `view_type_checks: true` to opt in and have `@user.nmae` in
|
|
180
|
+
`show.html.erb` reported like any other call.
|
|
181
|
+
|
|
182
|
+
`flow.*` **reports in a template like anywhere else.** It was
|
|
183
|
+
suppressed alongside `call.*` for one measured reason — a partial's
|
|
184
|
+
optional-local preamble (`<% path = nil unless defined? path %>`)
|
|
185
|
+
really did assign nil, because nothing told the unit its render site
|
|
186
|
+
had bound `path`. Render-site `locals:` are traced now, and the same
|
|
187
|
+
two projects re-measured with the family reporting are byte-identical
|
|
188
|
+
to the runs with it suppressed ([the #1047 note](../../notes/20260917-render-locals-and-layouts.md)).
|
|
189
|
+
|
|
190
|
+
### Locals come from the render site
|
|
191
|
+
|
|
192
|
+
A partial's parameters are bound by whoever renders it, and every
|
|
193
|
+
spelling of that is read — on both sides of the render:
|
|
194
|
+
|
|
195
|
+
```erb
|
|
196
|
+
<%= render partial: "card", locals: { user: @user } %>
|
|
197
|
+
<%= render "card", user: @user %> <%# a view's trailing hash IS locals %>
|
|
198
|
+
<%= render partial: "card", collection: @users, as: :row %>
|
|
199
|
+
<%= render partial: "card", object: @user %>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`collection:` binds `row`, `row_counter` and `row_iteration`;
|
|
203
|
+
`object:` and `as:` bind one local named after the partial or after
|
|
204
|
+
`as:`. A controller's `render partial: …, locals: …` is read the same
|
|
205
|
+
way — but a controller's *trailing hash* is options, so
|
|
206
|
+
`render :show, status: :ok` binds nothing.
|
|
207
|
+
|
|
208
|
+
A partial rendered from several sites gets the **union** of the
|
|
209
|
+
names. A name only some of them pass is still bound, typed
|
|
210
|
+
`Dynamic` — absence is what produced the false positives above.
|
|
211
|
+
A **type** is claimed only where every site agrees on one it could
|
|
212
|
+
settle from the call itself (`User.find(1)`, or an ivar the rendering
|
|
213
|
+
action's own seeds typed); anything else is `Dynamic`. A
|
|
214
|
+
strict-locals comment still wins where a template carries one.
|
|
215
|
+
|
|
216
|
+
A partial's **own** optional-local test counts too:
|
|
217
|
+
`<% size = nil unless defined?(size) %>`, `local_assigns[:size]` and
|
|
218
|
+
`local_assigns.key?(:size)` bind `size` even when no render site the
|
|
219
|
+
plugin can read passes it — a `locals: opts` hash, a `render` from a
|
|
220
|
+
helper, or a local with a default nobody passes. A name a helper under
|
|
221
|
+
`app/helpers` defines is left alone, so
|
|
222
|
+
`<% if defined?(current_user) %>` stays a helper call. A helper that a
|
|
223
|
+
gem or a concern defines is not seen by that scan, and its name is
|
|
224
|
+
bound as a `Dynamic` local instead.
|
|
225
|
+
|
|
226
|
+
### The controller → template edge
|
|
227
|
+
|
|
228
|
+
A controller action's summary **includes what its template does**.
|
|
229
|
+
`render :show`, `render "show"`, `render "admin/form"`,
|
|
230
|
+
`render template:`, `render action:`, `render partial:` (with or
|
|
231
|
+
without `collection:`) and the implicit render of
|
|
232
|
+
`<controller>/<action>` all reach the template's own unit, and a
|
|
233
|
+
template reaches the partials *it* renders — so an `io.db.write` in
|
|
234
|
+
`app/views/users/_card.html.erb` shows up on `UsersController#show`
|
|
235
|
+
three hops away, and `rigor effects explain` prints the path.
|
|
236
|
+
|
|
237
|
+
A partial is looked up in the rendering template's own format, with
|
|
238
|
+
the one fallback Action View itself hard-codes: a `.js.erb` template
|
|
239
|
+
reaches `_list.js.erb` where it exists and `_list.html.erb`
|
|
240
|
+
otherwise, which is how "a JS response that injects rendered HTML"
|
|
241
|
+
works at all. Only one of the two is joined, never both. A `.json`,
|
|
242
|
+
`.xml` or `.turbo_stream` template gets no fallback — what those fall
|
|
243
|
+
back to depends on the request's `Accept` header, which the source
|
|
244
|
+
does not say — and neither does a render site that names its format
|
|
245
|
+
(`formats: [:js]`, `render "list.js"`) or a controller-side `render`.
|
|
246
|
+
|
|
247
|
+
The fallback stops at a template that **exists and produced no
|
|
248
|
+
unit**: a `_list.js.haml`, or a `_list.js.erb` whose compiled Ruby
|
|
249
|
+
does not parse. Rails runs that file, so the `.html` one's effects
|
|
250
|
+
are not what the render produces, and the taint stays. That is why
|
|
251
|
+
this plugin claims `app/views/**/*.{haml,slim,jbuilder,builder,rabl,ruby}`
|
|
252
|
+
and compiles none of them: the claim is how the engine learns a
|
|
253
|
+
template is there. A handler outside that list is invisible, and a
|
|
254
|
+
render of one still falls back.
|
|
255
|
+
|
|
256
|
+
Two approximations ride along, and each costs labels rather than a
|
|
257
|
+
taint. A partial reached *through* the fallback renders its own
|
|
258
|
+
partials in `html`, while Action View's context is still
|
|
259
|
+
`[:js, :html]`; where a nested partial exists in both formats, the
|
|
260
|
+
`.js` template gets the `.html` one's labels. And a template whose
|
|
261
|
+
name carries no format at all (`_row.jbuilder`) blocks nothing, since
|
|
262
|
+
its key has no format to block — which happens to agree with Rails,
|
|
263
|
+
which ranks a formatted template above it. Both are zero occurrences
|
|
264
|
+
on the measured corpus.
|
|
265
|
+
|
|
266
|
+
The `template-not-analysed` taint on a `render` is discharged
|
|
267
|
+
exactly when the edge lands on a real unit. It **stays** when it
|
|
268
|
+
does not, and both cases are common enough to name:
|
|
269
|
+
|
|
270
|
+
- the target is computed — `render params[:view]`, or
|
|
271
|
+
`render formats: some_format`. The render site is read from
|
|
272
|
+
literals only, so anything computed keeps the honest "and possibly
|
|
273
|
+
more";
|
|
274
|
+
- the target names no template this plugin compiled — a `render
|
|
275
|
+
partial: @thing`, or a partial that exists in neither the requested
|
|
276
|
+
format nor its fallback;
|
|
277
|
+
- the template is outside `app/views/**/*.erb` — a Haml, Slim or
|
|
278
|
+
Jbuilder view, which this plugin does not claim.
|
|
279
|
+
|
|
280
|
+
`render json:`, `render plain:` and the rest of the non-template
|
|
281
|
+
family are left alone: they render no template, the rule declines,
|
|
282
|
+
and the row reads exactly as it did before.
|
|
283
|
+
|
|
284
|
+
An action that answered for itself is **not** edged to the
|
|
285
|
+
conventional template. `redirect_to`, `head`, `send_data` and
|
|
286
|
+
`send_file` each mean the implicit render did not happen, and
|
|
287
|
+
attributing `users/away` to an action that redirects would be a view
|
|
288
|
+
it never runs.
|
|
289
|
+
|
|
290
|
+
### Holding views to an effect budget
|
|
291
|
+
|
|
292
|
+
A view unit is an `effects.envelopes:` subject like any class, and a
|
|
293
|
+
finding is positioned in the template:
|
|
294
|
+
|
|
295
|
+
```yaml
|
|
296
|
+
effects:
|
|
297
|
+
envelopes:
|
|
298
|
+
- match: "app/views/**/*"
|
|
299
|
+
effect: [mutate.local]
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
A bound judges only what Rigor proved by reading code, so what this
|
|
303
|
+
catches in a view is what Rigor's own catalogue proves: a `puts`
|
|
304
|
+
(`io.output.stdout`) or a `Time.now` (`nondet.time`).
|
|
305
|
+
|
|
306
|
+
**Plugin-sourced labels are never judged by an envelope.** A plugin's
|
|
307
|
+
statement about a framework method — `User.find` is `io.db.read`,
|
|
308
|
+
`@user.update` is `io.db.write`, `perform_later` is `job.enqueue` —
|
|
309
|
+
rides the declared (`≤`) lane, and the envelope check reads the proven
|
|
310
|
+
one. Listing `io.db.read` in a view's envelope, or leaving it out,
|
|
311
|
+
changes nothing: the lazy `<%= user.posts.count %>` is not a finding
|
|
312
|
+
either way. That is a property of the whole Rails effect layer rather
|
|
313
|
+
than of views ([ADR-103](../../adr/103-effect-labels.md) WD17, ruled
|
|
314
|
+
in [#1059](https://github.com/rigortype/rigor/issues/1059)).
|
|
315
|
+
|
|
316
|
+
What notices a view that starts reading the database is the effect
|
|
317
|
+
snapshot. With `.rigor-effects.yml` committed, `rigor effects check`
|
|
318
|
+
fails by default on drift in either lane, and a template that gains a
|
|
319
|
+
query shows up in the diff as a declared-lane addition:
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
view:users/show.html ≤+ io.db.read
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
That is a ratchet on the whole project's recorded effects, reviewed as a
|
|
326
|
+
diff — not a policy scoped to `app/views/**/*`. See
|
|
327
|
+
[Effect labels](../19-effect-labels.md#what-a-bound-can-and-cannot-see).
|
|
328
|
+
|
|
114
329
|
## Limitations
|
|
115
330
|
|
|
331
|
+
- **A controller's own layout is not edged.** A layout is a unit
|
|
332
|
+
now, and a `render layout:` *inside a view* reaches it — but the
|
|
333
|
+
layout Rails wraps an action's template in (`layouts/application`,
|
|
334
|
+
or whatever `layout "base"` named) is not attributed to that
|
|
335
|
+
action. A callee rule may read the call's literals, the unit's
|
|
336
|
+
owner and the unit's key, and a layout's name is none of those:
|
|
337
|
+
it is a class-body declaration plus a convention lookup against
|
|
338
|
+
the view tree. So the layout's own effects reach a view that
|
|
339
|
+
renders it explicitly and no further.
|
|
340
|
+
- **`yield` in a layout is a `String` and nothing more.** The
|
|
341
|
+
keyword is rewritten into a declared call on the view context so
|
|
342
|
+
the body parses; what the inner template produced is never
|
|
343
|
+
modelled.
|
|
344
|
+
- **Unsaved render sites are not read in the editor.** The
|
|
345
|
+
render-site index reads templates and controllers from disk, so a
|
|
346
|
+
`locals:` you have typed but not saved does not reach the partial
|
|
347
|
+
until the save. A keystroke recompiles only the buffer, and a save
|
|
348
|
+
rebuilds the project's analysis as it always has. A view changed on
|
|
349
|
+
disk *without* a save the editor sees (a `git checkout`, a
|
|
350
|
+
formatter run elsewhere) recompiles every view on the next
|
|
351
|
+
publish, because a partial's locals can come from any of them. A
|
|
352
|
+
full `rigor check` does
|
|
353
|
+
the same compile work it did before — the index hands its compiled
|
|
354
|
+
sources to the unit transform rather than compiling twice.
|
|
355
|
+
- **ERB only, under `app/views`.** `template_globs:` is a manifest
|
|
356
|
+
row, read without running plugin code, so it cannot consult
|
|
357
|
+
`view_search_paths:`. Haml, Slim and Jbuilder are the same seam
|
|
358
|
+
behind a different compiler and are not claimed.
|
|
116
359
|
- **Implicit-self helpers only.** `*_path` / `*_url` calls with an
|
|
117
360
|
explicit receiver (`Rails.application.routes.url_helpers.x_path`)
|
|
118
361
|
are passed through.
|