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,16 +8,12 @@ require_relative "rails_i18n/analyzer"
|
|
|
8
8
|
|
|
9
9
|
module Rigor
|
|
10
10
|
module Plugin
|
|
11
|
-
# rigor-rails-i18n — validates `t('key.path')` /
|
|
12
|
-
# `I18n.t(...)` calls against `config/locales/*.yml`.
|
|
11
|
+
# rigor-rails-i18n — validates `t('key.path')` / `I18n.t(...)` calls against `config/locales/*.yml`.
|
|
13
12
|
#
|
|
14
13
|
# Tier 1B of the [Rails plugins roadmap](../../../../docs/design/20260508-rails-plugins-roadmap.md).
|
|
15
|
-
# Statically reads every YAML file under
|
|
16
|
-
# `
|
|
17
|
-
#
|
|
18
|
-
# leaf key path, and validates every `t(literal_key, ...)`
|
|
19
|
-
# call site against the catalogue. No Rails runtime
|
|
20
|
-
# dependency.
|
|
14
|
+
# Statically reads every YAML file under `locale_search_paths` (default `config/locales/`), builds a
|
|
15
|
+
# flat `dotted_key => Entry` index keyed by the leaf key path, and validates every `t(literal_key, ...)`
|
|
16
|
+
# call site against the catalogue. No Rails runtime dependency.
|
|
21
17
|
#
|
|
22
18
|
# ## Configuration
|
|
23
19
|
#
|
|
@@ -30,55 +26,37 @@ module Rigor
|
|
|
30
26
|
#
|
|
31
27
|
# ## What it checks
|
|
32
28
|
#
|
|
33
|
-
# 1. **Key existence** — `t('users.welcome')` is flagged
|
|
34
|
-
#
|
|
35
|
-
# 2. **Per-locale coverage** — when the key resolves in
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
# arguments. Missing placeholders are errors; extra
|
|
42
|
-
# arguments are warnings.
|
|
43
|
-
# 4. **View template lazy keys** — `t('.title')` inside
|
|
44
|
-
# `app/views/setting/index.html.erb` expands to
|
|
45
|
-
# `setting.index.title` and is validated against the
|
|
46
|
-
# locale index. ERB, Haml, and Slim templates are
|
|
29
|
+
# 1. **Key existence** — `t('users.welcome')` is flagged when `users.welcome` does not appear in any
|
|
30
|
+
# locale.
|
|
31
|
+
# 2. **Per-locale coverage** — when the key resolves in some locales but not all configured locales,
|
|
32
|
+
# the plugin emits a `missing-locale` warning. Suppressed when the call site passes `default:`.
|
|
33
|
+
# 3. **Interpolation variables** — the leaf string's `%{var}` placeholders must match the call's
|
|
34
|
+
# keyword arguments. Missing placeholders are errors; extra arguments are warnings.
|
|
35
|
+
# 4. **View template lazy keys** — `t('.title')` inside `app/views/setting/index.html.erb` expands to
|
|
36
|
+
# `setting.index.title` and is validated against the locale index. ERB, Haml, and Slim templates are
|
|
47
37
|
# scanned under `view_search_paths`.
|
|
48
38
|
#
|
|
49
39
|
# ## Limitations
|
|
50
40
|
#
|
|
51
|
-
# - Only literal-string keys are validated. `t(key)` with
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
# -
|
|
61
|
-
# Slim) are validated for key existence and per-locale
|
|
62
|
-
# coverage. Interpolation variable validation is skipped
|
|
63
|
-
# for view templates (the hash may come from controller
|
|
64
|
-
# instance variables not visible in the template).
|
|
65
|
-
# The view scan is a project-wide pass surfaced through the
|
|
66
|
-
# per-file diagnostic hook, so under `--workers` each
|
|
67
|
-
# fork-pool worker re-emits the full set (the same
|
|
68
|
-
# once-per-run limitation the `load-error` path carries);
|
|
41
|
+
# - Only literal-string keys are validated. `t(key)` with a variable receiver is silently passed through.
|
|
42
|
+
# - Lazy lookup (`t('.key')`) is supported for controller files (`app/controllers/**/*_controller.rb`):
|
|
43
|
+
# the key is expanded to `<controller_scope>.<action>.<key>` using the file path and the innermost
|
|
44
|
+
# enclosing `def`. Lazy keys in non-controller `.rb` files (models, helpers, mailers, …) are silently
|
|
45
|
+
# skipped — the controller/action scope cannot be statically determined there.
|
|
46
|
+
# - View template lazy keys (`t('.key')` inside ERB / Haml / Slim) are validated for key existence and
|
|
47
|
+
# per-locale coverage. Interpolation variable validation is skipped for view templates (the hash may
|
|
48
|
+
# come from controller instance variables not visible in the template). The view scan is a
|
|
49
|
+
# project-wide pass surfaced through the per-file diagnostic hook, so under `--workers` each fork-pool
|
|
50
|
+
# worker re-emits the full set (the same once-per-run limitation the `load-error` path carries);
|
|
69
51
|
# sequential `rigor check` is unaffected.
|
|
70
|
-
# - Pluralization (`t('errors.messages.too_short',
|
|
71
|
-
# count
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
# - YAML aliases / merges are accepted (Psych's standard
|
|
75
|
-
# `aliases: true`) but custom Ruby classes inside the
|
|
76
|
-
# YAML are NOT permitted (`safe_load`).
|
|
52
|
+
# - Pluralization (`t('errors.messages.too_short', count: n)`) is recognised at the call site but the
|
|
53
|
+
# `count` key is not used to validate the locale's pluralization branches.
|
|
54
|
+
# - YAML aliases / merges are accepted (Psych's standard `aliases: true`) but custom Ruby classes
|
|
55
|
+
# inside the YAML are NOT permitted (`safe_load`).
|
|
77
56
|
class RailsI18n < Rigor::Plugin::Base
|
|
78
57
|
manifest(
|
|
79
58
|
id: "rails-i18n",
|
|
80
|
-
# Bumped 2026-06-23 — view template lazy-key scanning
|
|
81
|
-
# (`t('.key')` inside ERB / Haml / Slim under
|
|
59
|
+
# Bumped 2026-06-23 — view template lazy-key scanning (`t('.key')` inside ERB / Haml / Slim under
|
|
82
60
|
# `view_search_paths`).
|
|
83
61
|
version: "0.3.0",
|
|
84
62
|
description: "Validates I18n `t(key)` calls against `config/locales/*.yml`.",
|
|
@@ -89,12 +67,10 @@ module Rigor
|
|
|
89
67
|
}
|
|
90
68
|
)
|
|
91
69
|
|
|
92
|
-
# `watch:` covers every `.yml` / `.yaml` file under the locale
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
# runs (a cache miss / a watched file changed), which is exactly
|
|
97
|
-
# when a malformed YAML must re-surface.
|
|
70
|
+
# `watch:` covers every `.yml` / `.yaml` file under the locale search paths so the cache invalidates
|
|
71
|
+
# when locale files are added, removed, or edited (ADR-60 WD3). `@load_errors` is a producer-side
|
|
72
|
+
# capture: it is populated only when the block runs (a cache miss / a watched file changed), which is
|
|
73
|
+
# exactly when a malformed YAML must re-surface.
|
|
98
74
|
producer :locale_index, watch: -> { [[@locale_search_paths, "**/*.yml", "**/*.yaml"]] } do |_params|
|
|
99
75
|
loader = LocaleLoader.new(
|
|
100
76
|
io_boundary: io_boundary,
|
|
@@ -105,14 +81,12 @@ module Rigor
|
|
|
105
81
|
index
|
|
106
82
|
end
|
|
107
83
|
|
|
108
|
-
# Scans view templates under `view_search_paths` for lazy
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
# validation is skipped — the hash may come from controller
|
|
112
|
-
# instance variables not visible in the template source.
|
|
84
|
+
# Scans view templates under `view_search_paths` for lazy `t('.key')` / `I18n.translate('.key')`
|
|
85
|
+
# calls and validates each expanded key against the locale index. Interpolation validation is
|
|
86
|
+
# skipped — the hash may come from controller instance variables not visible in the template source.
|
|
113
87
|
#
|
|
114
|
-
# Watches `**/*.{erb,haml,slim}` under each search root so
|
|
115
|
-
#
|
|
88
|
+
# Watches `**/*.{erb,haml,slim}` under each search root so the cache invalidates when templates are
|
|
89
|
+
# edited.
|
|
116
90
|
producer :view_diagnostics, watch: -> { [[@view_search_paths, "**/*.erb", "**/*.haml", "**/*.slim"]] } do |_params|
|
|
117
91
|
index = producer_value(:locale_index)
|
|
118
92
|
next [] if index.nil? || index.empty?
|
|
@@ -129,11 +103,10 @@ module Rigor
|
|
|
129
103
|
@view_diagnostics_emitted = false
|
|
130
104
|
end
|
|
131
105
|
|
|
132
|
-
# File-level only: the once-per-run YAML load errors + the
|
|
133
|
-
#
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
# are lazily loaded + memoised by `producer_value`.
|
|
106
|
+
# File-level only: the once-per-run YAML load errors + the runtime (cache-load) error + the
|
|
107
|
+
# view-template scan. Per-call `t('key')` validation runs over the engine-owned walk via the
|
|
108
|
+
# node_rule below (ADR-37). The locale index and view diagnostics are lazily loaded + memoised by
|
|
109
|
+
# `producer_value`.
|
|
137
110
|
def diagnostics_for_file(path:, scope:, root:) # rubocop:disable Lint/UnusedMethodArgument
|
|
138
111
|
index = producer_value(:locale_index)
|
|
139
112
|
diagnostics = []
|
|
@@ -151,9 +124,8 @@ module Rigor
|
|
|
151
124
|
diagnostics
|
|
152
125
|
end
|
|
153
126
|
|
|
154
|
-
# The lazy-key (`t('.key')`) expansion needs the enclosing method
|
|
155
|
-
#
|
|
156
|
-
# the controller scope comes from the file path.
|
|
127
|
+
# The lazy-key (`t('.key')`) expansion needs the enclosing method (the controller action), supplied
|
|
128
|
+
# by the node-rule NodeContext; the controller scope comes from the file path.
|
|
157
129
|
node_rule Prism::CallNode do |node, _scope, path, _fc, context|
|
|
158
130
|
index = producer_value(:locale_index)
|
|
159
131
|
next [] if index.nil? || index.empty?
|
|
@@ -205,12 +177,10 @@ module Rigor
|
|
|
205
177
|
end.sort
|
|
206
178
|
end
|
|
207
179
|
|
|
208
|
-
# Diagnostics anchor on a path relative to the working
|
|
209
|
-
#
|
|
210
|
-
#
|
|
211
|
-
#
|
|
212
|
-
# project-relative paths). Falls back to the absolute path
|
|
213
|
-
# for a view outside the working tree.
|
|
180
|
+
# Diagnostics anchor on a path relative to the working directory — the same base `File.expand_path`
|
|
181
|
+
# globbed the view files against — so view diagnostics render and match baselines like every other
|
|
182
|
+
# diagnostic (which carry project-relative paths). Falls back to the absolute path for a view outside
|
|
183
|
+
# the working tree.
|
|
214
184
|
def relative_view_path(absolute_path)
|
|
215
185
|
Pathname.new(absolute_path).relative_path_from(Pathname.pwd).to_s
|
|
216
186
|
rescue ArgumentError
|
|
@@ -232,12 +202,9 @@ module Rigor
|
|
|
232
202
|
)
|
|
233
203
|
end
|
|
234
204
|
|
|
235
|
-
# The runner only invokes `diagnostics_for_file` for
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
# offending locale file directly; instead, we emit
|
|
239
|
-
# them once per run on the first analyzed Ruby file,
|
|
240
|
-
# naming the offending YAML path in the message.
|
|
205
|
+
# The runner only invokes `diagnostics_for_file` for Ruby files (`paths:` is filtered to `.rb`). YAML
|
|
206
|
+
# parse errors therefore can't be anchored on the offending locale file directly; instead, we emit
|
|
207
|
+
# them once per run on the first analyzed Ruby file, naming the offending YAML path in the message.
|
|
241
208
|
def consume_load_error_diagnostics(path)
|
|
242
209
|
return [] if @load_errors_emitted
|
|
243
210
|
|
|
@@ -6,18 +6,13 @@ require "rigor/source/literals"
|
|
|
6
6
|
module Rigor
|
|
7
7
|
module Plugin
|
|
8
8
|
class RailsRoutes < Rigor::Plugin::Base
|
|
9
|
-
# Walks a parsed file's AST looking for `*_path` /
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# recognised helpers and error diagnostics for typos /
|
|
13
|
-
# arity mismatches.
|
|
9
|
+
# Walks a parsed file's AST looking for `*_path` / `*_url` calls and validates each against the
|
|
10
|
+
# plugin's {HelperTable}. Emits info diagnostics for recognised helpers and error diagnostics for
|
|
11
|
+
# typos / arity mismatches.
|
|
14
12
|
module Analyzer
|
|
15
|
-
# Built-in Rails helpers we don't want to flag as
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# widely-used asset gems) ship built-in helpers
|
|
19
|
-
# (`url_for`, `polymorphic_path`, vite_ruby's
|
|
20
|
-
# `vite_asset_path`, …) the plugin deliberately ignores.
|
|
13
|
+
# Built-in Rails helpers we don't want to flag as unknown. The plugin's HelperTable describes
|
|
14
|
+
# user-declared routes; Rails (and a small set of widely-used asset gems) ship built-in helpers
|
|
15
|
+
# (`url_for`, `polymorphic_path`, vite_ruby's `vite_asset_path`, …) the plugin deliberately ignores.
|
|
21
16
|
BUILTIN_PASSTHROUGH = %w[
|
|
22
17
|
url_for_path url_for_url
|
|
23
18
|
polymorphic_path polymorphic_url
|
|
@@ -32,35 +27,27 @@ module Rigor
|
|
|
32
27
|
audio_path audio_url
|
|
33
28
|
].freeze
|
|
34
29
|
|
|
35
|
-
# One route-helper observation. Carries no path/location — the
|
|
36
|
-
#
|
|
37
|
-
# `Plugin::Base#diagnostic`.
|
|
30
|
+
# One route-helper observation. Carries no path/location — the caller (the `node_rule` block)
|
|
31
|
+
# positions it via `Plugin::Base#diagnostic`.
|
|
38
32
|
Violation = Struct.new(:rule, :severity, :message, keyword_init: true)
|
|
39
33
|
|
|
40
34
|
module_function
|
|
41
35
|
|
|
42
|
-
# RSpec / Minitest DSL methods that DECLARE a memoized
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# positional Symbol argument is the local name. A bare
|
|
46
|
-
# `subject { ... }` (no arg) defines `:subject` itself.
|
|
36
|
+
# RSpec / Minitest DSL methods that DECLARE a memoized local — `let(:foo) { ... }`, `subject(:foo)
|
|
37
|
+
# { ... }`, plus the bang and let_it_be variants. The first positional Symbol argument is the local
|
|
38
|
+
# name. A bare `subject { ... }` (no arg) defines `:subject` itself.
|
|
47
39
|
SHADOWING_DSL = %i[
|
|
48
40
|
let let! let_it_be let_it_be!
|
|
49
41
|
subject subject!
|
|
50
42
|
].freeze
|
|
51
43
|
|
|
52
|
-
# Paths under which `unknown-helper` is suppressed. Route
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
# `
|
|
56
|
-
# `
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
# call-site is `Paperclip::UrlGenerator#default_url`, a
|
|
60
|
-
# gem-side instance method) would be a false positive.
|
|
61
|
-
# The core engine's `call.undefined-method` still catches
|
|
62
|
-
# a genuinely unreachable receiver. Cheap path-prefix
|
|
63
|
-
# check; no AR-column / gem-include analysis required.
|
|
44
|
+
# Paths under which `unknown-helper` is suppressed. Route helpers are not customarily resolved
|
|
45
|
+
# through these directories, so a bare `shared_inbox_url` call inside `app/models/account.rb` (the
|
|
46
|
+
# call-site is the `accounts.shared_inbox_url` AR column accessor — same name as a hypothetical
|
|
47
|
+
# route helper) or a `default_url` inside `lib/paperclip/url_generator_extensions.rb` (the
|
|
48
|
+
# call-site is `Paperclip::UrlGenerator#default_url`, a gem-side instance method) would be a false
|
|
49
|
+
# positive. The core engine's `call.undefined-method` still catches a genuinely unreachable
|
|
50
|
+
# receiver. Cheap path-prefix check; no AR-column / gem-include analysis required.
|
|
64
51
|
SKIP_UNKNOWN_HELPER_PATHS = [
|
|
65
52
|
%r{(?:\A|/)app/models/},
|
|
66
53
|
%r{(?:\A|/)app/services/},
|
|
@@ -78,12 +65,10 @@ module Rigor
|
|
|
78
65
|
# @param root [Prism::Node]
|
|
79
66
|
# @param helper_table [HelperTable]
|
|
80
67
|
# @return [Array<Diagnostic>]
|
|
81
|
-
# The route-helper violations for a single call node (0..2),
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
# same-file `shadowing` set is built once as the node-rule file
|
|
86
|
-
# context.
|
|
68
|
+
# The route-helper violations for a single call node (0..2), or `[]` when the node is not an
|
|
69
|
+
# implicit `*_path` / `*_url` helper call, is shadowed by a same-file binding, or is in a
|
|
70
|
+
# suppressed directory. ADR-37: the engine owns the walk; the same-file `shadowing` set is built
|
|
71
|
+
# once as the node-rule file context.
|
|
87
72
|
#
|
|
88
73
|
# @param call_node [Prism::Node]
|
|
89
74
|
# @param helper_table [HelperTable]
|
|
@@ -97,9 +82,8 @@ module Rigor
|
|
|
97
82
|
return [] if BUILTIN_PASSTHROUGH.include?(name)
|
|
98
83
|
return [] if shadowing.include?(name)
|
|
99
84
|
|
|
100
|
-
# The SKIP set silences both `unknown-helper` and `wrong-arity`:
|
|
101
|
-
#
|
|
102
|
-
# file's own method (an `attr_accessor`) than a route helper.
|
|
85
|
+
# The SKIP set silences both `unknown-helper` and `wrong-arity`: a `group_path` inside
|
|
86
|
+
# `app/services/...` is more likely the file's own method (an `attr_accessor`) than a route helper.
|
|
103
87
|
suppress_unknown = SKIP_UNKNOWN_HELPER_PATHS.any? { |re| path.match?(re) }
|
|
104
88
|
|
|
105
89
|
entry = helper_table.find(name)
|
|
@@ -111,26 +95,21 @@ module Rigor
|
|
|
111
95
|
violations << arity if arity
|
|
112
96
|
violations
|
|
113
97
|
elsif helper_table.recognised?(name) || suppress_unknown
|
|
114
|
-
# Recognised custom / dynamic helper (no arity to check), or
|
|
115
|
-
#
|
|
98
|
+
# Recognised custom / dynamic helper (no arity to check), or a directory where helpers aren't
|
|
99
|
+
# customarily resolved.
|
|
116
100
|
[]
|
|
117
101
|
else
|
|
118
102
|
[unknown_helper_violation(name, helper_table)]
|
|
119
103
|
end
|
|
120
104
|
end
|
|
121
105
|
|
|
122
|
-
# Walks the AST once and returns the Set of names that
|
|
123
|
-
# shadow a route helper for this file. Includes:
|
|
106
|
+
# Walks the AST once and returns the Set of names that shadow a route helper for this file. Includes:
|
|
124
107
|
#
|
|
125
|
-
# - `def name` declarations (any level — the
|
|
126
|
-
#
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
# - RSpec `let` / `let!` / `let_it_be` / `let_it_be!` /
|
|
131
|
-
# `subject` / `subject!` declarations.
|
|
132
|
-
# - Local assignments at any scope
|
|
133
|
-
# (`foo_url = "..."`).
|
|
108
|
+
# - `def name` declarations (any level — the per-method-scope visibility model Ruby uses means a
|
|
109
|
+
# local `def` shadows the helper at every call site reachable from where it is defined; we
|
|
110
|
+
# approximate "reachable" with "anywhere in the same file").
|
|
111
|
+
# - RSpec `let` / `let!` / `let_it_be` / `let_it_be!` / `subject` / `subject!` declarations.
|
|
112
|
+
# - Local assignments at any scope (`foo_url = "..."`).
|
|
134
113
|
def collect_shadowing_names(root)
|
|
135
114
|
names = Set.new
|
|
136
115
|
Source::NodeWalker.each(root) do |node|
|
|
@@ -160,22 +139,17 @@ module Rigor
|
|
|
160
139
|
end
|
|
161
140
|
end
|
|
162
141
|
|
|
163
|
-
# `*_path` / `*_url` calls without an explicit
|
|
164
|
-
#
|
|
165
|
-
# `Foo::users_path` are NOT route-helper invocations
|
|
166
|
-
# in Rails — controllers / views call helpers
|
|
142
|
+
# `*_path` / `*_url` calls without an explicit receiver. Calls like `obj.users_path` or
|
|
143
|
+
# `Foo::users_path` are NOT route-helper invocations in Rails — controllers / views call helpers
|
|
167
144
|
# implicitly.
|
|
168
145
|
def implicit_helper_call?(node)
|
|
169
146
|
node.receiver.nil? && (node.name.to_s.end_with?("_path") || node.name.to_s.end_with?("_url"))
|
|
170
147
|
end
|
|
171
148
|
|
|
172
|
-
# Paths where the implicit-params-fill pattern is
|
|
173
|
-
#
|
|
174
|
-
#
|
|
175
|
-
# the
|
|
176
|
-
# fills `:foo_id` segments from `request.params` at
|
|
177
|
-
# runtime (controllers / views) or from the call's
|
|
178
|
-
# polymorphic-friendly receiver (mailers).
|
|
149
|
+
# Paths where the implicit-params-fill pattern is idiomatic — controllers / mailers / views
|
|
150
|
+
# routinely call `*_path` / `*_url` helpers with fewer args than the route's static placeholder
|
|
151
|
+
# count because Rails fills `:foo_id` segments from `request.params` at runtime (controllers /
|
|
152
|
+
# views) or from the call's polymorphic-friendly receiver (mailers).
|
|
179
153
|
IMPLICIT_FILL_PATHS = [
|
|
180
154
|
%r{(?:\A|/)app/controllers/},
|
|
181
155
|
%r{(?:\A|/)app/mailers/},
|
|
@@ -200,40 +174,29 @@ module Rigor
|
|
|
200
174
|
def arity_violation(call_node, entry, helper_table, path)
|
|
201
175
|
args = call_node.arguments&.arguments || []
|
|
202
176
|
actual = args.size
|
|
203
|
-
# Uncountable nouns (`news` / `series` / `media`) cause
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
# arity 1 (show). The HelperTable multimap stores both;
|
|
207
|
-
# accepts_arity? checks the full set.
|
|
177
|
+
# Uncountable nouns (`news` / `series` / `media`) cause Rails to register two entries under the
|
|
178
|
+
# same helper name — `news_path` accepts both arity 0 (index) and arity 1 (show). The HelperTable
|
|
179
|
+
# multimap stores both; accepts_arity? checks the full set.
|
|
208
180
|
return nil if helper_table.accepts_arity?(entry.name, actual)
|
|
209
181
|
|
|
210
|
-
# Rails accepts a kwargs-only call shape that supplies
|
|
211
|
-
# route segments by name:
|
|
182
|
+
# Rails accepts a kwargs-only call shape that supplies route segments by name:
|
|
212
183
|
# short_account_status_url(account_username: u, id: i)
|
|
213
|
-
# for the route `/@:account_username/:id` (arity 2).
|
|
214
|
-
#
|
|
215
|
-
#
|
|
216
|
-
#
|
|
217
|
-
# has a trailing KeywordHashNode AND the positional
|
|
218
|
-
# count (excluding it) is `<= expected_arity`, accept
|
|
219
|
-
# — the kwargs may carry the missing segments.
|
|
184
|
+
# for the route `/@:account_username/:id` (arity 2). Our positional-arg count is 1 (the
|
|
185
|
+
# KeywordHashNode), so the strict check rejects — but Rails would resolve every segment from the
|
|
186
|
+
# hash. When the call has a trailing KeywordHashNode AND the positional count (excluding it) is
|
|
187
|
+
# `<= expected_arity`, accept — the kwargs may carry the missing segments.
|
|
220
188
|
if args.last.is_a?(Prism::KeywordHashNode)
|
|
221
189
|
positional = actual - 1
|
|
222
190
|
return nil if helper_table.acceptable_arities(entry.name).any? { |exp| positional <= exp }
|
|
223
191
|
end
|
|
224
192
|
|
|
225
|
-
# Underflow tolerance for controllers / mailers /
|
|
226
|
-
#
|
|
227
|
-
#
|
|
228
|
-
#
|
|
229
|
-
#
|
|
230
|
-
#
|
|
231
|
-
# (`
|
|
232
|
-
# commit.project). Silence when `actual < min_arity`
|
|
233
|
-
# AND the call site is in a controller / mailer / view
|
|
234
|
-
# directory (where the implicit-params-fill pattern is
|
|
235
|
-
# idiomatic). Overflow (`actual > max_arity`) still
|
|
236
|
-
# fires — that's almost always a typo.
|
|
193
|
+
# Underflow tolerance for controllers / mailers / views. A `redirect_to
|
|
194
|
+
# namespace_project_milestones_path` inside `Projects::MilestonesController` legitimately passes
|
|
195
|
+
# 0 args — Rails fills the missing `:namespace_id` / `:project_id` segments from `request.params`
|
|
196
|
+
# at runtime. Mailers reach helpers polymorphically too (`project_commit_url(commit)` resolves
|
|
197
|
+
# project from commit.project). Silence when `actual < min_arity` AND the call site is in a
|
|
198
|
+
# controller / mailer / view directory (where the implicit-params-fill pattern is idiomatic).
|
|
199
|
+
# Overflow (`actual > max_arity`) still fires — that's almost always a typo.
|
|
237
200
|
arities_set = helper_table.acceptable_arities(entry.name)
|
|
238
201
|
min_arity = arities_set.min
|
|
239
202
|
return nil if actual < min_arity && implicit_fill_path?(path)
|
|
@@ -248,8 +211,8 @@ module Rigor
|
|
|
248
211
|
end
|
|
249
212
|
|
|
250
213
|
def unknown_helper_violation(name, helper_table)
|
|
251
|
-
# ADR-39 / boilerplate 0c — the shared DidYouMean-backed suggester
|
|
252
|
-
#
|
|
214
|
+
# ADR-39 / boilerplate 0c — the shared DidYouMean-backed suggester replaces the hand-rolled
|
|
215
|
+
# Levenshtein this module used to carry.
|
|
253
216
|
suggestion = Rigor::Plugin::Base.suggest(name, helper_table.names)
|
|
254
217
|
message = "no route helper `#{name}`"
|
|
255
218
|
message += " (did you mean `#{suggestion}`?)" if suggestion
|