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
data/lib/rigor/sig_gen/writer.rb
CHANGED
|
@@ -8,64 +8,47 @@ require_relative "write_result"
|
|
|
8
8
|
|
|
9
9
|
module Rigor
|
|
10
10
|
module SigGen
|
|
11
|
-
# Applies a per-source-file group of {MethodCandidate}s to
|
|
12
|
-
#
|
|
11
|
+
# Applies a per-source-file group of {MethodCandidate}s to the target `.rbs` file under the project
|
|
12
|
+
# signature tree.
|
|
13
13
|
#
|
|
14
|
-
# ADR-14 slice 2: the writer parses the target with
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# closing `end` keyword. Existing declarations are NEVER
|
|
18
|
-
# touched unless `--overwrite` is set AND the candidate's
|
|
19
|
-
# classification is `tighter-return`.
|
|
14
|
+
# ADR-14 slice 2: the writer parses the target with `RBS::Parser` to find the matching class declaration and
|
|
15
|
+
# inserts new method declarations just before the class's closing `end` keyword. Existing declarations are
|
|
16
|
+
# NEVER touched unless `--overwrite` is set AND the candidate's classification is `tighter-return`.
|
|
20
17
|
#
|
|
21
|
-
# The slice does NOT re-render the whole file through
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
# the *same* class declaration may still lose trailing
|
|
27
|
-
# blank lines on the touched ranges; the `--diff` review
|
|
28
|
-
# surface from slice 1 is the user's escape hatch.
|
|
18
|
+
# The slice does NOT re-render the whole file through `RBS::Writer`. That would lose comments / blank-line
|
|
19
|
+
# formatting per upstream design; the byte-range insertion approach taken here preserves untouched
|
|
20
|
+
# declarations verbatim. Mixed hand-written + generated output inside the *same* class declaration may still
|
|
21
|
+
# lose trailing blank lines on the touched ranges; the `--diff` review surface from slice 1 is the user's
|
|
22
|
+
# escape hatch.
|
|
29
23
|
#
|
|
30
|
-
# Safety boundary: the writer ASSERTS the target lives
|
|
31
|
-
#
|
|
32
|
-
# disk. Files outside that tree route through
|
|
33
|
-
# `WriteResult(action: :skipped_outside_sig_root)`; the
|
|
24
|
+
# Safety boundary: the writer ASSERTS the target lives inside the configured signature tree before touching
|
|
25
|
+
# the disk. Files outside that tree route through `WriteResult(action: :skipped_outside_sig_root)`; the
|
|
34
26
|
# caller decides whether to warn or fail.
|
|
35
27
|
class Writer # rubocop:disable Metrics/ClassLength
|
|
36
28
|
INDENT = " "
|
|
37
29
|
private_constant :INDENT
|
|
38
30
|
|
|
39
|
-
# Per-`update_existing` accumulator. The merge_class
|
|
40
|
-
#
|
|
41
|
-
# `skipped` in place as each class is processed so the
|
|
42
|
-
# next class sees the latest byte positions.
|
|
31
|
+
# Per-`update_existing` accumulator. The merge_class helper mutates `source` / `decls` / `applied` /
|
|
32
|
+
# `skipped` in place as each class is processed so the next class sees the latest byte positions.
|
|
43
33
|
MergeState = Struct.new(:source, :decls, :applied, :skipped, keyword_init: true)
|
|
44
34
|
private_constant :MergeState
|
|
45
35
|
|
|
46
36
|
def initialize(path_mapper:, overwrite: false)
|
|
47
37
|
@path_mapper = path_mapper
|
|
48
38
|
@overwrite = overwrite
|
|
49
|
-
# Run-level (cross-file) namespace-kind view, populated
|
|
50
|
-
#
|
|
51
|
-
# Empty until then so the single-target `#write` path
|
|
52
|
-
# falls back to per-candidate kinds only.
|
|
39
|
+
# Run-level (cross-file) namespace-kind view, populated per `#write_all` from every candidate's per-file
|
|
40
|
+
# map. Empty until then so the single-target `#write` path falls back to per-candidate kinds only.
|
|
53
41
|
@global_namespace_kinds = {}
|
|
54
|
-
# Run-level qualified-class-name => superclass-token view,
|
|
55
|
-
# same lifecycle as `@global_namespace_kinds`.
|
|
42
|
+
# Run-level qualified-class-name => superclass-token view, same lifecycle as `@global_namespace_kinds`.
|
|
56
43
|
@global_superclasses = {}
|
|
57
44
|
end
|
|
58
45
|
|
|
59
|
-
# Process the full candidate list by resolving each
|
|
60
|
-
#
|
|
61
|
-
# may route consolidated-layout classes to existing
|
|
62
|
-
# files) and grouping candidates that share a target
|
|
46
|
+
# Process the full candidate list by resolving each candidate's target sig file via the path mapper (which
|
|
47
|
+
# may route consolidated-layout classes to existing files) and grouping candidates that share a target
|
|
63
48
|
# before writing.
|
|
64
49
|
#
|
|
65
|
-
# ADR-14 follow-up: this is the consolidated-layout
|
|
66
|
-
#
|
|
67
|
-
# below assumes all candidates share a target and
|
|
68
|
-
# remains for spec convenience.
|
|
50
|
+
# ADR-14 follow-up: this is the consolidated-layout entry point. The legacy `write(source_path,
|
|
51
|
+
# candidates)` below assumes all candidates share a target and remains for spec convenience.
|
|
69
52
|
#
|
|
70
53
|
# @param candidates [Array<MethodCandidate>]
|
|
71
54
|
# @return [Array<WriteResult>] one per target sig file.
|
|
@@ -95,10 +78,8 @@ module Rigor
|
|
|
95
78
|
|
|
96
79
|
private
|
|
97
80
|
|
|
98
|
-
# Shared per-target write path used by both `#write` and
|
|
99
|
-
#
|
|
100
|
-
# the {WriteResult} when multiple candidates merge into
|
|
101
|
-
# one target.
|
|
81
|
+
# Shared per-target write path used by both `#write` and `#write_all`. Picks a representative
|
|
82
|
+
# `source_path` for the {WriteResult} when multiple candidates merge into one target.
|
|
102
83
|
def write_target(target, candidates, source_path: nil)
|
|
103
84
|
source_path ||= candidates.first&.path
|
|
104
85
|
unless inside_sig_root?(target)
|
|
@@ -116,8 +97,7 @@ module Rigor
|
|
|
116
97
|
root = @path_mapper.sig_root_dir.realpath
|
|
117
98
|
target.expand_path.ascend.any? { |ancestor| realpath_or_nil(ancestor) == root }
|
|
118
99
|
rescue Errno::ENOENT
|
|
119
|
-
# The sig root doesn't exist yet; we'll create it
|
|
120
|
-
# alongside the target file. Allow this case.
|
|
100
|
+
# The sig root doesn't exist yet; we'll create it alongside the target file. Allow this case.
|
|
121
101
|
target.expand_path.to_s.start_with?(@path_mapper.sig_root_dir.expand_path.to_s)
|
|
122
102
|
end
|
|
123
103
|
|
|
@@ -134,26 +114,17 @@ module Rigor
|
|
|
134
114
|
action: :created, applied: candidates)
|
|
135
115
|
end
|
|
136
116
|
|
|
137
|
-
# ADR-14 gap-#3 follow-up (c): when one candidate's
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
# earlier `group_by(&:class_name)` flattened that into
|
|
144
|
-
# two top-level wraps (one for GemResolver's own
|
|
145
|
-
# methods, one for GemResolver::Resolved's), which
|
|
146
|
-
# Steep accepted but is not the canonical layout in
|
|
147
|
-
# this project's `sig/`.
|
|
117
|
+
# ADR-14 gap-#3 follow-up (c): when one candidate's `class_name` is a strict prefix of another's, emit a
|
|
118
|
+
# single nested tree instead of two flat sibling blocks. The third-round self-dogfood surfaced
|
|
119
|
+
# `Analysis::DependencySourceInference::GemResolver` containing `class Resolved < Data.define(...)` — the
|
|
120
|
+
# earlier `group_by(&:class_name)` flattened that into two top-level wraps (one for GemResolver's own
|
|
121
|
+
# methods, one for GemResolver::Resolved's), which Steep accepted but is not the canonical layout in this
|
|
122
|
+
# project's `sig/`.
|
|
148
123
|
#
|
|
149
|
-
# The writer now builds a tree keyed by qualified-name
|
|
150
|
-
#
|
|
151
|
-
#
|
|
152
|
-
#
|
|
153
|
-
# block. Empty class shells (gap-#3 follow-up (e), e.g.
|
|
154
|
-
# `Unresolvable = Data.define(...)`) participate as
|
|
155
|
-
# zero-method tree nodes — they emit `class Foo\nend`
|
|
156
|
-
# at their position.
|
|
124
|
+
# The writer now builds a tree keyed by qualified-name segments. Each tree node carries (qualified_name,
|
|
125
|
+
# methods, shells, children); rendering walks the tree so every nested class appears inside its parent's
|
|
126
|
+
# block. Empty class shells (gap-#3 follow-up (e), e.g. `Unresolvable = Data.define(...)`) participate as
|
|
127
|
+
# zero-method tree nodes — they emit `class Foo\nend` at their position.
|
|
157
128
|
def render_new_file(candidates)
|
|
158
129
|
shells = collect_class_shells(candidates)
|
|
159
130
|
tree = build_namespace_tree(candidates, shells)
|
|
@@ -162,8 +133,7 @@ module Rigor
|
|
|
162
133
|
render_tree_nodes(tree, kinds, supers, 0)
|
|
163
134
|
end
|
|
164
135
|
|
|
165
|
-
# Drains `class_shells` from every candidate; the
|
|
166
|
-
# generator's walker populates the same set on every
|
|
136
|
+
# Drains `class_shells` from every candidate; the generator's walker populates the same set on every
|
|
167
137
|
# candidate produced from a given file (gap-#3 (e)).
|
|
168
138
|
def collect_class_shells(candidates)
|
|
169
139
|
shells = Set.new
|
|
@@ -179,29 +149,22 @@ module Rigor
|
|
|
179
149
|
merged
|
|
180
150
|
end
|
|
181
151
|
|
|
182
|
-
# Folds every candidate's per-file namespace-kind map
|
|
183
|
-
#
|
|
184
|
-
#
|
|
185
|
-
#
|
|
186
|
-
# `foo
|
|
187
|
-
#
|
|
188
|
-
# no kind for it. Without this view that sibling target
|
|
189
|
-
# wraps the nested class in `module Foo` while `foo.rbs`
|
|
190
|
-
# declares `class Foo`; loading both raises
|
|
191
|
-
# `RBS::DuplicatedDeclarationError`, aborting the whole
|
|
192
|
-
# RBS env build.
|
|
152
|
+
# Folds every candidate's per-file namespace-kind map into one run-level view so a `class Foo` recorded
|
|
153
|
+
# while scanning `foo.rb` governs the wrapper keyword emitted for `Foo` in a *sibling* file's target — e.g.
|
|
154
|
+
# `foo/bar.rb` declaring `class Foo::Bar`, whose compact constant path never names `Foo`, so the walker
|
|
155
|
+
# records no kind for it. Without this view that sibling target wraps the nested class in `module Foo`
|
|
156
|
+
# while `foo.rbs` declares `class Foo`; loading both raises `RBS::DuplicatedDeclarationError`, aborting the
|
|
157
|
+
# whole RBS env build.
|
|
193
158
|
def build_namespace_kinds(candidates)
|
|
194
159
|
candidates.each_with_object({}) do |candidate, acc|
|
|
195
160
|
(candidate.namespace_kinds || {}).each { |name, kind| apply_namespace_kind(acc, name, kind) }
|
|
196
161
|
end
|
|
197
162
|
end
|
|
198
163
|
|
|
199
|
-
# Superclass twins of {#merged_namespace_kinds} /
|
|
200
|
-
#
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
-
# ever recorded (the generator skips computed ones), so a
|
|
204
|
-
# missing key means "emit no superclass", never "unknown".
|
|
164
|
+
# Superclass twins of {#merged_namespace_kinds} / {#build_namespace_kinds}: fold every candidate's
|
|
165
|
+
# per-file `class_superclasses` map into one view keyed by qualified class name. Only plain-constant
|
|
166
|
+
# superclasses are ever recorded (the generator skips computed ones), so a missing key means "emit no
|
|
167
|
+
# superclass", never "unknown".
|
|
205
168
|
def merged_superclasses(candidates)
|
|
206
169
|
merged = @global_superclasses.dup
|
|
207
170
|
candidates.each do |c|
|
|
@@ -220,13 +183,10 @@ module Rigor
|
|
|
220
183
|
end
|
|
221
184
|
end
|
|
222
185
|
|
|
223
|
-
# A `class` declaration is authoritative and MUST win
|
|
224
|
-
#
|
|
225
|
-
# `
|
|
226
|
-
#
|
|
227
|
-
# `Const = Data.define(...)` shell) seen elsewhere. This
|
|
228
|
-
# guarantees the generated tree never mixes `class` /
|
|
229
|
-
# `module` for the same constant.
|
|
186
|
+
# A `class` declaration is authoritative and MUST win over the `:module` wrapper default: a compact
|
|
187
|
+
# `class Foo::Bar` never names `Foo`, so the only signal for `Foo`'s kind is an actual `class Foo` (or
|
|
188
|
+
# `Const = Data.define(...)` shell) seen elsewhere. This guarantees the generated tree never mixes
|
|
189
|
+
# `class` / `module` for the same constant.
|
|
230
190
|
def apply_namespace_kind(map, key, kind)
|
|
231
191
|
if kind == :class
|
|
232
192
|
map[key] = :class
|
|
@@ -235,12 +195,9 @@ module Rigor
|
|
|
235
195
|
end
|
|
236
196
|
end
|
|
237
197
|
|
|
238
|
-
# Tree node: { name:, children: Hash{String => node},
|
|
239
|
-
# methods
|
|
240
|
-
#
|
|
241
|
-
# only (no methods of their own); rendering uses it to
|
|
242
|
-
# default the keyword to `:class` for the `class Const`
|
|
243
|
-
# = `Data.define(...)` case.
|
|
198
|
+
# Tree node: { name:, children: Hash{String => node}, methods: Array<MethodCandidate>, shell: Boolean }.
|
|
199
|
+
# `shell` flags nodes that came in via `class_shells` only (no methods of their own); rendering uses it to
|
|
200
|
+
# default the keyword to `:class` for the `class Const` = `Data.define(...)` case.
|
|
244
201
|
def build_namespace_tree(candidates, shells)
|
|
245
202
|
root = { name: nil, children: {}, methods: [], shell: false }
|
|
246
203
|
candidates.group_by(&:class_name).each do |class_name, methods|
|
|
@@ -287,12 +244,9 @@ module Rigor
|
|
|
287
244
|
method_lines + child_blocks
|
|
288
245
|
end
|
|
289
246
|
|
|
290
|
-
# ` < Super` for a `class` node whose qualified name has a
|
|
291
|
-
#
|
|
292
|
-
#
|
|
293
|
-
# gates emission — a name coincidentally recorded as both
|
|
294
|
-
# (impossible from one source class, but cheap to guard)
|
|
295
|
-
# stays a bare module.
|
|
247
|
+
# ` < Super` for a `class` node whose qualified name has a recorded superclass, else the empty string. A
|
|
248
|
+
# `module` never takes a superclass (RBS forbids it), so the keyword gates emission — a name coincidentally
|
|
249
|
+
# recorded as both (impossible from one source class, but cheap to guard) stays a bare module.
|
|
296
250
|
def superclass_suffix(keyword, supers, qualified)
|
|
297
251
|
return "" unless keyword == :class
|
|
298
252
|
|
|
@@ -300,14 +254,10 @@ module Rigor
|
|
|
300
254
|
superclass ? " < #{superclass}" : ""
|
|
301
255
|
end
|
|
302
256
|
|
|
303
|
-
# Per ADR-14 gap-#3 (a) the keyword for a segment comes
|
|
304
|
-
#
|
|
305
|
-
#
|
|
306
|
-
#
|
|
307
|
-
# `:class` for a method-bearing leaf node and `:module`
|
|
308
|
-
# for an intermediate (children-only) segment. Defaulting
|
|
309
|
-
# intermediates to `:module` matches RBS's "multiple
|
|
310
|
-
# `module Foo` declarations merge" rule.
|
|
257
|
+
# Per ADR-14 gap-#3 (a) the keyword for a segment comes from `namespace_kinds` when known. The default for
|
|
258
|
+
# an explicit class shell (gap-#3 (e), `Const = Data.define(...)`) is `:class`; otherwise default to
|
|
259
|
+
# `:class` for a method-bearing leaf node and `:module` for an intermediate (children-only) segment.
|
|
260
|
+
# Defaulting intermediates to `:module` matches RBS's "multiple `module Foo` declarations merge" rule.
|
|
311
261
|
def node_keyword(node, kinds, qualified)
|
|
312
262
|
return kinds.fetch(qualified) if kinds.key?(qualified)
|
|
313
263
|
return :class if node[:shell]
|
|
@@ -332,15 +282,11 @@ module Rigor
|
|
|
332
282
|
action: action, applied: state.applied, skipped: state.skipped)
|
|
333
283
|
end
|
|
334
284
|
|
|
335
|
-
# ADR-14 gap-#3 (e): for every requested class shell
|
|
336
|
-
#
|
|
337
|
-
#
|
|
338
|
-
#
|
|
339
|
-
#
|
|
340
|
-
# `applied` accumulator does NOT grow — shells are
|
|
341
|
-
# structural declarations, not methods, so the
|
|
342
|
-
# action-count surface (`updated +N`) keeps
|
|
343
|
-
# reflecting method changes only.
|
|
285
|
+
# ADR-14 gap-#3 (e): for every requested class shell that isn't already declared in the target file,
|
|
286
|
+
# insert an empty `class Const\nend` block inside the nearest existing ancestor. Shells already covered by
|
|
287
|
+
# an existing declaration are silently a no-op. The `applied` accumulator does NOT grow — shells are
|
|
288
|
+
# structural declarations, not methods, so the action-count surface (`updated +N`) keeps reflecting method
|
|
289
|
+
# changes only.
|
|
344
290
|
def merge_class_shells(state, shells, kinds)
|
|
345
291
|
shells.each do |qualified|
|
|
346
292
|
next if find_class_decl(state.decls, qualified)
|
|
@@ -371,11 +317,9 @@ module Rigor
|
|
|
371
317
|
[[], segments]
|
|
372
318
|
end
|
|
373
319
|
|
|
374
|
-
# Pulls the indent depth (in `INDENT` units) one level
|
|
375
|
-
#
|
|
376
|
-
#
|
|
377
|
-
# `class Foo; end`) so the keyword column is the
|
|
378
|
-
# robust signal.
|
|
320
|
+
# Pulls the indent depth (in `INDENT` units) one level deeper than the anchor decl's own column.
|
|
321
|
+
# Pre-existing members might be missing (an empty `class Foo; end`) so the keyword column is the robust
|
|
322
|
+
# signal.
|
|
379
323
|
def anchor_decl_indent_depth(decl)
|
|
380
324
|
decl_column = decl.location[:keyword].start_column
|
|
381
325
|
(decl_column / INDENT.size) + 1
|
|
@@ -423,12 +367,9 @@ module Rigor
|
|
|
423
367
|
state.decls = parse_signature(state.source) || state.decls
|
|
424
368
|
end
|
|
425
369
|
|
|
426
|
-
# Walks the parsed decl tree recursively, tracking the
|
|
427
|
-
#
|
|
428
|
-
#
|
|
429
|
-
# `qualified_name`. Recursing into modules lets us
|
|
430
|
-
# match `Rigor::Type::Nominal` against the
|
|
431
|
-
# `class Nominal` declaration nested inside
|
|
370
|
+
# Walks the parsed decl tree recursively, tracking the enclosing module/class prefix, and returns the
|
|
371
|
+
# declaration whose fully-qualified name matches `qualified_name`. Recursing into modules lets us match
|
|
372
|
+
# `Rigor::Type::Nominal` against the `class Nominal` declaration nested inside
|
|
432
373
|
# `module Rigor; module Type; … end; end`.
|
|
433
374
|
def find_class_decl(decls, qualified_name)
|
|
434
375
|
find_class_decl_in(decls, [], qualified_name)
|
|
@@ -448,8 +389,7 @@ module Rigor
|
|
|
448
389
|
nil
|
|
449
390
|
end
|
|
450
391
|
|
|
451
|
-
# Appends an entirely new `class Foo … end` block at the
|
|
452
|
-
# end of the file (with a leading blank line as
|
|
392
|
+
# Appends an entirely new `class Foo … end` block at the end of the file (with a leading blank line as
|
|
453
393
|
# separator).
|
|
454
394
|
def append_new_class(source, class_name, methods, applied, superclass = nil)
|
|
455
395
|
body = methods.map { |c| "#{INDENT}#{c.rbs}" }.join("\n")
|
|
@@ -481,13 +421,10 @@ module Rigor
|
|
|
481
421
|
source
|
|
482
422
|
end
|
|
483
423
|
|
|
484
|
-
# Returns a list of `[method_name (Symbol), kind (Symbol)]`
|
|
485
|
-
#
|
|
486
|
-
#
|
|
487
|
-
#
|
|
488
|
-
# `attr_*` declaration kinds so a source-side
|
|
489
|
-
# `attr_reader :name` and an RBS-side `attr_reader name: T`
|
|
490
|
-
# are treated as the same member (i.e. user-authored).
|
|
424
|
+
# Returns a list of `[method_name (Symbol), kind (Symbol)]` pairs for every method-like member in the
|
|
425
|
+
# declaration. ADR-14 slice 4 recognises `MethodDefinition`'s `:instance` / `:singleton` kind plus the
|
|
426
|
+
# three `attr_*` declaration kinds so a source-side `attr_reader :name` and an RBS-side
|
|
427
|
+
# `attr_reader name: T` are treated as the same member (i.e. user-authored).
|
|
491
428
|
def collect_member_pairs(decl)
|
|
492
429
|
pairs = []
|
|
493
430
|
decl.members.each { |m| collect_pairs_for_member(m, pairs) }
|
|
@@ -512,10 +449,9 @@ module Rigor
|
|
|
512
449
|
methods.partition { |c| !existing_pairs.include?([c.method_name, c.kind]) }
|
|
513
450
|
end
|
|
514
451
|
|
|
515
|
-
# Inserts each new method line one column before the
|
|
516
|
-
#
|
|
517
|
-
#
|
|
518
|
-
# the surrounding source's whitespace stays intact.
|
|
452
|
+
# Inserts each new method line one column before the class declaration's `end` keyword. The insertion
|
|
453
|
+
# text carries its own leading indent + trailing newline so the surrounding source's whitespace stays
|
|
454
|
+
# intact.
|
|
519
455
|
def insert_into_class(source, decl, new_methods)
|
|
520
456
|
return source if new_methods.empty?
|
|
521
457
|
|
|
@@ -524,34 +460,25 @@ module Rigor
|
|
|
524
460
|
source[0...end_pos] + addition + source[end_pos..]
|
|
525
461
|
end
|
|
526
462
|
|
|
527
|
-
# Walks the class's existing method declarations; for
|
|
528
|
-
#
|
|
529
|
-
# name, slices out the old declaration's source range
|
|
530
|
-
# and substitutes the new RBS one-liner. Members that
|
|
463
|
+
# Walks the class's existing method declarations; for each replaceable candidate that matches a member
|
|
464
|
+
# name, slices out the old declaration's source range and substitutes the new RBS one-liner. Members that
|
|
531
465
|
# are not `MethodDefinition`s are left alone.
|
|
532
466
|
#
|
|
533
|
-
# Two candidate classifications are eligible for
|
|
534
|
-
# replacement under `--overwrite`:
|
|
467
|
+
# Two candidate classifications are eligible for replacement under `--overwrite`:
|
|
535
468
|
#
|
|
536
|
-
# 1. `TIGHTER_RETURN` — the classifier already proved the
|
|
537
|
-
#
|
|
538
|
-
#
|
|
539
|
-
#
|
|
540
|
-
# `
|
|
541
|
-
#
|
|
542
|
-
# bypasses the existing-RBS comparison and always
|
|
543
|
-
# classifies as `NEW_METHOD` — when sig-gen's
|
|
544
|
-
# `--params=observed` upgrades a `(path: untyped) -> void`
|
|
545
|
-
# declaration to `(path: String) -> void` we want
|
|
546
|
-
# `--overwrite` to apply it.
|
|
469
|
+
# 1. `TIGHTER_RETURN` — the classifier already proved the new return type is a strict subtype of the
|
|
470
|
+
# declared one (with lenience guards passed).
|
|
471
|
+
# 2. `NEW_METHOD` whose new RBS strictly tightens an `untyped` position in the existing declaration. The
|
|
472
|
+
# canonical case is `initialize_stub_candidate`, which bypasses the existing-RBS comparison and always
|
|
473
|
+
# classifies as `NEW_METHOD` — when sig-gen's `--params=observed` upgrades a `(path: untyped) -> void`
|
|
474
|
+
# declaration to `(path: String) -> void` we want `--overwrite` to apply it.
|
|
547
475
|
def replace_eligible_conflicts(source, decl, candidates)
|
|
548
476
|
eligible = candidates.select { |c| eligible_for_replacement?(c, decl, source) }
|
|
549
477
|
return [source, []] if eligible.empty?
|
|
550
478
|
|
|
551
479
|
replaced = []
|
|
552
|
-
# Apply replacements from highest byte position downward
|
|
553
|
-
#
|
|
554
|
-
# grows or shrinks.
|
|
480
|
+
# Apply replacements from highest byte position downward so earlier byte offsets remain valid as the
|
|
481
|
+
# source grows or shrinks.
|
|
555
482
|
sorted = eligible.sort_by { |c| -member_position(decl, c.method_name, c.kind) }
|
|
556
483
|
sorted.each do |candidate|
|
|
557
484
|
source = apply_replacement(source, decl, candidate) and replaced << candidate
|
|
@@ -567,14 +494,10 @@ module Rigor
|
|
|
567
494
|
end
|
|
568
495
|
end
|
|
569
496
|
|
|
570
|
-
# Compares the existing member's source-side RBS text
|
|
571
|
-
#
|
|
572
|
-
#
|
|
573
|
-
# `untyped`
|
|
574
|
-
# least one `untyped` slot becomes a concrete type AND
|
|
575
|
-
# no concrete slot becomes `untyped`. Word-boundary
|
|
576
|
-
# matching ensures we count `untyped` only as a type
|
|
577
|
-
# token, not as a substring inside identifiers.
|
|
497
|
+
# Compares the existing member's source-side RBS text against the candidate's proposed RBS text. Returns
|
|
498
|
+
# true when the new spelling has STRICTLY FEWER bare `untyped` tokens than the existing one — i.e. at
|
|
499
|
+
# least one `untyped` slot becomes a concrete type AND no concrete slot becomes `untyped`. Word-boundary
|
|
500
|
+
# matching ensures we count `untyped` only as a type token, not as a substring inside identifiers.
|
|
578
501
|
def tightens_untyped?(candidate, decl, source)
|
|
579
502
|
member = find_method_member(decl, candidate.method_name, candidate.kind)
|
|
580
503
|
return false if member.nil?
|
|
@@ -598,10 +521,8 @@ module Rigor
|
|
|
598
521
|
end
|
|
599
522
|
end
|
|
600
523
|
|
|
601
|
-
# Splices the new RBS one-liner over the existing
|
|
602
|
-
#
|
|
603
|
-
# starts at the `def` keyword, NOT at the column zero of
|
|
604
|
-
# the line, so the leading whitespace stays inside
|
|
524
|
+
# Splices the new RBS one-liner over the existing declaration's byte range. `RBS::Parser`'s location
|
|
525
|
+
# starts at the `def` keyword, NOT at the column zero of the line, so the leading whitespace stays inside
|
|
605
526
|
# `source[0...start_pos]` and we do not re-emit it.
|
|
606
527
|
def apply_replacement(source, decl, candidate)
|
|
607
528
|
member = find_method_member(decl, candidate.method_name, candidate.kind)
|
data/lib/rigor/sig_gen.rb
CHANGED
|
@@ -13,13 +13,10 @@ require_relative "sig_gen/write_result"
|
|
|
13
13
|
require_relative "sig_gen/writer"
|
|
14
14
|
|
|
15
15
|
module Rigor
|
|
16
|
-
# Namespace for the RBS signature generator that powers
|
|
17
|
-
# `rigor sig-gen` (ADR-14).
|
|
16
|
+
# Namespace for the RBS signature generator that powers `rigor sig-gen` (ADR-14).
|
|
18
17
|
#
|
|
19
|
-
# The generator emits RBS from Rigor's inference results so
|
|
20
|
-
#
|
|
21
|
-
# See `docs/adr/14-rbs-sig-generation.md` for the design
|
|
22
|
-
# rationale and the slicing plan.
|
|
18
|
+
# The generator emits RBS from Rigor's inference results so users close RBS coverage gaps without freehand
|
|
19
|
+
# authorship. See `docs/adr/14-rbs-sig-generation.md` for the design rationale and the slicing plan.
|
|
23
20
|
module SigGen
|
|
24
21
|
end
|
|
25
22
|
end
|
|
@@ -1,33 +1,29 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
|
-
# Classifies each configured `signature_paths:` entry by what it
|
|
5
|
-
#
|
|
6
|
-
# when a configured path resolves to nothing.
|
|
4
|
+
# Classifies each configured `signature_paths:` entry by what it actually contributes to
|
|
5
|
+
# the RBS environment, so a caller can warn when a configured path resolves to nothing.
|
|
7
6
|
#
|
|
8
|
-
# The failure this guards against is silent. {Environment::RbsLoader}
|
|
9
|
-
# `
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# hundreds of plausible-looking false positives; surfacing the empty
|
|
17
|
-
# entry makes the real cause visible.
|
|
7
|
+
# The failure this guards against is silent. {Environment::RbsLoader} `add`s a
|
|
8
|
+
# `signature_paths:` entry only when `path.directory?`, and only the `.rbs` files under it
|
|
9
|
+
# carry signatures — so a typo'd or moved path (or a directory holding no `.rbs`) loads
|
|
10
|
+
# zero signatures with no trace on stderr or in the run summary. The downstream symptom is
|
|
11
|
+
# the most authoritative diagnostics: every call into the extensions the missing RBS was
|
|
12
|
+
# meant to describe fires `call.undefined-method` at `evidence_tier: high`. A one-character
|
|
13
|
+
# path typo can manufacture hundreds of plausible-looking false positives; surfacing the
|
|
14
|
+
# empty entry makes the real cause visible.
|
|
18
15
|
#
|
|
19
|
-
# The audit deliberately mirrors the loader's own acceptance test
|
|
20
|
-
#
|
|
21
|
-
#
|
|
16
|
+
# The audit deliberately mirrors the loader's own acceptance test (`path.directory?` + a
|
|
17
|
+
# recursive `**/*.rbs` glob) so a `:ok` verdict means the loader did load from it and a
|
|
18
|
+
# warning means it did not.
|
|
22
19
|
module SignaturePathAudit
|
|
23
20
|
# One configured `signature_paths:` entry's resolution status.
|
|
24
21
|
#
|
|
25
22
|
# `status` is one of:
|
|
26
23
|
# - `:ok` — a directory containing at least one `.rbs`.
|
|
27
24
|
# - `:missing` — the path does not exist.
|
|
28
|
-
# - `:not_directory` — the path exists but is not a directory (the
|
|
29
|
-
#
|
|
30
|
-
# is silently ignored).
|
|
25
|
+
# - `:not_directory` — the path exists but is not a directory (the loader only `add`s
|
|
26
|
+
# directories, so a `.rbs` file passed directly is silently ignored).
|
|
31
27
|
# - `:empty` — a directory with no `.rbs` file (recursive).
|
|
32
28
|
Entry = Data.define(:path, :status, :rbs_file_count) do
|
|
33
29
|
def ok?
|
|
@@ -38,9 +34,9 @@ module Rigor
|
|
|
38
34
|
!ok?
|
|
39
35
|
end
|
|
40
36
|
|
|
41
|
-
# One-line, human-facing reason. The wording matches the loader's
|
|
42
|
-
#
|
|
43
|
-
#
|
|
37
|
+
# One-line, human-facing reason. The wording matches the loader's actual behaviour
|
|
38
|
+
# ("loaded nothing from it") rather than the filesystem error, so the message points
|
|
39
|
+
# at the consequence.
|
|
44
40
|
def message
|
|
45
41
|
case status
|
|
46
42
|
when :missing
|
|
@@ -59,12 +55,11 @@ module Rigor
|
|
|
59
55
|
end
|
|
60
56
|
end
|
|
61
57
|
|
|
62
|
-
# Audits each configured entry. `signature_paths` is the
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
# a misconfiguration, so it is never audited.
|
|
58
|
+
# Audits each configured entry. `signature_paths` is the {Configuration#signature_paths}
|
|
59
|
+
# array (absolute paths, already resolved against the config file's directory). Pass
|
|
60
|
+
# `nil` — the unset default, where Rigor auto-detects `<root>/sig` — to get an empty
|
|
61
|
+
# result: an absent auto-detected `sig/` is a normal setup, not a misconfiguration, so it
|
|
62
|
+
# is never audited.
|
|
68
63
|
#
|
|
69
64
|
# @param signature_paths [Array<String, Pathname>, nil]
|
|
70
65
|
# @return [Array<Entry>]
|
|
@@ -72,8 +67,7 @@ module Rigor
|
|
|
72
67
|
Array(signature_paths).map { |path| classify(path.to_s) }
|
|
73
68
|
end
|
|
74
69
|
|
|
75
|
-
# The subset of {audit} that resolved to nothing — the entries worth
|
|
76
|
-
# warning about.
|
|
70
|
+
# The subset of {audit} that resolved to nothing — the entries worth warning about.
|
|
77
71
|
#
|
|
78
72
|
# @param signature_paths [Array<String, Pathname>, nil]
|
|
79
73
|
# @return [Array<Entry>]
|
|
@@ -2,24 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Source
|
|
5
|
-
# Flattens a Prism constant-reference node (`ConstantReadNode` /
|
|
6
|
-
#
|
|
5
|
+
# Flattens a Prism constant-reference node (`ConstantReadNode` / `ConstantPathNode`) to its
|
|
6
|
+
# source-qualified `"A::B::C"` string.
|
|
7
7
|
#
|
|
8
|
-
# Two nil policies for the one edge case that distinguishes the call
|
|
9
|
-
#
|
|
10
|
-
# the left side is a runtime expression rather than a constant):
|
|
8
|
+
# Two nil policies for the one edge case that distinguishes the call sites — a constant path rooted in a
|
|
9
|
+
# *dynamic* base (`expr::Bar`, where the left side is a runtime expression rather than a constant):
|
|
11
10
|
#
|
|
12
|
-
# * {.qualified_name} / {.render} are LENIENT — they drop the dynamic
|
|
13
|
-
#
|
|
14
|
-
# The scope indexer and statement evaluator feed only genuine
|
|
11
|
+
# * {.qualified_name} / {.render} are LENIENT — they drop the dynamic segment and render the trailing
|
|
12
|
+
# constant names (`expr::Bar` => "Bar"). The scope indexer and statement evaluator feed only genuine
|
|
15
13
|
# class/module path nodes and want a best-effort name.
|
|
16
|
-
# * {.qualified_name_or_nil} is STRICT — a dynamic base anywhere in the
|
|
17
|
-
#
|
|
18
|
-
# treat the path as opaque rather than guessing.
|
|
14
|
+
# * {.qualified_name_or_nil} is STRICT — a dynamic base anywhere in the chain yields `nil`, so a caller
|
|
15
|
+
# that statically names constants can treat the path as opaque rather than guessing.
|
|
19
16
|
#
|
|
20
|
-
# A leading `::` (absolute root, `::Foo`) renders as `"Foo"` under both
|
|
21
|
-
#
|
|
22
|
-
# `ConstantPathNode` yields `nil` under both.
|
|
17
|
+
# A leading `::` (absolute root, `::Foo`) renders as `"Foo"` under both policies. A node that is neither
|
|
18
|
+
# a `ConstantReadNode` nor a `ConstantPathNode` yields `nil` under both.
|
|
23
19
|
module ConstantPath
|
|
24
20
|
module_function
|
|
25
21
|
|