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
|
@@ -11,8 +11,8 @@ require_relative "command"
|
|
|
11
11
|
|
|
12
12
|
module Rigor
|
|
13
13
|
class CLI
|
|
14
|
-
# ADR-22 — `rigor baseline {generate,regenerate,dump,drift,
|
|
15
|
-
#
|
|
14
|
+
# ADR-22 — `rigor baseline {generate,regenerate,dump,drift, prune}` subcommands, backed by
|
|
15
|
+
# `Rigor::Analysis::Baseline`.
|
|
16
16
|
#
|
|
17
17
|
# rigor baseline generate # default: rule-ID rows
|
|
18
18
|
# rigor baseline generate --match-mode message
|
|
@@ -64,12 +64,9 @@ module Rigor
|
|
|
64
64
|
write_baseline(options, verb: "wrote baseline to")
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
# ADR-22 slice 5 — `regenerate` is `generate --force`: the
|
|
68
|
-
#
|
|
69
|
-
# baseline
|
|
70
|
-
# unconditionally (no existence guard, no `--force` flag),
|
|
71
|
-
# so `rigor baseline regenerate` reads cleanly as "make the
|
|
72
|
-
# baseline match reality again".
|
|
67
|
+
# ADR-22 slice 5 — `regenerate` is `generate --force`: the end-of-quality-improvement-session refresh after
|
|
68
|
+
# landing baseline-reducing fixes. It rewrites the file unconditionally (no existence guard, no `--force` flag),
|
|
69
|
+
# so `rigor baseline regenerate` reads cleanly as "make the baseline match reality again".
|
|
73
70
|
def run_regenerate
|
|
74
71
|
options = parse_generate_options(subcommand: "regenerate")
|
|
75
72
|
write_baseline(options, verb: "regenerated baseline")
|
|
@@ -126,10 +123,8 @@ module Rigor
|
|
|
126
123
|
|
|
127
124
|
def collect_diagnostics(configuration, _options)
|
|
128
125
|
cache_store = Cache::Store.new(root: configuration.cache_path)
|
|
129
|
-
# IMPORTANT: do NOT activate the existing baseline when
|
|
130
|
-
#
|
|
131
|
-
# records the post-filter (silenced) diagnostic set,
|
|
132
|
-
# which is empty after a successful first run.
|
|
126
|
+
# IMPORTANT: do NOT activate the existing baseline when generating a fresh one — otherwise the new file records
|
|
127
|
+
# the post-filter (silenced) diagnostic set, which is empty after a successful first run.
|
|
133
128
|
configuration_for_generation = override_configuration_baseline_off(configuration)
|
|
134
129
|
runner = Analysis::Runner.new(
|
|
135
130
|
configuration: configuration_for_generation,
|
|
@@ -140,9 +135,8 @@ module Rigor
|
|
|
140
135
|
end
|
|
141
136
|
|
|
142
137
|
def override_configuration_baseline_off(configuration)
|
|
143
|
-
# Synthesise a new Configuration with `baseline` explicitly
|
|
144
|
-
#
|
|
145
|
-
# round-trip through the constructor with an override hash.
|
|
138
|
+
# Synthesise a new Configuration with `baseline` explicitly disabled. The original Configuration is frozen-ish
|
|
139
|
+
# so we round-trip through the constructor with an override hash.
|
|
146
140
|
defaults = Configuration::DEFAULTS.merge(
|
|
147
141
|
"paths" => configuration.paths,
|
|
148
142
|
"exclude" => configuration.exclude_patterns,
|
|
@@ -206,8 +200,7 @@ module Rigor
|
|
|
206
200
|
return
|
|
207
201
|
end
|
|
208
202
|
|
|
209
|
-
# Group by rule for readability; rules with the most
|
|
210
|
-
# entries first.
|
|
203
|
+
# Group by rule for readability; rules with the most entries first.
|
|
211
204
|
by_rule = rows.group_by(&:rule).sort_by { |_rule, group| -group.size }
|
|
212
205
|
by_rule.each do |rule, group|
|
|
213
206
|
total = group.sum(&:count)
|
|
@@ -19,17 +19,14 @@ module Rigor
|
|
|
19
19
|
class CLI
|
|
20
20
|
# Executes `rigor check` — the analyzer's primary command.
|
|
21
21
|
#
|
|
22
|
-
# The other subcommands delegate to a `CLI::Command` subclass once they
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# it in its own class follows the same dispatch-vs-implementation split
|
|
28
|
-
# the rest of the CLI uses and keeps `Rigor::CLI` focused on dispatch.
|
|
22
|
+
# The other subcommands delegate to a `CLI::Command` subclass once they grow beyond a few lines; `check` is the
|
|
23
|
+
# largest of them, owning option parsing, the baseline filter (ADR-22), the incremental modes (ADR-46), cache-stats
|
|
24
|
+
# reporting, editor mode, the CI-native output formats (ADR-51), and the diagnostic-only / heap / budget appendices.
|
|
25
|
+
# Keeping it in its own class follows the same dispatch-vs-implementation split the rest of the CLI uses and keeps
|
|
26
|
+
# `Rigor::CLI` focused on dispatch.
|
|
29
27
|
#
|
|
30
|
-
# The class-length budget is relaxed (as on `Rigor::CLI` itself) because
|
|
31
|
-
#
|
|
32
|
-
# together than split across micro-classes.
|
|
28
|
+
# The class-length budget is relaxed (as on `Rigor::CLI` itself) because `check` aggregates several independent
|
|
29
|
+
# concerns that are clearer read together than split across micro-classes.
|
|
33
30
|
class CheckCommand < Command # rubocop:disable Metrics/ClassLength
|
|
34
31
|
# @return [Integer] CLI exit status.
|
|
35
32
|
def run # rubocop:disable Metrics/AbcSize
|
|
@@ -69,9 +66,8 @@ module Rigor
|
|
|
69
66
|
|
|
70
67
|
private
|
|
71
68
|
|
|
72
|
-
# ADR-46 — the two incremental-analysis check modes both fully handle
|
|
73
|
-
#
|
|
74
|
-
# returns nil for an ordinary check.
|
|
69
|
+
# ADR-46 — the two incremental-analysis check modes both fully handle the run and return an exit code (so `run`
|
|
70
|
+
# short-circuits); returns nil for an ordinary check.
|
|
75
71
|
def dispatch_special_check_mode(configuration, options, cache_root)
|
|
76
72
|
return run_verify_incremental(configuration) if options.fetch(:verify_incremental)
|
|
77
73
|
return run_incremental_check(configuration, options, cache_root) if options.fetch(:incremental)
|
|
@@ -79,22 +75,19 @@ module Rigor
|
|
|
79
75
|
nil
|
|
80
76
|
end
|
|
81
77
|
|
|
82
|
-
# ADR-46 — the incremental-analysis acceptance gate. Runs a baseline
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
# incremental machinery would serve a stale — manufactured —
|
|
88
|
-
# diagnostic, the soundness failure this gate exists to catch. Prints a
|
|
89
|
-
# one-line PASS (exit 0) or the differing diagnostics (exit 1).
|
|
78
|
+
# ADR-46 — the incremental-analysis acceptance gate. Runs a baseline analysis (recording cross-file dependencies),
|
|
79
|
+
# then re-analyzes a representative subset of files and serves the rest from the per-file cache (the body tier),
|
|
80
|
+
# and asserts the merged diagnostics are byte-identical to a full `--no-cache` analysis. A mismatch means the
|
|
81
|
+
# incremental machinery would serve a stale — manufactured — diagnostic, the soundness failure this gate exists to
|
|
82
|
+
# catch. Prints a one-line PASS (exit 0) or the differing diagnostics (exit 1).
|
|
90
83
|
def run_verify_incremental(configuration)
|
|
91
84
|
paths = @argv.empty? ? nil : @argv
|
|
92
85
|
session = Analysis::IncrementalSession.new(configuration: configuration, paths: paths)
|
|
93
86
|
session.baseline
|
|
94
87
|
analyzed = session.analyzed_files
|
|
95
88
|
|
|
96
|
-
# Every other file forms the re-analyzed subset, so the run exercises
|
|
97
|
-
#
|
|
89
|
+
# Every other file forms the re-analyzed subset, so the run exercises BOTH the subset-analysis path and the
|
|
90
|
+
# cache-serving path.
|
|
98
91
|
subset = analyzed.each_with_index.select { |_, index| index.even? }.map(&:first)
|
|
99
92
|
incremental = normalize_diagnostics(session.reanalyze_subset(subset))
|
|
100
93
|
full = normalize_diagnostics(verify_full_diagnostics(configuration, paths))
|
|
@@ -102,14 +95,11 @@ module Rigor
|
|
|
102
95
|
report_verify_incremental(incremental, full, subset_size: subset.size, total: analyzed.size)
|
|
103
96
|
end
|
|
104
97
|
|
|
105
|
-
# ADR-46 — cross-process incremental analysis (`--incremental`). Derives
|
|
106
|
-
# the
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
# snapshot for the next invocation. Diagnostics are identical to a full
|
|
111
|
-
# run (the `--verify-incremental` gate enforces this); the win is
|
|
112
|
-
# skipping per-file inference for unchanged files.
|
|
98
|
+
# ADR-46 — cross-process incremental analysis (`--incremental`). Derives the global fingerprint cheaply (no RBS
|
|
99
|
+
# env build), loads the disk snapshot, and on a fingerprint hit re-analyzes only the files changed since the last
|
|
100
|
+
# run (plus their dependents), serving the rest from the snapshot; on a miss runs a full baseline. Persists the
|
|
101
|
+
# updated snapshot for the next invocation. Diagnostics are identical to a full run (the `--verify-incremental`
|
|
102
|
+
# gate enforces this); the win is skipping per-file inference for unchanged files.
|
|
113
103
|
def run_incremental_check(configuration, options, cache_root)
|
|
114
104
|
paths = @argv.empty? ? nil : @argv
|
|
115
105
|
probe = Analysis::Runner.new(configuration: configuration, cache_store: nil)
|
|
@@ -159,14 +149,11 @@ module Rigor
|
|
|
159
149
|
1
|
|
160
150
|
end
|
|
161
151
|
|
|
162
|
-
# ADR-22 slice 5 — the `--baseline-strict` CI gate. When the
|
|
163
|
-
#
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
# (
|
|
167
|
-
# code and should be regenerated). A no-op, with a stderr
|
|
168
|
-
# note, when no baseline is active — the flag never
|
|
169
|
-
# implicitly loads a baseline the config did not name (WD2).
|
|
152
|
+
# ADR-22 slice 5 — the `--baseline-strict` CI gate. When the flag is set, ANY baseline drift fails the run — not
|
|
153
|
+
# only excess drift (a bucket over threshold, which already fails via the surfaced diagnostics) but also DEFICIT
|
|
154
|
+
# drift (`actual < count`: the baseline has grown looser than the code and should be regenerated). A no-op, with a
|
|
155
|
+
# stderr note, when no baseline is active — the flag never implicitly loads a baseline the config did not name
|
|
156
|
+
# (WD2).
|
|
170
157
|
def baseline_strict_violation?(raw_diagnostics, configuration, options)
|
|
171
158
|
return false unless options.fetch(:baseline_strict)
|
|
172
159
|
|
|
@@ -199,22 +186,17 @@ module Rigor
|
|
|
199
186
|
@err.puts("rigor: run `rigor baseline regenerate` to refresh the baseline.")
|
|
200
187
|
end
|
|
201
188
|
|
|
202
|
-
# ADR-22 — apply the baseline filter as the LAST step of
|
|
203
|
-
#
|
|
204
|
-
# `severity_profile`, etc. — WD6). Resolution order
|
|
205
|
-
# follows WD2 (b):
|
|
189
|
+
# ADR-22 — apply the baseline filter as the LAST step of the diagnostic pipeline (after `# rigor:disable`,
|
|
190
|
+
# `severity_profile`, etc. — WD6). Resolution order follows WD2 (b):
|
|
206
191
|
#
|
|
207
192
|
# 1. --no-baseline on the CLI → no baseline.
|
|
208
193
|
# 2. --baseline=PATH on the CLI → load that path.
|
|
209
194
|
# 3. .rigor.yml's `baseline: <path>` → load that path.
|
|
210
195
|
# 4. otherwise → no baseline.
|
|
211
196
|
#
|
|
212
|
-
# When the path resolves and loads successfully, the filter
|
|
213
|
-
#
|
|
214
|
-
#
|
|
215
|
-
# diagnostics were silenced. Load failures emit a warning
|
|
216
|
-
# to stderr and fall through to "no baseline" (graceful
|
|
217
|
-
# degradation).
|
|
197
|
+
# When the path resolves and loads successfully, the filter replaces `result.diagnostics` with the surfaced set
|
|
198
|
+
# and writes a one-line summary to stderr (WD7) when any diagnostics were silenced. Load failures emit a warning
|
|
199
|
+
# to stderr and fall through to "no baseline" (graceful degradation).
|
|
218
200
|
def apply_baseline_filter(result, configuration, options)
|
|
219
201
|
path = resolve_baseline_path(configuration, options)
|
|
220
202
|
return result if path.nil?
|
|
@@ -260,60 +242,42 @@ module Rigor
|
|
|
260
242
|
clear_cache: false,
|
|
261
243
|
no_cache: false,
|
|
262
244
|
stats: true,
|
|
263
|
-
# ADR-15 Phase 4c — when nil, falls back to
|
|
264
|
-
# `
|
|
265
|
-
# `parallel.workers:` then 0 (sequential). See
|
|
266
|
-
# `resolve_workers` for the precedence chain.
|
|
245
|
+
# ADR-15 Phase 4c — when nil, falls back to `RIGOR_RACTOR_WORKERS` then `.rigor.yml` `parallel.workers:` then
|
|
246
|
+
# 0 (sequential). See `resolve_workers` for the precedence chain.
|
|
267
247
|
workers: nil,
|
|
268
248
|
tmp_file: nil,
|
|
269
249
|
instead_of: nil,
|
|
270
|
-
# ADR-22 — baseline filter. `:unset` means "fall through
|
|
271
|
-
#
|
|
272
|
-
# the config; `false` (from `--no-baseline`) suppresses
|
|
273
|
-
# any baseline that the config might name.
|
|
250
|
+
# ADR-22 — baseline filter. `:unset` means "fall through to `.rigor.yml`'s `baseline:` key"; a String
|
|
251
|
+
# overrides the config; `false` (from `--no-baseline`) suppresses any baseline that the config might name.
|
|
274
252
|
baseline: :unset,
|
|
275
|
-
# ADR-22 slice 5 — `--baseline-strict` CI gate: fail the
|
|
276
|
-
# run on any baseline drift, in either direction.
|
|
253
|
+
# ADR-22 slice 5 — `--baseline-strict` CI gate: fail the run on any baseline drift, in either direction.
|
|
277
254
|
baseline_strict: false,
|
|
278
|
-
# ADR-32 WD10 carry-over — `--treat-all-as-inline-rbs`
|
|
279
|
-
#
|
|
280
|
-
#
|
|
281
|
-
#
|
|
282
|
-
# analysed file as inline-RBS without the user editing
|
|
283
|
-
# `.rigor.yml`. Intended for single-file / ad-hoc CI use;
|
|
284
|
-
# ordinary projects should configure the plugin in
|
|
285
|
-
# `.rigor.yml`.
|
|
255
|
+
# ADR-32 WD10 carry-over — `--treat-all-as-inline-rbs` forces the `rigor-rbs-inline` plugin into the loaded
|
|
256
|
+
# plugin set with `require_magic_comment: false` so a single ad-hoc `rigor check` invocation treats every
|
|
257
|
+
# analysed file as inline-RBS without the user editing `.rigor.yml`. Intended for single-file / ad-hoc CI use;
|
|
258
|
+
# ordinary projects should configure the plugin in `.rigor.yml`.
|
|
286
259
|
treat_all_as_inline_rbs: false,
|
|
287
|
-
# ADR-46 — the incremental-analysis acceptance gate. Runs a
|
|
288
|
-
#
|
|
289
|
-
#
|
|
290
|
-
# byte-identical to a full `--no-cache` run. Exits non-zero on any
|
|
291
|
-
# mismatch. Off by default.
|
|
260
|
+
# ADR-46 — the incremental-analysis acceptance gate. Runs a baseline analysis, re-analyzes a subset and serves
|
|
261
|
+
# the rest from the per-file cache, and asserts the merged diagnostics are byte-identical to a full
|
|
262
|
+
# `--no-cache` run. Exits non-zero on any mismatch. Off by default.
|
|
292
263
|
verify_incremental: false,
|
|
293
|
-
# ADR-46 — cross-process incremental analysis. With a disk snapshot
|
|
294
|
-
#
|
|
295
|
-
#
|
|
296
|
-
# snapshot. Off by default.
|
|
264
|
+
# ADR-46 — cross-process incremental analysis. With a disk snapshot of the prior run's per-file diagnostics +
|
|
265
|
+
# dependency graph, re-analyzes only the changed closure and serves the rest from the snapshot. Off by
|
|
266
|
+
# default.
|
|
297
267
|
incremental: false,
|
|
298
|
-
# ADR-51 WD7 — CI auto-detection. When the default `text` format is
|
|
299
|
-
#
|
|
300
|
-
#
|
|
301
|
-
# the human output; for GitLab / reviewdog-routed CIs, print a
|
|
302
|
-
# one-line hint. On by default; `--no-ci-detect` (or
|
|
268
|
+
# ADR-51 WD7 — CI auto-detection. When the default `text` format is in effect and a first-class CI is detected
|
|
269
|
+
# (GitHub Actions / TeamCity), also emit that platform's native annotations on top of the human output; for
|
|
270
|
+
# GitLab / reviewdog-routed CIs, print a one-line hint. On by default; `--no-ci-detect` (or
|
|
303
271
|
# `RIGOR_CI_DETECT=0`) disables it.
|
|
304
272
|
ci_detect: true,
|
|
305
|
-
# ADR-50 § WD2 — the `--bleeding-edge[=ids]` / `--no-bleeding-edge`
|
|
306
|
-
#
|
|
307
|
-
#
|
|
308
|
-
# overlay, `false` adopts none, and an Array of ids adopts only
|
|
309
|
-
# those (see `apply_bleeding_edge_override`).
|
|
273
|
+
# ADR-50 § WD2 — the `--bleeding-edge[=ids]` / `--no-bleeding-edge` CLI mirror of the `bleeding_edge:` config
|
|
274
|
+
# key. `:unset` means "no flag — use the configured selection"; `true` adopts the whole overlay, `false`
|
|
275
|
+
# adopts none, and an Array of ids adopts only those (see `apply_bleeding_edge_override`).
|
|
310
276
|
bleeding_edge: :unset,
|
|
311
|
-
# Type-precision coverage block. Off by default — it is a
|
|
312
|
-
#
|
|
313
|
-
# `
|
|
314
|
-
#
|
|
315
|
-
# a `coverage` object (scan_files + precision tiers) and the
|
|
316
|
-
# text output prints a one-line coverage summary.
|
|
277
|
+
# Type-precision coverage block. Off by default — it is a second precision pass over the analyzed files (the
|
|
278
|
+
# same scan `rigor coverage` runs), so it is opt-in to keep the default check path's cost unchanged. When set,
|
|
279
|
+
# `--format json` gains a `coverage` object (scan_files + precision tiers) and the text output prints a
|
|
280
|
+
# one-line coverage summary.
|
|
317
281
|
coverage: false
|
|
318
282
|
}
|
|
319
283
|
parser = OptionParser.new do |opts| # rubocop:disable Metrics/BlockLength
|
|
@@ -368,9 +332,8 @@ module Rigor
|
|
|
368
332
|
"ADR-51: do not auto-emit CI-native output when a CI environment is detected") do
|
|
369
333
|
options[:ci_detect] = false
|
|
370
334
|
end
|
|
371
|
-
# ADR-50 § WD2 — `=[LIST]` (not ` [LIST]`) so a bare `--bleeding-edge`
|
|
372
|
-
#
|
|
373
|
-
# --bleeding-edge lib` adopts the whole overlay and checks `lib`.
|
|
335
|
+
# ADR-50 § WD2 — `=[LIST]` (not ` [LIST]`) so a bare `--bleeding-edge` never swallows a following positional
|
|
336
|
+
# path: `rigor check --bleeding-edge lib` adopts the whole overlay and checks `lib`.
|
|
374
337
|
opts.on("--bleeding-edge=[LIST]",
|
|
375
338
|
"ADR-50: adopt the bleeding-edge overlay for this run " \
|
|
376
339
|
"(all features, or a comma-separated feature-id list)") do |value|
|
|
@@ -385,36 +348,26 @@ module Rigor
|
|
|
385
348
|
options
|
|
386
349
|
end
|
|
387
350
|
|
|
388
|
-
# Surfaces the class of mistake where a configured value resolves
|
|
389
|
-
#
|
|
390
|
-
#
|
|
391
|
-
#
|
|
392
|
-
#
|
|
393
|
-
#
|
|
394
|
-
#
|
|
395
|
-
#
|
|
396
|
-
#
|
|
397
|
-
# Each finding is emitted to STDERR (a warning, not a hard error —
|
|
398
|
-
# partial / optional bundles are a valid setup) and the returned
|
|
399
|
-
# list rides into the `--format=json` payload under `config_warnings`
|
|
400
|
-
# so CI and framework consumers can assert on it. The audits only
|
|
401
|
-
# fire on explicit, working-setup-safe signals (see {ConfigAudit}).
|
|
351
|
+
# Surfaces the class of mistake where a configured value resolves to nothing — a typo'd or moved
|
|
352
|
+
# `signature_paths:` / bundler / collection path, an unknown `libraries:` name, an inert `disable:` /
|
|
353
|
+
# `severity_overrides:` rule id ({ConfigAudit}). The loader filters each one silently, and the downstream symptom
|
|
354
|
+
# is confusing: missing signatures turn calls into high-confidence `call.undefined-method` firings, and an
|
|
355
|
+
# unrecognised suppression token leaves the rule firing as if the line were never written — so a one-character
|
|
356
|
+
# mistake can read as hundreds of real errors. Each finding is emitted to STDERR (a warning, not a hard error —
|
|
357
|
+
# partial / optional bundles are a valid setup) and the returned list rides into the `--format=json` payload under
|
|
358
|
+
# `config_warnings` so CI and framework consumers can assert on it. The audits only fire on explicit,
|
|
359
|
+
# working-setup-safe signals (see {ConfigAudit}).
|
|
402
360
|
def warn_unresolved_config(configuration)
|
|
403
361
|
warnings = ConfigAudit.warnings(configuration)
|
|
404
362
|
warnings.each { |warning| @err.puts("rigor: #{warning.message}") }
|
|
405
363
|
warnings
|
|
406
364
|
end
|
|
407
365
|
|
|
408
|
-
# ADR-32 WD10 carry-over — wraps `Configuration.load` so the
|
|
409
|
-
#
|
|
410
|
-
#
|
|
411
|
-
# `
|
|
412
|
-
#
|
|
413
|
-
# injection before `Configuration.new` so the new entry
|
|
414
|
-
# follows the normal coercion path. A pre-existing
|
|
415
|
-
# `rigor-rbs-inline` entry (by gem name or `id: rbs-inline`)
|
|
416
|
-
# is removed first so the synthesised entry's
|
|
417
|
-
# `require_magic_comment: false` wins unconditionally.
|
|
366
|
+
# ADR-32 WD10 carry-over — wraps `Configuration.load` so the CLI's `--treat-all-as-inline-rbs` flag can inject a
|
|
367
|
+
# `rigor-rbs-inline` plugin entry with `require_magic_comment: false` into the loaded plugin set. Re-runs the
|
|
368
|
+
# include-aware YAML load and applies the injection before `Configuration.new` so the new entry follows the normal
|
|
369
|
+
# coercion path. A pre-existing `rigor-rbs-inline` entry (by gem name or `id: rbs-inline`) is removed first so the
|
|
370
|
+
# synthesised entry's `require_magic_comment: false` wins unconditionally.
|
|
418
371
|
def load_check_configuration(options)
|
|
419
372
|
return Configuration.load(options.fetch(:config)) unless options.fetch(:treat_all_as_inline_rbs)
|
|
420
373
|
|
|
@@ -425,13 +378,11 @@ module Rigor
|
|
|
425
378
|
Configuration.new(Configuration::DEFAULTS.merge(data))
|
|
426
379
|
end
|
|
427
380
|
|
|
428
|
-
# ADR-50 § WD2 — applies the `--bleeding-edge[=ids]` / `--no-bleeding-edge`
|
|
429
|
-
#
|
|
430
|
-
#
|
|
431
|
-
#
|
|
432
|
-
#
|
|
433
|
-
# `SeverityProfile.resolve` sites (and the worker path, which receives the
|
|
434
|
-
# whole frozen Configuration) see the run's selection.
|
|
381
|
+
# ADR-50 § WD2 — applies the `--bleeding-edge[=ids]` / `--no-bleeding-edge` CLI selection over the configured
|
|
382
|
+
# `bleeding_edge:` value, mirroring the CLI-over-config precedence `--workers` and `--no-cache` follow. `:unset`
|
|
383
|
+
# (no flag) leaves the loaded configuration untouched; any other value is normalised by
|
|
384
|
+
# {Configuration#with_bleeding_edge}, so the two `SeverityProfile.resolve` sites (and the worker path, which
|
|
385
|
+
# receives the whole frozen Configuration) see the run's selection.
|
|
435
386
|
def apply_bleeding_edge_override(configuration, options)
|
|
436
387
|
selection = options.fetch(:bleeding_edge)
|
|
437
388
|
return configuration if selection == :unset
|
|
@@ -469,30 +420,24 @@ module Rigor
|
|
|
469
420
|
end
|
|
470
421
|
end
|
|
471
422
|
|
|
472
|
-
# Emits the {Analysis::RunStats} summary to STDERR so it
|
|
473
|
-
#
|
|
474
|
-
# JSON) on STDOUT. JSON consumers can pipe stdout cleanly;
|
|
475
|
-
# interactive users still see the summary on their tty.
|
|
423
|
+
# Emits the {Analysis::RunStats} summary to STDERR so it doesn't interleave with the diagnostic stream (text or
|
|
424
|
+
# JSON) on STDOUT. JSON consumers can pipe stdout cleanly; interactive users still see the summary on their tty.
|
|
476
425
|
def write_run_stats(stats)
|
|
477
426
|
@err.puts("")
|
|
478
427
|
stats.format(@err)
|
|
479
428
|
end
|
|
480
429
|
|
|
481
|
-
# Opt-in developer diagnostics printed after the run: the
|
|
482
|
-
#
|
|
483
|
-
# profile (RIGOR_HEAP_PROFILE). Each gates itself, so this is a no-op
|
|
484
|
-
# on a normal run.
|
|
430
|
+
# Opt-in developer diagnostics printed after the run: the inference-cutoff trace (RIGOR_BUDGET_TRACE) and the
|
|
431
|
+
# heap-attribution profile (RIGOR_HEAP_PROFILE). Each gates itself, so this is a no-op on a normal run.
|
|
485
432
|
def write_trace_appendices
|
|
486
433
|
write_budget_trace
|
|
487
434
|
write_heap_profile
|
|
488
435
|
end
|
|
489
436
|
|
|
490
|
-
# Dumps the opt-in inference-cutoff counters (RIGOR_BUDGET_TRACE).
|
|
491
|
-
#
|
|
492
|
-
#
|
|
493
|
-
#
|
|
494
|
-
# only on a single-process run (`--workers 0`), since they do not
|
|
495
|
-
# cross fork boundaries.
|
|
437
|
+
# Dumps the opt-in inference-cutoff counters (RIGOR_BUDGET_TRACE). These are the hard-coded "budget" guards that
|
|
438
|
+
# silently degrade to `Dynamic[top]` / a fallback bound — counting them shows where inference actually stopped.
|
|
439
|
+
# Process-global counters: meaningful only on a single-process run (`--workers 0`), since they do not cross fork
|
|
440
|
+
# boundaries.
|
|
496
441
|
def write_budget_trace
|
|
497
442
|
return unless Inference::BudgetTrace.enabled?
|
|
498
443
|
|
|
@@ -508,11 +453,9 @@ module Rigor
|
|
|
508
453
|
write_budget_distributions
|
|
509
454
|
end
|
|
510
455
|
|
|
511
|
-
# Dumps the read-only size distributions (ADR-41 Slice 2a). These
|
|
512
|
-
#
|
|
513
|
-
#
|
|
514
|
-
# `over` thresholds bracket the TypeProf prior (10) and Rigor's spec
|
|
515
|
-
# default (24).
|
|
456
|
+
# Dumps the read-only size distributions (ADR-41 Slice 2a). These observe how large unions actually get, with no
|
|
457
|
+
# cap enforced — the data the `union_size` budget default should be chosen from. The `over` thresholds bracket the
|
|
458
|
+
# TypeProf prior (10) and Rigor's spec default (24).
|
|
516
459
|
def write_budget_distributions
|
|
517
460
|
summary = Inference::BudgetTrace.summarize(Inference::BudgetTrace::UNION_ARITY, over: [10, 24, 40])
|
|
518
461
|
pct = summary[:percentiles]
|
|
@@ -522,14 +465,11 @@ module Rigor
|
|
|
522
465
|
@err.puts(" unions ≥10: #{over[10]} ≥24: #{over[24]} ≥40: #{over[40]}")
|
|
523
466
|
end
|
|
524
467
|
|
|
525
|
-
# Dumps a live-heap class breakdown (RIGOR_HEAP_PROFILE) — retained
|
|
526
|
-
#
|
|
527
|
-
#
|
|
528
|
-
#
|
|
529
|
-
#
|
|
530
|
-
# whole heap is slow — a dev probe, not a normal diagnostic. Run
|
|
531
|
-
# single-process (`--workers 0`) so the parent heap is the analysis
|
|
532
|
-
# heap; the gem is required lazily so a normal run never loads it.
|
|
468
|
+
# Dumps a live-heap class breakdown (RIGOR_HEAP_PROFILE) — retained objects by class after a forced GC, ranked by
|
|
469
|
+
# total memsize. The tool for attributing where the analyzer's resident memory goes (ADR-41 Slice 2b): it answers
|
|
470
|
+
# whether the heap is type carriers, RBS objects, Prism nodes, or fact-store Hashes/Strings. Walking the whole
|
|
471
|
+
# heap is slow — a dev probe, not a normal diagnostic. Run single-process (`--workers 0`) so the parent heap is
|
|
472
|
+
# the analysis heap; the gem is required lazily so a normal run never loads it.
|
|
533
473
|
def write_heap_profile
|
|
534
474
|
return if ENV["RIGOR_HEAP_PROFILE"].to_s.empty?
|
|
535
475
|
|
|
@@ -543,9 +483,8 @@ module Rigor
|
|
|
543
483
|
write_string_allocation_sites
|
|
544
484
|
end
|
|
545
485
|
|
|
546
|
-
# Loads the analysis-path dependencies lazily (so non-check commands
|
|
547
|
-
#
|
|
548
|
-
# any analysis object is allocated.
|
|
486
|
+
# Loads the analysis-path dependencies lazily (so non-check commands stay light) and starts heap-allocation
|
|
487
|
+
# tracing if requested, before any analysis object is allocated.
|
|
549
488
|
def load_check_dependencies
|
|
550
489
|
require_relative "../analysis/runner"
|
|
551
490
|
require_relative "../analysis/buffer_binding"
|
|
@@ -554,9 +493,8 @@ module Rigor
|
|
|
554
493
|
start_heap_trace_if_requested
|
|
555
494
|
end
|
|
556
495
|
|
|
557
|
-
# Starts allocation tracing (RIGOR_HEAP_TRACE) as early as possible so
|
|
558
|
-
#
|
|
559
|
-
# `file:line`. Very high overhead — run on a small file subset only.
|
|
496
|
+
# Starts allocation tracing (RIGOR_HEAP_TRACE) as early as possible so the heap profile can attribute retained
|
|
497
|
+
# Strings to their allocation `file:line`. Very high overhead — run on a small file subset only.
|
|
560
498
|
def start_heap_trace_if_requested
|
|
561
499
|
return if ENV["RIGOR_HEAP_TRACE"].to_s.empty?
|
|
562
500
|
|
|
@@ -564,11 +502,9 @@ module Rigor
|
|
|
564
502
|
ObjectSpace.trace_object_allocations_start
|
|
565
503
|
end
|
|
566
504
|
|
|
567
|
-
# When RIGOR_HEAP_TRACE is on, groups the live String objects by their
|
|
568
|
-
#
|
|
569
|
-
#
|
|
570
|
-
# that dominate the large-app heap (ADR-41 Slice 2b). Strings allocated
|
|
571
|
-
# before tracing started report `(pre-trace)`.
|
|
505
|
+
# When RIGOR_HEAP_TRACE is on, groups the live String objects by their allocation site (`sourcefile:sourceline`)
|
|
506
|
+
# and prints the top sites by count — pinpointing which engine code retains the millions of strings that dominate
|
|
507
|
+
# the large-app heap (ADR-41 Slice 2b). Strings allocated before tracing started report `(pre-trace)`.
|
|
572
508
|
def write_string_allocation_sites
|
|
573
509
|
return if ENV["RIGOR_HEAP_TRACE"].to_s.empty?
|
|
574
510
|
|
|
@@ -585,9 +521,8 @@ module Rigor
|
|
|
585
521
|
end
|
|
586
522
|
end
|
|
587
523
|
|
|
588
|
-
# Walks the whole live heap (after a forced GC) and tallies
|
|
589
|
-
#
|
|
590
|
-
# `[by_class, total]`. Slow — a dev probe only.
|
|
524
|
+
# Walks the whole live heap (after a forced GC) and tallies `{class_name => [count, memsize]}` plus the grand
|
|
525
|
+
# total. Returns `[by_class, total]`. Slow — a dev probe only.
|
|
591
526
|
def tally_live_heap
|
|
592
527
|
require "objspace"
|
|
593
528
|
GC.start
|
|
@@ -675,9 +610,8 @@ module Rigor
|
|
|
675
610
|
write_text_result(result)
|
|
676
611
|
write_coverage_summary(coverage) if coverage
|
|
677
612
|
when ->(fmt) { CLI::DiagnosticFormats.supports?(fmt) }
|
|
678
|
-
# ADR-51 — CI-native renderings (SARIF / GitHub Actions commands /
|
|
679
|
-
#
|
|
680
|
-
# diagnostics; the JSON forms always carry a document.
|
|
613
|
+
# ADR-51 — CI-native renderings (SARIF / GitHub Actions commands / GitLab Code Quality). The `github` form is
|
|
614
|
+
# empty when there are no diagnostics; the JSON forms always carry a document.
|
|
681
615
|
output = CLI::DiagnosticFormats.render(result, format)
|
|
682
616
|
@out.puts(output) unless output.empty?
|
|
683
617
|
else
|
|
@@ -685,11 +619,9 @@ module Rigor
|
|
|
685
619
|
end
|
|
686
620
|
end
|
|
687
621
|
|
|
688
|
-
# Runs the type-precision scan (`--coverage`) over the same file set
|
|
689
|
-
# the
|
|
690
|
-
#
|
|
691
|
-
# runs, reused via {CoverageScan} — so it is opt-in to keep the
|
|
692
|
-
# default check path's cost unchanged.
|
|
622
|
+
# Runs the type-precision scan (`--coverage`) over the same file set the check analyzed and returns a
|
|
623
|
+
# `CoverageReport`, or nil when the flag is off. It is a second pass — the same scan `rigor coverage` runs, reused
|
|
624
|
+
# via {CoverageScan} — so it is opt-in to keep the default check path's cost unchanged.
|
|
693
625
|
def compute_coverage(runner, configuration, options)
|
|
694
626
|
return nil unless options.fetch(:coverage)
|
|
695
627
|
|
|
@@ -697,11 +629,9 @@ module Rigor
|
|
|
697
629
|
CoverageScan.precision_report(files: files, configuration: configuration)
|
|
698
630
|
end
|
|
699
631
|
|
|
700
|
-
# The `coverage` block embedded in `--format json`. Mirrors the
|
|
701
|
-
#
|
|
702
|
-
#
|
|
703
|
-
# so a consumer reads one stream to learn both what fired and how
|
|
704
|
-
# much of the analyzed surface Rigor could type.
|
|
632
|
+
# The `coverage` block embedded in `--format json`. Mirrors the `summary` of `rigor coverage --format json` (the
|
|
633
|
+
# same vocabulary — `precise_ratio`, not a separate `typed_ratio`) plus `scan_files`, so a consumer reads one
|
|
634
|
+
# stream to learn both what fired and how much of the analyzed surface Rigor could type.
|
|
705
635
|
def coverage_payload(report)
|
|
706
636
|
{
|
|
707
637
|
"scan_files" => report.files.size - report.parse_errors.size,
|
|
@@ -722,13 +652,10 @@ module Rigor
|
|
|
722
652
|
"Run `rigor coverage` for the full per-file / per-tier breakdown.")
|
|
723
653
|
end
|
|
724
654
|
|
|
725
|
-
# Adds the per-rule `evidence_tier` and `documentation_url` fields
|
|
726
|
-
#
|
|
727
|
-
#
|
|
728
|
-
#
|
|
729
|
-
# layer here rather than threading through every diagnostic
|
|
730
|
-
# construction site. Only built-in rules carry catalogue metadata;
|
|
731
|
-
# plugin / `rbs_extended` / parse-error diagnostics are left
|
|
655
|
+
# Adds the per-rule `evidence_tier` and `documentation_url` fields to each diagnostic in the `--format json`
|
|
656
|
+
# payload. Both are pure functions of the rule id (the rule catalogue, ADR-61 / the 2026-06-15 feedback §4 +
|
|
657
|
+
# §5.1), so they enrich the presentation layer here rather than threading through every diagnostic construction
|
|
658
|
+
# site. Only built-in rules carry catalogue metadata; plugin / `rbs_extended` / parse-error diagnostics are left
|
|
732
659
|
# untouched (they host their own documentation and confidence).
|
|
733
660
|
def enrich_json(payload)
|
|
734
661
|
Array(payload["diagnostics"]).each do |diag|
|
|
@@ -745,14 +672,11 @@ module Rigor
|
|
|
745
672
|
payload
|
|
746
673
|
end
|
|
747
674
|
|
|
748
|
-
# ADR-51 WD7 — CI auto-detection. Only augments the default human
|
|
749
|
-
#
|
|
750
|
-
#
|
|
751
|
-
#
|
|
752
|
-
#
|
|
753
|
-
# like PHPStan's CI-detecting table formatter). For GitLab (native but
|
|
754
|
-
# artifact-based) and the reviewdog-routed CIs, a one-line hint goes to
|
|
755
|
-
# stderr — but only when there are diagnostics, so a clean run stays
|
|
675
|
+
# ADR-51 WD7 — CI auto-detection. Only augments the default human (`text`) output: an explicit `--format` means
|
|
676
|
+
# the caller is in control and is left untouched. For a first-class stdout-native CI (GitHub Actions / TeamCity)
|
|
677
|
+
# the platform's annotations are emitted on top of the text output (so the human log AND the inline surface both
|
|
678
|
+
# appear, like PHPStan's CI-detecting table formatter). For GitLab (native but artifact-based) and the
|
|
679
|
+
# reviewdog-routed CIs, a one-line hint goes to stderr — but only when there are diagnostics, so a clean run stays
|
|
756
680
|
# quiet.
|
|
757
681
|
def emit_ci_detected_output(result, options)
|
|
758
682
|
return unless options.fetch(:ci_detect)
|
|
@@ -780,10 +704,8 @@ module Rigor
|
|
|
780
704
|
end
|
|
781
705
|
end
|
|
782
706
|
|
|
783
|
-
# Text output adds a one-line summary so users see the
|
|
784
|
-
#
|
|
785
|
-
# the success and failure cases and reports the affected
|
|
786
|
-
# file count for failures.
|
|
707
|
+
# Text output adds a one-line summary so users see the diagnostic-count immediately. The summary distinguishes the
|
|
708
|
+
# success and failure cases and reports the affected file count for failures.
|
|
787
709
|
def write_text_result(result)
|
|
788
710
|
result.diagnostics.each { |diagnostic| @out.puts(diagnostic) }
|
|
789
711
|
|