Class: Rigor::Effects::MutationClassifier
- Inherits:
-
Object
- Object
- Rigor::Effects::MutationClassifier
- Defined in:
- lib/rigor/effects/mutation_classifier.rb
Overview
Decides whether a call mutates its receiver, and — when it does — which mutate.* label that earns
(ADR-103 WD4 / WD14).
Two independent questions, both answered conservatively:
- Is this a mutation? Only when the selector says so beyond doubt.
[]=and an attribute writer are writes on every receiver;<<and the bang family are claimed only when the typer named the receiver's class, becausen << 2is a bit shift andio << "x"is output. A wrong label in the proven lane is worse than a missing one — the proven lane is the one a verdict may read (ADR-5). - Who owns the receiver?
selfand its ivars aremutate.self(mutate.staticin singleton context), a class variable ismutate.static, a parameter ismutate.instance, a frame-owned local ismutate.local. Anything else answers nil, and the caller records anunknown-ownershiptaint rather than a proven baremutate: Ruby's ownership is a dataflow question, and a proven parent label on a fresh-but-unproven receiver would put findings on correct code (WD14).
Constant Summary collapse
- UNIVERSAL_MUTATORS =
The only selectors a mutation may be claimed from without knowing the receiver's class.
%i[[]=].to_set.freeze
- STRING_MUTATORS =
String's receiver-mutating surface.Array/Hashreuse the hand-audited sets the widening rules already maintain, cited rather than re-derived (ADR-103 WD3). %i[ << concat replace insert prepend clear upcase! downcase! capitalize! swapcase! reverse! strip! lstrip! rstrip! chomp! chop! squeeze! succ! next! sub! gsub! tr! tr_s! delete! slice! []= ].to_set.freeze
- ATTRIBUTE_WRITER =
foo=, and deliberately not==/<=/!=/===. /\A[a-z_][A-Za-z0-9_]*=\z/- LABELS =
{ self_state: LabelSet.new(["mutate.self"]), static: LabelSet.new(["mutate.static"]), instance: LabelSet.new(["mutate.instance"]), local: LabelSet.new(["mutate.local"]) }.freeze
Instance Method Summary collapse
-
#initialize(singleton:, parameters:, owned_locals:) ⇒ MutationClassifier
constructor
A new instance of MutationClassifier.
-
#label_for(receiver) ⇒ Object
The label a mutation of
receiverearns, or nil when ownership is not provable. -
#mutating?(node, receiver_class) ⇒ Boolean
Whether
nodemutates its receiver.
Constructor Details
#initialize(singleton:, parameters:, owned_locals:) ⇒ MutationClassifier
Returns a new instance of MutationClassifier.
48 49 50 51 52 |
# File 'lib/rigor/effects/mutation_classifier.rb', line 48 def initialize(singleton:, parameters:, owned_locals:) @singleton = singleton @parameters = parameters @owned_locals = owned_locals end |
Instance Method Details
#label_for(receiver) ⇒ Object
The label a mutation of receiver earns, or nil when ownership is not provable.
69 70 71 |
# File 'lib/rigor/effects/mutation_classifier.rb', line 69 def label_for(receiver) LABELS[ownership(receiver)] end |
#mutating?(node, receiver_class) ⇒ Boolean
Whether node mutates its receiver. receiver_class is the class the typer projected the
receiver's type to, or nil when it projected to none.
56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/rigor/effects/mutation_classifier.rb', line 56 def mutating?(node, receiver_class) name = node.name return true if UNIVERSAL_MUTATORS.include?(name) || ATTRIBUTE_WRITER.match?(name.to_s) case receiver_class when "Array" then Inference::MutationWidening::ARRAY_MUTATORS.include?(name) when "Hash" then Inference::MutationWidening::HASH_MUTATORS.include?(name) when "String" then STRING_MUTATORS.include?(name) else false end end |