rigortype 0.3.8 → 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/capability_roles/capability_roles.rbs +36 -0
- 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 +236 -17
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +132 -14
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +19 -1
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +525 -13
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +95 -6
- data/docs/manual/03-configuration.md +18 -0
- data/docs/manual/04-diagnostics.md +15 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/10-mcp-server.md +3 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +46 -8
- data/docs/manual/18-removing-dead-code.md +10 -8
- 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 +261 -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 +138 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +43 -7
- 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 +229 -1
- 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 +3 -3
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +6 -4
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +21 -6
- data/lib/rigor/analysis/check_rules/rule_walk.rb +41 -5
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/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 +38 -4
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +718 -79
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_recorder.rb +23 -0
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +15 -1
- data/lib/rigor/analysis/incremental.rb +42 -0
- data/lib/rigor/analysis/incremental_session.rb +117 -26
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +5 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +3 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +131 -1
- data/lib/rigor/analysis/run_cache_key.rb +21 -12
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +340 -33
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +18 -14
- data/lib/rigor/analysis/runner/pool_coordinator.rb +357 -97
- data/lib/rigor/analysis/runner/project_pre_passes.rb +38 -13
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +407 -51
- 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 +62 -20
- data/lib/rigor/bleeding_edge.rb +10 -24
- data/lib/rigor/builtins/hkt_builtins.rb +3 -2
- data/lib/rigor/builtins/imported_refinements.rb +159 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +21 -13
- data/lib/rigor/builtins/static_return_refinements.rb +7 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +63 -20
- data/lib/rigor/cache/engine_source.rb +33 -5
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +72 -6
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +112 -14
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +76 -32
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +4 -4
- data/lib/rigor/cli/docs_command.rb +6 -6
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +4 -4
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +1 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +212 -31
- data/lib/rigor/cli/skill_command.rb +2 -2
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +35 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +29 -11
- 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/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +89 -9
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +25 -9
- data/lib/rigor/configuration.rb +109 -7
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +12 -5
- 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 +15 -10
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +11 -4
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +9 -9
- data/lib/rigor/effects/envelope_index.rb +20 -8
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +5 -7
- data/lib/rigor/effects/identity.rb +20 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +22 -1
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +47 -37
- data/lib/rigor/effects/propagator.rb +297 -19
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +122 -73
- data/lib/rigor/effects/signature_sources.rb +4 -6
- data/lib/rigor/effects/snapshot.rb +12 -11
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +393 -38
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/effects/visibility.rb +101 -0
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +2 -2
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +68 -4
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +674 -79
- data/lib/rigor/environment.rb +174 -24
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +292 -17
- 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 +75 -30
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +7 -6
- data/lib/rigor/inference/budget_trace.rb +3 -6
- 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 +389 -18
- data/lib/rigor/inference/closure_escape_analyzer.rb +159 -17
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- 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/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +180 -0
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +2124 -468
- data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
- data/lib/rigor/inference/fork_map.rb +5 -7
- 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/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +4 -7
- data/lib/rigor/inference/index_write_widening.rb +16 -7
- 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 +168 -21
- 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 +87 -33
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +165 -111
- 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/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +1046 -69
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +129 -54
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -6
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +225 -25
- data/lib/rigor/inference/method_parameter_binder.rb +12 -9
- data/lib/rigor/inference/multi_target_binder.rb +341 -55
- data/lib/rigor/inference/mutation_rejoin.rb +171 -0
- data/lib/rigor/inference/mutation_widening.rb +156 -85
- data/lib/rigor/inference/narrowing.rb +835 -162
- 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 +153 -16
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +7 -7
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +9 -4
- data/lib/rigor/inference/project_patched_scanner.rb +10 -6
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +93 -4
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +76 -0
- 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 +5228 -627
- data/lib/rigor/inference/statement_evaluator.rb +3452 -595
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +97 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +7 -6
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +4 -4
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +7 -8
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +258 -26
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +65 -11
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +33 -13
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +94 -11
- data/lib/rigor/plugin/registry.rb +76 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +21 -4
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +14 -12
- data/lib/rigor/protection/kill_signature.rb +1 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +7 -11
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +88 -21
- data/lib/rigor/reflection/constant_ancestors.rb +97 -0
- data/lib/rigor/reflection/constant_path.rb +139 -0
- data/lib/rigor/reflection.rb +93 -110
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +165 -2
- data/lib/rigor/scope.rb +1041 -32
- 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 +673 -53
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +18 -16
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +128 -14
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +154 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +189 -41
- data/lib/rigor/sig_gen.rb +3 -0
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +0 -6
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +144 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +6 -6
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +4 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +10 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +14 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +32 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/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 +264 -3
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- 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/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +2 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +10 -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 +383 -7
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +322 -41
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +26 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +290 -19
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +3 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +2 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-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/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +78 -5
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +12 -7
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +36 -37
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +5 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +3 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +10 -17
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +324 -18
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +2 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +10 -5
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +51 -6
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -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/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- 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 +17 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +9 -14
- 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 +4 -0
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +51 -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 +9 -6
- data/sig/rigor/scope.rbs +148 -12
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +21 -0
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +33 -1
- data/sig/rigor.rbs +39 -20
- data/skills/rigor-ask/SKILL.md +16 -11
- 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 +25 -27
- 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 +15 -10
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +43 -14
- 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-project-init/references/06-agent-contract.md +67 -0
- 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 +226 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +265 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +125 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +12 -7
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +133 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "prism"
|
|
4
4
|
|
|
5
5
|
require_relative "method_key"
|
|
6
|
+
require_relative "../source/constant_path"
|
|
6
7
|
|
|
7
8
|
module Rigor
|
|
8
9
|
module Effects
|
|
@@ -30,9 +31,9 @@ module Rigor
|
|
|
30
31
|
NO_LINES = {}.freeze
|
|
31
32
|
private_constant :NO_LINES
|
|
32
33
|
|
|
33
|
-
# @param key
|
|
34
|
-
# @param path
|
|
35
|
-
# @return
|
|
34
|
+
# @param key — an effect unit key — `Tracer::Loud#emit`, `Net::HTTP.get`.
|
|
35
|
+
# @param path — the file the key was traced to.
|
|
36
|
+
# @return the `def`'s line, or nil when this file does not spell that key with a
|
|
36
37
|
# `def` — an unreadable file, a syntax error, and a synthesized method all land here.
|
|
37
38
|
def for(key:, path:)
|
|
38
39
|
index_for(path)[key]
|
|
@@ -62,10 +63,10 @@ module Rigor
|
|
|
62
63
|
def walk(node, nesting, singleton:, index:)
|
|
63
64
|
case node
|
|
64
65
|
when Prism::ModuleNode, Prism::ClassNode
|
|
65
|
-
|
|
66
|
-
return if
|
|
66
|
+
inner = header_nesting(nesting, node.constant_path)
|
|
67
|
+
return if inner.nil?
|
|
67
68
|
|
|
68
|
-
walk_children(node.body,
|
|
69
|
+
walk_children(node.body, inner, singleton: false, index: index)
|
|
69
70
|
when Prism::SingletonClassNode
|
|
70
71
|
walk_children(node.body, nesting, singleton: true, index: index)
|
|
71
72
|
when Prism::DefNode
|
|
@@ -76,6 +77,24 @@ module Rigor
|
|
|
76
77
|
end
|
|
77
78
|
end
|
|
78
79
|
|
|
80
|
+
# A ROOTED header is the one case that needs no resolution to place: `::` re-anchors the declaration at
|
|
81
|
+
# the top level wherever it is written, exactly as `Source::ConstantPath.declaration_prefix` reads it
|
|
82
|
+
# for the engine's own walks. Prism's `full_name` KEEPS the `::`, so `class ::Rooted` inside
|
|
83
|
+
# `module Outer` was indexed under `Outer::::Rooted#m` — a key no caller spells, which cost the row
|
|
84
|
+
# its line while every unrooted sibling in the same file kept one
|
|
85
|
+
# ([#722](https://github.com/rigortype/rigor/issues/722) residue 4).
|
|
86
|
+
#
|
|
87
|
+
# nil propagates the refusal {#constant_name} makes, so a header naming no constant leaves the subtree
|
|
88
|
+
# unindexed rather than under a guessed name.
|
|
89
|
+
def header_nesting(nesting, constant_path)
|
|
90
|
+
name = constant_name(constant_path)
|
|
91
|
+
return nil if name.nil?
|
|
92
|
+
|
|
93
|
+
return nesting + [name] unless Source::ConstantPath.rooted?(constant_path)
|
|
94
|
+
|
|
95
|
+
[name.delete_prefix("::")]
|
|
96
|
+
end
|
|
97
|
+
|
|
79
98
|
def walk_children(node, nesting, singleton:, index:)
|
|
80
99
|
node&.compact_child_nodes&.each { |child| walk(child, nesting, singleton: singleton, index: index) }
|
|
81
100
|
end
|
|
@@ -29,11 +29,19 @@ module Rigor
|
|
|
29
29
|
# The lanes are kept **raw** here — a declared label a proven one already subsumes is dropped where
|
|
30
30
|
# output is rendered ({LabelSet#excluding_subsumed_by}), never in the table, because a further join
|
|
31
31
|
# has to see what was actually declared.
|
|
32
|
+
# `unclaimed` is the transitive reading of {Summary#unclaimed?}: true when this method, or anything
|
|
33
|
+
# it reaches, called something nothing described (#391). It is not a label, not a taint, and is read
|
|
34
|
+
# by `rigor sig-gen`'s annotation emission alone — not by the report, the snapshot, or any
|
|
35
|
+
# diagnostic.
|
|
32
36
|
class Entry < Data.define(:key, :direct, :proven, :undischarged, :declared, :exhaustive, :causes,
|
|
33
|
-
:edges)
|
|
34
|
-
def initialize(undischarged: nil, declared: nil, **rest)
|
|
37
|
+
:edges, :unclaimed)
|
|
38
|
+
def initialize(undischarged: nil, declared: nil, unclaimed: false, **rest)
|
|
35
39
|
super(undischarged: undischarged || rest.fetch(:proven), declared: declared || LabelSet::EMPTY,
|
|
36
|
-
**rest)
|
|
40
|
+
unclaimed: unclaimed ? true : false, **rest)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def unclaimed?
|
|
44
|
+
unclaimed
|
|
37
45
|
end
|
|
38
46
|
|
|
39
47
|
# The declared labels worth printing beside `proven` — the rendering rule, in one place so the
|
|
@@ -61,7 +69,6 @@ module Rigor
|
|
|
61
69
|
@empty ||= new(EMPTY_ENTRIES)
|
|
62
70
|
end
|
|
63
71
|
|
|
64
|
-
# @param entries [Hash{String => Entry}]
|
|
65
72
|
def initialize(entries)
|
|
66
73
|
@entries = entries.sort_by { |key, _| key }.to_h.freeze
|
|
67
74
|
freeze
|
|
@@ -117,8 +117,6 @@ module Rigor
|
|
|
117
117
|
# one message: the unregistered-preset error (#433) and the note `rigor effects update` prints
|
|
118
118
|
# when `reach:` is empty (#436) have to give the same answer, and neither can hard-code a list
|
|
119
119
|
# that moves with `plugins:`.
|
|
120
|
-
#
|
|
121
|
-
# @return [String]
|
|
122
120
|
def availability
|
|
123
121
|
if @presets.empty?
|
|
124
122
|
return "no plugin in this project registers an entry-point preset — a preset is named by the " \
|
|
@@ -61,7 +61,7 @@ module Rigor
|
|
|
61
61
|
# It lives on the value rather than in {EnvelopeCheck} because {LiskovCheck} positions its
|
|
62
62
|
# findings identically, and two spellings of "where the fix goes" would eventually disagree.
|
|
63
63
|
#
|
|
64
|
-
# @return
|
|
64
|
+
# @return `[path, line]`; `[nil, 1]` for a key with no owner.
|
|
65
65
|
def for(key)
|
|
66
66
|
class_name, separator, selector = MethodKey.split(key)
|
|
67
67
|
return [nil, 1] if class_name.nil?
|
|
@@ -103,17 +103,17 @@ module Rigor
|
|
|
103
103
|
|
|
104
104
|
module_function
|
|
105
105
|
|
|
106
|
-
# @param table
|
|
107
|
-
# @param method_envelopes
|
|
108
|
-
# @param class_envelopes
|
|
109
|
-
# @param config_envelopes
|
|
106
|
+
# @param table — the run's propagated graph.
|
|
107
|
+
# @param method_envelopes — per-method envelopes, as written.
|
|
108
|
+
# @param class_envelopes — class- / module-level envelopes, to distribute.
|
|
109
|
+
# @param config_envelopes — `effects.envelopes:` entries already resolved
|
|
110
110
|
# to the classes they select ({ConfigEnvelopes.for_classes}), to distribute at the lowest precedence.
|
|
111
|
-
# @param positions
|
|
111
|
+
# @param positions — the discovery tables a finding's `def`
|
|
112
112
|
# position is read from — consulted only when a finding is built, so a deferred value's
|
|
113
113
|
# discovery force is reached exactly as often as a finding exists.
|
|
114
|
-
# @param apply_tolerated
|
|
114
|
+
# @param apply_tolerated — false judges against the undischarged-by-policy `proven` lane —
|
|
115
115
|
# the `--no-tolerated-effects` audit switch.
|
|
116
|
-
# @return
|
|
116
|
+
# @return sorted by position then key then label, so a run explains identically twice.
|
|
117
117
|
def run(table:, method_envelopes:, class_envelopes:, config_envelopes: {},
|
|
118
118
|
positions: Positions.empty, apply_tolerated: true)
|
|
119
119
|
envelopes = distribute(table, method_envelopes, class_envelopes, config_envelopes)
|
|
@@ -157,7 +157,7 @@ module Rigor
|
|
|
157
157
|
# synthesized `attr_*` / `define_method` members the effects scanner adds.
|
|
158
158
|
def keys_by_class(table)
|
|
159
159
|
table.keys.each_with_object({}) do |key, out|
|
|
160
|
-
owner = MethodKey.
|
|
160
|
+
owner = MethodKey.envelope_owner(key)
|
|
161
161
|
next if owner.nil?
|
|
162
162
|
|
|
163
163
|
(out[owner] ||= []) << key
|
|
@@ -56,15 +56,13 @@ module Rigor
|
|
|
56
56
|
|
|
57
57
|
# Reads every stratum this index serves, once per process.
|
|
58
58
|
#
|
|
59
|
-
# @param
|
|
60
|
-
# @param plugin_facts [Rigor::Effects::PluginFacts, nil] the loaded plugins' effect contributions
|
|
59
|
+
# @param plugin_facts — the loaded plugins' effect contributions
|
|
61
60
|
# (#387); their `effect_labels:` join the vocabulary an annotation is read against, so a gem's
|
|
62
61
|
# `%a{rigor:v1:effect rails.activejob.enqueue}` resolves rather than reading as unknown.
|
|
63
|
-
# @param environment
|
|
62
|
+
# @param environment — the run's environment. Its loader supplies the
|
|
64
63
|
# rbs-inline / plugin `virtual_rbs` buffers and the built RBS environment the accepted stratum is
|
|
65
64
|
# read from; without one, both are simply absent (the fail-quiet direction — a missing `≤` bound
|
|
66
65
|
# costs precision, never a finding).
|
|
67
|
-
# @return [EnvelopeIndex]
|
|
68
66
|
def self.build(configuration:, environment: nil, plugin_facts: nil)
|
|
69
67
|
# Required here rather than at the top of the file: the reader pulls in the whole
|
|
70
68
|
# `RbsExtended` surface, and this build runs only under an `effects:` block.
|
|
@@ -122,10 +120,9 @@ module Rigor
|
|
|
122
120
|
|
|
123
121
|
# The envelope bounding `owner`'s `selector`, or nil.
|
|
124
122
|
#
|
|
125
|
-
# @param owner
|
|
126
|
-
# @param singleton
|
|
127
|
-
# @
|
|
128
|
-
# @return [Envelope, nil] never a ⊤ envelope: a bound that bounds nothing is not a bound, and
|
|
123
|
+
# @param owner — the receiver's static class name, as the typer projected it
|
|
124
|
+
# @param singleton — whether the call is `Owner.selector` rather than `Owner#selector`
|
|
125
|
+
# @return never a ⊤ envelope: a bound that bounds nothing is not a bound, and
|
|
129
126
|
# importing it would both add nothing and discharge a taint on the strength of a typo.
|
|
130
127
|
def [](owner, singleton, selector)
|
|
131
128
|
return nil if owner.nil? || empty?
|
|
@@ -138,6 +135,21 @@ module Rigor
|
|
|
138
135
|
envelope
|
|
139
136
|
end
|
|
140
137
|
|
|
138
|
+
# Whether ANY stratum carries an annotation for this key — ⊤ envelopes included.
|
|
139
|
+
#
|
|
140
|
+
# {#[]} is the import question and rightly refuses a ⊤ envelope: a bound that bounds nothing is not
|
|
141
|
+
# a bound. `rigor sig-gen`'s emission asks the other one (#391): an author who wrote
|
|
142
|
+
# `%a{rigor:v1:effect bogus.nonsense}` has still written about this method, and replacing their
|
|
143
|
+
# typo with an inferred `%a{pure}` would delete the only evidence that they meant to bound it — the
|
|
144
|
+
# `effect.unknown-label` report points at the annotation, and there would no longer be one.
|
|
145
|
+
def annotated?(owner, singleton, selector)
|
|
146
|
+
return false if owner.nil? || empty?
|
|
147
|
+
|
|
148
|
+
key = "#{owner}#{singleton ? '.' : '#'}#{selector}"
|
|
149
|
+
!(@method_envelopes[key] || @class_envelopes[owner] || config_envelope(owner) ||
|
|
150
|
+
@accepted[key]).nil?
|
|
151
|
+
end
|
|
152
|
+
|
|
141
153
|
private
|
|
142
154
|
|
|
143
155
|
# The first `namespace:` entry selecting `owner`, memoised per class name — one call site's owner
|
|
@@ -35,12 +35,59 @@ module Rigor
|
|
|
35
35
|
# selector rather than a receiver's, and the propagator resolves it against the ancestry *above* that
|
|
36
36
|
# class with no closed-world override join — a different question from every other edge, which is why
|
|
37
37
|
# it is a field rather than a convention over the other three.
|
|
38
|
-
|
|
38
|
+
#
|
|
39
|
+
# `constant_receiver` marks a call whose receiver the author wrote as a **constant path** (#1039).
|
|
40
|
+
# The collector keys an edge on the receiver's TYPE, so `self.class.new` inside `Base`, `klass.new`
|
|
41
|
+
# on a `Singleton[Base]` local and a receiver-less `new` in a singleton body all produce the very
|
|
42
|
+
# same tuple as a literal `Base.new` — and the first three really do construct a subclass. Only the
|
|
43
|
+
# constructor rule reads it, and only to DROP the closed-world subclass join a written constant
|
|
44
|
+
# cannot reach.
|
|
45
|
+
#
|
|
46
|
+
# `unclaimed` marks a site NOTHING bounded: no catalogue row, no plugin row, no imported envelope
|
|
47
|
+
# (#391). It is not a taint and never becomes one — an unresolved edge here is overwhelmingly an
|
|
48
|
+
# inherited or gem call the catalogue simply has no row for, which is why the model drops it — but
|
|
49
|
+
# it IS the difference between "the analyzer read every callee" and "the analyzer read the callees
|
|
50
|
+
# it had something to read". Only sig-gen's annotation emission consults it, through
|
|
51
|
+
# {EffectTable::Entry#unclaimed?}: writing `%a{pure}` is a claim about a callee nobody described,
|
|
52
|
+
# and the emitter must decline rather than invent one.
|
|
53
|
+
# `taint_if_unresolved` is #1048's inversion of a taint. A `render :show` whose {CalleeRule} named
|
|
54
|
+
# `view:users/show.html` may or may not reach a unit — only the merged table knows, exactly as only
|
|
55
|
+
# the merged ancestry knows whether a `super` resolves — so the row's `template-not-analysed` cause
|
|
56
|
+
# travels ON the edge as a frozen `[cause, detail]` pair and {Propagator} seeds it only where the
|
|
57
|
+
# edge landed on nothing. Adding a cause where the walk failed rather than subtracting one where it
|
|
58
|
+
# succeeded keeps every step of the fixpoint monotone, and it is the same shape
|
|
59
|
+
# {Propagator.taint_unresolved_super} already has.
|
|
60
|
+
#
|
|
61
|
+
# `fallback_selectors` is #1065's lookup order: the selectors the propagator retries, in order, when
|
|
62
|
+
# `selector` resolves to nothing — `["html"]` on a `view:watchers/_watchers` + `js` edge, because a
|
|
63
|
+
# `.js.erb` template's lookup context is `[:js, :html]`. The FIRST that resolves is the edge's
|
|
64
|
+
# target and the taint stays unseeded; only when every one fails is `taint_if_unresolved` seeded.
|
|
65
|
+
# One ordered list rather than one edge per candidate, because two edges would join BOTH units
|
|
66
|
+
# where both exist, and only one of them runs.
|
|
67
|
+
Edge = Data.define(:receiver_class, :kind, :selector, :self_call, :super_call, :unclaimed,
|
|
68
|
+
:constant_receiver, :taint_if_unresolved, :fallback_selectors) do
|
|
39
69
|
# Defaulted because every producer but the `super` one records an ordinary call, and an ordinary
|
|
40
|
-
# call is not a `super
|
|
41
|
-
|
|
70
|
+
# call is not a `super`, and because a producer that says nothing about `unclaimed` /
|
|
71
|
+
# `constant_receiver` means the safe value of each.
|
|
72
|
+
#
|
|
73
|
+
# The defaults do NOT rescue a cache written before a member existed: `Marshal.load` of a `Data`
|
|
74
|
+
# whose member list has grown raises `TypeError: struct size differs`, which the store reads as a
|
|
75
|
+
# miss. That is the right outcome and not the one relied on — the cache identity carries a schema
|
|
76
|
+
# component ({Identity}, `schema:5`), so such an entry is never offered in the first place.
|
|
77
|
+
def initialize(super_call: false, unclaimed: false, constant_receiver: false,
|
|
78
|
+
taint_if_unresolved: nil, fallback_selectors: nil, **)
|
|
79
|
+
super
|
|
80
|
+
end
|
|
42
81
|
end
|
|
43
82
|
|
|
83
|
+
# An ancestry entry that names nothing and can never resolve. The scanner records it where a class
|
|
84
|
+
# body says its ancestry or its constructor is not readable from the source — a superclass
|
|
85
|
+
# expression that is not a constant path (`class K < Struct.new(:a)`), or an `alias` / `alias_method`
|
|
86
|
+
# that makes `initialize` some other method (#1039). It matches no summary key, so every existing
|
|
87
|
+
# walk steps over it; the constructor rule asks for it by name, because "unreadable" is exactly what
|
|
88
|
+
# that rule must not read as "empty".
|
|
89
|
+
OPAQUE_ANCESTOR = "<opaque>"
|
|
90
|
+
|
|
44
91
|
NO_TABLE = {}.freeze
|
|
45
92
|
private_constant :NO_TABLE
|
|
46
93
|
|
|
@@ -107,7 +154,12 @@ module Rigor
|
|
|
107
154
|
|
|
108
155
|
fold_summaries(summaries, collection.summaries)
|
|
109
156
|
fold_lists(edges, collection.edges)
|
|
110
|
-
|
|
157
|
+
# The opaque sentinel is sticky across files as well as within one: a class whose constructor one
|
|
158
|
+
# file says is unreadable does not become readable because another file spells its `<`, and the
|
|
159
|
+
# fold must not depend on which file the run reads first.
|
|
160
|
+
superclasses.update(collection.superclasses) do |_key, mine, theirs|
|
|
161
|
+
mine.include?(OPAQUE_ANCESTOR) ? mine : theirs
|
|
162
|
+
end
|
|
111
163
|
fold_lists(includes, collection.includes)
|
|
112
164
|
end
|
|
113
165
|
|
|
@@ -167,7 +219,9 @@ module Rigor
|
|
|
167
219
|
end
|
|
168
220
|
|
|
169
221
|
def edge_order(edge)
|
|
170
|
-
[edge.receiver_class.to_s, edge.kind.to_s, edge.selector, edge.self_call ? 1 : 0,
|
|
222
|
+
[edge.receiver_class.to_s, edge.kind.to_s, edge.selector, edge.self_call ? 1 : 0,
|
|
223
|
+
edge.super_call ? 1 : 0, edge.constant_receiver ? 1 : 0,
|
|
224
|
+
Array(edge.taint_if_unresolved).join("\x00"), Array(edge.fallback_selectors).join("\x00")]
|
|
171
225
|
end
|
|
172
226
|
end
|
|
173
227
|
end
|
|
@@ -76,13 +76,11 @@ module Rigor
|
|
|
76
76
|
|
|
77
77
|
# Every synthetic unit `class_name` earns, as `[key, Summary, edges]` triples.
|
|
78
78
|
#
|
|
79
|
-
# @param
|
|
80
|
-
# @param
|
|
81
|
-
# @param macros [Hash{String=>Array<String>}] receiver-less class-body calls to their literal symbol
|
|
79
|
+
# @param instance_methods — the instance methods the class body defines, in source order
|
|
80
|
+
# @param macros — receiver-less class-body calls to their literal symbol
|
|
82
81
|
# arguments, as the scanner harvested them
|
|
83
|
-
# @param uniqueness
|
|
84
|
-
# @param
|
|
85
|
-
# @param own_units [Hash{String=>Boolean}] the units the class body itself defines, keyed by the
|
|
82
|
+
# @param uniqueness — whether the class body declares a uniqueness validator
|
|
83
|
+
# @param own_units — the units the class body itself defines, keyed by the
|
|
86
84
|
# suffix a synthetic key carries (`"#save"`, `".create"`), each mapped to whether that body
|
|
87
85
|
# reaches `super`. Read by {.framework_row} and by nothing else.
|
|
88
86
|
def synthesize(class_name:, instance_methods:, macros:, uniqueness:, plugin_facts:, own_units: {})
|
|
@@ -198,7 +196,7 @@ module Rigor
|
|
|
198
196
|
end
|
|
199
197
|
|
|
200
198
|
# The uniqueness validator's own query. It rides the DECLARED lane with no taint, exactly as every
|
|
201
|
-
# other first-party plugin contribution does (ADR-103 WD6): the plugin read the app's own
|
|
199
|
+
# other first-party plugin contribution does (ADR-103 WD6 / WD17): the plugin read the app's own
|
|
202
200
|
# `validates … uniqueness: true` and knows what Rails does with it, but the analyzer did not read a
|
|
203
201
|
# body, so this is a trusted claim rather than a proof.
|
|
204
202
|
def uniqueness_summary(class_name)
|
|
@@ -45,13 +45,28 @@ module Rigor
|
|
|
45
45
|
# The effects identity as a hex digest — the form a store with no descriptor of its own (the ADR-46
|
|
46
46
|
# incremental snapshot) carries alongside its payload, and compares verbatim on restore.
|
|
47
47
|
#
|
|
48
|
-
# @param
|
|
49
|
-
# @param
|
|
50
|
-
# @
|
|
51
|
-
# @return [String] hex SHA-256
|
|
48
|
+
# @param registry — the vocabulary whose version participates
|
|
49
|
+
# @param catalog — the catalogue whose identity participates
|
|
50
|
+
# @return hex SHA-256
|
|
52
51
|
def digest(configuration:, registry: Registry.default, catalog: Catalog.default, plugin_facts: nil)
|
|
53
52
|
Digest::SHA256.hexdigest(
|
|
54
53
|
[
|
|
54
|
+
# #391 — the summary schema itself. A cached collection written before {Summary#unclaimed?}
|
|
55
|
+
# existed restores with the bit false, which is the direction sig-gen's emission must never
|
|
56
|
+
# guess, so such an entry must not be served rather than merely read carefully.
|
|
57
|
+
#
|
|
58
|
+
# schema:3 — #1039 moved edge RESOLUTION: a singleton `new` edge on a project class now
|
|
59
|
+
# reaches that class's `#initialize`. The warm lanes serve a propagated `EffectTable` (and its
|
|
60
|
+
# `unclaimed` bits) rather than re-resolving, so an entry written under the old rule would
|
|
61
|
+
# answer the old closure for source nothing changed.
|
|
62
|
+
#
|
|
63
|
+
# schema:4 — #1048 added {FileCollection::Edge#taint_if_unresolved}, so a marshalled edge
|
|
64
|
+
# written under schema:3 no longer restores at all (`Data` with a grown member list raises
|
|
65
|
+
# `TypeError`), and a warm entry would otherwise be offered and then read as a miss.
|
|
66
|
+
#
|
|
67
|
+
# schema:5 — #1065 added {FileCollection::Edge#fallback_selectors}, which both grows the
|
|
68
|
+
# marshalled member list again and changes which unit a cached `view:` edge resolves to.
|
|
69
|
+
"schema:5",
|
|
55
70
|
"vocabulary:#{registry.vocabulary_version}",
|
|
56
71
|
"catalog:#{catalog.identity}",
|
|
57
72
|
"effects:#{config_digest(configuration)}",
|
|
@@ -66,8 +81,7 @@ module Rigor
|
|
|
66
81
|
# the analyzed-path set — for free, so "the effects identity is the diagnostics identity plus three
|
|
67
82
|
# things" is a property of the code rather than a claim about it.
|
|
68
83
|
#
|
|
69
|
-
# @param base
|
|
70
|
-
# @return [Cache::Descriptor]
|
|
84
|
+
# @param base — the run's diagnostics key descriptor
|
|
71
85
|
def descriptor(base:, configuration:, registry: Registry.default, catalog: Catalog.default,
|
|
72
86
|
plugin_facts: nil)
|
|
73
87
|
Cache::Descriptor.compose(
|
|
@@ -49,9 +49,9 @@ module Rigor
|
|
|
49
49
|
SPELLING_PATTERN = /%a\{[^}]*\}/
|
|
50
50
|
private_constant :SPELLING_PATTERN
|
|
51
51
|
|
|
52
|
-
# @param path
|
|
53
|
-
# @param buffer
|
|
54
|
-
# @return
|
|
52
|
+
# @param path — the buffer's readable path, as {SignatureSources.source_path} renders it.
|
|
53
|
+
# @param buffer — the synthesized RBS the position was read out of.
|
|
54
|
+
# @return nil when `path` is not a Ruby file — a real `.rbs` needs no mapping,
|
|
55
55
|
# and its own line numbers are already the ones a reader can open.
|
|
56
56
|
def self.for(path:, buffer:)
|
|
57
57
|
return nil unless path.to_s.end_with?(RUBY_EXTENSION)
|
|
@@ -61,7 +61,7 @@ module Rigor
|
|
|
61
61
|
|
|
62
62
|
# One-shot form, for a caller that holds a `[path, buffer, line]` triple and no anchor.
|
|
63
63
|
#
|
|
64
|
-
# @return
|
|
64
|
+
# @return the `.rb` line, or `buffer_line` unchanged when there is nothing to map.
|
|
65
65
|
def self.ruby_line(path:, buffer:, buffer_line:, spelling: nil)
|
|
66
66
|
anchor = self.for(path: path, buffer: buffer)
|
|
67
67
|
return buffer_line if anchor.nil?
|
|
@@ -75,10 +75,10 @@ module Rigor
|
|
|
75
75
|
@ruby_lines = nil
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
# @param buffer_line
|
|
79
|
-
# @param spelling
|
|
78
|
+
# @param buffer_line — 1-based, counting into the synthesized buffer.
|
|
79
|
+
# @param spelling — the annotation's own text (`"%a{pure}"`); read off the buffer line
|
|
80
80
|
# when the caller does not already hold it.
|
|
81
|
-
# @return
|
|
81
|
+
# @return the 1-based line of the same annotation in the Ruby file.
|
|
82
82
|
def line_for(buffer_line, spelling: nil)
|
|
83
83
|
spelling = normalize(spelling) || spelling_at(buffer_line)
|
|
84
84
|
return buffer_line if spelling.nil?
|
|
@@ -37,11 +37,10 @@ module Rigor
|
|
|
37
37
|
|
|
38
38
|
# Whether reporting `token` as an unknown label is justified.
|
|
39
39
|
#
|
|
40
|
-
# @param token
|
|
41
|
-
# @param registry
|
|
40
|
+
# @param token — the spelling as written.
|
|
41
|
+
# @param registry — the vocabulary AFTER plugin load; `nil` (no
|
|
42
42
|
# vocabulary at all) makes every token unjudgeable and therefore silent.
|
|
43
|
-
# @param siblings
|
|
44
|
-
# @return [Boolean]
|
|
43
|
+
# @param siblings — the other tokens of the same list / the same config value.
|
|
45
44
|
def evident?(token, registry, siblings: [])
|
|
46
45
|
return false if registry.nil?
|
|
47
46
|
return false unless Label.valid?(token)
|
|
@@ -56,16 +56,16 @@ module Rigor
|
|
|
56
56
|
|
|
57
57
|
module_function
|
|
58
58
|
|
|
59
|
-
# @param table
|
|
60
|
-
# @param superclasses
|
|
59
|
+
# @param table — the run's propagated graph.
|
|
60
|
+
# @param superclasses — the collector's as-written superclass
|
|
61
61
|
# candidate lists (`FileCollection#superclasses`).
|
|
62
|
-
# @param method_envelopes
|
|
63
|
-
# @param class_envelopes
|
|
64
|
-
# @param config_envelopes
|
|
62
|
+
# @param method_envelopes — per-method envelopes, as written.
|
|
63
|
+
# @param class_envelopes — class- / module-level envelopes, to distribute.
|
|
64
|
+
# @param config_envelopes — `effects.envelopes:` entries already resolved
|
|
65
65
|
# to the classes they select.
|
|
66
|
-
# @param positions
|
|
67
|
-
# @param apply_tolerated
|
|
68
|
-
# @return
|
|
66
|
+
# @param positions — where the override's `def` is.
|
|
67
|
+
# @param apply_tolerated — false judges against `proven` — `--no-tolerated-effects`.
|
|
68
|
+
# @return sorted by position then key then label.
|
|
69
69
|
def run(table:, superclasses:, method_envelopes:, class_envelopes:, config_envelopes: {},
|
|
70
70
|
positions: EnvelopeCheck::Positions.empty, apply_tolerated: true)
|
|
71
71
|
# The distributed strata, over a base of the raw per-method annotations: a base class whose method
|
|
@@ -11,8 +11,8 @@ module Rigor
|
|
|
11
11
|
#
|
|
12
12
|
# Ruby has no by-ref parameters, so `mutate.local` cannot mean "a write into an out-parameter" as it
|
|
13
13
|
# does in PHP. It means the mutated receiver is invisible to the caller, and that is an ownership
|
|
14
|
-
# question: a local whose every assignment allocates (`[]`, `{}`, `""`,
|
|
15
|
-
# escapes the body is one no caller can observe being mutated.
|
|
14
|
+
# question: a local whose every assignment allocates (`[]`, `{}`, `""`, `Foo.new`, `.dup`) and which
|
|
15
|
+
# never escapes the body is one no caller can observe being mutated.
|
|
16
16
|
#
|
|
17
17
|
# The analysis is deliberately **flow-insensitive and whole-body**: a local that escapes anywhere
|
|
18
18
|
# disqualifies, even if the escape happens after the mutation. That is strictly more conservative than
|
|
@@ -23,16 +23,17 @@ module Rigor
|
|
|
23
23
|
# This is the tracer slice's approximation, not the eventual answer. `ClosureEscapeAnalyzer` answers a
|
|
24
24
|
# different question (fact retention, not "does the code contain") and is deliberately left alone.
|
|
25
25
|
module LocalOwnership
|
|
26
|
-
# Assignment right-hand sides that witness a fresh allocation. `.
|
|
26
|
+
# Assignment right-hand sides that witness a fresh allocation. `.dup` / `.clone` follow
|
|
27
27
|
# [ADR-76](../adr/76-effect-modeling-freeze-dup-shape-preservation.md)'s reading of `dup` as the
|
|
28
|
-
# allocation witness
|
|
29
|
-
|
|
28
|
+
# allocation witness; `.new` is {constructor?}'s, because only a class object's `new` is `Class#new`.
|
|
29
|
+
COPYING_SELECTORS = %i[dup clone].to_set.freeze
|
|
30
30
|
|
|
31
31
|
module_function
|
|
32
32
|
|
|
33
33
|
# The set of frame-owned local names in `body`, given the method's parameter names (a parameter is
|
|
34
|
-
# never frame-owned — the caller holds the same object, so mutating it is `mutate.instance`)
|
|
35
|
-
|
|
34
|
+
# never frame-owned — the caller holds the same object, so mutating it is `mutate.instance`) and
|
|
35
|
+
# whether `self` is a class there ({constructor?}).
|
|
36
|
+
def owned(body, parameter_names, singleton:)
|
|
36
37
|
return Set.new if body.nil?
|
|
37
38
|
|
|
38
39
|
assignments = {}
|
|
@@ -42,23 +43,48 @@ module Rigor
|
|
|
42
43
|
assignments.filter_map do |name, values|
|
|
43
44
|
next if escaped.include?(name) || parameter_names.include?(name)
|
|
44
45
|
|
|
45
|
-
name if values.all? { |value| allocation?(value) }
|
|
46
|
+
name if values.all? { |value| allocation?(value, singleton: singleton) }
|
|
46
47
|
end.to_set
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
# Whether `node` is an expression that allocates a fresh object this frame is the sole holder of.
|
|
50
|
-
|
|
51
|
+
# `singleton` is whether the enclosing unit's `self` is a class, as {constructor?} reads it.
|
|
52
|
+
def allocation?(node, singleton:)
|
|
51
53
|
case node
|
|
52
54
|
when Prism::ArrayNode, Prism::HashNode, Prism::StringNode, Prism::InterpolatedStringNode,
|
|
53
55
|
Prism::LambdaNode
|
|
54
56
|
true
|
|
55
57
|
when Prism::CallNode
|
|
56
|
-
|
|
58
|
+
COPYING_SELECTORS.include?(node.name) || constructor?(node, singleton: singleton) ||
|
|
59
|
+
unary_plus_string?(node)
|
|
57
60
|
else
|
|
58
61
|
false
|
|
59
62
|
end
|
|
60
63
|
end
|
|
61
64
|
|
|
65
|
+
# A `new` whose receiver the author wrote as a class object: a constant path, a `class` call
|
|
66
|
+
# (`self.class`, `other.class`), or `self` — explicit or implicit — in a singleton-method body. Any
|
|
67
|
+
# other receiver may be an object whose `new` only shares the name: an ActiveRecord association's
|
|
68
|
+
# `new` builds a record into the association's own target, where the caller can reach it, and a
|
|
69
|
+
# gem's method gives no edge that would carry that to the caller. `self` in an instance method is
|
|
70
|
+
# such a receiver too — an association extension's `new` is the association's.
|
|
71
|
+
def constructor?(node, singleton:)
|
|
72
|
+
return false unless node.name == :new
|
|
73
|
+
|
|
74
|
+
receiver = node.receiver
|
|
75
|
+
case receiver
|
|
76
|
+
when nil, Prism::SelfNode then singleton
|
|
77
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode then true
|
|
78
|
+
when Prism::CallNode then class_call?(receiver)
|
|
79
|
+
else false
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# `x.class` — `Kernel#class` answers the receiver's class, unless the receiver overrides it.
|
|
84
|
+
def class_call?(node)
|
|
85
|
+
node.name == :class && node.arguments.nil? && node.block.nil?
|
|
86
|
+
end
|
|
87
|
+
|
|
62
88
|
# `+""` — the frozen-string-literal era's spelling of "a fresh mutable String".
|
|
63
89
|
def unary_plus_string?(node)
|
|
64
90
|
node.name == :+@ && node.receiver.is_a?(Prism::StringNode)
|
|
@@ -125,8 +151,8 @@ module Rigor
|
|
|
125
151
|
last.is_a?(Prism::LocalVariableReadNode) ? [last.name.to_s] : []
|
|
126
152
|
end
|
|
127
153
|
|
|
128
|
-
private_class_method :collect, :record_assignment, :record_escapes, :stored_value,
|
|
129
|
-
:trailing_reads
|
|
154
|
+
private_class_method :class_call?, :collect, :record_assignment, :record_escapes, :stored_value,
|
|
155
|
+
:note_read, :trailing_reads
|
|
130
156
|
end
|
|
131
157
|
end
|
|
132
158
|
end
|
|
@@ -12,9 +12,14 @@ module Rigor
|
|
|
12
12
|
# key, and `effects.attribution:` in `.rigor.yml` names one. A key the loader accepts and the scanner
|
|
13
13
|
# would never produce is a table that silently matches nothing.
|
|
14
14
|
module MethodKey
|
|
15
|
+
# #393 — the prefix a template unit's key carries (`Plugin::TemplateUnit::KEY_PREFIX`, repeated here
|
|
16
|
+
# rather than required so the effects layer keeps no dependency on the plugin layer; the two are
|
|
17
|
+
# pinned equal by a spec).
|
|
18
|
+
TEMPLATE_UNIT_PREFIX = "view:"
|
|
19
|
+
|
|
15
20
|
module_function
|
|
16
21
|
|
|
17
|
-
# @return
|
|
22
|
+
# @return `[owner, separator, selector]`, or nil when `key` is
|
|
18
23
|
# not a method key at all.
|
|
19
24
|
def split(key)
|
|
20
25
|
text = key.to_s
|
|
@@ -35,6 +40,22 @@ module Rigor
|
|
|
35
40
|
def owner(key)
|
|
36
41
|
split(key)&.first
|
|
37
42
|
end
|
|
43
|
+
|
|
44
|
+
# #393 — a template unit's key: `view:users/show.html`. Deliberately NOT a method key — a view has
|
|
45
|
+
# no owner class and no selector — but {.split} cannot tell, because the format segment gives the
|
|
46
|
+
# string a dot: `owner("view:users/show.html")` answers `"view:users/show"`, a class name no run
|
|
47
|
+
# ever produced. Anything grouping keys by their envelope-bearing owner has to ask this first.
|
|
48
|
+
def template_unit?(key)
|
|
49
|
+
key.to_s.start_with?(TEMPLATE_UNIT_PREFIX)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# The name an `effects.envelopes:` entry selects a key by: the owner class for a method key, and the
|
|
53
|
+
# unit key ITSELF for a template unit. A view is its own envelope subject — there is no class to
|
|
54
|
+
# hang the bound on, and `Runner#effect_sources` already knows which file the key came from, which
|
|
55
|
+
# is what a `match:` entry needs.
|
|
56
|
+
def envelope_owner(key)
|
|
57
|
+
template_unit?(key) ? key.to_s : owner(key)
|
|
58
|
+
end
|
|
38
59
|
end
|
|
39
60
|
end
|
|
40
61
|
end
|