rigortype 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +37 -4
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +14 -1
- data/docs/handbook/11-sig-gen.md +96 -15
- 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 +39 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +12 -1
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +6 -1
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/plugins/rigor-actionpack.md +18 -0
- data/docs/manual/plugins/rigor-activerecord.md +28 -0
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
- data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
- data/lib/rigor/analysis/baseline.rb +2 -2
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +200 -32
- data/lib/rigor/analysis/crash_signature.rb +69 -13
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +6 -1
- data/lib/rigor/analysis/incremental.rb +16 -0
- data/lib/rigor/analysis/incremental_session.rb +100 -29
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +4 -1
- data/lib/rigor/analysis/run_cache_key.rb +1 -1
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +316 -40
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
- data/lib/rigor/analysis/runner/pool_coordinator.rb +430 -77
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +13 -3
- data/lib/rigor/analysis/runner.rb +200 -43
- data/lib/rigor/analysis/worker_session.rb +36 -12
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +166 -30
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +4 -4
- data/lib/rigor/builtins/static_return_refinements.rb +5 -6
- data/lib/rigor/cache/annotation_location.rb +70 -0
- data/lib/rigor/cache/descriptor.rb +58 -20
- data/lib/rigor/cache/engine_source.rb +4 -4
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +40 -6
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +79 -11
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +39 -0
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +85 -33
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +3 -3
- data/lib/rigor/cli/docs_command.rb +2 -2
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +1 -1
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +2 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +31 -2
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +36 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +10 -6
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +68 -6
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +6 -6
- data/lib/rigor/configuration.rb +43 -4
- data/lib/rigor/effects/attribution.rb +1 -3
- data/lib/rigor/effects/config_envelopes.rb +6 -8
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +0 -1
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +8 -8
- data/lib/rigor/effects/envelope_index.rb +5 -8
- data/lib/rigor/effects/framework_units.rb +4 -6
- data/lib/rigor/effects/identity.rb +4 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/method_key.rb +1 -1
- data/lib/rigor/effects/plugin_facts.rb +4 -6
- data/lib/rigor/effects/propagator.rb +2 -3
- data/lib/rigor/effects/scanner.rb +1 -1
- data/lib/rigor/effects/signature_sources.rb +3 -5
- data/lib/rigor/effects/snapshot.rb +10 -10
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/unit_scan.rb +10 -10
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +28 -0
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +51 -4
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +613 -113
- data/lib/rigor/environment.rb +189 -27
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/inference/acceptance.rb +153 -8
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/body_fixpoint.rb +5 -5
- data/lib/rigor/inference/budget_trace.rb +1 -5
- data/lib/rigor/inference/captured_locals.rb +2 -3
- data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +168 -0
- data/lib/rigor/inference/expression_typer.rb +763 -143
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +19 -10
- data/lib/rigor/inference/hkt_sugar_translator.rb +15 -21
- data/lib/rigor/inference/index_write_widening.rb +1 -5
- data/lib/rigor/inference/macro_block_self_type.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +12 -13
- data/lib/rigor/inference/method_parameter_binder.rb +4 -6
- data/lib/rigor/inference/multi_target_binder.rb +1 -3
- data/lib/rigor/inference/mutation_rejoin.rb +168 -0
- data/lib/rigor/inference/mutation_widening.rb +100 -51
- data/lib/rigor/inference/narrowing.rb +299 -46
- data/lib/rigor/inference/optimistic_origin.rb +1 -7
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_patched_methods.rb +2 -2
- data/lib/rigor/inference/project_patched_scanner.rb +3 -3
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +3 -3
- data/lib/rigor/inference/refinement_mutation.rb +72 -0
- data/lib/rigor/inference/scope_indexer.rb +688 -151
- data/lib/rigor/inference/statement_evaluator.rb +221 -50
- data/lib/rigor/inference/string_mutation.rb +60 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +1 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +2 -2
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +2 -5
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/plugin/base.rb +90 -21
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +7 -7
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +31 -12
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +23 -1
- data/lib/rigor/plugin/registry.rb +67 -14
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +11 -11
- data/lib/rigor/protection/kill_signature.rb +2 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +6 -9
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +12 -13
- data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
- data/lib/rigor/rbs_extended/reporter.rb +122 -16
- data/lib/rigor/rbs_extended.rb +67 -22
- data/lib/rigor/reflection/constant_path.rb +126 -0
- data/lib/rigor/reflection.rb +26 -10
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +30 -0
- data/lib/rigor/scope.rb +203 -4
- data/lib/rigor/sig_gen/generator.rb +181 -102
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/observation_collector.rb +17 -15
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +17 -11
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/writer.rb +23 -6
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +2 -5
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +68 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +5 -5
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +3 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/environment.rbs +11 -0
- data/sig/rigor/inference.rbs +3 -10
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/manifest.rbs +2 -1
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +24 -3
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
- data/sig/rigor/type.rbs +27 -1
- data/sig/rigor.rbs +1 -0
- data/skills/rigor-ask/SKILL.md +8 -6
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
- data/skills/rigor-project-init/SKILL.md +6 -3
- data/skills/rigor-project-init/references/02-configure.md +10 -6
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-type-oracle/SKILL.md +228 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
- metadata +28 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
data/lib/rigor/configuration.rb
CHANGED
|
@@ -117,6 +117,16 @@ module Rigor
|
|
|
117
117
|
# `"stat"` (or `RIGOR_CI_DETECT=0`).
|
|
118
118
|
"validation" => "auto"
|
|
119
119
|
},
|
|
120
|
+
# ADR-39 slice 5 / #911 — the plugin-isolation strategy for target-library invocation. `nil` (the
|
|
121
|
+
# default) names nothing, leaving the choice to `RIGOR_PLUGIN_ISOLATION` and then to
|
|
122
|
+
# `Plugin::Isolation::DEFAULT`. The environment variable WINS over this key: it is the
|
|
123
|
+
# one-invocation operator override (`RIGOR_PLUGIN_ISOLATION=none rigor check` on a platform where
|
|
124
|
+
# the fork worker misbehaves), and an override a committed file can veto is not an override.
|
|
125
|
+
# Only `none` and `process` are selectable here — `ruby_box` needs `RUBY_BOX=1` set before Ruby
|
|
126
|
+
# boots, which is what `exe/rigor` re-execs for, and the launcher deliberately does not parse YAML
|
|
127
|
+
# before that re-exec (ADR-87 / ADR-104 boot-slim), so naming it here raises instead of silently
|
|
128
|
+
# resolving to a different strategy.
|
|
129
|
+
"plugins_isolation" => nil,
|
|
120
130
|
"plugins_io" => {
|
|
121
131
|
"network" => "disabled",
|
|
122
132
|
"allowed_paths" => [],
|
|
@@ -212,6 +222,10 @@ module Rigor
|
|
|
212
222
|
# reservation FINDABLE by the next person adding config validation (#166 is exactly that), and to
|
|
213
223
|
# give the schema-parity spec something to key on: a reserved namespace is by definition absent
|
|
214
224
|
# from DEFAULTS, so the DEFAULTS-driven schema gate can never see it.
|
|
225
|
+
# The isolation strategies a `.rigor.yml` may name (ADR-39 slice 5 / #911). `ruby_box` is
|
|
226
|
+
# deliberately absent: it is a boot-time decision the config file is read too late to make.
|
|
227
|
+
CONFIGURABLE_PLUGIN_ISOLATIONS = %w[none process].freeze
|
|
228
|
+
|
|
215
229
|
RESERVED_NAMESPACES = %w[rigor_rs].freeze
|
|
216
230
|
|
|
217
231
|
# Every top-level key a conforming `.rigor.yml` may carry: the keys this implementation owns, plus
|
|
@@ -238,6 +252,7 @@ module Rigor
|
|
|
238
252
|
attr_reader :target_ruby, :paths, :exclude_patterns, :plugins, :cache_path, :cache_max_bytes,
|
|
239
253
|
:cache_validation, :disabled_rules,
|
|
240
254
|
:libraries, :signature_paths, :fold_platform_specific_paths, :parameter_inference,
|
|
255
|
+
:plugins_isolation,
|
|
241
256
|
:plugins_io_network, :plugins_io_allowed_paths,
|
|
242
257
|
:plugins_io_allowed_url_hosts,
|
|
243
258
|
:severity_profile, :severity_overrides,
|
|
@@ -466,7 +481,7 @@ module Rigor
|
|
|
466
481
|
|
|
467
482
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
468
483
|
#
|
|
469
|
-
# @param effects_key_present
|
|
484
|
+
# @param effects_key_present — ADR-103 WD15 — whether the file this `data` came from carried an
|
|
470
485
|
# `effects:` key at all, independent of its value. Defaults to `data.key?("effects")`, which is the
|
|
471
486
|
# right answer for a caller that passes a raw (non-`DEFAULTS`-merged) hash directly — the common shape
|
|
472
487
|
# in specs — but is always `true` once `data` has been through `DEFAULTS.merge`, because `DEFAULTS`
|
|
@@ -520,6 +535,9 @@ module Rigor
|
|
|
520
535
|
raw_max = cache.fetch("max_bytes")
|
|
521
536
|
@cache_max_bytes = raw_max.nil? ? nil : Integer(raw_max)
|
|
522
537
|
@cache_validation = coerce_cache_validation(cache.fetch("validation", "auto"))
|
|
538
|
+
@plugins_isolation = coerce_plugins_isolation(
|
|
539
|
+
data.fetch("plugins_isolation", DEFAULTS.fetch("plugins_isolation"))
|
|
540
|
+
)
|
|
523
541
|
@plugins_io_network = coerce_network_policy(plugins_io.fetch("network"))
|
|
524
542
|
@plugins_io_allowed_paths = Array(plugins_io.fetch("allowed_paths")).map(&:to_s).freeze
|
|
525
543
|
@plugins_io_allowed_url_hosts = Array(plugins_io.fetch("allowed_url_hosts")).map(&:to_s).freeze
|
|
@@ -583,6 +601,7 @@ module Rigor
|
|
|
583
601
|
"max_bytes" => cache_max_bytes,
|
|
584
602
|
"validation" => cache_validation
|
|
585
603
|
},
|
|
604
|
+
"plugins_isolation" => plugins_isolation,
|
|
586
605
|
"plugins_io" => {
|
|
587
606
|
"network" => plugins_io_network.to_s,
|
|
588
607
|
"allowed_paths" => plugins_io_allowed_paths,
|
|
@@ -626,9 +645,8 @@ module Rigor
|
|
|
626
645
|
# the same checkout: a typo is a bug, and reading it as `false` would ship the feature permanently off
|
|
627
646
|
# with no signal.
|
|
628
647
|
#
|
|
629
|
-
# @param id
|
|
630
|
-
# @
|
|
631
|
-
# @raise [ArgumentError] if `id` names no known feature.
|
|
648
|
+
# @param id — a feature id from {BleedingEdge::FEATURES} or {BleedingEdge::GRADUATED}.
|
|
649
|
+
# @raise ArgumentError — if `id` names no known feature.
|
|
632
650
|
def bleeding_edge_active?(id)
|
|
633
651
|
return true if BleedingEdge.graduated?(id)
|
|
634
652
|
unless BleedingEdge.known_id?(id)
|
|
@@ -967,6 +985,27 @@ module Rigor
|
|
|
967
985
|
VALID_CACHE_VALIDATIONS.include?(str) ? str : "auto"
|
|
968
986
|
end
|
|
969
987
|
|
|
988
|
+
# Rejects rather than fails soft — a strategy that silently resolves to a different one is exactly
|
|
989
|
+
# the failure #911 exists to close, and the run's isolation is not a detail the user can be assumed
|
|
990
|
+
# not to have meant.
|
|
991
|
+
def coerce_plugins_isolation(value)
|
|
992
|
+
return nil if value.nil?
|
|
993
|
+
|
|
994
|
+
str = value.to_s
|
|
995
|
+
return str if CONFIGURABLE_PLUGIN_ISOLATIONS.include?(str)
|
|
996
|
+
|
|
997
|
+
if str == "ruby_box"
|
|
998
|
+
raise ConfigurationError,
|
|
999
|
+
"plugins_isolation: ruby_box cannot be selected from the configuration file — `Ruby::Box` " \
|
|
1000
|
+
"must be active before Ruby boots. Run rigor with RIGOR_PLUGIN_ISOLATION=ruby_box instead " \
|
|
1001
|
+
"(the launcher re-execs itself with RUBY_BOX=1 set)."
|
|
1002
|
+
end
|
|
1003
|
+
|
|
1004
|
+
raise ConfigurationError,
|
|
1005
|
+
"plugins_isolation must be one of #{CONFIGURABLE_PLUGIN_ISOLATIONS.inspect} " \
|
|
1006
|
+
"(`ruby_box` is environment-only), got #{value.inspect}"
|
|
1007
|
+
end
|
|
1008
|
+
|
|
970
1009
|
def coerce_network_policy(value)
|
|
971
1010
|
sym = value.to_sym
|
|
972
1011
|
unless VALID_NETWORK_POLICIES.include?(sym)
|
|
@@ -30,7 +30,7 @@ module Rigor
|
|
|
30
30
|
@empty ||= new({})
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
# @param table
|
|
33
|
+
# @param table — `Configuration#effects_attribution` — method key to
|
|
34
34
|
# label list, both already shape-validated at load (tier 2).
|
|
35
35
|
def self.build(table)
|
|
36
36
|
return empty if table.nil? || table.empty?
|
|
@@ -49,8 +49,6 @@ module Rigor
|
|
|
49
49
|
|
|
50
50
|
# The labels attributed to a method key (`Net::HTTP.get`), or nil when the table says nothing about
|
|
51
51
|
# it. The key is spelled by the caller, which already builds the same string for the catalogue.
|
|
52
|
-
#
|
|
53
|
-
# @return [LabelSet, nil]
|
|
54
52
|
def [](key)
|
|
55
53
|
@rows[key]
|
|
56
54
|
end
|
|
@@ -69,9 +69,8 @@ module Rigor
|
|
|
69
69
|
|
|
70
70
|
# Resolves `Configuration#effects_envelopes` against a registry.
|
|
71
71
|
#
|
|
72
|
-
# @param entries
|
|
73
|
-
# @param registry
|
|
74
|
-
# @return [Array<Entry>]
|
|
72
|
+
# @param entries — the loaded, shape-validated entries
|
|
73
|
+
# @param registry — the vocabulary, project extensions included
|
|
75
74
|
def build(entries:, registry:)
|
|
76
75
|
entries.each_with_index.map do |entry, index|
|
|
77
76
|
labels = Array(entry["effect"]).map(&:to_s)
|
|
@@ -86,11 +85,10 @@ module Rigor
|
|
|
86
85
|
|
|
87
86
|
# The class-level envelopes the entries put on a project.
|
|
88
87
|
#
|
|
89
|
-
# @param
|
|
90
|
-
# @param
|
|
91
|
-
# @param
|
|
92
|
-
# @
|
|
93
|
-
# @return [Hash{String => Envelope}] one envelope per selected class, keyed by class name
|
|
88
|
+
# @param class_names — every class the run collected units for
|
|
89
|
+
# @param sources — `Runner#effect_sources` — `{method key => [path]}`
|
|
90
|
+
# @param project_root — what `sources` paths are relativised against
|
|
91
|
+
# @return one envelope per selected class, keyed by class name
|
|
94
92
|
def for_classes(entries:, class_names:, sources: {}, project_root: Dir.pwd)
|
|
95
93
|
return NO_ENVELOPES if entries.empty?
|
|
96
94
|
|
|
@@ -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
|
|
@@ -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)
|
|
@@ -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?
|
|
@@ -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: {})
|
|
@@ -45,10 +45,9 @@ 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
|
[
|
|
@@ -66,8 +65,7 @@ module Rigor
|
|
|
66
65
|
# the analyzed-path set — for free, so "the effects identity is the diagnostics identity plus three
|
|
67
66
|
# things" is a property of the code rather than a claim about it.
|
|
68
67
|
#
|
|
69
|
-
# @param base
|
|
70
|
-
# @return [Cache::Descriptor]
|
|
68
|
+
# @param base — the run's diagnostics key descriptor
|
|
71
69
|
def descriptor(base:, configuration:, registry: Registry.default, catalog: Catalog.default,
|
|
72
70
|
plugin_facts: nil)
|
|
73
71
|
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
|
|
@@ -14,7 +14,7 @@ module Rigor
|
|
|
14
14
|
module MethodKey
|
|
15
15
|
module_function
|
|
16
16
|
|
|
17
|
-
# @return
|
|
17
|
+
# @return `[owner, separator, selector]`, or nil when `key` is
|
|
18
18
|
# not a method key at all.
|
|
19
19
|
def split(key)
|
|
20
20
|
text = key.to_s
|
|
@@ -77,11 +77,10 @@ module Rigor
|
|
|
77
77
|
@empty ||= new(contributions: [], superclasses: NO_ROWS, includes: NO_ROWS)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
-
# @param
|
|
81
|
-
# @param superclasses [Hash{String=>String,Array<String>}] the project's as-written superclass table
|
|
80
|
+
# @param superclasses — the project's as-written superclass table
|
|
82
81
|
# (`Scope::DiscoveryIndex#discovered_superclasses`). Empty is legal and simply means no row matches
|
|
83
82
|
# through inheritance.
|
|
84
|
-
# @param includes
|
|
83
|
+
# @param includes — the project's as-written include / prepend table
|
|
85
84
|
# (`Scope::DiscoveryIndex#discovered_includes`), for the frameworks whose contract is a module
|
|
86
85
|
# rather than a base class (#456).
|
|
87
86
|
def self.build(plugin_registry, superclasses: NO_ROWS, includes: NO_ROWS)
|
|
@@ -144,9 +143,8 @@ module Rigor
|
|
|
144
143
|
|
|
145
144
|
# The row colouring `owner`'s `selector`, found on `owner` itself or on a project ancestor of it.
|
|
146
145
|
#
|
|
147
|
-
# @param owner
|
|
148
|
-
# @param singleton
|
|
149
|
-
# @return [Row, nil]
|
|
146
|
+
# @param owner — the receiver's class name as the syntax or the typer named it
|
|
147
|
+
# @param singleton — whether the call is `Owner.selector`
|
|
150
148
|
def class_row(owner, singleton, selector)
|
|
151
149
|
return nil if owner.nil? || @class_rows.empty?
|
|
152
150
|
|
|
@@ -45,10 +45,9 @@ module Rigor
|
|
|
45
45
|
|
|
46
46
|
module_function
|
|
47
47
|
|
|
48
|
-
# @param collection
|
|
49
|
-
# @param discharge
|
|
48
|
+
# @param collection — the run's merged per-file collections
|
|
49
|
+
# @param discharge — the `effects.tolerated:` policy the undischarged lane is computed
|
|
50
50
|
# under; {Discharge.none} makes the two lanes equal.
|
|
51
|
-
# @return [EffectTable]
|
|
52
51
|
def propagate(collection, discharge: Discharge.none)
|
|
53
52
|
return EffectTable.empty if collection.summaries.empty?
|
|
54
53
|
|
|
@@ -184,7 +184,7 @@ module Rigor
|
|
|
184
184
|
# per unit (ADR-103 WD13): a unit the scanner cannot finish is recorded as non-exhaustive with
|
|
185
185
|
# `collector-error` and its siblings are unaffected.
|
|
186
186
|
#
|
|
187
|
-
# @return
|
|
187
|
+
# @return the finished scan, or nil when the unit failed soft
|
|
188
188
|
def add_unit(class_name, method_name, singleton, body, parameters)
|
|
189
189
|
key = "#{class_name}#{singleton ? '.' : '#'}#{method_name}"
|
|
190
190
|
names = parameter_names(parameters)
|
|
@@ -52,9 +52,8 @@ module Rigor
|
|
|
52
52
|
stripped.start_with?(root) ? stripped[root.length..] : stripped
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
# @param signature_paths
|
|
56
|
-
# @param virtual_rbs
|
|
57
|
-
# @return [Array<Array(String, String)>]
|
|
55
|
+
# @param signature_paths — the configured roots; empty / nil takes the default.
|
|
56
|
+
# @param virtual_rbs — the loader's virtual entries.
|
|
58
57
|
def collect(signature_paths:, virtual_rbs: nil)
|
|
59
58
|
files = roots(signature_paths).flat_map { |root| Dir.glob(File.join(root.to_s, "**", "*.rbs")).sort }
|
|
60
59
|
sources = files.filter_map do |path|
|
|
@@ -76,8 +75,7 @@ module Rigor
|
|
|
76
75
|
# residual reports one annotation per run, so the carrier a run holds past the environment's own
|
|
77
76
|
# lifetime is a single synthesized buffer rather than the project's whole virtual tree.
|
|
78
77
|
#
|
|
79
|
-
# @
|
|
80
|
-
# @return [Array<Array(String, String)>] zero or one pair.
|
|
78
|
+
# @return zero or one pair.
|
|
81
79
|
def annotated_carrier(virtual_rbs)
|
|
82
80
|
Array(virtual_rbs).each do |name, content|
|
|
83
81
|
return [[name.to_s, content.to_s]] if ANNOTATION_HINT.match?(content)
|
|
@@ -117,12 +117,12 @@ module Rigor
|
|
|
117
117
|
class << self
|
|
118
118
|
# Builds the snapshot a run's effect table describes.
|
|
119
119
|
#
|
|
120
|
-
# @param table
|
|
121
|
-
# @param configuration
|
|
122
|
-
# @param sources
|
|
123
|
-
# @param full
|
|
124
|
-
# @param registry
|
|
125
|
-
# @param project_root
|
|
120
|
+
# @param table — the propagated graph
|
|
121
|
+
# @param configuration — supplies the reach globs and the digested block
|
|
122
|
+
# @param sources — `Runner#effect_sources` — where each unit is defined
|
|
123
|
+
# @param full — keep the rows {#omit?} drops
|
|
124
|
+
# @param registry — the vocabulary whose version the header carries
|
|
125
|
+
# @param project_root — what `sources` paths are relativised against for glob matching
|
|
126
126
|
def build(table:, configuration:, sources: {}, full: false, registry: Registry.default,
|
|
127
127
|
project_root: Dir.pwd)
|
|
128
128
|
globs = expand_reach(configuration.effects_snapshot_reach)
|
|
@@ -146,10 +146,10 @@ module Rigor
|
|
|
146
146
|
# It is built beside the snapshot and never inside it: the file stays flat and undischarged, and
|
|
147
147
|
# this index exists only for the duration of one comparison ({SnapshotDiff}).
|
|
148
148
|
#
|
|
149
|
-
# @param snapshot
|
|
150
|
-
# @param table
|
|
151
|
-
# @param discharge
|
|
152
|
-
# @return
|
|
149
|
+
# @param snapshot — the current side, already built
|
|
150
|
+
# @param table — the run that produced it
|
|
151
|
+
# @param discharge — the policy
|
|
152
|
+
# @return nil when the policy discharges nothing, which is the common case
|
|
153
153
|
def undischarged_index(snapshot:, table:, discharge:)
|
|
154
154
|
return nil if discharge.inert?
|
|
155
155
|
|
|
@@ -86,7 +86,7 @@ module Rigor
|
|
|
86
86
|
# Compares `current` against `recorded`. `recorded` may be nil — no snapshot on disk at all, which
|
|
87
87
|
# is drift with a routed message rather than an error.
|
|
88
88
|
#
|
|
89
|
-
# @param undischarged
|
|
89
|
+
# @param undischarged — `{table name => {symbol => [label]}}` — the current side's labels that
|
|
90
90
|
# survive per-origin discharge ({Snapshot.undischarged_index}). Optional; omitting it judges every
|
|
91
91
|
# event by label.
|
|
92
92
|
def self.compare(recorded:, current:, tolerated: [], gate: :symmetric, strict_tolerated: false,
|
|
@@ -87,7 +87,7 @@ module Rigor
|
|
|
87
87
|
# the enclosing method contains. A non-literal name has no key to file the block under, so it stays
|
|
88
88
|
# contained in the enclosing method and this returns nil.
|
|
89
89
|
#
|
|
90
|
-
# @return
|
|
90
|
+
# @return `[name, singleton, body, parameters]`
|
|
91
91
|
def self.define_method_unit(node)
|
|
92
92
|
return nil unless node.name == :define_method && node.receiver.nil?
|
|
93
93
|
|
|
@@ -100,17 +100,17 @@ module Rigor
|
|
|
100
100
|
[first.unescaped, false, block.body, block.parameters]
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
-
# @param singleton
|
|
103
|
+
# @param singleton — whether the unit's `self` is the class object (`def self.x`,
|
|
104
104
|
# `class << self`) — the axis that separates `mutate.self` from `mutate.static` on an ivar write
|
|
105
|
-
# @param block_parameter
|
|
105
|
+
# @param block_parameter — the unit's `&blk` parameter name, if any; a call on it is
|
|
106
106
|
# forwarding, not an opaque callable
|
|
107
|
-
# @param calls
|
|
108
|
-
# @param attribution
|
|
109
|
-
# @param envelopes
|
|
110
|
-
# @param plugin_facts
|
|
111
|
-
# @param owner_class
|
|
107
|
+
# @param calls — node-identity table of {Collector::CallRecord}s
|
|
108
|
+
# @param attribution — the project's `effects.attribution:` table
|
|
109
|
+
# @param envelopes — the envelopes a call site may import as a `≤` bound (#386)
|
|
110
|
+
# @param plugin_facts — the loaded plugins' `effect_attributions:` (#387)
|
|
111
|
+
# @param owner_class — the class this unit is defined on — the carrier an
|
|
112
112
|
# implicit-self call's envelope is looked up under, since the syntax spells `Kernel#name`
|
|
113
|
-
# @param method_name
|
|
113
|
+
# @param method_name — this unit's own selector — what a `super` in its body names as
|
|
114
114
|
# the target the propagator resolves above `owner_class` (#446). With no name to state, a `super`
|
|
115
115
|
# taints instead.
|
|
116
116
|
def initialize(singleton:, parameters:, block_parameter:, owned_locals:, calls:, # rubocop:disable Metrics/ParameterLists
|
|
@@ -283,7 +283,7 @@ module Rigor
|
|
|
283
283
|
# Three receiver shapes, tried nearest-syntax first — a written receiver path (`Rails.cache.read`),
|
|
284
284
|
# a receiver rooted at implicit self inside a framework class (`session[:x] = 1`), and the receiver's
|
|
285
285
|
# class through the project's inheritance chain (`user.save`).
|
|
286
|
-
# @return
|
|
286
|
+
# @return the row that claimed this call, for {#visit_call} to read as a bound
|
|
287
287
|
def attribute_plugin(node, record)
|
|
288
288
|
return nil unless @plugin_facts.attributions?
|
|
289
289
|
|
|
@@ -30,10 +30,6 @@ module Rigor
|
|
|
30
30
|
|
|
31
31
|
module_function
|
|
32
32
|
|
|
33
|
-
# @param method_envelopes [Hash{String => Envelope}]
|
|
34
|
-
# @param class_envelopes [Hash{String => Envelope}]
|
|
35
|
-
# @param registry [Rigor::Effects::Registry]
|
|
36
|
-
# @return [Array<Finding>]
|
|
37
33
|
def for_envelopes(method_envelopes:, class_envelopes:, registry:)
|
|
38
34
|
seen = {}
|
|
39
35
|
declarations(method_envelopes, class_envelopes).each do |envelope|
|
|
@@ -49,10 +45,9 @@ module Rigor
|
|
|
49
45
|
seen.values
|
|
50
46
|
end
|
|
51
47
|
|
|
52
|
-
# @param labels
|
|
53
|
-
# @param key_path
|
|
54
|
-
# @param consequence
|
|
55
|
-
# @return [Array<Finding>]
|
|
48
|
+
# @param labels — the list as written, in source order.
|
|
49
|
+
# @param key_path — the `.rigor.yml` key, for the message (`effects.tolerated`).
|
|
50
|
+
# @param consequence — what the degradation cost, for the message.
|
|
56
51
|
def for_config(labels:, key_path:, consequence:, registry:)
|
|
57
52
|
tokens = Array(labels).map(&:to_s)
|
|
58
53
|
tokens.filter_map do |token|
|