rigortype 0.1.8 → 0.1.10
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 +20 -0
- data/lib/rigor/analysis/runner.rb +67 -9
- data/lib/rigor/analysis/worker_session.rb +13 -4
- data/lib/rigor/cache/rbs_descriptor.rb +21 -2
- data/lib/rigor/cache/rbs_environment.rb +2 -1
- data/lib/rigor/cli/annotate_command.rb +274 -0
- data/lib/rigor/cli/baseline_command.rb +36 -16
- data/lib/rigor/cli/coverage_command.rb +126 -0
- data/lib/rigor/cli/coverage_renderer.rb +162 -0
- data/lib/rigor/cli/coverage_report.rb +75 -0
- data/lib/rigor/cli/mcp_command.rb +70 -0
- data/lib/rigor/cli/prism_colorizer.rb +111 -0
- data/lib/rigor/cli.rb +134 -6
- data/lib/rigor/environment/rbs_loader.rb +46 -5
- data/lib/rigor/environment/reporters.rb +3 -2
- data/lib/rigor/environment.rb +168 -5
- data/lib/rigor/inference/builtins/method_catalog.rb +17 -1
- data/lib/rigor/inference/builtins/time_catalog.rb +10 -1
- data/lib/rigor/inference/def_return_typer.rb +98 -0
- data/lib/rigor/inference/expression_typer.rb +308 -18
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +109 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +178 -10
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +53 -1
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +62 -15
- 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/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 +431 -9
- 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 +148 -1
- data/lib/rigor/inference/method_parameter_binder.rb +67 -10
- data/lib/rigor/inference/narrowing.rb +29 -10
- data/lib/rigor/inference/precision_scanner.rb +131 -0
- data/lib/rigor/inference/statement_evaluator.rb +29 -3
- data/lib/rigor/mcp/loop.rb +43 -0
- data/lib/rigor/mcp/server.rb +263 -0
- data/lib/rigor/mcp.rb +16 -0
- data/lib/rigor/plugin/base.rb +67 -5
- data/lib/rigor/plugin/loader.rb +22 -1
- data/lib/rigor/plugin/manifest.rb +101 -10
- data/lib/rigor/plugin/protocol_contract.rb +185 -0
- data/lib/rigor/plugin/registry.rb +87 -0
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +51 -0
- data/lib/rigor/sig_gen/generator.rb +150 -75
- data/lib/rigor/triage/catalogue.rb +2 -2
- data/lib/rigor/type/combinator.rb +57 -0
- data/lib/rigor/type/constant.rb +29 -2
- data/lib/rigor/version.rb +1 -1
- data/sig/rigor/analysis/baseline.rbs +39 -0
- data/sig/rigor/environment.rbs +3 -2
- data/sig/rigor/type.rbs +4 -0
- data/sig/rigor.rbs +2 -0
- metadata +42 -1
|
@@ -122,13 +122,30 @@ module Rigor
|
|
|
122
122
|
return match if match
|
|
123
123
|
return overloads.find { |mt| overload_has_block?(mt) } if block_required
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
# No block at the call site: prefer an overload that does
|
|
126
|
+
# not REQUIRE a block over `overloads.first`. Methods like
|
|
127
|
+
# `Array#filter` / `Enumerable#map` declare the block-
|
|
128
|
+
# bearing overload first (`() { ... } -> Array[Elem]`) and
|
|
129
|
+
# the bare-call overload second (`() -> Enumerator[...]`).
|
|
130
|
+
# Without this, a no-block `[1, 2].filter` would adopt the
|
|
131
|
+
# block overload's `Array[Elem]` return when the call
|
|
132
|
+
# actually yields an `Enumerator`.
|
|
133
|
+
overloads.find { |mt| !overload_requires_block?(mt) } || overloads.first
|
|
126
134
|
end
|
|
127
135
|
|
|
128
136
|
def overload_has_block?(method_type)
|
|
129
137
|
method_type.respond_to?(:block) && method_type.block
|
|
130
138
|
end
|
|
131
139
|
|
|
140
|
+
# True when the overload declares a block that the caller
|
|
141
|
+
# MUST supply (`{ ... }` in RBS). An optional block
|
|
142
|
+
# (`?{ ... }`) does NOT count — that overload is a valid
|
|
143
|
+
# match for a block-less call.
|
|
144
|
+
def overload_requires_block?(method_type)
|
|
145
|
+
block = overload_has_block?(method_type)
|
|
146
|
+
!!block && block.required
|
|
147
|
+
end
|
|
148
|
+
|
|
132
149
|
class << self
|
|
133
150
|
private
|
|
134
151
|
|
|
@@ -163,6 +180,7 @@ module Rigor
|
|
|
163
180
|
|
|
164
181
|
overloads.find do |method_type|
|
|
165
182
|
next false if block_required && !OverloadSelector.overload_has_block?(method_type)
|
|
183
|
+
next false if !block_required && OverloadSelector.overload_requires_block?(method_type)
|
|
166
184
|
next false if strict && !strictly_typed_params?(method_type, arg_types.size)
|
|
167
185
|
|
|
168
186
|
matches?(
|
|
@@ -199,6 +217,7 @@ module Rigor
|
|
|
199
217
|
def find_matching_overload_via_aliases(overloads, arg_types:, block_required:)
|
|
200
218
|
overloads.find do |method_type|
|
|
201
219
|
next false if block_required && !OverloadSelector.overload_has_block?(method_type)
|
|
220
|
+
next false if !block_required && OverloadSelector.overload_requires_block?(method_type)
|
|
202
221
|
|
|
203
222
|
fun = method_type.type
|
|
204
223
|
next false unless arity_compatible?(fun, arg_types.size)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../type"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Inference
|
|
7
|
+
module MethodDispatcher
|
|
8
|
+
# Folds `Regexp` class-method calls on statically known arguments.
|
|
9
|
+
#
|
|
10
|
+
# === Supported methods
|
|
11
|
+
#
|
|
12
|
+
# * `escape(str)` / `quote(str)` — escapes regexp meta-characters.
|
|
13
|
+
# Returns `Constant[String]`.
|
|
14
|
+
# * `new(str)` / `new(str, opts)` — constructs a Regexp at fold time
|
|
15
|
+
# when the pattern argument is a `Constant[String]`. The optional
|
|
16
|
+
# second argument may be a `Constant[Integer]` (flag bits), a
|
|
17
|
+
# `Constant[true/false]` (IGNORECASE shorthand), or absent.
|
|
18
|
+
# Returns `Constant[Regexp]`.
|
|
19
|
+
#
|
|
20
|
+
# === Non-constant / unsupported cases
|
|
21
|
+
#
|
|
22
|
+
# Returns `nil` (deferring to the next dispatcher tier) when:
|
|
23
|
+
# - the receiver is not `Singleton[Regexp]`,
|
|
24
|
+
# - the required pattern argument is not a `Constant[String]`,
|
|
25
|
+
# - the method is not in the supported set.
|
|
26
|
+
module RegexpFolding
|
|
27
|
+
REGEXP_ESCAPE_METHODS = Set[:escape, :quote].freeze
|
|
28
|
+
private_constant :REGEXP_ESCAPE_METHODS
|
|
29
|
+
|
|
30
|
+
module_function
|
|
31
|
+
|
|
32
|
+
# @return [Rigor::Type, nil] folded result, or nil to defer.
|
|
33
|
+
def try_dispatch(receiver:, method_name:, args:)
|
|
34
|
+
return nil unless dispatch_target?(receiver)
|
|
35
|
+
return fold_escape(args) if REGEXP_ESCAPE_METHODS.include?(method_name)
|
|
36
|
+
return fold_new(args) if method_name == :new
|
|
37
|
+
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def dispatch_target?(receiver)
|
|
42
|
+
receiver.is_a?(Type::Singleton) && receiver.class_name == "Regexp"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# `Regexp.escape(str)` / `.quote(str)` — one String arg.
|
|
46
|
+
def fold_escape(args)
|
|
47
|
+
return nil unless args.size == 1
|
|
48
|
+
|
|
49
|
+
arg = args.first
|
|
50
|
+
return nil unless arg.is_a?(Type::Constant) && arg.value.is_a?(String)
|
|
51
|
+
|
|
52
|
+
Type::Combinator.constant_of(Regexp.escape(arg.value))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# `Regexp.new(pattern)` / `Regexp.new(pattern, opts)` — constructs
|
|
56
|
+
# the pattern at inference time. Delegates to Ruby's real
|
|
57
|
+
# `Regexp.new` so all option forms (Integer flags, `true`/`false`,
|
|
58
|
+
# option strings) are handled without case-analysis; non-constant or
|
|
59
|
+
# invalid arguments decline through to the RBS tier.
|
|
60
|
+
def fold_new(args)
|
|
61
|
+
return nil if args.empty? || args.size > 2
|
|
62
|
+
|
|
63
|
+
pattern_arg = args.first
|
|
64
|
+
return nil unless pattern_arg.is_a?(Type::Constant) &&
|
|
65
|
+
pattern_arg.value.is_a?(String)
|
|
66
|
+
|
|
67
|
+
opts = args.size == 2 ? constant_value_or_nil(args[1]) : 0
|
|
68
|
+
return nil if args.size == 2 && opts.nil?
|
|
69
|
+
|
|
70
|
+
Type::Combinator.constant_of(Regexp.new(pattern_arg.value, opts))
|
|
71
|
+
rescue StandardError
|
|
72
|
+
nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def constant_value_or_nil(type)
|
|
76
|
+
type.is_a?(Type::Constant) ? type.value : nil
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../type"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Inference
|
|
7
|
+
module MethodDispatcher
|
|
8
|
+
# Folds `Set` constructor calls into `Constant[Set]` when all
|
|
9
|
+
# arguments are statically known.
|
|
10
|
+
#
|
|
11
|
+
# Ruby 3.2+ implements Set in C (`set.c`). The constructor is
|
|
12
|
+
# deterministic — it reads only its arguments and writes nothing
|
|
13
|
+
# to global state.
|
|
14
|
+
#
|
|
15
|
+
# === Supported methods
|
|
16
|
+
#
|
|
17
|
+
# * `Set[]` — variadic constructor; each argument must be a
|
|
18
|
+
# `Constant[T]`. Returns `Constant[Set]`.
|
|
19
|
+
# * `Set.new` — zero-argument form; returns `Constant[Set.new]`.
|
|
20
|
+
# * `Set.new(tuple)` — the argument must be a `Tuple` whose
|
|
21
|
+
# every element is a `Constant[T]`. Returns `Constant[Set]`.
|
|
22
|
+
#
|
|
23
|
+
# === Non-constant / unsupported cases
|
|
24
|
+
#
|
|
25
|
+
# Returns `nil` (deferring to the next dispatcher tier) when:
|
|
26
|
+
# - the receiver is not `Singleton[Set]`,
|
|
27
|
+
# - the method is not `[]` or `new`,
|
|
28
|
+
# - any argument is non-constant or structurally opaque.
|
|
29
|
+
module SetFolding
|
|
30
|
+
module_function
|
|
31
|
+
|
|
32
|
+
# @return [Rigor::Type, nil] folded result, or nil to defer.
|
|
33
|
+
def try_dispatch(receiver:, method_name:, args:)
|
|
34
|
+
return nil unless dispatch_target?(receiver)
|
|
35
|
+
|
|
36
|
+
case method_name
|
|
37
|
+
when :[] then fold_bracket(args)
|
|
38
|
+
when :new then fold_new(args)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def dispatch_target?(receiver)
|
|
43
|
+
receiver.is_a?(Type::Singleton) && receiver.class_name == "Set"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# `Set["a", "b", "c"]` — all positional args must be Constant.
|
|
47
|
+
def fold_bracket(args)
|
|
48
|
+
values = args.map do |a|
|
|
49
|
+
return nil unless a.is_a?(Type::Constant)
|
|
50
|
+
|
|
51
|
+
a.value
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
Type::Combinator.constant_of(::Set.new(values))
|
|
55
|
+
rescue StandardError
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# `Set.new` / `Set.new(tuple_of_constants)`.
|
|
60
|
+
def fold_new(args)
|
|
61
|
+
return Type::Combinator.constant_of(::Set.new) if args.empty?
|
|
62
|
+
return nil if args.size > 1
|
|
63
|
+
|
|
64
|
+
arg = args.first
|
|
65
|
+
values = case arg
|
|
66
|
+
when Type::Tuple
|
|
67
|
+
return nil unless arg.elements.all?(Type::Constant)
|
|
68
|
+
|
|
69
|
+
arg.elements.map(&:value)
|
|
70
|
+
else
|
|
71
|
+
return nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
Type::Combinator.constant_of(::Set.new(values))
|
|
75
|
+
rescue StandardError
|
|
76
|
+
nil
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|