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
|
@@ -8,54 +8,38 @@ require_relative "mailer_index"
|
|
|
8
8
|
module Rigor
|
|
9
9
|
module Plugin
|
|
10
10
|
class Actionmailer < Rigor::Plugin::Base
|
|
11
|
-
# Walks the configured mailer-search paths via the
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# is one of the configured base classes.
|
|
11
|
+
# Walks the configured mailer-search paths via the plugin's `IoBoundary`, parses each `.rb` file
|
|
12
|
+
# with Prism, and collects classes whose immediate superclass is one of the configured base
|
|
13
|
+
# classes.
|
|
15
14
|
#
|
|
16
15
|
# For each discovered class, the discoverer:
|
|
17
16
|
#
|
|
18
|
-
# - Reads the instance-side `def` nodes and records each
|
|
19
|
-
#
|
|
20
|
-
# - For each (class, action) pair, attempts to read every
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# descriptor (so the cache invalidates when the
|
|
25
|
-
# template changes); missing templates are recorded so
|
|
26
|
-
# the plugin can surface a diagnostic on the mailer
|
|
27
|
-
# class definition.
|
|
17
|
+
# - Reads the instance-side `def` nodes and records each one as an action method, capturing the
|
|
18
|
+
# arity envelope.
|
|
19
|
+
# - For each (class, action) pair, attempts to read every candidate view template under
|
|
20
|
+
# `app/views/<mailer_underscore>/<action>.{html,text}.erb`. Existing templates feed the
|
|
21
|
+
# IoBoundary's cache descriptor (so the cache invalidates when the template changes); missing
|
|
22
|
+
# templates are recorded so the plugin can surface a diagnostic on the mailer class definition.
|
|
28
23
|
#
|
|
29
24
|
# Limitations (intentional for v0.1.0):
|
|
30
25
|
#
|
|
31
|
-
# - Direct-superclass match only. `class CustomerMailer
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# `
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
# filters obvious non-actions (`initialize`, names
|
|
40
|
-
# prefixed with `_`).
|
|
41
|
-
# - Adding a brand-new view file under
|
|
42
|
-
# `app/views/<mailer>/` will NOT invalidate the
|
|
43
|
-
# cached index until something the mailer file
|
|
44
|
-
# touches changes. This is the standard read-tracking
|
|
45
|
-
# trade-off — only files we successfully read get
|
|
46
|
-
# digested into the descriptor.
|
|
26
|
+
# - Direct-superclass match only. `class CustomerMailer < BaseMailer` where `BaseMailer <
|
|
27
|
+
# ApplicationMailer` is NOT discovered. Add `BaseMailer` to `mailer_base_classes` if needed.
|
|
28
|
+
# - Action methods are read from the syntactic instance-side `def` list. Methods built via
|
|
29
|
+
# `define_method`, `private`, or non-action helpers (e.g. methods starting with `_`) are out of
|
|
30
|
+
# scope. The discoverer filters obvious non-actions (`initialize`, names prefixed with `_`).
|
|
31
|
+
# - Adding a brand-new view file under `app/views/<mailer>/` will NOT invalidate the cached index
|
|
32
|
+
# until something the mailer file touches changes. This is the standard read-tracking trade-off
|
|
33
|
+
# — only files we successfully read get digested into the descriptor.
|
|
47
34
|
class MailerDiscoverer
|
|
48
35
|
DEFAULT_VIEWS_ROOT = "app/views"
|
|
49
36
|
VIEW_FORMATS = %w[html text].freeze
|
|
50
37
|
VIEW_EXTENSIONS = %w[erb haml slim].freeze
|
|
51
38
|
|
|
52
39
|
# @param io_boundary [Rigor::Plugin::IoBoundary]
|
|
53
|
-
# @param search_paths [Array<String>] absolute or
|
|
54
|
-
#
|
|
55
|
-
# @param
|
|
56
|
-
# superclasses that mark a class as a mailer.
|
|
57
|
-
# @param views_root [String] absolute or project-
|
|
58
|
-
# relative path to the views directory (typically
|
|
40
|
+
# @param search_paths [Array<String>] absolute or project-relative paths to scan for mailers.
|
|
41
|
+
# @param base_classes [Array<String>] direct superclasses that mark a class as a mailer.
|
|
42
|
+
# @param views_root [String] absolute or project-relative path to the views directory (typically
|
|
59
43
|
# `app/views`).
|
|
60
44
|
def initialize(io_boundary:, search_paths:, base_classes:, views_root: DEFAULT_VIEWS_ROOT)
|
|
61
45
|
@io_boundary = io_boundary
|
|
@@ -66,11 +50,9 @@ module Rigor
|
|
|
66
50
|
|
|
67
51
|
# @return [MailerIndex]
|
|
68
52
|
def discover
|
|
69
|
-
# Two-pass: first collect every module's defs (for
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
# GitLab's `Notify` mailer derives every action from
|
|
73
|
-
# `Emails::*` concerns under `app/mailers/emails/`.
|
|
53
|
+
# Two-pass: first collect every module's defs (for the include-following step), then build
|
|
54
|
+
# per-class entries that pull in actions from include'd modules. GitLab's `Notify` mailer
|
|
55
|
+
# derives every action from `Emails::*` concerns under `app/mailers/emails/`.
|
|
74
56
|
module_actions = {} # module_fqn => Hash<Symbol, ActionEntry>
|
|
75
57
|
class_visits = [] # collected (class_name, path, def_nodes, includes)
|
|
76
58
|
|
|
@@ -135,10 +117,8 @@ module Rigor
|
|
|
135
117
|
walk_for_mailers(node.body, inner_path, &) if node.body
|
|
136
118
|
end
|
|
137
119
|
|
|
138
|
-
# Collects qualified-constant names passed to `include
|
|
139
|
-
#
|
|
140
|
-
# concern-module action definitions (GitLab's
|
|
141
|
-
# `Notify` mailer derives every action from
|
|
120
|
+
# Collects qualified-constant names passed to `include X` calls inside the class body. Used to
|
|
121
|
+
# look up concern-module action definitions (GitLab's `Notify` mailer derives every action from
|
|
142
122
|
# `Emails::Issues`, `Emails::MergeRequests`, etc.).
|
|
143
123
|
def collect_includes(body)
|
|
144
124
|
return [] if body.nil?
|
|
@@ -155,10 +135,9 @@ module Rigor
|
|
|
155
135
|
names
|
|
156
136
|
end
|
|
157
137
|
|
|
158
|
-
# Walks the AST collecting every module's instance-side
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
# `_`-prefixed / callback-target methods skipped).
|
|
138
|
+
# Walks the AST collecting every module's instance-side def nodes by fully-qualified module
|
|
139
|
+
# name. The same `collect_action_defs` filter applies (private / `_`-prefixed / callback-target
|
|
140
|
+
# methods skipped).
|
|
162
141
|
def collect_module_actions(node, lexical_path, accumulator)
|
|
163
142
|
return if node.nil?
|
|
164
143
|
|
|
@@ -210,25 +189,18 @@ module Rigor
|
|
|
210
189
|
end
|
|
211
190
|
end
|
|
212
191
|
|
|
213
|
-
# Returns the instance-side `def` nodes that look like
|
|
214
|
-
# mailer actions. Filters non-actions:
|
|
192
|
+
# Returns the instance-side `def` nodes that look like mailer actions. Filters non-actions:
|
|
215
193
|
# - `initialize`
|
|
216
|
-
# - methods starting with `_` (Ruby convention for
|
|
217
|
-
# private/internal)
|
|
194
|
+
# - methods starting with `_` (Ruby convention for private/internal)
|
|
218
195
|
# - `def self.<name>` (singleton-side)
|
|
219
|
-
# - methods after a bare `private` (or
|
|
220
|
-
#
|
|
221
|
-
# internal helpers, not actions
|
|
196
|
+
# - methods after a bare `private` (or `public` → `private` transition) — these are internal
|
|
197
|
+
# helpers, not actions
|
|
222
198
|
# - methods named as a `private :foo` argument
|
|
223
|
-
# - methods named as a callback target
|
|
224
|
-
# (`before_action :name`, `after_action`,
|
|
225
|
-
# `around_action`)
|
|
199
|
+
# - methods named as a callback target (`before_action :name`, `after_action`, `around_action`)
|
|
226
200
|
#
|
|
227
|
-
# Pre-fix, Mastodon's `AdminMailer#process_params` /
|
|
228
|
-
# `
|
|
229
|
-
#
|
|
230
|
-
# keyword wasn't honoured. ~19 false positives across
|
|
231
|
-
# Mastodon's mailers.
|
|
201
|
+
# Pre-fix, Mastodon's `AdminMailer#process_params` / `set_instance` / `set_locale` /
|
|
202
|
+
# `set_important_headers!` all surfaced as missing-view because the bare `private` keyword
|
|
203
|
+
# wasn't honoured. ~19 false positives across Mastodon's mailers.
|
|
232
204
|
CALLBACK_DECLARATIONS = %i[before_action after_action around_action].freeze
|
|
233
205
|
private_constant :CALLBACK_DECLARATIONS
|
|
234
206
|
|
|
@@ -252,11 +224,9 @@ module Rigor
|
|
|
252
224
|
end
|
|
253
225
|
end
|
|
254
226
|
|
|
255
|
-
# First pass over the class body: collect (a) names
|
|
256
|
-
#
|
|
257
|
-
#
|
|
258
|
-
# arguments to callback declarations
|
|
259
|
-
# (`before_action :setup`, etc.).
|
|
227
|
+
# First pass over the class body: collect (a) names passed to `private :foo` / `protected :foo`
|
|
228
|
+
# (explicit visibility-on-existing-method form), and (b) Symbol arguments to callback
|
|
229
|
+
# declarations (`before_action :setup`, etc.).
|
|
260
230
|
def collect_visibility_and_callbacks(body)
|
|
261
231
|
private_names = []
|
|
262
232
|
callback_names = []
|
|
@@ -278,10 +248,9 @@ module Rigor
|
|
|
278
248
|
[private_names.to_set, callback_names.to_set]
|
|
279
249
|
end
|
|
280
250
|
|
|
281
|
-
# Returns the new visibility scope state after observing
|
|
282
|
-
# `
|
|
283
|
-
#
|
|
284
|
-
# (already handled by `collect_visibility_and_callbacks`).
|
|
251
|
+
# Returns the new visibility scope state after observing `node`. Bare `private` / `protected` /
|
|
252
|
+
# `public` switch state; the `private :foo` arg-bearing form does NOT (already handled by
|
|
253
|
+
# `collect_visibility_and_callbacks`).
|
|
285
254
|
def next_visibility(node, current)
|
|
286
255
|
return current unless node.is_a?(Prism::CallNode)
|
|
287
256
|
return current unless node.receiver.nil?
|
|
@@ -301,11 +270,9 @@ module Rigor
|
|
|
301
270
|
[entry.method_name, entry]
|
|
302
271
|
end
|
|
303
272
|
|
|
304
|
-
# Merge actions from include'd modules (pre-collected
|
|
305
|
-
#
|
|
306
|
-
#
|
|
307
|
-
# (consumed by the analyzer) downgrades `unknown-action`
|
|
308
|
-
# to silence when any include remains unresolved.
|
|
273
|
+
# Merge actions from include'd modules (pre-collected in `module_actions` keyed by
|
|
274
|
+
# fully-qualified name). Unresolvable includes are tracked; `unresolved_includes?` (consumed
|
|
275
|
+
# by the analyzer) downgrades `unknown-action` to silence when any include remains unresolved.
|
|
309
276
|
unresolved_includes = []
|
|
310
277
|
includes.each do |include_name|
|
|
311
278
|
inc_actions = module_actions[include_name]
|
|
@@ -357,17 +324,14 @@ module Rigor
|
|
|
357
324
|
end
|
|
358
325
|
|
|
359
326
|
# Checks whether *any* template under
|
|
360
|
-
# `app/views/<underscore>/<action>.{html,text}.{erb,haml,slim}`
|
|
361
|
-
#
|
|
362
|
-
#
|
|
363
|
-
# boundary; failed reads (missing file or access
|
|
364
|
-
# denied) are swallowed.
|
|
327
|
+
# `app/views/<underscore>/<action>.{html,text}.{erb,haml,slim}` exists, by attempting to read
|
|
328
|
+
# each candidate via the IoBoundary. Successful reads are recorded by the boundary; failed reads
|
|
329
|
+
# (missing file or access denied) are swallowed.
|
|
365
330
|
def view_exists?(class_name, action_name)
|
|
366
331
|
views_root_absolute = File.expand_path(@views_root)
|
|
367
|
-
# ADR-39: the real ActiveSupport::Inflector resolves the mailer's
|
|
368
|
-
#
|
|
369
|
-
#
|
|
370
|
-
# missing-view check at the wrong directory.
|
|
332
|
+
# ADR-39: the real ActiveSupport::Inflector resolves the mailer's view directory
|
|
333
|
+
# (`Foo::BarMailer` → `foo/bar_mailer`), so a divergence from Rails' real underscore can't
|
|
334
|
+
# point the missing-view check at the wrong directory.
|
|
371
335
|
underscore_path = Rigor::Plugin::Inflector.underscore(class_name.delete_prefix("::"))
|
|
372
336
|
mailer_dir = File.join(views_root_absolute, underscore_path)
|
|
373
337
|
|
|
@@ -3,17 +3,13 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Plugin
|
|
5
5
|
class Actionmailer < Rigor::Plugin::Base
|
|
6
|
-
# Frozen catalogue of discovered Mailer classes, each
|
|
7
|
-
# carrying:
|
|
6
|
+
# Frozen catalogue of discovered Mailer classes, each carrying:
|
|
8
7
|
#
|
|
9
|
-
# - the action methods it defines (arity envelope per
|
|
10
|
-
# action; same shape as `rigor-activejob`'s
|
|
8
|
+
# - the action methods it defines (arity envelope per action; same shape as `rigor-activejob`'s
|
|
11
9
|
# `JobIndex::Entry`)
|
|
12
|
-
# - the source file path the class was declared in
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# - the list of `(action, location)` pairs whose view
|
|
16
|
-
# templates are missing from `app/views/`
|
|
10
|
+
# - the source file path the class was declared in (used to anchor missing-view diagnostics on
|
|
11
|
+
# the mailer file)
|
|
12
|
+
# - the list of `(action, location)` pairs whose view templates are missing from `app/views/`
|
|
17
13
|
class MailerIndex
|
|
18
14
|
ActionEntry = Data.define(:method_name, :min_arity, :max_arity, :def_line, :def_column) do
|
|
19
15
|
def arity_label
|
|
@@ -33,12 +29,10 @@ module Rigor
|
|
|
33
29
|
actions[method_name.to_sym]
|
|
34
30
|
end
|
|
35
31
|
|
|
36
|
-
# True when the mailer `include`s a module whose
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
# this case — the unresolved module may legitimately
|
|
41
|
-
# provide the action.
|
|
32
|
+
# True when the mailer `include`s a module whose source we couldn't index (typically a
|
|
33
|
+
# gem-shipped concern that defines additional mailer actions). Analyzer downgrades
|
|
34
|
+
# `unknown-action` to silence in this case — the unresolved module may legitimately provide
|
|
35
|
+
# the action.
|
|
42
36
|
def unresolved_includes?
|
|
43
37
|
!unresolved_includes.empty?
|
|
44
38
|
end
|
|
@@ -61,8 +55,7 @@ module Rigor
|
|
|
61
55
|
@by_name.key?(class_name.to_s)
|
|
62
56
|
end
|
|
63
57
|
|
|
64
|
-
# @param file_path [String] absolute path of a mailer
|
|
65
|
-
# file (canonicalised — see plugin entry's
|
|
58
|
+
# @param file_path [String] absolute path of a mailer file (canonicalised — see plugin entry's
|
|
66
59
|
# `harvest`)
|
|
67
60
|
# @return [ClassEntry, nil]
|
|
68
61
|
def find_by_file(file_path)
|
|
@@ -8,12 +8,10 @@ require_relative "actionmailer/analyzer"
|
|
|
8
8
|
|
|
9
9
|
module Rigor
|
|
10
10
|
module Plugin
|
|
11
|
-
# rigor-actionmailer — validates `Mailer.action(args)`
|
|
12
|
-
# call sites and detects missing view templates.
|
|
11
|
+
# rigor-actionmailer — validates `Mailer.action(args)` call sites and detects missing view templates.
|
|
13
12
|
#
|
|
14
13
|
# Tier 1C of the [Rails plugins roadmap](../../../../docs/design/20260508-rails-plugins-roadmap.md).
|
|
15
|
-
# Statically discovers mailer classes by walking
|
|
16
|
-
# `mailer_search_paths` and parsing each file with
|
|
14
|
+
# Statically discovers mailer classes by walking `mailer_search_paths` and parsing each file with
|
|
17
15
|
# Prism — no `action_mailer` runtime dependency.
|
|
18
16
|
#
|
|
19
17
|
# ## Configuration
|
|
@@ -27,35 +25,27 @@ module Rigor
|
|
|
27
25
|
#
|
|
28
26
|
# ## What it checks
|
|
29
27
|
#
|
|
30
|
-
# 1. **Method existence** — `UserMailer.welcome(user)`
|
|
31
|
-
# is flagged when `welcome` is not defined on
|
|
28
|
+
# 1. **Method existence** — `UserMailer.welcome(user)` is flagged when `welcome` is not defined on
|
|
32
29
|
# `UserMailer`.
|
|
33
|
-
# 2. **Argument arity** — calls with too few / too many
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
# `app/views/<mailer_underscore>/<action>.{html,text}.{erb,haml,slim}`
|
|
38
|
-
# must exist. Missing actions get a `missing-view`
|
|
39
|
-
# diagnostic anchored on the action's `def`.
|
|
30
|
+
# 2. **Argument arity** — calls with too few / too many positional arguments emit `wrong-arity`.
|
|
31
|
+
# 3. **View template existence** — for every action method, at least one of
|
|
32
|
+
# `app/views/<mailer_underscore>/<action>.{html,text}.{erb,haml,slim}` must exist. Missing
|
|
33
|
+
# actions get a `missing-view` diagnostic anchored on the action's `def`.
|
|
40
34
|
#
|
|
41
35
|
# ## Limitations (v0.1.0)
|
|
42
36
|
#
|
|
43
37
|
# - Direct-superclass match only.
|
|
44
|
-
# - Action methods are read from the syntactic instance-
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
# cache until something the mailer file touches
|
|
49
|
-
# changes.
|
|
38
|
+
# - Action methods are read from the syntactic instance-side `def` list. `define_method` actions are
|
|
39
|
+
# out of scope.
|
|
40
|
+
# - Adding a brand-new view file does not invalidate the cache until something the mailer file
|
|
41
|
+
# touches changes.
|
|
50
42
|
class Actionmailer < Rigor::Plugin::Base
|
|
51
43
|
manifest(
|
|
52
44
|
id: "actionmailer",
|
|
53
|
-
# Bumped 2026-05-28 — extended RESERVED_CLASS_METHODS to
|
|
54
|
-
#
|
|
55
|
-
# `
|
|
56
|
-
#
|
|
57
|
-
# `Mailer.public_send(action)`) stop firing
|
|
58
|
-
# `unknown-action` against the Ruby reflection method.
|
|
45
|
+
# Bumped 2026-05-28 — extended RESERVED_CLASS_METHODS to include `respond_to?` / `public_send` /
|
|
46
|
+
# `send` / `__send__` / `method` and friends so dynamic-dispatch idioms
|
|
47
|
+
# (`Mailer.respond_to?(action)` / `Mailer.public_send(action)`) stop firing `unknown-action`
|
|
48
|
+
# against the Ruby reflection method.
|
|
59
49
|
version: "0.4.0",
|
|
60
50
|
description: "Validates ActionMailer call shape and view template existence.",
|
|
61
51
|
config_schema: {
|
|
@@ -65,11 +55,10 @@ module Rigor
|
|
|
65
55
|
}
|
|
66
56
|
)
|
|
67
57
|
|
|
68
|
-
# `watch:` covers every mailer class under `mailer_search_paths`
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
# otherwise be invisible to the dependency descriptor.
|
|
58
|
+
# `watch:` covers every mailer class under `mailer_search_paths` AND every view template under
|
|
59
|
+
# `views_root` (ADR-60 WD3) — a newly-added view a mailer references must invalidate the index,
|
|
60
|
+
# and `view_exists?` failures the producer never records would otherwise be invisible to the
|
|
61
|
+
# dependency descriptor.
|
|
73
62
|
producer :mailer_index,
|
|
74
63
|
watch: -> { [[@mailer_search_paths, "**/*.rb"], [@views_root, "**/*"]] } do |_params|
|
|
75
64
|
MailerDiscoverer.new(
|
|
@@ -86,11 +75,10 @@ module Rigor
|
|
|
86
75
|
@views_root = config.fetch("views_root").to_s
|
|
87
76
|
end
|
|
88
77
|
|
|
89
|
-
# File-level: load-error + the missing-view check (anchored on the
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
# (ADR-37). The mailer index is lazily loaded + memoised, shared.
|
|
78
|
+
# File-level: load-error + the missing-view check (anchored on the mailer's own source file, so it
|
|
79
|
+
# is genuinely per-file, not per-call). The per-call mailer validation (action existence / arity)
|
|
80
|
+
# runs over the engine-owned walk via the node_rule below (ADR-37). The mailer index is lazily
|
|
81
|
+
# loaded + memoised, shared.
|
|
94
82
|
def diagnostics_for_file(path:, scope:, root:) # rubocop:disable Lint/UnusedMethodArgument
|
|
95
83
|
index = producer_value(:mailer_index)
|
|
96
84
|
return [load_error_diagnostic(path)] if index.nil? && producer_error(:mailer_index)
|
|
@@ -108,10 +96,9 @@ module Rigor
|
|
|
108
96
|
|
|
109
97
|
private
|
|
110
98
|
|
|
111
|
-
# Anchors `missing-view` diagnostics on the mailer file
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
# action template at the action's `def` location.
|
|
99
|
+
# Anchors `missing-view` diagnostics on the mailer file itself: when the file currently being
|
|
100
|
+
# analysed is the mailer's source file, emit one diagnostic per missing action template at the
|
|
101
|
+
# action's `def` location.
|
|
115
102
|
def missing_view_diagnostics(path, index)
|
|
116
103
|
canonical = canonical_path(path)
|
|
117
104
|
class_entry = index.find_by_file(canonical)
|