Skip to content
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

Add off-by-one mutation #26

Merged
merged 2 commits into from
Feb 25, 2018
Merged

Add off-by-one mutation #26

merged 2 commits into from
Feb 25, 2018

Conversation

gnieto
Copy link
Collaborator

@gnieto gnieto commented Feb 23, 2018

As suggested on #5, it would be
nice to mutate numeric constants adding or subtracting one unit to numeric constants.

At the moment, this is very conservative on the limits to add or
substract on default integer constants. I've did it like this to avoid
compilation issue, as we do not have information regarding the type of
the expression. So, in the default case, it subtracts one only if it's >
0 and it only adds one if the value is < 256.

If the constant has a type annotation (for example, 0i32), It adjust the
minimum and the maximum depending on the type hint. Note that it does
not check 128 bits numbers, as they are only available on nightly.

It chains the mutations in order to record only the needed
mutations. So, if the code finds a "0" on an unsigned type, it will only
record the "add one" mutation, but not the "sub one" (as it would overflow and panic).

After running it on the example project, it emits mutations like:

    let mut count = {
        if mutagen::now(2usize) {
            0 + 1
        } else {
            0
        }
    };

or

              ord < {
                    if mutagen::now(12usize) {
                        65 + 1
                    } else {
                        {
                            if mutagen::now(11usize) {
                                65 - 1
                            } else {
                                65
                            }
                        }
                    }
                }

And finally: I didn't add tests. I thought on adding another test on the example project that covers some of the edge cases and I've also thought on unit test the int_constant_can_subtract_one or int_constant_can_add_one, but I'm not sure which is the expected way of testing this library, so, if you have some idea, I can add the kind of tests you think will be more useful.

As suggeested on llogiq#5, it would be
nice to mutate numeric constants adding or substracting one unit.

At the moment, this is very conservative on the limits to add or
substract on default integer constants. I've did it like this to avoid
compilation issue, as we do not have information regarding the type of
the expression. So, in the default case, it subtracts one only if it's >
0 and it only adds one if the value is < 256.

If the constant has a type annotation (for example, 0i32), I adjust the
minimum and the maximum depending on the type hint. Note that it does
not check u128, as it's only available on nightly.

Finally, it chains the mutations in order to record only the needed
mutations. So, if the code finds a "0" on an unsigned type, it will only
record the "add one" mutation, but not the "sub one".
fn int_constant_can_add_one(i: u64, ty: LitIntType) -> bool {
let max: u64 = match ty {
LitIntType::Unsuffixed => 256,
LitIntType::Unsigned(UintTy::Usize) => std::u32::MAX as u64,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt which should be the max value for usize. As far as I know, it's system dependent, so I'm not sure which is the most safe value here.

Copy link
Owner

@llogiq llogiq Feb 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be a problem as long as we're consistent (that is, usize::max() is the right value on all targets)


fn int_constant_can_add_one(i: u64, ty: LitIntType) -> bool {
let max: u64 = match ty {
LitIntType::Unsuffixed => 256,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I also have doubts here. Maybe I should change it to std::i8::MAX, as it's the max positive value that won't cause compilation issues?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, let's be careful here.

@llogiq
Copy link
Owner

llogiq commented Feb 24, 2018

Great work so far! I was just thinking about implementing this mutation and you beat me to it 😄

Apart from small nits, this looks good to merge to me. If you like, feel free to add unit tests, but I won't block on them now.

@gnieto
Copy link
Collaborator Author

gnieto commented Feb 24, 2018

I've been checking this again and I've found that it does not properly work with negative values.
For example with the following code:

let test: i32 = -24322;

I get: LitKind(24322, LitIntType::Unsuffixed) . I'll look how I can workaround this.

@llogiq
Copy link
Owner

llogiq commented Feb 24, 2018

This is because you have an UnOp(UnNeg, Lit(..)) – working around will require folding UnOps and looking into whether the contained Expr is a Lit, then mutating with a 'negative' flag (which means it'll be an i* type). Namely, we'll have to extract the ExprLit match into its own method, add a boolean flag and call this.

@llogiq
Copy link
Owner

llogiq commented Feb 25, 2018

@gnieto Do you want to extend this or should I merge as is and do it myself? I'll be happy in either case.

@gnieto
Copy link
Collaborator Author

gnieto commented Feb 25, 2018 via email

@llogiq
Copy link
Owner

llogiq commented Feb 25, 2018

Great! No offense meant. I just wanted to make sure the PR doesn't become stale. 👍

@gnieto
Copy link
Collaborator Author

gnieto commented Feb 25, 2018

Handling for negative literals added 😃

As Rust AST does not track negative literals on ExprKind::Lit, the code
has been changed in order to check for Unary operations which contains a
negative operand and then the literal. The code has been factored in
order to allow to entry points: one for negative literals and one for
positive literals.
@llogiq llogiq merged commit 0a69132 into llogiq:master Feb 25, 2018
@llogiq
Copy link
Owner

llogiq commented Feb 25, 2018

Great work! Thanks again! 👍

@gnieto gnieto mentioned this pull request Feb 26, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants