rigortype 0.0.8 → 0.1.0
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 +234 -22
- data/data/builtins/ruby_core/encoding.yml +210 -0
- data/data/builtins/ruby_core/exception.yml +641 -0
- data/data/builtins/ruby_core/numeric.yml +3 -2
- data/data/builtins/ruby_core/proc.yml +731 -0
- data/data/builtins/ruby_core/random.yml +166 -0
- data/data/builtins/ruby_core/re.yml +689 -0
- data/data/builtins/ruby_core/struct.yml +449 -0
- data/lib/rigor/analysis/check_rules.rb +228 -40
- data/lib/rigor/analysis/diagnostic.rb +15 -1
- data/lib/rigor/analysis/runner.rb +199 -4
- data/lib/rigor/builtins/imported_refinements.rb +6 -1
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +63 -0
- data/lib/rigor/cache/rbs_class_type_param_names.rb +60 -0
- data/lib/rigor/cache/rbs_constant_table.rb +15 -51
- data/lib/rigor/cache/rbs_descriptor.rb +55 -0
- data/lib/rigor/cache/rbs_environment.rb +52 -0
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +40 -0
- data/lib/rigor/cache/rbs_instance_definitions.rb +79 -0
- data/lib/rigor/cache/rbs_known_class_names.rb +43 -0
- data/lib/rigor/cache/store.rb +81 -15
- data/lib/rigor/cli.rb +45 -7
- data/lib/rigor/configuration/severity_profile.rb +109 -0
- data/lib/rigor/configuration.rb +110 -6
- data/lib/rigor/environment/rbs_hierarchy.rb +18 -5
- data/lib/rigor/environment/rbs_loader.rb +220 -32
- data/lib/rigor/environment.rb +11 -2
- data/lib/rigor/flow_contribution/conflict.rb +81 -0
- data/lib/rigor/flow_contribution/element.rb +53 -0
- data/lib/rigor/flow_contribution/fact.rb +88 -0
- data/lib/rigor/flow_contribution/merge_result.rb +67 -0
- data/lib/rigor/flow_contribution/merger.rb +275 -0
- data/lib/rigor/flow_contribution.rb +179 -0
- data/lib/rigor/inference/block_parameter_binder.rb +15 -0
- data/lib/rigor/inference/builtins/encoding_catalog.rb +67 -0
- data/lib/rigor/inference/builtins/exception_catalog.rb +92 -0
- data/lib/rigor/inference/builtins/proc_catalog.rb +122 -0
- data/lib/rigor/inference/builtins/random_catalog.rb +58 -0
- data/lib/rigor/inference/builtins/re_catalog.rb +81 -0
- data/lib/rigor/inference/builtins/struct_catalog.rb +55 -0
- data/lib/rigor/inference/expression_typer.rb +110 -6
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +16 -1
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +173 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +21 -1
- data/lib/rigor/inference/method_dispatcher.rb +2 -0
- data/lib/rigor/inference/multi_target_binder.rb +2 -0
- data/lib/rigor/inference/narrowing.rb +134 -144
- data/lib/rigor/inference/scope_indexer.rb +75 -1
- data/lib/rigor/inference/statement_evaluator.rb +380 -40
- data/lib/rigor/plugin/access_denied_error.rb +24 -0
- data/lib/rigor/plugin/base.rb +241 -0
- data/lib/rigor/plugin/io_boundary.rb +102 -0
- data/lib/rigor/plugin/load_error.rb +23 -0
- data/lib/rigor/plugin/loader.rb +191 -0
- data/lib/rigor/plugin/manifest.rb +134 -0
- data/lib/rigor/plugin/registry.rb +50 -0
- data/lib/rigor/plugin/services.rb +65 -0
- data/lib/rigor/plugin/trust_policy.rb +99 -0
- data/lib/rigor/plugin.rb +61 -0
- data/lib/rigor/rbs_extended.rb +103 -0
- data/lib/rigor/reflection.rb +2 -2
- data/lib/rigor/type/combinator.rb +72 -0
- data/lib/rigor/type/refined.rb +50 -2
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +13 -0
- data/sig/rigor/environment.rbs +7 -1
- data/sig/rigor/inference.rbs +1 -0
- data/sig/rigor/rbs_extended.rbs +2 -0
- data/sig/rigor/scope.rbs +1 -0
- data/sig/rigor/type.rbs +7 -0
- data/sig/rigor.rbs +3 -1
- metadata +38 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 257469533556c55164c5dbb9ea351c142ea600b17a90b6f9e632b5307bbbac08
|
|
4
|
+
data.tar.gz: be2141e8f53748716bd7f19e95632a1fab17fde8110dd824d981baaf2522371c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 221248b2968dcd5d11963197468407ffbc34f2c41b79aa45321a5171e8183817e1ed67fbac37c852c6f22c910b065fef7214cf5f5ef2f03ec6f3c2aef5cce213
|
|
7
|
+
data.tar.gz: d2b096836618fd21a44810aa7c87bad2b0fd28a90baf395dd206ece02844d65819f1ab0cc80ea7a956a4b590351a4f67572be959d8b4effc9d086ff120498e2e
|
data/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Rigor
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/rb/rigortype)
|
|
4
|
-

|
|
4
|
+
[](https://github.com/rigortype/rigor/blob/master/LICENSE)
|
|
5
|
+
[](https://deepwiki.com/rigortype/rigor)
|
|
5
6
|
|
|
6
7
|
**Inference-first static analysis for Ruby.** Add Rigor to your
|
|
7
8
|
Gemfile and run `rigor check` over your code — no annotations,
|
|
@@ -14,14 +15,23 @@ for any class it can find, and reports a small but trustworthy
|
|
|
14
15
|
catalogue of bugs (undefined methods on typed receivers, wrong
|
|
15
16
|
positional arity, provable `Integer / 0`, …).
|
|
16
17
|
|
|
18
|
+
The differentiator is a richer type vocabulary than ordinary
|
|
19
|
+
RBS expresses. Rigor reasons about *what values an expression
|
|
20
|
+
actually produces* — literal values, integer ranges,
|
|
21
|
+
refinement-type carriers, per-position tuple / hash shapes —
|
|
22
|
+
not just *which class an object belongs to*. See **[Beyond
|
|
23
|
+
`Integer` and `String`](#beyond-integer-and-string-rigors-richer-type-vocabulary)**
|
|
24
|
+
for the full type-model story; the short pitch is below.
|
|
25
|
+
|
|
17
26
|
When you want tighter types than RBS expresses, refine them
|
|
18
27
|
through the
|
|
19
28
|
[`RBS::Extended`](docs/type-specification/rbs-extended.md)
|
|
20
29
|
annotation surface — `rigor:v1:return:` /
|
|
21
30
|
`rigor:v1:param:` / `rigor:v1:assert` directives accept the
|
|
22
31
|
imported-built-in refinement names (`non-empty-string`,
|
|
23
|
-
`positive-int`, `non-empty-array[Integer]`, `int<5, 10>`,
|
|
24
|
-
|
|
32
|
+
`positive-int`, `non-empty-array[Integer]`, `int<5, 10>`,
|
|
33
|
+
`literal-string`, `non-lowercase-string`, …) without changing
|
|
34
|
+
the underlying RBS.
|
|
25
35
|
|
|
26
36
|
## Installation
|
|
27
37
|
|
|
@@ -51,7 +61,7 @@ The gem ships an executable named `rigor` (gem name is
|
|
|
51
61
|
|
|
52
62
|
**Ruby version.** The gemspec requires `>= 4.0.0, < 4.1`.
|
|
53
63
|
|
|
54
|
-
##
|
|
64
|
+
## Quick start
|
|
55
65
|
|
|
56
66
|
Drop into your project root and run the canonical commands:
|
|
57
67
|
|
|
@@ -60,15 +70,15 @@ Drop into your project root and run the canonical commands:
|
|
|
60
70
|
# rule-driven bugs across `lib/`.
|
|
61
71
|
bundle exec rigor check lib
|
|
62
72
|
|
|
73
|
+
# Drop a starter .rigor.yml into the project root.
|
|
74
|
+
bundle exec rigor init
|
|
75
|
+
|
|
63
76
|
# Print the inferred type at a precise FILE:LINE:COL position.
|
|
64
77
|
bundle exec rigor type-of lib/foo.rb:10:5
|
|
65
78
|
|
|
66
79
|
# Report Scope#type_of coverage across a tree (handy when
|
|
67
80
|
# diagnosing why a particular call site reads as `untyped`).
|
|
68
81
|
bundle exec rigor type-scan lib
|
|
69
|
-
|
|
70
|
-
# Drop a starter .rigor.yml into the project root.
|
|
71
|
-
bundle exec rigor init
|
|
72
82
|
```
|
|
73
83
|
|
|
74
84
|
### Sample output
|
|
@@ -90,6 +100,130 @@ RBS or in-source `def` / `define_method` discovery. Implicit-
|
|
|
90
100
|
self calls, dynamic receivers, and constant-decl alias classes
|
|
91
101
|
(e.g. `YAML` → `Psych`) are skipped to avoid false positives.
|
|
92
102
|
|
|
103
|
+
### Faster runs through the cache
|
|
104
|
+
|
|
105
|
+
Rigor caches expensive RBS work (the loaded `RBS::Environment`,
|
|
106
|
+
constant-type translation, class hierarchy, type-parameter
|
|
107
|
+
names, known-class set) under `.rigor/cache/` so the second
|
|
108
|
+
`rigor check` is significantly faster than the first. The cache
|
|
109
|
+
is keyed by your project's `.rbs` file digests + the locked
|
|
110
|
+
`rbs` gem version, so a signature change or a gem upgrade
|
|
111
|
+
invalidates exactly what it should.
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
# Inspect what is cached on disk and what this run did.
|
|
115
|
+
bundle exec rigor check --cache-stats lib
|
|
116
|
+
|
|
117
|
+
# Wipe the cache (do this if you suspect staleness).
|
|
118
|
+
bundle exec rigor check --clear-cache lib
|
|
119
|
+
|
|
120
|
+
# Run with caching disabled.
|
|
121
|
+
bundle exec rigor check --no-cache lib
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Add `.rigor/` to your `.gitignore` — the cache is per-checkout
|
|
125
|
+
and contains nothing reproducible to share.
|
|
126
|
+
|
|
127
|
+
## Beyond `Integer` and `String`: Rigor's richer type vocabulary
|
|
128
|
+
|
|
129
|
+
A vanilla static checker answers "what *class* is this object?"
|
|
130
|
+
Rigor answers a much narrower question: "what *subset of values*
|
|
131
|
+
can this expression actually produce?" That distinction is the
|
|
132
|
+
whole point of Rigor — types like `Integer` and `String` describe
|
|
133
|
+
classes, but real-world code carries far more structure (a count
|
|
134
|
+
that's always non-negative, a name that's never empty, a flag
|
|
135
|
+
that's one of three Symbols). Rigor reasons about that structure
|
|
136
|
+
out of the box, without you writing a single annotation.
|
|
137
|
+
|
|
138
|
+
### The carrier zoo
|
|
139
|
+
|
|
140
|
+
| Carrier | What it records | Example |
|
|
141
|
+
| --- | --- | --- |
|
|
142
|
+
| **Literal types** (`Type::Constant`) | A single Ruby value | `Constant<42>`, `Constant<"hello">`, `Constant<:foo>` |
|
|
143
|
+
| **Integer ranges** (`Type::IntegerRange`) | A bounded integer interval `int<a, b>` | `positive-int = int<1, max>`, `int<5, 10>` |
|
|
144
|
+
| **Refinement types** — split into two halves: `Type::Difference` and `Type::Refined` | A base nominal minus a single value, or a base nominal restricted by a predicate | `non-empty-string = String - ""`, `lowercase-string = String & lowercase?`, `literal-string` |
|
|
145
|
+
| **Intersection** (`Type::Intersection`) | Composition of multiple refinements | `non-empty-lowercase-string = non-empty-string ∩ lowercase-string` |
|
|
146
|
+
| **Tuple / HashShape** | Heterogeneous arrays / known-key hashes that carry per-position / per-key types | `[1, "two", :three]` types as `Tuple[Constant<1>, Constant<"two">, Constant<:three>]`; `{name: "Alice", age: 30}` as `HashShape{name: Constant<"Alice">, age: Constant<30>}` |
|
|
147
|
+
| **Union** (`Type::Union`) | "One of these literal values" — finite enums Rigor can enumerate | `Constant<:zero> \| Constant<:small> \| Constant<:large>` |
|
|
148
|
+
| **`Dynamic[T]`** | The gradual carrier — wraps a static facet with a "could be anything" admission | `Dynamic[Top]` is the conservative fallback Rigor uses when it cannot prove a narrower type |
|
|
149
|
+
|
|
150
|
+
Each refinement / range / literal carrier **erases to its base
|
|
151
|
+
class** for ordinary RBS interop, so importing Rigor is a
|
|
152
|
+
strictly additive change: a method whose RBS sig says
|
|
153
|
+
`-> String` keeps that contract, and Rigor's narrower inference
|
|
154
|
+
just sits on top.
|
|
155
|
+
|
|
156
|
+
### What this buys you in practice
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
# Rigor doesn't just see "Integer", it sees "non-negative integer".
|
|
160
|
+
n = ARGV.size # int<0, max> (non-negative-int)
|
|
161
|
+
m = n + 1 # int<1, max> (positive-int)
|
|
162
|
+
m.zero? # Constant<false> — proven; the
|
|
163
|
+
# branch elision can drop the `else`
|
|
164
|
+
|
|
165
|
+
# String composition stays as precise as the inputs allow.
|
|
166
|
+
greeting = "Hello, " # Constant<"Hello, ">
|
|
167
|
+
name = ARGV.first # String? — RBS-declared
|
|
168
|
+
hello = "Hello, #{name}!" # literal-string — every part is
|
|
169
|
+
# literal-bearing, so the result is
|
|
170
|
+
# provably source-derived.
|
|
171
|
+
|
|
172
|
+
# Tuple-shaped destructuring stays per-position.
|
|
173
|
+
first, _middle, last = [10, 20, 30]
|
|
174
|
+
first # Constant<10>
|
|
175
|
+
last # Constant<30>
|
|
176
|
+
|
|
177
|
+
# Constant folding through user methods.
|
|
178
|
+
def is_odd(n) = n.odd?
|
|
179
|
+
is_odd(3) # Constant<true> — folded through
|
|
180
|
+
# the body, not just typed as `bool`
|
|
181
|
+
|
|
182
|
+
# Case/when narrowing produces a literal-set Union.
|
|
183
|
+
label = case n
|
|
184
|
+
when 0 then :zero
|
|
185
|
+
when 1..9 then :small
|
|
186
|
+
else :large
|
|
187
|
+
end
|
|
188
|
+
label # Constant<:zero> | Constant<:small>
|
|
189
|
+
# | Constant<:large>
|
|
190
|
+
|
|
191
|
+
# RBS::Extended directives let you tighten beyond what RBS expresses.
|
|
192
|
+
class Slug
|
|
193
|
+
%a{rigor:v1:return: non-empty-string}
|
|
194
|
+
def normalise: (::String id) -> ::String
|
|
195
|
+
end
|
|
196
|
+
Slug.new.normalise("foo").size # positive-int — provably ≥ 1
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Rigor never invents these answers — every narrower carrier is
|
|
200
|
+
derived from literals in the source, control-flow narrowing
|
|
201
|
+
(`is_a?`, `nil?`, `==` against finite literal sets, integer
|
|
202
|
+
comparisons), per-class catalogues for the bundled built-ins,
|
|
203
|
+
or `RBS::Extended` directives the user opted into. When the
|
|
204
|
+
inference cannot prove a value is in a narrower carrier, it
|
|
205
|
+
stays at the wider one (or `Dynamic[Top]`) and Rigor stays
|
|
206
|
+
silent — diagnostics fire only when the narrow type is
|
|
207
|
+
genuinely proved.
|
|
208
|
+
|
|
209
|
+
### Where the type model is documented
|
|
210
|
+
|
|
211
|
+
- **End-user handbook** — chapter-by-chapter walkthrough of
|
|
212
|
+
the type model written for Ruby programmers without prior
|
|
213
|
+
static-typing background:
|
|
214
|
+
[`docs/handbook/`](docs/handbook/README.md). Start here if
|
|
215
|
+
you want a guided tour of how Rigor sees your code rather
|
|
216
|
+
than a spec deep-dive.
|
|
217
|
+
- One-page mental model:
|
|
218
|
+
[`docs/types.md`](docs/types.md).
|
|
219
|
+
- Binding spec corpus:
|
|
220
|
+
[`docs/type-specification/`](docs/type-specification/README.md).
|
|
221
|
+
- Imported refinement names (kebab-case catalogue):
|
|
222
|
+
[`docs/type-specification/imported-built-in-types.md`](docs/type-specification/imported-built-in-types.md).
|
|
223
|
+
- The `RBS::Extended` annotation grammar that opens this
|
|
224
|
+
vocabulary up to your own RBS:
|
|
225
|
+
[`docs/type-specification/rbs-extended.md`](docs/type-specification/rbs-extended.md).
|
|
226
|
+
|
|
93
227
|
## How Rigor finds your types
|
|
94
228
|
|
|
95
229
|
Rigor consults, in order:
|
|
@@ -102,8 +236,9 @@ Rigor consults, in order:
|
|
|
102
236
|
3. **Gem RBS.** RBS files vendored with installed gems
|
|
103
237
|
(Prism's own `.rbs`, the `rbs` gem's, …).
|
|
104
238
|
4. **In-source class discovery.** When no RBS is available,
|
|
105
|
-
Rigor walks `def` / `define_method` / `attr_*`
|
|
106
|
-
user-defined methods on a class
|
|
239
|
+
Rigor walks `def` / `define_method` / `attr_*` /
|
|
240
|
+
`Data.define(*Symbol)` so user-defined methods on a class
|
|
241
|
+
are recognised.
|
|
107
242
|
|
|
108
243
|
If a type cannot be proved, the engine returns `Dynamic[Top]`
|
|
109
244
|
(Rigor's gradual carrier) and stays silent — Rigor never invents
|
|
@@ -139,8 +274,17 @@ Right-hand side accepts:
|
|
|
139
274
|
- Predicate refinements — `lowercase-string`,
|
|
140
275
|
`uppercase-string`, `numeric-string`, `decimal-int-string`,
|
|
141
276
|
`octal-int-string`, `hex-int-string`.
|
|
277
|
+
- Paired complements (`~T`-symmetric) —
|
|
278
|
+
`non-lowercase-string`, `non-uppercase-string`,
|
|
279
|
+
`non-numeric-string`. Writing `~lowercase-string` narrows
|
|
280
|
+
`String` to `non-lowercase-string` instead of the generic
|
|
281
|
+
`Difference[String, lowercase-string]` fallback.
|
|
142
282
|
- Composed shapes — `non-empty-lowercase-string`,
|
|
143
|
-
`non-empty-uppercase-string`.
|
|
283
|
+
`non-empty-uppercase-string`, `non-empty-literal-string`.
|
|
284
|
+
- Flow-tracked source-literal — `literal-string`. Rigor lifts
|
|
285
|
+
`"hi #{name}!"`, `"a" + literal_str`, and `literal_str * 3`
|
|
286
|
+
to `literal-string` when every operand is itself
|
|
287
|
+
literal-bearing.
|
|
144
288
|
|
|
145
289
|
The full directive table is in
|
|
146
290
|
[`docs/type-specification/rbs-extended.md`](docs/type-specification/rbs-extended.md);
|
|
@@ -191,32 +335,75 @@ sees `id` as `non-empty-string` (so `id.empty?` reduces to
|
|
|
191
335
|
- **Predicate narrowing** — truthiness, `nil?`, `is_a?` /
|
|
192
336
|
`kind_of?` / `instance_of?`, finite-literal equality,
|
|
193
337
|
case-equality (`===`) for Class / Module / Range / Regexp,
|
|
194
|
-
`case` / `when` integration.
|
|
338
|
+
`case` / `when` integration. Paired-complement narrowing for
|
|
339
|
+
Refined predicates (`~lowercase-string` →
|
|
340
|
+
`non-lowercase-string`).
|
|
195
341
|
- **Tuple / HashShape carriers** — shape-aware element access,
|
|
196
342
|
range / start-length slices, closed / open / required /
|
|
197
|
-
optional policies
|
|
343
|
+
optional policies, per-element block fold over
|
|
344
|
+
`map`, `select`, `filter_map`, `flat_map`, `find` /
|
|
345
|
+
`find_index`, `count`, `any?` / `all?` / `none?`, `zip`.
|
|
198
346
|
- **Constant folding** — aggressive arithmetic / string /
|
|
199
347
|
Symbol / Tuple-shaped `divmod` folding, cartesian fold over
|
|
200
348
|
`Union[Constant…]`, integer-range arithmetic
|
|
201
349
|
(`positive-int + 1` → `int<2, max>`), branch elision on
|
|
202
|
-
provably-truthy / falsey predicates
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
350
|
+
provably-truthy / falsey predicates,
|
|
351
|
+
`Constant<String>#%` format-string fold against
|
|
352
|
+
`Tuple` / `HashShape` arguments.
|
|
353
|
+
- **Built-in catalogues** — Numeric / Integer / Float, String /
|
|
354
|
+
Symbol, Array, Hash, IO, File, Range, Set, Time, Date /
|
|
355
|
+
DateTime, Comparable, Enumerable, Rational, Complex,
|
|
356
|
+
Pathname, Random, Struct (+ `Data`), Encoding, Regexp /
|
|
357
|
+
MatchData, Proc / Method / UnboundMethod, Exception. Each
|
|
358
|
+
catalog drives the fold dispatcher with per-class blocklists
|
|
359
|
+
for indirect mutators.
|
|
207
360
|
- **Refinement carriers** — `Type::Difference`,
|
|
208
361
|
`Type::Refined`, `Type::Intersection` provide the
|
|
209
362
|
imported-built-in catalogue end-to-end through
|
|
210
363
|
`Builtins::ImportedRefinements`.
|
|
211
364
|
- **`RBS::Extended` directive routes** — `return:`, `param:`
|
|
212
365
|
(call-site + body-side), `assert:` /
|
|
213
|
-
`predicate-if-(true|false)` accept refinement payloads
|
|
366
|
+
`predicate-if-(true|false)` accept refinement payloads, and
|
|
367
|
+
roll up into a single `Rigor::FlowContribution` bundle per
|
|
368
|
+
method (the v0.1.0 plugin contribution merger reads bundles
|
|
369
|
+
directly).
|
|
214
370
|
|
|
215
371
|
The full per-release surface lives in
|
|
216
372
|
[`CHANGELOG.md`](CHANGELOG.md). The internal contracts the
|
|
217
373
|
analyzer guarantees live under
|
|
218
374
|
[`docs/internal-spec/`](docs/internal-spec/).
|
|
219
375
|
|
|
376
|
+
## Plugins (v0.1.0)
|
|
377
|
+
|
|
378
|
+
`v0.1.0` adds an extension API so projects can teach Rigor about
|
|
379
|
+
their own DSLs. Six worked examples ship under
|
|
380
|
+
[`examples/`](examples/) — each is a fully-shaped plugin gem
|
|
381
|
+
with a runnable demo and an end-to-end integration spec, and
|
|
382
|
+
each spotlights a different facet of the plugin contract:
|
|
383
|
+
|
|
384
|
+
- [`rigor-deprecations`](examples/rigor-deprecations/) —
|
|
385
|
+
smallest possible plugin (~80 lines); config-driven rules.
|
|
386
|
+
- [`rigor-lisp-eval`](examples/rigor-lisp-eval/) — typing literal
|
|
387
|
+
AST arguments at a method call.
|
|
388
|
+
- [`rigor-statesman`](examples/rigor-statesman/) — two-pass DSL
|
|
389
|
+
analysis (collect declarations, then validate references).
|
|
390
|
+
- [`rigor-pattern`](examples/rigor-pattern/) — plugin →
|
|
391
|
+
analyzer collaboration via `Scope#type_of` and the
|
|
392
|
+
literal-string carrier.
|
|
393
|
+
- [`rigor-units`](examples/rigor-units/) — local-variable flow
|
|
394
|
+
tracking through arithmetic.
|
|
395
|
+
- [`rigor-routes`](examples/rigor-routes/) — `Plugin::IoBoundary`
|
|
396
|
+
reads under `TrustPolicy` plus cache producers (slice 2 +
|
|
397
|
+
slice 6) — the most architecturally complete example.
|
|
398
|
+
|
|
399
|
+
[`examples/README.md`](examples/README.md) is the plugin
|
|
400
|
+
authoring landing page — comparison table, recommended reading
|
|
401
|
+
order, and the architectural map of which surface each example
|
|
402
|
+
exercises. The binding contract for the plugin API lives in
|
|
403
|
+
[`docs/adr/2-extension-api.md`](docs/adr/2-extension-api.md)
|
|
404
|
+
and the slice-by-slice normative specs under
|
|
405
|
+
[`docs/internal-spec/plugin*.md`](docs/internal-spec/).
|
|
406
|
+
|
|
220
407
|
## Configuration
|
|
221
408
|
|
|
222
409
|
`rigor init` writes a starter `.rigor.yml`:
|
|
@@ -226,19 +413,42 @@ bundle exec rigor init # fails if .rigor.yml exists
|
|
|
226
413
|
bundle exec rigor init --force # overwrite
|
|
227
414
|
```
|
|
228
415
|
|
|
229
|
-
|
|
230
|
-
|
|
416
|
+
Common knobs the file exposes:
|
|
417
|
+
|
|
418
|
+
- `paths` — directories `rigor check` and `rigor type-scan`
|
|
419
|
+
scan when no path is given (defaults to `lib`).
|
|
420
|
+
- `target_ruby` — minimum Ruby version your project targets.
|
|
421
|
+
- `libraries` — extra stdlib libraries to load on top of the
|
|
422
|
+
bundled defaults (e.g. `["csv", "set"]`).
|
|
423
|
+
- `signature_paths` — explicit list of `sig/`-style directories.
|
|
424
|
+
Leave unset (or `null`) to auto-detect `<root>/sig`. Use `[]`
|
|
425
|
+
to disable project-RBS loading entirely.
|
|
426
|
+
- `disable` — rule identifiers to silence project-wide. Shipped
|
|
427
|
+
rules: `undefined-method`, `wrong-arity`,
|
|
428
|
+
`argument-type-mismatch`, `possible-nil-receiver`,
|
|
429
|
+
`dump-type`, `assert-type`, `always-raises`. In-source
|
|
430
|
+
`# rigor:disable <rule>` end-of-line comments silence
|
|
431
|
+
per-line; `# rigor:disable all` suppresses every rule.
|
|
231
432
|
|
|
232
433
|
## Status
|
|
233
434
|
|
|
234
|
-
Current
|
|
235
|
-
analyzer is usable on real Ruby code today but the rule
|
|
435
|
+
Current released version: **`v0.0.8`** (the eighth preview).
|
|
436
|
+
The analyzer is usable on real Ruby code today but the rule
|
|
236
437
|
catalogue is deliberately narrow — Rigor's stance is to surface
|
|
237
438
|
zero false positives while the inference surface stabilises.
|
|
238
439
|
The roadmap is tracked in
|
|
239
440
|
[`docs/MILESTONES.md`](docs/MILESTONES.md); release-by-release
|
|
240
441
|
detail lives in [`CHANGELOG.md`](CHANGELOG.md).
|
|
241
442
|
|
|
443
|
+
`v0.0.9` is the active development cluster on `master` and
|
|
444
|
+
covers the persistent cache infrastructure (`.rigor/cache/`,
|
|
445
|
+
`--cache-stats`, `--clear-cache`, `--no-cache`),
|
|
446
|
+
paired-complement Refined narrowing, `literal-string` flow
|
|
447
|
+
tracking, the `Rigor::FlowContribution` bundle struct, and
|
|
448
|
+
six additional built-in catalogues (Random, Struct, Encoding,
|
|
449
|
+
Regexp + MatchData, Proc / Method / UnboundMethod, Exception).
|
|
450
|
+
The next release after `0.0.9` will be `0.1.0`.
|
|
451
|
+
|
|
242
452
|
## Contributing
|
|
243
453
|
|
|
244
454
|
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the minimal
|
|
@@ -248,3 +458,5 @@ skill documentation contributors should know about.
|
|
|
248
458
|
## License
|
|
249
459
|
|
|
250
460
|
Mozilla Public License Version 2.0. See [`LICENSE`](LICENSE).
|
|
461
|
+
</content>
|
|
462
|
+
</invoke>
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# DO NOT EDIT — generated by tool/extract_builtin_catalog.rb
|
|
2
|
+
---
|
|
3
|
+
schema_version: 1
|
|
4
|
+
generated_from:
|
|
5
|
+
ruby_init_c: references/ruby/encoding.c
|
|
6
|
+
ruby_prelude:
|
|
7
|
+
rbs:
|
|
8
|
+
- references/rbs/core/encoding.rbs
|
|
9
|
+
purity_levels:
|
|
10
|
+
leaf: Prelude :leaf marker (VM-enforced) or C body uses no dispatch/yield/mutation.
|
|
11
|
+
trivial: Prelude method body is a literal return (self/true/false/nil/Integer).
|
|
12
|
+
leaf_when_numeric: C body falls through to rb_num_coerce_* only when an operand
|
|
13
|
+
is non-numeric; safe to fold when every argument is a concrete numeric.
|
|
14
|
+
inline_block: Prelude method carries :inline_block or :use_block; block-dependent.
|
|
15
|
+
block_dependent: C body yields or checks rb_block_given_p.
|
|
16
|
+
mutates_self: C body checks rb_check_frozen — typically a prelude to mutation.
|
|
17
|
+
dispatch: C body calls user-redefinable methods (rb_funcall*, rb_equal, rb_Float,
|
|
18
|
+
num_funcall*, etc).
|
|
19
|
+
unknown: C body not located in indexed C files.
|
|
20
|
+
classes:
|
|
21
|
+
Encoding:
|
|
22
|
+
parent: Object
|
|
23
|
+
defined_at: references/ruby/encoding.c:1992
|
|
24
|
+
includes: []
|
|
25
|
+
constants: {}
|
|
26
|
+
aliases:
|
|
27
|
+
to_s:
|
|
28
|
+
old: name
|
|
29
|
+
source: c
|
|
30
|
+
defined_at: references/ruby/encoding.c:2001
|
|
31
|
+
instance_methods:
|
|
32
|
+
inspect:
|
|
33
|
+
source: c
|
|
34
|
+
cfunc: enc_inspect
|
|
35
|
+
arity: 0
|
|
36
|
+
defined_at: references/ruby/encoding.c:2003
|
|
37
|
+
c_body_at: references/ruby/encoding.c:1344
|
|
38
|
+
c_effects:
|
|
39
|
+
- raises
|
|
40
|
+
purity: leaf
|
|
41
|
+
rbs:
|
|
42
|
+
- "() -> String"
|
|
43
|
+
rbs_at: references/rbs/core/encoding.rbs:281
|
|
44
|
+
names:
|
|
45
|
+
source: c
|
|
46
|
+
cfunc: enc_names
|
|
47
|
+
arity: 0
|
|
48
|
+
defined_at: references/ruby/encoding.c:2004
|
|
49
|
+
c_body_at: references/ruby/encoding.c:1384
|
|
50
|
+
c_effects: []
|
|
51
|
+
purity: leaf
|
|
52
|
+
rbs:
|
|
53
|
+
- "() -> Array[String]"
|
|
54
|
+
rbs_at: references/rbs/core/encoding.rbs:298
|
|
55
|
+
dummy?:
|
|
56
|
+
source: c
|
|
57
|
+
cfunc: enc_dummy_p
|
|
58
|
+
arity: 0
|
|
59
|
+
defined_at: references/ruby/encoding.c:2005
|
|
60
|
+
c_body_at: references/ruby/encoding.c:663
|
|
61
|
+
c_effects: []
|
|
62
|
+
purity: leaf
|
|
63
|
+
rbs:
|
|
64
|
+
- "() -> bool"
|
|
65
|
+
rbs_at: references/rbs/core/encoding.rbs:270
|
|
66
|
+
ascii_compatible?:
|
|
67
|
+
source: c
|
|
68
|
+
cfunc: enc_ascii_compatible_p
|
|
69
|
+
arity: 0
|
|
70
|
+
defined_at: references/ruby/encoding.c:2006
|
|
71
|
+
c_body_at: references/ruby/encoding.c:679
|
|
72
|
+
c_effects: []
|
|
73
|
+
purity: leaf
|
|
74
|
+
rbs:
|
|
75
|
+
- "() -> bool"
|
|
76
|
+
rbs_at: references/rbs/core/encoding.rbs:257
|
|
77
|
+
_dump:
|
|
78
|
+
source: c
|
|
79
|
+
cfunc: enc_dump
|
|
80
|
+
arity: -1
|
|
81
|
+
defined_at: references/ruby/encoding.c:2013
|
|
82
|
+
c_body_at: references/ruby/encoding.c:1502
|
|
83
|
+
c_effects: []
|
|
84
|
+
purity: leaf
|
|
85
|
+
singleton_methods:
|
|
86
|
+
list:
|
|
87
|
+
source: c
|
|
88
|
+
cfunc: enc_list
|
|
89
|
+
arity: 0
|
|
90
|
+
defined_at: references/ruby/encoding.c:2007
|
|
91
|
+
c_body_at: references/ruby/encoding.c:1416
|
|
92
|
+
c_effects: []
|
|
93
|
+
purity: leaf
|
|
94
|
+
rbs:
|
|
95
|
+
- "() -> Array[Encoding]"
|
|
96
|
+
rbs_at: references/rbs/core/encoding.rbs:232
|
|
97
|
+
name_list:
|
|
98
|
+
source: c
|
|
99
|
+
cfunc: rb_enc_name_list
|
|
100
|
+
arity: 0
|
|
101
|
+
defined_at: references/ruby/encoding.c:2008
|
|
102
|
+
c_body_at: references/ruby/encoding.c:1895
|
|
103
|
+
c_effects: []
|
|
104
|
+
purity: leaf
|
|
105
|
+
rbs:
|
|
106
|
+
- "() -> Array[String]"
|
|
107
|
+
rbs_at: references/rbs/core/encoding.rbs:246
|
|
108
|
+
aliases:
|
|
109
|
+
source: c
|
|
110
|
+
cfunc: rb_enc_aliases
|
|
111
|
+
arity: 0
|
|
112
|
+
defined_at: references/ruby/encoding.c:2009
|
|
113
|
+
c_body_at: references/ruby/encoding.c:1941
|
|
114
|
+
c_effects: []
|
|
115
|
+
purity: leaf
|
|
116
|
+
rbs:
|
|
117
|
+
- "() -> Hash[String, String]"
|
|
118
|
+
rbs_at: references/rbs/core/encoding.rbs:66
|
|
119
|
+
find:
|
|
120
|
+
source: c
|
|
121
|
+
cfunc: enc_find
|
|
122
|
+
arity: 1
|
|
123
|
+
defined_at: references/ruby/encoding.c:2010
|
|
124
|
+
c_body_at: references/ruby/encoding.c:1445
|
|
125
|
+
c_effects: []
|
|
126
|
+
purity: leaf
|
|
127
|
+
rbs:
|
|
128
|
+
- "(encoding enc) -> Encoding?"
|
|
129
|
+
rbs_at: references/rbs/core/encoding.rbs:213
|
|
130
|
+
compatible?:
|
|
131
|
+
source: c
|
|
132
|
+
cfunc: enc_compatible_p
|
|
133
|
+
arity: 2
|
|
134
|
+
defined_at: references/ruby/encoding.c:2011
|
|
135
|
+
c_body_at: references/ruby/encoding.c:1480
|
|
136
|
+
c_effects: []
|
|
137
|
+
purity: leaf
|
|
138
|
+
rbs:
|
|
139
|
+
- "(untyped obj1, untyped obj2) -> Encoding?"
|
|
140
|
+
rbs_at: references/rbs/core/encoding.rbs:91
|
|
141
|
+
_load:
|
|
142
|
+
source: c
|
|
143
|
+
cfunc: enc_load
|
|
144
|
+
arity: 1
|
|
145
|
+
defined_at: references/ruby/encoding.c:2014
|
|
146
|
+
c_body_at: references/ruby/encoding.c:1510
|
|
147
|
+
c_effects: []
|
|
148
|
+
purity: leaf
|
|
149
|
+
rbs:
|
|
150
|
+
- "[T] (T) -> T"
|
|
151
|
+
rbs_at: references/rbs/core/encoding.rbs:30
|
|
152
|
+
default_external:
|
|
153
|
+
source: c
|
|
154
|
+
cfunc: get_default_external
|
|
155
|
+
arity: 0
|
|
156
|
+
defined_at: references/ruby/encoding.c:2016
|
|
157
|
+
c_body_at: references/ruby/encoding.c:1704
|
|
158
|
+
c_effects: []
|
|
159
|
+
purity: leaf
|
|
160
|
+
rbs:
|
|
161
|
+
- "() -> Encoding"
|
|
162
|
+
rbs_at: references/rbs/core/encoding.rbs:121
|
|
163
|
+
default_external=:
|
|
164
|
+
source: c
|
|
165
|
+
cfunc: set_default_external
|
|
166
|
+
arity: 1
|
|
167
|
+
defined_at: references/ruby/encoding.c:2017
|
|
168
|
+
c_body_at: references/ruby/encoding.c:1733
|
|
169
|
+
c_effects: []
|
|
170
|
+
purity: leaf
|
|
171
|
+
rbs:
|
|
172
|
+
- "(Encoding enc) -> Encoding"
|
|
173
|
+
- "[T < _ToStr] (T enc) -> T"
|
|
174
|
+
rbs_at: references/rbs/core/encoding.rbs:135
|
|
175
|
+
default_internal:
|
|
176
|
+
source: c
|
|
177
|
+
cfunc: get_default_internal
|
|
178
|
+
arity: 0
|
|
179
|
+
defined_at: references/ruby/encoding.c:2018
|
|
180
|
+
c_body_at: references/ruby/encoding.c:1787
|
|
181
|
+
c_effects: []
|
|
182
|
+
purity: leaf
|
|
183
|
+
rbs:
|
|
184
|
+
- "() -> Encoding?"
|
|
185
|
+
rbs_at: references/rbs/core/encoding.rbs:166
|
|
186
|
+
default_internal=:
|
|
187
|
+
source: c
|
|
188
|
+
cfunc: set_default_internal
|
|
189
|
+
arity: 1
|
|
190
|
+
defined_at: references/ruby/encoding.c:2019
|
|
191
|
+
c_body_at: references/ruby/encoding.c:1813
|
|
192
|
+
c_effects: []
|
|
193
|
+
purity: leaf
|
|
194
|
+
rbs:
|
|
195
|
+
- "(Encoding enc) -> Encoding"
|
|
196
|
+
- "[T < _ToStr] (T enc) -> T"
|
|
197
|
+
- "(nil) -> nil"
|
|
198
|
+
rbs_at: references/rbs/core/encoding.rbs:181
|
|
199
|
+
locale_charmap:
|
|
200
|
+
source: c
|
|
201
|
+
cfunc: rb_locale_charmap
|
|
202
|
+
arity: 0
|
|
203
|
+
defined_at: references/ruby/encoding.c:2020
|
|
204
|
+
purity: unknown
|
|
205
|
+
c_body_at: not_found
|
|
206
|
+
rbs:
|
|
207
|
+
- "() -> String"
|
|
208
|
+
rbs_at: references/rbs/core/encoding.rbs:54
|
|
209
|
+
undefined:
|
|
210
|
+
- new
|