rigortype 0.3.1 → 0.3.3
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 +10 -10
- data/data/core_overlay/pathname.rbs +5 -0
- data/data/core_overlay/resolv.rbs +31 -0
- data/data/core_overlay/string_scanner.rbs +5 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +10 -6
- data/data/vendored_gem_sigs/bundler/bundler.rbs +39 -27
- data/data/vendored_gem_sigs/cgi/cgi_extras.rbs +6 -0
- data/data/vendored_gem_sigs/prism/prism_supplement.rbs +7 -0
- data/data/vendored_gem_sigs/racc/racc.rbs +67 -0
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +20 -9
- data/docs/manual/02-cli-reference.md +71 -11
- data/docs/manual/03-configuration.md +16 -1
- data/docs/manual/04-diagnostics.md +8 -0
- data/docs/manual/12-caching.md +17 -5
- data/docs/manual/15-type-protection-coverage.md +122 -1
- data/docs/manual/plugins/rigor-rbs-inline.md +22 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +8 -0
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +53 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -0
- data/lib/rigor/analysis/check_rules/rule_walk.rb +7 -0
- data/lib/rigor/analysis/check_rules.rb +107 -28
- data/lib/rigor/analysis/incremental_session.rb +156 -11
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +14 -0
- data/lib/rigor/analysis/rule_catalog.rb +5 -1
- data/lib/rigor/analysis/run_cache_key.rb +17 -1
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +201 -0
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +28 -10
- data/lib/rigor/analysis/runner/pool_coordinator.rb +7 -10
- data/lib/rigor/analysis/runner/project_pre_passes.rb +3 -1
- data/lib/rigor/analysis/runner.rb +78 -7
- data/lib/rigor/analysis/severity_stamp.rb +2 -1
- data/lib/rigor/bleeding_edge.rb +132 -7
- data/lib/rigor/cache/descriptor.rb +6 -1
- data/lib/rigor/cache/engine_source.rb +162 -0
- data/lib/rigor/cache/incremental_snapshot.rb +87 -18
- data/lib/rigor/cli/check_command.rb +49 -28
- data/lib/rigor/cli/coverage_command.rb +15 -2
- data/lib/rigor/cli/coverage_mutation.rb +242 -11
- data/lib/rigor/cli/fused_protection_renderer.rb +10 -0
- data/lib/rigor/cli/fused_protection_report.rb +12 -3
- data/lib/rigor/cli/mutation_fork_scan.rb +64 -0
- data/lib/rigor/cli/mutation_protection_renderer.rb +12 -0
- data/lib/rigor/cli/mutation_protection_report.rb +26 -5
- data/lib/rigor/cli/protection_renderer.rb +13 -0
- data/lib/rigor/cli/protection_report.rb +11 -3
- data/lib/rigor/cli/show_bleedingedge_command.rb +17 -4
- data/lib/rigor/cli/sig_gen_command.rb +5 -4
- data/lib/rigor/configuration.rb +45 -6
- data/lib/rigor/environment/bundle_sig_discovery.rb +2 -1
- data/lib/rigor/environment/rbs_coverage_report.rb +1 -1
- data/lib/rigor/environment/rbs_loader.rb +434 -75
- data/lib/rigor/environment.rb +28 -13
- data/lib/rigor/inference/anonymous_meta_class.rb +81 -0
- data/lib/rigor/inference/expression_typer.rb +62 -4
- data/lib/rigor/inference/fork_map.rb +47 -4
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +50 -2
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +35 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +158 -12
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +64 -5
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +234 -11
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +3 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +124 -1
- data/lib/rigor/inference/method_dispatcher.rb +41 -10
- data/lib/rigor/inference/mutation_widening.rb +30 -25
- data/lib/rigor/inference/narrowing.rb +3 -1
- data/lib/rigor/inference/optimistic_origin.rb +123 -0
- data/lib/rigor/inference/protection_scanner.rb +18 -2
- data/lib/rigor/inference/receiver_alias.rb +57 -0
- data/lib/rigor/inference/scope_indexer.rb +197 -55
- data/lib/rigor/inference/singleton_object_constant.rb +63 -0
- data/lib/rigor/inference/statement_evaluator.rb +55 -4
- data/lib/rigor/language_server/buffer_table.rb +22 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +203 -4
- data/lib/rigor/language_server/project_context.rb +45 -0
- data/lib/rigor/language_server/publish_batcher.rb +82 -0
- data/lib/rigor/language_server/server.rb +20 -1
- data/lib/rigor/language_server.rb +1 -0
- data/lib/rigor/plugin/node_rule_walk.rb +6 -0
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +9 -3
- data/lib/rigor/protection/closure_kill_oracle.rb +190 -0
- data/lib/rigor/protection/dependency_closure.rb +59 -0
- data/lib/rigor/protection/diagnostic_oracle.rb +12 -8
- data/lib/rigor/protection/discovery_seed.rb +116 -0
- data/lib/rigor/protection/kill_signature.rb +31 -0
- data/lib/rigor/protection/mutation_cache.rb +355 -0
- data/lib/rigor/protection/mutation_scanner.rb +55 -11
- data/lib/rigor/protection/mutator.rb +26 -4
- data/lib/rigor/runtime/jit.rb +63 -1
- data/lib/rigor/scope/discovery_index.rb +9 -0
- data/lib/rigor/scope.rb +114 -10
- data/lib/rigor/sig_gen/generator.rb +130 -31
- data/lib/rigor/sig_gen/layout_index.rb +6 -0
- data/lib/rigor/sig_gen/meta_class_shape.rb +84 -0
- data/lib/rigor/sig_gen/renderer.rb +20 -1
- data/lib/rigor/sig_gen/write_result.rb +6 -4
- data/lib/rigor/sig_gen/writer.rb +12 -0
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/source/node_walker.rb +12 -0
- data/lib/rigor/type/anonymous_class_name.rb +40 -0
- data/lib/rigor/type/nominal.rb +5 -2
- data/lib/rigor/type/singleton.rb +4 -1
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +6 -6
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/result_shape.rb +146 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +177 -12
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +74 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +257 -0
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +69 -0
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +87 -11
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +34 -4
- data/sig/rigor/inference/void_origin.rbs +6 -3
- data/sig/rigor/scope.rbs +12 -0
- data/sig/rigor.rbs +4 -0
- metadata +21 -1
|
@@ -13,8 +13,9 @@ module Rigor
|
|
|
13
13
|
#
|
|
14
14
|
# Prints the bleeding-edge overlay — the Rigor-maintained set of the next major's queued changes
|
|
15
15
|
# ({Rigor::BleedingEdge}) — as an explicit list, and reports which of them the project's `bleeding_edge:`
|
|
16
|
-
# configuration adopts.
|
|
17
|
-
#
|
|
16
|
+
# configuration adopts. Each feature is rendered with its kind: a `severity` feature prints the rule → severity
|
|
17
|
+
# diff it imposes, and a `behaviour` feature has no such diff to print, so its summary carries the whole
|
|
18
|
+
# explanation. Ids that have already graduated to default-on (ADR-50 § WD7) are listed after the overlay.
|
|
18
19
|
#
|
|
19
20
|
# Read-only: it loads `.rigor.yml` to resolve the active selection but runs no analysis.
|
|
20
21
|
class ShowBleedingedgeCommand < Command
|
|
@@ -62,7 +63,8 @@ module Rigor
|
|
|
62
63
|
"overlay" => BleedingEdge.features.map(&:to_h),
|
|
63
64
|
"selector" => configuration.to_h.fetch("bleeding_edge"),
|
|
64
65
|
"active" => BleedingEdge.active_features(selector).map(&:id),
|
|
65
|
-
"unknown_selected" => BleedingEdge.unknown_selected_ids(selector)
|
|
66
|
+
"unknown_selected" => BleedingEdge.unknown_selected_ids(selector),
|
|
67
|
+
"graduated" => BleedingEdge::GRADUATED
|
|
66
68
|
))
|
|
67
69
|
end
|
|
68
70
|
|
|
@@ -74,6 +76,7 @@ module Rigor
|
|
|
74
76
|
else
|
|
75
77
|
render_overlay
|
|
76
78
|
end
|
|
79
|
+
render_graduated
|
|
77
80
|
@out.puts("")
|
|
78
81
|
render_selection(configuration)
|
|
79
82
|
end
|
|
@@ -88,7 +91,7 @@ module Rigor
|
|
|
88
91
|
@out.puts("#{BleedingEdge.features.length} feature(s) queued for the next major:")
|
|
89
92
|
@out.puts("")
|
|
90
93
|
BleedingEdge.features.each do |feature|
|
|
91
|
-
@out.puts(" #{feature.id}")
|
|
94
|
+
@out.puts(" #{feature.id} [#{feature.kind}]")
|
|
92
95
|
@out.puts(" #{feature.summary}")
|
|
93
96
|
feature.severity_overrides.each do |rule, severity|
|
|
94
97
|
@out.puts(" severity: #{rule} → :#{severity}")
|
|
@@ -96,6 +99,16 @@ module Rigor
|
|
|
96
99
|
end
|
|
97
100
|
end
|
|
98
101
|
|
|
102
|
+
# ADR-50 § WD7 — only printed when something has graduated, so a release with an empty list reads exactly
|
|
103
|
+
# as it did before graduation existed.
|
|
104
|
+
def render_graduated
|
|
105
|
+
return if BleedingEdge::GRADUATED.empty?
|
|
106
|
+
|
|
107
|
+
@out.puts("")
|
|
108
|
+
@out.puts("Graduated — on by default, no longer selectable:")
|
|
109
|
+
BleedingEdge::GRADUATED.each { |id| @out.puts(" #{id}") }
|
|
110
|
+
end
|
|
111
|
+
|
|
99
112
|
def render_selection(configuration)
|
|
100
113
|
selector = configuration.bleeding_edge
|
|
101
114
|
active = BleedingEdge.active_features(selector).map(&:id)
|
|
@@ -97,10 +97,11 @@ module Rigor
|
|
|
97
97
|
results = writer.write_all(candidates)
|
|
98
98
|
|
|
99
99
|
SigGen::Renderer.new(out: @out).render_write(results: results, format: options.fetch(:format))
|
|
100
|
-
#
|
|
101
|
-
# did not get one, so the command must not report
|
|
102
|
-
# otherwise mean nothing.
|
|
103
|
-
|
|
100
|
+
# A refused write (an assembled file that does not parse, or an existing target that is not valid
|
|
101
|
+
# UTF-8) means the user asked for a write and did not get one, so the command must not report
|
|
102
|
+
# success — a green `sig-gen --write` in CI would otherwise mean nothing.
|
|
103
|
+
refusals = %i[skipped_invalid_rbs skipped_invalid_encoding]
|
|
104
|
+
results.any? { |result| refusals.include?(result.action) } ? 1 : 0
|
|
104
105
|
end
|
|
105
106
|
|
|
106
107
|
# Slice 3 — collect call-site argument observations when `--params=observed` is set. When `--observe=PATH` is not
|
data/lib/rigor/configuration.rb
CHANGED
|
@@ -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.
|
|
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).
|
|
98
|
-
#
|
|
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
|
|
528
|
-
# (every other ivar is the receiver's deeply-frozen value, safe to share read-only), the
|
|
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`,
|
|
@@ -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
|