rigortype 0.3.8 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/core_overlay/enumerable.rbs +51 -0
- data/data/core_overlay/enumerator.rbs +84 -0
- data/data/core_overlay/hash_rbs3.rbs +41 -0
- data/data/core_overlay/process.rbs +40 -0
- data/data/core_overlay/string_io.rbs +33 -0
- data/data/effects/core.yml +3 -3
- data/data/gem_overlay/activesupport/core_ext.rbs +236 -17
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +132 -14
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +19 -1
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +525 -13
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +95 -6
- data/docs/manual/03-configuration.md +18 -0
- data/docs/manual/04-diagnostics.md +15 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/10-mcp-server.md +3 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +46 -8
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/19-effect-labels.md +10 -0
- data/docs/manual/plugins/README.md +7 -0
- data/docs/manual/plugins/rigor-actioncable.md +8 -1
- data/docs/manual/plugins/rigor-actionmailer.md +7 -0
- data/docs/manual/plugins/rigor-actionpack.md +261 -0
- data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
- data/docs/manual/plugins/rigor-activejob.md +7 -0
- data/docs/manual/plugins/rigor-activerecord.md +138 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +43 -7
- data/docs/manual/plugins/rigor-grape.md +106 -0
- data/docs/manual/plugins/rigor-graphql.md +23 -2
- data/docs/manual/plugins/rigor-pundit.md +8 -1
- data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
- data/docs/manual/plugins/rigor-rails-routes.md +7 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +229 -1
- data/docs/manual/plugins/rigor-sidekiq.md +8 -1
- data/docs/manual/plugins/rigor-sorbet.md +21 -5
- data/exe/rigor +19 -4
- data/lib/rigor/analysis/baseline.rb +3 -3
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +6 -4
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +21 -6
- data/lib/rigor/analysis/check_rules/rule_walk.rb +41 -5
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +38 -4
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +718 -79
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_recorder.rb +23 -0
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +15 -1
- data/lib/rigor/analysis/incremental.rb +42 -0
- data/lib/rigor/analysis/incremental_session.rb +117 -26
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +5 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +3 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +131 -1
- data/lib/rigor/analysis/run_cache_key.rb +21 -12
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +340 -33
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +18 -14
- data/lib/rigor/analysis/runner/pool_coordinator.rb +357 -97
- data/lib/rigor/analysis/runner/project_pre_passes.rb +38 -13
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +407 -51
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +91 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +399 -0
- data/lib/rigor/analysis/worker_session.rb +62 -20
- data/lib/rigor/bleeding_edge.rb +10 -24
- data/lib/rigor/builtins/hkt_builtins.rb +3 -2
- data/lib/rigor/builtins/imported_refinements.rb +159 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +21 -13
- data/lib/rigor/builtins/static_return_refinements.rb +7 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +63 -20
- data/lib/rigor/cache/engine_source.rb +33 -5
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +72 -6
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +112 -14
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +76 -32
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +4 -4
- data/lib/rigor/cli/docs_command.rb +6 -6
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +4 -4
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +1 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +212 -31
- data/lib/rigor/cli/skill_command.rb +2 -2
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +35 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +29 -11
- data/lib/rigor/cli/type_of_renderer.rb +20 -9
- data/lib/rigor/cli/type_of_template_probe.rb +189 -0
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +89 -9
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +25 -9
- data/lib/rigor/configuration.rb +109 -7
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +12 -5
- data/lib/rigor/effects/callee_rule.rb +368 -0
- data/lib/rigor/effects/catalog.rb +7 -4
- data/lib/rigor/effects/collector.rb +11 -5
- data/lib/rigor/effects/config_envelopes.rb +15 -10
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +11 -4
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +9 -9
- data/lib/rigor/effects/envelope_index.rb +20 -8
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +5 -7
- data/lib/rigor/effects/identity.rb +20 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +22 -1
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +47 -37
- data/lib/rigor/effects/propagator.rb +297 -19
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +122 -73
- data/lib/rigor/effects/signature_sources.rb +4 -6
- data/lib/rigor/effects/snapshot.rb +12 -11
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +393 -38
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/effects/visibility.rb +101 -0
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +2 -2
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +68 -4
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +674 -79
- data/lib/rigor/environment.rb +174 -24
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +292 -17
- data/lib/rigor/inference/block_auto_splat.rb +216 -0
- data/lib/rigor/inference/block_call_timing.rb +338 -0
- data/lib/rigor/inference/block_parameter_binder.rb +75 -30
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +7 -6
- data/lib/rigor/inference/budget_trace.rb +3 -6
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +389 -18
- data/lib/rigor/inference/closure_escape_analyzer.rb +159 -17
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/def_return_typer.rb +11 -7
- data/lib/rigor/inference/define_method_block_self.rb +64 -0
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +180 -0
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +2124 -468
- data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
- data/lib/rigor/inference/global_write_census.rb +239 -0
- data/lib/rigor/inference/guard_rebinding.rb +447 -0
- data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +4 -7
- data/lib/rigor/inference/index_write_widening.rb +16 -7
- data/lib/rigor/inference/indexed_narrowing.rb +61 -9
- data/lib/rigor/inference/jump_targets.rb +82 -0
- data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
- data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
- data/lib/rigor/inference/last_line.rb +340 -0
- data/lib/rigor/inference/last_status.rb +144 -0
- data/lib/rigor/inference/macro_block_self_type.rb +168 -21
- data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
- data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
- data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
- data/lib/rigor/inference/match_rebinding.rb +392 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +87 -33
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +165 -111
- data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +1046 -69
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +129 -54
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -6
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +225 -25
- data/lib/rigor/inference/method_parameter_binder.rb +12 -9
- data/lib/rigor/inference/multi_target_binder.rb +341 -55
- data/lib/rigor/inference/mutation_rejoin.rb +171 -0
- data/lib/rigor/inference/mutation_widening.rb +156 -85
- data/lib/rigor/inference/narrowing.rb +835 -162
- data/lib/rigor/inference/operand_effects.rb +167 -0
- data/lib/rigor/inference/operand_walk.rb +88 -0
- data/lib/rigor/inference/optimistic_origin.rb +153 -16
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +7 -7
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +9 -4
- data/lib/rigor/inference/project_patched_scanner.rb +10 -6
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +93 -4
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +76 -0
- data/lib/rigor/inference/repeated_or_writes.rb +463 -0
- data/lib/rigor/inference/return_barrier.rb +54 -0
- data/lib/rigor/inference/rewrite_mutation.rb +120 -0
- data/lib/rigor/inference/scope_indexer.rb +5228 -627
- data/lib/rigor/inference/statement_evaluator.rb +3452 -595
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +97 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +7 -6
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +4 -4
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +7 -8
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +258 -26
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +65 -11
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +33 -13
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +94 -11
- data/lib/rigor/plugin/registry.rb +76 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +21 -4
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +14 -12
- data/lib/rigor/protection/kill_signature.rb +1 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +7 -11
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +88 -21
- data/lib/rigor/reflection/constant_ancestors.rb +97 -0
- data/lib/rigor/reflection/constant_path.rb +139 -0
- data/lib/rigor/reflection.rb +93 -110
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +165 -2
- data/lib/rigor/scope.rb +1041 -32
- data/lib/rigor/sig_gen/alias_index.rb +289 -0
- data/lib/rigor/sig_gen/classification.rb +22 -5
- data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
- data/lib/rigor/sig_gen/generator.rb +673 -53
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +18 -16
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +128 -14
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +154 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +189 -41
- data/lib/rigor/sig_gen.rb +3 -0
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +0 -6
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +144 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +6 -6
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +4 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +10 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +14 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +32 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +264 -3
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
- data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
- data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +2 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +10 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +383 -7
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +322 -41
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +26 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +290 -19
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +3 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +2 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
- data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
- data/plugins/rigor-grape/sig/grape.rbs +263 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +78 -5
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +12 -7
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +36 -37
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +5 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +3 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +10 -17
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +324 -18
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +2 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +10 -5
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +51 -6
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/baseline.rbs +69 -7
- data/sig/rigor/analysis/fact_store.rbs +1 -1
- data/sig/rigor/analysis/project_scan.rbs +74 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/effects/config_envelopes.rbs +100 -0
- data/sig/rigor/effects/effect_table.rbs +60 -0
- data/sig/rigor/effects/envelope.rbs +97 -0
- data/sig/rigor/effects/envelope_index.rbs +33 -0
- data/sig/rigor/effects/file_collection.rbs +81 -0
- data/sig/rigor/effects/label.rbs +26 -0
- data/sig/rigor/effects/label_set.rbs +46 -0
- data/sig/rigor/effects/method_key.rbs +24 -0
- data/sig/rigor/effects/origin.rbs +50 -0
- data/sig/rigor/effects/plugin_facts.rbs +141 -0
- data/sig/rigor/effects/registry.rbs +68 -0
- data/sig/rigor/effects/summary.rbs +50 -0
- data/sig/rigor/effects/taint_cause.rbs +12 -0
- data/sig/rigor/environment.rbs +17 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +9 -14
- data/sig/rigor/plugin/additional_initializer.rbs +36 -0
- data/sig/rigor/plugin/base.rbs +30 -7
- data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
- data/sig/rigor/plugin/effect_attribution.rbs +66 -0
- data/sig/rigor/plugin/effect_edge.rbs +33 -0
- data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +51 -11
- data/sig/rigor/plugin/protocol_contract.rbs +68 -0
- data/sig/rigor/plugin/registry.rbs +62 -1
- data/sig/rigor/plugin.rbs +1 -1
- data/sig/rigor/rbs_extended.rbs +1 -1
- data/sig/rigor/reflection.rbs +9 -6
- data/sig/rigor/scope.rbs +148 -12
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +21 -0
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +33 -1
- data/sig/rigor.rbs +39 -20
- data/skills/rigor-ask/SKILL.md +16 -11
- data/skills/rigor-baseline-reduce/SKILL.md +4 -6
- data/skills/rigor-ci-setup/SKILL.md +17 -21
- data/skills/rigor-doctor/SKILL.md +24 -22
- data/skills/rigor-doctor/references/01-checks.md +97 -33
- data/skills/rigor-editor-setup/SKILL.md +6 -4
- data/skills/rigor-mcp-setup/SKILL.md +5 -4
- data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
- data/skills/rigor-next-steps/SKILL.md +4 -2
- data/skills/rigor-plugin-author/SKILL.md +19 -23
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +25 -27
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
- data/skills/rigor-plugin-review/SKILL.md +6 -4
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
- data/skills/rigor-plugin-tune/SKILL.md +4 -2
- data/skills/rigor-project-init/SKILL.md +15 -10
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +43 -14
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
- data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-protection-uplift/SKILL.md +4 -6
- data/skills/rigor-rbs-setup/SKILL.md +4 -2
- data/skills/rigor-type-oracle/SKILL.md +226 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +265 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +125 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +12 -7
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +133 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -13,9 +13,10 @@ require_relative "../inference/scope_indexer"
|
|
|
13
13
|
require_relative "../inference/precision_scanner"
|
|
14
14
|
require_relative "../source/node_children"
|
|
15
15
|
require_relative "type_of_renderer"
|
|
16
|
+
require_relative "type_of_template_probe"
|
|
16
17
|
require_relative "command"
|
|
17
18
|
require_relative "options"
|
|
18
|
-
require_relative "
|
|
19
|
+
require_relative "../project_environment"
|
|
19
20
|
|
|
20
21
|
module Rigor
|
|
21
22
|
class CLI
|
|
@@ -28,10 +29,15 @@ module Rigor
|
|
|
28
29
|
# type-of UX (extra flags, watch mode, streaming output) without bloating the CLI shell. Output formatting is
|
|
29
30
|
# delegated to {TypeOfRenderer}.
|
|
30
31
|
class TypeOfCommand < Command
|
|
32
|
+
include TypeOfTemplateProbe
|
|
33
|
+
|
|
31
34
|
USAGE = "Usage: rigor type-of [options] FILE:LINE[:COL] [FILE:LINE[:COL] ...]"
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
# `location_mapper` is set for a template unit's result (#1040): the `--trace` fallback locations are
|
|
37
|
+
# positions in the COMPILED Ruby, and the mapper turns one into the `[template_line, column_or_nil]`
|
|
38
|
+
# the user can find in the file they named.
|
|
39
|
+
Result = Data.define(:file, :line, :column, :node, :type, :tracer, :enumeration, :location_mapper) do
|
|
40
|
+
def initialize(enumeration: nil, location_mapper: nil, **rest)
|
|
35
41
|
super
|
|
36
42
|
end
|
|
37
43
|
end
|
|
@@ -46,7 +52,7 @@ module Rigor
|
|
|
46
52
|
LINE_ENUMERATION_CAP = 40
|
|
47
53
|
private_constant :LINE_ENUMERATION_CAP
|
|
48
54
|
|
|
49
|
-
# @return
|
|
55
|
+
# @return CLI exit status.
|
|
50
56
|
def run
|
|
51
57
|
options = parse_options
|
|
52
58
|
buffer = Options.resolve_buffer_binding(options, err: @err)
|
|
@@ -106,13 +112,25 @@ module Rigor
|
|
|
106
112
|
physical = buffer ? buffer.resolve(file) : file
|
|
107
113
|
return 1 unless file_exists?(buffer ? physical : file)
|
|
108
114
|
|
|
115
|
+
unit_probe = template_unit_probe(file, base_scope.environment, buffer)
|
|
116
|
+
return unit_probe if unit_probe.is_a?(Integer)
|
|
117
|
+
|
|
118
|
+
if unit_probe
|
|
119
|
+
context = { configuration: configuration, base_scope: base_scope, options: options }
|
|
120
|
+
return resolve_template(file, physical, unit_probe, indexed_targets, context)
|
|
121
|
+
end
|
|
122
|
+
|
|
109
123
|
source = File.read(physical)
|
|
110
124
|
parse_result = Prism.parse(source, filepath: file, version: configuration.target_ruby)
|
|
111
125
|
return 1 if parse_errors?(parse_result, file)
|
|
112
126
|
|
|
113
127
|
# Built with no tracer attached — it would otherwise double-record fallback events with the per-node
|
|
114
|
-
# `type_of` calls below.
|
|
115
|
-
|
|
128
|
+
# `type_of` calls below. `with_source_path(file)` (like `annotate` and `trace` already do) matters
|
|
129
|
+
# beyond diagnostics attribution: a plugin `dynamic_return file_methods:` rule (ADR-52 slice 5a,
|
|
130
|
+
# e.g. rigor-rspec's `let`-binding resolver, #921) gates on `scope.source_path` and fails closed
|
|
131
|
+
# when it is nil — an unset source_path silently declined every such rule under this command.
|
|
132
|
+
scope_index = Inference::ScopeIndexer.index(parse_result.value,
|
|
133
|
+
default_scope: base_scope.with_source_path(file))
|
|
116
134
|
locator = Source::NodeLocator.new(source: source, root: parse_result.value)
|
|
117
135
|
lines = indexed_targets.filter_map { |target, _index| target.line if target.column.nil? }
|
|
118
136
|
line_nodes, line_totals = collect_line_nodes(parse_result.value, lines)
|
|
@@ -187,21 +205,21 @@ module Rigor
|
|
|
187
205
|
end
|
|
188
206
|
end
|
|
189
207
|
|
|
190
|
-
|
|
208
|
+
# `presentation` is the Result's optional `enumeration:` / `location_mapper:`.
|
|
209
|
+
def type_result(file, line, column, node, scope_index, options, **presentation)
|
|
191
210
|
tracer = options[:trace] ? Inference::FallbackTracer.new : nil
|
|
192
211
|
type = scope_index[node].type_of(node, tracer: tracer)
|
|
193
|
-
Result.new(file: file, line: line, column: column, node: node, type: type, tracer: tracer,
|
|
194
|
-
enumeration: enumeration)
|
|
212
|
+
Result.new(file: file, line: line, column: column, node: node, type: type, tracer: tracer, **presentation)
|
|
195
213
|
end
|
|
196
214
|
|
|
197
215
|
# Builds the plugin-aware environment relative to the probed file, so the reported type matches what `rigor
|
|
198
216
|
# check` computes for the same position — including types synthesized from inline RBS annotations by the
|
|
199
|
-
# ADR-93 auto-wired `rigor-rbs-inline` plugin (see {
|
|
217
|
+
# ADR-93 auto-wired `rigor-rbs-inline` plugin (see {ProjectEnvironment} for the #162 misattribution this
|
|
200
218
|
# closes). The probed file is threaded as the synthesizer's `source_files:`. Project-RBS auto-detection
|
|
201
219
|
# roots at CWD today; future work will walk parent directories to find the enclosing `Gemfile`/`*.gemspec`
|
|
202
220
|
# so probes against files outside the current process's CWD still see the right `sig/` tree.
|
|
203
221
|
def project_environment(files, configuration)
|
|
204
|
-
|
|
222
|
+
ProjectEnvironment.build(configuration: configuration, source_files: files)
|
|
205
223
|
end
|
|
206
224
|
|
|
207
225
|
def file_exists?(file)
|
|
@@ -84,7 +84,7 @@ module Rigor
|
|
|
84
84
|
@out.puts("fallbacks: none")
|
|
85
85
|
else
|
|
86
86
|
@out.puts("fallbacks (#{tracer.size}):")
|
|
87
|
-
tracer.each { |event| @out.puts(" - #{format_fallback_text(event, result
|
|
87
|
+
tracer.each { |event| @out.puts(" - #{format_fallback_text(event, result)}") }
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
90
|
|
|
@@ -118,21 +118,31 @@ module Rigor
|
|
|
118
118
|
type: result.type.describe,
|
|
119
119
|
erased: result.type.erase_to_rbs
|
|
120
120
|
}
|
|
121
|
-
payload[:fallbacks] = result.tracer.map { |event| fallback_to_h(event) } if result.tracer
|
|
121
|
+
payload[:fallbacks] = result.tracer.map { |event| fallback_to_h(event, result) } if result.tracer
|
|
122
122
|
payload
|
|
123
123
|
end
|
|
124
124
|
|
|
125
|
-
def format_fallback_text(event,
|
|
126
|
-
"#{event.node_class} (#{event.family}) @ #{location_text(event.location,
|
|
125
|
+
def format_fallback_text(event, result)
|
|
126
|
+
"#{event.node_class} (#{event.family}) @ #{location_text(event.location, result)}"
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
-
def location_text(location,
|
|
129
|
+
def location_text(location, result)
|
|
130
130
|
return "<no location>" unless location.respond_to?(:start_line)
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
line, column = fallback_position(location, result)
|
|
133
|
+
column ? "#{result.file}:#{line}:#{column}" : "#{result.file}:#{line}"
|
|
133
134
|
end
|
|
134
135
|
|
|
135
|
-
|
|
136
|
+
# A template unit's result (#1040) maps the compiled location back; a column that names nothing in the
|
|
137
|
+
# template is left out rather than printed as a column of bytes the user never wrote.
|
|
138
|
+
def fallback_position(location, result)
|
|
139
|
+
mapper = result.location_mapper
|
|
140
|
+
return [location.start_line, location.start_column + 1] if mapper.nil?
|
|
141
|
+
|
|
142
|
+
mapper.call(location)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def fallback_to_h(event, result)
|
|
136
146
|
hash = {
|
|
137
147
|
node_class: event.node_class.name,
|
|
138
148
|
family: event.family,
|
|
@@ -140,8 +150,9 @@ module Rigor
|
|
|
140
150
|
}
|
|
141
151
|
location = event.location
|
|
142
152
|
if location.respond_to?(:start_line)
|
|
143
|
-
|
|
144
|
-
hash[:
|
|
153
|
+
line, column = fallback_position(location, result)
|
|
154
|
+
hash[:line] = line
|
|
155
|
+
hash[:column] = column if column
|
|
145
156
|
end
|
|
146
157
|
hash
|
|
147
158
|
end
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "../analysis/template_unit_paths"
|
|
6
|
+
require_relative "../analysis/template_unit_positions"
|
|
7
|
+
require_relative "../analysis/template_units"
|
|
8
|
+
require_relative "../inference/precision_scanner"
|
|
9
|
+
require_relative "../inference/scope_indexer"
|
|
10
|
+
require_relative "../source/node_locator"
|
|
11
|
+
|
|
12
|
+
module Rigor
|
|
13
|
+
class CLI
|
|
14
|
+
# #1040 — `rigor type-of` against a **template unit**: a file a loaded plugin compiles into Ruby.
|
|
15
|
+
#
|
|
16
|
+
# Before this the command read the template from disk and parsed its bytes as Ruby, so an ERB view
|
|
17
|
+
# exited 1 on a parse error and an identity-transformed one answered about the wrong node under an
|
|
18
|
+
# unseeded scope. A template path now takes the path `rigor check` takes: the run's
|
|
19
|
+
# {Analysis::TemplateUnits} index is built, the COMPILED source is parsed with the unit's `scopes:`, the
|
|
20
|
+
# file scope is seeded with the unit's `self`, locals and ivars, and the requested template position is
|
|
21
|
+
# resolved through {Analysis::TemplateUnitPositions} — which declines, with a message, whenever the
|
|
22
|
+
# position does not denote exactly one compiled node.
|
|
23
|
+
#
|
|
24
|
+
# Mixed into {TypeOfCommand}; a path no loaded plugin claims never reaches anything here, so a `.rb`
|
|
25
|
+
# probe does not build the index or change a byte of its output.
|
|
26
|
+
module TypeOfTemplateProbe
|
|
27
|
+
# Why a template position has no answer, keyed by {Analysis::TemplateUnitPositions#node_at}'s reasons.
|
|
28
|
+
DECLINE_MESSAGES = {
|
|
29
|
+
no_compiled_line: "the template compiler emitted no Ruby for this line",
|
|
30
|
+
not_verbatim: "the position is in template markup, or in code the template compiler rewrote " \
|
|
31
|
+
"rather than copied, so no compiled expression is made of the bytes there",
|
|
32
|
+
ambiguous: "the bytes there are template markup, or code the compiler copied to more than one " \
|
|
33
|
+
"place, so the position does not name a single compiled expression"
|
|
34
|
+
}.freeze
|
|
35
|
+
private_constant :DECLINE_MESSAGES
|
|
36
|
+
Ractor.make_shareable(DECLINE_MESSAGES)
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# The unit `file` compiles to, `nil` for a path that is not a template unit (the caller probes it as
|
|
41
|
+
# Ruby), or an exit status after reporting a template the claiming plugin could not compile.
|
|
42
|
+
def template_unit_probe(file, environment, buffer)
|
|
43
|
+
registry = environment.plugin_registry
|
|
44
|
+
return nil unless template_claimed?(registry, file)
|
|
45
|
+
|
|
46
|
+
units = (@template_units ||= Analysis::TemplateUnits.collect(registry: registry, buffer: buffer))
|
|
47
|
+
entry = units[file]
|
|
48
|
+
return [units, entry] if entry
|
|
49
|
+
|
|
50
|
+
# A template the plugin DECLINED is not a unit, and is probed as the command always probed it —
|
|
51
|
+
# which for a template that is not Ruby is a Prism error about bytes the user did not write, so
|
|
52
|
+
# the decline is stated first rather than left to be inferred from the error.
|
|
53
|
+
failures = template_failures(units, file)
|
|
54
|
+
if failures.empty?
|
|
55
|
+
@err.puts("type-of: #{file}: plugin declined the template; probing its bytes as Ruby")
|
|
56
|
+
return nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
failures.each do |failure|
|
|
60
|
+
@err.puts("type-of: #{file}: plugin #{failure.plugin_id} could not compile the template: " \
|
|
61
|
+
"#{failure.message}")
|
|
62
|
+
end
|
|
63
|
+
1
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Only a claimed path builds the index — a `.rb` probe pays no glob expansion and no transform.
|
|
67
|
+
def template_claimed?(registry, file)
|
|
68
|
+
return false if registry.nil? || registry.empty?
|
|
69
|
+
|
|
70
|
+
relative = Analysis::TemplateUnitPaths.relative(file, Dir.pwd)
|
|
71
|
+
registry.plugins.any? do |plugin|
|
|
72
|
+
globs = begin
|
|
73
|
+
plugin.manifest.template_globs
|
|
74
|
+
rescue StandardError
|
|
75
|
+
[]
|
|
76
|
+
end
|
|
77
|
+
!globs.empty? && Analysis::TemplateUnitPaths.claims?(globs, relative)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def template_failures(units, file)
|
|
82
|
+
relative = Analysis::TemplateUnitPaths.relative(file, Dir.pwd)
|
|
83
|
+
units.failures.select { |failure| failure.path == relative }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Every result for one template: the compiled source parsed and indexed once under the unit's seeded
|
|
87
|
+
# scope, then each requested template position resolved through the inverse map.
|
|
88
|
+
def resolve_template(file, physical, unit_probe, indexed_targets, context)
|
|
89
|
+
units, entry = unit_probe
|
|
90
|
+
parse_result = Prism.parse(entry.source, filepath: file, version: context[:configuration].target_ruby,
|
|
91
|
+
scopes: units.parse_scopes(file))
|
|
92
|
+
return 1 if compiled_parse_errors?(parse_result, file, entry)
|
|
93
|
+
|
|
94
|
+
scope = units.seed(context[:base_scope].with_source_path(file), file)
|
|
95
|
+
scope_index = Inference::ScopeIndexer.index(parse_result.value, default_scope: scope)
|
|
96
|
+
positions = Analysis::TemplateUnitPositions.new(
|
|
97
|
+
entry: entry, template: File.binread(physical), root: parse_result.value,
|
|
98
|
+
skip_node_types: Inference::PrecisionScanner::NON_EXPRESSION_NODE_TYPES
|
|
99
|
+
)
|
|
100
|
+
probe = { file: file, entry: entry, positions: positions, scope_index: scope_index,
|
|
101
|
+
options: context[:options] }
|
|
102
|
+
indexed_targets.map do |target, index|
|
|
103
|
+
resolved = resolve_template_target(probe, target)
|
|
104
|
+
return resolved if resolved.is_a?(Integer)
|
|
105
|
+
|
|
106
|
+
[index, resolved]
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def resolve_template_target(probe, target)
|
|
111
|
+
file = probe[:file]
|
|
112
|
+
positions = probe[:positions]
|
|
113
|
+
return CLI::EXIT_USAGE unless template_position_in_range?(positions, target)
|
|
114
|
+
return enumerate_template_line(probe, target.line) if target.column.nil?
|
|
115
|
+
|
|
116
|
+
node = positions.node_at(line: target.line, column: target.column)
|
|
117
|
+
if node.is_a?(Symbol)
|
|
118
|
+
@err.puts("type-of: no expression found at #{file}:#{target.line}:#{target.column}: " \
|
|
119
|
+
"#{DECLINE_MESSAGES.fetch(node)}")
|
|
120
|
+
return []
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
[type_result(file, target.line, target.column, node, probe[:scope_index], probe[:options],
|
|
124
|
+
location_mapper: template_location_mapper(probe))]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def enumerate_template_line(probe, line)
|
|
128
|
+
unless line.between?(1, probe[:positions].template_line_count)
|
|
129
|
+
@err.puts("type-of: no expression found on #{probe[:file]}:#{line}")
|
|
130
|
+
return []
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
rows, total = probe[:positions].line_nodes(line)
|
|
134
|
+
if rows.empty?
|
|
135
|
+
@err.puts("type-of: no expression found on #{probe[:file]}:#{line}: no compiled expression maps to " \
|
|
136
|
+
"a single column of this line (markup, code the template compiler rewrote, or code " \
|
|
137
|
+
"copied to more than one place)")
|
|
138
|
+
return []
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
enumeration = TypeOfCommand::LineEnumeration.new(total: total, shown: rows.length)
|
|
142
|
+
mapper = template_location_mapper(probe)
|
|
143
|
+
rows.map do |column, node|
|
|
144
|
+
type_result(probe[:file], line, column, node, probe[:scope_index], probe[:options],
|
|
145
|
+
enumeration: enumeration, location_mapper: mapper)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# The same range errors, and the same exit status, a `.rb` probe gives — measured against the
|
|
150
|
+
# TEMPLATE's lines, which are the ones the user named. A bare `FILE:LINE` past the end is "no
|
|
151
|
+
# expression found", exactly as it is for Ruby.
|
|
152
|
+
def template_position_in_range?(positions, target)
|
|
153
|
+
return true if target.column.nil?
|
|
154
|
+
|
|
155
|
+
message = if target.line < 1 then "line must be >= 1, got #{target.line}"
|
|
156
|
+
elsif target.column < 1 then "column must be >= 1, got #{target.column}"
|
|
157
|
+
elsif target.line > positions.template_line_count
|
|
158
|
+
"line #{target.line} is past the end of the source buffer"
|
|
159
|
+
end
|
|
160
|
+
return true if message.nil?
|
|
161
|
+
|
|
162
|
+
@err.puts("type-of: #{message}")
|
|
163
|
+
false
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# A `--trace` fallback carries a COMPILED location; it is reported at the template line, and at a
|
|
167
|
+
# template column only when the location lies in a verbatim run.
|
|
168
|
+
def template_location_mapper(probe)
|
|
169
|
+
entry = probe[:entry]
|
|
170
|
+
positions = probe[:positions]
|
|
171
|
+
lambda do |location|
|
|
172
|
+
[entry.template_line(location.start_line), positions.template_column_for(location)]
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# A compiled source that does not parse is the plugin's output, not the user's text; its errors are
|
|
177
|
+
# reported at the template lines they map to, as `rigor check` reports every other finding in a unit.
|
|
178
|
+
def compiled_parse_errors?(result, file, entry)
|
|
179
|
+
return false if result.errors.empty?
|
|
180
|
+
|
|
181
|
+
result.errors.each do |error|
|
|
182
|
+
@err.puts("#{file}:#{entry.template_line(error.location.start_line)}: " \
|
|
183
|
+
"compiled template does not parse: #{error.message}")
|
|
184
|
+
end
|
|
185
|
+
true
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -11,7 +11,7 @@ require_relative "../scope"
|
|
|
11
11
|
require_relative "type_scan_renderer"
|
|
12
12
|
require_relative "type_scan_report"
|
|
13
13
|
require_relative "command"
|
|
14
|
-
require_relative "
|
|
14
|
+
require_relative "../project_environment"
|
|
15
15
|
require_relative "coverage_scan"
|
|
16
16
|
|
|
17
17
|
module Rigor
|
|
@@ -26,7 +26,7 @@ module Rigor
|
|
|
26
26
|
|
|
27
27
|
LocatedEvent = Data.define(:file, :event)
|
|
28
28
|
|
|
29
|
-
# @return
|
|
29
|
+
# @return CLI exit status.
|
|
30
30
|
def run
|
|
31
31
|
options = parse_options
|
|
32
32
|
paths = collect_paths(@argv, command_name: "type-scan")
|
|
@@ -89,9 +89,9 @@ module Rigor
|
|
|
89
89
|
# Builds the plugin-aware environment that auto-detects `<cwd>/sig` by default and honours the
|
|
90
90
|
# configuration's `libraries:` / `signature_paths:` keys when present. The scanned `paths` are threaded as
|
|
91
91
|
# the plugin `source_rbs_synthesizer` inputs so coverage reflects the same synthesized RBS `rigor check`
|
|
92
|
-
# sees (see {
|
|
92
|
+
# sees (see {ProjectEnvironment}).
|
|
93
93
|
def project_environment(configuration, source_files)
|
|
94
|
-
|
|
94
|
+
ProjectEnvironment.build(configuration: configuration, source_files: source_files)
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
def scan_one(path, scanner, accumulator, configuration)
|
|
@@ -14,7 +14,7 @@ require_relative "../analysis/reachability/project_files"
|
|
|
14
14
|
require_relative "../cache/store"
|
|
15
15
|
require_relative "options"
|
|
16
16
|
require_relative "command"
|
|
17
|
-
require_relative "
|
|
17
|
+
require_relative "../project_environment"
|
|
18
18
|
|
|
19
19
|
module Rigor
|
|
20
20
|
class CLI
|
|
@@ -219,15 +219,23 @@ module Rigor
|
|
|
219
219
|
# declaration, which produced three of redmine's artifacts from a single initializer. A name the bundled
|
|
220
220
|
# (non-project) environment already knows is not ours to call unused. The project's own `sig/` is
|
|
221
221
|
# deliberately excluded from this environment, so a project class that ships a signature stays owned.
|
|
222
|
+
# That exclusion is the ONLY thing withheld: the dependency-discovery axes (issue #882, the shape of
|
|
223
|
+
# #821) must be passed, because "outside the project" is exactly what they describe. Without the
|
|
224
|
+
# bundle's per-gem `sig/` and an installed `rbs collection`, a gem-declared class the project reopens
|
|
225
|
+
# is unknown to this environment, reads as project-owned, and lands in the candidate list — a false
|
|
226
|
+
# positive in a report whose measured precision cannot afford one.
|
|
222
227
|
#
|
|
223
228
|
# The cache store makes the environment a Marshal restore instead of a cold RBS build on every
|
|
224
229
|
# invocation — the same ADR-54 slot `rigor check` reads, keyed apart by this environment's own
|
|
225
|
-
# (sig-less) descriptor. The
|
|
230
|
+
# (sig-less) descriptor. The discovered bundle and collection directories ride in the loader's
|
|
231
|
+
# signature paths, so the descriptor digests them and this slot still cannot be served a run whose
|
|
232
|
+
# dependency sources differ. The report only ever asks "is this name known", which the cached
|
|
226
233
|
# environment answers identically: the one thing the cache degrades is `RBS::Location`, and no
|
|
227
234
|
# location is read here.
|
|
228
235
|
def foreign_predicate(configuration)
|
|
229
236
|
env = Environment.for_project(libraries: configuration.libraries, signature_paths: [],
|
|
230
|
-
cache_store: cache_store(configuration)
|
|
237
|
+
cache_store: cache_store(configuration),
|
|
238
|
+
**ProjectEnvironment.dependency_discovery_options(configuration))
|
|
231
239
|
->(fqn) { !env.singleton_for_name(fqn).nil? }
|
|
232
240
|
rescue StandardError
|
|
233
241
|
->(_fqn) { false }
|
|
@@ -13,7 +13,7 @@ module Rigor
|
|
|
13
13
|
class UpgradeCommand < Command
|
|
14
14
|
USAGE = "Usage: rigor upgrade [options]"
|
|
15
15
|
|
|
16
|
-
# @return
|
|
16
|
+
# @return CLI exit status.
|
|
17
17
|
def run
|
|
18
18
|
@out.puts("rigor upgrade: No migration target available yet (ADR-50 WD7, queued).")
|
|
19
19
|
@out.puts("Current version: #{Rigor::VERSION}")
|
data/lib/rigor/cli.rb
CHANGED
|
@@ -12,6 +12,7 @@ require_relative "analysis/result"
|
|
|
12
12
|
require_relative "cli/options"
|
|
13
13
|
require_relative "cli/diagnostic_formats"
|
|
14
14
|
require_relative "ci_detector"
|
|
15
|
+
require_relative "analysis/check_rules/rule_ids"
|
|
15
16
|
|
|
16
17
|
module Rigor
|
|
17
18
|
# The CLI class is a dispatcher: each `run_*` method delegates to a command-specific class once the command grows
|
|
@@ -20,6 +21,21 @@ module Rigor
|
|
|
20
21
|
class CLI # rubocop:disable Metrics/ClassLength
|
|
21
22
|
EXIT_USAGE = 64
|
|
22
23
|
|
|
24
|
+
# Issue [#609](https://github.com/rigortype/rigor/issues/609) — a run the analyzer could not finish.
|
|
25
|
+
# Distinct from `1` (the analysis ran and reported diagnostics) so a CI step can tell "your code has
|
|
26
|
+
# findings" from "Rigor died and this report is not an account of anything".
|
|
27
|
+
EXIT_INTERNAL_ERROR = 70
|
|
28
|
+
|
|
29
|
+
# The exceptions that mean "the process is out of a resource, and the backtrace names nothing
|
|
30
|
+
# actionable": `SystemStackError` from an ancestry walk that never terminates, `NoMemoryError` from a
|
|
31
|
+
# runaway allocation. Neither is a `StandardError`, so every rescue between here and the raise site
|
|
32
|
+
# declines them by design — the whole point of those rescues is to convert a per-file or per-plugin
|
|
33
|
+
# failure into a diagnostic, and a process running out of stack is not per-file. Left uncaught they
|
|
34
|
+
# abort with Ruby's own ~40-frame dump, which is non-zero (so a CI gate does catch it) but says
|
|
35
|
+
# neither that Rigor is at fault nor that the report on disk is empty. `Interrupt`, `SignalException`
|
|
36
|
+
# and `SystemExit` stay out: those are the user's answer, not a failure.
|
|
37
|
+
FATAL_ERRORS = [SystemStackError, NoMemoryError].freeze
|
|
38
|
+
|
|
23
39
|
# The published location of `schemas/rigor-config.schema.json`, written into the `.rigor.yml` that
|
|
24
40
|
# `rigor init` generates so an editor validates the file as the user types.
|
|
25
41
|
#
|
|
@@ -95,10 +111,28 @@ module Rigor
|
|
|
95
111
|
# register presets have loaded) would each need their own rescue at their own point.
|
|
96
112
|
@err.puts("rigor: #{e.message}")
|
|
97
113
|
EXIT_USAGE
|
|
114
|
+
rescue *FATAL_ERRORS => e
|
|
115
|
+
report_fatal_error(e)
|
|
116
|
+
EXIT_INTERNAL_ERROR
|
|
98
117
|
end
|
|
99
118
|
|
|
100
119
|
private
|
|
101
120
|
|
|
121
|
+
# One `rigor:` line naming the failure, then the innermost frames — enough to route a bug report,
|
|
122
|
+
# without the full dump the raise would otherwise print. The "no report" sentence is the part a user
|
|
123
|
+
# acts on: the crash usually happens with a redirect in place, so the artifact a CI step goes on to
|
|
124
|
+
# read is an empty file rather than a missing one.
|
|
125
|
+
def report_fatal_error(error)
|
|
126
|
+
@err.puts("rigor: analysis aborted: #{error.class}: #{error.message}")
|
|
127
|
+
Array(error.backtrace).first(FATAL_BACKTRACE_FRAMES).each { |frame| @err.puts(" from #{frame}") }
|
|
128
|
+
@err.puts(" Rigor could not finish this run, so its report is EMPTY rather than clean — do not")
|
|
129
|
+
@err.puts(" read this exit status as a passing check.")
|
|
130
|
+
@err.puts(" This is a defect in Rigor; please report it at https://github.com/rigortype/rigor/issues.")
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
FATAL_BACKTRACE_FRAMES = 5
|
|
134
|
+
private_constant :FATAL_BACKTRACE_FRAMES
|
|
135
|
+
|
|
102
136
|
def dispatch(command)
|
|
103
137
|
handler = HANDLERS[command]
|
|
104
138
|
return arm_jit_deadline { send(handler) } if handler
|
|
@@ -149,7 +183,7 @@ module Rigor
|
|
|
149
183
|
return 1
|
|
150
184
|
end
|
|
151
185
|
|
|
152
|
-
File.write(path, init_template)
|
|
186
|
+
File.write(path, init_template(path))
|
|
153
187
|
@out.puts("Created #{path}")
|
|
154
188
|
print_init_next_steps(path)
|
|
155
189
|
0
|
|
@@ -169,10 +203,34 @@ module Rigor
|
|
|
169
203
|
@out.puts " 3. Run `rigor check` to analyse your code."
|
|
170
204
|
end
|
|
171
205
|
|
|
206
|
+
# Word-wraps `CheckRules::ALL_RULES` into a comma-separated, `#`-commented block for `init_template`, so the
|
|
207
|
+
# listed rule ids cannot drift from the catalogue the way a hand-picked subset did (#920) — every rule and only
|
|
208
|
+
# a rule actually in `ALL_RULES` gets named here.
|
|
209
|
+
def rule_ids_comment
|
|
210
|
+
prefix = "# "
|
|
211
|
+
max_width = 78 - prefix.length
|
|
212
|
+
ids = Analysis::CheckRules::ALL_RULES
|
|
213
|
+
lines = []
|
|
214
|
+
current = +""
|
|
215
|
+
ids.each_with_index do |id, index|
|
|
216
|
+
token = "#{id}#{index == ids.length - 1 ? '.' : ','}"
|
|
217
|
+
if current.empty?
|
|
218
|
+
current = token
|
|
219
|
+
elsif current.length + 1 + token.length <= max_width
|
|
220
|
+
current << " " << token
|
|
221
|
+
else
|
|
222
|
+
lines << current
|
|
223
|
+
current = token
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
lines << current unless current.empty?
|
|
227
|
+
lines.map { |line| "#{prefix}#{line}" }.join("\n")
|
|
228
|
+
end
|
|
229
|
+
|
|
172
230
|
# Renders the starter `.rigor.yml` body. The template serialises `Configuration::DEFAULTS` (so the on-disk file
|
|
173
231
|
# round-trips through `Configuration.load`) and prepends a short header that points the user at the keys they are
|
|
174
232
|
# most likely to want to edit.
|
|
175
|
-
def init_template
|
|
233
|
+
def init_template(path = ".rigor.dist.yml")
|
|
176
234
|
<<~YAML
|
|
177
235
|
# yaml-language-server: $schema=#{CONFIG_SCHEMA_URL}
|
|
178
236
|
# Rigor configuration. The full key reference is
|
|
@@ -187,11 +245,10 @@ module Rigor
|
|
|
187
245
|
# See https://github.com/rigortype/rigor/tree/master/plugins
|
|
188
246
|
# for production plugins (rigor-activerecord, rigor-sorbet, …).
|
|
189
247
|
# - disable: list of `rigor check` rule identifiers to
|
|
190
|
-
# silence project-wide.
|
|
191
|
-
#
|
|
192
|
-
#
|
|
193
|
-
#
|
|
194
|
-
# assert.type-mismatch, flow.always-raises.
|
|
248
|
+
# silence project-wide. Every rule id Rigor
|
|
249
|
+
# ships (run `rigor explain <rule>` for what
|
|
250
|
+
# each one catches):
|
|
251
|
+
#{rule_ids_comment}
|
|
195
252
|
# A bare family token (`call`, `flow`,
|
|
196
253
|
# `assert`, `dump`, `def`) wildcards every
|
|
197
254
|
# rule under that prefix. Legacy unprefixed
|
|
@@ -209,6 +266,12 @@ module Rigor
|
|
|
209
266
|
# Leave unset (or `null`) to auto-detect
|
|
210
267
|
# `<root>/sig`. Use `[]` to disable
|
|
211
268
|
# project-RBS loading entirely.
|
|
269
|
+
# - test_paths: the directories holding your tests.
|
|
270
|
+
# `rigor sig-gen --params=observed` reads
|
|
271
|
+
# their call sites to type parameters.
|
|
272
|
+
# `rigor init` writes the spec/ and test/
|
|
273
|
+
# it finds; unset auto-detects them, and
|
|
274
|
+
# `[]` declares the project has none.
|
|
212
275
|
# - cache.path: where Rigor will eventually persist
|
|
213
276
|
# analysis results across runs.
|
|
214
277
|
#
|
|
@@ -218,10 +281,22 @@ module Rigor
|
|
|
218
281
|
# tempfile, uri, logger, date, prism, rbs). Adding a
|
|
219
282
|
# `sig/<gem>.rbs` file under `sig/` is the simplest way
|
|
220
283
|
# to extend type coverage today.
|
|
221
|
-
#{YAML.dump(
|
|
284
|
+
#{YAML.dump(init_settings(path)).sub(/\A---\n/, '')}
|
|
222
285
|
YAML
|
|
223
286
|
end
|
|
224
287
|
|
|
288
|
+
# The settings `rigor init` writes: the defaults, with `test_paths:` spelled out as the test roots found now, so
|
|
289
|
+
# the project's config says where its tests are rather than leaving it to auto-detection. The roots are written
|
|
290
|
+
# relative to the config file's directory, which is what {Configuration.load} resolves them against.
|
|
291
|
+
def init_settings(path)
|
|
292
|
+
test_paths = Configuration.new.resolved_test_paths
|
|
293
|
+
return Configuration::DEFAULTS if test_paths.empty?
|
|
294
|
+
|
|
295
|
+
config_dir = Pathname(File.expand_path(File.dirname(path)))
|
|
296
|
+
relative = test_paths.map { |root| Pathname(root).relative_path_from(config_dir).to_s }
|
|
297
|
+
Configuration::DEFAULTS.merge("test_paths" => relative)
|
|
298
|
+
end
|
|
299
|
+
|
|
225
300
|
def run_annotate
|
|
226
301
|
require_relative "cli/annotate_command"
|
|
227
302
|
|
|
@@ -320,7 +395,10 @@ module Rigor
|
|
|
320
395
|
@err.puts "Install it with: gem install rigor-playground"
|
|
321
396
|
return EXIT_USAGE
|
|
322
397
|
end
|
|
323
|
-
|
|
398
|
+
# `run` shifts the verb off `@argv` before dispatching, so the handler receives the arguments
|
|
399
|
+
# already. Slicing here dropped the first one — `rigor playground --port=4000` served the
|
|
400
|
+
# default port, and a bare `rigor playground` passed `nil` into a command that iterates it.
|
|
401
|
+
Rigor::CLI::PlaygroundCommand.new(@argv, @out, @err).run
|
|
324
402
|
end
|
|
325
403
|
|
|
326
404
|
def run_skill
|
|
@@ -383,11 +461,13 @@ module Rigor
|
|
|
383
461
|
(opt-in; effects update/check/diff/explain)
|
|
384
462
|
explain Print the description of one or all CheckRules
|
|
385
463
|
diff Compare current diagnostics to a saved baseline JSON
|
|
464
|
+
baseline Manage the baseline file (baseline generate/regenerate/dump/drift/prune)
|
|
386
465
|
sig-gen Emit RBS skeletons inferred from .rb sources
|
|
387
466
|
lsp Run the Rigor Language Server (LSP) over stdio
|
|
388
467
|
mcp Run the Rigor MCP server over stdio
|
|
389
468
|
triage Summarise diagnostics: distribution, hotspots, hints
|
|
390
469
|
coverage Report type-precision coverage (precise vs Dynamic ratio)
|
|
470
|
+
unused Report unreferenced classes, modules and constants as removal candidates
|
|
391
471
|
plugins Report activation status of every configured plugin
|
|
392
472
|
plugin Browse bundled plugin source as worked examples (list/path/print/root)
|
|
393
473
|
playground Start the browser playground (requires rigor-playground gem)
|
data/lib/rigor/config_audit.rb
CHANGED
|
@@ -18,6 +18,11 @@ module Rigor
|
|
|
18
18
|
# never written. This module surfaces each such entry up front so the cause is visible
|
|
19
19
|
# instead of inferred.
|
|
20
20
|
#
|
|
21
|
+
# {.bundled_plugin_signature_path_warnings} widens that by one step, to a value that
|
|
22
|
+
# resolves to *part* of what it looks like. It belongs here because the symptom is the
|
|
23
|
+
# same one — a cluster of `call.undefined-method` on working code, traceable to a config
|
|
24
|
+
# line nothing in the output names.
|
|
25
|
+
#
|
|
21
26
|
# Every check is held to the same bar that {SignaturePathAudit} set: it mirrors the
|
|
22
27
|
# loader's own acceptance test, so a warning means the loader really did load nothing, and
|
|
23
28
|
# it never fires on a setup that works. In particular the rule-token check only flags a
|
|
@@ -27,8 +32,8 @@ module Rigor
|
|
|
27
32
|
# a typo.
|
|
28
33
|
module ConfigAudit
|
|
29
34
|
# One config-level finding. `kind` discriminates the source key (`:signature_path`,
|
|
30
|
-
# `:library`, `:disabled_rule`, `:severity_override`,
|
|
31
|
-
# `:bundler_lockfile`, `:rbs_collection_lockfile`); `fields` carries the kind-specific
|
|
35
|
+
# `:bundled_plugin_signature_path`, `:library`, `:disabled_rule`, `:severity_override`,
|
|
36
|
+
# `:bundler_bundle_path`, `:bundler_lockfile`, `:rbs_collection_lockfile`); `fields` carries the kind-specific
|
|
32
37
|
# structured data merged into {#to_h} for JSON consumers.
|
|
33
38
|
Warning = Data.define(:kind, :message, :fields) do
|
|
34
39
|
def to_h
|
|
@@ -36,13 +41,12 @@ module Rigor
|
|
|
36
41
|
end
|
|
37
42
|
end
|
|
38
43
|
|
|
39
|
-
# @param
|
|
40
|
-
# @param project_root [String] the directory the run's relative bundler / collection
|
|
44
|
+
# @param project_root — the directory the run's relative bundler / collection
|
|
41
45
|
# paths resolve against (the CLI's CWD), used only by the explicit-path checks.
|
|
42
|
-
# @return [Array<Warning>]
|
|
43
46
|
def self.warnings(configuration, project_root: Dir.pwd)
|
|
44
47
|
unknown_key_warnings(configuration) +
|
|
45
48
|
signature_path_warnings(configuration) +
|
|
49
|
+
bundled_plugin_signature_path_warnings(configuration) +
|
|
46
50
|
library_warnings(configuration) +
|
|
47
51
|
rule_token_warnings(configuration) +
|
|
48
52
|
explicit_path_warnings(configuration, project_root)
|
|
@@ -100,6 +104,27 @@ module Rigor
|
|
|
100
104
|
end
|
|
101
105
|
end
|
|
102
106
|
|
|
107
|
+
# The one member of this module whose subject is a configured value that resolves to
|
|
108
|
+
# *something*: a `signature_paths:` entry loading a bundled plugin's own `sig/` while
|
|
109
|
+
# `plugins:` never names that plugin (issue #697). The RBS arrives, its manifest does not,
|
|
110
|
+
# and the declarations that manifest marks deliberately partial (ADR-26 `open_receivers:`)
|
|
111
|
+
# are then read as complete — so the symptom is this module's usual one, a cluster of
|
|
112
|
+
# high-confidence `call.undefined-method` firings on code that runs, with nothing pointing
|
|
113
|
+
# at the config line that caused them.
|
|
114
|
+
#
|
|
115
|
+
# It is a warning and not a fix on purpose. Teaching the check rules to recognise this
|
|
116
|
+
# route would be a fourth way for a class to be open-receiver protected, and settling
|
|
117
|
+
# where that membership belongs is [#660](https://github.com/rigortype/rigor/issues/660)'s
|
|
118
|
+
# question; until it is answered, the honest move is to make the misconfiguration loud
|
|
119
|
+
# rather than to grow the mechanism. The false positive itself still fires.
|
|
120
|
+
def self.bundled_plugin_signature_path_warnings(configuration)
|
|
121
|
+
routes = SignaturePathAudit.bundled_plugin_routes(configuration.signature_paths, configuration.plugins)
|
|
122
|
+
routes.map do |route|
|
|
123
|
+
Warning.new(kind: :bundled_plugin_signature_path, message: route.message,
|
|
124
|
+
fields: { "path" => route.path, "gem" => route.gem })
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
103
128
|
# `libraries:` entries RBS does not recognise. Uses the same
|
|
104
129
|
# `RBS::EnvironmentLoader#has_library?` guard the loader filters through
|
|
105
130
|
# ({Environment::RbsLoader} `build_env_for`), so a flagged name is exactly one the loader
|