rigortype 0.3.0 → 0.3.2

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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -10
  3. data/data/builtins/ruby_core/array.yml +416 -392
  4. data/data/builtins/ruby_core/file.yml +42 -42
  5. data/data/builtins/ruby_core/hash.yml +302 -302
  6. data/data/builtins/ruby_core/io.yml +191 -191
  7. data/data/builtins/ruby_core/numeric.yml +321 -366
  8. data/data/builtins/ruby_core/proc.yml +124 -124
  9. data/data/builtins/ruby_core/range.yml +21 -21
  10. data/data/builtins/ruby_core/rational.yml +39 -39
  11. data/data/builtins/ruby_core/re.yml +65 -65
  12. data/data/builtins/ruby_core/set.yml +106 -106
  13. data/data/builtins/ruby_core/struct.yml +14 -14
  14. data/data/core_overlay/pathname.rbs +5 -0
  15. data/data/core_overlay/resolv.rbs +31 -0
  16. data/data/core_overlay/string_scanner.rbs +11 -5
  17. data/data/gem_overlay/activesupport/core_ext.rbs +10 -6
  18. data/data/vendored_gem_sigs/bundler/bundler.rbs +39 -27
  19. data/data/vendored_gem_sigs/cgi/cgi_extras.rbs +6 -0
  20. data/data/vendored_gem_sigs/prism/prism_supplement.rbs +7 -0
  21. data/data/vendored_gem_sigs/racc/racc.rbs +67 -0
  22. data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +20 -9
  23. data/docs/handbook/01-getting-started.md +22 -34
  24. data/docs/handbook/06-classes.md +1 -1
  25. data/docs/handbook/07-rbs-and-extended.md +76 -101
  26. data/docs/handbook/08-understanding-errors.md +114 -247
  27. data/docs/handbook/09-plugins.md +54 -144
  28. data/docs/handbook/README.md +5 -3
  29. data/docs/handbook/appendix-liskov.md +4 -2
  30. data/docs/handbook/appendix-phpstan.md +14 -7
  31. data/docs/handbook/appendix-steep.md +4 -2
  32. data/docs/handbook/appendix-type-theory.md +3 -1
  33. data/docs/manual/02-cli-reference.md +103 -11
  34. data/docs/manual/03-configuration.md +16 -1
  35. data/docs/manual/04-diagnostics.md +36 -4
  36. data/docs/manual/06-baseline.md +35 -1
  37. data/docs/manual/08-skills.md +6 -1
  38. data/docs/manual/09-editor-integration.md +3 -2
  39. data/docs/manual/12-caching.md +17 -5
  40. data/docs/manual/15-type-protection-coverage.md +122 -1
  41. data/docs/manual/plugins/rigor-actioncable.md +32 -0
  42. data/docs/manual/plugins/rigor-devise.md +4 -2
  43. data/docs/manual/plugins/rigor-rbs-inline.md +22 -0
  44. data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -0
  45. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +21 -2
  46. data/lib/rigor/analysis/check_rules.rb +79 -19
  47. data/lib/rigor/analysis/incremental_session.rb +156 -11
  48. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +14 -0
  49. data/lib/rigor/analysis/run_cache_key.rb +27 -1
  50. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +201 -0
  51. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +28 -10
  52. data/lib/rigor/analysis/runner/pool_coordinator.rb +7 -10
  53. data/lib/rigor/analysis/runner/project_pre_passes.rb +3 -1
  54. data/lib/rigor/analysis/runner.rb +80 -8
  55. data/lib/rigor/bleeding_edge.rb +132 -7
  56. data/lib/rigor/cache/descriptor.rb +6 -1
  57. data/lib/rigor/cache/engine_source.rb +162 -0
  58. data/lib/rigor/cache/incremental_snapshot.rb +87 -18
  59. data/lib/rigor/cache/rbs_cache_producer.rb +11 -1
  60. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +38 -0
  61. data/lib/rigor/cache/store.rb +99 -24
  62. data/lib/rigor/cli/check_command.rb +61 -34
  63. data/lib/rigor/cli/check_invocation.rb +84 -0
  64. data/lib/rigor/cli/coverage_command.rb +15 -2
  65. data/lib/rigor/cli/coverage_mutation.rb +242 -11
  66. data/lib/rigor/cli/doctor_command.rb +6 -8
  67. data/lib/rigor/cli/fused_protection_renderer.rb +10 -0
  68. data/lib/rigor/cli/fused_protection_report.rb +12 -3
  69. data/lib/rigor/cli/mutation_fork_scan.rb +64 -0
  70. data/lib/rigor/cli/mutation_protection_renderer.rb +12 -0
  71. data/lib/rigor/cli/mutation_protection_report.rb +26 -5
  72. data/lib/rigor/cli/protection_renderer.rb +13 -0
  73. data/lib/rigor/cli/protection_report.rb +11 -3
  74. data/lib/rigor/cli/show_bleedingedge_command.rb +17 -4
  75. data/lib/rigor/cli/sig_gen_command.rb +5 -4
  76. data/lib/rigor/cli/skill_command.rb +21 -1
  77. data/lib/rigor/cli/skill_deep_probe.rb +172 -0
  78. data/lib/rigor/cli/skill_describe.rb +75 -9
  79. data/lib/rigor/configuration.rb +45 -6
  80. data/lib/rigor/environment/bundle_sig_discovery.rb +2 -1
  81. data/lib/rigor/environment/default_libraries.rb +5 -4
  82. data/lib/rigor/environment/rbs_coverage_report.rb +1 -1
  83. data/lib/rigor/environment/rbs_loader.rb +434 -75
  84. data/lib/rigor/environment.rb +38 -14
  85. data/lib/rigor/inference/expression_typer.rb +15 -0
  86. data/lib/rigor/inference/fork_map.rb +9 -0
  87. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +50 -2
  88. data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
  89. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +35 -1
  90. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +6 -1
  91. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +16 -0
  92. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +64 -5
  93. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +137 -9
  94. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +3 -2
  95. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +124 -1
  96. data/lib/rigor/inference/mutation_widening.rb +30 -25
  97. data/lib/rigor/inference/narrowing.rb +3 -1
  98. data/lib/rigor/inference/optimistic_origin.rb +58 -0
  99. data/lib/rigor/inference/protection_scanner.rb +18 -2
  100. data/lib/rigor/inference/receiver_alias.rb +57 -0
  101. data/lib/rigor/inference/scope_indexer.rb +78 -27
  102. data/lib/rigor/inference/statement_evaluator.rb +40 -2
  103. data/lib/rigor/language_server/buffer_resolution.rb +6 -3
  104. data/lib/rigor/language_server/buffer_table.rb +68 -6
  105. data/lib/rigor/language_server/diagnostic_publisher.rb +207 -4
  106. data/lib/rigor/language_server/incremental_sync.rb +159 -0
  107. data/lib/rigor/language_server/project_context.rb +45 -0
  108. data/lib/rigor/language_server/publish_batcher.rb +82 -0
  109. data/lib/rigor/language_server/server.rb +38 -9
  110. data/lib/rigor/language_server.rb +2 -0
  111. data/lib/rigor/plugin/base.rb +29 -2
  112. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +9 -3
  113. data/lib/rigor/protection/closure_kill_oracle.rb +171 -0
  114. data/lib/rigor/protection/dependency_closure.rb +59 -0
  115. data/lib/rigor/protection/diagnostic_oracle.rb +12 -8
  116. data/lib/rigor/protection/discovery_seed.rb +116 -0
  117. data/lib/rigor/protection/kill_signature.rb +31 -0
  118. data/lib/rigor/protection/mutation_cache.rb +355 -0
  119. data/lib/rigor/protection/mutation_scanner.rb +55 -11
  120. data/lib/rigor/protection/mutator.rb +26 -4
  121. data/lib/rigor/runtime/jit.rb +63 -1
  122. data/lib/rigor/scope/discovery_index.rb +9 -0
  123. data/lib/rigor/scope.rb +52 -8
  124. data/lib/rigor/sig_gen/generator.rb +130 -31
  125. data/lib/rigor/sig_gen/layout_index.rb +6 -0
  126. data/lib/rigor/sig_gen/meta_class_shape.rb +84 -0
  127. data/lib/rigor/sig_gen/renderer.rb +20 -1
  128. data/lib/rigor/sig_gen/write_result.rb +6 -4
  129. data/lib/rigor/sig_gen/writer.rb +195 -47
  130. data/lib/rigor/sig_gen.rb +1 -0
  131. data/lib/rigor/version.rb +1 -1
  132. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +51 -1
  133. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +6 -6
  134. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/result_shape.rb +146 -0
  135. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +177 -12
  136. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +74 -7
  137. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +257 -0
  138. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +69 -0
  139. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +87 -11
  140. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +34 -4
  141. data/sig/rigor/cache.rbs +6 -0
  142. data/sig/rigor/inference/void_origin.rbs +21 -0
  143. data/sig/rigor/plugin/base.rbs +4 -3
  144. data/sig/rigor/scope.rbs +8 -0
  145. metadata +23 -3
@@ -61,7 +61,9 @@ module Rigor
61
61
  # seed, so an undeclared `def` / `initialize` / setter parameter is typed to the union of its resolved
62
62
  # call-site argument types (precision-additive only — the in-body negative rules decline on the inferred
63
63
  # lower bound, WD6b). `false` (the default, resolved off by every severity profile) keeps the table empty
64
- # and the run byte-identical to today. Mutually exclusive with `--incremental` (WD6c).
64
+ # and the run byte-identical to today. Composes with `--incremental` (the WD6c mutual exclusion is
65
+ # lifted): the incremental session recomputes the table each run and diffs it against its snapshot,
66
+ # re-checking any callee whose seeds moved.
65
67
  "parameter_inference" => false,
66
68
  "cache" => {
67
69
  "path" => ".rigor/cache",
@@ -94,8 +96,9 @@ module Rigor
94
96
  # ADR-50 § WD2 — bleeding-edge overlay opt-in. Selects which of the *next major's* queued changes
95
97
  # ({Rigor::BleedingEdge}) this project adopts early. Orthogonal to `severity_profile:`. Accepts `false`
96
98
  # (default — adopt none), `true` (adopt the whole overlay), a list of feature ids (adopt only those),
97
- # or `{ all: true, except: [ids] }` (adopt all but the named). The overlay is empty today, so every
98
- # form is currently a no-op; it becomes live when the first discipline is queued for a major.
99
+ # or `{ all: true, except: [ids] }` (adopt all but the named). A queued change is either a severity
100
+ # promotion (composed by {SeverityProfile.resolve}) or a behaviour switch (read at its call site
101
+ # through {#bleeding_edge_active?}); `bleeding_edge:` selects both kinds the same way.
99
102
  "bleeding_edge" => false,
100
103
  "dependencies" => {
101
104
  "source_inference" => [],
@@ -437,6 +440,7 @@ module Rigor
437
440
  data.fetch("bleeding_edge", DEFAULTS.fetch("bleeding_edge"))
438
441
  )
439
442
  @bleeding_edge_severity_overrides = BleedingEdge.severity_overrides_for(@bleeding_edge)
443
+ @bleeding_edge_active_ids = BleedingEdge.active_ids_for(@bleeding_edge)
440
444
  @dependencies = Dependencies.from_h(
441
445
  data.fetch("dependencies", DEFAULTS.fetch("dependencies"))
442
446
  )
@@ -515,6 +519,39 @@ module Rigor
515
519
  }
516
520
  end
517
521
 
522
+ # ADR-50 § WD2 — whether the bleeding-edge feature `id` is in force for this run.
523
+ #
524
+ # This is how a **behaviour** feature reaches its call site: a severity feature composes itself into
525
+ # `bleeding_edge_severity_overrides` and needs no engine code, but a queued change to a measurement, an
526
+ # algorithm, or a default has to be asked about where it happens. Answering from `Configuration` keeps
527
+ # the selection logic in one place and puts nothing but the id — already ADR-50 WD1 contract vocabulary
528
+ # — into engine code.
529
+ #
530
+ # Two deliberate asymmetries with the `bleeding_edge:` config key:
531
+ #
532
+ # - An id in {BleedingEdge::GRADUATED} answers `true` unconditionally. Graduation (§ WD7) turns a
533
+ # feature on for everyone, so a gate that outlives it keeps the graduated behaviour rather than
534
+ # silently reverting, and the call-site cleanup can land whenever it is convenient.
535
+ # - An id that is in neither {BleedingEdge::FEATURES} nor {BleedingEdge::GRADUATED} **raises**. An
536
+ # unknown id in a *config file* stays inert on purpose (it may come from a newer gem — see
537
+ # {#coerce_bleeding_edge}), but an id here is written by a Rigor contributor against the registry in
538
+ # the same checkout: a typo is a bug, and reading it as `false` would ship the feature permanently off
539
+ # with no signal.
540
+ #
541
+ # @param id [String] a feature id from {BleedingEdge::FEATURES} or {BleedingEdge::GRADUATED}.
542
+ # @return [Boolean]
543
+ # @raise [ArgumentError] if `id` names no known feature.
544
+ def bleeding_edge_active?(id)
545
+ return true if BleedingEdge.graduated?(id)
546
+ unless BleedingEdge.known_id?(id)
547
+ raise ArgumentError,
548
+ "unknown bleeding-edge feature id #{id.inspect}; known ids: " \
549
+ "#{(BleedingEdge.feature_ids + BleedingEdge::GRADUATED).inspect}"
550
+ end
551
+
552
+ @bleeding_edge_active_ids.include?(id)
553
+ end
554
+
518
555
  # ADR-50 § WD2 — returns a sibling Configuration whose bleeding-edge selection (and the derived
519
556
  # `bleeding_edge_severity_overrides` the two {SeverityProfile.resolve} sites consult) is replaced by
520
557
  # `value`, leaving every other field shared with the receiver. `value` takes the same forms as the
@@ -524,16 +561,18 @@ module Rigor
524
561
  #
525
562
  # The CLI's `--bleeding-edge[=ids]` / `--no-bleeding-edge` flag uses this to override the configured
526
563
  # selection for a single run (the same CLI-over-config precedence `--workers` and `--no-cache` follow).
527
- # It is a frozen `dup` with the two bleeding-edge ivars re-set: `dup` returns an unfrozen shallow copy
528
- # (every other ivar is the receiver's deeply-frozen value, safe to share read-only), the two replacements
564
+ # It is a frozen `dup` with the bleeding-edge ivars re-set: `dup` returns an unfrozen shallow copy
565
+ # (every other ivar is the receiver's deeply-frozen value, safe to share read-only), the replacements
529
566
  # are themselves deeply frozen, and the re-`freeze` keeps the result `Ractor.shareable?` for the worker
530
- # path.
567
+ # path. **Every ivar `#initialize` derives from `@bleeding_edge` has to be re-derived here** — the
568
+ # selector, the severity map, and the active-id set the {#bleeding_edge_active?} predicate reads.
531
569
  def with_bleeding_edge(value)
532
570
  selector = coerce_bleeding_edge(value)
533
571
  copy = dup
534
572
  copy.instance_variable_set(:@bleeding_edge, selector)
535
573
  copy.instance_variable_set(:@bleeding_edge_severity_overrides,
536
574
  BleedingEdge.severity_overrides_for(selector))
575
+ copy.instance_variable_set(:@bleeding_edge_active_ids, BleedingEdge.active_ids_for(selector))
537
576
  copy.freeze
538
577
  end
539
578
 
@@ -42,7 +42,8 @@ module Rigor
42
42
  "strscan",
43
43
  "prism", "rbs",
44
44
  # data/vendored_gem_sigs/
45
- "pg", "mysql2", "nokogiri", "bcrypt", "redis", "idn-ruby"
45
+ "pg", "mysql2", "nokogiri", "bcrypt", "redis", "idn-ruby", "racc",
46
+ "bundler", "rubygems"
46
47
  ].freeze
47
48
 
48
49
  # @param bundle_path [String, Pathname, nil] explicit path to the bundler install root. When `nil`,
@@ -6,10 +6,11 @@ module Rigor
6
6
  # unless the caller passes an explicit `libraries:` array. Each entry MUST be a stdlib library name
7
7
  # accepted by `RBS::EnvironmentLoader#has_library?`; unknown libraries MUST fail-soft
8
8
  # (`RbsLoader#build_env` already filters through `has_library?`). The default set covers the common
9
- # stdlib surface a Ruby program is likely to import (`pathname`, `optparse`, `json`, `yaml`, `fileutils`,
10
- # `tempfile`, `uri`, `logger`, `date`) plus the analyzer-adjacent gems shipping their own RBS in this
11
- # bundle (`prism`, `rbs`). On hosts where one of these libraries is not installed, the loader silently
12
- # drops it.
9
+ # stdlib surface a Ruby program is likely to import, plus the analyzer-adjacent gems shipping their own
10
+ # RBS in this bundle (`prism`, `rbs`). The list below is the enumeration no prose gloss restates a
11
+ # subset of it, here or in `docs/internal-spec/inference-engine.md` § Environment Surface, because a
12
+ # restated subset silently rots as entries are added. On hosts where one of these libraries is not
13
+ # installed, the loader silently drops it.
13
14
  #
14
15
  # Callers MAY add to the default by passing `libraries: %w[csv ...]`; the explicit list is appended to
15
16
  # `DEFAULT_LIBRARIES` and de-duplicated. Callers that need a strictly RBS-core view MUST construct an
@@ -32,7 +32,7 @@ module Rigor
32
32
  # hard-coding is acceptable; a directory walk at every call would add stat-cost to no benefit.)
33
33
  VENDORED_GEM_NAMES = Set[
34
34
  "ast", "bcrypt", "bundler", "cgi", "did_you_mean",
35
- "idn-ruby", "mysql2", "nokogiri", "pg", "prism", "redis", "rubygems"
35
+ "idn-ruby", "mysql2", "nokogiri", "pg", "prism", "racc", "redis", "rubygems"
36
36
  ].freeze
37
37
 
38
38
  # @param locked_gems [Hash{String => LockfileResolver::LockedGem}] The lockfile-resolved gem set. Empty