rigortype 0.3.8 → 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 +72 -1
- 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 +32 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +11 -0
- 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 +8 -24
- 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 +81 -22
- 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 +177 -26
- 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 +256 -88
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +133 -35
- data/lib/rigor/analysis/worker_session.rb +9 -10
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +155 -21
- 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 +2 -4
- 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_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +77 -32
- 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 +2 -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 +2 -2
- 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 +388 -59
- data/lib/rigor/environment.rb +105 -20
- 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 +4 -7
- 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 +42 -12
- 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 +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +61 -21
- 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 +115 -22
- 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 +4 -5
- 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 +5 -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 +26 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -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|
|
|
@@ -17,10 +17,9 @@ module Rigor
|
|
|
17
17
|
# {.for} answers `nil` unless {LabelIntent} says the spelling is evidently a label — the whole
|
|
18
18
|
# point of the diagnostic is that it fires where intent is evident and nowhere else.
|
|
19
19
|
class UnknownLabelReport < Data.define(:token, :suggestion, :retirement)
|
|
20
|
-
# @param token
|
|
21
|
-
# @param registry
|
|
22
|
-
# @param siblings
|
|
23
|
-
# @return [UnknownLabelReport, nil]
|
|
20
|
+
# @param token — the spelling as written.
|
|
21
|
+
# @param registry — the vocabulary after plugin load.
|
|
22
|
+
# @param siblings — the other tokens written alongside it.
|
|
24
23
|
def self.for(token:, registry:, siblings: [])
|
|
25
24
|
return nil unless LabelIntent.evident?(token, registry, siblings: siblings)
|
|
26
25
|
|
|
@@ -46,14 +46,14 @@ module Rigor
|
|
|
46
46
|
"bundler", "rubygems"
|
|
47
47
|
].freeze
|
|
48
48
|
|
|
49
|
-
# @param bundle_path
|
|
49
|
+
# @param bundle_path — explicit path to the bundler install root. When `nil`,
|
|
50
50
|
# falls back to `auto_detect` if `auto_detect:` is true.
|
|
51
|
-
# @param project_root
|
|
52
|
-
# @param auto_detect
|
|
51
|
+
# @param project_root — resolution base for relative `bundle_path:` and the auto-detect search.
|
|
52
|
+
# @param auto_detect — when true and `bundle_path:` is nil, try `.bundle/config`'s
|
|
53
53
|
# `BUNDLE_PATH:` and `vendor/bundle/` under `project_root`.
|
|
54
|
-
# @param skip_gems
|
|
54
|
+
# @param skip_gems — gem names to exclude from discovery. Defaults to
|
|
55
55
|
# {SKIPPED_GEMS_BY_DEFAULT}.
|
|
56
|
-
# @param locked_gems
|
|
56
|
+
# @param locked_gems — Optional O4-Layer-3 filter.
|
|
57
57
|
# When non-nil and non-empty, only `sig/` directories whose gem `(name, version, platform)` tuple
|
|
58
58
|
# matches a lockfile entry are returned. Bundle entries absent from the lockfile (or at a drifted
|
|
59
59
|
# version) are silently dropped — the lockfile is treated as the source of truth for "what gems this
|
|
@@ -63,7 +63,7 @@ module Rigor
|
|
|
63
63
|
# source keeps its stale `bundler/gems/` directory under the SAME name until a `bundle clean`. Pass
|
|
64
64
|
# `nil` (the default) to keep the pre-Layer-3 behaviour of returning every non-skipped `sig/` under
|
|
65
65
|
# the bundle.
|
|
66
|
-
# @return
|
|
66
|
+
# @return every `<gem-dir>/sig` directory under the resolved bundle path, minus any
|
|
67
67
|
# whose gem name is in `skip_gems` and (when `locked_gems` is supplied) minus any whose `(name,
|
|
68
68
|
# version, platform)` does not match a lockfile entry.
|
|
69
69
|
def self.discover(bundle_path:, project_root: Dir.pwd, auto_detect: true,
|
|
@@ -136,8 +136,10 @@ module Rigor
|
|
|
136
136
|
|
|
137
137
|
private
|
|
138
138
|
|
|
139
|
+
# Allocates only when there is a prefix to strip (see `Environment#normalize_class_name`).
|
|
139
140
|
def normalize_name(name)
|
|
140
|
-
name.to_s
|
|
141
|
+
name = name.to_s
|
|
142
|
+
name.start_with?("::") ? name.delete_prefix("::") : name
|
|
141
143
|
end
|
|
142
144
|
end
|
|
143
145
|
end
|
|
@@ -16,12 +16,12 @@ module Rigor
|
|
|
16
16
|
@value = nil
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
# @param value
|
|
19
|
+
# @param value — a Marshal-clean tuple — the fork pool ships it back from the worker.
|
|
20
20
|
def record(value)
|
|
21
21
|
@value = value.freeze if @value.nil?
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
# @return
|
|
24
|
+
# @return the recorded tuple, or nil when the build never failed.
|
|
25
25
|
attr_reader :value
|
|
26
26
|
end
|
|
27
27
|
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lockfile_resolver"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
class Environment
|
|
7
|
+
# Issue #530 — the gem set for a project that has NO `Gemfile.lock`.
|
|
8
|
+
#
|
|
9
|
+
# ADR-82 WD9's constant-ownership index is driven by the LOCKED gem set, so a project without a
|
|
10
|
+
# lockfile (a plain library, a `gemspec`-only gem, an app vendored outside Bundler) handed the index
|
|
11
|
+
# nothing at all: every constant reaching into a real, installed, RBS-less gem recorded the generic
|
|
12
|
+
# cause, and the entire gem boundary reported as `engine_gap` — "report this to Rigor" — where the
|
|
13
|
+
# honest story is `add_rbs`. Measured on slim, whose whole Temple boundary lands that way while haml,
|
|
14
|
+
# which has a lockfile, attributes the same gem correctly.
|
|
15
|
+
#
|
|
16
|
+
# Degrading to the gems that are actually INSTALLED keeps ADR-82's honesty criterion intact. A name
|
|
17
|
+
# here is a gem that exists on disk; the index still has to READ its entry file before claiming any
|
|
18
|
+
# constant, and its lack of RBS is established by the same {RbsCoverageReport.classify} pass the
|
|
19
|
+
# lock-driven path uses. The version is the one on disk rather than a declared one — which is the
|
|
20
|
+
# version a lockfile-less project would load anyway.
|
|
21
|
+
#
|
|
22
|
+
# Only the missing-gem provenance index consumes this. The ADR-72 gem overlays stay Gemfile.lock-gated:
|
|
23
|
+
# an overlay changes what type-checks, so it needs the project's own declaration, while this
|
|
24
|
+
# side-channel only changes a label.
|
|
25
|
+
module InstalledGemSet
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
# @param bundle_path — the target's resolved bundler install root, or nil.
|
|
29
|
+
# @return frozen `gem name => LockfileResolver::LockedGem`, the shape
|
|
30
|
+
# {RbsCoverageReport.classify} consumes. Empty when neither resolver sees anything, which leaves
|
|
31
|
+
# today's generic cause in place.
|
|
32
|
+
def gems(bundle_path: nil)
|
|
33
|
+
found = from_bundle_tree(bundle_path)
|
|
34
|
+
found = from_installed_specs if found.empty?
|
|
35
|
+
found.freeze
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# `<bundle>/ruby/X.Y.Z/gems/<name>-<version>/` — the same pure-filesystem layout
|
|
39
|
+
# {MissingGemConstantIndex.bundle_gem_dirs} walks, preferred over the host's gems so a project that
|
|
40
|
+
# vendored its bundle without committing the lockfile is read from its own tree. A platform-tagged
|
|
41
|
+
# directory (`ffi-1.17.4-aarch64-linux-gnu`) does not split into a name/version pair and is skipped:
|
|
42
|
+
# those gems ship native code, not the pure-Ruby constants this feeds.
|
|
43
|
+
def from_bundle_tree(bundle_path)
|
|
44
|
+
return {} if bundle_path.nil?
|
|
45
|
+
|
|
46
|
+
base = Pathname.new(bundle_path)
|
|
47
|
+
return {} unless base.directory?
|
|
48
|
+
|
|
49
|
+
Dir.glob(base.join("ruby", "*", "gems", "*")).each_with_object({}) do |dir, acc|
|
|
50
|
+
name, version = split_name_version(File.basename(dir))
|
|
51
|
+
next unless name
|
|
52
|
+
|
|
53
|
+
acc[name] ||= locked_gem(name, version)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The gems the RUNNING Ruby can load. For the shipped `gem install rigortype` distribution that IS a
|
|
58
|
+
# lockfile-less project's gem environment — the same set its own `require` would reach. Under
|
|
59
|
+
# `bundle exec` it is the analyzer's own bundle instead (the `Gem.paths`-no-ops-under-Bundler
|
|
60
|
+
# mechanism ADR-90 documents), which yields fewer names, never wrong ones. Highest version per name,
|
|
61
|
+
# because `stubs` promises no ordering.
|
|
62
|
+
def from_installed_specs
|
|
63
|
+
Gem::Specification.stubs.each_with_object({}) do |stub, acc|
|
|
64
|
+
previous = acc[stub.name]
|
|
65
|
+
next if previous && Gem::Version.new(previous.version) >= stub.version
|
|
66
|
+
|
|
67
|
+
acc[stub.name] = locked_gem(stub.name, stub.version.to_s)
|
|
68
|
+
end
|
|
69
|
+
rescue StandardError
|
|
70
|
+
{}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def locked_gem(name, version)
|
|
74
|
+
LockfileResolver::LockedGem.new(name: name, version: version, platform: "ruby")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Splits a gem install directory's basename. The version tail is anchored on a leading digit and must
|
|
78
|
+
# carry no further hyphen, which is what excludes the platform-tagged variants above.
|
|
79
|
+
def split_name_version(basename)
|
|
80
|
+
match = /\A(.+)-(\d[^-]*)\z/.match(basename)
|
|
81
|
+
match ? [match[1], match[2]] : nil
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -39,13 +39,13 @@ module Rigor
|
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
# @param lockfile_path
|
|
42
|
+
# @param lockfile_path — explicit path to the Gemfile.lock. When `nil`, falls
|
|
43
43
|
# back to `auto_detect` if `auto_detect:` is true.
|
|
44
|
-
# @param project_root
|
|
44
|
+
# @param project_root — resolution base for a relative `lockfile_path:` and the auto-detect
|
|
45
45
|
# search.
|
|
46
|
-
# @param auto_detect
|
|
46
|
+
# @param auto_detect — when true and `lockfile_path:` is nil, look for
|
|
47
47
|
# `<project_root>/Gemfile.lock`.
|
|
48
|
-
# @return
|
|
48
|
+
# @return frozen map of gem name → locked entry. Returns the empty frozen
|
|
49
49
|
# hash when no lockfile is resolvable, when the file is unreadable, or when Bundler refuses to parse
|
|
50
50
|
# it.
|
|
51
51
|
def self.locked_gems(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
|
|
@@ -59,6 +59,53 @@ module Rigor
|
|
|
59
59
|
parse(resolved)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# The gems the project declares for itself — the lockfile's `DEPENDENCIES` section, which is what a
|
|
63
|
+
# `Gemfile` actually asked for, as opposed to {locked_gems}'s full resolved graph. A consumer that
|
|
64
|
+
# advises the user about their own stack has to read this one: `minitest`, `i18n`, `activesupport` and
|
|
65
|
+
# `ffi` sit in nearly every Rails lock as somebody else's dependency, so matching against the resolved
|
|
66
|
+
# graph advises about gems the project never chose.
|
|
67
|
+
#
|
|
68
|
+
# @param lockfile_path — explicit path to the Gemfile.lock; `nil` falls back to `auto_detect`.
|
|
69
|
+
# @param project_root — resolution base for a relative `lockfile_path:` and the auto-detect search.
|
|
70
|
+
# @param auto_detect — when true and `lockfile_path:` is nil, look for `<project_root>/Gemfile.lock`.
|
|
71
|
+
# @return frozen set of gem names. Empty when no lockfile is resolvable, when it is unreadable, when
|
|
72
|
+
# Bundler refuses to parse it, or when the lockfile has no `DEPENDENCIES` section at all.
|
|
73
|
+
def self.direct_dependency_names(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
|
|
74
|
+
resolved = resolve_lockfile_path(
|
|
75
|
+
lockfile_path: lockfile_path,
|
|
76
|
+
project_root: project_root,
|
|
77
|
+
auto_detect: auto_detect
|
|
78
|
+
)
|
|
79
|
+
return EMPTY_NAMES unless resolved
|
|
80
|
+
|
|
81
|
+
parse_dependency_names(resolved)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
EMPTY_NAMES = Set.new.freeze
|
|
85
|
+
private_constant :EMPTY_NAMES
|
|
86
|
+
|
|
87
|
+
def self.parse_dependency_names(path)
|
|
88
|
+
require "bundler"
|
|
89
|
+
rescue LoadError => e
|
|
90
|
+
warn "rigor: cannot read #{path}: bundler is not available (#{e.message})"
|
|
91
|
+
EMPTY_NAMES
|
|
92
|
+
else
|
|
93
|
+
do_parse_dependency_names(path)
|
|
94
|
+
end
|
|
95
|
+
private_class_method :parse_dependency_names
|
|
96
|
+
|
|
97
|
+
def self.do_parse_dependency_names(path)
|
|
98
|
+
parser = Bundler::LockfileParser.new(File.read(path.to_s))
|
|
99
|
+
# `#dependencies` is the parsed `DEPENDENCIES` section keyed by gem name, so the version constraint
|
|
100
|
+
# and the pinned-source `!` suffix are already stripped for us. The upstream bundler RBS shim does
|
|
101
|
+
# not declare it.
|
|
102
|
+
parser.dependencies.keys.to_set { |name| -name.to_s }.freeze # rigor:disable undefined-method
|
|
103
|
+
rescue StandardError => e
|
|
104
|
+
warn "rigor: ignoring malformed #{path} (#{e.class}: #{e.message})"
|
|
105
|
+
EMPTY_NAMES
|
|
106
|
+
end
|
|
107
|
+
private_class_method :do_parse_dependency_names
|
|
108
|
+
|
|
62
109
|
# Returns the resolved lockfile path (`Pathname`) or `nil` when neither explicit nor auto-detect
|
|
63
110
|
# produces one. Public so the stats banner can show what rigor picked up.
|
|
64
111
|
def self.resolve_lockfile_path(lockfile_path:, project_root: Dir.pwd, auto_detect: true)
|
|
@@ -37,12 +37,12 @@ module Rigor
|
|
|
37
37
|
module MissingGemConstantIndex
|
|
38
38
|
module_function
|
|
39
39
|
|
|
40
|
-
# @param gems
|
|
40
|
+
# @param gems — `[gem_name, version]` pairs (the `:missing` rows of
|
|
41
41
|
# {RbsCoverageReport}).
|
|
42
|
-
# @param bundle_path
|
|
43
|
-
# @param spec_resolver
|
|
42
|
+
# @param bundle_path — the target's resolved bundler install root, or nil.
|
|
43
|
+
# @param spec_resolver — `(name, version) -> String?` fallback dir resolver. Injectable for
|
|
44
44
|
# specs; the default is the RubyGems-metadata lookup (no code load).
|
|
45
|
-
# @return
|
|
45
|
+
# @return frozen `root constant name => gem name`. On a collision (two gems
|
|
46
46
|
# declaring the same top-level constant) the first gem wins — the CAUSE recorded downstream
|
|
47
47
|
# (external gem without RBS) is true under either owner.
|
|
48
48
|
def build(gems, bundle_path: nil, spec_resolver: method(:installed_gem_dir))
|
|
@@ -37,17 +37,17 @@ module Rigor
|
|
|
37
37
|
DEFAULT_COLLECTION_PATH = ".gem_rbs_collection"
|
|
38
38
|
private_constant :DEFAULT_COLLECTION_PATH
|
|
39
39
|
|
|
40
|
-
# @param lockfile_path
|
|
40
|
+
# @param lockfile_path — explicit path to `rbs_collection.lock.yaml`. When `nil`,
|
|
41
41
|
# falls back to `auto_detect` if `auto_detect:` is true.
|
|
42
|
-
# @param project_root
|
|
42
|
+
# @param project_root — resolution base for relative `lockfile_path:` and the auto-detect
|
|
43
43
|
# search.
|
|
44
|
-
# @param auto_detect
|
|
44
|
+
# @param auto_detect — when true and `lockfile_path:` is nil, look for
|
|
45
45
|
# `<project_root>/rbs_collection.lock.yaml`.
|
|
46
|
-
# @param skip_gem_names
|
|
46
|
+
# @param skip_gem_names — gem names rigor already loads from its bundled
|
|
47
47
|
# stdlib (the merged `DEFAULT_LIBRARIES + libraries:` set). Entries whose `name` is in this set are
|
|
48
48
|
# dropped regardless of `source.type` to avoid `RBS::DuplicatedDeclarationError` on
|
|
49
49
|
# stdlib-extracted default gems. Defaults to empty.
|
|
50
|
-
# @return
|
|
50
|
+
# @return every `<collection_path>/<gem-name>/<gem-version>/` directory listed in the
|
|
51
51
|
# lockfile whose entry has a non-skipped source type, whose `name` is not in `skip_gem_names:`, and
|
|
52
52
|
# whose directory exists on disk. Returns `[]` when no lockfile is resolvable, when the YAML is
|
|
53
53
|
# unreadable, or when the collection path doesn't exist.
|
|
@@ -35,16 +35,16 @@ module Rigor
|
|
|
35
35
|
"idn-ruby", "mysql2", "nokogiri", "pg", "prism", "racc", "redis", "rubygems"
|
|
36
36
|
].freeze
|
|
37
37
|
|
|
38
|
-
# @param locked_gems
|
|
38
|
+
# @param locked_gems — The lockfile-resolved gem set. Empty
|
|
39
39
|
# hash → no coverage analysis to do.
|
|
40
|
-
# @param default_libraries
|
|
40
|
+
# @param default_libraries — gem names rigor auto-loads through
|
|
41
41
|
# `RBS::EnvironmentLoader#add(library:)`. Pass `Rigor::Environment::DEFAULT_LIBRARIES` from callers
|
|
42
42
|
# running in a project context.
|
|
43
|
-
# @param bundle_sig_paths
|
|
43
|
+
# @param bundle_sig_paths — the discovered `<bundle>/.../gems/<name>-<ver>/sig`
|
|
44
44
|
# paths from {BundleSigDiscovery.discover}.
|
|
45
|
-
# @param rbs_collection_paths
|
|
45
|
+
# @param rbs_collection_paths — the discovered `<collection>/<name>/<version>/`
|
|
46
46
|
# paths from {RbsCollectionDiscovery.discover}.
|
|
47
|
-
# @return
|
|
47
|
+
# @return one row per locked gem; sorted by gem name for deterministic output.
|
|
48
48
|
def self.classify(locked_gems:, default_libraries:,
|
|
49
49
|
bundle_sig_paths:, rbs_collection_paths:)
|
|
50
50
|
default_set = default_libraries.to_set
|
|
@@ -63,8 +63,10 @@ module Rigor
|
|
|
63
63
|
@ancestor_names_cache[key] = loader.ancestor_names_for(key)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
# Allocates only when there is a prefix to strip (see `Environment#normalize_class_name`).
|
|
66
67
|
def normalize_name(name)
|
|
67
|
-
name.to_s
|
|
68
|
+
name = name.to_s
|
|
69
|
+
name.start_with?("::") ? name.delete_prefix("::") : name
|
|
68
70
|
end
|
|
69
71
|
end
|
|
70
72
|
end
|