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,82 +8,61 @@ require_relative "../../source/constant_path"
|
|
|
8
8
|
module Rigor
|
|
9
9
|
module Analysis
|
|
10
10
|
module DependencySourceInference
|
|
11
|
-
# Walks a resolved gem's `roots:` and collects the
|
|
12
|
-
# `
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# method as defined by an opt-in gem and contribute a
|
|
16
|
-
# `Type::Dynamic`-wrapped return at the call site.
|
|
11
|
+
# Walks a resolved gem's `roots:` and collects the `(class_name, method_name) → CatalogEntry(kind,
|
|
12
|
+
# return_type)` method catalog. The walker is the source of facts the dispatcher tier consults to
|
|
13
|
+
# recognise a method as defined by an opt-in gem and contribute a `Type::Dynamic`-wrapped return at
|
|
14
|
+
# the call site.
|
|
17
15
|
#
|
|
18
|
-
# The dispatcher tier wraps every walker-contributed return
|
|
19
|
-
#
|
|
20
|
-
# the heuristic
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# `Dynamic[top]` (the pre-heuristic behaviour). The
|
|
24
|
-
# heuristic is intentionally narrow — only literal-tail
|
|
25
|
-
# method bodies fold; everything else degrades silently.
|
|
16
|
+
# The dispatcher tier wraps every walker-contributed return in `Dynamic[T]` per ADR-10's gem-boundary
|
|
17
|
+
# contract. When the heuristic ({ReturnTypeHeuristic}) recognises the method body's tail expression,
|
|
18
|
+
# the dispatcher uses the heuristic's static facet; otherwise it falls back to `Dynamic[top]` (the
|
|
19
|
+
# pre-heuristic behaviour). The heuristic is intentionally narrow — only literal-tail method bodies
|
|
20
|
+
# fold; everything else degrades silently.
|
|
26
21
|
#
|
|
27
|
-
# Hard exclusions are NOT user-configurable, per ADR-10
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
# automatically because the walker only loads `.rb` files.
|
|
22
|
+
# Hard exclusions are NOT user-configurable, per ADR-10 § "Hard exclusions": top-level `spec/`,
|
|
23
|
+
# `test/`, `bin/`, plus any non-`.rb` source. C extensions fall out automatically because the walker
|
|
24
|
+
# only loads `.rb` files.
|
|
31
25
|
module Walker
|
|
32
|
-
# Top-level directories that MUST NOT participate in
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
# `test/` directories deeper inside `lib/` are NOT
|
|
37
|
-
# filtered (a few gems legitimately ship `lib/.../spec/`).
|
|
26
|
+
# Top-level directories that MUST NOT participate in gem-source inference even when the user lists
|
|
27
|
+
# them under `roots:`. The check is case-insensitive against the first segment of `roots:`; nested
|
|
28
|
+
# `spec/` / `test/` directories deeper inside `lib/` are NOT filtered (a few gems legitimately ship
|
|
29
|
+
# `lib/.../spec/`).
|
|
38
30
|
HARD_EXCLUDED_ROOTS = %w[spec test bin].freeze
|
|
39
31
|
|
|
40
|
-
# Walker outcome wrapping the harvested method catalog
|
|
41
|
-
#
|
|
42
|
-
# the cap
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# this per-gem so the Runner can surface a single
|
|
46
|
-
# `dynamic.dependency-source.budget-exceeded` warning
|
|
47
|
-
# naming the affected gem(s).
|
|
32
|
+
# Walker outcome wrapping the harvested method catalog plus a budget-exceeded flag. ADR-10 slice 4
|
|
33
|
+
# introduces the cap; the Walker stops appending to the accumulator once `catalog.size` reaches
|
|
34
|
+
# `budget`, and `truncated?` reports whether the cap was reached. The Index records this per-gem so
|
|
35
|
+
# the Runner can surface a single `dynamic.dependency-source.budget-exceeded` warning naming the
|
|
36
|
+
# affected gem(s).
|
|
48
37
|
class Outcome < Data.define(:catalog, :truncated)
|
|
49
38
|
def truncated? = truncated
|
|
50
39
|
end
|
|
51
40
|
|
|
52
|
-
# Per-method catalog entry. `kind` is `:instance` or
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
# The dispatcher wraps a non-nil `return_type` in
|
|
57
|
-
# `Dynamic[T]`; a `nil` `return_type` falls back to
|
|
58
|
-
# `Dynamic[top]`.
|
|
41
|
+
# Per-method catalog entry. `kind` is `:instance` or `:singleton`; `return_type` is the
|
|
42
|
+
# {ReturnTypeHeuristic}-extracted static facet (a `Rigor::Type::*`) or `nil` when the heuristic
|
|
43
|
+
# declined. The dispatcher wraps a non-nil `return_type` in `Dynamic[T]`; a `nil` `return_type` falls
|
|
44
|
+
# back to `Dynamic[top]`.
|
|
59
45
|
class CatalogEntry < Data.define(:kind, :return_type)
|
|
60
46
|
def initialize(kind:, return_type: nil)
|
|
61
47
|
super
|
|
62
48
|
end
|
|
63
49
|
end
|
|
64
50
|
|
|
65
|
-
# Sentinel for "no cap" — used by callers that don't
|
|
66
|
-
#
|
|
67
|
-
# code MUST pass an integer.
|
|
51
|
+
# Sentinel for "no cap" — used by callers that don't care about the budget (specs, tooling).
|
|
52
|
+
# Production code MUST pass an integer.
|
|
68
53
|
UNBOUNDED = Float::INFINITY
|
|
69
54
|
|
|
70
55
|
module_function
|
|
71
56
|
|
|
72
|
-
# @param gem_dir [String, Pathname] absolute path to the
|
|
73
|
-
#
|
|
74
|
-
# @param roots [Array<String>] subdirectory names within
|
|
75
|
-
# the gem to walk (defaults to `["lib"]` per
|
|
57
|
+
# @param gem_dir [String, Pathname] absolute path to the gem's installation directory.
|
|
58
|
+
# @param roots [Array<String>] subdirectory names within the gem to walk (defaults to `["lib"]` per
|
|
76
59
|
# `Configuration::Dependencies::Entry`).
|
|
77
|
-
# @param budget [Integer, Float] per-gem catalog cap
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
# walker stopped harvesting because the budget was
|
|
84
|
-
# reached. Methods of identical name on the same class
|
|
85
|
-
# with different kinds (rare; private API mostly)
|
|
86
|
-
# carry the kind that wins the per-class first walk.
|
|
60
|
+
# @param budget [Integer, Float] per-gem catalog cap (method-definition count). When unset, defaults
|
|
61
|
+
# to `UNBOUNDED` for backwards-compatible test paths.
|
|
62
|
+
# @return [Outcome] frozen wrapper carrying the catalog (`Hash{[class_name, method_name] =>
|
|
63
|
+
# :instance | :singleton}`) and a `truncated?` flag set when the walker stopped harvesting because
|
|
64
|
+
# the budget was reached. Methods of identical name on the same class with different kinds (rare;
|
|
65
|
+
# private API mostly) carry the kind that wins the per-class first walk.
|
|
87
66
|
def walk(gem_dir:, roots:, budget: UNBOUNDED)
|
|
88
67
|
accumulator = {}
|
|
89
68
|
truncated = false
|
|
@@ -95,18 +74,14 @@ module Rigor
|
|
|
95
74
|
Outcome.new(catalog: accumulator.freeze, truncated: truncated)
|
|
96
75
|
end
|
|
97
76
|
|
|
98
|
-
# Drops hard-excluded entries before any filesystem
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
# it — the noise from RSpec-style globals plus the
|
|
102
|
-
# cost of walking test fixtures isn't worth the
|
|
103
|
-
# marginal coverage.
|
|
77
|
+
# Drops hard-excluded entries before any filesystem walk happens. Reasoning: we never want a gem's
|
|
78
|
+
# `spec/` to participate even if the user requested it — the noise from RSpec-style globals plus
|
|
79
|
+
# the cost of walking test fixtures isn't worth the marginal coverage.
|
|
104
80
|
def accepted_roots(roots)
|
|
105
81
|
roots.reject { |root| HARD_EXCLUDED_ROOTS.include?(root.downcase) }
|
|
106
82
|
end
|
|
107
83
|
|
|
108
|
-
# Returns true when the budget tripped during this
|
|
109
|
-
# root's walk so the caller can stop iterating
|
|
84
|
+
# Returns true when the budget tripped during this root's walk so the caller can stop iterating
|
|
110
85
|
# subsequent roots.
|
|
111
86
|
def walk_root(root_dir, accumulator, budget) # rubocop:disable Naming/PredicateMethod
|
|
112
87
|
return false unless File.directory?(root_dir)
|
|
@@ -124,19 +99,15 @@ module Rigor
|
|
|
124
99
|
|
|
125
100
|
walk_node(parse_result.value, [], false, accumulator, budget)
|
|
126
101
|
rescue StandardError
|
|
127
|
-
# Gem source we can't parse / read silently degrades
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
# opt-in gem source MUST NOT pollute it with parse
|
|
131
|
-
# errors the user cannot fix.
|
|
102
|
+
# Gem source we can't parse / read silently degrades to "no contribution from this file". The
|
|
103
|
+
# user-facing diagnostic stream is reserved for the project source; opt-in gem source MUST NOT
|
|
104
|
+
# pollute it with parse errors the user cannot fix.
|
|
132
105
|
nil
|
|
133
106
|
end
|
|
134
107
|
|
|
135
|
-
# Walks a Prism subtree, accumulating method definitions
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
# decoupled from `Scope` because gem-source inference
|
|
139
|
-
# runs without a scope context.
|
|
108
|
+
# Walks a Prism subtree, accumulating method definitions under their qualified class name. Mirrors
|
|
109
|
+
# the shape of `Inference::ScopeIndexer#walk_methods` but stays decoupled from `Scope` because
|
|
110
|
+
# gem-source inference runs without a scope context.
|
|
140
111
|
def walk_node(node, qualified_prefix, in_singleton_class, accumulator, budget)
|
|
141
112
|
return unless node.is_a?(Prism::Node)
|
|
142
113
|
return if accumulator.size >= budget
|
|
@@ -161,11 +132,9 @@ module Rigor
|
|
|
161
132
|
end
|
|
162
133
|
end
|
|
163
134
|
|
|
164
|
-
# `class Foo` / `module Bar`. The dynamic-prefix shape
|
|
165
|
-
#
|
|
166
|
-
#
|
|
167
|
-
# children under the same prefix so any inner class
|
|
168
|
-
# definitions are still recorded under their own name.
|
|
135
|
+
# `class Foo` / `module Bar`. The dynamic-prefix shape (`module ::Foo`-rooted variants whose left
|
|
136
|
+
# side is a runtime expression) is treated as opaque — we walk the children under the same prefix
|
|
137
|
+
# so any inner class definitions are still recorded under their own name.
|
|
169
138
|
def descend_class_or_module(node, qualified_prefix, in_singleton_class, accumulator, budget)
|
|
170
139
|
name = Source::ConstantPath.qualified_name_or_nil(node.constant_path)
|
|
171
140
|
if name && node.body
|
|
@@ -175,10 +144,8 @@ module Rigor
|
|
|
175
144
|
end
|
|
176
145
|
end
|
|
177
146
|
|
|
178
|
-
# `class << self` only — `class << expr` for any other
|
|
179
|
-
#
|
|
180
|
-
# record per-instance singleton methods under the
|
|
181
|
-
# surrounding class.
|
|
147
|
+
# `class << self` only — `class << expr` for any other `expr` is treated as opaque so we don't
|
|
148
|
+
# accidentally record per-instance singleton methods under the surrounding class.
|
|
182
149
|
def descend_singleton_class(node, qualified_prefix, accumulator, budget)
|
|
183
150
|
if node.expression.is_a?(Prism::SelfNode) && node.body
|
|
184
151
|
walk_node(node.body, qualified_prefix, true, accumulator, budget)
|
|
@@ -13,16 +13,12 @@ module Rigor
|
|
|
13
13
|
#
|
|
14
14
|
# The namespace coordinates three components:
|
|
15
15
|
#
|
|
16
|
-
# - {GemResolver} maps a
|
|
17
|
-
# `
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# - {
|
|
21
|
-
#
|
|
22
|
-
# - {Index} holds the per-run state the dispatcher tier
|
|
23
|
-
# consults via `#contribution_for`; the method table is
|
|
24
|
-
# fully populated by {Walker} walking each resolved gem's
|
|
25
|
-
# `roots:`.
|
|
16
|
+
# - {GemResolver} maps a `Configuration::Dependencies::Entry` to either a frozen
|
|
17
|
+
# `Resolved(gem_name, version, gem_dir, mode, roots)` or an `Unresolvable(gem_name, reason)` value.
|
|
18
|
+
# - {Builder.build} folds a `Configuration::Dependencies` into a frozen {Index} carrying the partitioned
|
|
19
|
+
# outcomes.
|
|
20
|
+
# - {Index} holds the per-run state the dispatcher tier consults via `#contribution_for`; the method table
|
|
21
|
+
# is fully populated by {Walker} walking each resolved gem's `roots:`.
|
|
26
22
|
module DependencySourceInference
|
|
27
23
|
end
|
|
28
24
|
end
|
|
@@ -3,47 +3,34 @@
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Analysis
|
|
5
5
|
class Diagnostic
|
|
6
|
-
# The default source family. Matches the existing analyzer-
|
|
7
|
-
#
|
|
8
|
-
# baseline against which non-default families are recognised.
|
|
6
|
+
# The default source family. Matches the existing analyzer-internal rule families; serialised as
|
|
7
|
+
# `"builtin"` and is the baseline against which non-default families are recognised.
|
|
9
8
|
DEFAULT_SOURCE_FAMILY = :builtin
|
|
10
9
|
|
|
11
10
|
attr_reader :path, :line, :column, :message, :severity, :rule, :source_family,
|
|
12
11
|
:receiver_type, :method_name, :project_definition_site
|
|
13
12
|
|
|
14
|
-
# `rule:` is the stable identifier (a kebab-case string)
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# category. Diagnostics not produced by `CheckRules`
|
|
19
|
-
# (parse errors, path errors, internal analyzer errors)
|
|
20
|
-
# may leave `rule` as nil and stay unsuppressible.
|
|
13
|
+
# `rule:` is the stable identifier (a kebab-case string) of the diagnostic's source rule. It is used by
|
|
14
|
+
# the configuration and the in-source `# rigor:disable <rule>` suppression comment system to identify
|
|
15
|
+
# diagnostics by category. Diagnostics not produced by `CheckRules` (parse errors, path errors, internal
|
|
16
|
+
# analyzer errors) may leave `rule` as nil and stay unsuppressible.
|
|
21
17
|
#
|
|
22
|
-
# `source_family:` names the producer of the rule. The default
|
|
23
|
-
# `:
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
# distinguish where a diagnostic originated without committing
|
|
27
|
-
# to the plugin API itself.
|
|
18
|
+
# `source_family:` names the producer of the rule. The default `:builtin` covers analyzer-internal
|
|
19
|
+
# rules; future families like `:rbs_extended`, `:generated`, or `"plugin.<id>"` (per ADR-2 § "Plugin
|
|
20
|
+
# Diagnostic Provenance") let consumers distinguish where a diagnostic originated without committing to
|
|
21
|
+
# the plugin API itself.
|
|
28
22
|
#
|
|
29
|
-
# `receiver_type:` / `method_name:` are optional structured
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# the diagnostic message, so the catalogue no longer couples to
|
|
35
|
-
# message wording. Both stay nil for rules that have no such
|
|
36
|
-
# pair; a consumer that finds them nil falls back to message
|
|
37
|
-
# parsing.
|
|
23
|
+
# `receiver_type:` / `method_name:` are optional structured fields populated by the call-related rules
|
|
24
|
+
# (`call.undefined-method`) — the rendered receiver type and the called method name as plain strings.
|
|
25
|
+
# ADR-23 WD3 / slice 4: `rigor triage`'s heuristic recognisers read these directly instead of parsing
|
|
26
|
+
# the diagnostic message, so the catalogue no longer couples to message wording. Both stay nil for
|
|
27
|
+
# rules that have no such pair; a consumer that finds them nil falls back to message parsing.
|
|
38
28
|
#
|
|
39
|
-
# `project_definition_site:` is an optional `"path:line"` string
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
# is the high-confidence "this is a project monkey-patch, not a
|
|
45
|
-
# bug" signal `rigor triage` keys on to recommend `pre_eval:`.
|
|
46
|
-
# Nil for every other diagnostic.
|
|
29
|
+
# `project_definition_site:` is an optional `"path:line"` string set by `call.undefined-method` when
|
|
30
|
+
# the project itself defines the called method on the receiver class somewhere in the analyzed file set
|
|
31
|
+
# (a reopened core/stdlib/gem class the dispatcher does not apply cross-file — see ADR-17). Its
|
|
32
|
+
# presence is the high-confidence "this is a project monkey-patch, not a bug" signal `rigor triage` keys
|
|
33
|
+
# on to recommend `pre_eval:`. Nil for every other diagnostic.
|
|
47
34
|
def initialize(path:, line:, column:, message:, severity: :error, rule: nil, # rubocop:disable Metrics/ParameterLists
|
|
48
35
|
source_family: DEFAULT_SOURCE_FAMILY,
|
|
49
36
|
receiver_type: nil, method_name: nil, project_definition_site: nil)
|
|
@@ -62,16 +49,13 @@ module Rigor
|
|
|
62
49
|
@project_definition_site = project_definition_site
|
|
63
50
|
end
|
|
64
51
|
|
|
65
|
-
# Builds a Diagnostic positioned at a Prism node. Internalises
|
|
66
|
-
# the
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
# 1-based). Pass any node responding to `#location`; all other
|
|
70
|
-
# fields forward to `#initialize` unchanged.
|
|
52
|
+
# Builds a Diagnostic positioned at a Prism node. Internalises the load-bearing convention every caller
|
|
53
|
+
# otherwise repeats: the line is the node's 1-based `start_line` and the column is `start_column + 1`
|
|
54
|
+
# (Prism columns are 0-based; Rigor reports 1-based). Pass any node responding to `#location`; all
|
|
55
|
+
# other fields forward to `#initialize` unchanged.
|
|
71
56
|
#
|
|
72
|
-
# `Plugin::Base#diagnostic` wraps this for plugin authors (who
|
|
73
|
-
#
|
|
74
|
-
# rules and other producers call it directly.
|
|
57
|
+
# `Plugin::Base#diagnostic` wraps this for plugin authors (who must not set `source_family` — the
|
|
58
|
+
# runner stamps it); core rules and other producers call it directly.
|
|
75
59
|
def self.from_node(node, path:, message:, severity: :error, rule: nil, # rubocop:disable Metrics/ParameterLists
|
|
76
60
|
source_family: DEFAULT_SOURCE_FAMILY,
|
|
77
61
|
receiver_type: nil, method_name: nil, project_definition_site: nil)
|
|
@@ -82,12 +66,10 @@ module Rigor
|
|
|
82
66
|
)
|
|
83
67
|
end
|
|
84
68
|
|
|
85
|
-
# Builds a Diagnostic from an explicit Prism location, applying
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
# `message_loc` (the matcher / method name) instead of the
|
|
90
|
-
# receiver-spanning `node.location`. {.from_node} is sugar for
|
|
69
|
+
# Builds a Diagnostic from an explicit Prism location, applying the same 1-based `line` /
|
|
70
|
+
# `start_column + 1` convention as {.from_node}. Use this when the diagnostic should point at a
|
|
71
|
+
# *sub-location* rather than the whole node — most often a call's `message_loc` (the matcher / method
|
|
72
|
+
# name) instead of the receiver-spanning `node.location`. {.from_node} is sugar for
|
|
91
73
|
# `from_location(node.location, …)`.
|
|
92
74
|
def self.from_location(location, path:, message:, severity: :error, rule: nil, # rubocop:disable Metrics/ParameterLists
|
|
93
75
|
source_family: DEFAULT_SOURCE_FAMILY,
|
|
@@ -102,20 +84,17 @@ module Rigor
|
|
|
102
84
|
)
|
|
103
85
|
end
|
|
104
86
|
|
|
105
|
-
# Builds a Diagnostic at a call node's `message_loc` (the
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
# call-related rules otherwise repeat; all other fields forward to
|
|
110
|
-
# {.from_location}.
|
|
87
|
+
# Builds a Diagnostic at a call node's `message_loc` (the method-name / matcher span), falling back to
|
|
88
|
+
# the receiver-spanning `node.location` when no message location is available. Absorbs the
|
|
89
|
+
# `node.message_loc || node.location` idiom the call-related rules otherwise repeat; all other fields
|
|
90
|
+
# forward to {.from_location}.
|
|
111
91
|
def self.from_message_loc(node, **)
|
|
112
92
|
from_location(node.message_loc || node.location, **)
|
|
113
93
|
end
|
|
114
94
|
|
|
115
|
-
# Builds a Diagnostic at a definition / assignment node's
|
|
116
|
-
# `name_loc`
|
|
117
|
-
#
|
|
118
|
-
# idiom the def / write rules otherwise repeat.
|
|
95
|
+
# Builds a Diagnostic at a definition / assignment node's `name_loc` (the declared name span), falling
|
|
96
|
+
# back to `node.location`. Absorbs the `node.name_loc || node.location` idiom the def / write rules
|
|
97
|
+
# otherwise repeat.
|
|
119
98
|
def self.from_name_loc(node, **)
|
|
120
99
|
from_location(node.name_loc || node.location, **)
|
|
121
100
|
end
|
|
@@ -124,10 +103,9 @@ module Rigor
|
|
|
124
103
|
severity == :error
|
|
125
104
|
end
|
|
126
105
|
|
|
127
|
-
# The fully-qualified rule identifier — `<source_family>.<rule>`
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
# (e.g. parse errors and internal-analyzer errors).
|
|
106
|
+
# The fully-qualified rule identifier — `<source_family>.<rule>` when the source is non-default, or
|
|
107
|
+
# just `<rule>` for the `:builtin` family. Returns nil when `rule` itself is nil (e.g. parse errors and
|
|
108
|
+
# internal-analyzer errors).
|
|
131
109
|
def qualified_rule
|
|
132
110
|
return nil if rule.nil?
|
|
133
111
|
return rule if source_family == DEFAULT_SOURCE_FAMILY
|
|
@@ -135,12 +113,10 @@ module Rigor
|
|
|
135
113
|
"#{source_family}.#{rule}"
|
|
136
114
|
end
|
|
137
115
|
|
|
138
|
-
# `--format json` serialisation. The structured `receiver_type`
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
#
|
|
142
|
-
# called class / method without parsing the human-readable
|
|
143
|
-
# `message` — the message wording is presentation, not contract.
|
|
116
|
+
# `--format json` serialisation. The structured `receiver_type` / `method_name` /
|
|
117
|
+
# `project_definition_site` fields are emitted only when populated, so a consumer (`jq`, `rigor
|
|
118
|
+
# triage`, an AI agent) can group a `rigor check --format json` stream by the called class / method
|
|
119
|
+
# without parsing the human-readable `message` — the message wording is presentation, not contract.
|
|
144
120
|
def to_h
|
|
145
121
|
base = {
|
|
146
122
|
"path" => path,
|
|
@@ -157,13 +133,10 @@ module Rigor
|
|
|
157
133
|
base
|
|
158
134
|
end
|
|
159
135
|
|
|
160
|
-
# Text rendering for `rigor check`. The qualified rule
|
|
161
|
-
#
|
|
162
|
-
# `plugin
|
|
163
|
-
#
|
|
164
|
-
# whenever the diagnostic carries a non-default `source_family`,
|
|
165
|
-
# so plugin / RBS::Extended / generated provenance is visible
|
|
166
|
-
# in the standard text output without changing the layout for
|
|
136
|
+
# Text rendering for `rigor check`. The qualified rule identifier (per ADR-2 § "Plugin Diagnostic
|
|
137
|
+
# Provenance" — `plugin.<id>.<rule>`, `rbs_extended.<rule>`, `generated.<provider>.<rule>`) is appended
|
|
138
|
+
# in brackets whenever the diagnostic carries a non-default `source_family`, so plugin / RBS::Extended
|
|
139
|
+
# / generated provenance is visible in the standard text output without changing the layout for
|
|
167
140
|
# built-in rules. Slice 5 (v0.1.0) wires this surface.
|
|
168
141
|
def to_s
|
|
169
142
|
base = "#{path}:#{line}:#{column}: #{severity}: #{message}"
|
|
@@ -2,31 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
module Rigor
|
|
4
4
|
module Analysis
|
|
5
|
-
# Detects when a `.rb` file is actually an ERB template — a Rails
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# all of them noise from the user's perspective. The runner / worker
|
|
12
|
-
# consults this detector before turning parse errors into
|
|
13
|
-
# diagnostics; an ERB-shaped source is silently skipped.
|
|
5
|
+
# Detects when a `.rb` file is actually an ERB template — a Rails generator `templates/foo.rb` shape that
|
|
6
|
+
# uses `<%= ... %>` to interpolate identifiers. Prism rejects the template as invalid Ruby and the analyzer
|
|
7
|
+
# emits one parse-error diagnostic per scrap the parser tripped over (≈ 20 on Redmine's
|
|
8
|
+
# `lib/generators/redmine_plugin_model/templates/migration.rb`), all of them noise from the user's
|
|
9
|
+
# perspective. The runner / worker consults this detector before turning parse errors into diagnostics; an
|
|
10
|
+
# ERB-shaped source is silently skipped.
|
|
14
11
|
#
|
|
15
|
-
# Detection is byte-level on the raw source: any occurrence of the
|
|
16
|
-
# `%>`
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# comparison against a `%`-literal) but a real Ruby file with that
|
|
21
|
-
# shape would still not contain the closing `%>`.
|
|
12
|
+
# Detection is byte-level on the raw source: any occurrence of the `%>` closing marker proves the file is
|
|
13
|
+
# an ERB template. `%>` cannot appear in valid Ruby — `%` is a binary operator that requires an operand on
|
|
14
|
+
# its right side, never `>`. The opening `<%` is ambiguous in principle (`x<%y` parses as `x < %y`, a
|
|
15
|
+
# comparison against a `%`-literal) but a real Ruby file with that shape would still not contain the
|
|
16
|
+
# closing `%>`.
|
|
22
17
|
module ErbTemplateDetector
|
|
23
18
|
ERB_CLOSING_MARKER = /%>/
|
|
24
19
|
|
|
25
20
|
module_function
|
|
26
21
|
|
|
27
22
|
# @param parse_result [Prism::ParseResult]
|
|
28
|
-
# @return [Boolean] true when the parsed source looks like an
|
|
29
|
-
#
|
|
23
|
+
# @return [Boolean] true when the parsed source looks like an ERB template (parse errors expected;
|
|
24
|
+
# analysis should skip).
|
|
30
25
|
def template?(parse_result)
|
|
31
26
|
source = parse_result.source.source
|
|
32
27
|
return false unless source.is_a?(String) && !source.empty?
|
|
@@ -4,11 +4,9 @@ module Rigor
|
|
|
4
4
|
module Analysis
|
|
5
5
|
# Immutable storage for flow-sensitive facts attached to a Scope snapshot.
|
|
6
6
|
#
|
|
7
|
-
# Six buckets (see BUCKETS): local_binding, captured_local,
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# and conservatively join two stores by retaining only facts that both
|
|
11
|
-
# incoming edges share.
|
|
7
|
+
# Six buckets (see BUCKETS): local_binding, captured_local, object_content, global_storage, dynamic_origin,
|
|
8
|
+
# relational. Callers can record facts, invalidate all facts that mention a target, and conservatively
|
|
9
|
+
# join two stores by retaining only facts that both incoming edges share.
|
|
12
10
|
class FactStore
|
|
13
11
|
BUCKETS = %i[
|
|
14
12
|
local_binding
|
|
@@ -48,13 +46,9 @@ module Rigor
|
|
|
48
46
|
attr_reader :facts
|
|
49
47
|
|
|
50
48
|
class << self
|
|
51
|
-
# ADR-15 Phase 4b.x — return the eagerly-loaded
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
# Ractors and trip `Ractor::IsolationError`. The
|
|
55
|
-
# `@empty = new.freeze` at module body below
|
|
56
|
-
# pre-populates the ivar on the main Ractor at load
|
|
57
|
-
# time.
|
|
49
|
+
# ADR-15 Phase 4b.x — return the eagerly-loaded singleton-class `@empty` ivar. Lazy `@empty ||= new`
|
|
50
|
+
# would write to a class/module ivar from non-main Ractors and trip `Ractor::IsolationError`. The
|
|
51
|
+
# `@empty = new.freeze` at module body below pre-populates the ivar on the main Ractor at load time.
|
|
58
52
|
attr_reader :empty
|
|
59
53
|
end
|
|
60
54
|
|
|
@@ -131,11 +125,9 @@ module Rigor
|
|
|
131
125
|
unique.freeze
|
|
132
126
|
end
|
|
133
127
|
|
|
134
|
-
# `fact.target` is `Target | Array[Target]` per the carrier
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
# the bare `Target` case, so the wrapped tuple is `[Target]`
|
|
138
|
-
# and the union of return paths is exactly `Array[Target]`.
|
|
128
|
+
# `fact.target` is `Target | Array[Target]` per the carrier contract. Branching with an early return on
|
|
129
|
+
# the `Array` arm lets type narrowing collapse the post-return value to the bare `Target` case, so the
|
|
130
|
+
# wrapped tuple is `[Target]` and the union of return paths is exactly `Array[Target]`.
|
|
139
131
|
def fact_targets(fact)
|
|
140
132
|
target = fact.target
|
|
141
133
|
return target if target.is_a?(Array)
|
|
@@ -143,11 +135,9 @@ module Rigor
|
|
|
143
135
|
[target]
|
|
144
136
|
end
|
|
145
137
|
|
|
146
|
-
# ADR-15 Phase 4b.x — eager-load the singleton `@empty`
|
|
147
|
-
#
|
|
148
|
-
#
|
|
149
|
-
# class/module ivar WRITE (which non-main Ractors are
|
|
150
|
-
# forbidden from doing).
|
|
138
|
+
# ADR-15 Phase 4b.x — eager-load the singleton `@empty` on the main Ractor at module-load time. Workers
|
|
139
|
+
# then READ the populated ivar without ever attempting a class/module ivar WRITE (which non-main
|
|
140
|
+
# Ractors are forbidden from doing).
|
|
151
141
|
@empty = new.freeze
|
|
152
142
|
end
|
|
153
143
|
end
|