-
-
Notifications
You must be signed in to change notification settings - Fork 153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
2.7 Syntax - Upgrade to unparser ~> 0.5.2 #1062
Conversation
mbj
commented
Oct 16, 2020
- Remove all hacks for unparser normalization
- Adjust verification to enforce the better unparser constraints
- Add 2.7 syntax support
e1611f5
to
d12d1fa
Compare
d6c91fb
to
2b896a0
Compare
@@ -42,7 +42,7 @@ def prepare | |||
private | |||
|
|||
def wrap_node(mutant) | |||
s(:begin, mutant, s(:send, nil, :memoize, s(:args, s(:sym, name), *options))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@snusnu All this years we where generating invalid AST that would silently be emitted as correct intended source.
|
||
source s(:begin, s(:true)) | ||
# Mutation of each statement in block | ||
mutation s(:begin, s(:false)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dgollahon ^^ was part of the unparser preprocessor hack artifacts.
@@ -8,9 +8,9 @@ | |||
# edge cases | |||
mutation '0.0' | |||
mutation '1.0' | |||
mutation '(0.0 / 0.0)' | |||
mutation '(1.0 / 0.0)' | |||
mutation '(-1.0 / 0.0)' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dgollahon ^^ Another one. This redundant begin was needed as unparser pre 0.5 was not able to infer or
vs ||
based on AST structure and had to preserve semantic equivalency via extra parens. This extra parsens where an artifact of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one in particular seems like a nice quality of life improvement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the unparser preprocessor hack was a bad shortcut of mine I took years ago when I had "nothing" and had to 80/20 a lot of subsystems to get something.
@@ -1,16 +1,10 @@ | |||
# frozen_string_literal: true | |||
|
|||
Mutant::Meta::Example.add :lvar do | |||
source 'a = nil; a' | |||
declare_lvar :a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really an unparser artifact. But correctly using the parser API to avoid redundancy.
# TODO: fix invalid AST | ||
# These ASTs are not valid and should NOT be emitted | ||
# Mutations of lvarasgn need to be special cased to avoid this. | ||
mutation s(:begin, s(:lvasgn, :a__mutant__, s(:nil)), s(:lvar, :a)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need anymore to specify AST with forwad lvar declaration.
* Remove all hacks for unparser normalization * Adjust verification to enforce the better unparser constraints * Add 2.7 syntax support
mutation 'self.b += 1' | ||
# TODO: fix invalid AST | ||
# This should not get emitted as invalid AST with valid unparsed source | ||
mutation s(:op_asgn, s(:ivar, :@a), :+, s(:int, 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side effect of better unparser we can remove that one now.
let(:source) { 'false' } | ||
|
||
let(:expected) do | ||
[ | ||
Mutant::Meta::Example::Expected.new( | ||
node: s(:nil), | ||
original_source: 'nil' | ||
) | ||
] | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is kind of interesting. I'm not sure I fully understand the context of this spec, but does this mutate false
-> nil
somehow? I removed that mutation recently (I thought). Or is this just an artifact of test setup but wouldn't actually happen in practice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This spec does not demonstrate the mutations. It never calls the mutation engine, AKA Mutant::Mutator::Node
is never called. Instead it specifies on how mutations are specified. For this I run through the scenarios:
- There was a mutation being generated that was not expected.
- A mutation that was expected was not being generated.
- A mutation that was generated unparses to invalid syntax
- The original code that was being specified did not round trip.
And for each of these cases above I set-up an Mutant::Meta::Example
object in that specific state. But I intentionally do NOT run the engine here. But still use AST nodes that can round trip to unparser at low complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or TLDR: This code is the specification of the mutant mutation specifications and uses fake mutated nodes to not hard code the specification of properties of the specification against the mutation engine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or TLDR: This code is the specification of the mutant mutation specifications and uses fake mutated nodes to not hard code the specification of properties of the specification against the mutation engine.
Ok, cool. I thought that might be the case but wasn't 100% sure at a glance. Might make sense to use a valid mutation for documentation consistency but probably doesn't matter much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might make sense to use a valid mutation for documentation consistency but probably doesn't matter much.
it is a valid mutation example to demonstrate the specification of the specification on the properties I listed above. And I do not wish to call the real mutation engine here. I only wish to demonstrate I can find specific expected, missing and invalid mutations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I just meant I usually like to have examples in my tests match "real" behavior as much as possible. It is clearly valid in this context to mutate to anything but I thought it might be slightly less confusing to match the "real" behavior. Doesn't actually matter though, feel free to ignore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I where to start having to manually have generated mutations in sync here, I'd enforce something that adds no value to the test. And even would break as mutations are about to change drastically.
I'm experimenting a lot with "tiers" of mutations to default to only the most useful etc.