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,17 +8,13 @@ require_relative "factorybot/factory_index"
|
|
|
8
8
|
|
|
9
9
|
module Rigor
|
|
10
10
|
module Plugin
|
|
11
|
-
# rigor-factorybot — validates `FactoryBot.create(:name,
|
|
12
|
-
#
|
|
13
|
-
# attributes_for / *_list family against a per-run index
|
|
14
|
-
# built from `factory_search_paths`.
|
|
11
|
+
# rigor-factorybot — validates `FactoryBot.create(:name, key: ...)` and the build / build_stubbed /
|
|
12
|
+
# attributes_for / *_list family against a per-run index built from `factory_search_paths`.
|
|
15
13
|
#
|
|
16
|
-
# Recognises factory NAMES + literal ATTRIBUTE KEYS in
|
|
17
|
-
# the
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# Traits, sequences, parent / child factories, and dynamic
|
|
21
|
-
# factory names are deferred to follow-up slices.
|
|
14
|
+
# Recognises factory NAMES + literal ATTRIBUTE KEYS in the call's keyword hash. The AR column
|
|
15
|
+
# cross-check uses the `rigor-activerecord` `:model_index` ADR-9 fact when that plugin is loaded
|
|
16
|
+
# (optional `consumes:`). Traits, sequences, parent / child factories, and dynamic factory names are
|
|
17
|
+
# deferred to follow-up slices.
|
|
22
18
|
#
|
|
23
19
|
# ## Configuration
|
|
24
20
|
#
|
|
@@ -33,43 +29,32 @@ module Rigor
|
|
|
33
29
|
#
|
|
34
30
|
# ## What it checks
|
|
35
31
|
#
|
|
36
|
-
# - **Factory existence** — every entry call's first
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
# with a `DidYouMean`
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
# declared attribute
|
|
43
|
-
# `unknown-attribute` with a `DidYouMean` suggestion.
|
|
44
|
-
# - **Trace** — recognised entry calls also emit a
|
|
45
|
-
# `factory-call` info diagnostic listing the factory's
|
|
46
|
-
# declared attribute set.
|
|
32
|
+
# - **Factory existence** — every entry call's first positional Symbol / String literal is looked up
|
|
33
|
+
# in the index. Missing factories emit `unknown-factory` with a `DidYouMean` suggestion.
|
|
34
|
+
# - **Attribute key existence** — every literal-Symbol keyword-argument key is matched against the
|
|
35
|
+
# factory's declared attribute names. Missing keys emit `unknown-attribute` with a `DidYouMean`
|
|
36
|
+
# suggestion.
|
|
37
|
+
# - **Trace** — recognised entry calls also emit a `factory-call` info diagnostic listing the
|
|
38
|
+
# factory's declared attribute set.
|
|
47
39
|
#
|
|
48
40
|
# ## Recognised entry methods
|
|
49
41
|
#
|
|
50
|
-
# `FactoryBot.create`, `.build`, `.build_stubbed`,
|
|
51
|
-
# `.
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
# context) are NOT recognised — too many false positives on
|
|
56
|
-
# plain `create` calls outside test files; deferred until
|
|
57
|
-
# receiver-type inference can disambiguate.
|
|
42
|
+
# `FactoryBot.create`, `.build`, `.build_stubbed`, `.attributes_for`, `.create_list`, `.build_list`,
|
|
43
|
+
# `.build_stubbed_list`. The legacy `FactoryGirl` constant is recognised identically. Implicit-receiver
|
|
44
|
+
# calls (`create(:name)` inside an `include FactoryBot::Syntax::Methods` context) are NOT recognised —
|
|
45
|
+
# too many false positives on plain `create` calls outside test files; deferred until receiver-type
|
|
46
|
+
# inference can disambiguate.
|
|
58
47
|
#
|
|
59
48
|
# ## What's recognised inside `factory :name do ... end`
|
|
60
49
|
#
|
|
61
|
-
# - `name { "Alice" }` — implicit attribute via
|
|
62
|
-
#
|
|
63
|
-
# - `name "Alice"` — implicit attribute with a positional
|
|
64
|
-
# argument (legacy syntax).
|
|
50
|
+
# - `name { "Alice" }` — implicit attribute via `method_missing` with a block (modern syntax).
|
|
51
|
+
# - `name "Alice"` — implicit attribute with a positional argument (legacy syntax).
|
|
65
52
|
# - `add_attribute(:name) { "Alice" }` — explicit form.
|
|
66
53
|
#
|
|
67
|
-
# Sequences (`sequence(:email) { ... }`), associations
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
# slices. Factories whose name is a non-literal expression
|
|
72
|
-
# (`factory FACTORY_NAME do ... end`) are silently skipped.
|
|
54
|
+
# Sequences (`sequence(:email) { ... }`), associations (`association :author`), traits (`trait :admin
|
|
55
|
+
# do ... end`), and parent / child relationships (`factory :admin, parent: :user do ... end`) are
|
|
56
|
+
# deferred to follow-up slices. Factories whose name is a non-literal expression (`factory
|
|
57
|
+
# FACTORY_NAME do ... end`) are silently skipped.
|
|
73
58
|
class Factorybot < Rigor::Plugin::Base
|
|
74
59
|
manifest(
|
|
75
60
|
id: "factorybot",
|
|
@@ -96,18 +81,16 @@ module Rigor
|
|
|
96
81
|
@factory_search_paths = Array(config.fetch("factory_search_paths")).map(&:to_s)
|
|
97
82
|
end
|
|
98
83
|
|
|
99
|
-
# ADR-37 — per-call factory/attribute validation over the
|
|
100
|
-
#
|
|
101
|
-
# (
|
|
102
|
-
#
|
|
103
|
-
# diagnostic remains, so there is no `diagnostics_for_file`.
|
|
84
|
+
# ADR-37 — per-call factory/attribute validation over the engine-owned walk. Each violation carries
|
|
85
|
+
# its own location (the call's message_loc, or the offending attribute key), so it is positioned
|
|
86
|
+
# via `diagnostic(node, location:)`. No file-level diagnostic remains, so there is no
|
|
87
|
+
# `diagnostics_for_file`.
|
|
104
88
|
node_rule Prism::CallNode do |node, _scope, path|
|
|
105
89
|
index = producer_value(:factory_index)
|
|
106
90
|
next [] if index.nil? || index.empty?
|
|
107
91
|
|
|
108
|
-
# `:model_index` is rigor-activerecord's published fact (ADR-9);
|
|
109
|
-
#
|
|
110
|
-
# falls back to factory-attributes-only checking.
|
|
92
|
+
# `:model_index` is rigor-activerecord's published fact (ADR-9); nil when that plugin isn't
|
|
93
|
+
# loaded, in which case the analyzer falls back to factory-attributes-only checking.
|
|
111
94
|
violations = Analyzer.violations_for(
|
|
112
95
|
call_node: node, factory_index: index,
|
|
113
96
|
model_index: read_fact(plugin_id: "activerecord", name: :model_index)
|
|
@@ -6,17 +6,13 @@ require "rigor/source/literals"
|
|
|
6
6
|
module Rigor
|
|
7
7
|
module Plugin
|
|
8
8
|
class Graphql < Rigor::Plugin::Base
|
|
9
|
-
# Walks project source for `class T < GraphQL::Schema::Object`
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# inside the class body.
|
|
9
|
+
# Walks project source for `class T < GraphQL::Schema::Object` subclasses and emits a
|
|
10
|
+
# `{type_class_fqn => {field_name => {type:, nullable:}}}` table covering every `field :name, Type,
|
|
11
|
+
# null: ...` declaration inside the class body.
|
|
13
12
|
module TypeScanner
|
|
14
|
-
# The canonical GraphQL scalar type names accepted as
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# consumers can cross-reference against Ruby types
|
|
18
|
-
# without re-implementing the GraphQL→Ruby coercion
|
|
19
|
-
# table.
|
|
13
|
+
# The canonical GraphQL scalar type names accepted as `field`'s second positional argument. The
|
|
14
|
+
# plugin maps each to the underlying Ruby class name so downstream consumers can cross-reference
|
|
15
|
+
# against Ruby types without re-implementing the GraphQL→Ruby coercion table.
|
|
20
16
|
CANONICAL_TYPES = {
|
|
21
17
|
"String" => "String",
|
|
22
18
|
"Integer" => "Integer",
|
|
@@ -26,21 +22,17 @@ module Rigor
|
|
|
26
22
|
"ID" => "String"
|
|
27
23
|
}.freeze
|
|
28
24
|
|
|
29
|
-
# The base class name a Schema::Object subclass MUST
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# `BaseObject = GraphQL::Schema::Object` shape work when
|
|
34
|
-
# the alias's RHS is the canonical path.
|
|
25
|
+
# The base class name a Schema::Object subclass MUST inherit from to be recognised. Match is on
|
|
26
|
+
# the rightmost segment of the superclass constant chain so both `GraphQL::Schema::Object` and
|
|
27
|
+
# the locally-aliased `BaseObject = GraphQL::Schema::Object` shape work when the alias's RHS is
|
|
28
|
+
# the canonical path.
|
|
35
29
|
SCHEMA_OBJECT_TAIL = "Object"
|
|
36
30
|
SCHEMA_ENUM_TAIL = "Enum"
|
|
37
31
|
SCHEMA_INPUT_OBJECT_TAIL = "InputObject"
|
|
38
32
|
SCHEMA_MUTATION_TAIL = "Mutation"
|
|
39
|
-
# Common path-segment for `Schema::Object` / `Schema::Enum`
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
# fully-qualified `GraphQL::Schema::X` or lexically nested
|
|
43
|
-
# `Schema::X` inside `module GraphQL`).
|
|
33
|
+
# Common path-segment for `Schema::Object` / `Schema::Enum` / `Schema::InputObject` /
|
|
34
|
+
# `Schema::Mutation`; the second-to-last segment must be `Schema` (either fully-qualified
|
|
35
|
+
# `GraphQL::Schema::X` or lexically nested `Schema::X` inside `module GraphQL`).
|
|
44
36
|
SCHEMA_PARENT_SEGMENTS = %w[Schema GraphQL].freeze
|
|
45
37
|
private_constant :SCHEMA_OBJECT_TAIL, :SCHEMA_ENUM_TAIL,
|
|
46
38
|
:SCHEMA_INPUT_OBJECT_TAIL, :SCHEMA_MUTATION_TAIL,
|
|
@@ -48,15 +40,12 @@ module Rigor
|
|
|
48
40
|
|
|
49
41
|
module_function
|
|
50
42
|
|
|
51
|
-
# @param paths [Array<String>] absolute paths to `.rb` files
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
# `:mutations` (per-`Schema::Mutation` arguments+fields table).
|
|
58
|
-
# Any subset may be empty when no recognisable declaration
|
|
59
|
-
# of that kind is found.
|
|
43
|
+
# @param paths [Array<String>] absolute paths to `.rb` files the project's `paths:` resolves to.
|
|
44
|
+
# @return [Hash{Symbol => Hash}] frozen 4-key result: `:types` (per-`Schema::Object` field
|
|
45
|
+
# table), `:enums` (per-`Schema::Enum` value list), `:input_objects`
|
|
46
|
+
# (per-`Schema::InputObject` argument table), `:mutations` (per-`Schema::Mutation`
|
|
47
|
+
# arguments+fields table). Any subset may be empty when no recognisable declaration of that
|
|
48
|
+
# kind is found.
|
|
60
49
|
def scan(paths:)
|
|
61
50
|
acc = empty_accumulator
|
|
62
51
|
paths.each do |path|
|
|
@@ -97,10 +86,9 @@ module Rigor
|
|
|
97
86
|
end
|
|
98
87
|
private_class_method :scan_file
|
|
99
88
|
|
|
100
|
-
# Walks the AST collecting `class X < GraphQL::Schema::Object`,
|
|
101
|
-
# `Schema::
|
|
102
|
-
#
|
|
103
|
-
# caller can publish multiple cross-plugin facts from one walk.
|
|
89
|
+
# Walks the AST collecting `class X < GraphQL::Schema::Object`, `Schema::Enum`,
|
|
90
|
+
# `Schema::InputObject`, and `Schema::Mutation` decls at any nesting level. Returns a 4-key hash
|
|
91
|
+
# so the caller can publish multiple cross-plugin facts from one walk.
|
|
104
92
|
def collect_definitions(node, qualified_prefix)
|
|
105
93
|
return empty_accumulator if node.nil?
|
|
106
94
|
|
|
@@ -154,10 +142,9 @@ module Rigor
|
|
|
154
142
|
end
|
|
155
143
|
private_class_method :collect_module_node
|
|
156
144
|
|
|
157
|
-
# `class X < GraphQL::Schema::<Tail>` matches when the
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
# and `< Schema::<Tail>` (lexically inside `module GraphQL`).
|
|
145
|
+
# `class X < GraphQL::Schema::<Tail>` matches when the superclass's last two path segments are
|
|
146
|
+
# `Schema::<Tail>`. Matches both `< GraphQL::Schema::<Tail>` (fully qualified) and `<
|
|
147
|
+
# Schema::<Tail>` (lexically inside `module GraphQL`).
|
|
161
148
|
def schema_subclass?(class_node, tail)
|
|
162
149
|
superclass = class_node.superclass
|
|
163
150
|
return false if superclass.nil?
|
|
@@ -193,19 +180,16 @@ module Rigor
|
|
|
193
180
|
end
|
|
194
181
|
private_class_method :statement_nodes
|
|
195
182
|
|
|
196
|
-
# Walks every top-level `value "..."` call inside an
|
|
197
|
-
#
|
|
198
|
-
# Array<String>. Both shapes graphql-ruby accepts work:
|
|
183
|
+
# Walks every top-level `value "..."` call inside an enum subclass body and returns the value
|
|
184
|
+
# names as an Array<String>. Both shapes graphql-ruby accepts work:
|
|
199
185
|
#
|
|
200
186
|
# value "ACTIVE"
|
|
201
187
|
# value "DISABLED", value: :off, description: "..."
|
|
202
188
|
#
|
|
203
|
-
# The first positional must be a String literal — the
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
#
|
|
207
|
-
# `value:` kwarg (Ruby-side override) and `description:`
|
|
208
|
-
# are omitted from the published table.
|
|
189
|
+
# The first positional must be a String literal — the graphql-ruby `value` API also accepts a
|
|
190
|
+
# Symbol form (`value :ACTIVE`) but the documented idiom is String. Only the GraphQL-side value
|
|
191
|
+
# name is stored; the optional `value:` kwarg (Ruby-side override) and `description:` are
|
|
192
|
+
# omitted from the published table.
|
|
209
193
|
def collect_values(body)
|
|
210
194
|
return [] if body.nil?
|
|
211
195
|
|
|
@@ -220,13 +204,10 @@ module Rigor
|
|
|
220
204
|
end
|
|
221
205
|
private_class_method :collect_values
|
|
222
206
|
|
|
223
|
-
# Walks every top-level `argument :name, Type, required: ...`
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
# `required:` (default `false` — per graphql-ruby's
|
|
228
|
-
# `argument` default; the OPPOSITE polarity of `field`'s
|
|
229
|
-
# `null:`).
|
|
207
|
+
# Walks every top-level `argument :name, Type, required: ...` call inside an InputObject (or
|
|
208
|
+
# Mutation) subclass body and returns the per-argument shape table. Argument syntax mirrors
|
|
209
|
+
# `field` except the nullability axis is named `required:` (default `false` — per graphql-ruby's
|
|
210
|
+
# `argument` default; the OPPOSITE polarity of `field`'s `null:`).
|
|
230
211
|
#
|
|
231
212
|
# argument :name, String, required: true
|
|
232
213
|
# argument :tags, [String], required: false
|
|
@@ -270,10 +251,9 @@ module Rigor
|
|
|
270
251
|
end
|
|
271
252
|
private_class_method :parse_argument_call
|
|
272
253
|
|
|
273
|
-
# Mirror of `extract_nullability` but reads the `required:`
|
|
274
|
-
#
|
|
275
|
-
# default
|
|
276
|
-
# nullability default).
|
|
254
|
+
# Mirror of `extract_nullability` but reads the `required:` kwarg, defaulting to `false`
|
|
255
|
+
# (graphql-ruby's argument default — the OPPOSITE polarity of `field`'s `null:` / nullability
|
|
256
|
+
# default).
|
|
277
257
|
# rubocop:disable Naming/PredicateMethod -- extractor returns the literal required value
|
|
278
258
|
def extract_required_flag(args)
|
|
279
259
|
kwargs = args.last
|
|
@@ -293,12 +273,10 @@ module Rigor
|
|
|
293
273
|
# rubocop:enable Naming/PredicateMethod
|
|
294
274
|
private_class_method :extract_required_flag
|
|
295
275
|
|
|
296
|
-
# `field :name, Type, null: false` shape. The first
|
|
297
|
-
#
|
|
298
|
-
#
|
|
299
|
-
#
|
|
300
|
-
# the nullability keyword (defaults to TRUE per
|
|
301
|
-
# graphql-ruby's field defaults so we mirror that).
|
|
276
|
+
# `field :name, Type, null: false` shape. The first positional is a Symbol (field name); the
|
|
277
|
+
# second is a constant reference (GraphQL type) OR a single-element ArrayNode (`[Type]`) for
|
|
278
|
+
# GraphQL list types; `null:` is the nullability keyword (defaults to TRUE per graphql-ruby's
|
|
279
|
+
# field defaults so we mirror that).
|
|
302
280
|
def parse_field_call(node)
|
|
303
281
|
args = node.arguments&.arguments
|
|
304
282
|
return nil if args.nil? || args.size < 2
|
|
@@ -320,13 +298,10 @@ module Rigor
|
|
|
320
298
|
end
|
|
321
299
|
private_class_method :parse_field_call
|
|
322
300
|
|
|
323
|
-
# Resolves the `Type` positional argument to a
|
|
324
|
-
# `
|
|
325
|
-
#
|
|
326
|
-
#
|
|
327
|
-
# not lists. Returns nil for unrecognised shapes (string
|
|
328
|
-
# types `"User"`, Proc lazy types, etc.) so callers drop
|
|
329
|
-
# the field.
|
|
301
|
+
# Resolves the `Type` positional argument to a `{type: "ClassName", list: bool}` tuple.
|
|
302
|
+
# ArrayNode forms (`[String]` / `[Types::User]`) unwrap the single element and mark `list: true`.
|
|
303
|
+
# Bare constant refs are not lists. Returns nil for unrecognised shapes (string types `"User"`,
|
|
304
|
+
# Proc lazy types, etc.) so callers drop the field.
|
|
330
305
|
def resolve_field_type(node)
|
|
331
306
|
if node.is_a?(Prism::ArrayNode)
|
|
332
307
|
element = node.elements.first
|
|
@@ -354,9 +329,8 @@ module Rigor
|
|
|
354
329
|
end
|
|
355
330
|
private_class_method :resolve_constant_type
|
|
356
331
|
|
|
357
|
-
# Defaults to `true` (matches graphql-ruby's `field`
|
|
358
|
-
#
|
|
359
|
-
# keyword and reads its boolean literal.
|
|
332
|
+
# Defaults to `true` (matches graphql-ruby's `field` default nullability). Looks for an explicit
|
|
333
|
+
# `null:` keyword and reads its boolean literal.
|
|
360
334
|
# rubocop:disable Naming/PredicateMethod -- extractor returns the literal nullability value
|
|
361
335
|
def extract_nullability(args)
|
|
362
336
|
kwargs = args.last
|
|
@@ -376,9 +350,8 @@ module Rigor
|
|
|
376
350
|
# rubocop:enable Naming/PredicateMethod
|
|
377
351
|
private_class_method :extract_nullability
|
|
378
352
|
|
|
379
|
-
# Returns the constant chain as an Array of String
|
|
380
|
-
#
|
|
381
|
-
# array for unrecognised node kinds.
|
|
353
|
+
# Returns the constant chain as an Array of String segments (`["GraphQL", "Schema", "Object"]`).
|
|
354
|
+
# Empty array for unrecognised node kinds.
|
|
382
355
|
def constant_path_segments(node)
|
|
383
356
|
case node
|
|
384
357
|
when Prism::ConstantReadNode then [node.name.to_s]
|
|
@@ -397,9 +370,8 @@ module Rigor
|
|
|
397
370
|
end
|
|
398
371
|
private_class_method :constant_path_segments
|
|
399
372
|
|
|
400
|
-
# Joined `::`-form of {.constant_path_segments}. Returns
|
|
401
|
-
#
|
|
402
|
-
# circuit).
|
|
373
|
+
# Joined `::`-form of {.constant_path_segments}. Returns nil for unrecognised node kinds (so
|
|
374
|
+
# callers can short-circuit).
|
|
403
375
|
def constant_name_for(node)
|
|
404
376
|
segments = constant_path_segments(node)
|
|
405
377
|
segments.empty? ? nil : segments.join("::")
|
|
@@ -12,51 +12,41 @@ module Rigor
|
|
|
12
12
|
# [Rails plugins roadmap](../../../../../docs/design/20260508-rails-plugins-roadmap.md)
|
|
13
13
|
# § "3D".
|
|
14
14
|
#
|
|
15
|
-
# Recognises `class T < GraphQL::Schema::Object` subclasses
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# `field` DSL emits NO Ruby methods (it just records a
|
|
24
|
-
# `Schema::Field` on the class's `own_fields`). The user writes
|
|
25
|
-
# resolver methods themselves; rigor's value here is producing a
|
|
26
|
-
# static type table downstream consumers can cross-reference.
|
|
15
|
+
# Recognises `class T < GraphQL::Schema::Object` subclasses and walks every `field :name, Type,
|
|
16
|
+
# null: false` declaration inside, publishing the resulting field-type map as the
|
|
17
|
+
# `:graphql_type_table` cross-plugin fact (ADR-9). The macro expansion library survey at
|
|
18
|
+
# docs/notes/20260515-macro-expansion-library-survey.md § "GraphQL-Ruby" documents WHY this is a
|
|
19
|
+
# pure metadata-recorder plugin rather than an ADR-16
|
|
20
|
+
# substrate consumer: graphql-ruby's `field` DSL emits NO Ruby methods (it just records a
|
|
21
|
+
# `Schema::Field` on the class's `own_fields`). The user writes resolver methods themselves; rigor's
|
|
22
|
+
# value here is producing a static type table downstream consumers can cross-reference.
|
|
27
23
|
#
|
|
28
24
|
# ## What downstream consumers DO with the published facts
|
|
29
25
|
#
|
|
30
|
-
# The tables are the substrate for two future capabilities
|
|
31
|
-
# (demand-driven, not yet implemented):
|
|
26
|
+
# The tables are the substrate for two future capabilities (demand-driven, not yet implemented):
|
|
32
27
|
#
|
|
33
|
-
# - Resolver-method check: for each `field :name, Type` whose
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
# plugin could type `Schema.execute(query).to_h` against the
|
|
38
|
-
# queried fields.
|
|
28
|
+
# - Resolver-method check: for each `field :name, Type` whose `name` is also defined as a Ruby method
|
|
29
|
+
# on the class, verify the method's return type matches `Type`'s underlying class.
|
|
30
|
+
# - Schema-query result typing: a future `rigor-graphql-execute` plugin could type
|
|
31
|
+
# `Schema.execute(query).to_h` against the queried fields.
|
|
39
32
|
#
|
|
40
33
|
# ## What's recognised
|
|
41
34
|
#
|
|
42
|
-
# - `class T < GraphQL::Schema::Object` subclasses (including
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# and GraphQL→Ruby scalar mapping.
|
|
35
|
+
# - `class T < GraphQL::Schema::Object` subclasses (including nested namespaces); `field :name, Type,
|
|
36
|
+
# null: ...` declarations with constant-reference or list-array types and GraphQL→Ruby scalar
|
|
37
|
+
# mapping.
|
|
46
38
|
# - `class T < GraphQL::Schema::Enum`; `value "ACTIVE"` calls.
|
|
47
|
-
# - `class T < GraphQL::Schema::InputObject` /
|
|
48
|
-
# `GraphQL::Schema::Mutation`; `argument :name, Type,
|
|
39
|
+
# - `class T < GraphQL::Schema::InputObject` / `GraphQL::Schema::Mutation`; `argument :name, Type,
|
|
49
40
|
# required: ...` declarations.
|
|
50
41
|
# - No user-facing diagnostics yet.
|
|
51
42
|
#
|
|
52
43
|
# ## Deferred (demand-driven)
|
|
53
44
|
#
|
|
54
45
|
# - **`resolver:` / `mutation:` reroute** recognition.
|
|
55
|
-
# - **String type expressions** (`field :foo, "User"`) — defeats
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
# user at the constant-reference form for static typing.
|
|
46
|
+
# - **String type expressions** (`field :foo, "User"`) — defeats static resolution by design
|
|
47
|
+
# (graphql-ruby's `BuildType.parse_type` constantizes at runtime); a future slice could surface
|
|
48
|
+
# these as `graphql.string-type` `:info` diagnostics pointing the user at the constant-reference
|
|
49
|
+
# form for static typing.
|
|
60
50
|
class Graphql < Rigor::Plugin::Base
|
|
61
51
|
manifest(
|
|
62
52
|
id: "graphql",
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Gem entry point. Required by Rigor's plugin loader when
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# `Rigor::Plugin.register`, which the body of
|
|
7
|
-
# `lib/rigor/plugin/graphql.rb` performs at load time.
|
|
3
|
+
# Gem entry point. Required by Rigor's plugin loader when `.rigor.yml` lists `rigor-graphql` under
|
|
4
|
+
# `plugins:`. The loader expects this `require` to side-effect a call to `Rigor::Plugin.register`,
|
|
5
|
+
# which the body of `lib/rigor/plugin/graphql.rb` performs at load time.
|
|
8
6
|
require_relative "rigor/plugin/graphql"
|
|
@@ -5,16 +5,12 @@ require "prism"
|
|
|
5
5
|
module Rigor
|
|
6
6
|
module Plugin
|
|
7
7
|
class Hanami < Rigor::Plugin::Base
|
|
8
|
-
# Checks the "presence" half of the Hanami::Action
|
|
9
|
-
# ADR-28 protocol contract.
|
|
8
|
+
# Checks the "presence" half of the Hanami::Action ADR-28 protocol contract.
|
|
10
9
|
#
|
|
11
|
-
# Every class defined in a file matching a contract's
|
|
12
|
-
# `
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# is handled engine-side by
|
|
16
|
-
# `Inference::MethodParameterBinder`. Return type is
|
|
17
|
-
# void so no conformance check is performed here.
|
|
10
|
+
# Every class defined in a file matching a contract's `path_glob` must declare `#handle(request,
|
|
11
|
+
# response)`. The "provide" half — binding `Hanami::Action::Request` and `Hanami::Action::Response`
|
|
12
|
+
# into the method body — is handled engine-side by `Inference::MethodParameterBinder`. Return type
|
|
13
|
+
# is void so no conformance check is performed here.
|
|
18
14
|
class ActionChecker
|
|
19
15
|
FNMATCH_FLAGS = File::FNM_PATHNAME | File::FNM_EXTGLOB
|
|
20
16
|
|
|
@@ -22,11 +18,10 @@ module Rigor
|
|
|
22
18
|
@contracts = contracts
|
|
23
19
|
end
|
|
24
20
|
|
|
25
|
-
# Per-`ClassNode` check over the engine-owned walk (ADR-37):
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# ships no `class_nodes` traversal of its own.
|
|
21
|
+
# Per-`ClassNode` check over the engine-owned walk (ADR-37): called once per class node the
|
|
22
|
+
# `node_rule` in `hanami.rb` dispatches, against every contract whose `path_glob` matches the
|
|
23
|
+
# file. No cross-class-node state is needed, so the checker ships no `class_nodes` traversal of
|
|
24
|
+
# its own.
|
|
30
25
|
def check_class(class_node, path:)
|
|
31
26
|
@contracts.filter_map do |contract|
|
|
32
27
|
next unless path_matches?(contract.path_glob, path)
|
|
@@ -42,9 +37,8 @@ module Rigor
|
|
|
42
37
|
|
|
43
38
|
private
|
|
44
39
|
|
|
45
|
-
# Contract globs are project-root-relative; the analyzer
|
|
46
|
-
#
|
|
47
|
-
# matched both directly and with a `**/`-prefixed suffix.
|
|
40
|
+
# Contract globs are project-root-relative; the analyzer may supply a relative or absolute path,
|
|
41
|
+
# so the glob is matched both directly and with a `**/`-prefixed suffix.
|
|
48
42
|
def path_matches?(glob, path)
|
|
49
43
|
return false if path.nil?
|
|
50
44
|
|
|
@@ -9,37 +9,28 @@ module Rigor
|
|
|
9
9
|
module Plugin
|
|
10
10
|
# rigor-hanami — enforces the Hanami::Action protocol.
|
|
11
11
|
#
|
|
12
|
-
# Hanami 3.x actions inherit from `Hanami::Action` and
|
|
13
|
-
# define a single entry-point:
|
|
12
|
+
# Hanami 3.x actions inherit from `Hanami::Action` and define a single entry-point:
|
|
14
13
|
#
|
|
15
14
|
# def handle(request, response)
|
|
16
15
|
# response.status = 200
|
|
17
16
|
# response.body = "Hello"
|
|
18
17
|
# end
|
|
19
18
|
#
|
|
20
|
-
# The method is void — the response is mutated in-place;
|
|
21
|
-
#
|
|
19
|
+
# The method is void — the response is mutated in-place; the return value is discarded by the
|
|
20
|
+
# framework.
|
|
22
21
|
#
|
|
23
|
-
# This plugin uses the **ADR-28 path-scoped
|
|
24
|
-
# method-protocol contract** to:
|
|
22
|
+
# This plugin uses the **ADR-28 path-scoped method-protocol contract** to:
|
|
25
23
|
#
|
|
26
|
-
# 1. **Provide** `Hanami::Action::Request` and
|
|
27
|
-
# `
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
# The engine substitutes these types for the usual
|
|
31
|
-
# `Dynamic[Top]` fallback, so misuse of `request` or
|
|
32
|
-
# `response` inside an action body surfaces as a
|
|
33
|
-
# core diagnostic.
|
|
24
|
+
# 1. **Provide** `Hanami::Action::Request` and `Hanami::Action::Response` as the types of the first
|
|
25
|
+
# and second parameters of every `#handle` method defined inside `app/actions/**/*.rb`. The
|
|
26
|
+
# engine substitutes these types for the usual `Dynamic[Top]` fallback, so misuse of `request` or
|
|
27
|
+
# `response` inside an action body surfaces as a core diagnostic.
|
|
34
28
|
#
|
|
35
|
-
# 2. **Check** that every class under `app/actions/`
|
|
36
|
-
# defines `#handle`. Missing definitions emit
|
|
29
|
+
# 2. **Check** that every class under `app/actions/` defines `#handle`. Missing definitions emit
|
|
37
30
|
# `missing-handle-method`.
|
|
38
31
|
#
|
|
39
|
-
# Return-type conformance is NOT checked — Hanami
|
|
40
|
-
#
|
|
41
|
-
# return would produce false positives on every `if`
|
|
42
|
-
# branch that doesn't end with an explicit `nil`.
|
|
32
|
+
# Return-type conformance is NOT checked — Hanami actions are void by contract, and checking a void
|
|
33
|
+
# return would produce false positives on every `if` branch that doesn't end with an explicit `nil`.
|
|
43
34
|
#
|
|
44
35
|
# ## Configuration
|
|
45
36
|
#
|
|
@@ -55,10 +46,8 @@ module Rigor
|
|
|
55
46
|
# | action class defines no `#handle` | `:error` | `missing-handle-method` |
|
|
56
47
|
# | `request` / `response` misuse in body | core engine diagnostic |
|
|
57
48
|
#
|
|
58
|
-
# A misused `request` or `response` (e.g.
|
|
59
|
-
# `
|
|
60
|
-
# engine `call.undefined-method` diagnostic once the
|
|
61
|
-
# plugin provides the parameter types.
|
|
49
|
+
# A misused `request` or `response` (e.g. `request.no_such_method`) surfaces as a standard engine
|
|
50
|
+
# `call.undefined-method` diagnostic once the plugin provides the parameter types.
|
|
62
51
|
class Hanami < Rigor::Plugin::Base
|
|
63
52
|
manifest(
|
|
64
53
|
id: "hanami",
|
|
@@ -91,20 +80,17 @@ module Rigor
|
|
|
91
80
|
end
|
|
92
81
|
end
|
|
93
82
|
|
|
94
|
-
# ADR-28: override so the per-project `action_path` config
|
|
95
|
-
#
|
|
96
|
-
# this plugin's check half.
|
|
83
|
+
# ADR-28: override so the per-project `action_path` config reaches both the engine's
|
|
84
|
+
# parameter-provision tier and this plugin's check half.
|
|
97
85
|
def protocol_contracts
|
|
98
86
|
@protocol_contracts || manifest.protocol_contracts
|
|
99
87
|
end
|
|
100
88
|
|
|
101
|
-
# ADR-37 — per-class-node validation over the engine-owned walk.
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
# `node_rule` is for — the return type is void, so no `scope`
|
|
107
|
-
# query is needed.)
|
|
89
|
+
# ADR-37 — per-class-node validation over the engine-owned walk. Each `Prism::ClassNode` is checked
|
|
90
|
+
# against every contract independently, so the plugin no longer ships its own `class_nodes`
|
|
91
|
+
# traversal; `ActionChecker#check_class` keeps the per-class contract logic. (A per-class contract
|
|
92
|
+
# check is exactly what `node_rule` is for — the return type is void, so no `scope` query is
|
|
93
|
+
# needed.)
|
|
108
94
|
node_rule Prism::ClassNode do |node, _scope, path|
|
|
109
95
|
contracts = protocol_contracts
|
|
110
96
|
next [] if contracts.empty?
|