rigortype 0.1.7 → 0.1.9
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 +186 -513
- data/lib/rigor/analysis/check_rules.rb +23 -1
- data/lib/rigor/analysis/diagnostic.rb +17 -3
- data/lib/rigor/analysis/runner.rb +178 -3
- data/lib/rigor/analysis/worker_session.rb +14 -3
- data/lib/rigor/cli/annotate_command.rb +224 -0
- data/lib/rigor/cli/baseline_command.rb +36 -16
- data/lib/rigor/cli/prism_colorizer.rb +111 -0
- data/lib/rigor/cli/triage_command.rb +83 -0
- data/lib/rigor/cli/triage_renderer.rb +77 -0
- data/lib/rigor/cli.rb +71 -5
- data/lib/rigor/environment.rb +9 -1
- data/lib/rigor/inference/builtins/method_catalog.rb +17 -1
- data/lib/rigor/inference/builtins/time_catalog.rb +10 -1
- data/lib/rigor/inference/expression_typer.rb +300 -18
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +109 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +173 -10
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +53 -1
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +149 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +20 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +33 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +81 -0
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +81 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +316 -2
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +126 -0
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +67 -0
- data/lib/rigor/inference/method_dispatcher.rb +179 -4
- data/lib/rigor/inference/method_parameter_binder.rb +67 -10
- data/lib/rigor/inference/narrowing.rb +29 -10
- data/lib/rigor/inference/scope_indexer.rb +156 -6
- data/lib/rigor/inference/statement_evaluator.rb +43 -21
- data/lib/rigor/plugin/base.rb +39 -0
- data/lib/rigor/plugin/loader.rb +22 -1
- data/lib/rigor/plugin/manifest.rb +73 -10
- data/lib/rigor/plugin/protocol_contract.rb +185 -0
- data/lib/rigor/plugin/registry.rb +66 -0
- data/lib/rigor/scope.rb +46 -0
- data/lib/rigor/triage/catalogue.rb +296 -0
- data/lib/rigor/triage/hint.rb +27 -0
- data/lib/rigor/triage.rb +89 -0
- data/lib/rigor/type/constant.rb +29 -2
- data/lib/rigor/version.rb +1 -1
- data/sig/rigor/inference.rbs +1 -0
- data/sig/rigor/scope.rbs +6 -0
- metadata +16 -1
data/lib/rigor/triage.rb
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "triage/hint"
|
|
4
|
+
require_relative "triage/catalogue"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
# ADR-23 — diagnostic triage. Aggregates a `rigor check`
|
|
8
|
+
# diagnostic stream into the data behind the `rigor triage`
|
|
9
|
+
# report: a rule-ID distribution, per-file hotspots, and the
|
|
10
|
+
# heuristic hint catalogue ({Triage::Catalogue}).
|
|
11
|
+
#
|
|
12
|
+
# Pure over the diagnostic stream — no second analysis pass, no
|
|
13
|
+
# analyzer internals. `Triage.analyze` is the single entry point;
|
|
14
|
+
# rendering is {CLI::TriageRenderer}'s job.
|
|
15
|
+
module Triage
|
|
16
|
+
UNCATEGORISED = "(uncategorised)"
|
|
17
|
+
|
|
18
|
+
Summary = Data.define(:total, :error, :warning, :info)
|
|
19
|
+
RuleCount = Data.define(:rule, :count)
|
|
20
|
+
Hotspot = Data.define(:file, :count, :by_rule)
|
|
21
|
+
Report = Data.define(:summary, :distribution, :hotspots, :hints)
|
|
22
|
+
|
|
23
|
+
module_function
|
|
24
|
+
|
|
25
|
+
# @param diagnostics [Array<Analysis::Diagnostic>]
|
|
26
|
+
# @param top [Integer] hotspot-file cap
|
|
27
|
+
# @param hints [Boolean] run the heuristic catalogue
|
|
28
|
+
# @return [Report]
|
|
29
|
+
def analyze(diagnostics, top: 10, hints: true)
|
|
30
|
+
Report.new(
|
|
31
|
+
summary: build_summary(diagnostics),
|
|
32
|
+
distribution: build_distribution(diagnostics),
|
|
33
|
+
hotspots: build_hotspots(diagnostics, top),
|
|
34
|
+
hints: hints ? Catalogue.recognise(diagnostics) : []
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Diagnostics without a `rule` (parse errors, internal-analyzer
|
|
39
|
+
# errors) bucket under a single sentinel rather than vanishing.
|
|
40
|
+
def rule_key(diagnostic)
|
|
41
|
+
diagnostic.qualified_rule || UNCATEGORISED
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def build_summary(diagnostics)
|
|
45
|
+
by_severity = diagnostics.group_by(&:severity).transform_values(&:size)
|
|
46
|
+
Summary.new(
|
|
47
|
+
total: diagnostics.size,
|
|
48
|
+
error: by_severity.fetch(:error, 0),
|
|
49
|
+
warning: by_severity.fetch(:warning, 0),
|
|
50
|
+
info: by_severity.fetch(:info, 0)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def build_distribution(diagnostics)
|
|
55
|
+
diagnostics.group_by { |d| rule_key(d) }
|
|
56
|
+
.map { |rule, group| RuleCount.new(rule: rule, count: group.size) }
|
|
57
|
+
.sort_by { |row| [-row.count, row.rule] }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def build_hotspots(diagnostics, top)
|
|
61
|
+
diagnostics.group_by(&:path)
|
|
62
|
+
.map { |path, group| hotspot_for(path, group) }
|
|
63
|
+
.sort_by { |spot| [-spot.count, spot.file] }
|
|
64
|
+
.first(top)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def hotspot_for(path, group)
|
|
68
|
+
by_rule = group.group_by { |d| rule_key(d) }
|
|
69
|
+
.transform_values(&:size)
|
|
70
|
+
.sort_by { |rule, count| [-count, rule] }
|
|
71
|
+
.to_h
|
|
72
|
+
Hotspot.new(file: path, count: group.size, by_rule: by_rule)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def report_to_h(report)
|
|
76
|
+
{
|
|
77
|
+
"summary" => {
|
|
78
|
+
"total" => report.summary.total, "error" => report.summary.error,
|
|
79
|
+
"warning" => report.summary.warning, "info" => report.summary.info
|
|
80
|
+
},
|
|
81
|
+
"distribution" => report.distribution.map { |r| { "rule" => r.rule, "count" => r.count } },
|
|
82
|
+
"hotspots" => report.hotspots.map do |h|
|
|
83
|
+
{ "file" => h.file, "count" => h.count, "by_rule" => h.by_rule }
|
|
84
|
+
end,
|
|
85
|
+
"hints" => report.hints.map(&:to_h)
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
data/lib/rigor/type/constant.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "date"
|
|
3
4
|
require_relative "../trinary"
|
|
4
5
|
|
|
5
6
|
module Rigor
|
|
@@ -24,6 +25,13 @@ module Rigor
|
|
|
24
25
|
Complex,
|
|
25
26
|
Regexp,
|
|
26
27
|
Pathname,
|
|
28
|
+
::Set,
|
|
29
|
+
# `Date` covers `DateTime` (a subclass). `Time` is core.
|
|
30
|
+
# Both arise only from deterministic constructor folding
|
|
31
|
+
# (`Date.new` / `Time.utc`) — there is no Date / Time
|
|
32
|
+
# literal node — so a `Constant` carrier is always sound.
|
|
33
|
+
Date,
|
|
34
|
+
Time,
|
|
27
35
|
TrueClass,
|
|
28
36
|
FalseClass,
|
|
29
37
|
NilClass
|
|
@@ -42,12 +50,31 @@ module Rigor
|
|
|
42
50
|
raise ArgumentError, "Rigor::Type::Constant only carries scalar literals; got #{value.class}"
|
|
43
51
|
end
|
|
44
52
|
|
|
45
|
-
@value =
|
|
53
|
+
@value = freezable_carrier?(value) ? value.dup.freeze : value
|
|
46
54
|
freeze
|
|
47
55
|
end
|
|
48
56
|
|
|
57
|
+
# Mutable-ish carriers are stored as a frozen copy so a later
|
|
58
|
+
# in-place mutation cannot rewrite the literal under us. `Time`
|
|
59
|
+
# joins `String` / `Set` here: `Time#localtime` mutates the
|
|
60
|
+
# receiver's zone in place, so the carrier holds a frozen copy
|
|
61
|
+
# (the catalog also blocklists the mutators). `Date` is already
|
|
62
|
+
# immutable, but is duped-and-frozen for symmetry.
|
|
63
|
+
def freezable_carrier?(value)
|
|
64
|
+
value.is_a?(String) || value.is_a?(::Set) ||
|
|
65
|
+
value.is_a?(Date) || value.is_a?(Time)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# `Date#inspect` / `DateTime#inspect` spell out the internal
|
|
69
|
+
# astronomical-Julian-day representation, which is unreadable
|
|
70
|
+
# in a diagnostic. ISO-8601 is the compact, deterministic
|
|
71
|
+
# form. `Time#inspect` is already compact (`2026-01-01
|
|
72
|
+
# 00:00:00 UTC`), so it keeps the default.
|
|
49
73
|
def describe(_verbosity = :short)
|
|
50
|
-
value
|
|
74
|
+
case value
|
|
75
|
+
when Date then value.iso8601
|
|
76
|
+
else value.inspect
|
|
77
|
+
end
|
|
51
78
|
end
|
|
52
79
|
|
|
53
80
|
# RBS supports `Literal` types for booleans, nil, integer
|
data/lib/rigor/version.rb
CHANGED
data/sig/rigor/inference.rbs
CHANGED
|
@@ -135,6 +135,7 @@ module Rigor
|
|
|
135
135
|
def self?.build_declaration_overrides: (untyped root) -> Hash[untyped, Type::t]
|
|
136
136
|
def self?.record_declarations: (untyped node, Array[String] qualified_prefix, Hash[untyped, Type::t] identity_table, Hash[String, Type::t] discovered) -> void
|
|
137
137
|
def self?.discovered_classes_for_paths: (Array[String] paths, ?buffer: untyped) -> Hash[String, Type::t]
|
|
138
|
+
def self?.discovered_def_index_for_paths: (Array[String] paths, ?buffer: untyped) -> Hash[Symbol, untyped]
|
|
138
139
|
def self?.collect_class_decls: (untyped node, Array[String] qualified_prefix, Hash[String, Type::t] accumulator) -> void
|
|
139
140
|
def self?.qualified_name_for: (untyped constant_path_node) -> String?
|
|
140
141
|
def self?.render_constant_path: (untyped node) -> String
|
data/sig/rigor/scope.rbs
CHANGED
|
@@ -16,6 +16,8 @@ module Rigor
|
|
|
16
16
|
attr_reader discovered_methods: Hash[String, Hash[Symbol, Symbol]]
|
|
17
17
|
attr_reader discovered_def_nodes: Hash[String, Hash[Symbol, untyped]]
|
|
18
18
|
attr_reader discovered_method_visibilities: Hash[String, Hash[Symbol, Symbol]]
|
|
19
|
+
attr_reader discovered_superclasses: Hash[String, String]
|
|
20
|
+
attr_reader discovered_includes: Hash[String, Array[String]]
|
|
19
21
|
attr_reader source_path: String?
|
|
20
22
|
|
|
21
23
|
def self.empty: (?environment: Environment, ?source_path: String?) -> Scope
|
|
@@ -44,6 +46,10 @@ module Rigor
|
|
|
44
46
|
def top_level_def_for: (String | Symbol method_name) -> untyped?
|
|
45
47
|
def with_discovered_method_visibilities: (Hash[String, Hash[Symbol, Symbol]] table) -> Scope
|
|
46
48
|
def discovered_method_visibility: (String | Symbol class_name, String | Symbol method_name) -> Symbol?
|
|
49
|
+
def superclass_of: (String | Symbol class_name) -> String?
|
|
50
|
+
def with_discovered_superclasses: (Hash[String, String] table) -> Scope
|
|
51
|
+
def includes_of: (String | Symbol class_name) -> Array[String]
|
|
52
|
+
def with_discovered_includes: (Hash[String, Array[String]] table) -> Scope
|
|
47
53
|
def with_fact: (Analysis::FactStore::Fact fact) -> Scope
|
|
48
54
|
def with_self_type: (Type::t? type) -> Scope
|
|
49
55
|
def with_declared_types: (Hash[untyped, Type::t] table) -> Scope
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rigortype
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rigor contributors
|
|
@@ -261,11 +261,15 @@ files:
|
|
|
261
261
|
- lib/rigor/cache/rbs_known_class_names.rb
|
|
262
262
|
- lib/rigor/cache/store.rb
|
|
263
263
|
- lib/rigor/cli.rb
|
|
264
|
+
- lib/rigor/cli/annotate_command.rb
|
|
264
265
|
- lib/rigor/cli/baseline_command.rb
|
|
265
266
|
- lib/rigor/cli/diff_command.rb
|
|
266
267
|
- lib/rigor/cli/explain_command.rb
|
|
267
268
|
- lib/rigor/cli/lsp_command.rb
|
|
269
|
+
- lib/rigor/cli/prism_colorizer.rb
|
|
268
270
|
- lib/rigor/cli/sig_gen_command.rb
|
|
271
|
+
- lib/rigor/cli/triage_command.rb
|
|
272
|
+
- lib/rigor/cli/triage_renderer.rb
|
|
269
273
|
- lib/rigor/cli/type_of_command.rb
|
|
270
274
|
- lib/rigor/cli/type_of_renderer.rb
|
|
271
275
|
- lib/rigor/cli/type_scan_command.rb
|
|
@@ -325,16 +329,23 @@ files:
|
|
|
325
329
|
- lib/rigor/inference/macro_block_self_type.rb
|
|
326
330
|
- lib/rigor/inference/method_dispatcher.rb
|
|
327
331
|
- lib/rigor/inference/method_dispatcher/block_folding.rb
|
|
332
|
+
- lib/rigor/inference/method_dispatcher/cgi_folding.rb
|
|
328
333
|
- lib/rigor/inference/method_dispatcher/constant_folding.rb
|
|
329
334
|
- lib/rigor/inference/method_dispatcher/file_folding.rb
|
|
330
335
|
- lib/rigor/inference/method_dispatcher/iterator_dispatch.rb
|
|
331
336
|
- lib/rigor/inference/method_dispatcher/kernel_dispatch.rb
|
|
332
337
|
- lib/rigor/inference/method_dispatcher/literal_string_folding.rb
|
|
338
|
+
- lib/rigor/inference/method_dispatcher/math_folding.rb
|
|
333
339
|
- lib/rigor/inference/method_dispatcher/method_folding.rb
|
|
334
340
|
- lib/rigor/inference/method_dispatcher/overload_selector.rb
|
|
335
341
|
- lib/rigor/inference/method_dispatcher/rbs_dispatch.rb
|
|
336
342
|
- lib/rigor/inference/method_dispatcher/receiver_affinity.rb
|
|
343
|
+
- lib/rigor/inference/method_dispatcher/regexp_folding.rb
|
|
344
|
+
- lib/rigor/inference/method_dispatcher/set_folding.rb
|
|
337
345
|
- lib/rigor/inference/method_dispatcher/shape_dispatch.rb
|
|
346
|
+
- lib/rigor/inference/method_dispatcher/shellwords_folding.rb
|
|
347
|
+
- lib/rigor/inference/method_dispatcher/time_folding.rb
|
|
348
|
+
- lib/rigor/inference/method_dispatcher/uri_folding.rb
|
|
338
349
|
- lib/rigor/inference/method_parameter_binder.rb
|
|
339
350
|
- lib/rigor/inference/multi_target_binder.rb
|
|
340
351
|
- lib/rigor/inference/narrowing.rb
|
|
@@ -376,6 +387,7 @@ files:
|
|
|
376
387
|
- lib/rigor/plugin/macro/heredoc_template.rb
|
|
377
388
|
- lib/rigor/plugin/macro/trait_registry.rb
|
|
378
389
|
- lib/rigor/plugin/manifest.rb
|
|
390
|
+
- lib/rigor/plugin/protocol_contract.rb
|
|
379
391
|
- lib/rigor/plugin/registry.rb
|
|
380
392
|
- lib/rigor/plugin/services.rb
|
|
381
393
|
- lib/rigor/plugin/trust_policy.rb
|
|
@@ -401,6 +413,9 @@ files:
|
|
|
401
413
|
- lib/rigor/source/node_locator.rb
|
|
402
414
|
- lib/rigor/source/node_walker.rb
|
|
403
415
|
- lib/rigor/testing.rb
|
|
416
|
+
- lib/rigor/triage.rb
|
|
417
|
+
- lib/rigor/triage/catalogue.rb
|
|
418
|
+
- lib/rigor/triage/hint.rb
|
|
404
419
|
- lib/rigor/trinary.rb
|
|
405
420
|
- lib/rigor/type.rb
|
|
406
421
|
- lib/rigor/type/accepts_result.rb
|