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
|
@@ -5,18 +5,14 @@ require_relative "../cache/store"
|
|
|
5
5
|
|
|
6
6
|
module Rigor
|
|
7
7
|
class CLI
|
|
8
|
-
# Shared factory for building an {Analysis::Runner} from CLI option
|
|
9
|
-
# hashes + a loaded {Configuration}.
|
|
8
|
+
# Shared factory for building an {Analysis::Runner} from CLI option hashes + a loaded {Configuration}.
|
|
10
9
|
#
|
|
11
|
-
# Extracted from {CheckCommand} so that `rigor doctor` and future
|
|
12
|
-
#
|
|
13
|
-
# (cache, workers, buffer, explain) without diverging from the
|
|
14
|
-
# primary check path (ADR-77 WD3).
|
|
10
|
+
# Extracted from {CheckCommand} so that `rigor doctor` and future `describe --deep` can reuse the exact same
|
|
11
|
+
# runner-setup logic (cache, workers, buffer, explain) without diverging from the primary check path (ADR-77 WD3).
|
|
15
12
|
module CheckRunnerFactory
|
|
16
13
|
module_function
|
|
17
14
|
|
|
18
|
-
# Builds a runner matching the configuration/plugin/cache resolution
|
|
19
|
-
# that `rigor check` itself uses.
|
|
15
|
+
# Builds a runner matching the configuration/plugin/cache resolution that `rigor check` itself uses.
|
|
20
16
|
#
|
|
21
17
|
# @param configuration [Rigor::Configuration]
|
|
22
18
|
# @param options [Hash] parsed CLI options (must contain at least
|
|
@@ -43,12 +39,10 @@ module Rigor
|
|
|
43
39
|
)
|
|
44
40
|
end
|
|
45
41
|
|
|
46
|
-
# Resolves the worker count by precedence: CLI `--workers=N`
|
|
47
|
-
# (
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
# loudly. CLI / env may pass a negative value — clamped to 0
|
|
51
|
-
# (sequential) so a stray `-1` doesn't crash the pool spawn loop.
|
|
42
|
+
# Resolves the worker count by precedence: CLI `--workers=N` (most explicit) > env `RIGOR_RACTOR_WORKERS` > config
|
|
43
|
+
# `.rigor.yml` `parallel.workers:` > 0 (sequential default). Returns an Integer; non-numeric values raise so typos
|
|
44
|
+
# fail loudly. CLI / env may pass a negative value — clamped to 0 (sequential) so a stray `-1` doesn't crash the
|
|
45
|
+
# pool spawn loop.
|
|
52
46
|
def resolve_workers(options, configuration)
|
|
53
47
|
cli_value = options[:workers]
|
|
54
48
|
return [Integer(cli_value), 0].max if cli_value
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
class CLI
|
|
5
|
-
# Runtime CI-environment detection (ADR-51 WD7), modelled on
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# {Platform}, classifying it into a **tier** that decides how
|
|
9
|
-
# `rigor check` surfaces diagnostics there:
|
|
5
|
+
# Runtime CI-environment detection (ADR-51 WD7), modelled on `OndraM/ci-detector` (the library PHPStan uses). Reads
|
|
6
|
+
# the well-known environment variables a CI provider sets and returns the matching {Platform}, classifying it into a
|
|
7
|
+
# **tier** that decides how `rigor check` surfaces diagnostics there:
|
|
10
8
|
#
|
|
11
9
|
# :native_stdout — Rigor has a native format that renders purely from
|
|
12
10
|
# stdout, so it is auto-emitted on top of the human
|
|
@@ -19,9 +17,8 @@ module Rigor
|
|
|
19
17
|
# reviewdog (`checkstyle`/`sarif`) or `junit`. Hint
|
|
20
18
|
# only.
|
|
21
19
|
#
|
|
22
|
-
# Detection is a pure function of the environment hash, so it is fully
|
|
23
|
-
#
|
|
24
|
-
# disables it globally — the seam the spec suite uses for determinism.
|
|
20
|
+
# Detection is a pure function of the environment hash, so it is fully testable; the CLI passes `ENV`.
|
|
21
|
+
# `RIGOR_CI_DETECT=0` (or `false`/`no`) disables it globally — the seam the spec suite uses for determinism.
|
|
25
22
|
module CiDetector
|
|
26
23
|
Platform = Struct.new(:id, :name, :format, :tier, keyword_init: true) do
|
|
27
24
|
def native_stdout? = tier == :native_stdout
|
|
@@ -29,10 +26,9 @@ module Rigor
|
|
|
29
26
|
def reviewdog? = tier == :reviewdog
|
|
30
27
|
end
|
|
31
28
|
|
|
32
|
-
# The detection table, ordered most-specific first so the generic
|
|
33
|
-
# `CI
|
|
34
|
-
#
|
|
35
|
-
# 1/true/yes/on), `:present` (variable set non-empty), or `:equals`.
|
|
29
|
+
# The detection table, ordered most-specific first so the generic `CI=true` catch-all is last (a provider that
|
|
30
|
+
# also sets `CI` is still recognised by its own variable). `match` is `:truthy` (value in 1/true/yes/on),
|
|
31
|
+
# `:present` (variable set non-empty), or `:equals`.
|
|
36
32
|
PROVIDERS = [
|
|
37
33
|
{ id: "github-actions", name: "GitHub Actions", format: "github", tier: :native_stdout,
|
|
38
34
|
var: "GITHUB_ACTIONS", match: :truthy },
|
|
@@ -66,8 +62,7 @@ module Rigor
|
|
|
66
62
|
|
|
67
63
|
module_function
|
|
68
64
|
|
|
69
|
-
# Returns the detected {Platform}, or nil when no CI is recognised or
|
|
70
|
-
# detection is disabled via `RIGOR_CI_DETECT`.
|
|
65
|
+
# Returns the detected {Platform}, or nil when no CI is recognised or detection is disabled via `RIGOR_CI_DETECT`.
|
|
71
66
|
def detect(env = ENV)
|
|
72
67
|
return nil if disabled?(env)
|
|
73
68
|
|
data/lib/rigor/cli/command.rb
CHANGED
|
@@ -4,16 +4,12 @@ module Rigor
|
|
|
4
4
|
class CLI
|
|
5
5
|
# Base class for the `rigor <subcommand>` command objects.
|
|
6
6
|
#
|
|
7
|
-
# Every subcommand captured the same invariant wiring — the argument
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# Centralising it here gives one consistent shape and lets a test
|
|
12
|
-
# instantiate a command with just `argv:` (the streams default so a
|
|
13
|
-
# spec can pass a `StringIO` for one and ignore the other).
|
|
7
|
+
# Every subcommand captured the same invariant wiring — the argument vector plus the output and error streams — in
|
|
8
|
+
# an identical `initialize(argv:, out:, err:)`, and defaulted the streams inconsistently (some to `$stdout` /
|
|
9
|
+
# `$stderr`, most not at all). Centralising it here gives one consistent shape and lets a test instantiate a command
|
|
10
|
+
# with just `argv:` (the streams default so a spec can pass a `StringIO` for one and ignore the other).
|
|
14
11
|
#
|
|
15
|
-
# Subclasses read the `@argv` / `@out` / `@err` ivars directly, as
|
|
16
|
-
# they did before.
|
|
12
|
+
# Subclasses read the `@argv` / `@out` / `@err` ivars directly, as they did before.
|
|
17
13
|
class Command
|
|
18
14
|
def initialize(argv:, out: $stdout, err: $stderr)
|
|
19
15
|
@argv = argv
|
|
@@ -23,11 +19,9 @@ module Rigor
|
|
|
23
19
|
|
|
24
20
|
private
|
|
25
21
|
|
|
26
|
-
# Expands `args` (a mix of files and directories) into a unique
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# `@err` — on the first arg that is neither. Shared by the
|
|
30
|
-
# path-walking commands (`type-scan`, `coverage`).
|
|
22
|
+
# Expands `args` (a mix of files and directories) into a unique list of `.rb` paths, recursing into directories.
|
|
23
|
+
# Returns nil — after writing `<command_name>: not a file or directory: <arg>` to `@err` — on the first arg that
|
|
24
|
+
# is neither. Shared by the path-walking commands (`type-scan`, `coverage`).
|
|
31
25
|
def collect_paths(args, command_name:)
|
|
32
26
|
paths = []
|
|
33
27
|
args.each do |arg|
|
|
@@ -31,12 +31,11 @@ module Rigor
|
|
|
31
31
|
class CLI
|
|
32
32
|
# Executes the `rigor coverage` command.
|
|
33
33
|
#
|
|
34
|
-
# Walks every Prism node in one or more files, infers its type via
|
|
35
|
-
#
|
|
36
|
-
# (constant / nominal / shaped / refined / bot / dynamic_specific /
|
|
34
|
+
# Walks every Prism node in one or more files, infers its type via `Rigor::Scope#type_of`, and classifies the result
|
|
35
|
+
# into precision tiers (constant / nominal / shaped / refined / bot / dynamic_specific /
|
|
37
36
|
# dynamic_top / top). Reports aggregate and per-file statistics so
|
|
38
|
-
# maintainers can track type-precision trends and SKILL pipelines can
|
|
39
|
-
#
|
|
37
|
+
# maintainers can track type-precision trends and SKILL pipelines can measure the impact of adding new constant-fold
|
|
38
|
+
# or shape-dispatch rules.
|
|
40
39
|
#
|
|
41
40
|
# Exit codes:
|
|
42
41
|
# 0 — scan complete, precision ratio ≥ threshold (or no threshold given)
|
|
@@ -47,8 +46,8 @@ module Rigor
|
|
|
47
46
|
|
|
48
47
|
USAGE = "Usage: rigor coverage [options] PATH..."
|
|
49
48
|
|
|
50
|
-
# ADR-70 — the default test runner hook for `--with-tests`. The
|
|
51
|
-
#
|
|
49
|
+
# ADR-70 — the default test runner hook for `--with-tests`. The conventional Ruby test task; override with
|
|
50
|
+
# `--test-command`.
|
|
52
51
|
DEFAULT_TEST_COMMAND = %w[bundle exec rake].freeze
|
|
53
52
|
|
|
54
53
|
# @return [Integer] CLI exit status.
|
|
@@ -153,9 +152,8 @@ module Rigor
|
|
|
153
152
|
accumulator.to_report
|
|
154
153
|
end
|
|
155
154
|
|
|
156
|
-
# Seed the protection scan's scope with the same cross-file facts
|
|
157
|
-
#
|
|
158
|
-
# actually has rather than a stripped-scope `Dynamic`:
|
|
155
|
+
# Seed the protection scan's scope with the same cross-file facts `rigor check` resolves against, so a receiver
|
|
156
|
+
# reads the type it actually has rather than a stripped-scope `Dynamic`:
|
|
159
157
|
#
|
|
160
158
|
# - `discovered_classes` — a project constant referring to a class
|
|
161
159
|
# defined in a *sibling* file (`Account`, `User`) types as
|
|
@@ -168,8 +166,8 @@ module Rigor
|
|
|
168
166
|
# parameter) counts as protected when its call sites resolve to
|
|
169
167
|
# concrete argument types.
|
|
170
168
|
#
|
|
171
|
-
# Both span the scanned `paths` only (no whole-project pre-pass) —
|
|
172
|
-
#
|
|
169
|
+
# Both span the scanned `paths` only (no whole-project pre-pass) — a site that gains neither is classified exactly
|
|
170
|
+
# as before.
|
|
173
171
|
def scope_with_inferred_params(paths, configuration, environment)
|
|
174
172
|
base = Scope.empty(environment: environment)
|
|
175
173
|
seed = {}
|
|
@@ -206,24 +204,19 @@ module Rigor
|
|
|
206
204
|
CoverageScan.precision_report(files: paths, configuration: Configuration.load(options.fetch(:config)))
|
|
207
205
|
end
|
|
208
206
|
|
|
209
|
-
# Delegated to the shared scan module (see {CoverageScan}); the
|
|
210
|
-
#
|
|
211
|
-
# reuses `precision_report` over the same machinery.
|
|
207
|
+
# Delegated to the shared scan module (see {CoverageScan}); the protection path below reuses both, and `rigor
|
|
208
|
+
# check --coverage` reuses `precision_report` over the same machinery.
|
|
212
209
|
def project_environment(configuration)
|
|
213
210
|
CoverageScan.project_environment(configuration)
|
|
214
211
|
end
|
|
215
212
|
|
|
216
|
-
# The protection scan must see the same receiver types `rigor check`
|
|
217
|
-
#
|
|
218
|
-
#
|
|
219
|
-
#
|
|
220
|
-
#
|
|
221
|
-
# registry
|
|
222
|
-
#
|
|
223
|
-
# undercount of what Rigor actually types on a plugin-using project.
|
|
224
|
-
# `ProjectContext` builds the plugin-aware environment (registry
|
|
225
|
-
# materialised + the per-run prepare pass that primes producers like
|
|
226
|
-
# the controller / model index) exactly as the LSP and the runner do.
|
|
213
|
+
# The protection scan must see the same receiver types `rigor check` does — including plugin-contributed
|
|
214
|
+
# `dynamic_return` types (a controller's `params` → `ActionController::Parameters`, a `Model.where` →
|
|
215
|
+
# `ActiveRecord::Relation[Model]`). The bare `project_environment` carries only the RBS environment (no plugin
|
|
216
|
+
# registry), so every plugin-typed receiver reads `Dynamic` and its dispatch site is miscounted as *unprotected* —
|
|
217
|
+
# a systematic undercount of what Rigor actually types on a plugin-using project. `ProjectContext` builds the
|
|
218
|
+
# plugin-aware environment (registry materialised + the per-run prepare pass that primes producers like the
|
|
219
|
+
# controller / model index) exactly as the LSP and the runner do.
|
|
227
220
|
def plugin_aware_environment(configuration)
|
|
228
221
|
LanguageServer::ProjectContext.new(configuration: configuration).environment
|
|
229
222
|
end
|
|
@@ -5,19 +5,16 @@ require "prism"
|
|
|
5
5
|
|
|
6
6
|
module Rigor
|
|
7
7
|
class CLI
|
|
8
|
-
# ADR-63 Tier 2 + ADR-70 — the mutation-effectiveness and fused static∪dynamic
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# and the Protection + LanguageServer collaborators the command requires).
|
|
8
|
+
# ADR-63 Tier 2 + ADR-70 — the mutation-effectiveness and fused static∪dynamic protection paths, factored out of
|
|
9
|
+
# {CoverageCommand} to keep that command focused on dispatch. Mixed in, so each method runs in the command instance
|
|
10
|
+
# (using `@out` / `@err` / `@argv` / `collect_paths` / `determine_protection_exit` and the Protection +
|
|
11
|
+
# LanguageServer collaborators the command requires).
|
|
13
12
|
module CoverageMutation
|
|
14
13
|
private
|
|
15
14
|
|
|
16
|
-
# ADR-63 Tier 2 — the mutation-effectiveness deep dive. Builds the RBS
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# git-changed `.rb` files; explicit paths override (and enable the
|
|
20
|
-
# whole-project opt-in, which is minutes).
|
|
15
|
+
# ADR-63 Tier 2 — the mutation-effectiveness deep dive. Builds the RBS environment + project pre-pass once (the
|
|
16
|
+
# warm loop), then re-analyses each target file's mutants against its clean baseline. Defaults to the git-changed
|
|
17
|
+
# `.rb` files; explicit paths override (and enable the whole-project opt-in, which is minutes).
|
|
21
18
|
def run_mutation_protection(options)
|
|
22
19
|
explicit = collect_paths(@argv, command_name: "coverage")
|
|
23
20
|
return CLI::EXIT_USAGE if explicit.nil?
|
|
@@ -36,8 +33,8 @@ module Rigor
|
|
|
36
33
|
determine_protection_exit(report, options)
|
|
37
34
|
end
|
|
38
35
|
|
|
39
|
-
# A `--limit` sample makes the report an estimate (per-file ratios over a
|
|
40
|
-
#
|
|
36
|
+
# A `--limit` sample makes the report an estimate (per-file ratios over a random N of the mutations). Say so on
|
|
37
|
+
# stderr — stdout stays clean for JSON.
|
|
41
38
|
def note_sampling(options)
|
|
42
39
|
return unless options[:limit]
|
|
43
40
|
|
|
@@ -47,10 +44,9 @@ module Rigor
|
|
|
47
44
|
)
|
|
48
45
|
end
|
|
49
46
|
|
|
50
|
-
# ADR-70 — the fused static∪dynamic deep dive. The type pass is the ADR-63
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
# "a mutant survived" is meaningless — abort with a clear message if not.
|
|
47
|
+
# ADR-70 — the fused static∪dynamic deep dive. The type pass is the ADR-63 Tier 2 warm loop; each type-survivor is
|
|
48
|
+
# then run against the project's test suite (the runner hook). The suite MUST pass on clean code first, or "a
|
|
49
|
+
# mutant survived" is meaningless — abort with a clear message if not.
|
|
54
50
|
def run_fused_protection(paths, options)
|
|
55
51
|
configuration = Configuration.load(options.fetch(:config))
|
|
56
52
|
test_oracle = Protection::TestSuiteOracle.new(command: options.fetch(:test_command))
|
|
@@ -117,9 +113,8 @@ module Rigor
|
|
|
117
113
|
accumulator.absorb(scanner.scan_file(path, source: source))
|
|
118
114
|
end
|
|
119
115
|
|
|
120
|
-
# The git-changed (modified / added / untracked) `.rb` files that exist on
|
|
121
|
-
#
|
|
122
|
-
# when git is unavailable; the caller then reports "nothing to measure".
|
|
116
|
+
# The git-changed (modified / added / untracked) `.rb` files that exist on disk — the default Tier 2 scope.
|
|
117
|
+
# Returns [] outside a git work tree or when git is unavailable; the caller then reports "nothing to measure".
|
|
123
118
|
def changed_ruby_files
|
|
124
119
|
output = git_status_porcelain
|
|
125
120
|
return [] if output.nil?
|
|
@@ -11,7 +11,6 @@ module Rigor
|
|
|
11
11
|
:per_file,
|
|
12
12
|
:total
|
|
13
13
|
)
|
|
14
|
-
# Sum of all per-file totals.
|
|
15
14
|
def grand_total
|
|
16
15
|
total.total
|
|
17
16
|
end
|
|
@@ -44,7 +43,6 @@ module Rigor
|
|
|
44
43
|
def initialize
|
|
45
44
|
@per_file = []
|
|
46
45
|
@parse_errors = []
|
|
47
|
-
# Accumulated totals across all files.
|
|
48
46
|
@total_total = 0
|
|
49
47
|
@total_tier_counts = Inference::PrecisionScanner::TIERS.to_h { |t| [t, 0] }
|
|
50
48
|
end
|
|
@@ -10,12 +10,10 @@ require_relative "coverage_report"
|
|
|
10
10
|
|
|
11
11
|
module Rigor
|
|
12
12
|
class CLI
|
|
13
|
-
# Shared type-precision scan behind both `rigor coverage` (the
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# `CoverageReport`. Extracted so the two surfaces stay byte-identical
|
|
18
|
-
# on the same file set.
|
|
13
|
+
# Shared type-precision scan behind both `rigor coverage` (the dedicated command) and `rigor check --coverage` (the
|
|
14
|
+
# in-run coverage block). Walks each file's AST, types every expression via `Scope#type_of`, and accumulates the
|
|
15
|
+
# precision-tier breakdown into a `CoverageReport`. Extracted so the two surfaces stay byte-identical on the same
|
|
16
|
+
# file set.
|
|
19
17
|
module CoverageScan
|
|
20
18
|
module_function
|
|
21
19
|
|
|
@@ -37,11 +35,9 @@ module Rigor
|
|
|
37
35
|
)
|
|
38
36
|
end
|
|
39
37
|
|
|
40
|
-
# Parses one file and feeds the scan result (or a parse-error
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
# `ProtectionScanner` + `ProtectionAccumulator` — both of which
|
|
44
|
-
# respond to `scan(node)` and `absorb(path, result)`.
|
|
38
|
+
# Parses one file and feeds the scan result (or a parse-error record) into `accumulator`. `scanner` /
|
|
39
|
+
# `accumulator` are a matched pair — a `PrecisionScanner` + `CoverageAccumulator`, or a `ProtectionScanner` +
|
|
40
|
+
# `ProtectionAccumulator` — both of which respond to `scan(node)` and `absorb(path, result)`.
|
|
45
41
|
def scan_into(path, scanner, accumulator, configuration)
|
|
46
42
|
source = File.read(path)
|
|
47
43
|
parse_result = Prism.parse(source, filepath: path, version: configuration.target_ruby)
|
|
@@ -6,11 +6,9 @@ require_relative "../version"
|
|
|
6
6
|
|
|
7
7
|
module Rigor
|
|
8
8
|
class CLI
|
|
9
|
-
# CI-native diagnostic output formats (ADR-51). Each renders an
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# job log. They read the same `Diagnostic` fields as `--format json`
|
|
13
|
-
# (path / line / column / severity / qualified rule / message) and add
|
|
9
|
+
# CI-native diagnostic output formats (ADR-51). Each renders an `Analysis::Result` to a string a CI platform
|
|
10
|
+
# consumes to surface diagnostics inline in a pull / merge request, rather than only in the job log. They read the
|
|
11
|
+
# same `Diagnostic` fields as `--format json` (path / line / column / severity / qualified rule / message) and add
|
|
14
12
|
# no new information — only a platform-native rendering of it.
|
|
15
13
|
#
|
|
16
14
|
# sarif — SARIF 2.1.0 (cross-platform; GitHub code-scanning, any
|
|
@@ -23,11 +21,9 @@ module Rigor
|
|
|
23
21
|
# reviewdog (`-f=checkstyle`) and Jenkins/etc. consume
|
|
24
22
|
# junit — JUnit XML, the test-report format many CI systems render
|
|
25
23
|
#
|
|
26
|
-
# Severity maps once per format from Rigor's `:error` / `:warning` /
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# ruleless producers (parse / internal errors), which each format
|
|
30
|
-
# degrades gracefully.
|
|
24
|
+
# Severity maps once per format from Rigor's `:error` / `:warning` / `:info`; the qualified rule
|
|
25
|
+
# (`<source_family>.<rule>` or the bare rule for the builtin family) is the stable identifier, nil only for the
|
|
26
|
+
# ruleless producers (parse / internal errors), which each format degrades gracefully.
|
|
31
27
|
module DiagnosticFormats
|
|
32
28
|
FORMATS = %w[sarif github gitlab checkstyle junit teamcity].freeze
|
|
33
29
|
|
|
@@ -37,8 +33,7 @@ module Rigor
|
|
|
37
33
|
FORMATS.include?(format)
|
|
38
34
|
end
|
|
39
35
|
|
|
40
|
-
# Renders `result` in the named CI format. Callers gate on
|
|
41
|
-
# {.supports?} first; an unrecognised format returns nil.
|
|
36
|
+
# Renders `result` in the named CI format. Callers gate on {.supports?} first; an unrecognised format returns nil.
|
|
42
37
|
def render(result, format)
|
|
43
38
|
case format
|
|
44
39
|
when "sarif" then Sarif.new(result).render
|
|
@@ -50,9 +45,8 @@ module Rigor
|
|
|
50
45
|
end
|
|
51
46
|
end
|
|
52
47
|
|
|
53
|
-
# XML attribute / text escaping shared by the Checkstyle and JUnit
|
|
54
|
-
#
|
|
55
|
-
# message carrying `<`, `&`, or a quote can't break the document.
|
|
48
|
+
# XML attribute / text escaping shared by the Checkstyle and JUnit formatters. Covers the five predefined XML
|
|
49
|
+
# entities so a diagnostic message carrying `<`, `&`, or a quote can't break the document.
|
|
56
50
|
module XmlEscaping
|
|
57
51
|
ENTITIES = { "&" => "&", "<" => "<", ">" => ">",
|
|
58
52
|
'"' => """, "'" => "'" }.freeze
|
|
@@ -62,15 +56,14 @@ module Rigor
|
|
|
62
56
|
end
|
|
63
57
|
end
|
|
64
58
|
|
|
65
|
-
# SARIF 2.1.0 — the OASIS static-analysis interchange format. GitHub's
|
|
66
|
-
#
|
|
67
|
-
# diff and in the Security tab; it is the cross-platform anchor format.
|
|
59
|
+
# SARIF 2.1.0 — the OASIS static-analysis interchange format. GitHub's `codeql-action/upload-sarif` ingests it to
|
|
60
|
+
# render findings on the PR diff and in the Security tab; it is the cross-platform anchor format.
|
|
68
61
|
class Sarif
|
|
69
62
|
SCHEMA = "https://json.schemastore.org/sarif-2.1.0.json"
|
|
70
63
|
INFORMATION_URI = "https://github.com/rigortype/rigor"
|
|
71
64
|
|
|
72
|
-
# SARIF defines exactly three result levels; Rigor's `:info` is a
|
|
73
|
-
#
|
|
65
|
+
# SARIF defines exactly three result levels; Rigor's `:info` is a `note` (the SARIF spelling for advisory
|
|
66
|
+
# findings).
|
|
74
67
|
LEVELS = { error: "error", warning: "warning", info: "note" }.freeze
|
|
75
68
|
|
|
76
69
|
def initialize(result)
|
|
@@ -103,9 +96,8 @@ module Rigor
|
|
|
103
96
|
}
|
|
104
97
|
end
|
|
105
98
|
|
|
106
|
-
# The distinct rule ids seen in this run, declared so consumers can
|
|
107
|
-
#
|
|
108
|
-
# SARIF rule object; richer per-rule metadata is a later enrichment.
|
|
99
|
+
# The distinct rule ids seen in this run, declared so consumers can cross-reference each result's `ruleId`.
|
|
100
|
+
# Id-only is a valid minimal SARIF rule object; richer per-rule metadata is a later enrichment.
|
|
109
101
|
def rules
|
|
110
102
|
@result.diagnostics.filter_map(&:qualified_rule).uniq.map { |id| { "id" => id } }
|
|
111
103
|
end
|
|
@@ -121,9 +113,8 @@ module Rigor
|
|
|
121
113
|
entry
|
|
122
114
|
end
|
|
123
115
|
|
|
124
|
-
# Rigor lines / columns are already 1-based, matching SARIF's
|
|
125
|
-
#
|
|
126
|
-
# SARIF URIs use forward slashes on every platform.
|
|
116
|
+
# Rigor lines / columns are already 1-based, matching SARIF's 1-based `startLine` / `startColumn`. Paths are
|
|
117
|
+
# project-relative; SARIF URIs use forward slashes on every platform.
|
|
127
118
|
def location_for(diagnostic)
|
|
128
119
|
{
|
|
129
120
|
"physicalLocation" => {
|
|
@@ -134,9 +125,8 @@ module Rigor
|
|
|
134
125
|
end
|
|
135
126
|
end
|
|
136
127
|
|
|
137
|
-
# GitHub Actions workflow commands — `::<level> file=…,line=…,col=…,
|
|
138
|
-
#
|
|
139
|
-
# into inline PR annotations, with no separate upload step.
|
|
128
|
+
# GitHub Actions workflow commands — `::<level> file=…,line=…,col=…, title=…::<message>` lines the runner parses
|
|
129
|
+
# out of stdout and turns into inline PR annotations, with no separate upload step.
|
|
140
130
|
class GithubActions
|
|
141
131
|
# GitHub's annotation levels; Rigor's `:info` is a `notice`.
|
|
142
132
|
LEVELS = { error: "error", warning: "warning", info: "notice" }.freeze
|
|
@@ -160,26 +150,24 @@ module Rigor
|
|
|
160
150
|
"::#{level} #{props.join(',')}::#{escape_data(diagnostic.message)}"
|
|
161
151
|
end
|
|
162
152
|
|
|
163
|
-
# GitHub's documented workflow-command escaping: `%` and the CR / LF
|
|
164
|
-
#
|
|
153
|
+
# GitHub's documented workflow-command escaping: `%` and the CR / LF that would otherwise terminate the command
|
|
154
|
+
# line, for message data.
|
|
165
155
|
def escape_data(value)
|
|
166
156
|
value.to_s.gsub("%", "%25").gsub("\r", "%0D").gsub("\n", "%0A")
|
|
167
157
|
end
|
|
168
158
|
|
|
169
|
-
# Property values additionally escape the `,` (property separator)
|
|
170
|
-
#
|
|
159
|
+
# Property values additionally escape the `,` (property separator) and `:` (command terminator) so a path or
|
|
160
|
+
# rule id can carry them.
|
|
171
161
|
def escape_property(value)
|
|
172
162
|
escape_data(value).gsub(",", "%2C").gsub(":", "%3A")
|
|
173
163
|
end
|
|
174
164
|
end
|
|
175
165
|
|
|
176
|
-
# GitLab Code Quality report — the CodeClimate-subset JSON array GitLab
|
|
177
|
-
#
|
|
178
|
-
# Code Quality widget.
|
|
166
|
+
# GitLab Code Quality report — the CodeClimate-subset JSON array GitLab reads from a `codequality` CI artifact to
|
|
167
|
+
# populate the merge-request Code Quality widget.
|
|
179
168
|
class GitlabCodeQuality
|
|
180
|
-
# GitLab's severity vocabulary (a CodeClimate subset). Rigor maps
|
|
181
|
-
#
|
|
182
|
-
# are left for a louder future tier.
|
|
169
|
+
# GitLab's severity vocabulary (a CodeClimate subset). Rigor maps error → major, warning → minor, info → info;
|
|
170
|
+
# `critical` / `blocker` are left for a louder future tier.
|
|
183
171
|
SEVERITIES = { error: "major", warning: "minor", info: "info" }.freeze
|
|
184
172
|
|
|
185
173
|
def initialize(result)
|
|
@@ -205,17 +193,16 @@ module Rigor
|
|
|
205
193
|
}
|
|
206
194
|
end
|
|
207
195
|
|
|
208
|
-
# The rule id is folded into the description (the widget shows it)
|
|
209
|
-
#
|
|
196
|
+
# The rule id is folded into the description (the widget shows it) because Code Quality has no dedicated rule
|
|
197
|
+
# field.
|
|
210
198
|
def description(diagnostic)
|
|
211
199
|
rule_id = diagnostic.qualified_rule
|
|
212
200
|
rule_id ? "#{diagnostic.message} [#{rule_id}]" : diagnostic.message
|
|
213
201
|
end
|
|
214
202
|
|
|
215
|
-
# GitLab dedups findings by fingerprint and tracks them across runs
|
|
216
|
-
#
|
|
217
|
-
#
|
|
218
|
-
# message) satisfies both — order-independent, no run-volatile input.
|
|
203
|
+
# GitLab dedups findings by fingerprint and tracks them across runs by it, so it must be stable for an unchanged
|
|
204
|
+
# finding and unique per finding. Hashing the locating tuple (path, rule, line, column, message) satisfies both
|
|
205
|
+
# — order-independent, no run-volatile input.
|
|
219
206
|
def fingerprint(diagnostic)
|
|
220
207
|
payload = [diagnostic.path, diagnostic.qualified_rule, diagnostic.line,
|
|
221
208
|
diagnostic.column, diagnostic.message].join("")
|
|
@@ -223,12 +210,10 @@ module Rigor
|
|
|
223
210
|
end
|
|
224
211
|
end
|
|
225
212
|
|
|
226
|
-
# Checkstyle XML — the lint-interchange format a wide range of tools
|
|
227
|
-
#
|
|
228
|
-
#
|
|
229
|
-
# are
|
|
230
|
-
# code reviewdog surfaces). Checkstyle's native severities are
|
|
231
|
-
# `error` / `warning` / `info`, so Rigor's map through unchanged.
|
|
213
|
+
# Checkstyle XML — the lint-interchange format a wide range of tools read, most usefully reviewdog
|
|
214
|
+
# (`-f=checkstyle`), which then posts to any of its reporters (GitHub PR review, GitLab MR, Gerrit, …). Errors are
|
|
215
|
+
# grouped by file; the qualified rule rides in `source` (the rule code reviewdog surfaces). Checkstyle's native
|
|
216
|
+
# severities are `error` / `warning` / `info`, so Rigor's map through unchanged.
|
|
232
217
|
class Checkstyle
|
|
233
218
|
include XmlEscaping
|
|
234
219
|
|
|
@@ -258,12 +243,10 @@ module Rigor
|
|
|
258
243
|
end
|
|
259
244
|
end
|
|
260
245
|
|
|
261
|
-
# JUnit XML — the test-report format GitHub's test reporting, GitLab,
|
|
262
|
-
#
|
|
263
|
-
#
|
|
264
|
-
#
|
|
265
|
-
# are visible in the report. The exit code (errors only) remains the
|
|
266
|
-
# gate; this view is for surfacing, not gating.
|
|
246
|
+
# JUnit XML — the test-report format GitHub's test reporting, GitLab, Jenkins, and CircleCI render. Following the
|
|
247
|
+
# established linter convention (rubocop / eslint / PHPStan): every diagnostic is a `testcase` carrying a
|
|
248
|
+
# `failure` typed by its severity, so all of them are visible in the report. The exit code (errors only) remains
|
|
249
|
+
# the gate; this view is for surfacing, not gating.
|
|
267
250
|
class Junit
|
|
268
251
|
include XmlEscaping
|
|
269
252
|
|
|
@@ -299,11 +282,9 @@ module Rigor
|
|
|
299
282
|
end
|
|
300
283
|
end
|
|
301
284
|
|
|
302
|
-
# TeamCity inspection service messages — the `##teamcity[…]` lines a
|
|
303
|
-
#
|
|
304
|
-
#
|
|
305
|
-
# CI-detection auto-emits. One `inspectionType` declares the category;
|
|
306
|
-
# each diagnostic is an `inspection` typed by severity.
|
|
285
|
+
# TeamCity inspection service messages — the `##teamcity[…]` lines a TeamCity build agent parses out of the build
|
|
286
|
+
# log into its Inspections view. The one stdout-native format (besides `github`) that CI-detection auto-emits. One
|
|
287
|
+
# `inspectionType` declares the category; each diagnostic is an `inspection` typed by severity.
|
|
307
288
|
class Teamcity
|
|
308
289
|
SEVERITIES = { error: "ERROR", warning: "WARNING", info: "INFO" }.freeze
|
|
309
290
|
|
|
@@ -7,10 +7,8 @@ require "optionparser"
|
|
|
7
7
|
|
|
8
8
|
module Rigor
|
|
9
9
|
class CLI
|
|
10
|
-
# Executes `rigor diff <baseline.json> [paths...]`. Compares
|
|
11
|
-
# the
|
|
12
|
-
# baseline JSON (the output of a previous `rigor check
|
|
13
|
-
# --format=json` run) and prints the delta:
|
|
10
|
+
# Executes `rigor diff <baseline.json> [paths...]`. Compares the current `rigor check` diagnostics against a saved
|
|
11
|
+
# baseline JSON (the output of a previous `rigor check --format=json` run) and prints the delta:
|
|
14
12
|
#
|
|
15
13
|
# - **new** — diagnostics in the current run that were not
|
|
16
14
|
# in the baseline (typically a regression introduced in
|
|
@@ -18,19 +16,13 @@ module Rigor
|
|
|
18
16
|
# - **fixed** — diagnostics in the baseline that no longer
|
|
19
17
|
# appear in the current run (typically progress).
|
|
20
18
|
#
|
|
21
|
-
# Identity for matching is the tuple
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# one fixed + one new pair, which lines up with the user's
|
|
25
|
-
# mental model of "you changed the line, the analyzer's
|
|
26
|
-
# position changed too."
|
|
19
|
+
# Identity for matching is the tuple `(path, line, column, rule, source_family, message)`. An edit that moves a
|
|
20
|
+
# diagnostic to a new line surfaces as one fixed + one new pair, which lines up with the user's mental model of "you
|
|
21
|
+
# changed the line, the analyzer's position changed too."
|
|
27
22
|
#
|
|
28
|
-
# CI usage: commit a `rigor.baseline.json` produced once
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
# is `1` when any new diagnostic appears, `0` otherwise —
|
|
32
|
-
# so adding new errors fails CI but legacy errors recorded
|
|
33
|
-
# in the baseline don't.
|
|
23
|
+
# CI usage: commit a `rigor.baseline.json` produced once with `rigor check --format=json > rigor.baseline.json`,
|
|
24
|
+
# then run `rigor diff rigor.baseline.json` in CI. Exit code is `1` when any new diagnostic appears, `0` otherwise —
|
|
25
|
+
# so adding new errors fails CI but legacy errors recorded in the baseline don't.
|
|
34
26
|
class DiffCommand < Command
|
|
35
27
|
USAGE = "Usage: rigor diff [options] <baseline.json> [paths...]"
|
|
36
28
|
|
|
@@ -77,10 +69,8 @@ module Rigor
|
|
|
77
69
|
options
|
|
78
70
|
end
|
|
79
71
|
|
|
80
|
-
# Runs `rigor check` against the remaining argv (or the
|
|
81
|
-
#
|
|
82
|
-
# Reuses the analyzer + configuration plumbing the
|
|
83
|
-
# check-command path uses.
|
|
72
|
+
# Runs `rigor check` against the remaining argv (or the configured paths) and returns the diagnostics array.
|
|
73
|
+
# Reuses the analyzer + configuration plumbing the check-command path uses.
|
|
84
74
|
def run_current(options)
|
|
85
75
|
require_relative "../analysis/runner"
|
|
86
76
|
require_relative "../configuration"
|