rigortype 0.1.15 → 0.1.17
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 +4 -2
- data/exe/rigor +19 -0
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +100 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +209 -0
- data/lib/rigor/analysis/check_rules.rb +174 -71
- data/lib/rigor/analysis/dependency_recorder.rb +122 -0
- data/lib/rigor/analysis/diagnostic.rb +58 -0
- data/lib/rigor/analysis/incremental.rb +162 -0
- data/lib/rigor/analysis/incremental_session.rb +337 -0
- data/lib/rigor/analysis/rule_catalog.rb +48 -0
- data/lib/rigor/analysis/runner.rb +485 -29
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +121 -0
- data/lib/rigor/analysis/worker_session.rb +3 -2
- data/lib/rigor/builtins/static_return_refinements.rb +7 -1
- data/lib/rigor/cache/descriptor.rb +56 -51
- data/lib/rigor/cache/incremental_snapshot.rb +147 -0
- data/lib/rigor/cache/rbs_cache_producer.rb +30 -0
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +2 -8
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -8
- data/lib/rigor/cache/rbs_constant_table.rb +2 -8
- data/lib/rigor/cache/rbs_environment.rb +2 -8
- data/lib/rigor/cache/rbs_instance_definitions.rb +3 -16
- data/lib/rigor/cache/rbs_known_class_names.rb +2 -8
- data/lib/rigor/cache/store.rb +99 -1
- data/lib/rigor/cli/annotate_command.rb +2 -7
- data/lib/rigor/cli/baseline_command.rb +2 -7
- data/lib/rigor/cli/command.rb +47 -0
- data/lib/rigor/cli/coverage_command.rb +3 -23
- data/lib/rigor/cli/coverage_renderer.rb +3 -8
- data/lib/rigor/cli/diff_command.rb +3 -7
- data/lib/rigor/cli/explain_command.rb +2 -7
- data/lib/rigor/cli/lsp_command.rb +3 -7
- data/lib/rigor/cli/mcp_command.rb +3 -7
- data/lib/rigor/cli/options.rb +57 -0
- data/lib/rigor/cli/plugin_command.rb +3 -7
- data/lib/rigor/cli/plugins_command.rb +52 -10
- data/lib/rigor/cli/plugins_renderer.rb +86 -1
- data/lib/rigor/cli/renderable.rb +26 -0
- data/lib/rigor/cli/sig_gen_command.rb +2 -7
- data/lib/rigor/cli/skill_command.rb +3 -7
- data/lib/rigor/cli/triage_command.rb +2 -7
- data/lib/rigor/cli/type_of_command.rb +5 -38
- data/lib/rigor/cli/type_of_renderer.rb +4 -9
- data/lib/rigor/cli/type_scan_command.rb +3 -23
- data/lib/rigor/cli/type_scan_renderer.rb +4 -9
- data/lib/rigor/cli.rb +260 -48
- data/lib/rigor/configuration/dependencies.rb +18 -1
- data/lib/rigor/configuration/severity_profile.rb +22 -3
- data/lib/rigor/configuration.rb +13 -3
- data/lib/rigor/environment/rbs_loader.rb +335 -4
- data/lib/rigor/environment.rb +8 -2
- data/lib/rigor/inference/block_parameter_binder.rb +1 -2
- data/lib/rigor/inference/budget_trace.rb +137 -0
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/comparable_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/complex_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/date_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/encoding_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/hash_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/method_catalog.rb +15 -0
- data/lib/rigor/inference/builtins/numeric_catalog.rb +21 -93
- data/lib/rigor/inference/builtins/pathname_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/proc_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/range_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/rational_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/re_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/set_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/string_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/struct_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/time_catalog.rb +2 -5
- data/lib/rigor/inference/expression_typer.rb +149 -22
- data/lib/rigor/inference/hkt_reducer.rb +2 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +5 -1
- data/lib/rigor/inference/method_dispatcher/call_context.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +11 -10
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +12 -6
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +246 -0
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +6 -2
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +4 -1
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +4 -1
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +6 -6
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +12 -7
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +23 -6
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +100 -23
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +9 -9
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -6
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +120 -9
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +12 -12
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +49 -0
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +6 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -9
- data/lib/rigor/inference/method_dispatcher.rb +147 -60
- data/lib/rigor/inference/narrowing.rb +202 -5
- data/lib/rigor/inference/precision_scanner.rb +60 -1
- data/lib/rigor/inference/scope_indexer.rb +257 -11
- data/lib/rigor/inference/statement_evaluator.rb +110 -26
- data/lib/rigor/inference/synthetic_method_index.rb +23 -4
- data/lib/rigor/inference/synthetic_method_scanner.rb +148 -14
- data/lib/rigor/language_server/buffer_resolution.rb +33 -0
- data/lib/rigor/language_server/completion_provider.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +4 -4
- data/lib/rigor/language_server/folding_range_provider.rb +4 -4
- data/lib/rigor/language_server/hover_provider.rb +4 -4
- data/lib/rigor/language_server/selection_range_provider.rb +4 -4
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/plugin/additional_initializer.rb +108 -0
- data/lib/rigor/plugin/base.rb +337 -2
- data/lib/rigor/plugin/box.rb +64 -0
- data/lib/rigor/plugin/inflector.rb +121 -0
- data/lib/rigor/plugin/isolation.rb +191 -0
- data/lib/rigor/plugin/macro/nested_class_template.rb +140 -0
- data/lib/rigor/plugin/macro.rb +1 -0
- data/lib/rigor/plugin/manifest.rb +120 -23
- data/lib/rigor/plugin/node_context.rb +62 -0
- data/lib/rigor/plugin/registry.rb +49 -1
- data/lib/rigor/plugin.rb +3 -0
- data/lib/rigor/rbs_extended/conformance_checker.rb +208 -0
- data/lib/rigor/rbs_extended.rb +39 -0
- data/lib/rigor/scope.rb +123 -9
- data/lib/rigor/sig_gen/generator.rb +2 -3
- data/lib/rigor/sig_gen/observation_collector.rb +2 -2
- data/lib/rigor/source/literals.rb +118 -0
- data/lib/rigor/source/node_walker.rb +26 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/type/acceptance_router.rb +19 -0
- data/lib/rigor/type/accepts_result.rb +3 -10
- data/lib/rigor/type/app.rb +3 -7
- data/lib/rigor/type/bot.rb +2 -3
- data/lib/rigor/type/bound_method.rb +5 -12
- data/lib/rigor/type/combinator.rb +23 -1
- data/lib/rigor/type/constant.rb +2 -3
- data/lib/rigor/type/data_class.rb +80 -0
- data/lib/rigor/type/data_instance.rb +100 -0
- data/lib/rigor/type/difference.rb +5 -10
- data/lib/rigor/type/dynamic.rb +5 -10
- data/lib/rigor/type/hash_shape.rb +5 -15
- data/lib/rigor/type/integer_range.rb +5 -10
- data/lib/rigor/type/intersection.rb +5 -10
- data/lib/rigor/type/nominal.rb +5 -10
- data/lib/rigor/type/refined.rb +5 -10
- data/lib/rigor/type/singleton.rb +5 -10
- data/lib/rigor/type/top.rb +2 -3
- data/lib/rigor/type/tuple.rb +5 -10
- data/lib/rigor/type/union.rb +69 -10
- data/lib/rigor/type.rb +2 -0
- data/lib/rigor/value_semantics.rb +77 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +2 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +31 -53
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +21 -23
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +38 -59
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +7 -13
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +22 -33
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +298 -413
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +69 -71
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +24 -34
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +18 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +4 -46
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +4 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +1 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +17 -12
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +2 -8
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +2 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +2 -6
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +4 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +40 -45
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +7 -17
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +20 -42
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +7 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +4 -8
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +188 -0
- data/plugins/rigor-mangrove/lib/rigor-mangrove.rb +3 -0
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +4 -0
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +24 -8
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +31 -48
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +21 -23
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +54 -82
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +25 -25
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +63 -147
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +4 -17
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +23 -114
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +48 -33
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +0 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +6 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +4 -2
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +13 -12
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +28 -40
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +44 -47
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +11 -10
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +45 -87
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +11 -12
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +29 -42
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +20 -19
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +73 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +43 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +21 -29
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +36 -96
- data/sig/rigor/cache.rbs +19 -0
- data/sig/rigor/inference.rbs +22 -0
- data/sig/rigor/plugin/access_denied_error.rbs +3 -1
- data/sig/rigor/plugin/base.rbs +58 -3
- data/sig/rigor/plugin/io_boundary.rbs +3 -0
- data/sig/rigor/plugin/manifest.rbs +31 -1
- data/sig/rigor/rbs_extended.rbs +2 -0
- data/sig/rigor/scope.rbs +5 -0
- data/sig/rigor/source.rbs +12 -0
- data/sig/rigor/type.rbs +58 -1
- data/sig/rigor.rbs +11 -1
- data/skills/rigor-plugin-author/SKILL.md +13 -9
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +6 -5
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +159 -75
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +3 -3
- metadata +73 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/inflector.rb +0 -114
data/lib/rigor/cli.rb
CHANGED
|
@@ -9,6 +9,7 @@ require_relative "configuration"
|
|
|
9
9
|
require_relative "version"
|
|
10
10
|
require_relative "analysis/diagnostic"
|
|
11
11
|
require_relative "analysis/result"
|
|
12
|
+
require_relative "cli/options"
|
|
12
13
|
|
|
13
14
|
module Rigor
|
|
14
15
|
# The CLI class is a dispatcher: each `run_*` method delegates to a
|
|
@@ -77,20 +78,19 @@ module Rigor
|
|
|
77
78
|
EXIT_USAGE
|
|
78
79
|
end
|
|
79
80
|
|
|
80
|
-
def run_check
|
|
81
|
-
|
|
82
|
-
require_relative "analysis/buffer_binding"
|
|
83
|
-
require_relative "analysis/baseline"
|
|
84
|
-
require_relative "cache/store"
|
|
85
|
-
|
|
81
|
+
def run_check # rubocop:disable Metrics/AbcSize
|
|
82
|
+
load_check_dependencies
|
|
86
83
|
options = parse_check_options
|
|
87
|
-
buffer = resolve_buffer_binding(options)
|
|
84
|
+
buffer = Options.resolve_buffer_binding(options, err: @err)
|
|
88
85
|
return EXIT_USAGE if buffer == :usage_error
|
|
89
86
|
|
|
90
87
|
configuration = load_check_configuration(options)
|
|
91
88
|
cache_root = configuration.cache_path
|
|
92
89
|
handle_clear_cache(cache_root) if options.fetch(:clear_cache)
|
|
93
90
|
|
|
91
|
+
special = dispatch_special_check_mode(configuration, options, cache_root)
|
|
92
|
+
return special unless special.nil?
|
|
93
|
+
|
|
94
94
|
runner = build_check_runner(
|
|
95
95
|
configuration: configuration, options: options,
|
|
96
96
|
buffer: buffer, cache_root: cache_root
|
|
@@ -100,6 +100,8 @@ module Rigor
|
|
|
100
100
|
|
|
101
101
|
write_result(result, options.fetch(:format))
|
|
102
102
|
write_run_stats(result.stats) if result.stats
|
|
103
|
+
write_trace_appendices
|
|
104
|
+
runner.cache_store&.evict!
|
|
103
105
|
write_cache_stats(cache_root, runner.cache_store) if options.fetch(:cache_stats)
|
|
104
106
|
|
|
105
107
|
exit_code = result.success? ? 0 : 1
|
|
@@ -107,6 +109,95 @@ module Rigor
|
|
|
107
109
|
exit_code
|
|
108
110
|
end
|
|
109
111
|
|
|
112
|
+
# ADR-46 — the two incremental-analysis check modes both fully handle
|
|
113
|
+
# the run and return an exit code (so `run_check` short-circuits);
|
|
114
|
+
# returns nil for an ordinary check.
|
|
115
|
+
def dispatch_special_check_mode(configuration, options, cache_root)
|
|
116
|
+
return run_verify_incremental(configuration) if options.fetch(:verify_incremental)
|
|
117
|
+
return run_incremental_check(configuration, options, cache_root) if options.fetch(:incremental)
|
|
118
|
+
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# ADR-46 — the incremental-analysis acceptance gate. Runs a baseline
|
|
123
|
+
# analysis (recording cross-file dependencies), then re-analyzes a
|
|
124
|
+
# representative subset of files and serves the rest from the per-file
|
|
125
|
+
# cache (the body tier), and asserts the merged diagnostics are
|
|
126
|
+
# byte-identical to a full `--no-cache` analysis. A mismatch means the
|
|
127
|
+
# incremental machinery would serve a stale — manufactured —
|
|
128
|
+
# diagnostic, the soundness failure this gate exists to catch. Prints a
|
|
129
|
+
# one-line PASS (exit 0) or the differing diagnostics (exit 1).
|
|
130
|
+
def run_verify_incremental(configuration)
|
|
131
|
+
paths = @argv.empty? ? nil : @argv
|
|
132
|
+
session = Analysis::IncrementalSession.new(configuration: configuration, paths: paths)
|
|
133
|
+
session.baseline
|
|
134
|
+
analyzed = session.analyzed_files
|
|
135
|
+
|
|
136
|
+
# Every other file forms the re-analyzed subset, so the run exercises
|
|
137
|
+
# BOTH the subset-analysis path and the cache-serving path.
|
|
138
|
+
subset = analyzed.each_with_index.select { |_, index| index.even? }.map(&:first)
|
|
139
|
+
incremental = normalize_diagnostics(session.reanalyze_subset(subset))
|
|
140
|
+
full = normalize_diagnostics(verify_full_diagnostics(configuration, paths))
|
|
141
|
+
|
|
142
|
+
report_verify_incremental(incremental, full, subset_size: subset.size, total: analyzed.size)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# ADR-46 — cross-process incremental analysis (`--incremental`). Derives
|
|
146
|
+
# the global fingerprint cheaply (no RBS env build), loads the disk
|
|
147
|
+
# snapshot, and on a fingerprint hit re-analyzes only the files changed
|
|
148
|
+
# since the last run (plus their dependents), serving the rest from the
|
|
149
|
+
# snapshot; on a miss runs a full baseline. Persists the updated
|
|
150
|
+
# snapshot for the next invocation. Diagnostics are identical to a full
|
|
151
|
+
# run (the `--verify-incremental` gate enforces this); the win is
|
|
152
|
+
# skipping per-file inference for unchanged files.
|
|
153
|
+
def run_incremental_check(configuration, options, cache_root)
|
|
154
|
+
paths = @argv.empty? ? nil : @argv
|
|
155
|
+
probe = Analysis::Runner.new(configuration: configuration, cache_store: nil)
|
|
156
|
+
files = paths ? probe.analysis_file_set(paths) : probe.analysis_file_set
|
|
157
|
+
fingerprint = Cache::IncrementalSnapshot.fingerprint(
|
|
158
|
+
configuration: configuration, roots: paths || configuration.paths
|
|
159
|
+
)
|
|
160
|
+
snapshot = Cache::IncrementalSnapshot.new(root: cache_root)
|
|
161
|
+
session = Analysis::IncrementalSession.new(configuration: configuration, paths: paths)
|
|
162
|
+
|
|
163
|
+
diagnostics, warm = session.run_incremental(snapshot: snapshot, fingerprint: fingerprint)
|
|
164
|
+
@err.puts("rigor: --incremental #{warm ? 'warm — reused cached diagnostics' : 'cold — full analysis'} " \
|
|
165
|
+
"(#{files.size} files)")
|
|
166
|
+
|
|
167
|
+
result = apply_baseline_filter(Analysis::Result.new(diagnostics: diagnostics, stats: nil), configuration, options)
|
|
168
|
+
write_result(result, options.fetch(:format))
|
|
169
|
+
result.success? ? 0 : 1
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def verify_full_diagnostics(configuration, paths)
|
|
173
|
+
runner = Analysis::Runner.new(configuration: configuration, cache_store: nil)
|
|
174
|
+
(paths ? runner.run(paths) : runner.run).diagnostics
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def normalize_diagnostics(diagnostics)
|
|
178
|
+
diagnostics.map(&:to_h).sort_by do |hash|
|
|
179
|
+
[hash["path"].to_s, hash["line"].to_i, hash["column"].to_i, hash["rule"].to_s, hash["message"].to_s]
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def report_verify_incremental(incremental, full, subset_size:, total:)
|
|
184
|
+
if incremental == full
|
|
185
|
+
@out.puts("rigor: --verify-incremental OK — incremental " \
|
|
186
|
+
"(#{subset_size}/#{total} files re-analyzed, rest from cache) " \
|
|
187
|
+
"matches full (#{full.size} diagnostics)")
|
|
188
|
+
return 0
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
only_incremental = incremental - full
|
|
192
|
+
only_full = full - incremental
|
|
193
|
+
@err.puts("rigor: --verify-incremental FAILED — incremental and full diagnostics differ.")
|
|
194
|
+
@err.puts(" incremental-only: #{only_incremental.size}, full-only: #{only_full.size}")
|
|
195
|
+
(only_incremental + only_full).first(10).each do |hash|
|
|
196
|
+
@err.puts(" #{hash['path']}:#{hash['line']}:#{hash['column']}: [#{hash['rule']}] #{hash['message']}")
|
|
197
|
+
end
|
|
198
|
+
1
|
|
199
|
+
end
|
|
200
|
+
|
|
110
201
|
# ADR-22 slice 5 — the `--baseline-strict` CI gate. When the
|
|
111
202
|
# flag is set, ANY baseline drift fails the run — not only
|
|
112
203
|
# excess drift (a bucket over threshold, which already fails
|
|
@@ -193,7 +284,14 @@ module Rigor
|
|
|
193
284
|
end
|
|
194
285
|
|
|
195
286
|
def build_check_runner(configuration:, options:, buffer:, cache_root:)
|
|
196
|
-
cache_store = options.fetch(:no_cache)
|
|
287
|
+
cache_store = if options.fetch(:no_cache)
|
|
288
|
+
nil
|
|
289
|
+
else
|
|
290
|
+
Cache::Store.new(
|
|
291
|
+
root: cache_root,
|
|
292
|
+
max_bytes: configuration.cache_max_bytes
|
|
293
|
+
)
|
|
294
|
+
end
|
|
197
295
|
Analysis::Runner.new(
|
|
198
296
|
configuration: configuration,
|
|
199
297
|
explain: options.fetch(:explain),
|
|
@@ -204,37 +302,6 @@ module Rigor
|
|
|
204
302
|
)
|
|
205
303
|
end
|
|
206
304
|
|
|
207
|
-
# Editor-mode CLI envelope. The `--tmp-file=PATH` /
|
|
208
|
-
# `--instead-of=PATH` pair binds an in-flight buffer file to
|
|
209
|
-
# the logical project path it represents (see
|
|
210
|
-
# `docs/design/20260516-editor-mode.md`). Both flags must
|
|
211
|
-
# appear together; either alone is a usage error. The
|
|
212
|
-
# physical file must be readable; missing-file is a usage
|
|
213
|
-
# error too so editors get one consistent failure shape.
|
|
214
|
-
#
|
|
215
|
-
# Returns:
|
|
216
|
-
# - `nil` when neither flag was supplied (legacy path).
|
|
217
|
-
# - `Rigor::Analysis::BufferBinding` when the pair is valid.
|
|
218
|
-
# - `:usage_error` after writing one diagnostic to stderr;
|
|
219
|
-
# the caller MUST translate this to `EXIT_USAGE`.
|
|
220
|
-
def resolve_buffer_binding(options)
|
|
221
|
-
tmp = options[:tmp_file]
|
|
222
|
-
instead = options[:instead_of]
|
|
223
|
-
return nil if tmp.nil? && instead.nil?
|
|
224
|
-
|
|
225
|
-
if tmp.nil? || instead.nil?
|
|
226
|
-
@err.puts("--tmp-file and --instead-of must appear together")
|
|
227
|
-
return :usage_error
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
unless File.file?(tmp)
|
|
231
|
-
@err.puts("--tmp-file #{tmp.inspect}: no such file or not readable")
|
|
232
|
-
return :usage_error
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
Analysis::BufferBinding.new(logical_path: instead, physical_path: tmp)
|
|
236
|
-
end
|
|
237
|
-
|
|
238
305
|
# ADR-15 Phase 4c — resolves the worker count by
|
|
239
306
|
# precedence: CLI `--workers=N` (most explicit) > env
|
|
240
307
|
# `RIGOR_RACTOR_WORKERS` > config `.rigor.yml`
|
|
@@ -296,7 +363,18 @@ module Rigor
|
|
|
296
363
|
# `.rigor.yml`. Intended for single-file / ad-hoc CI use;
|
|
297
364
|
# ordinary projects should configure the plugin in
|
|
298
365
|
# `.rigor.yml`.
|
|
299
|
-
treat_all_as_inline_rbs: false
|
|
366
|
+
treat_all_as_inline_rbs: false,
|
|
367
|
+
# ADR-46 — the incremental-analysis acceptance gate. Runs a
|
|
368
|
+
# baseline analysis, re-analyzes a subset and serves the rest from
|
|
369
|
+
# the per-file cache, and asserts the merged diagnostics are
|
|
370
|
+
# byte-identical to a full `--no-cache` run. Exits non-zero on any
|
|
371
|
+
# mismatch. Off by default.
|
|
372
|
+
verify_incremental: false,
|
|
373
|
+
# ADR-46 — cross-process incremental analysis. With a disk snapshot
|
|
374
|
+
# of the prior run's per-file diagnostics + dependency graph,
|
|
375
|
+
# re-analyzes only the changed closure and serves the rest from the
|
|
376
|
+
# snapshot. Off by default.
|
|
377
|
+
incremental: false
|
|
300
378
|
}
|
|
301
379
|
parser = OptionParser.new do |opts| # rubocop:disable Metrics/BlockLength
|
|
302
380
|
opts.banner = "Usage: rigor check [options] [paths]"
|
|
@@ -314,14 +392,7 @@ module Rigor
|
|
|
314
392
|
"Dispatch per-file analysis across N Ractor workers (default: 0; sequential)") do |value|
|
|
315
393
|
options[:workers] = value
|
|
316
394
|
end
|
|
317
|
-
|
|
318
|
-
"Editor mode: read source bytes from PATH instead of --instead-of (paired)") do |value|
|
|
319
|
-
options[:tmp_file] = value
|
|
320
|
-
end
|
|
321
|
-
opts.on("--instead-of=PATH",
|
|
322
|
-
"Editor mode: the logical project path the buffer represents (paired with --tmp-file)") do |value|
|
|
323
|
-
options[:instead_of] = value
|
|
324
|
-
end
|
|
395
|
+
Options.add_editor_mode(opts, options)
|
|
325
396
|
opts.on("--baseline=PATH",
|
|
326
397
|
"ADR-22: load baseline from PATH (overrides .rigor.yml `baseline:`)") do |value|
|
|
327
398
|
options[:baseline] = value
|
|
@@ -338,6 +409,14 @@ module Rigor
|
|
|
338
409
|
"ADR-32: force-load rigor-rbs-inline with require_magic_comment: false") do
|
|
339
410
|
options[:treat_all_as_inline_rbs] = true
|
|
340
411
|
end
|
|
412
|
+
opts.on("--verify-incremental",
|
|
413
|
+
"ADR-46: assert incremental analysis matches a full run, then exit") do
|
|
414
|
+
options[:verify_incremental] = true
|
|
415
|
+
end
|
|
416
|
+
opts.on("--incremental",
|
|
417
|
+
"ADR-46: re-analyze only files changed since the last run (cross-process cache)") do
|
|
418
|
+
options[:incremental] = true
|
|
419
|
+
end
|
|
341
420
|
end
|
|
342
421
|
parser.parse!(@argv)
|
|
343
422
|
options
|
|
@@ -402,6 +481,139 @@ module Rigor
|
|
|
402
481
|
stats.format(@err)
|
|
403
482
|
end
|
|
404
483
|
|
|
484
|
+
# Opt-in developer diagnostics printed after the run: the
|
|
485
|
+
# inference-cutoff trace (RIGOR_BUDGET_TRACE) and the heap-attribution
|
|
486
|
+
# profile (RIGOR_HEAP_PROFILE). Each gates itself, so this is a no-op
|
|
487
|
+
# on a normal run.
|
|
488
|
+
def write_trace_appendices
|
|
489
|
+
write_budget_trace
|
|
490
|
+
write_heap_profile
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
# Dumps the opt-in inference-cutoff counters (RIGOR_BUDGET_TRACE).
|
|
494
|
+
# These are the hard-coded "budget" guards that silently degrade
|
|
495
|
+
# to `Dynamic[top]` / a fallback bound — counting them shows where
|
|
496
|
+
# inference actually stopped. Process-global counters: meaningful
|
|
497
|
+
# only on a single-process run (`--workers 0`), since they do not
|
|
498
|
+
# cross fork boundaries.
|
|
499
|
+
def write_budget_trace
|
|
500
|
+
return unless Inference::BudgetTrace.enabled?
|
|
501
|
+
|
|
502
|
+
counts = Inference::BudgetTrace.snapshot
|
|
503
|
+
@err.puts("")
|
|
504
|
+
@err.puts("Inference cutoffs (RIGOR_BUDGET_TRACE; --workers 0 for an exact count)")
|
|
505
|
+
@err.puts(" recursion-guard hits: #{counts[Inference::BudgetTrace::RECURSION_GUARD]}")
|
|
506
|
+
@err.puts(" ancestor-walk-limit hits: #{counts[Inference::BudgetTrace::ANCESTOR_WALK_LIMIT]}")
|
|
507
|
+
@err.puts(" hkt-fuel-exhausted hits: #{counts[Inference::BudgetTrace::HKT_FUEL_EXHAUSTED]}")
|
|
508
|
+
write_budget_distributions
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
# Dumps the read-only size distributions (ADR-41 Slice 2a). These
|
|
512
|
+
# observe how large unions actually get, with no cap enforced — the
|
|
513
|
+
# data the `union_size` budget default should be chosen from. The
|
|
514
|
+
# `over` thresholds bracket the TypeProf prior (10) and Rigor's spec
|
|
515
|
+
# default (24).
|
|
516
|
+
def write_budget_distributions
|
|
517
|
+
summary = Inference::BudgetTrace.summarize(Inference::BudgetTrace::UNION_ARITY, over: [10, 24, 40])
|
|
518
|
+
pct = summary[:percentiles]
|
|
519
|
+
@err.puts(" union arity: n=#{summary[:count]} max=#{summary[:max]} " \
|
|
520
|
+
"p50=#{pct[:p50]} p90=#{pct[:p90]} p99=#{pct[:p99]}")
|
|
521
|
+
over = summary[:over]
|
|
522
|
+
@err.puts(" unions ≥10: #{over[10]} ≥24: #{over[24]} ≥40: #{over[40]}")
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# Dumps a live-heap class breakdown (RIGOR_HEAP_PROFILE) — retained
|
|
526
|
+
# objects by class after a forced GC, ranked by total memsize. The
|
|
527
|
+
# tool for attributing where the analyzer's resident memory goes
|
|
528
|
+
# (ADR-41 Slice 2b): it answers whether the heap is type carriers,
|
|
529
|
+
# RBS objects, Prism nodes, or fact-store Hashes/Strings. Walking the
|
|
530
|
+
# whole heap is slow — a dev probe, not a normal diagnostic. Run
|
|
531
|
+
# single-process (`--workers 0`) so the parent heap is the analysis
|
|
532
|
+
# heap; the gem is required lazily so a normal run never loads it.
|
|
533
|
+
def write_heap_profile
|
|
534
|
+
return if ENV["RIGOR_HEAP_PROFILE"].to_s.empty?
|
|
535
|
+
|
|
536
|
+
by_class, total = tally_live_heap
|
|
537
|
+
@err.puts("")
|
|
538
|
+
@err.puts("Heap profile (RIGOR_HEAP_PROFILE; live objects after GC, by class)")
|
|
539
|
+
@err.puts(" total tracked: #{heap_mb(total)} across #{by_class.size} classes")
|
|
540
|
+
by_class.sort_by { |_, (_, bytes)| -bytes }.first(30).each do |name, (count, bytes)|
|
|
541
|
+
@err.puts(" #{heap_mb(bytes).rjust(10)} #{count.to_s.rjust(9)} obj #{name}")
|
|
542
|
+
end
|
|
543
|
+
write_string_allocation_sites
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# Loads the analysis-path dependencies lazily (so non-check commands
|
|
547
|
+
# stay light) and starts heap-allocation tracing if requested, before
|
|
548
|
+
# any analysis object is allocated.
|
|
549
|
+
def load_check_dependencies
|
|
550
|
+
require_relative "analysis/runner"
|
|
551
|
+
require_relative "analysis/buffer_binding"
|
|
552
|
+
require_relative "analysis/baseline"
|
|
553
|
+
require_relative "cache/store"
|
|
554
|
+
start_heap_trace_if_requested
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
# Starts allocation tracing (RIGOR_HEAP_TRACE) as early as possible so
|
|
558
|
+
# the heap profile can attribute retained Strings to their allocation
|
|
559
|
+
# `file:line`. Very high overhead — run on a small file subset only.
|
|
560
|
+
def start_heap_trace_if_requested
|
|
561
|
+
return if ENV["RIGOR_HEAP_TRACE"].to_s.empty?
|
|
562
|
+
|
|
563
|
+
require "objspace"
|
|
564
|
+
ObjectSpace.trace_object_allocations_start
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
# When RIGOR_HEAP_TRACE is on, groups the live String objects by their
|
|
568
|
+
# allocation site (`sourcefile:sourceline`) and prints the top sites by
|
|
569
|
+
# count — pinpointing which engine code retains the millions of strings
|
|
570
|
+
# that dominate the large-app heap (ADR-41 Slice 2b). Strings allocated
|
|
571
|
+
# before tracing started report `(pre-trace)`.
|
|
572
|
+
def write_string_allocation_sites
|
|
573
|
+
return if ENV["RIGOR_HEAP_TRACE"].to_s.empty?
|
|
574
|
+
|
|
575
|
+
by_site = Hash.new(0)
|
|
576
|
+
ObjectSpace.each_object(String) do |str|
|
|
577
|
+
file = ObjectSpace.allocation_sourcefile(str)
|
|
578
|
+
line = ObjectSpace.allocation_sourceline(str)
|
|
579
|
+
by_site[file ? "#{file}:#{line}" : "(pre-trace)"] += 1
|
|
580
|
+
end
|
|
581
|
+
@err.puts("")
|
|
582
|
+
@err.puts(" String allocation sites (top 25 by live count)")
|
|
583
|
+
by_site.sort_by { |_, n| -n }.first(25).each do |site, n|
|
|
584
|
+
@err.puts(" #{n.to_s.rjust(9)} #{site}")
|
|
585
|
+
end
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
# Walks the whole live heap (after a forced GC) and tallies
|
|
589
|
+
# `{class_name => [count, memsize]}` plus the grand total. Returns
|
|
590
|
+
# `[by_class, total]`. Slow — a dev probe only.
|
|
591
|
+
def tally_live_heap
|
|
592
|
+
require "objspace"
|
|
593
|
+
GC.start
|
|
594
|
+
by_class = Hash.new { |h, k| h[k] = [0, 0] }
|
|
595
|
+
total = 0
|
|
596
|
+
ObjectSpace.each_object do |obj|
|
|
597
|
+
size = ObjectSpace.memsize_of(obj)
|
|
598
|
+
entry = by_class[heap_class_name(obj)]
|
|
599
|
+
entry[0] += 1
|
|
600
|
+
entry[1] += size
|
|
601
|
+
total += size
|
|
602
|
+
end
|
|
603
|
+
[by_class, total]
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
def heap_class_name(obj)
|
|
607
|
+
klass = Object.instance_method(:class).bind_call(obj)
|
|
608
|
+
klass.name || klass.inspect
|
|
609
|
+
rescue StandardError
|
|
610
|
+
"(unknown)"
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
def heap_mb(bytes)
|
|
614
|
+
Kernel.format("%.1f MB", bytes / 1_048_576.0)
|
|
615
|
+
end
|
|
616
|
+
|
|
405
617
|
def write_cache_stats(cache_root, runtime_store)
|
|
406
618
|
inv = Cache::Store.disk_inventory(root: cache_root)
|
|
407
619
|
|
|
@@ -77,7 +77,7 @@ module Rigor
|
|
|
77
77
|
return new([]) if data.nil?
|
|
78
78
|
raise ArgumentError, "dependencies: must be a Hash, got #{data.inspect}" unless data.is_a?(Hash)
|
|
79
79
|
|
|
80
|
-
raw_entries =
|
|
80
|
+
raw_entries = coerce_source_inference(data["source_inference"])
|
|
81
81
|
entries, warnings = dedupe_entries(raw_entries)
|
|
82
82
|
budget = coerce_budget_per_gem(data.fetch("budget_per_gem", DEFAULT_BUDGET_PER_GEM))
|
|
83
83
|
strategy = coerce_budget_overrun_strategy(
|
|
@@ -158,6 +158,23 @@ module Rigor
|
|
|
158
158
|
|
|
159
159
|
private
|
|
160
160
|
|
|
161
|
+
# `source_inference:` is a list of per-gem entries, or `false` /
|
|
162
|
+
# omitted to disable it (the default). Guard the Ruby
|
|
163
|
+
# `Array(false) == [false]` quirk that would otherwise feed `false`
|
|
164
|
+
# straight into coerce_entry and crash the whole run on a perfectly
|
|
165
|
+
# reasonable "off" config (`dependencies: { source_inference: false }`).
|
|
166
|
+
def coerce_source_inference(value)
|
|
167
|
+
return [] if value.nil? || value == false
|
|
168
|
+
|
|
169
|
+
unless value.is_a?(Array)
|
|
170
|
+
raise ArgumentError,
|
|
171
|
+
"dependencies.source_inference: must be a list of entries " \
|
|
172
|
+
"(or false / omitted to disable), got #{value.inspect}"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
value.map { |raw| coerce_entry(raw) }
|
|
176
|
+
end
|
|
177
|
+
|
|
161
178
|
def coerce_entry(raw)
|
|
162
179
|
unless raw.is_a?(Hash)
|
|
163
180
|
raise ArgumentError,
|
|
@@ -39,6 +39,7 @@ module Rigor
|
|
|
39
39
|
PROFILES = {
|
|
40
40
|
lenient: {
|
|
41
41
|
"call.undefined-method" => :error,
|
|
42
|
+
"call.self-undefined-method" => :off,
|
|
42
43
|
"call.unresolved-toplevel" => :off,
|
|
43
44
|
"call.wrong-arity" => :error,
|
|
44
45
|
"call.argument-type-mismatch" => :warning,
|
|
@@ -47,6 +48,7 @@ module Rigor
|
|
|
47
48
|
"flow.unreachable-branch" => :info,
|
|
48
49
|
"flow.dead-assignment" => :info,
|
|
49
50
|
"flow.always-truthy-condition" => :info,
|
|
51
|
+
"flow.unreachable-clause" => :info,
|
|
50
52
|
"assert.type-mismatch" => :error,
|
|
51
53
|
"dump.type" => :info,
|
|
52
54
|
"def.return-type-mismatch" => :warning,
|
|
@@ -54,10 +56,15 @@ module Rigor
|
|
|
54
56
|
"def.override-visibility-reduced" => :off,
|
|
55
57
|
"def.override-return-widened" => :off,
|
|
56
58
|
"def.override-param-narrowed" => :off,
|
|
57
|
-
"def.ivar-write-mismatch" => :warning
|
|
59
|
+
"def.ivar-write-mismatch" => :warning,
|
|
60
|
+
# Opt-in author assertion: you only see it if you wrote a
|
|
61
|
+
# `conforms-to` directive, so it stays a :warning even in
|
|
62
|
+
# lenient — it is never unsolicited noise.
|
|
63
|
+
"rbs_extended.unsatisfied-conformance" => :warning
|
|
58
64
|
}.freeze,
|
|
59
65
|
balanced: {
|
|
60
66
|
"call.undefined-method" => :error,
|
|
67
|
+
"call.self-undefined-method" => :off,
|
|
61
68
|
"call.unresolved-toplevel" => :warning,
|
|
62
69
|
"call.wrong-arity" => :error,
|
|
63
70
|
"call.argument-type-mismatch" => :error,
|
|
@@ -66,6 +73,11 @@ module Rigor
|
|
|
66
73
|
"flow.unreachable-branch" => :warning,
|
|
67
74
|
"flow.dead-assignment" => :warning,
|
|
68
75
|
"flow.always-truthy-condition" => :warning,
|
|
76
|
+
# ADR-47 WD4: stays :info (not :warning like its siblings) in the
|
|
77
|
+
# default balanced profile until the regression-corpus FP gate is
|
|
78
|
+
# green; promote to :warning once Mastodon/GitLab/Redmine triage
|
|
79
|
+
# to zero net false positives.
|
|
80
|
+
"flow.unreachable-clause" => :info,
|
|
69
81
|
"assert.type-mismatch" => :error,
|
|
70
82
|
"dump.type" => :info,
|
|
71
83
|
"def.return-type-mismatch" => :warning,
|
|
@@ -73,10 +85,12 @@ module Rigor
|
|
|
73
85
|
"def.override-visibility-reduced" => :warning,
|
|
74
86
|
"def.override-return-widened" => :warning,
|
|
75
87
|
"def.override-param-narrowed" => :warning,
|
|
76
|
-
"def.ivar-write-mismatch" => :warning
|
|
88
|
+
"def.ivar-write-mismatch" => :warning,
|
|
89
|
+
"rbs_extended.unsatisfied-conformance" => :warning
|
|
77
90
|
}.freeze,
|
|
78
91
|
strict: {
|
|
79
92
|
"call.undefined-method" => :error,
|
|
93
|
+
"call.self-undefined-method" => :off,
|
|
80
94
|
"call.unresolved-toplevel" => :error,
|
|
81
95
|
"call.wrong-arity" => :error,
|
|
82
96
|
"call.argument-type-mismatch" => :error,
|
|
@@ -85,6 +99,10 @@ module Rigor
|
|
|
85
99
|
"flow.unreachable-branch" => :error,
|
|
86
100
|
"flow.dead-assignment" => :error,
|
|
87
101
|
"flow.always-truthy-condition" => :error,
|
|
102
|
+
# ADR-47: strict opts into the new rule at :warning (one notch
|
|
103
|
+
# below its :error siblings) while it proves out — see the
|
|
104
|
+
# balanced-profile note above.
|
|
105
|
+
"flow.unreachable-clause" => :warning,
|
|
88
106
|
"assert.type-mismatch" => :error,
|
|
89
107
|
"dump.type" => :error,
|
|
90
108
|
"def.return-type-mismatch" => :error,
|
|
@@ -92,7 +110,8 @@ module Rigor
|
|
|
92
110
|
"def.override-visibility-reduced" => :error,
|
|
93
111
|
"def.override-return-widened" => :error,
|
|
94
112
|
"def.override-param-narrowed" => :error,
|
|
95
|
-
"def.ivar-write-mismatch" => :error
|
|
113
|
+
"def.ivar-write-mismatch" => :error,
|
|
114
|
+
"rbs_extended.unsatisfied-conformance" => :error
|
|
96
115
|
}.freeze
|
|
97
116
|
}.freeze
|
|
98
117
|
|
data/lib/rigor/configuration.rb
CHANGED
|
@@ -69,7 +69,13 @@ module Rigor
|
|
|
69
69
|
"baseline" => nil,
|
|
70
70
|
"fold_platform_specific_paths" => false,
|
|
71
71
|
"cache" => {
|
|
72
|
-
"path" => ".rigor/cache"
|
|
72
|
+
"path" => ".rigor/cache",
|
|
73
|
+
# LRU eviction cap in bytes. nil (the default) disables eviction;
|
|
74
|
+
# the cache grows until the user runs `rigor check --clear-cache`.
|
|
75
|
+
# Set to a positive integer (e.g. 536870912 for 512 MB) to keep the
|
|
76
|
+
# cache bounded — the least-recently-used entries are removed at the
|
|
77
|
+
# end of each run when the total exceeds this limit.
|
|
78
|
+
"max_bytes" => nil
|
|
73
79
|
},
|
|
74
80
|
"plugins_io" => {
|
|
75
81
|
"network" => "disabled",
|
|
@@ -166,7 +172,8 @@ module Rigor
|
|
|
166
172
|
PATH_KEYS = %w[paths signature_paths pre_eval].freeze
|
|
167
173
|
private_constant :PATH_KEYS
|
|
168
174
|
|
|
169
|
-
attr_reader :target_ruby, :paths, :exclude_patterns, :plugins, :cache_path, :
|
|
175
|
+
attr_reader :target_ruby, :paths, :exclude_patterns, :plugins, :cache_path, :cache_max_bytes,
|
|
176
|
+
:disabled_rules,
|
|
170
177
|
:libraries, :signature_paths, :fold_platform_specific_paths,
|
|
171
178
|
:plugins_io_network, :plugins_io_allowed_paths,
|
|
172
179
|
:plugins_io_allowed_url_hosts,
|
|
@@ -334,6 +341,8 @@ module Rigor
|
|
|
334
341
|
"fold_platform_specific_paths", DEFAULTS.fetch("fold_platform_specific_paths")
|
|
335
342
|
) == true
|
|
336
343
|
@cache_path = cache.fetch("path").to_s
|
|
344
|
+
raw_max = cache.fetch("max_bytes")
|
|
345
|
+
@cache_max_bytes = raw_max.nil? ? nil : Integer(raw_max)
|
|
337
346
|
@plugins_io_network = coerce_network_policy(plugins_io.fetch("network"))
|
|
338
347
|
@plugins_io_allowed_paths = Array(plugins_io.fetch("allowed_paths")).map(&:to_s).freeze
|
|
339
348
|
@plugins_io_allowed_url_hosts = Array(plugins_io.fetch("allowed_url_hosts")).map(&:to_s).freeze
|
|
@@ -383,7 +392,8 @@ module Rigor
|
|
|
383
392
|
"pre_eval" => pre_eval,
|
|
384
393
|
"fold_platform_specific_paths" => fold_platform_specific_paths,
|
|
385
394
|
"cache" => {
|
|
386
|
-
"path" => cache_path
|
|
395
|
+
"path" => cache_path,
|
|
396
|
+
"max_bytes" => cache_max_bytes
|
|
387
397
|
},
|
|
388
398
|
"plugins_io" => {
|
|
389
399
|
"network" => plugins_io_network.to_s,
|