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
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
|
|
@@ -169,6 +203,30 @@ 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.
|
|
@@ -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
|
|
@@ -320,7 +377,10 @@ module Rigor
|
|
|
320
377
|
@err.puts "Install it with: gem install rigor-playground"
|
|
321
378
|
return EXIT_USAGE
|
|
322
379
|
end
|
|
323
|
-
|
|
380
|
+
# `run` shifts the verb off `@argv` before dispatching, so the handler receives the arguments
|
|
381
|
+
# already. Slicing here dropped the first one — `rigor playground --port=4000` served the
|
|
382
|
+
# default port, and a bare `rigor playground` passed `nil` into a command that iterates it.
|
|
383
|
+
Rigor::CLI::PlaygroundCommand.new(@argv, @out, @err).run
|
|
324
384
|
end
|
|
325
385
|
|
|
326
386
|
def run_skill
|
|
@@ -383,11 +443,13 @@ module Rigor
|
|
|
383
443
|
(opt-in; effects update/check/diff/explain)
|
|
384
444
|
explain Print the description of one or all CheckRules
|
|
385
445
|
diff Compare current diagnostics to a saved baseline JSON
|
|
446
|
+
baseline Manage the baseline file (baseline generate/regenerate/dump/drift/prune)
|
|
386
447
|
sig-gen Emit RBS skeletons inferred from .rb sources
|
|
387
448
|
lsp Run the Rigor Language Server (LSP) over stdio
|
|
388
449
|
mcp Run the Rigor MCP server over stdio
|
|
389
450
|
triage Summarise diagnostics: distribution, hotspots, hints
|
|
390
451
|
coverage Report type-precision coverage (precise vs Dynamic ratio)
|
|
452
|
+
unused Report unreferenced classes, modules and constants as removal candidates
|
|
391
453
|
plugins Report activation status of every configured plugin
|
|
392
454
|
plugin Browse bundled plugin source as worked examples (list/path/print/root)
|
|
393
455
|
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
|
|
@@ -167,20 +167,20 @@ module Rigor
|
|
|
167
167
|
# Resolves the configured severity for a diagnostic given the active profile and any per-rule
|
|
168
168
|
# overrides.
|
|
169
169
|
#
|
|
170
|
-
# @param rule
|
|
171
|
-
# @param authored_severity
|
|
170
|
+
# @param rule — canonical rule id (`call.undefined-method`).
|
|
171
|
+
# @param authored_severity — severity the rule emitted the diagnostic with (`:error`, `:warning`,
|
|
172
172
|
# `:info`).
|
|
173
|
-
# @param profile
|
|
174
|
-
# @param overrides
|
|
173
|
+
# @param profile — one of {VALID_PROFILES}; falls back to {DEFAULT_PROFILE} for unknown values.
|
|
174
|
+
# @param overrides — per-rule severity overrides from `.rigor.yml`'s
|
|
175
175
|
# `severity_overrides:` map. Keys are canonical rule ids; values are {VALID_SEVERITIES} symbols.
|
|
176
176
|
# Family-wildcard keys (`call`) match every rule under that prefix.
|
|
177
|
-
# @param bleeding_edge_overrides
|
|
177
|
+
# @param bleeding_edge_overrides — the severity map imposed by the active ADR-50
|
|
178
178
|
# § WD2 bleeding-edge features ({Rigor::BleedingEdge.severity_overrides_for}). Consulted *below* the
|
|
179
179
|
# user's own `overrides` (so an explicit `severity_overrides:` entry, exact or family wildcard,
|
|
180
180
|
# always wins) and *above* the profile table. Exact rule ids only — the overlay never carries family
|
|
181
181
|
# wildcards. Empty while the overlay is unpopulated, so the default leaves resolution bit-for-bit
|
|
182
182
|
# unchanged.
|
|
183
|
-
# @return
|
|
183
|
+
# @return the resolved severity. Returns `:off` to mean "drop the diagnostic entirely".
|
|
184
184
|
def resolve(rule:, authored_severity:, profile: DEFAULT_PROFILE, overrides: {}, bleeding_edge_overrides: {})
|
|
185
185
|
return authored_severity if rule.nil?
|
|
186
186
|
|
data/lib/rigor/configuration.rb
CHANGED
|
@@ -117,6 +117,16 @@ module Rigor
|
|
|
117
117
|
# `"stat"` (or `RIGOR_CI_DETECT=0`).
|
|
118
118
|
"validation" => "auto"
|
|
119
119
|
},
|
|
120
|
+
# ADR-39 slice 5 / #911 — the plugin-isolation strategy for target-library invocation. `nil` (the
|
|
121
|
+
# default) names nothing, leaving the choice to `RIGOR_PLUGIN_ISOLATION` and then to
|
|
122
|
+
# `Plugin::Isolation::DEFAULT`. The environment variable WINS over this key: it is the
|
|
123
|
+
# one-invocation operator override (`RIGOR_PLUGIN_ISOLATION=none rigor check` on a platform where
|
|
124
|
+
# the fork worker misbehaves), and an override a committed file can veto is not an override.
|
|
125
|
+
# Only `none` and `process` are selectable here — `ruby_box` needs `RUBY_BOX=1` set before Ruby
|
|
126
|
+
# boots, which is what `exe/rigor` re-execs for, and the launcher deliberately does not parse YAML
|
|
127
|
+
# before that re-exec (ADR-87 / ADR-104 boot-slim), so naming it here raises instead of silently
|
|
128
|
+
# resolving to a different strategy.
|
|
129
|
+
"plugins_isolation" => nil,
|
|
120
130
|
"plugins_io" => {
|
|
121
131
|
"network" => "disabled",
|
|
122
132
|
"allowed_paths" => [],
|
|
@@ -212,6 +222,10 @@ module Rigor
|
|
|
212
222
|
# reservation FINDABLE by the next person adding config validation (#166 is exactly that), and to
|
|
213
223
|
# give the schema-parity spec something to key on: a reserved namespace is by definition absent
|
|
214
224
|
# from DEFAULTS, so the DEFAULTS-driven schema gate can never see it.
|
|
225
|
+
# The isolation strategies a `.rigor.yml` may name (ADR-39 slice 5 / #911). `ruby_box` is
|
|
226
|
+
# deliberately absent: it is a boot-time decision the config file is read too late to make.
|
|
227
|
+
CONFIGURABLE_PLUGIN_ISOLATIONS = %w[none process].freeze
|
|
228
|
+
|
|
215
229
|
RESERVED_NAMESPACES = %w[rigor_rs].freeze
|
|
216
230
|
|
|
217
231
|
# Every top-level key a conforming `.rigor.yml` may carry: the keys this implementation owns, plus
|
|
@@ -238,6 +252,7 @@ module Rigor
|
|
|
238
252
|
attr_reader :target_ruby, :paths, :exclude_patterns, :plugins, :cache_path, :cache_max_bytes,
|
|
239
253
|
:cache_validation, :disabled_rules,
|
|
240
254
|
:libraries, :signature_paths, :fold_platform_specific_paths, :parameter_inference,
|
|
255
|
+
:plugins_isolation,
|
|
241
256
|
:plugins_io_network, :plugins_io_allowed_paths,
|
|
242
257
|
:plugins_io_allowed_url_hosts,
|
|
243
258
|
:severity_profile, :severity_overrides,
|
|
@@ -466,7 +481,7 @@ module Rigor
|
|
|
466
481
|
|
|
467
482
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
468
483
|
#
|
|
469
|
-
# @param effects_key_present
|
|
484
|
+
# @param effects_key_present — ADR-103 WD15 — whether the file this `data` came from carried an
|
|
470
485
|
# `effects:` key at all, independent of its value. Defaults to `data.key?("effects")`, which is the
|
|
471
486
|
# right answer for a caller that passes a raw (non-`DEFAULTS`-merged) hash directly — the common shape
|
|
472
487
|
# in specs — but is always `true` once `data` has been through `DEFAULTS.merge`, because `DEFAULTS`
|
|
@@ -520,6 +535,9 @@ module Rigor
|
|
|
520
535
|
raw_max = cache.fetch("max_bytes")
|
|
521
536
|
@cache_max_bytes = raw_max.nil? ? nil : Integer(raw_max)
|
|
522
537
|
@cache_validation = coerce_cache_validation(cache.fetch("validation", "auto"))
|
|
538
|
+
@plugins_isolation = coerce_plugins_isolation(
|
|
539
|
+
data.fetch("plugins_isolation", DEFAULTS.fetch("plugins_isolation"))
|
|
540
|
+
)
|
|
523
541
|
@plugins_io_network = coerce_network_policy(plugins_io.fetch("network"))
|
|
524
542
|
@plugins_io_allowed_paths = Array(plugins_io.fetch("allowed_paths")).map(&:to_s).freeze
|
|
525
543
|
@plugins_io_allowed_url_hosts = Array(plugins_io.fetch("allowed_url_hosts")).map(&:to_s).freeze
|
|
@@ -583,6 +601,7 @@ module Rigor
|
|
|
583
601
|
"max_bytes" => cache_max_bytes,
|
|
584
602
|
"validation" => cache_validation
|
|
585
603
|
},
|
|
604
|
+
"plugins_isolation" => plugins_isolation,
|
|
586
605
|
"plugins_io" => {
|
|
587
606
|
"network" => plugins_io_network.to_s,
|
|
588
607
|
"allowed_paths" => plugins_io_allowed_paths,
|
|
@@ -626,9 +645,8 @@ module Rigor
|
|
|
626
645
|
# the same checkout: a typo is a bug, and reading it as `false` would ship the feature permanently off
|
|
627
646
|
# with no signal.
|
|
628
647
|
#
|
|
629
|
-
# @param id
|
|
630
|
-
# @
|
|
631
|
-
# @raise [ArgumentError] if `id` names no known feature.
|
|
648
|
+
# @param id — a feature id from {BleedingEdge::FEATURES} or {BleedingEdge::GRADUATED}.
|
|
649
|
+
# @raise ArgumentError — if `id` names no known feature.
|
|
632
650
|
def bleeding_edge_active?(id)
|
|
633
651
|
return true if BleedingEdge.graduated?(id)
|
|
634
652
|
unless BleedingEdge.known_id?(id)
|
|
@@ -967,6 +985,27 @@ module Rigor
|
|
|
967
985
|
VALID_CACHE_VALIDATIONS.include?(str) ? str : "auto"
|
|
968
986
|
end
|
|
969
987
|
|
|
988
|
+
# Rejects rather than fails soft — a strategy that silently resolves to a different one is exactly
|
|
989
|
+
# the failure #911 exists to close, and the run's isolation is not a detail the user can be assumed
|
|
990
|
+
# not to have meant.
|
|
991
|
+
def coerce_plugins_isolation(value)
|
|
992
|
+
return nil if value.nil?
|
|
993
|
+
|
|
994
|
+
str = value.to_s
|
|
995
|
+
return str if CONFIGURABLE_PLUGIN_ISOLATIONS.include?(str)
|
|
996
|
+
|
|
997
|
+
if str == "ruby_box"
|
|
998
|
+
raise ConfigurationError,
|
|
999
|
+
"plugins_isolation: ruby_box cannot be selected from the configuration file — `Ruby::Box` " \
|
|
1000
|
+
"must be active before Ruby boots. Run rigor with RIGOR_PLUGIN_ISOLATION=ruby_box instead " \
|
|
1001
|
+
"(the launcher re-execs itself with RUBY_BOX=1 set)."
|
|
1002
|
+
end
|
|
1003
|
+
|
|
1004
|
+
raise ConfigurationError,
|
|
1005
|
+
"plugins_isolation must be one of #{CONFIGURABLE_PLUGIN_ISOLATIONS.inspect} " \
|
|
1006
|
+
"(`ruby_box` is environment-only), got #{value.inspect}"
|
|
1007
|
+
end
|
|
1008
|
+
|
|
970
1009
|
def coerce_network_policy(value)
|
|
971
1010
|
sym = value.to_sym
|
|
972
1011
|
unless VALID_NETWORK_POLICIES.include?(sym)
|
|
@@ -30,7 +30,7 @@ module Rigor
|
|
|
30
30
|
@empty ||= new({})
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
# @param table
|
|
33
|
+
# @param table — `Configuration#effects_attribution` — method key to
|
|
34
34
|
# label list, both already shape-validated at load (tier 2).
|
|
35
35
|
def self.build(table)
|
|
36
36
|
return empty if table.nil? || table.empty?
|
|
@@ -49,8 +49,6 @@ module Rigor
|
|
|
49
49
|
|
|
50
50
|
# The labels attributed to a method key (`Net::HTTP.get`), or nil when the table says nothing about
|
|
51
51
|
# it. The key is spelled by the caller, which already builds the same string for the catalogue.
|
|
52
|
-
#
|
|
53
|
-
# @return [LabelSet, nil]
|
|
54
52
|
def [](key)
|
|
55
53
|
@rows[key]
|
|
56
54
|
end
|
|
@@ -69,9 +69,8 @@ module Rigor
|
|
|
69
69
|
|
|
70
70
|
# Resolves `Configuration#effects_envelopes` against a registry.
|
|
71
71
|
#
|
|
72
|
-
# @param entries
|
|
73
|
-
# @param registry
|
|
74
|
-
# @return [Array<Entry>]
|
|
72
|
+
# @param entries — the loaded, shape-validated entries
|
|
73
|
+
# @param registry — the vocabulary, project extensions included
|
|
75
74
|
def build(entries:, registry:)
|
|
76
75
|
entries.each_with_index.map do |entry, index|
|
|
77
76
|
labels = Array(entry["effect"]).map(&:to_s)
|
|
@@ -86,11 +85,10 @@ module Rigor
|
|
|
86
85
|
|
|
87
86
|
# The class-level envelopes the entries put on a project.
|
|
88
87
|
#
|
|
89
|
-
# @param
|
|
90
|
-
# @param
|
|
91
|
-
# @param
|
|
92
|
-
# @
|
|
93
|
-
# @return [Hash{String => Envelope}] one envelope per selected class, keyed by class name
|
|
88
|
+
# @param class_names — every class the run collected units for
|
|
89
|
+
# @param sources — `Runner#effect_sources` — `{method key => [path]}`
|
|
90
|
+
# @param project_root — what `sources` paths are relativised against
|
|
91
|
+
# @return one envelope per selected class, keyed by class name
|
|
94
92
|
def for_classes(entries:, class_names:, sources: {}, project_root: Dir.pwd)
|
|
95
93
|
return NO_ENVELOPES if entries.empty?
|
|
96
94
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "prism"
|
|
4
4
|
|
|
5
5
|
require_relative "method_key"
|
|
6
|
+
require_relative "../source/constant_path"
|
|
6
7
|
|
|
7
8
|
module Rigor
|
|
8
9
|
module Effects
|
|
@@ -30,9 +31,9 @@ module Rigor
|
|
|
30
31
|
NO_LINES = {}.freeze
|
|
31
32
|
private_constant :NO_LINES
|
|
32
33
|
|
|
33
|
-
# @param key
|
|
34
|
-
# @param path
|
|
35
|
-
# @return
|
|
34
|
+
# @param key — an effect unit key — `Tracer::Loud#emit`, `Net::HTTP.get`.
|
|
35
|
+
# @param path — the file the key was traced to.
|
|
36
|
+
# @return the `def`'s line, or nil when this file does not spell that key with a
|
|
36
37
|
# `def` — an unreadable file, a syntax error, and a synthesized method all land here.
|
|
37
38
|
def for(key:, path:)
|
|
38
39
|
index_for(path)[key]
|
|
@@ -62,10 +63,10 @@ module Rigor
|
|
|
62
63
|
def walk(node, nesting, singleton:, index:)
|
|
63
64
|
case node
|
|
64
65
|
when Prism::ModuleNode, Prism::ClassNode
|
|
65
|
-
|
|
66
|
-
return if
|
|
66
|
+
inner = header_nesting(nesting, node.constant_path)
|
|
67
|
+
return if inner.nil?
|
|
67
68
|
|
|
68
|
-
walk_children(node.body,
|
|
69
|
+
walk_children(node.body, inner, singleton: false, index: index)
|
|
69
70
|
when Prism::SingletonClassNode
|
|
70
71
|
walk_children(node.body, nesting, singleton: true, index: index)
|
|
71
72
|
when Prism::DefNode
|
|
@@ -76,6 +77,24 @@ module Rigor
|
|
|
76
77
|
end
|
|
77
78
|
end
|
|
78
79
|
|
|
80
|
+
# A ROOTED header is the one case that needs no resolution to place: `::` re-anchors the declaration at
|
|
81
|
+
# the top level wherever it is written, exactly as `Source::ConstantPath.declaration_prefix` reads it
|
|
82
|
+
# for the engine's own walks. Prism's `full_name` KEEPS the `::`, so `class ::Rooted` inside
|
|
83
|
+
# `module Outer` was indexed under `Outer::::Rooted#m` — a key no caller spells, which cost the row
|
|
84
|
+
# its line while every unrooted sibling in the same file kept one
|
|
85
|
+
# ([#722](https://github.com/rigortype/rigor/issues/722) residue 4).
|
|
86
|
+
#
|
|
87
|
+
# nil propagates the refusal {#constant_name} makes, so a header naming no constant leaves the subtree
|
|
88
|
+
# unindexed rather than under a guessed name.
|
|
89
|
+
def header_nesting(nesting, constant_path)
|
|
90
|
+
name = constant_name(constant_path)
|
|
91
|
+
return nil if name.nil?
|
|
92
|
+
|
|
93
|
+
return nesting + [name] unless Source::ConstantPath.rooted?(constant_path)
|
|
94
|
+
|
|
95
|
+
[name.delete_prefix("::")]
|
|
96
|
+
end
|
|
97
|
+
|
|
79
98
|
def walk_children(node, nesting, singleton:, index:)
|
|
80
99
|
node&.compact_child_nodes&.each { |child| walk(child, nesting, singleton: singleton, index: index) }
|
|
81
100
|
end
|
|
@@ -117,8 +117,6 @@ module Rigor
|
|
|
117
117
|
# one message: the unregistered-preset error (#433) and the note `rigor effects update` prints
|
|
118
118
|
# when `reach:` is empty (#436) have to give the same answer, and neither can hard-code a list
|
|
119
119
|
# that moves with `plugins:`.
|
|
120
|
-
#
|
|
121
|
-
# @return [String]
|
|
122
120
|
def availability
|
|
123
121
|
if @presets.empty?
|
|
124
122
|
return "no plugin in this project registers an entry-point preset — a preset is named by the " \
|
|
@@ -61,7 +61,7 @@ module Rigor
|
|
|
61
61
|
# It lives on the value rather than in {EnvelopeCheck} because {LiskovCheck} positions its
|
|
62
62
|
# findings identically, and two spellings of "where the fix goes" would eventually disagree.
|
|
63
63
|
#
|
|
64
|
-
# @return
|
|
64
|
+
# @return `[path, line]`; `[nil, 1]` for a key with no owner.
|
|
65
65
|
def for(key)
|
|
66
66
|
class_name, separator, selector = MethodKey.split(key)
|
|
67
67
|
return [nil, 1] if class_name.nil?
|
|
@@ -103,17 +103,17 @@ module Rigor
|
|
|
103
103
|
|
|
104
104
|
module_function
|
|
105
105
|
|
|
106
|
-
# @param table
|
|
107
|
-
# @param method_envelopes
|
|
108
|
-
# @param class_envelopes
|
|
109
|
-
# @param config_envelopes
|
|
106
|
+
# @param table — the run's propagated graph.
|
|
107
|
+
# @param method_envelopes — per-method envelopes, as written.
|
|
108
|
+
# @param class_envelopes — class- / module-level envelopes, to distribute.
|
|
109
|
+
# @param config_envelopes — `effects.envelopes:` entries already resolved
|
|
110
110
|
# to the classes they select ({ConfigEnvelopes.for_classes}), to distribute at the lowest precedence.
|
|
111
|
-
# @param positions
|
|
111
|
+
# @param positions — the discovery tables a finding's `def`
|
|
112
112
|
# position is read from — consulted only when a finding is built, so a deferred value's
|
|
113
113
|
# discovery force is reached exactly as often as a finding exists.
|
|
114
|
-
# @param apply_tolerated
|
|
114
|
+
# @param apply_tolerated — false judges against the undischarged-by-policy `proven` lane —
|
|
115
115
|
# the `--no-tolerated-effects` audit switch.
|
|
116
|
-
# @return
|
|
116
|
+
# @return sorted by position then key then label, so a run explains identically twice.
|
|
117
117
|
def run(table:, method_envelopes:, class_envelopes:, config_envelopes: {},
|
|
118
118
|
positions: Positions.empty, apply_tolerated: true)
|
|
119
119
|
envelopes = distribute(table, method_envelopes, class_envelopes, config_envelopes)
|
|
@@ -56,15 +56,13 @@ module Rigor
|
|
|
56
56
|
|
|
57
57
|
# Reads every stratum this index serves, once per process.
|
|
58
58
|
#
|
|
59
|
-
# @param
|
|
60
|
-
# @param plugin_facts [Rigor::Effects::PluginFacts, nil] the loaded plugins' effect contributions
|
|
59
|
+
# @param plugin_facts — the loaded plugins' effect contributions
|
|
61
60
|
# (#387); their `effect_labels:` join the vocabulary an annotation is read against, so a gem's
|
|
62
61
|
# `%a{rigor:v1:effect rails.activejob.enqueue}` resolves rather than reading as unknown.
|
|
63
|
-
# @param environment
|
|
62
|
+
# @param environment — the run's environment. Its loader supplies the
|
|
64
63
|
# rbs-inline / plugin `virtual_rbs` buffers and the built RBS environment the accepted stratum is
|
|
65
64
|
# read from; without one, both are simply absent (the fail-quiet direction — a missing `≤` bound
|
|
66
65
|
# costs precision, never a finding).
|
|
67
|
-
# @return [EnvelopeIndex]
|
|
68
66
|
def self.build(configuration:, environment: nil, plugin_facts: nil)
|
|
69
67
|
# Required here rather than at the top of the file: the reader pulls in the whole
|
|
70
68
|
# `RbsExtended` surface, and this build runs only under an `effects:` block.
|
|
@@ -122,10 +120,9 @@ module Rigor
|
|
|
122
120
|
|
|
123
121
|
# The envelope bounding `owner`'s `selector`, or nil.
|
|
124
122
|
#
|
|
125
|
-
# @param owner
|
|
126
|
-
# @param singleton
|
|
127
|
-
# @
|
|
128
|
-
# @return [Envelope, nil] never a ⊤ envelope: a bound that bounds nothing is not a bound, and
|
|
123
|
+
# @param owner — the receiver's static class name, as the typer projected it
|
|
124
|
+
# @param singleton — whether the call is `Owner.selector` rather than `Owner#selector`
|
|
125
|
+
# @return never a ⊤ envelope: a bound that bounds nothing is not a bound, and
|
|
129
126
|
# importing it would both add nothing and discharge a taint on the strength of a typo.
|
|
130
127
|
def [](owner, singleton, selector)
|
|
131
128
|
return nil if owner.nil? || empty?
|
|
@@ -76,13 +76,11 @@ module Rigor
|
|
|
76
76
|
|
|
77
77
|
# Every synthetic unit `class_name` earns, as `[key, Summary, edges]` triples.
|
|
78
78
|
#
|
|
79
|
-
# @param
|
|
80
|
-
# @param
|
|
81
|
-
# @param macros [Hash{String=>Array<String>}] receiver-less class-body calls to their literal symbol
|
|
79
|
+
# @param instance_methods — the instance methods the class body defines, in source order
|
|
80
|
+
# @param macros — receiver-less class-body calls to their literal symbol
|
|
82
81
|
# arguments, as the scanner harvested them
|
|
83
|
-
# @param uniqueness
|
|
84
|
-
# @param
|
|
85
|
-
# @param own_units [Hash{String=>Boolean}] the units the class body itself defines, keyed by the
|
|
82
|
+
# @param uniqueness — whether the class body declares a uniqueness validator
|
|
83
|
+
# @param own_units — the units the class body itself defines, keyed by the
|
|
86
84
|
# suffix a synthetic key carries (`"#save"`, `".create"`), each mapped to whether that body
|
|
87
85
|
# reaches `super`. Read by {.framework_row} and by nothing else.
|
|
88
86
|
def synthesize(class_name:, instance_methods:, macros:, uniqueness:, plugin_facts:, own_units: {})
|
|
@@ -45,10 +45,9 @@ module Rigor
|
|
|
45
45
|
# The effects identity as a hex digest — the form a store with no descriptor of its own (the ADR-46
|
|
46
46
|
# incremental snapshot) carries alongside its payload, and compares verbatim on restore.
|
|
47
47
|
#
|
|
48
|
-
# @param
|
|
49
|
-
# @param
|
|
50
|
-
# @
|
|
51
|
-
# @return [String] hex SHA-256
|
|
48
|
+
# @param registry — the vocabulary whose version participates
|
|
49
|
+
# @param catalog — the catalogue whose identity participates
|
|
50
|
+
# @return hex SHA-256
|
|
52
51
|
def digest(configuration:, registry: Registry.default, catalog: Catalog.default, plugin_facts: nil)
|
|
53
52
|
Digest::SHA256.hexdigest(
|
|
54
53
|
[
|
|
@@ -66,8 +65,7 @@ module Rigor
|
|
|
66
65
|
# the analyzed-path set — for free, so "the effects identity is the diagnostics identity plus three
|
|
67
66
|
# things" is a property of the code rather than a claim about it.
|
|
68
67
|
#
|
|
69
|
-
# @param base
|
|
70
|
-
# @return [Cache::Descriptor]
|
|
68
|
+
# @param base — the run's diagnostics key descriptor
|
|
71
69
|
def descriptor(base:, configuration:, registry: Registry.default, catalog: Catalog.default,
|
|
72
70
|
plugin_facts: nil)
|
|
73
71
|
Cache::Descriptor.compose(
|
|
@@ -49,9 +49,9 @@ module Rigor
|
|
|
49
49
|
SPELLING_PATTERN = /%a\{[^}]*\}/
|
|
50
50
|
private_constant :SPELLING_PATTERN
|
|
51
51
|
|
|
52
|
-
# @param path
|
|
53
|
-
# @param buffer
|
|
54
|
-
# @return
|
|
52
|
+
# @param path — the buffer's readable path, as {SignatureSources.source_path} renders it.
|
|
53
|
+
# @param buffer — the synthesized RBS the position was read out of.
|
|
54
|
+
# @return nil when `path` is not a Ruby file — a real `.rbs` needs no mapping,
|
|
55
55
|
# and its own line numbers are already the ones a reader can open.
|
|
56
56
|
def self.for(path:, buffer:)
|
|
57
57
|
return nil unless path.to_s.end_with?(RUBY_EXTENSION)
|
|
@@ -61,7 +61,7 @@ module Rigor
|
|
|
61
61
|
|
|
62
62
|
# One-shot form, for a caller that holds a `[path, buffer, line]` triple and no anchor.
|
|
63
63
|
#
|
|
64
|
-
# @return
|
|
64
|
+
# @return the `.rb` line, or `buffer_line` unchanged when there is nothing to map.
|
|
65
65
|
def self.ruby_line(path:, buffer:, buffer_line:, spelling: nil)
|
|
66
66
|
anchor = self.for(path: path, buffer: buffer)
|
|
67
67
|
return buffer_line if anchor.nil?
|
|
@@ -75,10 +75,10 @@ module Rigor
|
|
|
75
75
|
@ruby_lines = nil
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
# @param buffer_line
|
|
79
|
-
# @param spelling
|
|
78
|
+
# @param buffer_line — 1-based, counting into the synthesized buffer.
|
|
79
|
+
# @param spelling — the annotation's own text (`"%a{pure}"`); read off the buffer line
|
|
80
80
|
# when the caller does not already hold it.
|
|
81
|
-
# @return
|
|
81
|
+
# @return the 1-based line of the same annotation in the Ruby file.
|
|
82
82
|
def line_for(buffer_line, spelling: nil)
|
|
83
83
|
spelling = normalize(spelling) || spelling_at(buffer_line)
|
|
84
84
|
return buffer_line if spelling.nil?
|
|
@@ -37,11 +37,10 @@ module Rigor
|
|
|
37
37
|
|
|
38
38
|
# Whether reporting `token` as an unknown label is justified.
|
|
39
39
|
#
|
|
40
|
-
# @param token
|
|
41
|
-
# @param registry
|
|
40
|
+
# @param token — the spelling as written.
|
|
41
|
+
# @param registry — the vocabulary AFTER plugin load; `nil` (no
|
|
42
42
|
# vocabulary at all) makes every token unjudgeable and therefore silent.
|
|
43
|
-
# @param siblings
|
|
44
|
-
# @return [Boolean]
|
|
43
|
+
# @param siblings — the other tokens of the same list / the same config value.
|
|
45
44
|
def evident?(token, registry, siblings: [])
|
|
46
45
|
return false if registry.nil?
|
|
47
46
|
return false unless Label.valid?(token)
|