rigortype 0.4.3 → 0.4.4
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 +1 -1
- data/data/core_overlay/ostruct.rbs +40 -0
- data/docs/handbook/11-sig-gen.md +16 -0
- data/docs/manual/02-cli-reference.md +4 -3
- data/docs/manual/03-configuration.md +16 -5
- data/docs/manual/04-diagnostics.md +44 -2
- data/docs/manual/07-plugins.md +1 -1
- data/docs/manual/10-mcp-server.md +32 -3
- data/docs/manual/18-removing-dead-code.md +11 -1
- data/docs/manual/plugins/rigor-rails-routes.md +2 -2
- data/docs/manual/plugins/rigor-shoulda-matchers.md +4 -1
- data/exe/rigor +12 -17
- data/lib/rigor/analysis/check_rules/rule_ids.rb +57 -0
- data/lib/rigor/analysis/check_rules.rb +46 -36
- data/lib/rigor/analysis/incremental.rb +12 -0
- data/lib/rigor/analysis/incremental_session.rb +121 -21
- data/lib/rigor/analysis/reachability/graph.rb +23 -3
- data/lib/rigor/analysis/reachability/scan.rb +51 -9
- data/lib/rigor/analysis/rule_catalog.rb +6 -1
- data/lib/rigor/analysis/run_cache_key.rb +31 -7
- data/lib/rigor/analysis/run_cache_probe.rb +5 -3
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +48 -12
- data/lib/rigor/analysis/runner/pool_coordinator.rb +110 -15
- data/lib/rigor/analysis/runner/project_pre_passes.rb +29 -4
- data/lib/rigor/analysis/runner.rb +35 -9
- data/lib/rigor/analysis/severity_stamp.rb +34 -1
- data/lib/rigor/analysis/worker_session.rb +57 -1
- data/lib/rigor/cache/descriptor.rb +40 -6
- data/lib/rigor/cache/incremental_snapshot.rb +20 -4
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/config_audit.rb +111 -15
- data/lib/rigor/configuration/dependencies.rb +19 -11
- data/lib/rigor/environment/rbs_loader.rb +46 -10
- data/lib/rigor/inference/block_call_timing.rb +5 -0
- data/lib/rigor/inference/captured_locals.rb +26 -3
- data/lib/rigor/inference/definer_resolution.rb +16 -7
- data/lib/rigor/inference/expression_typer.rb +59 -4
- data/lib/rigor/inference/guard_rebinding.rb +7 -2
- data/lib/rigor/inference/last_line/self_evidence.rb +1 -1
- data/lib/rigor/inference/module_function_state.rb +80 -13
- data/lib/rigor/inference/narrowing.rb +184 -68
- data/lib/rigor/inference/reflective_ivar_write.rb +116 -0
- data/lib/rigor/inference/repeated_or_writes.rb +414 -1
- data/lib/rigor/inference/scope_indexer.rb +439 -70
- data/lib/rigor/inference/self_call_ivar_writes.rb +341 -0
- data/lib/rigor/inference/statement_evaluator.rb +84 -29
- data/lib/rigor/lens/anchor.rb +3 -3
- data/lib/rigor/lens/contract_reader.rb +15 -0
- data/lib/rigor/lens/document.rb +2 -2
- data/lib/rigor/lens/file_reader.rb +442 -73
- data/lib/rigor/lens/members.rb +65 -15
- data/lib/rigor/lens/run.rb +44 -2
- data/lib/rigor/lens.rb +2 -1
- data/lib/rigor/mcp/server.rb +61 -20
- data/lib/rigor/plugin/box_probe.rb +154 -7
- data/lib/rigor/project_environment.rb +6 -3
- data/lib/rigor/scope.rb +11 -6
- data/lib/rigor/sig_gen/layout_index.rb +36 -0
- data/lib/rigor/sig_gen/method_candidate.rb +8 -0
- data/lib/rigor/sig_gen/path_mapper.rb +18 -2
- data/lib/rigor/sig_gen/writer.rb +47 -34
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +5 -1
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +28 -6
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +1 -1
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +5 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +155 -12
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +10 -8
- metadata +4 -1
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "../source/node_children"
|
|
6
|
+
require_relative "../source/parameter_envelope"
|
|
7
|
+
require_relative "../type"
|
|
8
|
+
require_relative "guard_rebinding"
|
|
9
|
+
require_relative "project_method_ownership"
|
|
10
|
+
require_relative "refined_dispatch/own_method"
|
|
11
|
+
|
|
12
|
+
module Rigor
|
|
13
|
+
module Inference
|
|
14
|
+
# Issue #1596 — the instance variables an implicit-self or `self.` statement call may write, when the analysis
|
|
15
|
+
# can prove that set. The intervening-call reset (`StatementEvaluator#invalidate_ivars_for_intervening_call`)
|
|
16
|
+
# and the class-guard restore ({GuardRebinding}) both assume such a call may write any instance variable and
|
|
17
|
+
# run any code; with a proven set they widen only the instance variables in it, so `return unless
|
|
18
|
+
# @io.is_a?(StringIO); log("dumping"); @io.string` keeps `@io` a `StringIO` when `log` writes no instance
|
|
19
|
+
# variable.
|
|
20
|
+
#
|
|
21
|
+
# {.of} answers a frozen `Set` of names, or nil when it cannot prove one. It proves one only for an instance
|
|
22
|
+
# method's `self` (a project class, not a module, whose instance chain the resolution chain settles) and a call
|
|
23
|
+
# with no block argument (`&blk`, `...`), and only when every method the call can reach is proven:
|
|
24
|
+
#
|
|
25
|
+
# - A method the project defines on the class's chain must resolve to one `def` (not `define_method`, an
|
|
26
|
+
# `attr_*` writer or an alias), found ahead of every external ancestor, with every project subclass resolving
|
|
27
|
+
# the name to the same definer, and no `refine` block defining the name. Its parameters' default values and
|
|
28
|
+
# its body are read. A recursive or mutually recursive call adds nothing the first read did not.
|
|
29
|
+
# - Any other method on `self` must be one {GuardRebinding} counts as running no foreign code (`warn`, `puts`,
|
|
30
|
+
# `format`), and not a code-running name (`send`, `instance_variable_set`, `instance_eval`, …).
|
|
31
|
+
# - A call on another receiver counts only on a literal, a global, a constant naming a core or standard-library
|
|
32
|
+
# class, or an instance variable read as its class-ivar seed, and only when {GuardRebinding} counts its method
|
|
33
|
+
# as running no foreign code there. A local, a parameter or a call result is a receiver the read cannot type.
|
|
34
|
+
#
|
|
35
|
+
# Read code that writes an instance variable adds its name (an `instance_variable_set` call is a code-running
|
|
36
|
+
# name, so it proves nothing). Code that writes a global or a constant, yields, calls `super`, defines a method
|
|
37
|
+
# or opens a singleton class, an operator write on a local, and a `for` loop prove nothing. A literal block at
|
|
38
|
+
# the call site or in a read body is read with the code around it. The accepted gap is {GuardRebinding}'s:
|
|
39
|
+
# an implicit conversion a core method runs (`warn obj` runs `obj.to_s`) is read as the core method alone, and
|
|
40
|
+
# so is a singleton method an `extend` or `def obj.m` gives one object at run time.
|
|
41
|
+
module SelfCallIvarWrites
|
|
42
|
+
# The most `def` bodies one proof reads before it gives up.
|
|
43
|
+
DEF_BUDGET = 32
|
|
44
|
+
# Code the proof does not read past: it rebinds a global or constant, runs code the body does not show, or
|
|
45
|
+
# changes which method a later call dispatches to.
|
|
46
|
+
UNPROVABLE_NODES = Set[
|
|
47
|
+
Prism::GlobalVariableWriteNode, Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableAndWriteNode,
|
|
48
|
+
Prism::GlobalVariableOperatorWriteNode, Prism::GlobalVariableTargetNode,
|
|
49
|
+
Prism::ConstantWriteNode, Prism::ConstantOrWriteNode, Prism::ConstantAndWriteNode,
|
|
50
|
+
Prism::ConstantOperatorWriteNode, Prism::ConstantTargetNode, Prism::ConstantPathWriteNode,
|
|
51
|
+
Prism::ConstantPathOrWriteNode, Prism::ConstantPathAndWriteNode, Prism::ConstantPathOperatorWriteNode,
|
|
52
|
+
Prism::ConstantPathTargetNode,
|
|
53
|
+
Prism::YieldNode, Prism::SuperNode, Prism::ForwardingSuperNode, Prism::ForwardingArgumentsNode,
|
|
54
|
+
Prism::BlockArgumentNode, Prism::DefNode, Prism::SingletonClassNode, Prism::ForNode,
|
|
55
|
+
Prism::LocalVariableOperatorWriteNode, Prism::ClassVariableOperatorWriteNode, Prism::CallOperatorWriteNode,
|
|
56
|
+
Prism::CallOrWriteNode, Prism::CallAndWriteNode, Prism::IndexOperatorWriteNode, Prism::IndexOrWriteNode,
|
|
57
|
+
Prism::IndexAndWriteNode
|
|
58
|
+
].freeze
|
|
59
|
+
# Literal receivers, by the core class their value is an instance of.
|
|
60
|
+
LITERAL_CLASSES = {
|
|
61
|
+
Prism::StringNode => "String", Prism::InterpolatedStringNode => "String", Prism::XStringNode => "String",
|
|
62
|
+
Prism::SymbolNode => "Symbol", Prism::InterpolatedSymbolNode => "Symbol", Prism::IntegerNode => "Integer",
|
|
63
|
+
Prism::FloatNode => "Float", Prism::ArrayNode => "Array", Prism::HashNode => "Hash",
|
|
64
|
+
Prism::RangeNode => "Range", Prism::RegularExpressionNode => "Regexp",
|
|
65
|
+
Prism::InterpolatedRegularExpressionNode => "Regexp", Prism::NilNode => "NilClass",
|
|
66
|
+
Prism::TrueNode => "TrueClass", Prism::FalseNode => "FalseClass"
|
|
67
|
+
}.freeze
|
|
68
|
+
# The methods `Kernel` or `Object` own that a read accepts on `self` when the project does not define them. Any
|
|
69
|
+
# other (`display`, `inspect`, `==`, `hash`, `extend`, `method`, `binding`, `instance_variables`) proves nothing.
|
|
70
|
+
# These may still convert a value they are given, which runs that value's methods: {PROTOCOL_NAMES} answers for
|
|
71
|
+
# a value that is `self`.
|
|
72
|
+
SELF_SAFE_NAMES = Set[
|
|
73
|
+
:warn, :puts, :print, :p, :pp, :format, :sprintf, :printf, :raise, :fail, :loop, :catch, :throw, :rand,
|
|
74
|
+
:sleep, :Integer, :Float, :String, :Array, :Hash, :Rational, :Complex, :block_given?, :frozen?, :nil?,
|
|
75
|
+
:is_a?, :kind_of?, :instance_of?, :object_id, :class, :__method__, :caller, :abort, :exit
|
|
76
|
+
].freeze
|
|
77
|
+
# Issue #1596 — the methods core code calls on a value without the program spelling them: conversions,
|
|
78
|
+
# comparisons, hashing, iteration, `respond_to?` and its hook, `exception` for `raise`, `divmod` for `sleep`.
|
|
79
|
+
# A value aliasing `self` (`@me = self`, `me = self`, a constant holding the instance) handed to a core
|
|
80
|
+
# method runs `self`'s own definition of one of these, which the read never sees, so a class defining any
|
|
81
|
+
# proves nothing.
|
|
82
|
+
PROTOCOL_NAMES = Set[
|
|
83
|
+
:to_s, :inspect, :hash, :eql?, :==, :===, :<=>, :to_a, :to_ary, :to_str, :to_hash, :to_int, :to_i, :to_f,
|
|
84
|
+
:to_r, :to_c, :to_proc, :to_path, :to_sym, :to_io, :coerce, :exception, :divmod, :each, :respond_to?,
|
|
85
|
+
:respond_to_missing?, :method_missing, :initialize_copy, :marshal_dump, :pretty_print
|
|
86
|
+
].freeze
|
|
87
|
+
private_constant :UNPROVABLE_NODES, :LITERAL_CLASSES, :SELF_SAFE_NAMES, :PROTOCOL_NAMES
|
|
88
|
+
|
|
89
|
+
# Raised inside a proof to abandon it.
|
|
90
|
+
class Unprovable < StandardError
|
|
91
|
+
end
|
|
92
|
+
private_constant :Unprovable
|
|
93
|
+
|
|
94
|
+
module_function
|
|
95
|
+
|
|
96
|
+
# The names of the instance variables `call_node`, an implicit-self or `self.` call, may write, as a frozen
|
|
97
|
+
# `Set`, or nil when the analysis cannot prove the set. `scope` is the scope the call's operands were typed in.
|
|
98
|
+
def of(call_node, scope)
|
|
99
|
+
return nil unless call_node.is_a?(Prism::CallNode)
|
|
100
|
+
return nil unless call_node.receiver.nil? || call_node.receiver.is_a?(Prism::SelfNode)
|
|
101
|
+
|
|
102
|
+
self_type = scope.self_type
|
|
103
|
+
return nil unless self_type.is_a?(Type::Nominal)
|
|
104
|
+
|
|
105
|
+
proof = Proof.new(self_type, scope)
|
|
106
|
+
proof.no_self_value(call_node.arguments)
|
|
107
|
+
proof.no_protocol_override
|
|
108
|
+
proof.self_call(call_node)
|
|
109
|
+
proof.read(call_node.block)
|
|
110
|
+
proof.written.freeze
|
|
111
|
+
rescue StandardError # an `Unprovable`, or a lookup that raised: either proves nothing
|
|
112
|
+
nil
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# One proof's state: the instance variables read code writes, and the `def` bodies already read.
|
|
116
|
+
class Proof
|
|
117
|
+
attr_reader :written
|
|
118
|
+
|
|
119
|
+
def initialize(self_type, scope)
|
|
120
|
+
@self_type = self_type
|
|
121
|
+
@class_name = self_type.class_name
|
|
122
|
+
@scope = scope
|
|
123
|
+
@written = Set.new
|
|
124
|
+
@visited = {}.compare_by_identity
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Reads the method an implicit-self or `self.` call to `call_node`'s name runs.
|
|
128
|
+
def self_call(call_node)
|
|
129
|
+
name = call_node.name
|
|
130
|
+
unprovable! if GuardRebinding.code_running_name?(name) || refined?(name)
|
|
131
|
+
|
|
132
|
+
if ProjectMethodOwnership.defines?(@class_name, name, :instance, @scope)
|
|
133
|
+
read_def(proven_def(name))
|
|
134
|
+
elsif !SELF_SAFE_NAMES.include?(name) || GuardRebinding.type_method_foreign?(@self_type, name, @scope)
|
|
135
|
+
unprovable!
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Issue #1596 — gives up when `self`'s class, a project subclass, or a project ancestor or module on either's
|
|
140
|
+
# chain defines a {PROTOCOL_NAMES} method (a `def`, `define_method`, `attr_*` or alias), rewrites its methods
|
|
141
|
+
# beyond the names it spells, or is refined on one; or when either chain holds an external ancestor, whose
|
|
142
|
+
# own definitions the read cannot see.
|
|
143
|
+
def no_protocol_override
|
|
144
|
+
classes = [@class_name]
|
|
145
|
+
RefinedDispatch::OwnMethod.each_subclass(@scope, @class_name) { |subclass| classes << subclass }
|
|
146
|
+
classes.each do |class_name|
|
|
147
|
+
chain = Scope::ResolutionChain.for(@scope, class_name.to_s, :instance, :methods)
|
|
148
|
+
unprovable! if chain.truncated? || chain.wildcard_mixin? || !settled?(chain)
|
|
149
|
+
chain.search(@scope) do |entry|
|
|
150
|
+
unprovable! if entry.external? || rewritten?(entry.name) || protocol_defined?(entry.name)
|
|
151
|
+
nil
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Issue #1596 — `self` handed on as a value (an argument, an interpolation, a splat, a `when` value, a literal
|
|
157
|
+
# element or key) lets the code it reaches run `self`'s methods by protocol (`to_s`, `hash`, `==`, `to_a`),
|
|
158
|
+
# which the read does not follow, so it proves nothing. Reads `node` for one, without reading anything else.
|
|
159
|
+
def no_self_value(node)
|
|
160
|
+
return unless node.is_a?(Prism::Node)
|
|
161
|
+
|
|
162
|
+
unprovable! if node.is_a?(Prism::SelfNode)
|
|
163
|
+
node.rigor_each_child { |child| no_self_value(child) }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Reads `node`, adding each instance variable it writes.
|
|
167
|
+
def read(node)
|
|
168
|
+
return unless node.is_a?(Prism::Node)
|
|
169
|
+
|
|
170
|
+
unprovable! if UNPROVABLE_NODES.include?(node.class) || node.is_a?(Prism::SelfNode)
|
|
171
|
+
written = ivar_write_name(node)
|
|
172
|
+
@written << written if written
|
|
173
|
+
if node.is_a?(Prism::InstanceVariableOperatorWriteNode) && GuardRebinding.type_method_foreign?(
|
|
174
|
+
ivar_seed(node.name), node.binary_operator, @scope
|
|
175
|
+
)
|
|
176
|
+
unprovable!
|
|
177
|
+
end
|
|
178
|
+
read_call(node) if node.is_a?(Prism::CallNode)
|
|
179
|
+
# `self.m` is the one place `self` may stand: a call on it, which {#read_call} read.
|
|
180
|
+
receiver = call_receiver(node)
|
|
181
|
+
node.rigor_each_child { |child| read(child) unless child.is_a?(Prism::SelfNode) && child.equal?(receiver) }
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
private
|
|
185
|
+
|
|
186
|
+
# Whether a `refine` block anywhere in the project defines `name`, on any class: a `using` in effect where the
|
|
187
|
+
# read code sits may route the call to it (`refine Kernel do def warn …`), whatever the receiver.
|
|
188
|
+
def refined?(name)
|
|
189
|
+
@scope.discovered_refinements.any? { |_refined, methods| methods.key?(name) }
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def call_receiver(node) = node.is_a?(Prism::CallNode) ? node.receiver : nil
|
|
193
|
+
|
|
194
|
+
def ivar_write_name(node)
|
|
195
|
+
case node
|
|
196
|
+
when Prism::InstanceVariableWriteNode, Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableAndWriteNode,
|
|
197
|
+
Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableTargetNode
|
|
198
|
+
node.name
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def read_call(node)
|
|
203
|
+
receiver = node.receiver
|
|
204
|
+
return self_call(node) if receiver.nil? || receiver.is_a?(Prism::SelfNode)
|
|
205
|
+
|
|
206
|
+
unprovable! if GuardRebinding.code_running_name?(node.name) || refined?(node.name)
|
|
207
|
+
type = receiver_type(receiver)
|
|
208
|
+
unprovable! if type.nil? || GuardRebinding.type_method_foreign?(type, node.name, @scope)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def read_def(def_node)
|
|
212
|
+
return if @visited.key?(def_node)
|
|
213
|
+
|
|
214
|
+
@visited[def_node] = true
|
|
215
|
+
unprovable! if @visited.size > DEF_BUDGET
|
|
216
|
+
read(def_node.parameters)
|
|
217
|
+
read(def_node.body)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# The `def` the name resolves to on the class and on every project subclass, or unprovable.
|
|
221
|
+
def proven_def(name)
|
|
222
|
+
unprovable! if RefinedDispatch::OwnMethod.module_name?(@scope, @class_name)
|
|
223
|
+
|
|
224
|
+
owner = definer_ahead(@class_name, name, nil)
|
|
225
|
+
unprovable! if owner.nil?
|
|
226
|
+
def_node = @scope.user_def_for(owner, name)
|
|
227
|
+
unprovable! unless def_node.is_a?(Prism::DefNode) && def_node.receiver.nil?
|
|
228
|
+
single_definition(owner, name)
|
|
229
|
+
|
|
230
|
+
# A subclass whose superclass the tables do not know is enumerated too, and its chain ends short of the
|
|
231
|
+
# owner; one that reaches no definer of its own leaves the call to its superclass.
|
|
232
|
+
RefinedDispatch::OwnMethod.each_subclass(@scope, @class_name) do |subclass|
|
|
233
|
+
unprovable! unless [owner, nil].include?(definer_ahead(subclass, name, owner))
|
|
234
|
+
end
|
|
235
|
+
def_node
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Issue #1596 — the `def` the table holds must be the owner's only definition of `name`: the owner declared in
|
|
239
|
+
# one file, with one `def` of the name there (a reopening or a `class_eval` body in the same file is a second
|
|
240
|
+
# one), and no `define_method`, `attr_*` or alias of it, nor a rewrite no literal names (`T.define_method`,
|
|
241
|
+
# `T.send(:define_method, …)`, `T.class_eval` with a computed name), nor a definition the walk could not prove
|
|
242
|
+
# runs (a possible or contested one, as `DefinerResolution` reads them). The class edge files every declaring
|
|
243
|
+
# file.
|
|
244
|
+
def single_definition(owner, name)
|
|
245
|
+
Scope::ResolutionChain.record_class(@scope, owner) if Analysis::DependencyRecorder.active?
|
|
246
|
+
unprovable! if possible_definition?(owner, name)
|
|
247
|
+
bucket = @scope.discovered_parameter_envelopes[owner] || {}
|
|
248
|
+
unprovable! if bucket[[:instance, name]] == Source::ParameterEnvelope::OPAQUE
|
|
249
|
+
unprovable! if (@scope.discovered_class_sources[owner]&.size || 0) > 1
|
|
250
|
+
unprovable! if definition_count(owner, name) > 1
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def possible_definition?(owner, name)
|
|
254
|
+
discovery = @scope.discovery
|
|
255
|
+
discovery.possible_method?(owner, name, :instance) ||
|
|
256
|
+
discovery.contested?(:discovered_def_nodes, [owner, name]) ||
|
|
257
|
+
discovery.contested?(:discovered_parameter_envelopes, [owner, [:instance, name]])
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def definition_count(owner, name)
|
|
261
|
+
@scope.discovered_deferred_ranges.sum do |_path, rows|
|
|
262
|
+
rows.count { |row| row[2] == name && row[3] == :instance && row[4] == owner }
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Whether `chain`'s own order stands with no second world: no order mark and no fork. `ResolutionChain#settle`
|
|
267
|
+
# answers `:chain` for an answer no world can match only then; a fork sends it to the retro world, which the
|
|
268
|
+
# block answers otherwise, or to master's order.
|
|
269
|
+
def settled?(chain)
|
|
270
|
+
chain.settle(@scope, :chain_order) { :other_world } == :chain
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def protocol_defined?(class_name)
|
|
274
|
+
refined = @scope.discovered_refinements[class_name] || {}
|
|
275
|
+
PROTOCOL_NAMES.any? { |name| defines_here?(class_name, name) || refined.key?(name) }
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Whether the project rewrites `class_name`'s method table beyond the names it spells, or refines it.
|
|
279
|
+
def rewritten?(class_name)
|
|
280
|
+
bucket = @scope.discovered_parameter_envelopes[class_name]
|
|
281
|
+
!bucket.nil? && Scope::DiscoveryIndex.rewritten_surface?(bucket)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# The first project class or module on `class_name`'s settled instance chain that defines `name`, stopping
|
|
285
|
+
# at `owner`; nil when the chain ends without one. An external ancestor reached first is unprovable: its
|
|
286
|
+
# signature, or a gem without one, may define the name.
|
|
287
|
+
def definer_ahead(class_name, name, owner)
|
|
288
|
+
chain = Scope::ResolutionChain.for(@scope, class_name.to_s, :instance, :methods)
|
|
289
|
+
unprovable! if chain.truncated? || chain.wildcard_mixin? || !settled?(chain)
|
|
290
|
+
|
|
291
|
+
chain.search(@scope) do |entry|
|
|
292
|
+
unprovable! if entry.external? || rewritten?(entry.name)
|
|
293
|
+
|
|
294
|
+
entry.name if entry.name == owner || defines_here?(entry.name, name)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# A `def`, `define_method`, `attr_*` or alias of `name` in `class_name`'s own body: the discovery table holds
|
|
299
|
+
# the last three project-wide, and the `def` table the first.
|
|
300
|
+
def defines_here?(class_name, name)
|
|
301
|
+
@scope.discovered_method?(class_name, name, :instance) || !@scope.def_node_slot(class_name, name).nil?
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def receiver_type(node)
|
|
305
|
+
literal = LITERAL_CLASSES[node.class]
|
|
306
|
+
return Type::Combinator.nominal_of(literal) if literal
|
|
307
|
+
|
|
308
|
+
case node
|
|
309
|
+
when Prism::GlobalVariableReadNode then @scope.type_of(node)
|
|
310
|
+
when Prism::InstanceVariableReadNode then ivar_seed(node.name)
|
|
311
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode then core_constant_type(node)
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def ivar_seed(name)
|
|
316
|
+
@scope.class_ivars_for(@class_name)[name] || unprovable!
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# A constant receiver read in the caller's scope, which the callee's lexical scope may resolve otherwise: only
|
|
320
|
+
# a core or standard-library class object or instance, under a name no project class or constant ends with.
|
|
321
|
+
def core_constant_type(node)
|
|
322
|
+
type = @scope.type_of(node)
|
|
323
|
+
return nil unless type.is_a?(Type::Singleton) || type.is_a?(Type::Nominal)
|
|
324
|
+
|
|
325
|
+
loader = @scope.environment.rbs_loader
|
|
326
|
+
return nil if loader.nil? || !loader.core_or_stdlib_class?(type.class_name)
|
|
327
|
+
|
|
328
|
+
segment = node.name.to_s
|
|
329
|
+
project = [@scope.discovered_classes, @scope.in_source_constants]
|
|
330
|
+
return nil if project.any? { |table| table.each_key.any? { |name| name.to_s.split("::").last == segment } }
|
|
331
|
+
|
|
332
|
+
type
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def unprovable!
|
|
336
|
+
raise Unprovable
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
@@ -27,6 +27,7 @@ require_relative "content_join"
|
|
|
27
27
|
require_relative "define_method_block_self"
|
|
28
28
|
require_relative "macro_block_self_type"
|
|
29
29
|
require_relative "guard_rebinding"
|
|
30
|
+
require_relative "self_call_ivar_writes"
|
|
30
31
|
require_relative "match_rebinding"
|
|
31
32
|
require_relative "element_read_widening"
|
|
32
33
|
require_relative "hash_lookup_mutation"
|
|
@@ -123,9 +124,9 @@ module Rigor
|
|
|
123
124
|
Prism::BeginNode => :eval_begin_node,
|
|
124
125
|
Prism::RescueNode => :eval_rescue,
|
|
125
126
|
Prism::EnsureNode => :eval_ensure,
|
|
126
|
-
Prism::WhileNode => :
|
|
127
|
-
Prism::UntilNode => :
|
|
128
|
-
Prism::ForNode => :
|
|
127
|
+
Prism::WhileNode => :eval_repeating_loop,
|
|
128
|
+
Prism::UntilNode => :eval_repeating_loop,
|
|
129
|
+
Prism::ForNode => :eval_repeating_for,
|
|
129
130
|
Prism::AndNode => :eval_and_or,
|
|
130
131
|
Prism::OrNode => :eval_and_or,
|
|
131
132
|
Prism::ParenthesesNode => :eval_parentheses,
|
|
@@ -525,10 +526,13 @@ module Rigor
|
|
|
525
526
|
[result_type, current]
|
|
526
527
|
end
|
|
527
528
|
|
|
529
|
+
# The top level is a straight-line body: an index `||=` there whose slot an earlier store may have filled is
|
|
530
|
+
# marked ({RepeatedOrWrites.straight_line_sites}).
|
|
528
531
|
def eval_program(node)
|
|
529
532
|
return [Type::Combinator.constant_of(nil), scope] if node.statements.nil?
|
|
530
533
|
|
|
531
|
-
|
|
534
|
+
marks = RepeatedOrWrites.straight_line_sites(node.statements, scope)
|
|
535
|
+
sub_eval(node.statements, scope.with_repeated_or_writes(marks))
|
|
532
536
|
end
|
|
533
537
|
|
|
534
538
|
# `name = rvalue` evaluates the rvalue under the entry scope (so earlier assignments in a chained `a = b = expr`
|
|
@@ -1380,46 +1384,46 @@ module Rigor
|
|
|
1380
1384
|
end
|
|
1381
1385
|
|
|
1382
1386
|
# Returns `[body_scope, updated_falsey_scope]` for a single branch. `WhenNode` branches narrow through
|
|
1383
|
-
# `Narrowing.case_when_scopes
|
|
1384
|
-
#
|
|
1385
|
-
#
|
|
1387
|
+
# `Narrowing.case_when_scopes`, or through {#subjectless_when_scopes} in a `case` without a subject. `InNode`
|
|
1388
|
+
# branches narrow soundly only for a bare class pattern (`in C` / `in C => x`, pure `is_a?`); every other pattern
|
|
1389
|
+
# keeps the conservative "body = entry + bindings, falsey unchanged" shape. `subject_type` is the predicate's
|
|
1390
|
+
# type, which an `in` branch's pattern decomposes to type the names it binds.
|
|
1386
1391
|
def branch_body_and_falsey_scopes(subject_type, subject, branch, falsey_scope)
|
|
1387
1392
|
if branch.is_a?(Prism::InNode)
|
|
1388
1393
|
in_branch_body_and_falsey_scopes(subject_type, subject, branch, falsey_scope)
|
|
1394
|
+
elsif subject.nil?
|
|
1395
|
+
subjectless_when_scopes(branch.conditions, falsey_scope)
|
|
1389
1396
|
else
|
|
1390
1397
|
when_conditions = branch.respond_to?(:conditions) ? branch.conditions : []
|
|
1391
1398
|
Narrowing.case_when_scopes(subject, when_conditions, falsey_scope)
|
|
1392
1399
|
end
|
|
1393
1400
|
end
|
|
1394
1401
|
|
|
1402
|
+
# Issue #1495 — a `case` without a subject tests each `when` condition for truthiness, in order, and stops at the
|
|
1403
|
+
# first truthy one, so a clause is the `||` of its conditions in an `if` / `elsif` chain. Each condition is
|
|
1404
|
+
# evaluated under the falsey edges of the conditions before it, in this clause and every earlier one; the body
|
|
1405
|
+
# joins the conditions' truthy edges, and the next clause starts from the last falsey edge. A condition
|
|
1406
|
+
# `Narrowing.predicate_scopes` does not recognise narrows nothing: a splat (`when *conds`), and a regexp literal,
|
|
1407
|
+
# which is tested for its truthiness and matches nothing, so it binds no match global (#1486).
|
|
1408
|
+
def subjectless_when_scopes(conditions, entry)
|
|
1409
|
+
falsey = entry
|
|
1410
|
+
truthy_edges = conditions.map do |condition|
|
|
1411
|
+
_type, _post, truthy, falsey = eval_with_edges(condition, falsey)
|
|
1412
|
+
truthy
|
|
1413
|
+
end
|
|
1414
|
+
[reduce_scopes_with_nil_injection(truthy_edges), falsey]
|
|
1415
|
+
end
|
|
1416
|
+
|
|
1395
1417
|
# ADR-47 WD3a — a bare class pattern matches on `C === subject`, i.e. exactly `subject.is_a?(C)` with no
|
|
1396
1418
|
# deconstruction, so it narrows like `when C`: the body sees the subject narrowed to `C` and the next clause's
|
|
1397
1419
|
# falsey scope has `C` removed. Other patterns can fail to match even when a class test would pass (deconstruction
|
|
1398
1420
|
# arity, hash keys, ...), so removing anything from the falsey scope would be unsound — they keep the conservative
|
|
1399
1421
|
# shape.
|
|
1400
1422
|
def in_branch_body_and_falsey_scopes(subject_type, subject, branch, falsey_scope)
|
|
1401
|
-
|
|
1402
|
-
unless class_node
|
|
1403
|
-
bound = apply_in_pattern_bindings(subject_type, subject, branch.pattern, falsey_scope)
|
|
1404
|
-
return [bound, falsey_scope]
|
|
1405
|
-
end
|
|
1406
|
-
|
|
1407
|
-
truthy_scope, narrowed_falsey = Narrowing.case_when_scopes(subject, [class_node], falsey_scope)
|
|
1423
|
+
truthy_scope, narrowed_falsey = Narrowing.case_clause_scopes(subject, branch, falsey_scope)
|
|
1408
1424
|
[apply_in_pattern_bindings(subject_type, subject, branch.pattern, truthy_scope), narrowed_falsey]
|
|
1409
1425
|
end
|
|
1410
1426
|
|
|
1411
|
-
# The class-constant node of a `in C` / `in C => x` pattern (the only `in` shapes whose match is pure `is_a?`), or
|
|
1412
|
-
# nil for any pattern that deconstructs, binds, or matches a value.
|
|
1413
|
-
def bare_class_pattern_node(pattern)
|
|
1414
|
-
case pattern
|
|
1415
|
-
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
|
1416
|
-
pattern
|
|
1417
|
-
when Prism::CapturePatternNode
|
|
1418
|
-
value = pattern.value
|
|
1419
|
-
value if value.is_a?(Prism::ConstantReadNode) || value.is_a?(Prism::ConstantPathNode)
|
|
1420
|
-
end
|
|
1421
|
-
end
|
|
1422
|
-
|
|
1423
1427
|
def eval_case_else(else_clause, falsey_scope)
|
|
1424
1428
|
return sub_eval(else_clause, falsey_scope) if else_clause
|
|
1425
1429
|
|
|
@@ -2065,6 +2069,12 @@ module Rigor
|
|
|
2065
2069
|
after.global(:$_) == Type::Combinator.untyped && !LastLine.may_set?(node.statements, scope)
|
|
2066
2070
|
end
|
|
2067
2071
|
|
|
2072
|
+
# Issue #1889 — {#eval_loop} / {#eval_for} with a literal-name `instance_variable_set` in the body joining its
|
|
2073
|
+
# value into the ivar's binding rather than replacing it ({ReflectiveIvarWrite.in_repeating_body}).
|
|
2074
|
+
def eval_repeating_loop(node) = ReflectiveIvarWrite.in_repeating_body { eval_loop(node) }
|
|
2075
|
+
|
|
2076
|
+
def eval_repeating_for(node) = ReflectiveIvarWrite.in_repeating_body { eval_for(node) }
|
|
2077
|
+
|
|
2068
2078
|
# `while pred; body; end` / `until pred; body; end`. The body might run zero or more times, so half-bound names
|
|
2069
2079
|
# degrade to `T | nil` in the post-loop scope. The loop expression itself types as `Constant[nil]` (Slice 3 phase
|
|
2070
2080
|
# 1), reflecting the common case where no `break VALUE` is observed.
|
|
@@ -2929,6 +2939,7 @@ module Rigor
|
|
|
2929
2939
|
# evaluates `def` to the method's name as a Symbol, so the produced type is `Constant[:name]`.
|
|
2930
2940
|
def eval_def(node)
|
|
2931
2941
|
body_scope = build_method_entry_scope(node)
|
|
2942
|
+
body_scope = body_scope.with_repeated_or_writes(RepeatedOrWrites.straight_line_sites(node.body, body_scope))
|
|
2932
2943
|
# Parameter default value expressions (e.g. `self.x` in `def copy(x: self.x)`) execute when the method is
|
|
2933
2944
|
# *invoked*, not when the `def` is read; their `self` is the instance receiver, not the surrounding class body.
|
|
2934
2945
|
# Walk the parameters subtree under `body_scope` so the scope-index records the instance `self_type` for every
|
|
@@ -3170,6 +3181,9 @@ module Rigor
|
|
|
3170
3181
|
# observes the seed's union (not the pre-call narrowed value). Always-safe (only widens; no new facts). See
|
|
3171
3182
|
# [`docs/CURRENT_WORK.md`](../../../docs/CURRENT_WORK.md) § "Flow-folding" — G2 intervening-call case.
|
|
3172
3183
|
post_scope = invalidate_ivars_for_intervening_call(node, post_scope) if statement_call
|
|
3184
|
+
# Issue #1889 — `instance_variable_set` on `self` writes an ivar no write node shows. It runs after the reset
|
|
3185
|
+
# above, which would join a literal-name write back into the class seed.
|
|
3186
|
+
post_scope = ReflectiveIvarWrite.apply(node, post_scope) { type_operand(ReflectiveIvarWrite.value_node(node)) }
|
|
3173
3187
|
# C1 — regex match-data globals (`$~`, `$1..$N`, `$&`, …) are narrowed to non-nil on a successful-match edge; a
|
|
3174
3188
|
# later call that itself runs a regex match rebinds them, so the narrowed facts must be dropped. We forget them
|
|
3175
3189
|
# only when the call may run a match in this frame ({#rebinds_match_globals?}). A call provably match-free on a
|
|
@@ -3191,7 +3205,13 @@ module Rigor
|
|
|
3191
3205
|
post_scope = post_scope.forget_match_globals if rebinds_match_globals?(node, post_scope)
|
|
3192
3206
|
post_scope = post_scope.forget_last_line if rebinds_last_line?(node, post_scope)
|
|
3193
3207
|
if post_scope.guard_narrowed? && GuardRebinding.call_may_rebind?(node, operand_scope)
|
|
3194
|
-
|
|
3208
|
+
# Issue #1596 — a self-call proven to write only some instance variables restores only theirs.
|
|
3209
|
+
writes = intervening_call_candidate?(node) ? self_call_ivar_writes(node) : nil
|
|
3210
|
+
post_scope = if writes
|
|
3211
|
+
writes.reduce(post_scope) { |acc, name| acc.forget_guard_narrowing_of(:ivar, name) }
|
|
3212
|
+
else
|
|
3213
|
+
post_scope.forget_guard_narrowings
|
|
3214
|
+
end
|
|
3195
3215
|
end
|
|
3196
3216
|
forget_rescued_status(LastStatus.after(node, post_scope, scope), node)
|
|
3197
3217
|
end
|
|
@@ -3316,6 +3336,7 @@ module Rigor
|
|
|
3316
3336
|
# Returns a scope with each ivar's narrowed local binding widened back to its class-ivar seed value when the call
|
|
3317
3337
|
# is one that could plausibly mutate ivars on the enclosing class (implicit-self or explicit `self.foo`).
|
|
3318
3338
|
# External- receiver calls (`obj.method`) cannot reach the caller's ivars; they pass through unchanged.
|
|
3339
|
+
# Issue #1596 — where the call is proven to write only some ivars ({#self_call_ivar_writes}), only those widen.
|
|
3319
3340
|
def invalidate_ivars_for_intervening_call(call_node, current_scope)
|
|
3320
3341
|
return current_scope unless intervening_call_candidate?(call_node)
|
|
3321
3342
|
|
|
@@ -3330,11 +3351,27 @@ module Rigor
|
|
|
3330
3351
|
local_type = current_scope.ivar(ivar_name)
|
|
3331
3352
|
next if local_type.nil? || local_type == seed_type
|
|
3332
3353
|
|
|
3333
|
-
|
|
3354
|
+
writes = self_call_ivar_writes(call_node)
|
|
3355
|
+
next if writes && !writes.include?(ivar_name)
|
|
3356
|
+
|
|
3357
|
+
joined = Type::Combinator.union(local_type, seed_type)
|
|
3358
|
+
widened = widened.with_ivar(ivar_name, joined)
|
|
3359
|
+
# Issue #1889 — an ivar a reflective write widened keeps its declaration mark, so the reset must too.
|
|
3360
|
+
widened = widened.seed_declaration_sourced_ivar(ivar_name, joined) if
|
|
3361
|
+
current_scope.declaration_sourced?(:ivar, ivar_name)
|
|
3334
3362
|
end
|
|
3335
3363
|
widened
|
|
3336
3364
|
end
|
|
3337
3365
|
|
|
3366
|
+
# Issue #1596 — the ivars the implicit-self / `self.` call `call_node` may write, or nil when that is not proven
|
|
3367
|
+
# ({SelfCallIvarWrites.of}). Memoised per call node: the reset and the guard restore ask it of the same call.
|
|
3368
|
+
def self_call_ivar_writes(call_node)
|
|
3369
|
+
memo = (@self_call_ivar_writes ||= {}.compare_by_identity)
|
|
3370
|
+
return memo[call_node] if memo.key?(call_node)
|
|
3371
|
+
|
|
3372
|
+
memo[call_node] = SelfCallIvarWrites.of(call_node, operand_scope)
|
|
3373
|
+
end
|
|
3374
|
+
|
|
3338
3375
|
def intervening_call_candidate?(call_node)
|
|
3339
3376
|
return false unless call_node.is_a?(Prism::CallNode)
|
|
3340
3377
|
|
|
@@ -5412,7 +5449,7 @@ module Rigor
|
|
|
5412
5449
|
return [] if receiver_type.nil?
|
|
5413
5450
|
|
|
5414
5451
|
arg_types = call_arg_types_for(call_node)
|
|
5415
|
-
MethodDispatcher.expected_block_param_types(
|
|
5452
|
+
expected = MethodDispatcher.expected_block_param_types(
|
|
5416
5453
|
receiver_type: receiver_type,
|
|
5417
5454
|
method_name: call_node.name,
|
|
5418
5455
|
arg_types: arg_types,
|
|
@@ -5420,10 +5457,28 @@ module Rigor
|
|
|
5420
5457
|
scope: scope,
|
|
5421
5458
|
call_node: call_node
|
|
5422
5459
|
)
|
|
5460
|
+
with_inject_accumulator(call_node, receiver_type, arg_types, expected)
|
|
5423
5461
|
rescue StandardError
|
|
5424
5462
|
[]
|
|
5425
5463
|
end
|
|
5426
5464
|
|
|
5465
|
+
# Issue #1889 — a block-form `inject` / `reduce` call's accumulator parameter at the type it holds across
|
|
5466
|
+
# every run ({ExpressionTyper#inject_accumulator_type}) rather than the seed the signature binds it to, once
|
|
5467
|
+
# per call: every write-back pass builds the entry again.
|
|
5468
|
+
def with_inject_accumulator(call_node, receiver_type, arg_types, expected)
|
|
5469
|
+
memo = @inject_accumulator
|
|
5470
|
+
accumulator =
|
|
5471
|
+
if memo && memo[0].equal?(call_node)
|
|
5472
|
+
memo[1]
|
|
5473
|
+
else
|
|
5474
|
+
typed = ExpressionTyper.new(scope: scope, tracer: tracer)
|
|
5475
|
+
.inject_accumulator_type(call_node, receiver_type, arg_types)
|
|
5476
|
+
@inject_accumulator = [call_node, typed]
|
|
5477
|
+
typed
|
|
5478
|
+
end
|
|
5479
|
+
accumulator ? [accumulator, *expected.drop(1)] : expected
|
|
5480
|
+
end
|
|
5481
|
+
|
|
5427
5482
|
def call_arg_types_for(call_node)
|
|
5428
5483
|
arguments = call_node.arguments
|
|
5429
5484
|
return [] if arguments.nil?
|
data/lib/rigor/lens/anchor.rb
CHANGED
|
@@ -32,9 +32,9 @@ module Rigor
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# The anchor of the declaration spanning bytes `start_offset...end_offset` and lines `start_line..end_line`.
|
|
35
|
-
def for_span(start_offset, end_offset, start_line, end_line)
|
|
36
|
-
leading = leading_contract_comments(start_line)
|
|
37
|
-
trailing = trailing_contract_comment(end_line, end_offset)
|
|
35
|
+
def for_span(start_offset, end_offset, start_line, end_line, leading: true, trailing: true)
|
|
36
|
+
leading = leading ? leading_contract_comments(start_line) : []
|
|
37
|
+
trailing = trailing ? trailing_contract_comment(end_line, end_offset) : nil
|
|
38
38
|
material = +"".b
|
|
39
39
|
leading.each { |comment| material << line_text_from(comment.location.start_offset) << "\n" }
|
|
40
40
|
material << @source.byteslice(start_offset, end_offset - start_offset)
|
|
@@ -34,6 +34,21 @@ module Rigor
|
|
|
34
34
|
unreadable("definition_build_failed", [])
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
# The declared type of the constant `name` (qualified), as a slot with its declaration's provenance, or nil
|
|
38
|
+
# when no RBS or inline declaration names it.
|
|
39
|
+
def read_constant(name, environment)
|
|
40
|
+
declaration = environment&.rbs_loader&.constant_declaration(name)
|
|
41
|
+
return nil if declaration.nil?
|
|
42
|
+
|
|
43
|
+
type, buffer = declaration
|
|
44
|
+
provenance, path = @provenance.classify(buffer)
|
|
45
|
+
return Slot.unavailable("not_computed") if provenance.nil?
|
|
46
|
+
|
|
47
|
+
Slot.from_rbs(type, provenance, path)
|
|
48
|
+
rescue StandardError
|
|
49
|
+
Slot.unavailable("not_computed")
|
|
50
|
+
end
|
|
51
|
+
|
|
37
52
|
private
|
|
38
53
|
|
|
39
54
|
def contract(defs, annotations)
|
data/lib/rigor/lens/document.rb
CHANGED
|
@@ -10,8 +10,8 @@ module Rigor
|
|
|
10
10
|
module Lens
|
|
11
11
|
# Assembles the `lens/v0` document (docs/internal-spec/lens.md § Document) from a {Run::Outcome}.
|
|
12
12
|
class Document
|
|
13
|
-
SITE_FIELDS = %w[key kind name owner scope attribute members ordinal span contract_span
|
|
14
|
-
contract params block body_return].freeze
|
|
13
|
+
SITE_FIELDS = %w[key kind name owner scope attribute members module_function builder ordinal span contract_span
|
|
14
|
+
anchor visibility contract params block body_return value alias_of].freeze
|
|
15
15
|
|
|
16
16
|
def initialize(outcome:, configuration:, root:, config_path:)
|
|
17
17
|
@outcome = outcome
|