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
|
@@ -54,6 +54,11 @@ module Rigor
|
|
|
54
54
|
"def.override-return-widened" => :off,
|
|
55
55
|
"def.override-param-narrowed" => :off,
|
|
56
56
|
"def.ivar-write-mismatch" => :warning,
|
|
57
|
+
# Issue #1367 — both report a write its setter rejects, so it raises on every run. The type check rests on
|
|
58
|
+
# an inferred value type, so it follows `call.argument-type-mismatch` (warning under lenient); the
|
|
59
|
+
# read-only write needs no type at all, so it is an error in every profile.
|
|
60
|
+
"global.write-type-mismatch" => :warning,
|
|
61
|
+
"global.readonly-write" => :error,
|
|
57
62
|
# ADR-8 companion (PHPStan IgnoreParseErrorRule-modelled): a broken suppression comment is
|
|
58
63
|
# equally bad in every profile — it silently fails to do what the author believes it does — so
|
|
59
64
|
# both rules stay :warning across all three profiles (including strict).
|
|
@@ -80,7 +85,12 @@ module Rigor
|
|
|
80
85
|
# Opt-in author assertion: you only see it if you wrote a
|
|
81
86
|
# `conforms-to` directive, so it stays a :warning even in
|
|
82
87
|
# lenient — it is never unsolicited noise.
|
|
83
|
-
"rbs_extended.unsatisfied-conformance" => :warning
|
|
88
|
+
"rbs_extended.unsatisfied-conformance" => :warning,
|
|
89
|
+
# ADR-112 WD5 / #1075 — an error in every profile, lenient included. The rule guards its
|
|
90
|
+
# false-positive rate through its narrow definition of a contradiction, and ADR-112 WD5 rules
|
|
91
|
+
# out lowering the severity instead; only a proven disagreement between two declarations the
|
|
92
|
+
# project wrote itself can fire.
|
|
93
|
+
"rbs.contradicting-signature" => :error
|
|
84
94
|
}.freeze,
|
|
85
95
|
balanced: {
|
|
86
96
|
"call.undefined-method" => :error,
|
|
@@ -110,6 +120,8 @@ module Rigor
|
|
|
110
120
|
"def.override-return-widened" => :warning,
|
|
111
121
|
"def.override-param-narrowed" => :warning,
|
|
112
122
|
"def.ivar-write-mismatch" => :warning,
|
|
123
|
+
"global.write-type-mismatch" => :error,
|
|
124
|
+
"global.readonly-write" => :error,
|
|
113
125
|
"suppression.unknown-rule" => :warning,
|
|
114
126
|
"suppression.empty" => :warning,
|
|
115
127
|
"suppression.unknown-marker" => :warning,
|
|
@@ -118,7 +130,8 @@ module Rigor
|
|
|
118
130
|
"effect.liskov-widened" => :warning,
|
|
119
131
|
"effect.unknown-label" => :info,
|
|
120
132
|
"effect.annotations-unchecked" => :info,
|
|
121
|
-
"rbs_extended.unsatisfied-conformance" => :warning
|
|
133
|
+
"rbs_extended.unsatisfied-conformance" => :warning,
|
|
134
|
+
"rbs.contradicting-signature" => :error
|
|
122
135
|
}.freeze,
|
|
123
136
|
strict: {
|
|
124
137
|
"call.undefined-method" => :error,
|
|
@@ -147,6 +160,8 @@ module Rigor
|
|
|
147
160
|
"def.override-return-widened" => :error,
|
|
148
161
|
"def.override-param-narrowed" => :error,
|
|
149
162
|
"def.ivar-write-mismatch" => :error,
|
|
163
|
+
"global.write-type-mismatch" => :error,
|
|
164
|
+
"global.readonly-write" => :error,
|
|
150
165
|
"suppression.unknown-rule" => :warning,
|
|
151
166
|
"suppression.empty" => :warning,
|
|
152
167
|
"suppression.unknown-marker" => :warning,
|
|
@@ -158,7 +173,8 @@ module Rigor
|
|
|
158
173
|
# `:info` even under strict: a residual that failed a build would punish the project for
|
|
159
174
|
# carrying an annotation it has not opted into checking, which is the opposite of the point.
|
|
160
175
|
"effect.annotations-unchecked" => :info,
|
|
161
|
-
"rbs_extended.unsatisfied-conformance" => :error
|
|
176
|
+
"rbs_extended.unsatisfied-conformance" => :error,
|
|
177
|
+
"rbs.contradicting-signature" => :error
|
|
162
178
|
}.freeze
|
|
163
179
|
}.freeze
|
|
164
180
|
|
|
@@ -167,20 +183,20 @@ module Rigor
|
|
|
167
183
|
# Resolves the configured severity for a diagnostic given the active profile and any per-rule
|
|
168
184
|
# overrides.
|
|
169
185
|
#
|
|
170
|
-
# @param rule
|
|
171
|
-
# @param authored_severity
|
|
186
|
+
# @param rule — canonical rule id (`call.undefined-method`).
|
|
187
|
+
# @param authored_severity — severity the rule emitted the diagnostic with (`:error`, `:warning`,
|
|
172
188
|
# `:info`).
|
|
173
|
-
# @param profile
|
|
174
|
-
# @param overrides
|
|
189
|
+
# @param profile — one of {VALID_PROFILES}; falls back to {DEFAULT_PROFILE} for unknown values.
|
|
190
|
+
# @param overrides — per-rule severity overrides from `.rigor.yml`'s
|
|
175
191
|
# `severity_overrides:` map. Keys are canonical rule ids; values are {VALID_SEVERITIES} symbols.
|
|
176
192
|
# Family-wildcard keys (`call`) match every rule under that prefix.
|
|
177
|
-
# @param bleeding_edge_overrides
|
|
193
|
+
# @param bleeding_edge_overrides — the severity map imposed by the active ADR-50
|
|
178
194
|
# § WD2 bleeding-edge features ({Rigor::BleedingEdge.severity_overrides_for}). Consulted *below* the
|
|
179
195
|
# user's own `overrides` (so an explicit `severity_overrides:` entry, exact or family wildcard,
|
|
180
196
|
# always wins) and *above* the profile table. Exact rule ids only — the overlay never carries family
|
|
181
197
|
# wildcards. Empty while the overlay is unpopulated, so the default leaves resolution bit-for-bit
|
|
182
198
|
# unchanged.
|
|
183
|
-
# @return
|
|
199
|
+
# @return the resolved severity. Returns `:off` to mean "drop the diagnostic entirely".
|
|
184
200
|
def resolve(rule:, authored_severity:, profile: DEFAULT_PROFILE, overrides: {}, bleeding_edge_overrides: {})
|
|
185
201
|
return authored_severity if rule.nil?
|
|
186
202
|
|
data/lib/rigor/configuration.rb
CHANGED
|
@@ -49,6 +49,9 @@ module Rigor
|
|
|
49
49
|
**/node_modules/**
|
|
50
50
|
].freeze
|
|
51
51
|
|
|
52
|
+
# The conventional test roots `test_paths: nil` auto-detects, in this order.
|
|
53
|
+
CONVENTIONAL_TEST_PATHS = %w[spec test].freeze
|
|
54
|
+
|
|
52
55
|
DEFAULTS = {
|
|
53
56
|
"target_ruby" => "4.0",
|
|
54
57
|
"paths" => ["lib"],
|
|
@@ -57,6 +60,20 @@ module Rigor
|
|
|
57
60
|
"disable" => [],
|
|
58
61
|
"libraries" => [],
|
|
59
62
|
"signature_paths" => nil,
|
|
63
|
+
# The project's test roots: the directories (or files) holding its tests. `nil` (the default) auto-detects
|
|
64
|
+
# whichever of `spec/` and `test/` exist, the same way `signature_paths: nil` auto-detects `sig/`; `[]`
|
|
65
|
+
# declares that the project has none. `rigor sig-gen --params=observed` harvests call-site observations
|
|
66
|
+
# from them when no `--observe=PATH` is given. See {#resolved_test_paths}.
|
|
67
|
+
"test_paths" => nil,
|
|
68
|
+
# ADR-112 WD4 — `rigor sig-gen` settings. `inline_declared:` decides what happens to a method the inline
|
|
69
|
+
# reader (`rigor-rbs-inline`) already declares from `# @rbs` / `#:`. `write` (the default) copies the
|
|
70
|
+
# inline declaration into `sig/`, so the generated signature is the complete contract a gem ships;
|
|
71
|
+
# `skip` leaves every member that reader declares out of `sig/`, for a project whose Steep reads the
|
|
72
|
+
# same annotations (`inline: true` beside `signature "sig"`), where the copy would be a second
|
|
73
|
+
# declaration of one method (`DuplicatedMethodDefinition`). See {#sig_gen_inline_declared}.
|
|
74
|
+
"sig_gen" => {
|
|
75
|
+
"inline_declared" => "write"
|
|
76
|
+
},
|
|
60
77
|
# ADR-17 — project-side monkey-patch pre-evaluation. Empty by default; users opt in by listing explicit
|
|
61
78
|
# files that the analyzer walks before per-file inference so patched-method declarations are visible
|
|
62
79
|
# across the project (e.g. `lib/core_ext/string_extensions.rb`). Slice 1 plumbing only — listed files
|
|
@@ -117,6 +134,16 @@ module Rigor
|
|
|
117
134
|
# `"stat"` (or `RIGOR_CI_DETECT=0`).
|
|
118
135
|
"validation" => "auto"
|
|
119
136
|
},
|
|
137
|
+
# ADR-39 slice 5 / #911 — the plugin-isolation strategy for target-library invocation. `nil` (the
|
|
138
|
+
# default) names nothing, leaving the choice to `RIGOR_PLUGIN_ISOLATION` and then to
|
|
139
|
+
# `Plugin::Isolation::DEFAULT`. The environment variable WINS over this key: it is the
|
|
140
|
+
# one-invocation operator override (`RIGOR_PLUGIN_ISOLATION=none rigor check` on a platform where
|
|
141
|
+
# the fork worker misbehaves), and an override a committed file can veto is not an override.
|
|
142
|
+
# Only `none` and `process` are selectable here — `ruby_box` needs `RUBY_BOX=1` set before Ruby
|
|
143
|
+
# boots, which is what `exe/rigor` re-execs for, and the launcher deliberately does not parse YAML
|
|
144
|
+
# before that re-exec (ADR-87 / ADR-104 boot-slim), so naming it here raises instead of silently
|
|
145
|
+
# resolving to a different strategy.
|
|
146
|
+
"plugins_isolation" => nil,
|
|
120
147
|
"plugins_io" => {
|
|
121
148
|
"network" => "disabled",
|
|
122
149
|
"allowed_paths" => [],
|
|
@@ -192,11 +219,12 @@ module Rigor
|
|
|
192
219
|
"auto_detect" => true
|
|
193
220
|
}
|
|
194
221
|
}.freeze
|
|
222
|
+
Ractor.make_shareable(DEFAULTS)
|
|
195
223
|
|
|
196
224
|
# Top-level keys whose values are file/directory paths that MUST be resolved relative to the config
|
|
197
225
|
# file's directory. `exclude:` is intentionally NOT in this list — its entries are glob patterns
|
|
198
226
|
# (`**/vendor/**`), not paths.
|
|
199
|
-
PATH_KEYS = %w[paths signature_paths pre_eval].freeze
|
|
227
|
+
PATH_KEYS = %w[paths signature_paths test_paths pre_eval].freeze
|
|
200
228
|
private_constant :PATH_KEYS
|
|
201
229
|
|
|
202
230
|
# Top-level keys this implementation DECLARES but never reads — see `docs/internal-spec/config.md`
|
|
@@ -212,6 +240,10 @@ module Rigor
|
|
|
212
240
|
# reservation FINDABLE by the next person adding config validation (#166 is exactly that), and to
|
|
213
241
|
# give the schema-parity spec something to key on: a reserved namespace is by definition absent
|
|
214
242
|
# from DEFAULTS, so the DEFAULTS-driven schema gate can never see it.
|
|
243
|
+
# The isolation strategies a `.rigor.yml` may name (ADR-39 slice 5 / #911). `ruby_box` is
|
|
244
|
+
# deliberately absent: it is a boot-time decision the config file is read too late to make.
|
|
245
|
+
CONFIGURABLE_PLUGIN_ISOLATIONS = %w[none process].freeze
|
|
246
|
+
|
|
215
247
|
RESERVED_NAMESPACES = %w[rigor_rs].freeze
|
|
216
248
|
|
|
217
249
|
# Every top-level key a conforming `.rigor.yml` may carry: the keys this implementation owns, plus
|
|
@@ -237,7 +269,8 @@ module Rigor
|
|
|
237
269
|
|
|
238
270
|
attr_reader :target_ruby, :paths, :exclude_patterns, :plugins, :cache_path, :cache_max_bytes,
|
|
239
271
|
:cache_validation, :disabled_rules,
|
|
240
|
-
:libraries, :signature_paths, :fold_platform_specific_paths, :parameter_inference,
|
|
272
|
+
:libraries, :signature_paths, :test_paths, :fold_platform_specific_paths, :parameter_inference,
|
|
273
|
+
:plugins_isolation,
|
|
241
274
|
:plugins_io_network, :plugins_io_allowed_paths,
|
|
242
275
|
:plugins_io_allowed_url_hosts,
|
|
243
276
|
:severity_profile, :severity_overrides,
|
|
@@ -245,7 +278,7 @@ module Rigor
|
|
|
245
278
|
:dependencies, :parallel_workers,
|
|
246
279
|
:bundler_bundle_path, :bundler_auto_detect, :bundler_lockfile,
|
|
247
280
|
:rbs_collection_lockfile, :rbs_collection_auto_detect,
|
|
248
|
-
:pre_eval, :baseline_path, :effects,
|
|
281
|
+
:pre_eval, :baseline_path, :effects, :sig_gen_inline_declared,
|
|
249
282
|
:effects_snapshot_path, :effects_snapshot_reach, :effects_snapshot_gate, :effects_tolerated,
|
|
250
283
|
:effects_labels, :effects_attribution, :effects_envelopes
|
|
251
284
|
|
|
@@ -267,6 +300,16 @@ module Rigor
|
|
|
267
300
|
@effects_check
|
|
268
301
|
end
|
|
269
302
|
|
|
303
|
+
# The test roots a run uses: the declared `test_paths:` as written (already resolved against the
|
|
304
|
+
# config file's directory by {.load}), or — when the key is unset — whichever of
|
|
305
|
+
# {CONVENTIONAL_TEST_PATHS} is a directory under `root`, as absolute paths. `root` is the directory
|
|
306
|
+
# the run treats as the project root (the CLI's CWD), as for `signature_paths:` auto-detection.
|
|
307
|
+
def resolved_test_paths(root: Dir.pwd)
|
|
308
|
+
return test_paths unless test_paths.nil?
|
|
309
|
+
|
|
310
|
+
CONVENTIONAL_TEST_PATHS.map { |dir| File.expand_path(dir, root) }.select { |dir| File.directory?(dir) }
|
|
311
|
+
end
|
|
312
|
+
|
|
270
313
|
# Loads a configuration file.
|
|
271
314
|
#
|
|
272
315
|
# `path == nil` triggers auto-discovery against {DISCOVERY_ORDER}. The first present file in that list is
|
|
@@ -466,7 +509,7 @@ module Rigor
|
|
|
466
509
|
|
|
467
510
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
468
511
|
#
|
|
469
|
-
# @param effects_key_present
|
|
512
|
+
# @param effects_key_present — ADR-103 WD15 — whether the file this `data` came from carried an
|
|
470
513
|
# `effects:` key at all, independent of its value. Defaults to `data.key?("effects")`, which is the
|
|
471
514
|
# right answer for a caller that passes a raw (non-`DEFAULTS`-merged) hash directly — the common shape
|
|
472
515
|
# in specs — but is always `true` once `data` has been through `DEFAULTS.merge`, because `DEFAULTS`
|
|
@@ -494,6 +537,9 @@ module Rigor
|
|
|
494
537
|
@libraries = Array(data.fetch("libraries", DEFAULTS.fetch("libraries"))).map(&:to_s).freeze
|
|
495
538
|
sig_paths = data.fetch("signature_paths", DEFAULTS.fetch("signature_paths"))
|
|
496
539
|
@signature_paths = sig_paths.nil? ? nil : Array(sig_paths).map(&:to_s).freeze
|
|
540
|
+
test_paths = data.fetch("test_paths", DEFAULTS.fetch("test_paths"))
|
|
541
|
+
@test_paths = test_paths.nil? ? nil : Array(test_paths).map(&:to_s).freeze
|
|
542
|
+
@sig_gen_inline_declared = coerce_sig_gen_inline_declared(data.fetch("sig_gen", nil))
|
|
497
543
|
@pre_eval = expand_pre_eval_entries(
|
|
498
544
|
Array(data.fetch("pre_eval", DEFAULTS.fetch("pre_eval"))).map(&:to_s)
|
|
499
545
|
)
|
|
@@ -520,6 +566,9 @@ module Rigor
|
|
|
520
566
|
raw_max = cache.fetch("max_bytes")
|
|
521
567
|
@cache_max_bytes = raw_max.nil? ? nil : Integer(raw_max)
|
|
522
568
|
@cache_validation = coerce_cache_validation(cache.fetch("validation", "auto"))
|
|
569
|
+
@plugins_isolation = coerce_plugins_isolation(
|
|
570
|
+
data.fetch("plugins_isolation", DEFAULTS.fetch("plugins_isolation"))
|
|
571
|
+
)
|
|
523
572
|
@plugins_io_network = coerce_network_policy(plugins_io.fetch("network"))
|
|
524
573
|
@plugins_io_allowed_paths = Array(plugins_io.fetch("allowed_paths")).map(&:to_s).freeze
|
|
525
574
|
@plugins_io_allowed_url_hosts = Array(plugins_io.fetch("allowed_url_hosts")).map(&:to_s).freeze
|
|
@@ -575,6 +624,9 @@ module Rigor
|
|
|
575
624
|
"disable" => disabled_rules,
|
|
576
625
|
"libraries" => libraries,
|
|
577
626
|
"signature_paths" => signature_paths,
|
|
627
|
+
# `test_paths:` is deliberately absent, as `effects:` is: `to_h` feeds the run cache key and the
|
|
628
|
+
# incremental-snapshot fingerprint, and the test roots change no diagnostic, so editing them must not
|
|
629
|
+
# invalidate either. `sig_gen:` is absent for the same reason: only `rigor sig-gen` reads it.
|
|
578
630
|
"pre_eval" => pre_eval,
|
|
579
631
|
"fold_platform_specific_paths" => fold_platform_specific_paths,
|
|
580
632
|
"parameter_inference" => parameter_inference,
|
|
@@ -583,6 +635,7 @@ module Rigor
|
|
|
583
635
|
"max_bytes" => cache_max_bytes,
|
|
584
636
|
"validation" => cache_validation
|
|
585
637
|
},
|
|
638
|
+
"plugins_isolation" => plugins_isolation,
|
|
586
639
|
"plugins_io" => {
|
|
587
640
|
"network" => plugins_io_network.to_s,
|
|
588
641
|
"allowed_paths" => plugins_io_allowed_paths,
|
|
@@ -626,9 +679,8 @@ module Rigor
|
|
|
626
679
|
# the same checkout: a typo is a bug, and reading it as `false` would ship the feature permanently off
|
|
627
680
|
# with no signal.
|
|
628
681
|
#
|
|
629
|
-
# @param id
|
|
630
|
-
# @
|
|
631
|
-
# @raise [ArgumentError] if `id` names no known feature.
|
|
682
|
+
# @param id — a feature id from {BleedingEdge::FEATURES} or {BleedingEdge::GRADUATED}.
|
|
683
|
+
# @raise ArgumentError — if `id` names no known feature.
|
|
632
684
|
def bleeding_edge_active?(id)
|
|
633
685
|
return true if BleedingEdge.graduated?(id)
|
|
634
686
|
unless BleedingEdge.known_id?(id)
|
|
@@ -962,11 +1014,61 @@ module Rigor
|
|
|
962
1014
|
VALID_CACHE_VALIDATIONS = %w[auto stat digest].freeze
|
|
963
1015
|
private_constant :VALID_CACHE_VALIDATIONS
|
|
964
1016
|
|
|
1017
|
+
# The `sig_gen.inline_declared:` values, as the Symbols {#sig_gen_inline_declared} answers.
|
|
1018
|
+
VALID_SIG_GEN_INLINE_DECLARED = %w[write skip].freeze
|
|
1019
|
+
private_constant :VALID_SIG_GEN_INLINE_DECLARED
|
|
1020
|
+
|
|
1021
|
+
# Rejects rather than fails soft: an unrecognised value read as `write` would put a second declaration of
|
|
1022
|
+
# every inline-annotated method into `sig/` for exactly the project that asked for none. A `sig_gen:`
|
|
1023
|
+
# block that omits the key, or is written empty, keeps the default.
|
|
1024
|
+
def coerce_sig_gen_inline_declared(section)
|
|
1025
|
+
section = {} if section.nil?
|
|
1026
|
+
raise ConfigurationError, "sig_gen must be a mapping, got #{section.inspect}" unless section.is_a?(Hash)
|
|
1027
|
+
|
|
1028
|
+
# The schema closes this object (`additionalProperties: false`); a misspelled key here would otherwise
|
|
1029
|
+
# load as the default and write exactly what `inline_declare: skip` was meant to prevent.
|
|
1030
|
+
unknown = section.keys.map(&:to_s) - DEFAULTS.fetch("sig_gen").keys
|
|
1031
|
+
unless unknown.empty?
|
|
1032
|
+
raise ConfigurationError,
|
|
1033
|
+
"sig_gen has unknown key(s) #{unknown.inspect}; known keys: #{DEFAULTS.fetch('sig_gen').keys.inspect}"
|
|
1034
|
+
end
|
|
1035
|
+
|
|
1036
|
+
value = section.fetch("inline_declared", DEFAULTS.dig("sig_gen", "inline_declared")).to_s
|
|
1037
|
+
unless VALID_SIG_GEN_INLINE_DECLARED.include?(value)
|
|
1038
|
+
raise ConfigurationError,
|
|
1039
|
+
"sig_gen.inline_declared must be one of #{VALID_SIG_GEN_INLINE_DECLARED.inspect}, " \
|
|
1040
|
+
"got #{section['inline_declared'].inspect}"
|
|
1041
|
+
end
|
|
1042
|
+
|
|
1043
|
+
value.to_sym
|
|
1044
|
+
end
|
|
1045
|
+
|
|
965
1046
|
def coerce_cache_validation(value)
|
|
966
1047
|
str = value.to_s
|
|
967
1048
|
VALID_CACHE_VALIDATIONS.include?(str) ? str : "auto"
|
|
968
1049
|
end
|
|
969
1050
|
|
|
1051
|
+
# Rejects rather than fails soft — a strategy that silently resolves to a different one is exactly
|
|
1052
|
+
# the failure #911 exists to close, and the run's isolation is not a detail the user can be assumed
|
|
1053
|
+
# not to have meant.
|
|
1054
|
+
def coerce_plugins_isolation(value)
|
|
1055
|
+
return nil if value.nil?
|
|
1056
|
+
|
|
1057
|
+
str = value.to_s
|
|
1058
|
+
return str if CONFIGURABLE_PLUGIN_ISOLATIONS.include?(str)
|
|
1059
|
+
|
|
1060
|
+
if str == "ruby_box"
|
|
1061
|
+
raise ConfigurationError,
|
|
1062
|
+
"plugins_isolation: ruby_box cannot be selected from the configuration file — `Ruby::Box` " \
|
|
1063
|
+
"must be active before Ruby boots. Run rigor with RIGOR_PLUGIN_ISOLATION=ruby_box instead " \
|
|
1064
|
+
"(the launcher re-execs itself with RUBY_BOX=1 set)."
|
|
1065
|
+
end
|
|
1066
|
+
|
|
1067
|
+
raise ConfigurationError,
|
|
1068
|
+
"plugins_isolation must be one of #{CONFIGURABLE_PLUGIN_ISOLATIONS.inspect} " \
|
|
1069
|
+
"(`ruby_box` is environment-only), got #{value.inspect}"
|
|
1070
|
+
end
|
|
1071
|
+
|
|
970
1072
|
def coerce_network_policy(value)
|
|
971
1073
|
sym = value.to_sym
|
|
972
1074
|
unless VALID_NETWORK_POLICIES.include?(sym)
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "../source/constant_path"
|
|
6
|
+
require_relative "file_collection"
|
|
7
|
+
|
|
8
|
+
module Rigor
|
|
9
|
+
module Effects
|
|
10
|
+
# The ancestry half of a file's {FileCollection}: what each class declares above itself, what it
|
|
11
|
+
# includes, and where the scan has to admit it could not read the ancestry at all.
|
|
12
|
+
#
|
|
13
|
+
# Split out of {Scanner} because the three questions are one subject with one invariant — a name goes
|
|
14
|
+
# in **as written**, and the propagator, which is the only place the whole project is in view, decides
|
|
15
|
+
# which constant it meant. The scanner owns unit identity and delegates this; the two tables are handed
|
|
16
|
+
# back by {#superclasses} / {#includes} and go into the collection unchanged.
|
|
17
|
+
class AncestryRecorder
|
|
18
|
+
attr_reader :superclasses, :includes
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@superclasses = {}
|
|
22
|
+
@includes = {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# `class Loud < Base` inside `module Tracer` names `Base`, so the candidates go in and the propagator
|
|
26
|
+
# picks the one the merged project defines.
|
|
27
|
+
#
|
|
28
|
+
# A superclass expression that is **not** a constant path — `class K < Struct.new(:a)`,
|
|
29
|
+
# `< Data.define(:a)`, `< DelegateClass(X)`, `< Sequel::Model(:t)` — records
|
|
30
|
+
# {FileCollection::OPAQUE_ANCESTOR} rather than nothing (#1039). Recording nothing made it
|
|
31
|
+
# indistinguishable from a class with no `<` at all, which is the one reading the constructor rule
|
|
32
|
+
# turns into an answer; such a class inherits a constructor built at load time, which is the
|
|
33
|
+
# opposite of an absent one.
|
|
34
|
+
def record_superclass(full_name, node, prefix)
|
|
35
|
+
return if node.superclass.nil? || opaque?(full_name)
|
|
36
|
+
|
|
37
|
+
superclass = Source::ConstantPath.qualified_name(node.superclass)
|
|
38
|
+
@superclasses[full_name] =
|
|
39
|
+
superclass ? lexical_candidates(superclass, prefix) : [FileCollection::OPAQUE_ANCESTOR]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# A class BUILT AT LOAD TIME and assigned to a constant — `Anon = Class.new(Base)`,
|
|
43
|
+
# `Point = Struct.new(:x)`, `Rec = Data.define(:x)`, `Dele = DelegateClass(X)` — records the opaque
|
|
44
|
+
# sentinel (#1039). Without it a later `class Point; def more; end; end` reopening is the only thing
|
|
45
|
+
# the scan sees: the class becomes project-known, nothing says what is above it, and the constructor
|
|
46
|
+
# rule would read that silence as "no constructor anywhere" when the constructor is precisely what
|
|
47
|
+
# the load-time call built. It is the `class K < Struct.new(:a)` case in its other spelling.
|
|
48
|
+
#
|
|
49
|
+
# Only a **call**-valued assignment qualifies. A literal cannot be a class, and a constant-path value
|
|
50
|
+
# (`Alias = Real`) is a second name for a class whose own ancestry the scan already recorded.
|
|
51
|
+
#
|
|
52
|
+
# The sentinel is **sticky**: it overwrites a spelled `< Base` and no later one displaces it, here or
|
|
53
|
+
# in {FileCollection.merge_all}. A reopening's `class Anon < Base` is not more than this says but
|
|
54
|
+
# less — `Anon = Class.new(Base) { def initialize; … end }` puts a constructor in the block, which the
|
|
55
|
+
# scan files under the enclosing namespace and cannot attribute to `Anon` at all, and the spelled
|
|
56
|
+
# superclass says nothing about it. Sticky is also what makes the answer independent of which file a
|
|
57
|
+
# run reads first.
|
|
58
|
+
#
|
|
59
|
+
# `Class.new(Base)` additionally keeps `Base` beside the sentinel. The sentinel still declines
|
|
60
|
+
# `Anon.new` itself, and the parent link is what files `Anon` under `Base` in the subclass index, so
|
|
61
|
+
# a `self.class.new` in `Base` — which the closed-world join says may construct `Anon` — sees an
|
|
62
|
+
# unreadable constructor below it and declines too.
|
|
63
|
+
def record_constant_class(node, prefix)
|
|
64
|
+
value = node.value
|
|
65
|
+
return unless value.is_a?(Prism::CallNode)
|
|
66
|
+
|
|
67
|
+
name = constant_write_name(node, prefix)
|
|
68
|
+
return if name.nil?
|
|
69
|
+
|
|
70
|
+
@superclasses[name] = [FileCollection::OPAQUE_ANCESTOR, *load_time_parent(value, prefix)]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# A receiver-less `include` / `prepend` in `class_name`'s body, its constant arguments recorded as
|
|
74
|
+
# written.
|
|
75
|
+
#
|
|
76
|
+
# Both are calls on `self`, like `define_method`, and the include table is the instance ancestry that
|
|
77
|
+
# `super` and the constructor rule walk, so a call is recorded only where `self` is the class the body
|
|
78
|
+
# opened (see {#instance_side_self?}). Inside `class << self`, or a `class_eval` / `instance_eval`
|
|
79
|
+
# block on `singleton_class`, it mixes the module into the singleton class, which is what `extend`
|
|
80
|
+
# does, and the collection is as blind to it as it is to `extend`.
|
|
81
|
+
#
|
|
82
|
+
# @param context — the {DefinitionContext} of the class-body position the call sits at
|
|
83
|
+
def record_includes(class_name, node, prefix, context)
|
|
84
|
+
return unless instance_side_self?(context)
|
|
85
|
+
|
|
86
|
+
names = node.arguments&.arguments&.filter_map { |argument| Source::ConstantPath.qualified_name(argument) }
|
|
87
|
+
candidates = (names || []).flat_map { |name| lexical_candidates(name, prefix) }
|
|
88
|
+
(@includes[class_name] ||= []).concat(candidates) unless candidates.empty?
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# `alias initialize setup` / `alias_method :initialize, :setup` makes the constructor another
|
|
92
|
+
# method's body, and the scan models no aliases at all (#1039). The opaque sentinel is the minimal
|
|
93
|
+
# honest answer: the ancestry stops being readable, the constructor rule declines, and this class's
|
|
94
|
+
# callers stay exactly as unclaimed as they were before that rule existed.
|
|
95
|
+
def record_initialize_alias(class_name)
|
|
96
|
+
(@includes[class_name] ||= []) << FileCollection::OPAQUE_ANCESTOR
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Whether this node aliases the instance-side `initialize`, in either spelling. A `CallNode` qualifies
|
|
100
|
+
# only as a receiver-less `alias_method` whose first symbol argument is the new name.
|
|
101
|
+
#
|
|
102
|
+
# `alias` works on the default definee and `alias_method` on `self`, so the two part in an
|
|
103
|
+
# `instance_eval` block, and inside `class << self` both alias the singleton class's `initialize`,
|
|
104
|
+
# a class method `new` never calls. Where the syntax does not say which class either one is, it
|
|
105
|
+
# records nothing here, as an include there does not (see {#instance_side_self?}).
|
|
106
|
+
#
|
|
107
|
+
# @param context — the {DefinitionContext} of the class-body position the node sits at
|
|
108
|
+
def alias_to_initialize?(node, context)
|
|
109
|
+
case node
|
|
110
|
+
when Prism::AliasMethodNode
|
|
111
|
+
context.definee_singleton == false && literal_name(node.new_name) == "initialize"
|
|
112
|
+
when Prism::CallNode
|
|
113
|
+
node.receiver.nil? && node.name == :alias_method && instance_side_self?(context) &&
|
|
114
|
+
literal_name(first_argument(node)) == "initialize"
|
|
115
|
+
else false
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# An ancestry name is recorded AS WRITTEN — a single file cannot say which constant it resolves to.
|
|
120
|
+
# So the candidates Ruby's own lexical lookup would try go in, most-qualified first, and the
|
|
121
|
+
# propagator picks the one the merged project actually defines. Same shape as `ScopeIndexer`'s
|
|
122
|
+
# as-written superclass table, resolved at the same point: when the whole project is in view.
|
|
123
|
+
def lexical_candidates(name, prefix)
|
|
124
|
+
return [name] if prefix.empty? || name.start_with?("#{prefix.join('::')}::")
|
|
125
|
+
|
|
126
|
+
prefix.length.downto(1).map { |depth| "#{prefix.first(depth).join('::')}::#{name}" } + [name]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
# Whether the syntax shows that a call on `self` written here works on the class the enclosing body
|
|
132
|
+
# opened, or on `Object` through `main` at the top level, so what it declares is that class's instance
|
|
133
|
+
# ancestry.
|
|
134
|
+
#
|
|
135
|
+
# It does not where `self` is the singleton class, nor where the syntax does not say what `self` is:
|
|
136
|
+
# the block of `Class.new` and its kin, an eval on another receiver, `class << obj`. The syntax does not
|
|
137
|
+
# name the class the module goes into there, and filing it under the enclosing class would put it
|
|
138
|
+
# where `super` and the constructor rule look for that class. The class Ruby does put it in misses it,
|
|
139
|
+
# as it would an `extend`, and so does `W` for a `W.class_eval { include M }` inside `class W`, which
|
|
140
|
+
# the context reads as an eval on any other receiver (#1322).
|
|
141
|
+
def instance_side_self?(context)
|
|
142
|
+
!context.self_singleton_class? && context.self_kind != :unknown
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def opaque?(full_name)
|
|
146
|
+
@superclasses.fetch(full_name, []).include?(FileCollection::OPAQUE_ANCESTOR)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# The superclass a `Class.new(Base)` names, as candidates. Read from `Class.new` alone: it is the one
|
|
150
|
+
# load-time builder whose first argument IS the superclass, and inventing an ancestry edge from any
|
|
151
|
+
# other call's first constant argument would let an unrelated class's methods resolve through it.
|
|
152
|
+
def load_time_parent(value, prefix)
|
|
153
|
+
return [] unless value.name == :new && constant_receiver_name(value.receiver) == "Class"
|
|
154
|
+
|
|
155
|
+
argument = value.arguments&.arguments&.first
|
|
156
|
+
name = argument && Source::ConstantPath.qualified_name(argument)
|
|
157
|
+
name ? lexical_candidates(name, prefix) : []
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def constant_receiver_name(receiver)
|
|
161
|
+
Source::ConstantPath.qualified_name(receiver) if receiver.is_a?(Prism::ConstantReadNode)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# `alias_method :initialize, :setup` and `alias_method "initialize", "setup"` are the same
|
|
165
|
+
# declaration; only an interpolated name is beyond the scan.
|
|
166
|
+
def literal_name(node)
|
|
167
|
+
node.unescaped if node.is_a?(Prism::SymbolNode) || node.is_a?(Prism::StringNode)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# The key a constant assignment declares, qualified by the nesting it is written in.
|
|
171
|
+
def constant_write_name(node, prefix)
|
|
172
|
+
case node
|
|
173
|
+
when Prism::ConstantWriteNode then [*prefix, node.name.to_s].join("::")
|
|
174
|
+
when Prism::ConstantPathWriteNode then qualified_write_name(node, prefix)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def qualified_write_name(node, prefix)
|
|
179
|
+
target = Source::ConstantPath.qualified_name(node.target)
|
|
180
|
+
return nil if target.nil?
|
|
181
|
+
return target if prefix.empty? || target.start_with?("#{prefix.join('::')}::")
|
|
182
|
+
|
|
183
|
+
[*prefix, target].join("::")
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def first_argument(node)
|
|
187
|
+
node.arguments&.arguments&.first
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
@@ -26,11 +26,15 @@ module Rigor
|
|
|
26
26
|
EMPTY_ROWS = {}.freeze
|
|
27
27
|
private_constant :EMPTY_ROWS
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
# The run's empty table. Eagerly built on the main Ractor at load time (bottom of the class body)
|
|
30
|
+
# rather than memoised on first use: `@empty ||= new({})` is a class-ivar WRITE, which a non-main
|
|
31
|
+
# Ractor may not perform, and every project that configures no attribution reaches it — including
|
|
32
|
+
# from inside {Analysis::WorkerSession#initialize}, which is where a Ractor-pool worker runs (#1055).
|
|
33
|
+
class << self
|
|
34
|
+
attr_reader :empty
|
|
31
35
|
end
|
|
32
36
|
|
|
33
|
-
# @param table
|
|
37
|
+
# @param table — `Configuration#effects_attribution` — method key to
|
|
34
38
|
# label list, both already shape-validated at load (tier 2).
|
|
35
39
|
def self.build(table)
|
|
36
40
|
return empty if table.nil? || table.empty?
|
|
@@ -49,8 +53,6 @@ module Rigor
|
|
|
49
53
|
|
|
50
54
|
# The labels attributed to a method key (`Net::HTTP.get`), or nil when the table says nothing about
|
|
51
55
|
# it. The key is spelled by the caller, which already builds the same string for the catalogue.
|
|
52
|
-
#
|
|
53
|
-
# @return [LabelSet, nil]
|
|
54
56
|
def [](key)
|
|
55
57
|
@rows[key]
|
|
56
58
|
end
|
|
@@ -71,6 +73,11 @@ module Rigor
|
|
|
71
73
|
out[key.to_s] = set unless set.empty?
|
|
72
74
|
end.freeze
|
|
73
75
|
end
|
|
76
|
+
|
|
77
|
+
# Populates the `@empty` singleton on the main Ractor at load time. `Ractor.make_shareable` rather
|
|
78
|
+
# than `freeze` so a worker's READ of the class ivar is legal too: a non-main Ractor may read a
|
|
79
|
+
# class/module ivar only when the value is deeply shareable.
|
|
80
|
+
@empty = Ractor.make_shareable(new({}))
|
|
74
81
|
end
|
|
75
82
|
end
|
|
76
83
|
end
|