rigortype 0.2.7 → 0.2.8
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/docs/manual/04-diagnostics.md +7 -4
- data/docs/manual/06-baseline.md +5 -2
- data/docs/manual/15-type-protection-coverage.md +6 -4
- data/lib/rigor/analysis/baseline.rb +56 -96
- data/lib/rigor/analysis/buffer_binding.rb +13 -20
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
- data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
- data/lib/rigor/analysis/check_rules.rb +174 -274
- data/lib/rigor/analysis/dependency_recorder.rb +26 -38
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
- data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
- data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
- data/lib/rigor/analysis/diagnostic.rb +48 -75
- data/lib/rigor/analysis/erb_template_detector.rb +13 -18
- data/lib/rigor/analysis/fact_store.rb +12 -22
- data/lib/rigor/analysis/incremental.rb +47 -63
- data/lib/rigor/analysis/incremental_session.rb +74 -100
- data/lib/rigor/analysis/project_scan.rb +12 -23
- data/lib/rigor/analysis/result.rb +3 -5
- data/lib/rigor/analysis/rule_catalog.rb +67 -95
- data/lib/rigor/analysis/run_stats.rb +38 -61
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
- data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
- data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
- data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
- data/lib/rigor/analysis/runner.rb +220 -333
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
- data/lib/rigor/analysis/worker_session.rb +64 -102
- data/lib/rigor/ast/type_node.rb +6 -8
- data/lib/rigor/ast.rb +6 -10
- data/lib/rigor/bleeding_edge.rb +27 -35
- data/lib/rigor/builtins/hkt_builtins.rb +7 -14
- data/lib/rigor/builtins/imported_refinements.rb +106 -149
- data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
- data/lib/rigor/builtins/regex_refinement.rb +32 -43
- data/lib/rigor/builtins/static_return_refinements.rb +57 -81
- data/lib/rigor/cache/descriptor.rb +58 -94
- data/lib/rigor/cache/incremental_snapshot.rb +32 -48
- data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
- data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
- data/lib/rigor/cache/rbs_constant_table.rb +8 -13
- data/lib/rigor/cache/rbs_descriptor.rb +8 -13
- data/lib/rigor/cache/rbs_environment.rb +11 -19
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
- data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
- data/lib/rigor/cache/store.rb +96 -151
- data/lib/rigor/cli/annotate_command.rb +51 -88
- data/lib/rigor/cli/baseline_command.rb +10 -17
- data/lib/rigor/cli/check_command.rb +121 -199
- data/lib/rigor/cli/check_runner_factory.rb +8 -14
- data/lib/rigor/cli/ci_detector.rb +9 -14
- data/lib/rigor/cli/command.rb +8 -14
- data/lib/rigor/cli/coverage_command.rb +19 -26
- data/lib/rigor/cli/coverage_mutation.rb +14 -19
- data/lib/rigor/cli/coverage_report.rb +0 -2
- data/lib/rigor/cli/coverage_scan.rb +7 -11
- data/lib/rigor/cli/diagnostic_formats.rb +43 -62
- data/lib/rigor/cli/diff_command.rb +10 -20
- data/lib/rigor/cli/docs_command.rb +26 -40
- data/lib/rigor/cli/doctor_command.rb +8 -9
- data/lib/rigor/cli/explain_command.rb +4 -7
- data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
- data/lib/rigor/cli/fused_protection_report.rb +6 -8
- data/lib/rigor/cli/lsp_command.rb +11 -19
- data/lib/rigor/cli/mcp_command.rb +4 -6
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
- data/lib/rigor/cli/mutation_protection_report.rb +6 -9
- data/lib/rigor/cli/options.rb +11 -19
- data/lib/rigor/cli/plugin_command.rb +18 -30
- data/lib/rigor/cli/plugins_command.rb +29 -51
- data/lib/rigor/cli/plugins_renderer.rb +12 -20
- data/lib/rigor/cli/prism_colorizer.rb +13 -19
- data/lib/rigor/cli/protection_renderer.rb +5 -7
- data/lib/rigor/cli/protection_report.rb +27 -17
- data/lib/rigor/cli/renderable.rb +4 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
- data/lib/rigor/cli/sig_gen_command.rb +14 -26
- data/lib/rigor/cli/skill_command.rb +30 -47
- data/lib/rigor/cli/skill_describe.rb +40 -64
- data/lib/rigor/cli/trace_command.rb +9 -16
- data/lib/rigor/cli/trace_renderer.rb +35 -51
- data/lib/rigor/cli/triage_command.rb +6 -10
- data/lib/rigor/cli/triage_renderer.rb +1 -2
- data/lib/rigor/cli/type_of_command.rb +14 -23
- data/lib/rigor/cli/type_of_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_command.rb +6 -10
- data/lib/rigor/cli/type_scan_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_report.rb +2 -3
- data/lib/rigor/cli/upgrade_command.rb +2 -3
- data/lib/rigor/cli.rb +16 -28
- data/lib/rigor/config_audit.rb +35 -44
- data/lib/rigor/configuration/dependencies.rb +41 -70
- data/lib/rigor/configuration/severity_profile.rb +28 -42
- data/lib/rigor/configuration.rb +150 -241
- data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
- data/lib/rigor/environment/class_registry.rb +19 -26
- data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
- data/lib/rigor/environment/lockfile_resolver.rb +31 -47
- data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
- data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
- data/lib/rigor/environment/rbs_loader.rb +420 -518
- data/lib/rigor/environment/reflection.rb +28 -53
- data/lib/rigor/environment/reporters.rb +13 -24
- data/lib/rigor/environment.rb +176 -291
- data/lib/rigor/flow_contribution/conflict.rb +16 -27
- data/lib/rigor/flow_contribution/element.rb +7 -12
- data/lib/rigor/flow_contribution/fact.rb +33 -57
- data/lib/rigor/flow_contribution/merge_result.rb +6 -9
- data/lib/rigor/flow_contribution/merger.rb +32 -47
- data/lib/rigor/flow_contribution.rb +37 -55
- data/lib/rigor/inference/acceptance.rb +133 -219
- data/lib/rigor/inference/block_parameter_binder.rb +52 -82
- data/lib/rigor/inference/body_fixpoint.rb +22 -30
- data/lib/rigor/inference/budget_trace.rb +43 -63
- data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
- data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
- data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
- data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
- data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
- data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
- data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
- data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
- data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
- data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
- data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
- data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
- data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
- data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
- data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
- data/lib/rigor/inference/coverage_scanner.rb +13 -18
- data/lib/rigor/inference/def_return_typer.rb +8 -14
- data/lib/rigor/inference/dynamic_origin.rb +23 -20
- data/lib/rigor/inference/expression_typer.rb +774 -1152
- data/lib/rigor/inference/fallback.rb +8 -12
- data/lib/rigor/inference/fallback_tracer.rb +4 -10
- data/lib/rigor/inference/flow_tracer.rb +25 -36
- data/lib/rigor/inference/hkt_body.rb +45 -68
- data/lib/rigor/inference/hkt_body_parser.rb +33 -52
- data/lib/rigor/inference/hkt_reducer.rb +38 -59
- data/lib/rigor/inference/hkt_registry.rb +50 -76
- data/lib/rigor/inference/indexed_narrowing.rb +55 -81
- data/lib/rigor/inference/macro_block_self_type.rb +21 -34
- data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
- data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
- data/lib/rigor/inference/method_dispatcher.rb +378 -524
- data/lib/rigor/inference/method_parameter_binder.rb +81 -114
- data/lib/rigor/inference/multi_target_binder.rb +51 -68
- data/lib/rigor/inference/mutation_widening.rb +115 -158
- data/lib/rigor/inference/narrowing.rb +509 -727
- data/lib/rigor/inference/origin_lookup.rb +38 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
- data/lib/rigor/inference/precision_scanner.rb +26 -35
- data/lib/rigor/inference/project_patched_methods.rb +20 -32
- data/lib/rigor/inference/project_patched_scanner.rb +23 -37
- data/lib/rigor/inference/protection_scanner.rb +16 -18
- data/lib/rigor/inference/rbs_type_translator.rb +49 -66
- data/lib/rigor/inference/scope_indexer.rb +473 -817
- data/lib/rigor/inference/statement_evaluator.rb +706 -1111
- data/lib/rigor/inference/struct_fold_safety.rb +34 -48
- data/lib/rigor/inference/synthetic_method.rb +9 -16
- data/lib/rigor/inference/synthetic_method_index.rb +20 -35
- data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
- data/lib/rigor/language_server/buffer_resolution.rb +6 -9
- data/lib/rigor/language_server/buffer_table.rb +11 -18
- data/lib/rigor/language_server/completion_provider.rb +69 -116
- data/lib/rigor/language_server/debouncer.rb +16 -25
- data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
- data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
- data/lib/rigor/language_server/folding_range_provider.rb +11 -19
- data/lib/rigor/language_server/hover_provider.rb +8 -11
- data/lib/rigor/language_server/hover_renderer.rb +56 -93
- data/lib/rigor/language_server/loop.rb +14 -19
- data/lib/rigor/language_server/project_context.rb +46 -74
- data/lib/rigor/language_server/selection_range_provider.rb +9 -12
- data/lib/rigor/language_server/server.rb +55 -83
- data/lib/rigor/language_server/signature_help_provider.rb +29 -46
- data/lib/rigor/language_server/synchronized_writer.rb +4 -7
- data/lib/rigor/language_server/uri.rb +8 -13
- data/lib/rigor/language_server.rb +4 -6
- data/lib/rigor/mcp/loop.rb +2 -3
- data/lib/rigor/mcp/server.rb +4 -7
- data/lib/rigor/mcp.rb +3 -6
- data/lib/rigor/plugin/access_denied_error.rb +5 -8
- data/lib/rigor/plugin/additional_initializer.rb +21 -31
- data/lib/rigor/plugin/base.rb +335 -518
- data/lib/rigor/plugin/blueprint.rb +14 -23
- data/lib/rigor/plugin/box.rb +18 -29
- data/lib/rigor/plugin/fact_store.rb +16 -26
- data/lib/rigor/plugin/inflector.rb +37 -53
- data/lib/rigor/plugin/io_boundary.rb +33 -56
- data/lib/rigor/plugin/isolation.rb +42 -55
- data/lib/rigor/plugin/load_error.rb +10 -15
- data/lib/rigor/plugin/loader.rb +30 -49
- data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
- data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
- data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
- data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
- data/lib/rigor/plugin/macro.rb +10 -15
- data/lib/rigor/plugin/manifest.rb +85 -144
- data/lib/rigor/plugin/node_context.rb +14 -22
- data/lib/rigor/plugin/node_rule_walk.rb +49 -74
- data/lib/rigor/plugin/protocol_contract.rb +25 -39
- data/lib/rigor/plugin/registry.rb +132 -205
- data/lib/rigor/plugin/services.rb +21 -33
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
- data/lib/rigor/plugin/trust_policy.rb +24 -38
- data/lib/rigor/plugin/type_node_resolver.rb +15 -24
- data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
- data/lib/rigor/protection/mutation_scanner.rb +27 -35
- data/lib/rigor/protection/mutator.rb +50 -70
- data/lib/rigor/protection/test_suite_oracle.rb +20 -27
- data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
- data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
- data/lib/rigor/rbs_extended/reporter.rb +24 -40
- data/lib/rigor/rbs_extended.rb +107 -197
- data/lib/rigor/reflection.rb +68 -86
- data/lib/rigor/scope/discovery_index.rb +14 -19
- data/lib/rigor/scope.rb +255 -310
- data/lib/rigor/sig_gen/classification.rb +6 -10
- data/lib/rigor/sig_gen/generator.rb +197 -323
- data/lib/rigor/sig_gen/layout_index.rb +12 -20
- data/lib/rigor/sig_gen/method_candidate.rb +12 -17
- data/lib/rigor/sig_gen/observation_collector.rb +38 -70
- data/lib/rigor/sig_gen/observed_call.rb +13 -23
- data/lib/rigor/sig_gen/path_mapper.rb +17 -29
- data/lib/rigor/sig_gen/renderer.rb +7 -13
- data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
- data/lib/rigor/sig_gen/write_result.rb +8 -16
- data/lib/rigor/sig_gen/writer.rb +95 -174
- data/lib/rigor/sig_gen.rb +3 -6
- data/lib/rigor/signature_path_audit.rb +24 -30
- data/lib/rigor/source/constant_path.rb +10 -14
- data/lib/rigor/source/literals.rb +31 -45
- data/lib/rigor/source/node_locator.rb +9 -11
- data/lib/rigor/source/node_walker.rb +9 -13
- data/lib/rigor/source.rb +3 -4
- data/lib/rigor/testing.rb +16 -20
- data/lib/rigor/triage/catalogue.rb +38 -62
- data/lib/rigor/triage.rb +31 -52
- data/lib/rigor/trinary.rb +9 -13
- data/lib/rigor/type/acceptance_router.rb +4 -6
- data/lib/rigor/type/accepts_result.rb +10 -14
- data/lib/rigor/type/app.rb +19 -27
- data/lib/rigor/type/bot.rb +4 -6
- data/lib/rigor/type/bound_method.rb +10 -15
- data/lib/rigor/type/combinator.rb +165 -257
- data/lib/rigor/type/constant.rb +23 -34
- data/lib/rigor/type/data_class.rb +10 -15
- data/lib/rigor/type/data_instance.rb +14 -20
- data/lib/rigor/type/difference.rb +21 -32
- data/lib/rigor/type/dynamic.rb +3 -5
- data/lib/rigor/type/hash_shape.rb +32 -18
- data/lib/rigor/type/integer_range.rb +11 -16
- data/lib/rigor/type/intersection.rb +27 -42
- data/lib/rigor/type/nominal.rb +10 -15
- data/lib/rigor/type/plain_lattice.rb +9 -13
- data/lib/rigor/type/refined.rb +67 -114
- data/lib/rigor/type/singleton.rb +4 -6
- data/lib/rigor/type/struct_class.rb +11 -16
- data/lib/rigor/type/struct_instance.rb +15 -21
- data/lib/rigor/type/tuple.rb +14 -19
- data/lib/rigor/type/union.rb +30 -42
- data/lib/rigor/type_node/generic.rb +14 -26
- data/lib/rigor/type_node/identifier.rb +12 -19
- data/lib/rigor/type_node.rb +3 -12
- data/lib/rigor/value_semantics.rb +16 -21
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
- data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
- data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
- data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
- data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
- data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
- data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
- data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
- data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
- data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
- data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
- data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
- data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
- data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
- data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
- data/sig/rigor/scope.rbs +6 -0
- metadata +2 -1
|
@@ -18,14 +18,11 @@ module Rigor
|
|
|
18
18
|
class CLI
|
|
19
19
|
# Executes the `rigor type-of` command.
|
|
20
20
|
#
|
|
21
|
-
# The command is a thin probe over `Rigor::Scope#type_of`: it locates the
|
|
22
|
-
#
|
|
23
|
-
# inferred type, RBS erasure, and (optionally) the recorded fail-soft
|
|
24
|
-
# fallbacks.
|
|
21
|
+
# The command is a thin probe over `Rigor::Scope#type_of`: it locates the deepest expression at a `(file, line,
|
|
22
|
+
# column)` triple and prints the inferred type, RBS erasure, and (optionally) the recorded fail-soft fallbacks.
|
|
25
23
|
#
|
|
26
|
-
# Encapsulating the command in its own class keeps `Rigor::CLI` focused on
|
|
27
|
-
#
|
|
28
|
-
# streaming output) without bloating the CLI shell. Output formatting is
|
|
24
|
+
# Encapsulating the command in its own class keeps `Rigor::CLI` focused on dispatching and lets us evolve the
|
|
25
|
+
# type-of UX (extra flags, watch mode, streaming output) without bloating the CLI shell. Output formatting is
|
|
29
26
|
# delegated to {TypeOfRenderer}.
|
|
30
27
|
class TypeOfCommand < Command
|
|
31
28
|
USAGE = "Usage: rigor type-of [options] FILE:LINE:COL"
|
|
@@ -63,11 +60,9 @@ module Rigor
|
|
|
63
60
|
|
|
64
61
|
def execute(target:, options:, buffer: nil)
|
|
65
62
|
file, line, column = target
|
|
66
|
-
# Under editor mode the logical `file` may not exist on disk
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
# has already enforced. For non-editor mode `file` must
|
|
70
|
-
# exist.
|
|
63
|
+
# Under editor mode the logical `file` may not exist on disk (user editing a new file); the runtime check is
|
|
64
|
+
# only that the BUFFER is readable, which `resolve_buffer_binding` has already enforced. For non-editor mode
|
|
65
|
+
# `file` must exist.
|
|
71
66
|
physical = buffer ? buffer.resolve(file) : file
|
|
72
67
|
return 1 unless file_exists?(buffer ? physical : file)
|
|
73
68
|
|
|
@@ -83,12 +78,10 @@ module Rigor
|
|
|
83
78
|
tracer = options[:trace] ? Inference::FallbackTracer.new : nil
|
|
84
79
|
base_scope = Scope.empty(environment: project_environment(file, configuration))
|
|
85
80
|
|
|
86
|
-
# Build a per-node scope index so locals bound earlier in the
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
# call below), then look up the scope visible at the queried
|
|
91
|
-
# node and run the actual probe under it.
|
|
81
|
+
# Build a per-node scope index so locals bound earlier in the file flow into the scope used to type the queried
|
|
82
|
+
# node. We build the index with no tracer attached (it would otherwise double-record fallback events with the
|
|
83
|
+
# second-pass type_of call below), then look up the scope visible at the queried node and run the actual probe
|
|
84
|
+
# under it.
|
|
92
85
|
scope_index = Inference::ScopeIndexer.index(parse_result.value, default_scope: base_scope)
|
|
93
86
|
node_scope = scope_index[node]
|
|
94
87
|
|
|
@@ -99,11 +92,9 @@ module Rigor
|
|
|
99
92
|
0
|
|
100
93
|
end
|
|
101
94
|
|
|
102
|
-
# Builds a project-aware environment relative to the probed file.
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
# so probes against files outside the current process's CWD still
|
|
106
|
-
# see the right `sig/` tree.
|
|
95
|
+
# Builds a project-aware environment relative to the probed file. Project-RBS auto-detection roots at CWD today;
|
|
96
|
+
# future work will walk parent directories to find the enclosing `Gemfile`/`*.gemspec` so probes against files
|
|
97
|
+
# outside the current process's CWD still see the right `sig/` tree.
|
|
107
98
|
def project_environment(_file, configuration)
|
|
108
99
|
Environment.for_project(
|
|
109
100
|
libraries: configuration.libraries,
|
|
@@ -7,12 +7,10 @@ require_relative "renderable"
|
|
|
7
7
|
|
|
8
8
|
module Rigor
|
|
9
9
|
class CLI
|
|
10
|
-
# Renders a `TypeOfCommand::Result` as either human-readable text or a
|
|
11
|
-
# machine-readable JSON document.
|
|
10
|
+
# Renders a `TypeOfCommand::Result` as either human-readable text or a machine-readable JSON document.
|
|
12
11
|
#
|
|
13
|
-
# The renderer is a separate concern from the command itself so that future
|
|
14
|
-
#
|
|
15
|
-
# plug in without disturbing argument parsing or the inference call site.
|
|
12
|
+
# The renderer is a separate concern from the command itself so that future output formats (sexp, lsp-style hover
|
|
13
|
+
# payloads, color decoration) can plug in without disturbing argument parsing or the inference call site.
|
|
16
14
|
class TypeOfRenderer
|
|
17
15
|
include Renderable
|
|
18
16
|
|
|
@@ -16,11 +16,9 @@ module Rigor
|
|
|
16
16
|
class CLI
|
|
17
17
|
# Executes the `rigor type-scan` command.
|
|
18
18
|
#
|
|
19
|
-
# The command walks every Prism node in one or more files, runs
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# primary CI gate for tracking how much of an input source the engine can
|
|
23
|
-
# name without falling back to `Dynamic[Top]`.
|
|
19
|
+
# The command walks every Prism node in one or more files, runs `Rigor::Scope#type_of` on each, and reports
|
|
20
|
+
# per-node-class coverage of the inference engine's directly recognized classes. It is the project's primary CI gate
|
|
21
|
+
# for tracking how much of an input source the engine can name without falling back to `Dynamic[Top]`.
|
|
24
22
|
class TypeScanCommand < Command
|
|
25
23
|
USAGE = "Usage: rigor type-scan [options] PATH..."
|
|
26
24
|
|
|
@@ -78,9 +76,8 @@ module Rigor
|
|
|
78
76
|
accumulator.to_report(paths, options)
|
|
79
77
|
end
|
|
80
78
|
|
|
81
|
-
# Builds a project-aware environment that auto-detects `<cwd>/sig`
|
|
82
|
-
#
|
|
83
|
-
# `signature_paths:` keys when present.
|
|
79
|
+
# Builds a project-aware environment that auto-detects `<cwd>/sig` by default and honours the configuration's
|
|
80
|
+
# `libraries:` / `signature_paths:` keys when present.
|
|
84
81
|
def project_environment(configuration)
|
|
85
82
|
Environment.for_project(
|
|
86
83
|
libraries: configuration.libraries,
|
|
@@ -108,8 +105,7 @@ module Rigor
|
|
|
108
105
|
report.unrecognized_ratio > threshold ? 1 : 0
|
|
109
106
|
end
|
|
110
107
|
|
|
111
|
-
# Internal helper that accumulates per-file scan results into the
|
|
112
|
-
# totals carried by `Report`.
|
|
108
|
+
# Internal helper that accumulates per-file scan results into the totals carried by `Report`.
|
|
113
109
|
class ScanAccumulator
|
|
114
110
|
def initialize
|
|
115
111
|
@visits = Hash.new(0)
|
|
@@ -7,11 +7,9 @@ require_relative "renderable"
|
|
|
7
7
|
|
|
8
8
|
module Rigor
|
|
9
9
|
class CLI
|
|
10
|
-
# Renders a `TypeScanCommand::Report` as either a terminal-friendly text
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# the two formats stay in lockstep; that pairing is why this class is a
|
|
14
|
-
# bit longer than the default class-length budget.
|
|
10
|
+
# Renders a `TypeScanCommand::Report` as either a terminal-friendly text summary or a JSON document suitable for CI
|
|
11
|
+
# ingestion. Text and JSON branches share a single source of truth (the `Report` value object) so the two formats
|
|
12
|
+
# stay in lockstep; that pairing is why this class is a bit longer than the default class-length budget.
|
|
15
13
|
class TypeScanRenderer
|
|
16
14
|
include Renderable
|
|
17
15
|
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
class CLI
|
|
5
|
-
# Aggregated report assembled by `TypeScanCommand` and consumed by
|
|
6
|
-
#
|
|
7
|
-
# per-class counts, located fallback events, and any parse errors.
|
|
5
|
+
# Aggregated report assembled by `TypeScanCommand` and consumed by `TypeScanRenderer`. The struct holds per-file
|
|
6
|
+
# paths, accumulated per-class counts, located fallback events, and any parse errors.
|
|
8
7
|
class Report < Data.define(
|
|
9
8
|
:files,
|
|
10
9
|
:parse_errors,
|
|
@@ -6,9 +6,8 @@ module Rigor
|
|
|
6
6
|
class CLI
|
|
7
7
|
# `rigor upgrade` — the ADR-50 WD7 migration command skeleton.
|
|
8
8
|
#
|
|
9
|
-
# The real body lands when a concrete backwards-compatibility break
|
|
10
|
-
#
|
|
11
|
-
# strengthened default profile, surfacing renamed suppression ids,
|
|
9
|
+
# The real body lands when a concrete backwards-compatibility break gives it a target (e.g. re-running `baseline
|
|
10
|
+
# regenerate` against a strengthened default profile, surfacing renamed suppression ids,
|
|
12
11
|
# reporting `bleeding_edge:` graduations). Until then it prints the
|
|
13
12
|
# current version and notes that upgrade is queued.
|
|
14
13
|
class UpgradeCommand < Command
|
data/lib/rigor/cli.rb
CHANGED
|
@@ -14,11 +14,9 @@ require_relative "cli/diagnostic_formats"
|
|
|
14
14
|
require_relative "cli/ci_detector"
|
|
15
15
|
|
|
16
16
|
module Rigor
|
|
17
|
-
# The CLI class is a dispatcher: each `run_*` method delegates to a
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# one delegator + one help line per command, so — like the command
|
|
21
|
-
# classes it fans out to — it carries an explicit ClassLength exemption.
|
|
17
|
+
# The CLI class is a dispatcher: each `run_*` method delegates to a command-specific class once the command grows
|
|
18
|
+
# beyond a few lines (see {CLI::TypeOfCommand} and {CLI::CheckCommand}). It necessarily grows by one delegator + one
|
|
19
|
+
# help line per command, so — like the command classes it fans out to — it carries an explicit ClassLength exemption.
|
|
22
20
|
class CLI # rubocop:disable Metrics/ClassLength
|
|
23
21
|
EXIT_USAGE = 64
|
|
24
22
|
|
|
@@ -94,11 +92,9 @@ module Rigor
|
|
|
94
92
|
end
|
|
95
93
|
|
|
96
94
|
def run_init
|
|
97
|
-
# Default destination is `.rigor.dist.yml` — the
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
# alongside it (auto-discovery prefers `.rigor.yml` when
|
|
101
|
-
# both are present; no implicit merge).
|
|
95
|
+
# Default destination is `.rigor.dist.yml` — the project-default config that gets committed. Developers who want a
|
|
96
|
+
# personal override layer create `.rigor.yml` alongside it (auto-discovery prefers `.rigor.yml` when both are
|
|
97
|
+
# present; no implicit merge).
|
|
102
98
|
options = {
|
|
103
99
|
force: false,
|
|
104
100
|
path: ".rigor.dist.yml"
|
|
@@ -123,16 +119,11 @@ module Rigor
|
|
|
123
119
|
0
|
|
124
120
|
end
|
|
125
121
|
|
|
126
|
-
# `rigor init`'s template ships empty `plugins:` so a fresh
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
# the
|
|
131
|
-
# silent failures (the cwd / Gemfile / signature_paths
|
|
132
|
-
# mismatches that surfaced during the Mastodon trial) get
|
|
133
|
-
# caught the first time the user wires a plugin, not the first
|
|
134
|
-
# time `rigor check` reports false positives that should have
|
|
135
|
-
# been covered.
|
|
122
|
+
# `rigor init`'s template ships empty `plugins:` so a fresh init has nothing to validate — but the moment the user
|
|
123
|
+
# adds any plugin entry, the activation-failure surfaces enumerated in `rigor plugins`'s docstring become real.
|
|
124
|
+
# Point them at the verification command + the canonical readiness flow so silent failures (the cwd / Gemfile /
|
|
125
|
+
# signature_paths mismatches that surfaced during the Mastodon trial) get caught the first time the user wires a
|
|
126
|
+
# plugin, not the first time `rigor check` reports false positives that should have been covered.
|
|
136
127
|
def print_init_next_steps(path)
|
|
137
128
|
@out.puts ""
|
|
138
129
|
@out.puts "Next steps:"
|
|
@@ -142,10 +133,8 @@ module Rigor
|
|
|
142
133
|
@out.puts " 3. Run `rigor check` to analyse your code."
|
|
143
134
|
end
|
|
144
135
|
|
|
145
|
-
# Renders the starter `.rigor.yml` body. The template
|
|
146
|
-
#
|
|
147
|
-
# round-trips through `Configuration.load`) and prepends a
|
|
148
|
-
# short header that points the user at the keys they are
|
|
136
|
+
# Renders the starter `.rigor.yml` body. The template serialises `Configuration::DEFAULTS` (so the on-disk file
|
|
137
|
+
# round-trips through `Configuration.load`) and prepends a short header that points the user at the keys they are
|
|
149
138
|
# most likely to want to edit.
|
|
150
139
|
def init_template
|
|
151
140
|
<<~YAML
|
|
@@ -291,10 +280,9 @@ module Rigor
|
|
|
291
280
|
CLI::SkillCommand.new(argv: @argv, out: @out, err: @err).run
|
|
292
281
|
end
|
|
293
282
|
|
|
294
|
-
# `rigor describe` — a top-level alias for `rigor skill describe`,
|
|
295
|
-
# the
|
|
296
|
-
#
|
|
297
|
-
# trial saw it tried and met "Unknown command").
|
|
283
|
+
# `rigor describe` — a top-level alias for `rigor skill describe`, the entry point most users reach for first.
|
|
284
|
+
# Surfaced because a bare `rigor describe` is the intuitive guess (the onboarding field trial saw it tried and met
|
|
285
|
+
# "Unknown command").
|
|
298
286
|
def run_describe
|
|
299
287
|
require_relative "cli/skill_command"
|
|
300
288
|
|
data/lib/rigor/config_audit.rb
CHANGED
|
@@ -4,30 +4,26 @@ require_relative "signature_path_audit"
|
|
|
4
4
|
require_relative "analysis/check_rules"
|
|
5
5
|
|
|
6
6
|
module Rigor
|
|
7
|
-
# Audits a loaded {Configuration} for the class of mistake where a
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# were never written. This module surfaces each such entry up front so
|
|
16
|
-
# the cause is visible instead of inferred.
|
|
7
|
+
# Audits a loaded {Configuration} for the class of mistake where a configured value
|
|
8
|
+
# silently resolves to nothing — a typo'd or moved path, an unknown library name, an inert
|
|
9
|
+
# rule id. The shared failure mode is that the loader filters the bad entry without a word,
|
|
10
|
+
# so the only symptom is downstream and confusing: missing signatures turn every call into
|
|
11
|
+
# the types they were meant to cover into a high-confidence `call.undefined-method`, and an
|
|
12
|
+
# unrecognised suppression token leaves the rule firing as if the `disable:` line were
|
|
13
|
+
# never written. This module surfaces each such entry up front so the cause is visible
|
|
14
|
+
# instead of inferred.
|
|
17
15
|
#
|
|
18
|
-
# Every check is held to the same bar that {SignaturePathAudit} set:
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# rule
|
|
24
|
-
#
|
|
25
|
-
# disabling a plugin rule is never mistaken for a typo.
|
|
16
|
+
# Every check is held to the same bar that {SignaturePathAudit} set: it mirrors the
|
|
17
|
+
# loader's own acceptance test, so a warning means the loader really did load nothing, and
|
|
18
|
+
# it never fires on a setup that works. In particular the rule-token check only flags a
|
|
19
|
+
# token under a built-in family (`call`, `flow`, …) — a plugin- or `rbs_extended.*` rule id
|
|
20
|
+
# (whose family Rigor cannot statically enumerate, since a `node_rule` block emits any
|
|
21
|
+
# `rule:` string it likes) is left alone, so disabling a plugin rule is never mistaken for
|
|
22
|
+
# a typo.
|
|
26
23
|
module ConfigAudit
|
|
27
|
-
# One config-level finding. `kind` discriminates the source key
|
|
28
|
-
#
|
|
29
|
-
# `:
|
|
30
|
-
# `:rbs_collection_lockfile`); `fields` carries the kind-specific
|
|
24
|
+
# One config-level finding. `kind` discriminates the source key (`:signature_path`,
|
|
25
|
+
# `:library`, `:disabled_rule`, `:severity_override`, `:bundler_bundle_path`,
|
|
26
|
+
# `:bundler_lockfile`, `:rbs_collection_lockfile`); `fields` carries the kind-specific
|
|
31
27
|
# structured data merged into {#to_h} for JSON consumers.
|
|
32
28
|
Warning = Data.define(:kind, :message, :fields) do
|
|
33
29
|
def to_h
|
|
@@ -36,9 +32,8 @@ module Rigor
|
|
|
36
32
|
end
|
|
37
33
|
|
|
38
34
|
# @param configuration [Rigor::Configuration]
|
|
39
|
-
# @param project_root [String] the directory the run's relative
|
|
40
|
-
#
|
|
41
|
-
# only by the explicit-path checks.
|
|
35
|
+
# @param project_root [String] the directory the run's relative bundler / collection
|
|
36
|
+
# paths resolve against (the CLI's CWD), used only by the explicit-path checks.
|
|
42
37
|
# @return [Array<Warning>]
|
|
43
38
|
def self.warnings(configuration, project_root: Dir.pwd)
|
|
44
39
|
signature_path_warnings(configuration) +
|
|
@@ -47,9 +42,8 @@ module Rigor
|
|
|
47
42
|
explicit_path_warnings(configuration, project_root)
|
|
48
43
|
end
|
|
49
44
|
|
|
50
|
-
# `signature_paths:` entries that resolve to nothing — delegated to
|
|
51
|
-
#
|
|
52
|
-
# recursive `**/*.rbs` acceptance test.
|
|
45
|
+
# `signature_paths:` entries that resolve to nothing — delegated to {SignaturePathAudit},
|
|
46
|
+
# which mirrors the loader's `directory?` + recursive `**/*.rbs` acceptance test.
|
|
53
47
|
def self.signature_path_warnings(configuration)
|
|
54
48
|
SignaturePathAudit.warnings(configuration.signature_paths).map do |entry|
|
|
55
49
|
Warning.new(
|
|
@@ -61,10 +55,9 @@ module Rigor
|
|
|
61
55
|
end
|
|
62
56
|
|
|
63
57
|
# `libraries:` entries RBS does not recognise. Uses the same
|
|
64
|
-
# `RBS::EnvironmentLoader#has_library?` guard the loader filters
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
# raises, no warning rather than a crash.
|
|
58
|
+
# `RBS::EnvironmentLoader#has_library?` guard the loader filters through
|
|
59
|
+
# ({Environment::RbsLoader} `build_env_for`), so a flagged name is exactly one the loader
|
|
60
|
+
# skipped. Fails soft: if RBS itself raises, no warning rather than a crash.
|
|
68
61
|
def self.library_warnings(configuration)
|
|
69
62
|
configured = Array(configuration.libraries)
|
|
70
63
|
return [] if configured.empty?
|
|
@@ -81,9 +74,9 @@ module Rigor
|
|
|
81
74
|
[]
|
|
82
75
|
end
|
|
83
76
|
|
|
84
|
-
# `disable:` tokens and `severity_overrides:` keys that name no rule.
|
|
85
|
-
#
|
|
86
|
-
#
|
|
77
|
+
# `disable:` tokens and `severity_overrides:` keys that name no rule. Restricted to
|
|
78
|
+
# tokens under a built-in family so a plugin rule id is never mis-flagged (see the module
|
|
79
|
+
# docstring).
|
|
87
80
|
def self.rule_token_warnings(configuration)
|
|
88
81
|
disable = Array(configuration.disabled_rules).filter_map do |token|
|
|
89
82
|
next unless inert_builtin_token?(token.to_s)
|
|
@@ -106,11 +99,10 @@ module Rigor
|
|
|
106
99
|
disable + overrides
|
|
107
100
|
end
|
|
108
101
|
|
|
109
|
-
# True when `token` looks like a built-in rule id but matches none —
|
|
110
|
-
#
|
|
111
|
-
# family
|
|
112
|
-
#
|
|
113
|
-
# alias) is deliberately NOT flagged: it may resolve at run time, so
|
|
102
|
+
# True when `token` looks like a built-in rule id but matches none — its first segment
|
|
103
|
+
# is a built-in family yet it is neither a bare family wildcard nor a known canonical id.
|
|
104
|
+
# A token whose family is not built-in (a plugin / `rbs_extended.*` rule, or a bare
|
|
105
|
+
# legacy alias) is deliberately NOT flagged: it may resolve at run time, so
|
|
114
106
|
# under-warning is the FP-safe choice.
|
|
115
107
|
def self.inert_builtin_token?(token)
|
|
116
108
|
family = token.split(".").first
|
|
@@ -121,10 +113,9 @@ module Rigor
|
|
|
121
113
|
true
|
|
122
114
|
end
|
|
123
115
|
|
|
124
|
-
# Explicitly-configured bundler / rbs-collection paths that do not
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
# factual about the path rather than guessing the fallback.
|
|
116
|
+
# Explicitly-configured bundler / rbs-collection paths that do not exist. Only the
|
|
117
|
+
# explicit form is audited (a nil value means auto-detection, which finding nothing is
|
|
118
|
+
# normal); messages stay factual about the path rather than guessing the fallback.
|
|
128
119
|
def self.explicit_path_warnings(configuration, project_root)
|
|
129
120
|
warnings = []
|
|
130
121
|
add_missing_dir(warnings, configuration.bundler_bundle_path, project_root,
|
|
@@ -3,62 +3,47 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
class Configuration
|
|
5
5
|
# Parsed `dependencies:` section of `.rigor.yml`. Per
|
|
6
|
-
# [ADR-10](../../../docs/adr/10-dependency-source-inference.md),
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# inference instead of degrading to `Dynamic[top]` at the
|
|
10
|
-
# dependency boundary.
|
|
6
|
+
# [ADR-10](../../../docs/adr/10-dependency-source-inference.md), the only nested key today is
|
|
7
|
+
# `source_inference:`, listing gems whose Ruby implementation Rigor MAY walk during inference instead of
|
|
8
|
+
# degrading to `Dynamic[top]` at the dependency boundary.
|
|
11
9
|
#
|
|
12
|
-
# Parser for the `dependencies:` YAML section; consumed by
|
|
13
|
-
# `Analysis::DependencySourceInference` (ADR-10).
|
|
10
|
+
# Parser for the `dependencies:` YAML section; consumed by `Analysis::DependencySourceInference` (ADR-10).
|
|
14
11
|
class Dependencies
|
|
15
|
-
# Walking modes per
|
|
16
|
-
#
|
|
12
|
+
# Walking modes per [ADR-10 §
|
|
13
|
+
# "Decision"](../../../docs/adr/10-dependency-source-inference.md#decision).
|
|
17
14
|
VALID_MODES = %i[disabled when_missing full].freeze
|
|
18
15
|
|
|
19
|
-
# Default `roots:` for an entry that does not supply one.
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# parser — see ADR-10 § "Hard exclusions".
|
|
16
|
+
# Default `roots:` for an entry that does not supply one. The hard-excluded directories (`spec/` /
|
|
17
|
+
# `test/` / `bin/` / C extensions) are enforced by the walker, not the parser — see ADR-10 § "Hard
|
|
18
|
+
# exclusions".
|
|
23
19
|
DEFAULT_ROOTS = %w[lib].freeze
|
|
24
20
|
|
|
25
|
-
# Default per-gem catalog cap. ADR-10 slice 4 picks
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# ActiveSupport-class libraries (~10 000+ methods) where
|
|
30
|
-
# the user should ship RBS or de-list the gem instead.
|
|
21
|
+
# Default per-gem catalog cap. ADR-10 slice 4 picks 5000 method definitions: it covers Rack (~1500),
|
|
22
|
+
# Faraday (~500), Sidekiq (~800) and other realistic opt-in targets, while still surfacing a diagnostic
|
|
23
|
+
# for ActiveSupport-class libraries (~10 000+ methods) where the user should ship RBS or de-list the gem
|
|
24
|
+
# instead.
|
|
31
25
|
DEFAULT_BUDGET_PER_GEM = 5000
|
|
32
26
|
|
|
33
|
-
# Range bounds per ADR-10 § "Budget interaction"
|
|
34
|
-
# ("range 0.25× – 4×"). Configured against the default,
|
|
27
|
+
# Range bounds per ADR-10 § "Budget interaction" ("range 0.25× – 4×"). Configured against the default,
|
|
35
28
|
# this lands at 1250 – 20 000.
|
|
36
29
|
MIN_BUDGET_PER_GEM = (DEFAULT_BUDGET_PER_GEM * 0.25).to_i
|
|
37
30
|
MAX_BUDGET_PER_GEM = (DEFAULT_BUDGET_PER_GEM * 4).to_i
|
|
38
31
|
|
|
39
32
|
# ADR-10 5b — budget-overrun strategy enum.
|
|
40
33
|
#
|
|
41
|
-
# - `:walker_cap` (default): the (α) semantics. The
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
# When the receiver's class belongs to a budget-
|
|
49
|
-
# exceeded gem, the call resolves to `Dynamic[top]`
|
|
50
|
-
# rather than falling through to user-class fallback.
|
|
51
|
-
# This silences `call.undefined-method` for unrecorded
|
|
52
|
-
# methods at the cost of weaker static checking on
|
|
53
|
-
# that gem's surface.
|
|
34
|
+
# - `:walker_cap` (default): the (α) semantics. The walker stops harvesting at the cap; methods past
|
|
35
|
+
# the cap fall through to the existing user-class fallback path. Existing v0.1.3 behaviour.
|
|
36
|
+
# - `:dependency_silence`: the (β) semantics. Same walker behaviour, but the dispatcher additionally
|
|
37
|
+
# consults `Index#class_to_gem` after a catalog miss. When the receiver's class belongs to a
|
|
38
|
+
# budget-exceeded gem, the call resolves to `Dynamic[top]` rather than falling through to user-class
|
|
39
|
+
# fallback. This silences `call.undefined-method` for unrecorded methods at the cost of weaker static
|
|
40
|
+
# checking on that gem's surface.
|
|
54
41
|
VALID_BUDGET_OVERRUN_STRATEGIES = %i[walker_cap dependency_silence].freeze
|
|
55
42
|
DEFAULT_BUDGET_OVERRUN_STRATEGY = :walker_cap
|
|
56
43
|
|
|
57
|
-
# Frozen value object describing a single per-gem opt-in.
|
|
58
|
-
# `
|
|
59
|
-
#
|
|
60
|
-
# the list of subdirectories within the gem's installation
|
|
61
|
-
# directory to walk (defaults to `["lib"]`).
|
|
44
|
+
# Frozen value object describing a single per-gem opt-in. `gem:` is the gem name (matched against the
|
|
45
|
+
# bundle at walk time); `mode:` is one of {VALID_MODES}; `roots:` is the list of subdirectories within
|
|
46
|
+
# the gem's installation directory to walk (defaults to `["lib"]`).
|
|
62
47
|
class Entry < Data.define(:gem, :mode, :roots)
|
|
63
48
|
def disabled? = mode == :disabled
|
|
64
49
|
def when_missing? = mode == :when_missing
|
|
@@ -67,10 +52,8 @@ module Rigor
|
|
|
67
52
|
|
|
68
53
|
attr_reader :source_inference, :budget_per_gem, :budget_overrun_strategy, :warnings
|
|
69
54
|
|
|
70
|
-
# Parse the YAML-shaped `dependencies:` value into a
|
|
71
|
-
#
|
|
72
|
-
# `source_inference:` and / or `budget_per_gem:` /
|
|
73
|
-
# `budget_overrun_strategy:` present.
|
|
55
|
+
# Parse the YAML-shaped `dependencies:` value into a frozen {Dependencies}. Accepts `nil` / `{}` / a
|
|
56
|
+
# Hash with `source_inference:` and / or `budget_per_gem:` / `budget_overrun_strategy:` present.
|
|
74
57
|
def self.from_h(data)
|
|
75
58
|
return new([]) if data.nil?
|
|
76
59
|
raise ArgumentError, "dependencies: must be a Hash, got #{data.inspect}" unless data.is_a?(Hash)
|
|
@@ -110,25 +93,17 @@ module Rigor
|
|
|
110
93
|
def empty? = @source_inference.empty?
|
|
111
94
|
|
|
112
95
|
class << self
|
|
113
|
-
# ADR-10 § "config-conflict diagnostic" — merges a
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
# `Configuration.deep_merge`'s special-case for
|
|
117
|
-
# `dependencies.source_inference`) into a single
|
|
118
|
-
# canonical entry per gem name. The merge rules:
|
|
96
|
+
# ADR-10 § "config-conflict diagnostic" — merges a potentially-duplicated entry list (the `includes:`
|
|
97
|
+
# chain produces concatenated arrays via `Configuration.deep_merge`'s special-case for
|
|
98
|
+
# `dependencies.source_inference`) into a single canonical entry per gem name. The merge rules:
|
|
119
99
|
#
|
|
120
|
-
# - Same gem, same all fields → idempotent collapse
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
# AND emit a `:warning` so the user knows their
|
|
125
|
-
# `includes:` chain is ambiguous.
|
|
126
|
-
# - Same gem, different `roots:` → union the roots
|
|
127
|
-
# silently (no warning). The walker is happy to
|
|
100
|
+
# - Same gem, same all fields → idempotent collapse (no warning).
|
|
101
|
+
# - Same gem, different `mode:` → keep the LAST entry (matches existing right-wins semantics
|
|
102
|
+
# elsewhere) AND emit a `:warning` so the user knows their `includes:` chain is ambiguous.
|
|
103
|
+
# - Same gem, different `roots:` → union the roots silently (no warning). The walker is happy to
|
|
128
104
|
# visit the union.
|
|
129
105
|
#
|
|
130
|
-
# Returns `[entries, warnings]` so the caller can
|
|
131
|
-
# plumb the warning list through to the Runner for
|
|
106
|
+
# Returns `[entries, warnings]` so the caller can plumb the warning list through to the Runner for
|
|
132
107
|
# diagnostic emission.
|
|
133
108
|
def dedupe_entries(entries)
|
|
134
109
|
warnings = []
|
|
@@ -156,11 +131,10 @@ module Rigor
|
|
|
156
131
|
|
|
157
132
|
private
|
|
158
133
|
|
|
159
|
-
# `source_inference:` is a list of per-gem entries, or `false` /
|
|
160
|
-
#
|
|
161
|
-
#
|
|
162
|
-
#
|
|
163
|
-
# reasonable "off" config (`dependencies: { source_inference: false }`).
|
|
134
|
+
# `source_inference:` is a list of per-gem entries, or `false` / omitted to disable it (the default).
|
|
135
|
+
# Guard the Ruby `Array(false) == [false]` quirk that would otherwise feed `false` straight into
|
|
136
|
+
# coerce_entry and crash the whole run on a perfectly reasonable "off" config
|
|
137
|
+
# (`dependencies: { source_inference: false }`).
|
|
164
138
|
def coerce_source_inference(value)
|
|
165
139
|
return [] if value.nil? || value == false
|
|
166
140
|
|
|
@@ -222,12 +196,9 @@ module Rigor
|
|
|
222
196
|
"#{VALID_BUDGET_OVERRUN_STRATEGIES.inspect}, got #{value.inspect}"
|
|
223
197
|
end
|
|
224
198
|
|
|
225
|
-
# ADR-10 slice 4. Per-gem catalog cap is mandatory
|
|
226
|
-
#
|
|
227
|
-
#
|
|
228
|
-
# `budget_per_gem: ~`). Range bounds match
|
|
229
|
-
# MIN_BUDGET_PER_GEM .. MAX_BUDGET_PER_GEM
|
|
230
|
-
# (i.e. 0.25× – 4× of the default).
|
|
199
|
+
# ADR-10 slice 4. Per-gem catalog cap is mandatory (the parser supplies the default before this is
|
|
200
|
+
# called, so `nil` only reaches here on an explicit `budget_per_gem: ~`). Range bounds match
|
|
201
|
+
# MIN_BUDGET_PER_GEM .. MAX_BUDGET_PER_GEM (i.e. 0.25× – 4× of the default).
|
|
231
202
|
def coerce_budget_per_gem(value)
|
|
232
203
|
unless value.is_a?(Integer)
|
|
233
204
|
raise ArgumentError,
|