rigortype 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/data/core_overlay/csv.rbs +28 -0
- data/data/core_overlay/psych.rbs +22 -0
- data/docs/handbook/01-getting-started.md +9 -1
- data/docs/handbook/02-everyday-types.md +4 -1
- data/docs/handbook/08-understanding-errors.md +3 -3
- data/docs/manual/01-installation.md +1 -0
- data/docs/manual/02-cli-reference.md +16 -7
- data/docs/manual/04-diagnostics.md +7 -4
- data/docs/manual/06-baseline.md +5 -2
- data/docs/manual/07-plugins.md +1 -1
- data/docs/manual/14-rails-quickstart.md +4 -2
- data/docs/manual/15-type-protection-coverage.md +27 -4
- data/docs/manual/plugins/rigor-actionpack.md +1 -1
- data/docs/manual/plugins/rigor-activerecord.md +12 -5
- data/docs/manual/plugins/rigor-rails-routes.md +11 -0
- 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 +248 -186
- 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 +62 -48
- 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 +46 -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_fork_scan.rb +55 -0
- data/lib/rigor/cli/protection_renderer.rb +5 -7
- data/lib/rigor/cli/protection_report.rb +34 -18
- 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/missing_gem_constant_index.rb +128 -0
- 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 +225 -304
- 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 +794 -1154
- 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/fork_map.rb +87 -0
- 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 +627 -727
- data/lib/rigor/inference/origin_lookup.rb +38 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +154 -109
- 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 -821
- data/lib/rigor/inference/statement_evaluator.rb +754 -1114
- 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 +141 -184
- 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 +92 -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 +40 -48
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +140 -129
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +56 -59
- 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/structure_sql_parser.rb +172 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +168 -241
- 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-activesupport-core-ext/sig/active_support/core_ext.rbs +32 -0
- 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/grape_api_discoverer.rb +189 -0
- 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 +61 -75
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +319 -505
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +91 -96
- 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/environment.rbs +1 -0
- data/sig/rigor/scope.rbs +6 -0
- metadata +9 -1
|
@@ -8,20 +8,15 @@ require_relative "actionpack/controller_index"
|
|
|
8
8
|
|
|
9
9
|
module Rigor
|
|
10
10
|
module Plugin
|
|
11
|
-
# rigor-actionpack — validates Action Pack DSL calls in
|
|
12
|
-
# controller files.
|
|
11
|
+
# rigor-actionpack — validates Action Pack DSL calls in controller files.
|
|
13
12
|
#
|
|
14
|
-
# **Phase 4 of the Action Pack plugin family** (route-helper
|
|
15
|
-
#
|
|
16
|
-
# `
|
|
17
|
-
# every implicit-self `*_path` / `*_url` call inside files
|
|
18
|
-
# under `controller_search_paths` (default `app/controllers`).
|
|
13
|
+
# **Phase 4 of the Action Pack plugin family** (route-helper consumption). Reads the `:helper_table`
|
|
14
|
+
# fact published by `rigor-rails-routes` (ADR-9 cross-plugin API) and validates every implicit-self
|
|
15
|
+
# `*_path` / `*_url` call inside files under `controller_search_paths` (default `app/controllers`).
|
|
19
16
|
#
|
|
20
17
|
# Tier 2 of the [Rails plugins roadmap](../../../../docs/design/20260508-rails-plugins-roadmap.md).
|
|
21
|
-
# Phase 1 (strong-parameters → AR column validation), Phase 2
|
|
22
|
-
#
|
|
23
|
-
# separate slices; each phase composes additively under the
|
|
24
|
-
# same plugin id.
|
|
18
|
+
# Phase 1 (strong-parameters → AR column validation), Phase 2 (filter chains), and Phase 3 (render
|
|
19
|
+
# targets) ship as separate slices; each phase composes additively under the same plugin id.
|
|
25
20
|
#
|
|
26
21
|
# ## Configuration
|
|
27
22
|
#
|
|
@@ -36,46 +31,35 @@ module Rigor
|
|
|
36
31
|
#
|
|
37
32
|
# ## What it checks
|
|
38
33
|
#
|
|
39
|
-
# - **Helper existence** — every `*_path` / `*_url` call
|
|
40
|
-
#
|
|
41
|
-
# table.
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
# is excluded; same convention `rigor-rails-routes` uses).
|
|
47
|
-
# Mismatches emit `wrong-helper-arity`.
|
|
48
|
-
# - **Trace** — recognised helpers also emit a
|
|
49
|
-
# `helper-call` info diagnostic naming the action and
|
|
34
|
+
# - **Helper existence** — every `*_path` / `*_url` call inside a controller file is looked up in
|
|
35
|
+
# the helper table. Missing entries emit `unknown-helper` with a `DidYouMean` suggestion drawn
|
|
36
|
+
# from the table.
|
|
37
|
+
# - **Helper arity** — the call's positional-argument count is matched against the helper's
|
|
38
|
+
# recorded arity (a trailing `KeywordHashNode` like `users_path(format: :json)` is excluded; same
|
|
39
|
+
# convention `rigor-rails-routes` uses). Mismatches emit `wrong-helper-arity`.
|
|
40
|
+
# - **Trace** — recognised helpers also emit a `helper-call` info diagnostic naming the action and
|
|
50
41
|
# path, mirroring the trace shape of the upstream plugin.
|
|
51
42
|
#
|
|
52
43
|
# ## Limitations
|
|
53
44
|
#
|
|
54
|
-
# - Implicit-self calls only. `Rails.application.routes.url_helpers.users_path`
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
# -
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
# - When `rigor-rails-routes` is not installed (or its
|
|
64
|
-
# helper table is empty), Phase 4 silently degrades to a
|
|
65
|
-
# no-op. No load-error diagnostic is emitted; the user
|
|
66
|
-
# gets the "no checks happened" failure mode rather than
|
|
67
|
-
# a wall of "is this configured right?" warnings.
|
|
45
|
+
# - Implicit-self calls only. `Rails.application.routes.url_helpers.users_path` and other
|
|
46
|
+
# explicit-receiver shapes are passed through; they're rare in controller code and the helper
|
|
47
|
+
# table doesn't include any extra context to validate them.
|
|
48
|
+
# - Files outside `controller_search_paths` are skipped. The plugin doesn't try to detect "is this
|
|
49
|
+
# a controller?" by class hierarchy — Phase 1's strong-parameters work needs that, so it lives
|
|
50
|
+
# there. Phase 4's job is the single-purpose helper check.
|
|
51
|
+
# - When `rigor-rails-routes` is not installed (or its helper table is empty), Phase 4 silently
|
|
52
|
+
# degrades to a no-op. No load-error diagnostic is emitted; the user gets the "no checks
|
|
53
|
+
# happened" failure mode rather than a wall of "is this configured right?" warnings.
|
|
68
54
|
class Actionpack < Rigor::Plugin::Base
|
|
69
55
|
manifest(
|
|
70
56
|
id: "actionpack",
|
|
71
|
-
# ADR-37: the four phases (helper / filter / render /
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
# `
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
# `ControllerDiscoverer`), so render paths and filter-chain
|
|
78
|
-
# validation on nested controllers are correct.
|
|
57
|
+
# ADR-37: the four phases (helper / filter / render / strong-params) run per-call over the
|
|
58
|
+
# engine-owned walk; the enclosing controller is read from the node-rule `NodeContext`
|
|
59
|
+
# ancestors. Nested-module qualification is preserved — `module Admin; class
|
|
60
|
+
# DomainBlocksController` resolves as `Admin::DomainBlocksController` (matching the
|
|
61
|
+
# `ControllerDiscoverer`), so render paths and filter-chain validation on nested controllers are
|
|
62
|
+
# correct.
|
|
79
63
|
version: "0.9.0",
|
|
80
64
|
description: "Validates Action Pack route-helper calls and filter chains inside controllers, and types the request-context readers (`params` / `session` / `request`).",
|
|
81
65
|
config_schema: {
|
|
@@ -88,12 +72,10 @@ module Rigor
|
|
|
88
72
|
]
|
|
89
73
|
)
|
|
90
74
|
|
|
91
|
-
# Phase 2 cached producer — the controller index built from
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
# in-block `io_boundary` reads are captured into the dependency
|
|
96
|
-
# descriptor too, so no explicit priming is needed.
|
|
75
|
+
# Phase 2 cached producer — the controller index built from `controller_search_paths`. `watch:`
|
|
76
|
+
# (ADR-60 WD3) covers every `.rb` file under those roots so the cache invalidates when a
|
|
77
|
+
# controller is added, removed, or edited; the discoverer's in-block `io_boundary` reads are
|
|
78
|
+
# captured into the dependency descriptor too, so no explicit priming is needed.
|
|
97
79
|
producer :controller_index, watch: -> { [[@controller_search_paths, "**/*.rb"]] } do |_params|
|
|
98
80
|
ControllerDiscoverer.new(
|
|
99
81
|
io_boundary: io_boundary,
|
|
@@ -106,18 +88,15 @@ module Rigor
|
|
|
106
88
|
@view_search_paths = Array(config.fetch("view_search_paths")).map(&:to_s)
|
|
107
89
|
end
|
|
108
90
|
|
|
109
|
-
# ADR-37 — the four Action Pack phases run per-call over the
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
|
|
116
|
-
#
|
|
117
|
-
|
|
118
|
-
# Phase 4 — route-helper consumption. `:helper_table` is
|
|
119
|
-
# rigor-rails-routes's published fact (ADR-9), read lazily via
|
|
120
|
-
# `read_fact`.
|
|
91
|
+
# ADR-37 — the four Action Pack phases run per-call over the engine-owned walk. Each rule gates on
|
|
92
|
+
# `controller_file?(path)` (the plugin only validates files under `controller_search_paths`,
|
|
93
|
+
# exactly as the former `diagnostics_for_file` top-level guard did), then delegates to a per-node
|
|
94
|
+
# `Analyzer.*_violations_for` and positions each location-free `Violation` with `Base#diagnostic`.
|
|
95
|
+
# The filter / render phases read the enclosing controller from the node-rule `NodeContext`
|
|
96
|
+
# ancestors (its fifth block argument).
|
|
97
|
+
|
|
98
|
+
# Phase 4 — route-helper consumption. `:helper_table` is rigor-rails-routes's published fact
|
|
99
|
+
# (ADR-9), read lazily via `read_fact`.
|
|
121
100
|
node_rule Prism::CallNode do |node, _scope, path|
|
|
122
101
|
next [] unless controller_file?(path)
|
|
123
102
|
|
|
@@ -127,9 +106,8 @@ module Rigor
|
|
|
127
106
|
diagnostics_for(Analyzer.helper_violations_for(call_node: node, helper_table: table), path: path, node: node)
|
|
128
107
|
end
|
|
129
108
|
|
|
130
|
-
# Phase 2 — filter-chain validation. Skips silently when the
|
|
131
|
-
#
|
|
132
|
-
# class.
|
|
109
|
+
# Phase 2 — filter-chain validation. Skips silently when the controller index is absent or
|
|
110
|
+
# doesn't recognise the enclosing class.
|
|
133
111
|
node_rule Prism::CallNode do |node, _scope, path, _fc, context|
|
|
134
112
|
next [] unless controller_file?(path)
|
|
135
113
|
|
|
@@ -142,11 +120,9 @@ module Rigor
|
|
|
142
120
|
)
|
|
143
121
|
end
|
|
144
122
|
|
|
145
|
-
# Phase 3 — render-target validation against the configured
|
|
146
|
-
#
|
|
147
|
-
#
|
|
148
|
-
# needed; the controller index is consulted only to suppress
|
|
149
|
-
# gem-shipped-view false positives.
|
|
123
|
+
# Phase 3 — render-target validation against the configured `view_search_paths`. Recognised purely
|
|
124
|
+
# from the call site + the enclosing controller name, so no per-controller pre-discovery is
|
|
125
|
+
# needed; the controller index is consulted only to suppress gem-shipped-view false positives.
|
|
150
126
|
node_rule Prism::CallNode do |node, _scope, path, _fc, context|
|
|
151
127
|
next [] unless controller_file?(path)
|
|
152
128
|
|
|
@@ -159,11 +135,9 @@ module Rigor
|
|
|
159
135
|
)
|
|
160
136
|
end
|
|
161
137
|
|
|
162
|
-
# Phase 1 — strong-parameter validation. Reads the `:model_index`
|
|
163
|
-
#
|
|
164
|
-
#
|
|
165
|
-
# `params.require(:user).permit(:name, :email)` chain against the
|
|
166
|
-
# User model's column list.
|
|
138
|
+
# Phase 1 — strong-parameter validation. Reads the `:model_index` fact from the cross-plugin fact
|
|
139
|
+
# store (published by rigor-activerecord) and validates every `params.require(:user).permit(:name,
|
|
140
|
+
# :email)` chain against the User model's column list.
|
|
167
141
|
node_rule Prism::CallNode do |node, _scope, path|
|
|
168
142
|
next [] unless controller_file?(path)
|
|
169
143
|
|
|
@@ -173,28 +147,21 @@ module Rigor
|
|
|
173
147
|
diagnostics_for(Analyzer.permit_violations_for(call_node: node, model_index: index), path: path, node: node)
|
|
174
148
|
end
|
|
175
149
|
|
|
176
|
-
# Phase 5 (2026-07-04) — type the implicit-self request-context
|
|
177
|
-
#
|
|
178
|
-
#
|
|
179
|
-
# typing-
|
|
180
|
-
# (
|
|
181
|
-
#
|
|
182
|
-
# largest protection-coverage hole on real Rails apps: `params[:x]`
|
|
183
|
-
# is the #1 dispatch cluster (redmine app+lib: `[]` 2378 sites) and
|
|
184
|
-
# `session[:x] =` a large share of the `[]=` cluster, all
|
|
185
|
-
# unprotected because the receiver is Dynamic.
|
|
150
|
+
# Phase 5 (2026-07-04) — type the implicit-self request-context readers (`params`, `session`,
|
|
151
|
+
# `request`, `flash`, `cookies`) inside controllers. The typing-obstacle probe
|
|
152
|
+
# (docs/notes/20260704-rails-coverage-onboarding-carrier-trap.md, obstacle O3) found `params`
|
|
153
|
+
# typing to `Dynamic[top]` the single largest protection-coverage hole on real Rails apps:
|
|
154
|
+
# `params[:x]` is the #1 dispatch cluster (redmine app+lib: `[]` 2378 sites) and `session[:x] =` a
|
|
155
|
+
# large share of the `[]=` cluster, all unprotected because the receiver is Dynamic.
|
|
186
156
|
#
|
|
187
|
-
# Each returns a bare nominal with NO bundled RBS on purpose. That
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
#
|
|
192
|
-
#
|
|
193
|
-
# `
|
|
194
|
-
#
|
|
195
|
-
# trap (a declared class drops every member the RBS omits → false
|
|
196
|
-
# `undefined-method`). ADR-5: this types the container, never the
|
|
197
|
-
# caller's argument, so the values stay lenient.
|
|
157
|
+
# Each returns a bare nominal with NO bundled RBS on purpose. That makes the reader a *concrete*
|
|
158
|
+
# receiver (so `coverage --protection` counts the site as protected and the dispatch resolves
|
|
159
|
+
# against a named class) while its method surface stays engine-lenient — Rigor does not fire
|
|
160
|
+
# `undefined-method` on a class it has no RBS for, so `params.require(...).permit(...)`,
|
|
161
|
+
# `session.delete(:x)`, `request.xhr?`, and every other method on these stay FP-safe. Shipping a
|
|
162
|
+
# partial RBS would re-introduce the carrier-additivity trap (a declared class drops every member
|
|
163
|
+
# the RBS omits → false `undefined-method`). ADR-5: this types the container, never the caller's
|
|
164
|
+
# argument, so the values stay lenient.
|
|
198
165
|
REQUEST_CONTEXT_READER_TYPES = {
|
|
199
166
|
params: "ActionController::Parameters",
|
|
200
167
|
session: "ActionDispatch::Request::Session",
|
|
@@ -215,14 +182,33 @@ module Rigor
|
|
|
215
182
|
Rigor::Type::Combinator.nominal_of(class_name)
|
|
216
183
|
end
|
|
217
184
|
|
|
185
|
+
# Phase 5b (2026-07-10) — keep the strong-parameters fluent chain typed. `params` types to
|
|
186
|
+
# `ActionController::Parameters` (above), but the chained `params.require(:user).permit(:name)` /
|
|
187
|
+
# `params.permit(...)` calls returned `Dynamic` at the first hop (Parameters ships no bundled RBS,
|
|
188
|
+
# so a call on it resolves lenient-to-Dynamic), leaking every downstream site (`.permit`, `.to_h`,
|
|
189
|
+
# `.each`) to unprotected. These three methods return another `Parameters`, so gate on a
|
|
190
|
+
# `Parameters` receiver and re-type the result as the same lenient nominal — the chain stays a
|
|
191
|
+
# concrete receiver end-to-end and `coverage --protection` counts the sites as protected.
|
|
192
|
+
#
|
|
193
|
+
# `require` may return a scalar for a flat key at runtime (`params.require(:id) → String`), but
|
|
194
|
+
# typing it as the RBS-less `Parameters` is FP-safe by the same argument as the readers: every
|
|
195
|
+
# method on a Parameters value stays engine-lenient (no `undefined-method`), and this types the
|
|
196
|
+
# container only, never a caller's argument (ADR-5). The GitLab strong-params survey (108 leaked
|
|
197
|
+
# `.permit` sites) is the demand.
|
|
198
|
+
STRONG_PARAMS_CHAIN_METHODS = %i[require permit permit!].freeze
|
|
199
|
+
|
|
200
|
+
dynamic_return receivers: [REQUEST_CONTEXT_READER_TYPES[:params]], methods: STRONG_PARAMS_CHAIN_METHODS do |call_node, _scope|
|
|
201
|
+
next nil unless call_node.is_a?(Prism::CallNode)
|
|
202
|
+
|
|
203
|
+
Rigor::Type::Combinator.nominal_of(REQUEST_CONTEXT_READER_TYPES[:params])
|
|
204
|
+
end
|
|
205
|
+
|
|
218
206
|
private
|
|
219
207
|
|
|
220
|
-
# True when the current `self` is a controller — the enclosing
|
|
221
|
-
#
|
|
222
|
-
#
|
|
223
|
-
#
|
|
224
|
-
# Typing `params` is precision-additive, so the name-convention
|
|
225
|
-
# fallback is FP-safe.
|
|
208
|
+
# True when the current `self` is a controller — the enclosing class is one the discoverer
|
|
209
|
+
# indexed, or its name follows the Rails `*Controller` convention (covering controllers outside
|
|
210
|
+
# `controller_search_paths`, e.g. one shipped by an engine). Typing `params` is precision-additive,
|
|
211
|
+
# so the name-convention fallback is FP-safe.
|
|
226
212
|
def controller_scope?(scope)
|
|
227
213
|
self_type = scope&.self_type
|
|
228
214
|
return false unless self_type.respond_to?(:class_name)
|
|
@@ -238,12 +224,10 @@ module Rigor
|
|
|
238
224
|
|
|
239
225
|
def controller_file?(path)
|
|
240
226
|
@controller_search_paths.any? do |root|
|
|
241
|
-
# The runner may pass `path` as either an absolute
|
|
242
|
-
#
|
|
243
|
-
# relative
|
|
244
|
-
#
|
|
245
|
-
# root-relative. Match the configured root as a
|
|
246
|
-
# /-bracketed substring so both shapes resolve.
|
|
227
|
+
# The runner may pass `path` as either an absolute path (when `paths:` was configured
|
|
228
|
+
# absolutely) or a relative one (when configured relatively). The `controller_search_paths`
|
|
229
|
+
# knob is always project-root-relative. Match the configured root as a /-bracketed substring
|
|
230
|
+
# so both shapes resolve.
|
|
247
231
|
path.include?("/#{root}/") || path.start_with?("#{root}/") || path == root
|
|
248
232
|
end
|
|
249
233
|
end
|
|
@@ -5,34 +5,26 @@ require "prism"
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Plugin
|
|
7
7
|
class Activejob < Rigor::Plugin::Base
|
|
8
|
-
# Walks a parsed file's AST looking for
|
|
9
|
-
#
|
|
10
|
-
# `.perform_now(...)` / `.perform(...)` calls and
|
|
11
|
-
# validates each against the {JobIndex}.
|
|
8
|
+
# Walks a parsed file's AST looking for `<JobClass>.perform_later(...)` / `.perform_now(...)` /
|
|
9
|
+
# `.perform(...)` calls and validates each against the {JobIndex}.
|
|
12
10
|
#
|
|
13
|
-
# The plugin recognises a call as job-shaped when the
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# method name is one of the three ActiveJob entry
|
|
17
|
-
# points.
|
|
11
|
+
# The plugin recognises a call as job-shaped when the receiver is a `ConstantReadNode` /
|
|
12
|
+
# `ConstantPathNode` whose resolved name appears in the index, and the method name is one of the
|
|
13
|
+
# three ActiveJob entry points.
|
|
18
14
|
module Analyzer
|
|
19
|
-
# Methods that delegate to the job's `#perform`. All
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# `perform_now` runs synchronously, and `perform` is
|
|
23
|
-
# the bare execution path.
|
|
15
|
+
# Methods that delegate to the job's `#perform`. All three accept the same argument shape —
|
|
16
|
+
# `perform_later` is the most common (queues for later execution), `perform_now` runs
|
|
17
|
+
# synchronously, and `perform` is the bare execution path.
|
|
24
18
|
ENTRY_METHODS = %i[perform_later perform_now perform].freeze
|
|
25
19
|
|
|
26
|
-
# One job-call observation. Carries no path/location — the
|
|
27
|
-
#
|
|
28
|
-
# `Plugin::Base#diagnostic`.
|
|
20
|
+
# One job-call observation. Carries no path/location — the caller (the `node_rule` block)
|
|
21
|
+
# positions it via `Plugin::Base#diagnostic`.
|
|
29
22
|
Violation = Struct.new(:rule, :severity, :message, keyword_init: true)
|
|
30
23
|
|
|
31
24
|
module_function
|
|
32
25
|
|
|
33
|
-
# The job-call violations for a single call node (0..2), or `[]`
|
|
34
|
-
#
|
|
35
|
-
# job. ADR-37: the engine owns the walk.
|
|
26
|
+
# The job-call violations for a single call node (0..2), or `[]` when the node is not a
|
|
27
|
+
# `<Job>.perform_*` entry call on a known job. ADR-37: the engine owns the walk.
|
|
36
28
|
#
|
|
37
29
|
# @param call_node [Prism::Node]
|
|
38
30
|
# @param job_index [JobIndex]
|
|
@@ -76,8 +68,7 @@ module Rigor
|
|
|
76
68
|
)
|
|
77
69
|
end
|
|
78
70
|
|
|
79
|
-
# Renders a constant-path receiver as a String.
|
|
80
|
-
# Mirrors the helpers in rigor-activerecord /
|
|
71
|
+
# Renders a constant-path receiver as a String. Mirrors the helpers in rigor-activerecord /
|
|
81
72
|
# rigor-rails-routes for parity.
|
|
82
73
|
def constant_receiver_name(node)
|
|
83
74
|
case node
|
|
@@ -7,25 +7,19 @@ require_relative "job_index"
|
|
|
7
7
|
module Rigor
|
|
8
8
|
module Plugin
|
|
9
9
|
class Activejob < Rigor::Plugin::Base
|
|
10
|
-
# Walks the configured job-search paths via the plugin's
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# the
|
|
14
|
-
# the discoverer also reads the `#perform` method's
|
|
15
|
-
# parameter list and computes the arity envelope.
|
|
10
|
+
# Walks the configured job-search paths via the plugin's `IoBoundary`, parses each `.rb` file with
|
|
11
|
+
# Prism, and collects classes whose immediate superclass is one of the configured base classes.
|
|
12
|
+
# For each discovered class, the discoverer also reads the `#perform` method's parameter list and
|
|
13
|
+
# computes the arity envelope.
|
|
16
14
|
#
|
|
17
15
|
# Limitations (intentional for v0.1.0):
|
|
18
16
|
#
|
|
19
|
-
# - Only direct-superclass matches. `class WelcomeJob <
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# if needed.
|
|
23
|
-
# - The qualified class name is the lexical path
|
|
24
|
-
# (`Admin::WelcomeJob` for a class declared inside
|
|
17
|
+
# - Only direct-superclass matches. `class WelcomeJob < BaseJob` where `BaseJob < ApplicationJob`
|
|
18
|
+
# is NOT discovered. List `BaseJob` in `job_base_classes` if needed.
|
|
19
|
+
# - The qualified class name is the lexical path (`Admin::WelcomeJob` for a class declared inside
|
|
25
20
|
# `module Admin`).
|
|
26
|
-
# - The `#perform` arity is read from the syntactic
|
|
27
|
-
#
|
|
28
|
-
# are out of scope.
|
|
21
|
+
# - The `#perform` arity is read from the syntactic parameter list. Methods built via
|
|
22
|
+
# `define_method` are out of scope.
|
|
29
23
|
class JobDiscoverer
|
|
30
24
|
def initialize(io_boundary:, search_paths:, base_classes:)
|
|
31
25
|
@io_boundary = io_boundary
|
|
@@ -119,9 +113,8 @@ module Rigor
|
|
|
119
113
|
end
|
|
120
114
|
end
|
|
121
115
|
|
|
122
|
-
# Returns the `def perform(...)` node from a class
|
|
123
|
-
#
|
|
124
|
-
# `#perform`. Only matches instance-side `def perform`.
|
|
116
|
+
# Returns the `def perform(...)` node from a class body, or `nil` when the class doesn't
|
|
117
|
+
# override `#perform`. Only matches instance-side `def perform`.
|
|
125
118
|
def lookup_perform_def(body)
|
|
126
119
|
return nil if body.nil?
|
|
127
120
|
|
|
@@ -134,13 +127,10 @@ module Rigor
|
|
|
134
127
|
nil
|
|
135
128
|
end
|
|
136
129
|
|
|
137
|
-
# Builds a JobIndex::Entry from the discovered class's
|
|
138
|
-
# `#perform
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
# `perform_later` on a job that didn't override it is
|
|
142
|
-
# itself a bug, but it's the user's bug, not the
|
|
143
|
-
# plugin's call to flag without runtime context.
|
|
130
|
+
# Builds a JobIndex::Entry from the discovered class's `#perform` def. When the class doesn't
|
|
131
|
+
# override `#perform`, we record an "any-arity" entry — Active Job's default `#perform` is
|
|
132
|
+
# abstract; calling `perform_later` on a job that didn't override it is itself a bug, but it's
|
|
133
|
+
# the user's bug, not the plugin's call to flag without runtime context.
|
|
144
134
|
def build_entry(class_name, perform_def)
|
|
145
135
|
if perform_def.nil?
|
|
146
136
|
return JobIndex::Entry.new(
|
|
@@ -3,22 +3,17 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Plugin
|
|
5
5
|
class Activejob < Rigor::Plugin::Base
|
|
6
|
-
# Frozen catalogue of discovered ActiveJob subclasses
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# validate `Job.perform_later(...)` call sites.
|
|
6
|
+
# Frozen catalogue of discovered ActiveJob subclasses keyed by qualified class name. Each entry
|
|
7
|
+
# holds the `#perform` method's arity envelope so the analyzer can validate
|
|
8
|
+
# `Job.perform_later(...)` call sites.
|
|
10
9
|
#
|
|
11
|
-
# `min_arity` / `max_arity` form a closed range
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# keyword
|
|
15
|
-
# but they're rare in user code, so the analyzer
|
|
16
|
-
# validates positional arity only (keyword arity
|
|
17
|
-
# validation is deferred).
|
|
10
|
+
# `min_arity` / `max_arity` form a closed range (`Float::INFINITY` for the upper bound when
|
|
11
|
+
# `*args` is present). `keyword_required` lists any required keyword arguments — Active Job
|
|
12
|
+
# supports keyword args but they're rare in user code, so the analyzer validates positional arity
|
|
13
|
+
# only (keyword arity validation is deferred).
|
|
18
14
|
class JobIndex
|
|
19
15
|
Entry = Data.define(:class_name, :min_arity, :max_arity, :keyword_required) do
|
|
20
|
-
# Flexible-friendly textual form of the arity for
|
|
21
|
-
# error messages: `1`, `1..2`, `2+`.
|
|
16
|
+
# Flexible-friendly textual form of the arity for error messages: `1`, `1..2`, `2+`.
|
|
22
17
|
def arity_label
|
|
23
18
|
return "#{min_arity}+" if max_arity == Float::INFINITY
|
|
24
19
|
return min_arity.to_s if min_arity == max_arity
|
|
@@ -8,14 +8,12 @@ require_relative "activejob/analyzer"
|
|
|
8
8
|
|
|
9
9
|
module Rigor
|
|
10
10
|
module Plugin
|
|
11
|
-
# rigor-activejob — validates `Job.perform_later(...)` /
|
|
12
|
-
#
|
|
13
|
-
# against the discovered `#perform` definitions.
|
|
11
|
+
# rigor-activejob — validates `Job.perform_later(...)` / `.perform_now(...)` / `.perform(...)`
|
|
12
|
+
# argument arity against the discovered `#perform` definitions.
|
|
14
13
|
#
|
|
15
14
|
# Tier 1D of the [Rails plugins roadmap](../../../../docs/design/20260508-rails-plugins-roadmap.md).
|
|
16
|
-
# Statically discovers ActiveJob subclasses by walking
|
|
17
|
-
#
|
|
18
|
-
# file with Prism — no `active_job` runtime dependency.
|
|
15
|
+
# Statically discovers ActiveJob subclasses by walking the configured `job_search_paths` and
|
|
16
|
+
# parsing each file with Prism — no `active_job` runtime dependency.
|
|
19
17
|
#
|
|
20
18
|
# ## Configuration
|
|
21
19
|
#
|
|
@@ -27,16 +25,12 @@ module Rigor
|
|
|
27
25
|
#
|
|
28
26
|
# ## Limitations (v0.1.0)
|
|
29
27
|
#
|
|
30
|
-
# - Direct-superclass match only. `class WelcomeJob <
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# -
|
|
35
|
-
#
|
|
36
|
-
# are out of scope.
|
|
37
|
-
# - Required keyword arguments are recognised but not
|
|
38
|
-
# validated at the call site (positional arity only
|
|
39
|
-
# for v0.1.0).
|
|
28
|
+
# - Direct-superclass match only. `class WelcomeJob < BaseJob` where `BaseJob < ApplicationJob` is
|
|
29
|
+
# NOT discovered. Add `BaseJob` to `job_base_classes` if needed.
|
|
30
|
+
# - The `#perform` arity is read from the syntactic parameter list. Methods built via
|
|
31
|
+
# `define_method` are out of scope.
|
|
32
|
+
# - Required keyword arguments are recognised but not validated at the call site (positional arity
|
|
33
|
+
# only for v0.1.0).
|
|
40
34
|
class Activejob < Rigor::Plugin::Base
|
|
41
35
|
manifest(
|
|
42
36
|
id: "activejob",
|
|
@@ -48,11 +42,10 @@ module Rigor
|
|
|
48
42
|
}
|
|
49
43
|
)
|
|
50
44
|
|
|
51
|
-
# Cached: discovered job index. `watch:` (ADR-60 WD3) covers
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
# validate dependency descriptor after the block runs.
|
|
45
|
+
# Cached: discovered job index. `watch:` (ADR-60 WD3) covers every `.rb` under `job_search_paths`
|
|
46
|
+
# so the cache invalidates when a job is added, removed, or edited; the discoverer's in-block
|
|
47
|
+
# `IoBoundary` reads are captured into the record-and-validate dependency descriptor after the
|
|
48
|
+
# block runs.
|
|
56
49
|
producer :job_index, watch: -> { [[@job_search_paths, "**/*.rb"]] } do |_params|
|
|
57
50
|
JobDiscoverer.new(
|
|
58
51
|
io_boundary: io_boundary,
|
|
@@ -66,9 +59,8 @@ module Rigor
|
|
|
66
59
|
@job_base_classes = Array(config.fetch("job_base_classes")).map(&:to_s)
|
|
67
60
|
end
|
|
68
61
|
|
|
69
|
-
# File-level only: the load-error emission. Per-call arity
|
|
70
|
-
#
|
|
71
|
-
# below (ADR-37). The job index is lazily loaded + memoised by
|
|
62
|
+
# File-level only: the load-error emission. Per-call arity validation runs over the engine-owned
|
|
63
|
+
# walk via the node_rule below (ADR-37). The job index is lazily loaded + memoised by
|
|
72
64
|
# `producer_value`, shared by both surfaces.
|
|
73
65
|
def diagnostics_for_file(path:, scope:, root:) # rubocop:disable Lint/UnusedMethodArgument
|
|
74
66
|
index = producer_value(:job_index)
|