mutant icon indicating copy to clipboard operation
mutant copied to clipboard

Add mutation from `@foo ||= bar` to `@foo = bar || @foo`

Open tjchambers opened this issue 10 years ago • 7 comments

This mutation would have helped me locate a short circuit issue that individual unit tests did not surface:


def initialize
  @changed = false
end

def add(x)
  @changed ||= add_record(x)
end

def remove(x)
  @changed ||= remove_record(x)
end

or

def initialize
  @changed = false
end

def change(x)
  @changed ||= remove_record(x)
  @changed ||= add_record(x)
end

if add(x) is called then subsequently remove(x) (or vice versa) the short_circuit caused the second execution to fail. I could not find this mutation in the current code. Testing either add(x) or remove(x) individually would not be an effective spec to surface an issue. This may be a HOM.

tjchambers avatar Oct 17 '15 08:10 tjchambers

I assumed (which may not be the case) that mutant already does foo || bar -> bar || foo and more importantly perhaps foo && bar -> bar && foo mutations.

tjchambers avatar Oct 17 '15 12:10 tjchambers

This library is a little new to me. What category of transformation would this be? I see that it would have exposed the logic error in your code, but how is it more generally applicable?

dogweather avatar Oct 18 '15 20:10 dogweather

@tjchambers Would it have helped if expressions that are just syntactic sugar were expanded first, then mutated:

@foo ||= bar

Would become:

@foo = @foo || bar

Then that would be mutated in the different ways that mutant does, including negating @foo and bar, replacing each with nil, and so on.

dkubb avatar Oct 18 '15 20:10 dkubb

@tjchambers Would it have helped if expressions that are just syntactic sugar were expanded first, then mutated:

@dkubb JFYI: a ||= b is NOT syntactic sugar for a = a || bar. At least not in all cases.

If a is an lvar / ivar / gvar your statement is true. If a is an attribute its not true. Since it would trigger an attribute read and an attribute write separately:

class Foo
  def attribute=(value)
    p __method__
  end

  def attribute
    p __method__
    nil
  end
end

Foo.new.attribute ||= :bar

prints:

:attribute
:attribute=

mbj avatar Oct 18 '15 22:10 mbj

BTW Above inorthogonality is the reason rubies parser has separate nodes for all op assigns.

mbj avatar Oct 18 '15 22:10 mbj

@mbj so it's more like:

foo || (foo = bar)

dkubb avatar Oct 19 '15 00:10 dkubb

@mbj so it's more like:

foo || (foo = bar)

Yes.

mbj avatar Oct 19 '15 06:10 mbj