rigortype 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/data/core_overlay/csv.rbs +28 -0
- data/data/core_overlay/psych.rbs +22 -0
- data/docs/handbook/01-getting-started.md +9 -1
- data/docs/handbook/02-everyday-types.md +4 -1
- data/docs/handbook/08-understanding-errors.md +3 -3
- data/docs/manual/01-installation.md +1 -0
- data/docs/manual/02-cli-reference.md +16 -7
- data/docs/manual/04-diagnostics.md +7 -4
- data/docs/manual/06-baseline.md +5 -2
- data/docs/manual/07-plugins.md +1 -1
- data/docs/manual/14-rails-quickstart.md +4 -2
- data/docs/manual/15-type-protection-coverage.md +27 -4
- data/docs/manual/plugins/rigor-actionpack.md +1 -1
- data/docs/manual/plugins/rigor-activerecord.md +12 -5
- data/docs/manual/plugins/rigor-rails-routes.md +11 -0
- data/lib/rigor/analysis/baseline.rb +56 -96
- data/lib/rigor/analysis/buffer_binding.rb +13 -20
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
- data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
- data/lib/rigor/analysis/check_rules.rb +174 -274
- data/lib/rigor/analysis/dependency_recorder.rb +26 -38
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
- data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
- data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
- data/lib/rigor/analysis/diagnostic.rb +48 -75
- data/lib/rigor/analysis/erb_template_detector.rb +13 -18
- data/lib/rigor/analysis/fact_store.rb +12 -22
- data/lib/rigor/analysis/incremental.rb +47 -63
- data/lib/rigor/analysis/incremental_session.rb +74 -100
- data/lib/rigor/analysis/project_scan.rb +12 -23
- data/lib/rigor/analysis/result.rb +3 -5
- data/lib/rigor/analysis/rule_catalog.rb +67 -95
- data/lib/rigor/analysis/run_stats.rb +38 -61
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
- data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
- data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
- data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
- data/lib/rigor/analysis/runner.rb +220 -333
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
- data/lib/rigor/analysis/worker_session.rb +64 -102
- data/lib/rigor/ast/type_node.rb +6 -8
- data/lib/rigor/ast.rb +6 -10
- data/lib/rigor/bleeding_edge.rb +27 -35
- data/lib/rigor/builtins/hkt_builtins.rb +7 -14
- data/lib/rigor/builtins/imported_refinements.rb +106 -149
- data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
- data/lib/rigor/builtins/regex_refinement.rb +32 -43
- data/lib/rigor/builtins/static_return_refinements.rb +57 -81
- data/lib/rigor/cache/descriptor.rb +58 -94
- data/lib/rigor/cache/incremental_snapshot.rb +32 -48
- data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
- data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
- data/lib/rigor/cache/rbs_constant_table.rb +8 -13
- data/lib/rigor/cache/rbs_descriptor.rb +8 -13
- data/lib/rigor/cache/rbs_environment.rb +11 -19
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
- data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
- data/lib/rigor/cache/store.rb +248 -186
- data/lib/rigor/cli/annotate_command.rb +51 -88
- data/lib/rigor/cli/baseline_command.rb +10 -17
- data/lib/rigor/cli/check_command.rb +121 -199
- data/lib/rigor/cli/check_runner_factory.rb +8 -14
- data/lib/rigor/cli/ci_detector.rb +9 -14
- data/lib/rigor/cli/command.rb +8 -14
- data/lib/rigor/cli/coverage_command.rb +62 -48
- data/lib/rigor/cli/coverage_mutation.rb +14 -19
- data/lib/rigor/cli/coverage_report.rb +0 -2
- data/lib/rigor/cli/coverage_scan.rb +7 -11
- data/lib/rigor/cli/diagnostic_formats.rb +46 -62
- data/lib/rigor/cli/diff_command.rb +10 -20
- data/lib/rigor/cli/docs_command.rb +26 -40
- data/lib/rigor/cli/doctor_command.rb +8 -9
- data/lib/rigor/cli/explain_command.rb +4 -7
- data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
- data/lib/rigor/cli/fused_protection_report.rb +6 -8
- data/lib/rigor/cli/lsp_command.rb +11 -19
- data/lib/rigor/cli/mcp_command.rb +4 -6
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
- data/lib/rigor/cli/mutation_protection_report.rb +6 -9
- data/lib/rigor/cli/options.rb +11 -19
- data/lib/rigor/cli/plugin_command.rb +18 -30
- data/lib/rigor/cli/plugins_command.rb +29 -51
- data/lib/rigor/cli/plugins_renderer.rb +12 -20
- data/lib/rigor/cli/prism_colorizer.rb +13 -19
- data/lib/rigor/cli/protection_fork_scan.rb +55 -0
- data/lib/rigor/cli/protection_renderer.rb +5 -7
- data/lib/rigor/cli/protection_report.rb +34 -18
- data/lib/rigor/cli/renderable.rb +4 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
- data/lib/rigor/cli/sig_gen_command.rb +14 -26
- data/lib/rigor/cli/skill_command.rb +30 -47
- data/lib/rigor/cli/skill_describe.rb +40 -64
- data/lib/rigor/cli/trace_command.rb +9 -16
- data/lib/rigor/cli/trace_renderer.rb +35 -51
- data/lib/rigor/cli/triage_command.rb +6 -10
- data/lib/rigor/cli/triage_renderer.rb +1 -2
- data/lib/rigor/cli/type_of_command.rb +14 -23
- data/lib/rigor/cli/type_of_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_command.rb +6 -10
- data/lib/rigor/cli/type_scan_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_report.rb +2 -3
- data/lib/rigor/cli/upgrade_command.rb +2 -3
- data/lib/rigor/cli.rb +16 -28
- data/lib/rigor/config_audit.rb +35 -44
- data/lib/rigor/configuration/dependencies.rb +41 -70
- data/lib/rigor/configuration/severity_profile.rb +28 -42
- data/lib/rigor/configuration.rb +150 -241
- data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
- data/lib/rigor/environment/class_registry.rb +19 -26
- data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
- data/lib/rigor/environment/lockfile_resolver.rb +31 -47
- data/lib/rigor/environment/missing_gem_constant_index.rb +128 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
- data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
- data/lib/rigor/environment/rbs_loader.rb +420 -518
- data/lib/rigor/environment/reflection.rb +28 -53
- data/lib/rigor/environment/reporters.rb +13 -24
- data/lib/rigor/environment.rb +225 -304
- data/lib/rigor/flow_contribution/conflict.rb +16 -27
- data/lib/rigor/flow_contribution/element.rb +7 -12
- data/lib/rigor/flow_contribution/fact.rb +33 -57
- data/lib/rigor/flow_contribution/merge_result.rb +6 -9
- data/lib/rigor/flow_contribution/merger.rb +32 -47
- data/lib/rigor/flow_contribution.rb +37 -55
- data/lib/rigor/inference/acceptance.rb +133 -219
- data/lib/rigor/inference/block_parameter_binder.rb +52 -82
- data/lib/rigor/inference/body_fixpoint.rb +22 -30
- data/lib/rigor/inference/budget_trace.rb +43 -63
- data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
- data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
- data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
- data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
- data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
- data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
- data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
- data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
- data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
- data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
- data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
- data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
- data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
- data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
- data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
- data/lib/rigor/inference/coverage_scanner.rb +13 -18
- data/lib/rigor/inference/def_return_typer.rb +8 -14
- data/lib/rigor/inference/dynamic_origin.rb +23 -20
- data/lib/rigor/inference/expression_typer.rb +794 -1154
- data/lib/rigor/inference/fallback.rb +8 -12
- data/lib/rigor/inference/fallback_tracer.rb +4 -10
- data/lib/rigor/inference/flow_tracer.rb +25 -36
- data/lib/rigor/inference/fork_map.rb +87 -0
- data/lib/rigor/inference/hkt_body.rb +45 -68
- data/lib/rigor/inference/hkt_body_parser.rb +33 -52
- data/lib/rigor/inference/hkt_reducer.rb +38 -59
- data/lib/rigor/inference/hkt_registry.rb +50 -76
- data/lib/rigor/inference/indexed_narrowing.rb +55 -81
- data/lib/rigor/inference/macro_block_self_type.rb +21 -34
- data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
- data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
- data/lib/rigor/inference/method_dispatcher.rb +378 -524
- data/lib/rigor/inference/method_parameter_binder.rb +81 -114
- data/lib/rigor/inference/multi_target_binder.rb +51 -68
- data/lib/rigor/inference/mutation_widening.rb +115 -158
- data/lib/rigor/inference/narrowing.rb +627 -727
- data/lib/rigor/inference/origin_lookup.rb +38 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +154 -109
- data/lib/rigor/inference/precision_scanner.rb +26 -35
- data/lib/rigor/inference/project_patched_methods.rb +20 -32
- data/lib/rigor/inference/project_patched_scanner.rb +23 -37
- data/lib/rigor/inference/protection_scanner.rb +16 -18
- data/lib/rigor/inference/rbs_type_translator.rb +49 -66
- data/lib/rigor/inference/scope_indexer.rb +473 -821
- data/lib/rigor/inference/statement_evaluator.rb +754 -1114
- data/lib/rigor/inference/struct_fold_safety.rb +34 -48
- data/lib/rigor/inference/synthetic_method.rb +9 -16
- data/lib/rigor/inference/synthetic_method_index.rb +20 -35
- data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
- data/lib/rigor/language_server/buffer_resolution.rb +6 -9
- data/lib/rigor/language_server/buffer_table.rb +11 -18
- data/lib/rigor/language_server/completion_provider.rb +69 -116
- data/lib/rigor/language_server/debouncer.rb +16 -25
- data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
- data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
- data/lib/rigor/language_server/folding_range_provider.rb +11 -19
- data/lib/rigor/language_server/hover_provider.rb +8 -11
- data/lib/rigor/language_server/hover_renderer.rb +56 -93
- data/lib/rigor/language_server/loop.rb +14 -19
- data/lib/rigor/language_server/project_context.rb +46 -74
- data/lib/rigor/language_server/selection_range_provider.rb +9 -12
- data/lib/rigor/language_server/server.rb +55 -83
- data/lib/rigor/language_server/signature_help_provider.rb +29 -46
- data/lib/rigor/language_server/synchronized_writer.rb +4 -7
- data/lib/rigor/language_server/uri.rb +8 -13
- data/lib/rigor/language_server.rb +4 -6
- data/lib/rigor/mcp/loop.rb +2 -3
- data/lib/rigor/mcp/server.rb +4 -7
- data/lib/rigor/mcp.rb +3 -6
- data/lib/rigor/plugin/access_denied_error.rb +5 -8
- data/lib/rigor/plugin/additional_initializer.rb +21 -31
- data/lib/rigor/plugin/base.rb +335 -518
- data/lib/rigor/plugin/blueprint.rb +14 -23
- data/lib/rigor/plugin/box.rb +18 -29
- data/lib/rigor/plugin/fact_store.rb +16 -26
- data/lib/rigor/plugin/inflector.rb +37 -53
- data/lib/rigor/plugin/io_boundary.rb +33 -56
- data/lib/rigor/plugin/isolation.rb +42 -55
- data/lib/rigor/plugin/load_error.rb +10 -15
- data/lib/rigor/plugin/loader.rb +30 -49
- data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
- data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
- data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
- data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
- data/lib/rigor/plugin/macro.rb +10 -15
- data/lib/rigor/plugin/manifest.rb +85 -144
- data/lib/rigor/plugin/node_context.rb +14 -22
- data/lib/rigor/plugin/node_rule_walk.rb +49 -74
- data/lib/rigor/plugin/protocol_contract.rb +25 -39
- data/lib/rigor/plugin/registry.rb +132 -205
- data/lib/rigor/plugin/services.rb +21 -33
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
- data/lib/rigor/plugin/trust_policy.rb +24 -38
- data/lib/rigor/plugin/type_node_resolver.rb +15 -24
- data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
- data/lib/rigor/protection/mutation_scanner.rb +27 -35
- data/lib/rigor/protection/mutator.rb +50 -70
- data/lib/rigor/protection/test_suite_oracle.rb +20 -27
- data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
- data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
- data/lib/rigor/rbs_extended/reporter.rb +24 -40
- data/lib/rigor/rbs_extended.rb +107 -197
- data/lib/rigor/reflection.rb +68 -86
- data/lib/rigor/scope/discovery_index.rb +14 -19
- data/lib/rigor/scope.rb +255 -310
- data/lib/rigor/sig_gen/classification.rb +6 -10
- data/lib/rigor/sig_gen/generator.rb +197 -323
- data/lib/rigor/sig_gen/layout_index.rb +12 -20
- data/lib/rigor/sig_gen/method_candidate.rb +12 -17
- data/lib/rigor/sig_gen/observation_collector.rb +38 -70
- data/lib/rigor/sig_gen/observed_call.rb +13 -23
- data/lib/rigor/sig_gen/path_mapper.rb +17 -29
- data/lib/rigor/sig_gen/renderer.rb +7 -13
- data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
- data/lib/rigor/sig_gen/write_result.rb +8 -16
- data/lib/rigor/sig_gen/writer.rb +95 -174
- data/lib/rigor/sig_gen.rb +3 -6
- data/lib/rigor/signature_path_audit.rb +24 -30
- data/lib/rigor/source/constant_path.rb +10 -14
- data/lib/rigor/source/literals.rb +31 -45
- data/lib/rigor/source/node_locator.rb +9 -11
- data/lib/rigor/source/node_walker.rb +9 -13
- data/lib/rigor/source.rb +3 -4
- data/lib/rigor/testing.rb +16 -20
- data/lib/rigor/triage/catalogue.rb +38 -62
- data/lib/rigor/triage.rb +31 -52
- data/lib/rigor/trinary.rb +9 -13
- data/lib/rigor/type/acceptance_router.rb +4 -6
- data/lib/rigor/type/accepts_result.rb +10 -14
- data/lib/rigor/type/app.rb +19 -27
- data/lib/rigor/type/bot.rb +4 -6
- data/lib/rigor/type/bound_method.rb +10 -15
- data/lib/rigor/type/combinator.rb +165 -257
- data/lib/rigor/type/constant.rb +23 -34
- data/lib/rigor/type/data_class.rb +10 -15
- data/lib/rigor/type/data_instance.rb +14 -20
- data/lib/rigor/type/difference.rb +21 -32
- data/lib/rigor/type/dynamic.rb +3 -5
- data/lib/rigor/type/hash_shape.rb +32 -18
- data/lib/rigor/type/integer_range.rb +11 -16
- data/lib/rigor/type/intersection.rb +27 -42
- data/lib/rigor/type/nominal.rb +10 -15
- data/lib/rigor/type/plain_lattice.rb +9 -13
- data/lib/rigor/type/refined.rb +67 -114
- data/lib/rigor/type/singleton.rb +4 -6
- data/lib/rigor/type/struct_class.rb +11 -16
- data/lib/rigor/type/struct_instance.rb +15 -21
- data/lib/rigor/type/tuple.rb +14 -19
- data/lib/rigor/type/union.rb +30 -42
- data/lib/rigor/type_node/generic.rb +14 -26
- data/lib/rigor/type_node/identifier.rb +12 -19
- data/lib/rigor/type_node.rb +3 -12
- data/lib/rigor/value_semantics.rb +16 -21
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +141 -184
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +92 -108
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +40 -48
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +140 -129
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +56 -59
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +172 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +168 -241
- data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
- data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +32 -0
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
- data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
- data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
- data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
- data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
- data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
- data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
- data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +189 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +61 -75
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +319 -505
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +91 -96
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
- data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
- data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
- data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
- data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
- data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
- data/sig/rigor/environment.rbs +1 -0
- data/sig/rigor/scope.rbs +6 -0
- metadata +9 -1
|
@@ -3,53 +3,35 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Plugin
|
|
5
5
|
class RailsRoutes < Rigor::Plugin::Base
|
|
6
|
-
# Generates the catalogue of route helpers a `devise_for
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# enumerates every generated helper); the generator
|
|
11
|
-
# mirrors it.
|
|
6
|
+
# Generates the catalogue of route helpers a `devise_for :resources` call adds to a Rails app's
|
|
7
|
+
# routing table. The set is fixed and well-documented (the
|
|
8
|
+
# [Devise wiki](https://github.com/heartcombo/devise/wiki/How-To:-Add-routes-helpers) enumerates
|
|
9
|
+
# every generated helper); the generator mirrors it.
|
|
12
10
|
#
|
|
13
|
-
# Used by `RoutesParser` when it sees a top-level
|
|
14
|
-
# `
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# `
|
|
18
|
-
# check accepts the canonical signatures (Devise helpers
|
|
19
|
-
# take zero positional args plus an optional trailing
|
|
20
|
-
# options hash, which `HelperTable#accepts_arity?`
|
|
21
|
-
# already handles via the `arity + 1` rule).
|
|
11
|
+
# Used by `RoutesParser` when it sees a top-level `devise_for :name` (or `devise_for :name, skip:
|
|
12
|
+
# [...]`) call. Each generated helper is materialised as a `HelperTable::Entry` so the analyzer's
|
|
13
|
+
# `unknown-helper` rule recognises them and the arity check accepts the canonical signatures (Devise
|
|
14
|
+
# helpers take zero positional args plus an optional trailing options hash, which
|
|
15
|
+
# `HelperTable#accepts_arity?` already handles via the `arity + 1` rule).
|
|
22
16
|
#
|
|
23
17
|
# Scope:
|
|
24
18
|
#
|
|
25
|
-
# - Recognises the standard six controllers
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# - Honours `
|
|
29
|
-
#
|
|
30
|
-
# -
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
# - `omniauth_callbacks` helpers are dynamic — the
|
|
37
|
-
# provider name is supplied at call time
|
|
38
|
-
# (`user_facebook_omniauth_authorize_path`) and Devise
|
|
39
|
-
# declares them from the configured providers, which
|
|
40
|
-
# live in an initializer this static parser does not
|
|
41
|
-
# read. The suffix patterns are registered in
|
|
42
|
-
# `OMNIAUTH_SUFFIXES` and consulted by
|
|
43
|
-
# `HelperTable#omniauth_match?`.
|
|
19
|
+
# - Recognises the standard six controllers (`:sessions`, `:passwords`, `:confirmations`, `:unlocks`,
|
|
20
|
+
# `:registrations`, `:omniauth_callbacks`).
|
|
21
|
+
# - Honours `skip: [...]` to suppress controllers the project disables.
|
|
22
|
+
# - Honours `path: "..."` to remap the URL path (does NOT remap the helper-name prefix — Devise uses
|
|
23
|
+
# the resource name for the helper, not the path).
|
|
24
|
+
# - Does NOT yet honour `as: "..."` overrides, `class_name:`, `controllers: { ... }` (these are rare;
|
|
25
|
+
# expand on demand).
|
|
26
|
+
# - `omniauth_callbacks` helpers are dynamic — the provider name is supplied at call time
|
|
27
|
+
# (`user_facebook_omniauth_authorize_path`) and Devise declares them from the configured providers,
|
|
28
|
+
# which live in an initializer this static parser does not read. The suffix patterns are registered
|
|
29
|
+
# in `OMNIAUTH_SUFFIXES` and consulted by `HelperTable#omniauth_match?`.
|
|
44
30
|
module DeviseRoutes
|
|
45
|
-
# The standard Devise controllers and the helper
|
|
46
|
-
#
|
|
47
|
-
# name
|
|
48
|
-
#
|
|
49
|
-
# the part BEFORE the resource-name segment (Rails
|
|
50
|
-
# produces `<prefix>_<resource>_<suffix>_path`; for
|
|
51
|
-
# the bare `<resource>_session_path` shape the prefix
|
|
52
|
-
# is empty).
|
|
31
|
+
# The standard Devise controllers and the helper actions each generates. Keys are the controller
|
|
32
|
+
# name; values are arrays of `[helper_prefix, http_method, action]` triples — `helper_prefix` is
|
|
33
|
+
# the part BEFORE the resource-name segment (Rails produces `<prefix>_<resource>_<suffix>_path`;
|
|
34
|
+
# for the bare `<resource>_session_path` shape the prefix is empty).
|
|
53
35
|
CONTROLLER_HELPERS = {
|
|
54
36
|
sessions: [
|
|
55
37
|
["new", "session", :get, :new],
|
|
@@ -77,13 +59,10 @@ module Rigor
|
|
|
77
59
|
]
|
|
78
60
|
}.freeze
|
|
79
61
|
|
|
80
|
-
# Dynamic-provider helper patterns. `_omniauth_authorize_path`
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
# `<resource>_<provider>_omniauth_(authorize|callback)_(path|url)`
|
|
85
|
-
# call as recognised when the resource is one the
|
|
86
|
-
# project declared via `devise_for`.
|
|
62
|
+
# Dynamic-provider helper patterns. `_omniauth_authorize_path` and `_omniauth_callback_path` are
|
|
63
|
+
# generated per configured provider (Facebook, GitHub, …) by an initializer this parser does not
|
|
64
|
+
# read. We treat any `<resource>_<provider>_omniauth_(authorize|callback)_(path|url)` call as
|
|
65
|
+
# recognised when the resource is one the project declared via `devise_for`.
|
|
87
66
|
OMNIAUTH_SUFFIXES = %w[
|
|
88
67
|
_omniauth_authorize_path _omniauth_authorize_url
|
|
89
68
|
_omniauth_callback_path _omniauth_callback_url
|
|
@@ -91,16 +70,12 @@ module Rigor
|
|
|
91
70
|
|
|
92
71
|
module_function
|
|
93
72
|
|
|
94
|
-
# @param resource [String, Symbol] the `devise_for`
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
# @
|
|
99
|
-
#
|
|
100
|
-
# @return [Array<HelperTable::Entry>] one entry per
|
|
101
|
-
# generated `_path` helper. The caller is expected to
|
|
102
|
-
# pair `_url` variants the same way `RoutesParser` does
|
|
103
|
-
# for other entries.
|
|
73
|
+
# @param resource [String, Symbol] the `devise_for` argument — typically `:users`. Used as both the
|
|
74
|
+
# helper-name segment (`new_user_session_path`) and to derive the resource path.
|
|
75
|
+
# @param skip [Array<Symbol>] controllers the project disables via `devise_for :users, skip:
|
|
76
|
+
# [:registrations]`.
|
|
77
|
+
# @return [Array<HelperTable::Entry>] one entry per generated `_path` helper. The caller is
|
|
78
|
+
# expected to pair `_url` variants the same way `RoutesParser` does for other entries.
|
|
104
79
|
def generate(resource:, skip: [])
|
|
105
80
|
resource_segment = singularize(resource.to_s)
|
|
106
81
|
skip_set = skip.to_set(&:to_sym)
|
|
@@ -126,21 +101,15 @@ module Rigor
|
|
|
126
101
|
end
|
|
127
102
|
end
|
|
128
103
|
|
|
129
|
-
# Devise also ships *scoped* helpers
|
|
130
|
-
# (`
|
|
131
|
-
# resource segment and take the scope as a positional
|
|
132
|
-
# argument: `new_password_path(resource_name)` is the
|
|
104
|
+
# Devise also ships *scoped* helpers (`Devise::Controllers::UrlHelpers`) that DROP the resource
|
|
105
|
+
# segment and take the scope as a positional argument: `new_password_path(resource_name)` is the
|
|
133
106
|
# bare form of `new_user_password_path`. Used inside
|
|
134
|
-
# `Auth::PasswordsController < Devise::PasswordsController`
|
|
135
|
-
#
|
|
136
|
-
# trailing options-hash bumps it via the existing
|
|
137
|
-
# `arity + 1` rule.
|
|
107
|
+
# `Auth::PasswordsController < Devise::PasswordsController` (Mastodon's idiom). Arity 1 (the
|
|
108
|
+
# scope), and a trailing options-hash bumps it via the existing `arity + 1` rule.
|
|
138
109
|
entries.concat(scoped_helpers(skip_set))
|
|
139
110
|
|
|
140
|
-
# `omniauth_authorize_path(scope, provider)` and
|
|
141
|
-
# `
|
|
142
|
-
# `Devise::Controllers::UrlHelpers` scoped helpers
|
|
143
|
-
# that delegate to the OmniAuth gem. Arity 2.
|
|
111
|
+
# `omniauth_authorize_path(scope, provider)` and `omniauth_callback_path(scope, provider)` are
|
|
112
|
+
# `Devise::Controllers::UrlHelpers` scoped helpers that delegate to the OmniAuth gem. Arity 2.
|
|
144
113
|
unless skip_set.include?(:omniauth_callbacks)
|
|
145
114
|
%w[omniauth_authorize_path omniauth_callback_path].each do |name|
|
|
146
115
|
entries << HelperTable::Entry.new(
|
|
@@ -156,11 +125,9 @@ module Rigor
|
|
|
156
125
|
entries
|
|
157
126
|
end
|
|
158
127
|
|
|
159
|
-
# Scoped helpers Devise exposes inside its controllers.
|
|
160
|
-
#
|
|
161
|
-
# scope
|
|
162
|
-
# Names mirror the qualified `<scope>_<name>_path`
|
|
163
|
-
# entries above, minus the scope segment.
|
|
128
|
+
# Scoped helpers Devise exposes inside its controllers. The arity-1 family — caller supplies the
|
|
129
|
+
# resource scope (`:user` etc.) as the first positional arg. Names mirror the qualified
|
|
130
|
+
# `<scope>_<name>_path` entries above, minus the scope segment.
|
|
164
131
|
SCOPED_HELPERS = {
|
|
165
132
|
sessions: %w[new_session_path session_path destroy_session_path],
|
|
166
133
|
passwords: %w[new_password_path edit_password_path password_path],
|
|
@@ -186,28 +153,22 @@ module Rigor
|
|
|
186
153
|
end
|
|
187
154
|
end
|
|
188
155
|
|
|
189
|
-
# Returns the set of OmniAuth pattern suffixes the
|
|
190
|
-
# analyzer
|
|
191
|
-
#
|
|
192
|
-
# when a `*_path` / `*_url` call's name does not match
|
|
193
|
-
# any registered entry and its prefix matches a Devise
|
|
194
|
-
# resource.
|
|
156
|
+
# Returns the set of OmniAuth pattern suffixes the analyzer accepts for a given resource. The
|
|
157
|
+
# analyzer consults this set (via `HelperTable#omniauth_match?`) when a `*_path` / `*_url` call's
|
|
158
|
+
# name does not match any registered entry and its prefix matches a Devise resource.
|
|
195
159
|
def omniauth_suffixes
|
|
196
160
|
OMNIAUTH_SUFFIXES
|
|
197
161
|
end
|
|
198
162
|
|
|
199
|
-
# ADR-39 — delegates to the shared inflector (real
|
|
200
|
-
#
|
|
201
|
-
# AS-replica this module used to duplicate from `RoutesParser`.
|
|
163
|
+
# ADR-39 — delegates to the shared inflector (real `ActiveSupport::Inflector` when available),
|
|
164
|
+
# replacing the tiny AS-replica this module used to duplicate from `RoutesParser`.
|
|
202
165
|
def singularize(word)
|
|
203
166
|
Rigor::Plugin::Inflector.singularize(word)
|
|
204
167
|
end
|
|
205
168
|
|
|
206
|
-
# Approximate path generation. We don't honour the
|
|
207
|
-
# `path
|
|
208
|
-
#
|
|
209
|
-
# informational and surfaces only in the `:helper` info
|
|
210
|
-
# diagnostic.
|
|
169
|
+
# Approximate path generation. We don't honour the `path:` Devise option here because the helper
|
|
170
|
+
# NAME is what the analyzer cares about; the `path` field is informational and surfaces only in
|
|
171
|
+
# the `:helper` info diagnostic.
|
|
211
172
|
def devise_path_for(resource_segment, controller, action)
|
|
212
173
|
plural = "#{resource_segment}s"
|
|
213
174
|
base = "/#{plural}"
|
|
@@ -3,39 +3,27 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Plugin
|
|
5
5
|
class RailsRoutes < Rigor::Plugin::Base
|
|
6
|
-
# Generates the catalogue of OAuth route helpers a
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# [Routing](https://github.com/doorkeeper-gem/doorkeeper/wiki/Customizing-routes)
|
|
11
|
-
# documentation; this module mirrors the canonical
|
|
12
|
-
# helper names.
|
|
6
|
+
# Generates the catalogue of OAuth route helpers a `use_doorkeeper do ... end` block adds to a Rails
|
|
7
|
+
# app's routing table. The set is the Doorkeeper gem's documented default — see Doorkeeper's
|
|
8
|
+
# [Routing](https://github.com/doorkeeper-gem/doorkeeper/wiki/Customizing-routes) documentation; this
|
|
9
|
+
# module mirrors the canonical helper names.
|
|
13
10
|
#
|
|
14
|
-
# Used by `RoutesParser` when it sees a `use_doorkeeper`
|
|
15
|
-
#
|
|
16
|
-
# `
|
|
17
|
-
#
|
|
18
|
-
# `oauth_application_path(:id)` / `oauth_authorized_application_path(:id)`
|
|
19
|
-
# forms is 1 (the `:id`); all other Doorkeeper helpers
|
|
20
|
-
# are arity 0.
|
|
11
|
+
# Used by `RoutesParser` when it sees a `use_doorkeeper` call. Each generated helper is materialised
|
|
12
|
+
# as a `HelperTable::Entry` so the analyzer's `unknown-helper` rule recognises them. Arity for the
|
|
13
|
+
# `oauth_application_path(:id)` / `oauth_authorized_application_path(:id)` forms is 1 (the `:id`);
|
|
14
|
+
# all other Doorkeeper helpers are arity 0.
|
|
21
15
|
#
|
|
22
16
|
# Scope:
|
|
23
17
|
#
|
|
24
|
-
# - Recognises the standard helper set: authorization,
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# -
|
|
28
|
-
#
|
|
29
|
-
# - Does NOT yet honour `
|
|
30
|
-
# those change WHICH class serves the route but not
|
|
31
|
-
# the helper NAMES, so omitting them is sound.
|
|
32
|
-
# - Does NOT yet honour custom `as:` / `at:` on
|
|
33
|
-
# `use_doorkeeper` itself.
|
|
18
|
+
# - Recognises the standard helper set: authorization, token, revoke, introspect, token_info,
|
|
19
|
+
# userinfo, applications (resources), authorized_applications.
|
|
20
|
+
# - Honours `skip_controllers <names>` inside the block to omit the named controller's helpers.
|
|
21
|
+
# - Does NOT yet honour `controllers ...:` remappings — those change WHICH class serves the route but
|
|
22
|
+
# not the helper NAMES, so omitting them is sound.
|
|
23
|
+
# - Does NOT yet honour custom `as:` / `at:` on `use_doorkeeper` itself.
|
|
34
24
|
module DoorkeeperRoutes
|
|
35
|
-
# Per-controller helper catalogue. Keys are the
|
|
36
|
-
# `
|
|
37
|
-
# entries (name + arity + path + http_method).
|
|
38
|
-
# Both `_path` and `_url` variants are paired by
|
|
25
|
+
# Per-controller helper catalogue. Keys are the `skip_controllers :<key>` names; values are the
|
|
26
|
+
# entries (name + arity + path + http_method). Both `_path` and `_url` variants are paired by
|
|
39
27
|
# `RoutesParser` after registration.
|
|
40
28
|
CONTROLLER_HELPERS = {
|
|
41
29
|
authorizations: [
|
|
@@ -78,8 +66,7 @@ module Rigor
|
|
|
78
66
|
|
|
79
67
|
module_function
|
|
80
68
|
|
|
81
|
-
# @param skip [Array<Symbol>] controller names the
|
|
82
|
-
# project omits via `skip_controllers :name, ...`.
|
|
69
|
+
# @param skip [Array<Symbol>] controller names the project omits via `skip_controllers :name, ...`.
|
|
83
70
|
# @return [Array<HelperTable::Entry>] flattened entries.
|
|
84
71
|
def generate(skip: [])
|
|
85
72
|
skip_set = skip.to_set(&:to_sym)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Plugin
|
|
7
|
+
class RailsRoutes < Rigor::Plugin::Base
|
|
8
|
+
# Recognises the helper *namespaces* the `grape-path-helpers` gem generates, so a call to one of them
|
|
9
|
+
# does not false-fire `unknown-helper`.
|
|
10
|
+
#
|
|
11
|
+
# The gem names each helper after its route's path segments, joined with `_`
|
|
12
|
+
# (`DecoratedRoute#path_helper_name`): `/api/:version/groups/:id/badges` under `version 'v4'` becomes
|
|
13
|
+
# `api_v4_groups_badges_path`. It walks `Grape::API::Instance.routes` — the *runtime* route table — and
|
|
14
|
+
# real grape sources build that table with metaprogramming (GitLab's `%w[group project].each { |t|
|
|
15
|
+
# resource t.pluralize … }`). A static parser cannot enumerate those names, and deriving only the
|
|
16
|
+
# easy ones would be worse than deriving none: every route it missed would keep firing on working
|
|
17
|
+
# code.
|
|
18
|
+
#
|
|
19
|
+
# What IS static is the leading segments. `prefix :api` contributes the first; `version 'v4'`
|
|
20
|
+
# contributes the second when the strategy is `:path` (Grape's default — `using: :header` / `:param`
|
|
21
|
+
# keeps the version out of the URL, and so out of the helper name). Both are literal arguments in the
|
|
22
|
+
# body of a class whose superclass chain reaches `Grape::API`. Everything after them is opaque.
|
|
23
|
+
#
|
|
24
|
+
# So the namespace `api_v4_…_path` is treated as open — a name the project's routes may define but
|
|
25
|
+
# Rigor cannot enumerate, so proving it undefined is unsound (the ADR-26 `open_receivers` reasoning,
|
|
26
|
+
# already applied in this plugin to Devise's runtime-provider OmniAuth family). Teeth survive where the
|
|
27
|
+
# gem's own contract keeps them sound: it defines no `_url` helper
|
|
28
|
+
# (`NamedRouteMatcher#method_missing` returns `super` unless the name ends `_path`), so
|
|
29
|
+
# `api_v4_anything_url` still fires, as does any name outside a declared prefix.
|
|
30
|
+
#
|
|
31
|
+
# Design note: `docs/notes/20260710-grape-path-helper-namespace.md`.
|
|
32
|
+
module GrapeApiDiscoverer
|
|
33
|
+
# A class is a grape API when its superclass chain reaches one of these. `Grape::API::Instance` is
|
|
34
|
+
# what a mounted `Grape::API` subclass actually becomes, and real code inherits from it directly
|
|
35
|
+
# (GitLab's `API::Base < Grape::API::Instance`).
|
|
36
|
+
GRAPE_BASE_NAMES = ["Grape::API", "Grape::API::Instance"].freeze
|
|
37
|
+
|
|
38
|
+
# `version 'v4', using: :path` puts the version in the URL. `:header` / `:param` do not, so those
|
|
39
|
+
# versions contribute no helper-name segment. Grape's default strategy is `:path`.
|
|
40
|
+
PATH_VERSION_STRATEGY = :path
|
|
41
|
+
|
|
42
|
+
module_function
|
|
43
|
+
|
|
44
|
+
# @param contents_per_path [Hash{String => String}] file path → source text, read by the caller
|
|
45
|
+
# (through the trusted `IoBoundary`, so cache invalidation works).
|
|
46
|
+
# @return [Array<String>] the recognised helper-name prefixes (`["api_v3", "api_v4"]`). Empty when
|
|
47
|
+
# the project declares no grape API — nothing changes for such a project.
|
|
48
|
+
def discover(contents_per_path)
|
|
49
|
+
declarations = {}
|
|
50
|
+
superclasses = {}
|
|
51
|
+
contents_per_path.each_value do |contents|
|
|
52
|
+
collect_from_contents(contents, declarations, superclasses)
|
|
53
|
+
rescue StandardError
|
|
54
|
+
# Best-effort: a parse failure in one grape file must not abort discovery for the rest. The core
|
|
55
|
+
# pipeline surfaces the parse error separately.
|
|
56
|
+
next
|
|
57
|
+
end
|
|
58
|
+
prefixes_for(declarations, superclasses)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Composes `prefix` × `version` into helper-name prefixes, for grape classes only. A class with a
|
|
62
|
+
# prefix and no path-strategy version contributes the bare prefix; one with versions and no prefix
|
|
63
|
+
# contributes each version alone.
|
|
64
|
+
def prefixes_for(declarations, superclasses)
|
|
65
|
+
declarations.filter_map do |class_name, declaration|
|
|
66
|
+
next unless grape_api?(class_name, superclasses)
|
|
67
|
+
|
|
68
|
+
segments = declaration[:prefix]
|
|
69
|
+
versions = declaration[:versions]
|
|
70
|
+
next segments.join("_") if versions.empty? && !segments.empty?
|
|
71
|
+
|
|
72
|
+
versions.map { |version| (segments + [version]).join("_") }
|
|
73
|
+
end.flatten.reject(&:empty?).uniq
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Follows the recorded superclass chain (cycle-guarded) looking for a grape base. The chain lives
|
|
77
|
+
# entirely inside the scanned files, so an unresolvable parent simply ends the walk.
|
|
78
|
+
def grape_api?(class_name, superclasses)
|
|
79
|
+
seen = {}
|
|
80
|
+
current = class_name
|
|
81
|
+
while current && !seen[current]
|
|
82
|
+
seen[current] = true
|
|
83
|
+
parent = superclasses[current]
|
|
84
|
+
return true if parent && GRAPE_BASE_NAMES.include?(parent)
|
|
85
|
+
|
|
86
|
+
current = parent
|
|
87
|
+
end
|
|
88
|
+
false
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def collect_from_contents(contents, declarations, superclasses)
|
|
92
|
+
root = Prism.parse(contents).value
|
|
93
|
+
walk(root, [], declarations, superclasses)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def walk(node, prefix_path, declarations, superclasses)
|
|
97
|
+
return unless node.is_a?(Prism::Node)
|
|
98
|
+
|
|
99
|
+
if node.is_a?(Prism::ClassNode) || node.is_a?(Prism::ModuleNode)
|
|
100
|
+
name = constant_name(node.constant_path)
|
|
101
|
+
if name
|
|
102
|
+
qualified = (prefix_path + [name]).join("::")
|
|
103
|
+
record_class(node, qualified, declarations, superclasses) if node.is_a?(Prism::ClassNode)
|
|
104
|
+
walk(node.body, prefix_path + [name], declarations, superclasses) if node.body
|
|
105
|
+
return
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
node.compact_child_nodes.each { |child| walk(child, prefix_path, declarations, superclasses) }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def record_class(node, qualified, declarations, superclasses)
|
|
113
|
+
parent = constant_name(node.superclass)
|
|
114
|
+
superclasses[qualified] = parent if parent
|
|
115
|
+
declaration = { prefix: [], versions: [] }
|
|
116
|
+
statements_of(node.body).each { |statement| absorb_declaration(statement, declaration) }
|
|
117
|
+
declarations[qualified] = declaration unless declaration[:prefix].empty? && declaration[:versions].empty?
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# `prefix :api` / `prefix 'api/v2'` (a multi-segment prefix splits on `/`), and `version 'v4'` /
|
|
121
|
+
# `version 'v4', using: :path` — both in block and non-block form.
|
|
122
|
+
def absorb_declaration(node, declaration)
|
|
123
|
+
return unless node.is_a?(Prism::CallNode) && node.receiver.nil?
|
|
124
|
+
|
|
125
|
+
case node.name
|
|
126
|
+
when :prefix
|
|
127
|
+
value = literal_value(first_argument(node))
|
|
128
|
+
declaration[:prefix] = value.split("/").reject(&:empty?) if value
|
|
129
|
+
when :version
|
|
130
|
+
value = literal_value(first_argument(node))
|
|
131
|
+
declaration[:versions] << value if value && path_strategy?(node)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# True unless an explicit `using:` names a non-path strategy. Grape defaults to `:path`.
|
|
136
|
+
def path_strategy?(node)
|
|
137
|
+
arguments = node.arguments&.arguments || []
|
|
138
|
+
keywords = arguments.find { |argument| argument.is_a?(Prism::KeywordHashNode) }
|
|
139
|
+
return true if keywords.nil?
|
|
140
|
+
|
|
141
|
+
using = keywords.elements.find do |element|
|
|
142
|
+
element.is_a?(Prism::AssocNode) && literal_value(element.key) == "using"
|
|
143
|
+
end
|
|
144
|
+
return true if using.nil?
|
|
145
|
+
|
|
146
|
+
literal_value(using.value) == PATH_VERSION_STRATEGY.to_s
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def first_argument(node)
|
|
150
|
+
node.arguments&.arguments&.first
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Symbol and String literals only. A computed prefix / version is not a declaration we can ground a
|
|
154
|
+
# namespace in, so it contributes nothing.
|
|
155
|
+
def literal_value(node)
|
|
156
|
+
case node
|
|
157
|
+
when Prism::SymbolNode, Prism::StringNode then node.unescaped
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def constant_name(node)
|
|
162
|
+
case node
|
|
163
|
+
when Prism::ConstantReadNode then node.name.to_s
|
|
164
|
+
when Prism::ConstantPathNode then constant_path_name(node)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def constant_path_name(node)
|
|
169
|
+
parent = node.parent
|
|
170
|
+
own = node.name&.to_s
|
|
171
|
+
return nil unless own
|
|
172
|
+
return own if parent.nil? # `::Foo` — a cbase root
|
|
173
|
+
|
|
174
|
+
parent_name = constant_name(parent)
|
|
175
|
+
parent_name ? "#{parent_name}::#{own}" : own
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def statements_of(body)
|
|
179
|
+
case body
|
|
180
|
+
when Prism::StatementsNode then body.body
|
|
181
|
+
when Prism::BeginNode then statements_of(body.statements)
|
|
182
|
+
when nil then []
|
|
183
|
+
else [body]
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -5,76 +5,54 @@ require "prism"
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Plugin
|
|
7
7
|
class RailsRoutes < Rigor::Plugin::Base
|
|
8
|
-
# Walks `app/helpers/**/*.rb` (or any configured set of
|
|
9
|
-
# helper
|
|
10
|
-
#
|
|
11
|
-
# `
|
|
12
|
-
# project-defined `*_path` / `*_url` helpers (the
|
|
13
|
-
# canonical Rails pattern: a `UrlHelper` module exposing
|
|
14
|
-
# `full_asset_url`, `host_to_url`, `frontend_asset_url`,
|
|
15
|
-
# and so on).
|
|
8
|
+
# Walks `app/helpers/**/*.rb` (or any configured set of helper directories) and extracts every public
|
|
9
|
+
# module-level method name so the analyzer's `unknown-helper` rule does not false-fire on calls to
|
|
10
|
+
# project-defined `*_path` / `*_url` helpers (the canonical Rails pattern: a `UrlHelper` module
|
|
11
|
+
# exposing `full_asset_url`, `host_to_url`, `frontend_asset_url`, and so on).
|
|
16
12
|
#
|
|
17
13
|
# The discoverer is deliberately conservative:
|
|
18
14
|
#
|
|
19
|
-
# - Walks `def name(...)` declarations at the module /
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# `
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# where the receiver genuinely cannot see the method.
|
|
35
|
-
# - Ignores `def self.x` (singleton-method definitions) —
|
|
36
|
-
# Rails helpers are instance methods on the helper
|
|
37
|
-
# module; class-side `self.x` shapes are usually
|
|
38
|
-
# internal utilities, not view helpers.
|
|
39
|
-
# - Filters the resulting set to names ending in `_path`
|
|
40
|
-
# or `_url` (the dispatch surface the analyzer's rule
|
|
41
|
-
# actually cares about). A helper whose name does not
|
|
42
|
-
# end with either suffix is irrelevant to the rule's
|
|
43
|
-
# false-positive surface, so excluding it costs nothing
|
|
44
|
-
# and keeps the registered set focused.
|
|
15
|
+
# - Walks `def name(...)` declarations at the module / class top level. Nested `def` inside a method
|
|
16
|
+
# body or a `define_method` macro is NOT extracted (the same reason `rigor-actionpack` is not the
|
|
17
|
+
# canonical custom-helper source — metaprogrammed helpers are out of scope for a static scan).
|
|
18
|
+
# - **Visibility-agnostic.** A `private` / `protected` `def some_url` IS registered. The
|
|
19
|
+
# `unknown-helper` rule is a project-wide name-presence check, not a visibility check — if the user
|
|
20
|
+
# wrote a `_path` / `_url` method anywhere in `app/`, they very likely intend to call it (often
|
|
21
|
+
# Mastodon's pattern: a controller's `private def page_url(page)` invoked by a `link_to` in its own
|
|
22
|
+
# view). The core engine's `call.undefined-method` rule still catches the case where the receiver
|
|
23
|
+
# genuinely cannot see the method.
|
|
24
|
+
# - Ignores `def self.x` (singleton-method definitions) — Rails helpers are instance methods on the
|
|
25
|
+
# helper module; class-side `self.x` shapes are usually internal utilities, not view helpers.
|
|
26
|
+
# - Filters the resulting set to names ending in `_path` or `_url` (the dispatch surface the
|
|
27
|
+
# analyzer's rule actually cares about). A helper whose name does not end with either suffix is
|
|
28
|
+
# irrelevant to the rule's false-positive surface, so excluding it costs nothing and keeps the
|
|
29
|
+
# registered set focused.
|
|
45
30
|
#
|
|
46
31
|
# Out of scope (intentional):
|
|
47
32
|
#
|
|
48
|
-
# - Helpers defined via `define_method` or via macros like
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
# -
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
# companion `DeviseRoutes` generator; other gem-injected
|
|
55
|
-
# helpers fall to ADR-25 plugin-contributed RBS or the
|
|
56
|
-
# ADR-10 `dependencies.source_inference:` path.
|
|
33
|
+
# - Helpers defined via `define_method` or via macros like `inheritance_traversed_helpers`. These
|
|
34
|
+
# would need ADR-16 macro substrate or per-plugin custom recognizers.
|
|
35
|
+
# - Helpers inherited from gems (`Devise::Controllers::Helpers`, `ViteRails::TagHelpers`, etc.).
|
|
36
|
+
# Those need their own handling — Devise auto-routes are covered by the companion `DeviseRoutes`
|
|
37
|
+
# generator; other gem-injected helpers fall to ADR-25 plugin-contributed RBS or the ADR-10
|
|
38
|
+
# `dependencies.source_inference:` path.
|
|
57
39
|
module HelperDiscoverer
|
|
58
40
|
HELPER_SUFFIXES = [/_path\z/, /_url\z/].freeze
|
|
59
41
|
|
|
60
42
|
module_function
|
|
61
43
|
|
|
62
|
-
# @param contents_per_path [Hash{String => String}]
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
# @return [Set<String>] method names suitable for
|
|
67
|
-
# inclusion in the `HelperTable`'s custom-helper set.
|
|
44
|
+
# @param contents_per_path [Hash{String => String}] file path → source text. The caller is
|
|
45
|
+
# responsible for reading files (typically through the trusted `IoBoundary` so cache invalidation
|
|
46
|
+
# works).
|
|
47
|
+
# @return [Set<String>] method names suitable for inclusion in the `HelperTable`'s custom-helper set.
|
|
68
48
|
def discover(contents_per_path)
|
|
69
49
|
names = []
|
|
70
50
|
contents_per_path.each_value do |contents|
|
|
71
51
|
names.concat(extract_from_contents(contents))
|
|
72
52
|
rescue StandardError
|
|
73
|
-
# Skip any file whose AST walk explodes — discovery
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
# rules will still surface the parse failure on the
|
|
77
|
-
# affected file through the core pipeline.
|
|
53
|
+
# Skip any file whose AST walk explodes — discovery is best-effort and a parse failure in one
|
|
54
|
+
# helper file MUST NOT abort discovery for the rest. Other rules will still surface the parse
|
|
55
|
+
# failure on the affected file through the core pipeline.
|
|
78
56
|
next
|
|
79
57
|
end
|
|
80
58
|
names.to_set
|
|
@@ -89,17 +67,9 @@ module Rigor
|
|
|
89
67
|
walker.helper_names
|
|
90
68
|
end
|
|
91
69
|
|
|
92
|
-
# Per-file AST walker.
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
# `private` is excluded.
|
|
96
|
-
#
|
|
97
|
-
# The walker is recursive into module / class bodies so
|
|
98
|
-
# `module UrlHelpers; module Routing; def foo_url; end;
|
|
99
|
-
# end; end` registers `foo_url`. It does NOT recurse
|
|
100
|
-
# into method bodies (a `def inside def` would be
|
|
101
|
-
# `define_method`-shaped, which we skip per the
|
|
102
|
-
# docstring).
|
|
70
|
+
# Per-file AST walker. The walker is recursive into module / class bodies so `module UrlHelpers;
|
|
71
|
+
# module Routing; def foo_url; end; end; end` registers `foo_url`. It does NOT recurse into method
|
|
72
|
+
# bodies (a `def inside def` would be `define_method`-shaped, which we skip per the docstring).
|
|
103
73
|
class ModuleBodyWalker
|
|
104
74
|
attr_reader :helper_names
|
|
105
75
|
|
|
@@ -134,11 +104,9 @@ module Rigor
|
|
|
134
104
|
def visit_statements(body)
|
|
135
105
|
return if body.nil?
|
|
136
106
|
|
|
137
|
-
# Visibility tracking is intentionally absent — see
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
# a controller IS registered so a caller in the
|
|
141
|
-
# paired view does not false-fire `unknown-helper`.
|
|
107
|
+
# Visibility tracking is intentionally absent — see the "visibility-agnostic" point in the
|
|
108
|
+
# module docstring. A `private def page_url(page)` inside a controller IS registered so a
|
|
109
|
+
# caller in the paired view does not false-fire `unknown-helper`.
|
|
142
110
|
statements_of(body).each do |stmt|
|
|
143
111
|
case stmt
|
|
144
112
|
when Prism::DefNode
|