rigortype 0.2.0 → 0.2.2
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 +82 -20
- data/data/core_overlay/numeric.rbs +33 -0
- data/data/core_overlay/pathname.rbs +25 -0
- data/data/core_overlay/string_scanner.rbs +28 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +473 -0
- data/data/vendored_gem_sigs/ast/ast.rbs +130 -0
- data/data/vendored_gem_sigs/bcrypt/bcrypt.rbs +47 -0
- data/data/vendored_gem_sigs/bundler/bundler.rbs +238 -0
- data/data/vendored_gem_sigs/cgi/cgi_extras.rbs +34 -0
- data/data/vendored_gem_sigs/did_you_mean/did_you_mean_extras.rbs +34 -0
- data/data/vendored_gem_sigs/idn-ruby/idn.rbs +54 -0
- data/data/vendored_gem_sigs/mysql2/client.rbs +55 -0
- data/data/vendored_gem_sigs/mysql2/error.rbs +5 -0
- data/data/vendored_gem_sigs/mysql2/result.rbs +31 -0
- data/data/vendored_gem_sigs/mysql2/statement.rbs +5 -0
- data/data/vendored_gem_sigs/nokogiri/nokogiri.rbs +2332 -0
- data/data/vendored_gem_sigs/nokogiri/nokogiri_html5.rbs +47 -0
- data/data/vendored_gem_sigs/pg/pg.rbs +212 -0
- data/data/vendored_gem_sigs/prism/prism_supplement.rbs +44 -0
- data/data/vendored_gem_sigs/redis/errors.rbs +50 -0
- data/data/vendored_gem_sigs/redis/future.rbs +5 -0
- data/data/vendored_gem_sigs/redis/redis.rbs +348 -0
- data/data/vendored_gem_sigs/redis/redis_extras.rbs +130 -0
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +226 -0
- data/docs/handbook/01-getting-started.md +311 -0
- data/docs/handbook/02-everyday-types.md +337 -0
- data/docs/handbook/03-narrowing.md +359 -0
- data/docs/handbook/04-tuples-and-shapes.md +321 -0
- data/docs/handbook/05-methods-and-blocks.md +339 -0
- data/docs/handbook/06-classes.md +305 -0
- data/docs/handbook/07-rbs-and-extended.md +427 -0
- data/docs/handbook/08-understanding-errors.md +373 -0
- data/docs/handbook/09-plugins.md +241 -0
- data/docs/handbook/10-sorbet.md +347 -0
- data/docs/handbook/11-sig-gen.md +312 -0
- data/docs/handbook/12-lightweight-hkt.md +333 -0
- data/docs/handbook/README.md +275 -0
- data/docs/handbook/appendix-elixir.md +370 -0
- data/docs/handbook/appendix-go.md +399 -0
- data/docs/handbook/appendix-java-csharp.md +470 -0
- data/docs/handbook/appendix-liskov.md +580 -0
- data/docs/handbook/appendix-mypy.md +370 -0
- data/docs/handbook/appendix-phpstan.md +338 -0
- data/docs/handbook/appendix-protocols-and-structural-typing.md +292 -0
- data/docs/handbook/appendix-rust.md +446 -0
- data/docs/handbook/appendix-steep.md +336 -0
- data/docs/handbook/appendix-type-theory.md +1662 -0
- data/docs/handbook/appendix-typeprof.md +416 -0
- data/docs/handbook/appendix-typescript.md +332 -0
- data/docs/install.md +189 -0
- data/docs/llms.txt +72 -0
- data/docs/manual/01-installation.md +342 -0
- data/docs/manual/02-cli-reference.md +557 -0
- data/docs/manual/03-configuration.md +152 -0
- data/docs/manual/04-diagnostics.md +206 -0
- data/docs/manual/05-inspecting-types.md +109 -0
- data/docs/manual/06-baseline.md +104 -0
- data/docs/manual/07-plugins.md +92 -0
- data/docs/manual/08-skills.md +143 -0
- data/docs/manual/09-editor-integration.md +245 -0
- data/docs/manual/10-mcp-server.md +532 -0
- data/docs/manual/11-ci.md +274 -0
- data/docs/manual/12-caching.md +116 -0
- data/docs/manual/13-troubleshooting.md +120 -0
- data/docs/manual/14-rails-quickstart.md +332 -0
- data/docs/manual/15-type-protection-coverage.md +204 -0
- data/docs/manual/16-rbs-extended-annotations.md +190 -0
- data/docs/manual/17-driving-improvement.md +160 -0
- data/docs/manual/README.md +87 -0
- data/docs/manual/ci-templates/README.md +58 -0
- data/docs/manual/plugins/README.md +86 -0
- data/docs/manual/plugins/rigor-actioncable.md +78 -0
- data/docs/manual/plugins/rigor-actionmailer.md +74 -0
- data/docs/manual/plugins/rigor-actionpack.md +80 -0
- data/docs/manual/plugins/rigor-activejob.md +58 -0
- data/docs/manual/plugins/rigor-activerecord.md +102 -0
- data/docs/manual/plugins/rigor-activestorage.md +74 -0
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +86 -0
- data/docs/manual/plugins/rigor-devise.md +70 -0
- data/docs/manual/plugins/rigor-dry-schema.md +56 -0
- data/docs/manual/plugins/rigor-dry-struct.md +60 -0
- data/docs/manual/plugins/rigor-dry-types.md +59 -0
- data/docs/manual/plugins/rigor-dry-validation.md +62 -0
- data/docs/manual/plugins/rigor-factorybot.md +76 -0
- data/docs/manual/plugins/rigor-graphql.md +89 -0
- data/docs/manual/plugins/rigor-hanami.md +83 -0
- data/docs/manual/plugins/rigor-mangrove.md +73 -0
- data/docs/manual/plugins/rigor-minitest.md +86 -0
- data/docs/manual/plugins/rigor-pundit.md +72 -0
- data/docs/manual/plugins/rigor-rails-i18n.md +92 -0
- data/docs/manual/plugins/rigor-rails-routes.md +94 -0
- data/docs/manual/plugins/rigor-rails.md +44 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +83 -0
- data/docs/manual/plugins/rigor-rspec-rails.md +72 -0
- data/docs/manual/plugins/rigor-rspec.md +86 -0
- data/docs/manual/plugins/rigor-shoulda-matchers.md +78 -0
- data/docs/manual/plugins/rigor-sidekiq.md +78 -0
- data/docs/manual/plugins/rigor-sinatra.md +61 -0
- data/docs/manual/plugins/rigor-sorbet.md +63 -0
- data/docs/manual/plugins/rigor-statesman.md +75 -0
- data/docs/manual/plugins/rigor-typescript-utility-types.md +71 -0
- data/exe/rigor +1 -1
- data/lib/rigor/analysis/incremental_session.rb +4 -2
- data/lib/rigor/analysis/run_stats.rb +13 -1
- data/lib/rigor/analysis/runner.rb +54 -12
- data/lib/rigor/cli/check_command.rb +26 -3
- data/lib/rigor/cli/coverage_command.rb +67 -92
- data/lib/rigor/cli/coverage_mutation.rb +149 -0
- data/lib/rigor/cli/docs_command.rb +248 -0
- data/lib/rigor/cli/fused_protection_renderer.rb +67 -0
- data/lib/rigor/cli/fused_protection_report.rb +76 -0
- data/lib/rigor/cli/skill_command.rb +103 -41
- data/lib/rigor/cli/skill_describe.rb +346 -0
- data/lib/rigor/cli.rb +25 -3
- data/lib/rigor/config_audit.rb +152 -0
- data/lib/rigor/configuration.rb +12 -0
- data/lib/rigor/environment/rbs_loader.rb +27 -0
- data/lib/rigor/environment.rb +49 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +140 -38
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +37 -6
- data/lib/rigor/inference/scope_indexer.rb +87 -89
- data/lib/rigor/inference/statement_evaluator.rb +27 -0
- data/lib/rigor/plugin/isolation.rb +5 -5
- data/lib/rigor/plugin/loader.rb +4 -2
- data/lib/rigor/protection/diagnostic_oracle.rb +51 -0
- data/lib/rigor/protection/mutation_scanner.rb +98 -38
- data/lib/rigor/protection/mutator.rb +21 -0
- data/lib/rigor/protection/test_suite_oracle.rb +68 -0
- data/lib/rigor/signature_path_audit.rb +92 -0
- data/lib/rigor/version.rb +1 -1
- data/skills/rigor-ask/SKILL.md +172 -0
- data/skills/rigor-doctor/SKILL.md +87 -0
- data/skills/rigor-editor-setup/SKILL.md +114 -0
- data/skills/rigor-mcp-setup/SKILL.md +117 -0
- data/skills/rigor-monkeypatch-resolve/SKILL.md +79 -0
- data/skills/rigor-next-steps/SKILL.md +113 -0
- data/skills/rigor-plugin-tune/SKILL.md +79 -0
- data/skills/rigor-protection-uplift/SKILL.md +133 -0
- data/skills/rigor-rbs-setup/SKILL.md +128 -0
- data/skills/rigor-upgrade/SKILL.md +79 -0
- metadata +120 -1
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# Classes
|
|
2
|
+
|
|
3
|
+
This chapter covers class-side typing — what `self` means in
|
|
4
|
+
different positions, how constants are resolved, and how
|
|
5
|
+
Rigor reads `attr_*`, `Data.define`, and `Struct.new`
|
|
6
|
+
declarations. It is more reference than narrative: read it
|
|
7
|
+
through once, then jump back to the section you need.
|
|
8
|
+
|
|
9
|
+
## Instance-side and class-side `self`
|
|
10
|
+
|
|
11
|
+
Inside an instance method body, `self` is a `Nominal[T]` of
|
|
12
|
+
the enclosing class:
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
class User
|
|
16
|
+
def name
|
|
17
|
+
self # Nominal[User]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Inside a singleton method body (`def self.foo` or
|
|
23
|
+
`def User.foo`), `self` is a `Singleton[T]` — the class
|
|
24
|
+
object itself, not an instance:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
class User
|
|
28
|
+
def self.find(id)
|
|
29
|
+
self # Singleton[User]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
User # Singleton[User]
|
|
34
|
+
User.find(1) # Nominal[User] (declared by RBS)
|
|
35
|
+
User.new # Nominal[User]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The distinction matters for method dispatch: instance methods
|
|
39
|
+
run on `Nominal[User]`, singleton methods run on
|
|
40
|
+
`Singleton[User]`. Rigor reads the right side of the colon in
|
|
41
|
+
RBS sigs (`def self.find: (Integer) -> User`) to know which
|
|
42
|
+
side a method lives on.
|
|
43
|
+
|
|
44
|
+
## Constants
|
|
45
|
+
|
|
46
|
+
Constant lookup walks four sources, in this order:
|
|
47
|
+
|
|
48
|
+
1. **Lexical scope.** If `Foo` is referenced inside
|
|
49
|
+
`class A; module B; ...`, Rigor looks for `A::B::Foo`,
|
|
50
|
+
`A::Foo`, `Foo`.
|
|
51
|
+
2. **RBS-core and bundled stdlib.** `String`, `Integer`,
|
|
52
|
+
`Symbol`, `Array`, `Pathname`, `URI`, `OptParse`, `JSON`,
|
|
53
|
+
`YAML`, etc.
|
|
54
|
+
3. **Project RBS.** `sig/` files in your project add to the
|
|
55
|
+
lookup.
|
|
56
|
+
4. **In-source class discovery.** When no RBS exists, Rigor
|
|
57
|
+
walks `class Foo`, `module Bar`, and constant assignments
|
|
58
|
+
(`MAX = 100`).
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
MAX = 100
|
|
62
|
+
class Counter
|
|
63
|
+
def initial = MAX
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
Counter.new.initial # Constant<100> — the constant value
|
|
67
|
+
# propagates through the in-source
|
|
68
|
+
# class lookup
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
For constants whose right-hand side Rigor can fold, the
|
|
72
|
+
constant carries a `Constant<value>` type. For others, it
|
|
73
|
+
carries the wider RBS-erased form.
|
|
74
|
+
|
|
75
|
+
## `attr_reader`, `attr_writer`, `attr_accessor`
|
|
76
|
+
|
|
77
|
+
Rigor reads `attr_*` declarations and treats them as method
|
|
78
|
+
definitions. The reader's return type matches the
|
|
79
|
+
corresponding ivar's inferred type:
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
class User
|
|
83
|
+
attr_reader :name
|
|
84
|
+
|
|
85
|
+
def initialize(name)
|
|
86
|
+
@name = name
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
u = User.new("Alice")
|
|
91
|
+
u.name # Constant<"Alice"> — through in-source dispatch +
|
|
92
|
+
# ivar tracking
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`attr_writer` exposes the setter; `attr_accessor` exposes
|
|
96
|
+
both. The setter's argument type is whatever the call site
|
|
97
|
+
provides. The `def.ivar-write-mismatch` rule (v0.1.2) checks
|
|
98
|
+
that two writes to the same ivar in the same class body
|
|
99
|
+
agree on the concrete class — see
|
|
100
|
+
[Chapter 8 — Understanding errors](08-understanding-errors.md)
|
|
101
|
+
for the rule's exact contract; it lets you catch an
|
|
102
|
+
accidental rebind from `String` to `Array` in the same class
|
|
103
|
+
without authoring an explicit ivar type.
|
|
104
|
+
|
|
105
|
+
## Instance variables across methods
|
|
106
|
+
|
|
107
|
+
Rigor accumulates ivar facts across all methods in a class:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
class Counter
|
|
111
|
+
def initialize
|
|
112
|
+
@count = 0 # @count: Constant<0> after init
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def bump
|
|
116
|
+
@count += 1 # @count rebound to int<1, max>
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def value
|
|
120
|
+
@count # int<0, max> (union of seen writes)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The ivar type at each read site is the union of every
|
|
126
|
+
statically-visible write — including writes from a different
|
|
127
|
+
method on the same class.
|
|
128
|
+
|
|
129
|
+
## `Data.define`
|
|
130
|
+
|
|
131
|
+
`Data.define` produces a small immutable struct. Rigor
|
|
132
|
+
recognises the declaration and surfaces the constructor
|
|
133
|
+
arity, the per-field accessors, and the resulting class
|
|
134
|
+
type:
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
Point = Data.define(:x, :y)
|
|
138
|
+
|
|
139
|
+
p = Point.new(x: 3, y: 4)
|
|
140
|
+
assert_type("Point(x: 3, y: 4)", p)
|
|
141
|
+
assert_type("3", p.x)
|
|
142
|
+
assert_type("4", p.y)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Member reads are not the only thing that folds. The standard
|
|
146
|
+
`Data` projections stay precise too — `p[:x]`, `p.to_h`,
|
|
147
|
+
`p.deconstruct`, `p.members`, and `p.with(x: 9)` all carry the
|
|
148
|
+
per-member types through:
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
p.to_h # HashShape{x: 3, y: 4}
|
|
152
|
+
p.members # Tuple[Constant<:x>, Constant<:y>]
|
|
153
|
+
p.with(x: 9).x # Constant<9>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The folding covers all three definition forms — a constant
|
|
157
|
+
(`Point = Data.define(...)`), the `class X < Data.define(...)`
|
|
158
|
+
subclass idiom, and a bare local — and both positional and
|
|
159
|
+
keyword construction.
|
|
160
|
+
|
|
161
|
+
The discovery walks `define_method`-style block bodies too,
|
|
162
|
+
so `Point = Data.define(:x, :y) do ... end` still works,
|
|
163
|
+
including a block-defined `def initialize(...)` whose
|
|
164
|
+
arguments override the synthesised keyword-argument
|
|
165
|
+
constructor (v0.1.2). The same rule covers
|
|
166
|
+
`Const = Struct.new(*Symbol) do ... end` — block-body method
|
|
167
|
+
discovery composes uniformly across both shapes.
|
|
168
|
+
|
|
169
|
+
## `Struct.new`
|
|
170
|
+
|
|
171
|
+
`Struct.new(*Symbol)` produces a positional-arg constructor
|
|
172
|
+
plus the same accessors as `Data.define`. Rigor folds struct
|
|
173
|
+
member reads too, but — because a `Struct` is mutable — only
|
|
174
|
+
where the value cannot have changed:
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
Coord = Struct.new(:x, :y)
|
|
178
|
+
|
|
179
|
+
# A never-mutated struct local folds its member reads.
|
|
180
|
+
c = Coord.new(10, 20)
|
|
181
|
+
assert_type("10", c.x)
|
|
182
|
+
assert_type("20", c.y)
|
|
183
|
+
|
|
184
|
+
# A local that is mutated, aliased, or escapes is not fold-safe —
|
|
185
|
+
# its reads degrade to Dynamic, never a stale value.
|
|
186
|
+
m = Coord.new(1, 2)
|
|
187
|
+
m.x = 9
|
|
188
|
+
assert_type("Dynamic[top]", m.x)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Because `Struct` accessors are also writers, the fold is gated:
|
|
192
|
+
a member read off a freshly constructed instance (`Coord.new(1,
|
|
193
|
+
2).x`) or a local the analysis proves is never written, aliased,
|
|
194
|
+
or passed away folds to the member's type; anything else widens
|
|
195
|
+
to `Dynamic[top]`. `Data` is read-only, so its reads always fold.
|
|
196
|
+
|
|
197
|
+
## Inheritance and method resolution
|
|
198
|
+
|
|
199
|
+
When you call a method on `Nominal[Subclass]`, Rigor walks
|
|
200
|
+
the class hierarchy: subclass's RBS / in-source body first,
|
|
201
|
+
then each ancestor's RBS / body, then included modules in
|
|
202
|
+
their declaration order. The first one to define the method
|
|
203
|
+
wins.
|
|
204
|
+
|
|
205
|
+
The hierarchy is read from:
|
|
206
|
+
|
|
207
|
+
- RBS `class Foo < Bar` declarations.
|
|
208
|
+
- In-source `class Foo < Bar` lines.
|
|
209
|
+
- `include` / `prepend` / `extend` calls Rigor walked.
|
|
210
|
+
|
|
211
|
+
When the hierarchy is statically incomplete (a class
|
|
212
|
+
references a parent Rigor cannot locate), the receiver type
|
|
213
|
+
falls back to the deepest known ancestor — never to
|
|
214
|
+
`Dynamic[top]` for a class Rigor saw the declaration of.
|
|
215
|
+
|
|
216
|
+
## `class` and `singleton(C)` types
|
|
217
|
+
|
|
218
|
+
Method signatures sometimes return "the class object itself":
|
|
219
|
+
|
|
220
|
+
```rbs
|
|
221
|
+
class Foo
|
|
222
|
+
def self.factory: () -> Foo # returns an instance
|
|
223
|
+
def self.subclasses: () -> Array[singleton(Foo)] # returns class objects
|
|
224
|
+
end
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`singleton(Foo)` is the type of the class object `Foo`.
|
|
228
|
+
`Singleton[Foo]` (Rigor's internal carrier display form) is
|
|
229
|
+
the same idea. `Foo` (in `Array[Foo]`) means "an instance of
|
|
230
|
+
`Foo`" / `Nominal[Foo]`.
|
|
231
|
+
|
|
232
|
+
Calling an instance method on a `singleton(Foo)` is an error
|
|
233
|
+
unless `Foo` itself defines that singleton method — `String`
|
|
234
|
+
is `singleton(String)`, `String#upcase` is on instances, so
|
|
235
|
+
`String.upcase` flags `call.undefined-method`.
|
|
236
|
+
|
|
237
|
+
## Custom `case_eq` (`===`)
|
|
238
|
+
|
|
239
|
+
Rigor recognises `===` for `Class` / `Module` / `Range` /
|
|
240
|
+
`Regexp` — these are the standard `case x; when …` shapes.
|
|
241
|
+
Custom `case_eq` implementations on user classes are NOT
|
|
242
|
+
recognised:
|
|
243
|
+
|
|
244
|
+
```ruby
|
|
245
|
+
class IPv4
|
|
246
|
+
def self.===(s)
|
|
247
|
+
s.match?(/\A\d+\.\d+\.\d+\.\d+\z/)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
case some_input
|
|
252
|
+
when IPv4
|
|
253
|
+
# Rigor does not narrow `some_input` here — IPv4.=== is a
|
|
254
|
+
# user-defined case-equality, which the engine cannot prove
|
|
255
|
+
# narrows a specific class.
|
|
256
|
+
some_input
|
|
257
|
+
end
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
For these cases, write an explicit `is_a?` / `respond_to?`
|
|
261
|
+
guard, or use an `RBS::Extended` `predicate-if-true` directive
|
|
262
|
+
on the `===` method (see [Chapter 7](07-rbs-and-extended.md)).
|
|
263
|
+
|
|
264
|
+
## Constant-decl alias classes
|
|
265
|
+
|
|
266
|
+
Some Ruby idioms create a class alias by constant assignment:
|
|
267
|
+
|
|
268
|
+
```ruby
|
|
269
|
+
YAML = Psych
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
When the right-hand side is itself a class, Rigor follows the
|
|
273
|
+
alias for receiver typing — `YAML.load(...)` is treated as
|
|
274
|
+
`Psych.load(...)`. Method-existence checks deliberately stay
|
|
275
|
+
silent on the aliased name, however; the analyzer cannot
|
|
276
|
+
distinguish a deliberate alias from an accidental shadowing
|
|
277
|
+
without more context, so `YAML.unknown` does not fire
|
|
278
|
+
`call.undefined-method`. Use the canonical name when you need
|
|
279
|
+
the diagnostic.
|
|
280
|
+
|
|
281
|
+
## Modules
|
|
282
|
+
|
|
283
|
+
`module M; def foo; end; end` is structurally similar to a
|
|
284
|
+
class for typing purposes. Methods are looked up the same
|
|
285
|
+
way; `include M` adds `M`'s methods to the including class's
|
|
286
|
+
hierarchy.
|
|
287
|
+
|
|
288
|
+
`extend self`-style mixin patterns (`module_function` /
|
|
289
|
+
`extend self`) are recognised — both instance-side and
|
|
290
|
+
singleton-side surface the same methods.
|
|
291
|
+
|
|
292
|
+
## `protected` and `private`
|
|
293
|
+
|
|
294
|
+
Rigor reads visibility modifiers and respects them in the
|
|
295
|
+
limited context of `def.method-visibility-mismatch` rules
|
|
296
|
+
(future). Today, calling a private method on an external
|
|
297
|
+
receiver does not fire a diagnostic — visibility is more a
|
|
298
|
+
concern for `rubocop-style` linters than a type-system
|
|
299
|
+
question.
|
|
300
|
+
|
|
301
|
+
## What's next
|
|
302
|
+
|
|
303
|
+
Chapter 7 covers RBS and `RBS::Extended` — the external
|
|
304
|
+
signature surface that takes you beyond what inference alone
|
|
305
|
+
can prove.
|