diff --git a/.gitignore b/.gitignore index e23fbae..7ed0fca 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ Cargo.lock # Generated file from integration tests **/mutations + +.vscode/ diff --git a/mutagen-core/src/comm.rs b/mutagen-core/src/comm.rs index 7e5b4a4..899df34 100644 --- a/mutagen-core/src/comm.rs +++ b/mutagen-core/src/comm.rs @@ -1,13 +1,13 @@ //! Types and functions for communication between mutagen-processes //! -//! Mutagen requires several communication-channles to function fully +//! Mutagen requires several communication-channels to function fully //! //! * The procedural macro informs the runner about all baked mutations //! * The runner informs the test-suite about its mode (mutation or coverage) and additional required information (mutation_id, num_mutations) //! * The mutators in the test suite inform the runner about coverage-hits //! //! Currently, communication from the procedural macro and test-suite is implemented via files in the `target/mutagen` directory. -//! The communication to the test-suite is implemented via environemnt variables +//! The communication to the test-suite is implemented via environment variables mod coverage; mod mutagen_files; mod mutation; diff --git a/mutagen-core/src/comm/coverage.rs b/mutagen-core/src/comm/coverage.rs index 4abe2b8..8f8cda0 100644 --- a/mutagen-core/src/comm/coverage.rs +++ b/mutagen-core/src/comm/coverage.rs @@ -101,7 +101,7 @@ mod tests { } #[test] - fn coverage_collection_sinlge_covered() { + fn coverage_collection_single_covered() { let c = CoverageCollection::from_coverage_hits( 2, &[CoverageHit { mutator_id: 1 }], @@ -136,16 +136,10 @@ mod tests { Mutation::new_stub().with_id(3, 3), Mutation::new_stub().with_id(4, 3), ]; - let c1 = CoverageCollection::from_coverage_hits( - 4, - &[CoverageHit { mutator_id: 2 }], - &mutations, - ); - let c2 = CoverageCollection::from_coverage_hits( - 4, - &[CoverageHit { mutator_id: 3 }], - &mutations, - ); + let c1 = + CoverageCollection::from_coverage_hits(4, &[CoverageHit { mutator_id: 2 }], &mutations); + let c2 = + CoverageCollection::from_coverage_hits(4, &[CoverageHit { mutator_id: 3 }], &mutations); let c = CoverageCollection::merge(4, &[c1, c2]); diff --git a/mutagen-core/src/mutator/mutator_binop_bit.rs b/mutagen-core/src/mutator/mutator_binop_bit.rs index a248f6c..fd38563 100644 --- a/mutagen-core/src/mutator/mutator_binop_bit.rs +++ b/mutagen-core/src/mutator/mutator_binop_bit.rs @@ -4,7 +4,7 @@ use std::convert::TryFrom; use std::ops::Deref; use std::ops::{BitAnd, BitOr, BitXor}; -use proc_macro2::{Span, TokenStream}; +use proc_macro2::Span; use quote::quote_spanned; use syn::spanned::Spanned; use syn::{BinOp, Expr}; @@ -70,25 +70,6 @@ pub fn run_xor, R>( } } -pub fn run_native_num( - mutator_id: usize, - left: I, - right: I, - original_op: BinopBit, - runtime: impl Deref, -) -> I -where - I: BitAnd + BitOr + BitXor, -{ - runtime.covered(mutator_id); - let mutations = MutationBinopBit::possible_mutations(original_op); - if let Some(m) = runtime.get_mutation_for_mutator(mutator_id, &mutations) { - m.mutate(left, right) - } else { - original_op.calc(left, right) - } -} - pub fn transform( e: Expr, transform_info: &SharedTransformInfo, @@ -108,31 +89,24 @@ pub fn transform( let left = &e.left; let right = &e.right; - // if the current expression is based on numbers, use the function `run_native_num` instead - syn::parse2(if context.is_num_expr() { - let op = e.op_tokens(); - quote_spanned! {e.span=> - ::mutagen::mutator::mutator_binop_bit::run_native_num( - #mutator_id, - #left, - #right, - #op, - ::mutagen::MutagenRuntimeConfig::get_default() - ) - } - } else { - let run_fn = match e.op { - BinopBit::And => quote_spanned! {e.span=> run_and}, - BinopBit::Or => quote_spanned! {e.span=> run_or}, - BinopBit::Xor => quote_spanned! {e.span=> run_xor}, - }; - quote_spanned! {e.span=> - ::mutagen::mutator::mutator_binop_bit::#run_fn( + let run_fn = match e.op { + BinopBit::And => quote_spanned! {e.span()=> run_and}, + BinopBit::Or => quote_spanned! {e.span()=> run_or}, + BinopBit::Xor => quote_spanned! {e.span()=> run_xor}, + }; + let op_token = e.op_token; + let tmp_var = transform_info.get_next_tmp_var(op_token.span()); + syn::parse2(quote_spanned! {e.span()=> + { + let #tmp_var = #left; + if false {#tmp_var #op_token #right} else { + ::mutagen::mutator::mutator_binop_bit::#run_fn( #mutator_id, - #left, + #tmp_var, #right, ::mutagen::MutagenRuntimeConfig::get_default() ) + } } }) .expect("transformed code invalid") @@ -153,20 +127,13 @@ impl MutationBinopBit { .collect() } - fn mutate(self, left: I, right: I) -> I - where - I: BitAnd + BitOr + BitXor, - { - self.op.calc(left, right) - } - fn to_mutation(self, original_expr: &ExprBinopBit, context: &TransformContext) -> Mutation { Mutation::new_spanned( &context, "binop_bit".to_owned(), format!("{}", original_expr.op), format!("{}", self.op), - original_expr.span, + original_expr.span(), ) } } @@ -176,7 +143,7 @@ struct ExprBinopBit { op: BinopBit, left: Expr, right: Expr, - span: Span, + op_token: syn::BinOp, } impl TryFrom for ExprBinopBit { @@ -184,23 +151,23 @@ impl TryFrom for ExprBinopBit { fn try_from(expr: Expr) -> Result { match expr { Expr::Binary(expr) => match expr.op { - BinOp::BitAnd(t) => Ok(ExprBinopBit { + BinOp::BitAnd(_) => Ok(ExprBinopBit { op: BinopBit::And, left: *expr.left, right: *expr.right, - span: t.span(), + op_token: expr.op, }), - BinOp::BitOr(t) => Ok(ExprBinopBit { + BinOp::BitOr(_) => Ok(ExprBinopBit { op: BinopBit::Or, left: *expr.left, right: *expr.right, - span: t.span(), + op_token: expr.op, }), - BinOp::BitXor(t) => Ok(ExprBinopBit { + BinOp::BitXor(_) => Ok(ExprBinopBit { op: BinopBit::Xor, left: *expr.left, right: *expr.right, - span: t.span(), + op_token: expr.op, }), _ => Err(Expr::Binary(expr)), }, @@ -209,40 +176,19 @@ impl TryFrom for ExprBinopBit { } } -impl ExprBinopBit { - fn op_tokens(&self) -> TokenStream { - let mut tokens = TokenStream::new(); - tokens.extend(quote_spanned!(self.span=> - ::mutagen::mutator::mutator_binop_bit::BinopBit::)); - tokens.extend(match self.op { - BinopBit::And => quote_spanned!(self.span=> And), - BinopBit::Or => quote_spanned!(self.span=> Or), - BinopBit::Xor => quote_spanned!(self.span=> Xor), - }); - tokens +impl syn::spanned::Spanned for ExprBinopBit { + fn span(&self) -> Span { + self.op_token.span() } } #[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub enum BinopBit { +enum BinopBit { And, Or, Xor, } -impl BinopBit { - fn calc(self, l: I, r: I) -> I - where - I: BitAnd + BitOr + BitXor, - { - match self { - BinopBit::And => l & r, - BinopBit::Or => l | r, - BinopBit::Xor => l ^ r, - } - } -} - use std::fmt; impl fmt::Display for BinopBit { @@ -270,7 +216,7 @@ macro_rules! binary_x_to_y { impl $may_ty for L where L: $t1 { type Output = >::Output; default fn $may_fn(self, _r: R) -> >::Output { - MutagenRuntimeConfig::get_default().optimistic_assmuption_failed(); + MutagenRuntimeConfig::get_default().optimistic_assumption_failed(); } } @@ -350,38 +296,4 @@ mod tests { let result = run_xor(1, 0b11, 0b10, &MutagenRuntimeConfig::with_mutation_id(2)); assert_eq!(result, 0b11); } - - #[test] - fn or_native_inactive() { - let result = run_native_num( - 1, - 0b11, - 0b10, - BinopBit::Or, - &MutagenRuntimeConfig::without_mutation(), - ); - assert_eq!(result, 0b11); - } - #[test] - fn or_native_active1() { - let result = run_native_num( - 1, - 0b11, - 0b10, - BinopBit::Or, - &MutagenRuntimeConfig::with_mutation_id(1), - ); - assert_eq!(result, 0b10); - } - #[test] - fn or_native_active2() { - let result = run_native_num( - 1, - 0b11, - 0b10, - BinopBit::Or, - &MutagenRuntimeConfig::with_mutation_id(2), - ); - assert_eq!(result, 0b01); - } } diff --git a/mutagen-core/src/mutator/mutator_binop_num.rs b/mutagen-core/src/mutator/mutator_binop_num.rs index 4d55304..6976144 100644 --- a/mutagen-core/src/mutator/mutator_binop_num.rs +++ b/mutagen-core/src/mutator/mutator_binop_num.rs @@ -4,7 +4,7 @@ use std::convert::TryFrom; use std::ops::Deref; use std::ops::{Add, Div, Mul, Sub}; -use proc_macro2::{Span, TokenStream}; +use proc_macro2::Span; use quote::quote_spanned; use syn::spanned::Spanned; use syn::{BinOp, Expr}; @@ -68,25 +68,6 @@ pub fn run_div, R>( } } -pub fn run_native_num( - mutator_id: usize, - left: I, - right: I, - original_op: BinopNum, - runtime: impl Deref, -) -> I -where - I: Add + Sub + Mul + Div, -{ - runtime.covered(mutator_id); - let mutations = MutationBinopNum::possible_mutations(original_op); - if let Some(m) = runtime.get_mutation_for_mutator(mutator_id, &mutations) { - m.mutate(left, right) - } else { - original_op.calc(left, right) - } -} - pub fn transform( e: Expr, transform_info: &SharedTransformInfo, @@ -105,33 +86,25 @@ pub fn transform( let left = &e.left; let right = &e.right; - - // if the current expression is based on numbers, use the function `run_native_num` instead - syn::parse2(if context.is_num_expr() { - let op = e.op_tokens(); - quote_spanned! {e.span=> - ::mutagen::mutator::mutator_binop_num::run_native_num( - #mutator_id, - #left, - #right, - #op, - ::mutagen::MutagenRuntimeConfig::get_default() - ) - } - } else { - let run_fn = match e.op { - BinopNum::Add => quote_spanned! {e.span=> run_add}, - BinopNum::Sub => quote_spanned! {e.span=> run_sub}, - BinopNum::Mul => quote_spanned! {e.span=> run_mul}, - BinopNum::Div => quote_spanned! {e.span=> run_div}, - }; - quote_spanned! {e.span=> - ::mutagen::mutator::mutator_binop_num::#run_fn( + let run_fn = match e.op { + BinopNum::Add => quote_spanned! {e.span()=> run_add}, + BinopNum::Sub => quote_spanned! {e.span()=> run_sub}, + BinopNum::Mul => quote_spanned! {e.span()=> run_mul}, + BinopNum::Div => quote_spanned! {e.span()=> run_div}, + }; + let op_token = e.op_token; + let tmp_var = transform_info.get_next_tmp_var(op_token.span()); + syn::parse2(quote_spanned! {e.span()=> + { + let #tmp_var = #left; + if false {#tmp_var #op_token #right} else { + ::mutagen::mutator::mutator_binop_num::#run_fn( #mutator_id, - #left, + #tmp_var, #right, ::mutagen::MutagenRuntimeConfig::get_default() ) + } } }) .expect("transformed code invalid") @@ -152,20 +125,13 @@ impl MutationBinopNum { } } - fn mutate(self, left: I, right: I) -> I - where - I: Add + Sub + Mul + Div, - { - self.op.calc(left, right) - } - fn to_mutation(self, original_expr: &ExprBinopNum, context: &TransformContext) -> Mutation { Mutation::new_spanned( &context, "binop_num".to_owned(), format!("{}", original_expr.op), format!("{}", self.op), - original_expr.span, + original_expr.span(), ) } } @@ -175,7 +141,7 @@ struct ExprBinopNum { op: BinopNum, left: Expr, right: Expr, - span: Span, + op_token: syn::BinOp, } impl TryFrom for ExprBinopNum { @@ -183,29 +149,29 @@ impl TryFrom for ExprBinopNum { fn try_from(expr: Expr) -> Result { match expr { Expr::Binary(expr) => match expr.op { - BinOp::Add(t) => Ok(ExprBinopNum { + BinOp::Add(_) => Ok(ExprBinopNum { op: BinopNum::Add, left: *expr.left, right: *expr.right, - span: t.span(), + op_token: expr.op, }), - BinOp::Sub(t) => Ok(ExprBinopNum { + BinOp::Sub(_) => Ok(ExprBinopNum { op: BinopNum::Sub, left: *expr.left, right: *expr.right, - span: t.span(), + op_token: expr.op, }), - BinOp::Mul(t) => Ok(ExprBinopNum { + BinOp::Mul(_) => Ok(ExprBinopNum { op: BinopNum::Mul, left: *expr.left, right: *expr.right, - span: t.span(), + op_token: expr.op, }), - BinOp::Div(t) => Ok(ExprBinopNum { + BinOp::Div(_) => Ok(ExprBinopNum { op: BinopNum::Div, left: *expr.left, right: *expr.right, - span: t.span(), + op_token: expr.op, }), _ => Err(Expr::Binary(expr)), }, @@ -214,43 +180,20 @@ impl TryFrom for ExprBinopNum { } } -impl ExprBinopNum { - fn op_tokens(&self) -> TokenStream { - let mut tokens = TokenStream::new(); - tokens.extend(quote_spanned!(self.span=> - ::mutagen::mutator::mutator_binop_num::BinopNum::)); - tokens.extend(match self.op { - BinopNum::Add => quote_spanned!(self.span=> Add), - BinopNum::Sub => quote_spanned!(self.span=> Sub), - BinopNum::Mul => quote_spanned!(self.span=> Mul), - BinopNum::Div => quote_spanned!(self.span=> Div), - }); - tokens +impl syn::spanned::Spanned for ExprBinopNum { + fn span(&self) -> Span { + self.op_token.span() } } #[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub enum BinopNum { +enum BinopNum { Add, Sub, Mul, Div, } -impl BinopNum { - fn calc(self, l: I, r: I) -> I - where - I: Add + Sub + Mul + Div, - { - match self { - BinopNum::Add => l + r, - BinopNum::Sub => l - r, - BinopNum::Mul => l * r, - BinopNum::Div => l / r, - } - } -} - use std::fmt; impl fmt::Display for BinopNum { @@ -279,7 +222,7 @@ macro_rules! binary_x_to_y { impl $may_ty for L where L: $t1 { type Output = >::Output; default fn $may_fn(self, _r: R) -> >::Output { - MutagenRuntimeConfig::get_default().optimistic_assmuption_failed(); + MutagenRuntimeConfig::get_default().optimistic_assumption_failed(); } } @@ -341,28 +284,4 @@ mod tests { &MutagenRuntimeConfig::with_mutation_id(1), ); } - - #[test] - fn sum_native_inactive() { - let result = run_native_num( - 1, - 5, - 4, - BinopNum::Add, - &MutagenRuntimeConfig::without_mutation(), - ); - assert_eq!(result, 9); - } - - #[test] - fn sum_native_active() { - let result = run_native_num( - 1, - 5, - 4, - BinopNum::Add, - &MutagenRuntimeConfig::with_mutation_id(1), - ); - assert_eq!(result, 1); - } } diff --git a/mutagen-core/src/mutator/mutator_stmt_call.rs b/mutagen-core/src/mutator/mutator_stmt_call.rs index cc3e4bd..db0bf40 100644 --- a/mutagen-core/src/mutator/mutator_stmt_call.rs +++ b/mutagen-core/src/mutator/mutator_stmt_call.rs @@ -85,7 +85,7 @@ impl TryFrom for StmtCall { } } -/// a trait for optimistically removing a statement containing a method- or functioncall. +/// a trait for optimistically removing a statement containing a method- or function call. /// /// This operation is optimistic, since the statement could have the type `!` and can be used in surprising contexts: /// @@ -99,7 +99,7 @@ pub trait StmtCallToNone { impl StmtCallToNone for T { default fn stmt_call_to_none() -> Self { - MutagenRuntimeConfig::get_default().optimistic_assmuption_failed(); + MutagenRuntimeConfig::get_default().optimistic_assumption_failed(); } } diff --git a/mutagen-core/src/mutator/mutator_unop_not.rs b/mutagen-core/src/mutator/mutator_unop_not.rs index 0dfcbb3..6b38ee9 100644 --- a/mutagen-core/src/mutator/mutator_unop_not.rs +++ b/mutagen-core/src/mutator/mutator_unop_not.rs @@ -28,19 +28,6 @@ pub fn run( } } -pub fn run_native_num>( - mutator_id: usize, - val: I, - runtime: impl Deref, -) -> I { - runtime.covered(mutator_id); - if runtime.is_mutation_active(mutator_id) { - val - } else { - !val - } -} - pub fn transform( e: Expr, transform_info: &SharedTransformInfo, @@ -56,32 +43,31 @@ pub fn transform( "unop_not".to_owned(), "!".to_owned(), "".to_owned(), - e.span, + e.span(), )); let expr = &e.expr; - - // if the current expression is based on numbers, use the function `run_native_num` instead - let run_fn = if context.is_num_expr() { - quote_spanned! {e.span=> run_native_num} - } else { - quote_spanned! {e.span=> run} - }; - - syn::parse2(quote_spanned! {e.span=> - ::mutagen::mutator::mutator_unop_not::#run_fn( - #mutator_id, - #expr, - ::mutagen::MutagenRuntimeConfig::get_default() - ) + let op_token = e.op_token; + let tmp_var = transform_info.get_next_tmp_var(op_token.span()); + syn::parse2(quote_spanned! {e.span()=> + { + let #tmp_var = #expr; + if false {!#tmp_var} else { + ::mutagen::mutator::mutator_unop_not::run( + #mutator_id, + #tmp_var, + ::mutagen::MutagenRuntimeConfig::get_default() + ) + } + } }) .expect("transformed code invalid") } #[derive(Clone, Debug)] -pub struct ExprUnopNot { - pub expr: Expr, - pub span: Span, +struct ExprUnopNot { + expr: Expr, + op_token: syn::UnOp, } impl TryFrom for ExprUnopNot { @@ -89,9 +75,9 @@ impl TryFrom for ExprUnopNot { fn try_from(expr: Expr) -> Result { match expr { Expr::Unary(expr) => match expr.op { - UnOp::Not(t) => Ok(ExprUnopNot { + UnOp::Not(_) => Ok(ExprUnopNot { expr: *expr.expr, - span: t.span(), + op_token: expr.op, }), _ => Err(Expr::Unary(expr)), }, @@ -100,6 +86,12 @@ impl TryFrom for ExprUnopNot { } } +impl syn::spanned::Spanned for ExprUnopNot { + fn span(&self) -> Span { + self.op_token.span() + } +} + /// trait that is used to optimistically remove a negation `!` from an expression /// /// This trait provides a function `may_none` that passes the input value unchanged @@ -117,7 +109,7 @@ where type Output = ::Output; default fn may_none(self) -> ::Output { - MutagenRuntimeConfig::get_default().optimistic_assmuption_failed(); + MutagenRuntimeConfig::get_default().optimistic_assumption_failed(); } } @@ -133,7 +125,7 @@ where /// types for testing the optimistic mutator that removes the negation #[cfg(any(test, feature = "self_test"))] -pub mod optimistc_types { +pub mod optimistic_types { use std::ops::Not; @@ -154,7 +146,7 @@ pub mod optimistc_types { #[cfg(test)] mod tests { - use super::optimistc_types::*; + use super::optimistic_types::*; use super::*; #[test] @@ -168,11 +160,6 @@ mod tests { let result = run(1, true, &MutagenRuntimeConfig::with_mutation_id(1)); assert_eq!(result, true); } - #[test] - fn intnot_active() { - let result = run_native_num(1, 1, &MutagenRuntimeConfig::with_mutation_id(1)); - assert_eq!(result, 1); - } #[test] fn optimistic_incorrect_inactive() { diff --git a/mutagen-core/src/runtime_config.rs b/mutagen-core/src/runtime_config.rs index 1534786..5f6d991 100644 --- a/mutagen-core/src/runtime_config.rs +++ b/mutagen-core/src/runtime_config.rs @@ -51,7 +51,7 @@ pub struct CoverageRecorder { } impl MutagenRuntimeConfig { - /// Sccess the currently active runtime-config based on the environment variable `MUATION_ID`. + /// Success the currently active runtime-config based on the environment variable `MUTATION_ID`. /// /// During tests, the global runtime_config can be set to any value to allow /// exhaustive testing. @@ -71,7 +71,7 @@ impl MutagenRuntimeConfig { let num_mutations = std::env::var("MUTAGEN_NUM_MUTATIONS") .ok() .and_then(|s| s.parse().ok()) - .expect("environemnt variable `MUTAGEN_NUM_MUTATIONS` missing"); + .expect("environment variable `MUTAGEN_NUM_MUTATIONS` missing"); Self::Coverage(CoverageRecorder::new(num_mutations)) } "" | "mutation" => { @@ -101,7 +101,7 @@ impl MutagenRuntimeConfig { /// Function to abort the computation in case a optimistic mutation fails. /// /// In the future, this will be configurable - pub fn optimistic_assmuption_failed(&self) -> ! { + pub fn optimistic_assumption_failed(&self) -> ! { match self { Self::Mutation(m_id) => { panic!("optimistic assumption failed for mutation {}", m_id); diff --git a/mutagen-core/src/transformer.rs b/mutagen-core/src/transformer.rs index 267b373..df4b476 100644 --- a/mutagen-core/src/transformer.rs +++ b/mutagen-core/src/transformer.rs @@ -3,7 +3,6 @@ use quote::ToTokens; use syn::fold::Fold; mod arg_ast; -pub(crate) mod ast_inspect; mod mutate_args; pub mod transform_context; pub mod transform_info; diff --git a/mutagen-core/src/transformer/ast_inspect.rs b/mutagen-core/src/transformer/ast_inspect.rs index 7b3c737..dd163be 100644 --- a/mutagen-core/src/transformer/ast_inspect.rs +++ b/mutagen-core/src/transformer/ast_inspect.rs @@ -7,33 +7,57 @@ /// * it is an binary arithmetic- or bit-operation that has an integer expression on the left side /// * it is an unary operation with an numeric expression /// * it is a reference to a numeric expression. This lets us count `*&1` as numeric expression. +/// * it is a block that ends in a numeric expression. This lets us count {...; 1} as numeric expression. +/// * it is a if expression with an numeric expression as one of the cases pub fn is_num_expr(e: &syn::Expr) -> bool { match e { syn::Expr::Lit(expr) => match expr.lit { - syn::Lit::Int(_) => return true, - syn::Lit::Byte(_) => return true, - syn::Lit::Float(_) => return true, - _ => {} + syn::Lit::Int(_) => true, + syn::Lit::Byte(_) => true, + syn::Lit::Float(_) => true, + _ => false, }, syn::Expr::Binary(expr) => match expr.op { - syn::BinOp::Add(_) => return is_num_expr(&expr.left), - syn::BinOp::Sub(_) => return is_num_expr(&expr.left), - syn::BinOp::Mul(_) => return is_num_expr(&expr.left), - syn::BinOp::Div(_) => return is_num_expr(&expr.left), - syn::BinOp::Rem(_) => return is_num_expr(&expr.left), - syn::BinOp::BitAnd(_) => return is_num_expr(&expr.left), - syn::BinOp::BitOr(_) => return is_num_expr(&expr.left), - syn::BinOp::BitXor(_) => return is_num_expr(&expr.left), - syn::BinOp::Shl(_) => return is_num_expr(&expr.left), - syn::BinOp::Shr(_) => return is_num_expr(&expr.left), - _ => {} + syn::BinOp::Add(_) => is_num_expr(&expr.left), + syn::BinOp::Sub(_) => is_num_expr(&expr.left), + syn::BinOp::Mul(_) => is_num_expr(&expr.left), + syn::BinOp::Div(_) => is_num_expr(&expr.left), + syn::BinOp::Rem(_) => is_num_expr(&expr.left), + syn::BinOp::BitAnd(_) => is_num_expr(&expr.left), + syn::BinOp::BitOr(_) => is_num_expr(&expr.left), + syn::BinOp::BitXor(_) => is_num_expr(&expr.left), + syn::BinOp::Shl(_) => is_num_expr(&expr.left), + syn::BinOp::Shr(_) => is_num_expr(&expr.left), + _ => false, }, - syn::Expr::Unary(expr) => return is_num_expr(&expr.expr), - syn::Expr::Reference(expr) => return is_num_expr(&expr.expr), - syn::Expr::Paren(expr) => return is_num_expr(&expr.expr), - _ => {} - }; - return false; + syn::Expr::Unary(expr) => is_num_expr(&expr.expr), + syn::Expr::Reference(expr) => is_num_expr(&expr.expr), + syn::Expr::Paren(expr) => is_num_expr(&expr.expr), + syn::Expr::Block(expr) => is_num_block(&expr.block), + syn::Expr::If(expr) => is_num_expr_if(&expr), + _ => false, + } +} + +fn is_num_expr_if(expr: &syn::ExprIf) -> bool { + is_num_block(&expr.then_branch) + || match &expr.else_branch { + Some((_, else_expr)) => is_num_expr(else_expr), + _ => false, + } +} + +fn is_num_block(block: &syn::Block) -> bool { + match block.stmts.last() { + Some(stmt) => is_num_stmt(&stmt), + _ => false, + } +} +fn is_num_stmt(stmt: &syn::Stmt) -> bool { + match stmt { + syn::Stmt::Expr(expr) => is_num_expr(&expr), + _ => false, + } } #[cfg(test)] diff --git a/mutagen-core/src/transformer/transform_context.rs b/mutagen-core/src/transformer/transform_context.rs index 0abe1d9..d4ec601 100644 --- a/mutagen-core/src/transformer/transform_context.rs +++ b/mutagen-core/src/transformer/transform_context.rs @@ -1,5 +1,3 @@ -use super::ast_inspect; - #[derive(Debug, Default)] pub struct TransformContext { pub impl_name: Option, @@ -7,12 +5,3 @@ pub struct TransformContext { pub original_stmt: Option, pub original_expr: Option, } - -impl TransformContext { - pub fn is_num_expr(&self) -> bool { - self.original_expr - .as_ref() - .map(|e| ast_inspect::is_num_expr(e)) - .unwrap_or(false) - } -} diff --git a/mutagen-core/src/transformer/transform_info.rs b/mutagen-core/src/transformer/transform_info.rs index 4f6fafc..172561a 100644 --- a/mutagen-core/src/transformer/transform_info.rs +++ b/mutagen-core/src/transformer/transform_info.rs @@ -3,6 +3,8 @@ use std::fs::{create_dir_all, File}; use std::iter; use std::sync::{Arc, Mutex, MutexGuard}; +use proc_macro2::Span; + use super::mutate_args::LocalConf; use crate::comm; use crate::comm::{BakedMutation, Mutation}; @@ -16,12 +18,15 @@ pub struct SharedTransformInfo(Arc>); /// Contains information about all mutations inserted into the code under test /// -/// This struct is used to collect the mutations during transformation. After running all transformers, this struct contains all mutators and their mutaitons inserted into the code +/// This struct is used to collect the mutations during transformation. +/// After running all transformers, this struct contains all mutators +/// and their mutations inserted into the code #[derive(Debug)] pub struct MutagenTransformInfo { mutations: Vec, mutagen_file: Option, expected_mutations: Option, + tmp_var_id: usize, } impl Default for MutagenTransformInfo { @@ -30,12 +35,13 @@ impl Default for MutagenTransformInfo { mutations: vec![], mutagen_file: None, expected_mutations: None, + tmp_var_id: 0, } } } impl MutagenTransformInfo { - pub fn with_default_mutagen_file(&mut self) { + fn with_default_mutagen_file(&mut self) { // open file only once if self.mutagen_file.is_none() { let mutagen_filepath = comm::get_mutations_file().unwrap(); @@ -51,7 +57,7 @@ impl MutagenTransformInfo { } /// add a mutation and return the id used for it, also writes the mutation to the global file. - pub fn add_mutation(&mut self, mutation: Mutation, mutator_id: usize) -> usize { + fn add_mutation(&mut self, mutation: Mutation, mutator_id: usize) -> usize { let mut_id = 1 + self.mutations.len(); let mutation = mutation.with_id(mut_id, mutator_id); @@ -67,15 +73,15 @@ impl MutagenTransformInfo { mut_id } - pub fn get_num_mutations(&self) -> usize { + fn get_num_mutations(&self) -> usize { self.mutations.len() } - pub fn get_next_mutation_id(&self) -> usize { + fn get_next_mutation_id(&self) -> usize { self.mutations.len() + 1 } - pub fn check_mutations(&mut self) { + fn check_mutations(&mut self) { if let Some(expected_mutations) = self.expected_mutations { let actual_mutations = self.mutations.len(); if expected_mutations != actual_mutations { @@ -86,10 +92,15 @@ impl MutagenTransformInfo { } } } + + fn get_next_tmp_var(&mut self, span: Span) -> syn::Ident { + self.tmp_var_id += 1; + syn::Ident::new(&format!("__mutagen_tmp_{}", self.tmp_var_id), span) + } } impl SharedTransformInfo { - fn lock_tranform_info(&self) -> MutexGuard { + fn lock_transform_info(&self) -> MutexGuard { self.0.lock().unwrap() } @@ -99,7 +110,7 @@ impl SharedTransformInfo { pub fn global_info() -> Self { GLOBAL_TRANSFORM_INFO - .lock_tranform_info() + .lock_transform_info() .with_default_mutagen_file(); GLOBAL_TRANSFORM_INFO.clone_shared() } @@ -117,7 +128,7 @@ impl SharedTransformInfo { } pub fn add_mutations(&self, mutations: impl IntoIterator) -> usize { - let mut transform_info = self.lock_tranform_info(); + let mut transform_info = self.lock_transform_info(); let mutator_id = transform_info.get_next_mutation_id(); @@ -133,10 +144,14 @@ impl SharedTransformInfo { } pub fn get_num_mutations(&self) -> usize { - self.lock_tranform_info().get_num_mutations() + self.lock_transform_info().get_num_mutations() } pub fn check_mutations(&self) { - self.lock_tranform_info().check_mutations() + self.lock_transform_info().check_mutations() + } + + pub fn get_next_tmp_var(&self, span: Span) -> syn::Ident { + self.lock_transform_info().get_next_tmp_var(span) } } diff --git a/mutagen-runner/src/progress.rs b/mutagen-runner/src/progress.rs index 9088640..5983a33 100644 --- a/mutagen-runner/src/progress.rs +++ b/mutagen-runner/src/progress.rs @@ -9,8 +9,8 @@ use failure::Fallible; -use std::time::Duration; use std::path::Path; +use std::time::Duration; use mutagen_core::comm::{BakedMutation, MutantStatus}; @@ -162,7 +162,10 @@ impl Progress { /// clears the progress-bar pub fn finish(mut self, mutagen_time: Duration) -> Fallible<()> { let rounded_time = Duration::from_secs(mutagen_time.as_secs()); - self.bar.println(&format!("Total time: {}", ::humantime::format_duration(rounded_time)))?; + self.bar.println(&format!( + "Total time: {}", + ::humantime::format_duration(rounded_time) + ))?; self.bar.finish()?; Ok(()) } diff --git a/mutagen-runner/src/test_bin.rs b/mutagen-runner/src/test_bin.rs index 914c7fc..bc27d78 100644 --- a/mutagen-runner/src/test_bin.rs +++ b/mutagen-runner/src/test_bin.rs @@ -86,7 +86,6 @@ impl<'a> TestBin<'a> { } impl<'a> TestBinTested<'a> { - /// Checks if any mutation is covered. /// /// Returns false, if no mutation is covered by the testsuite diff --git a/mutagen-selftest/src/mutator/test_binop_num.rs b/mutagen-selftest/src/mutator/test_binop_num.rs index 63079e9..418679c 100644 --- a/mutagen-selftest/src/mutator/test_binop_num.rs +++ b/mutagen-selftest/src/mutator/test_binop_num.rs @@ -27,7 +27,7 @@ mod test_sum_u32 { use ::mutagen::mutate; use ::mutagen::MutagenRuntimeConfig; - // simple function that sums 2 u32 values. Unfortunately, the tag `u32` is necessary + // simple function that sums 2 u32 values #[mutate(conf = local(expected_mutations = 1), mutators = only(binop_num))] fn sum_u32() -> u32 { 5 + 1 @@ -132,7 +132,7 @@ mod test_mul_2_3 { use ::mutagen::mutate; use ::mutagen::MutagenRuntimeConfig; - // sum of multiple values without brackets + // multiplication of two integers #[mutate(conf = local(expected_mutations = 1), mutators = only(binop_num))] pub fn mul_2_3() -> u32 { 2 * 3 @@ -157,7 +157,7 @@ mod test_div_4_4 { use ::mutagen::mutate; use ::mutagen::MutagenRuntimeConfig; - // sum of multiple values without brackets + // division of two integers #[mutate(conf = local(expected_mutations = 1), mutators = only(binop_num))] pub fn div_4_4() -> u32 { 4 / 4 @@ -176,3 +176,82 @@ mod test_div_4_4 { }) } } + +mod test_add_after_if { + use ::mutagen::mutate; + use ::mutagen::MutagenRuntimeConfig; + + // addition after an if expression + #[mutate(conf = local(expected_mutations = 1), mutators = only(binop_num))] + pub fn add_after_if(bit: bool) -> i8 { + (if bit { 1 } else { 0 }) + 2 + } + + #[test] + fn add_after_if_inactive() { + MutagenRuntimeConfig::test_without_mutation(|| { + assert_eq!(add_after_if(true), 3); + assert_eq!(add_after_if(false), 2); + }) + } + #[test] + fn add_after_if_active1() { + MutagenRuntimeConfig::test_with_mutation_id(1, || { + assert_eq!(add_after_if(true), -1); + assert_eq!(add_after_if(false), -2); + }) + } +} + +mod test_add_after_block { + use ::mutagen::mutate; + use ::mutagen::MutagenRuntimeConfig; + + // addition after a block expression + #[mutate(conf = local(expected_mutations = 1), mutators = only(binop_num))] + pub fn add_after_block() -> i8 { + ({ + ""; + 1 + }) + 2 + } + + #[test] + fn add_after_block_inactive() { + MutagenRuntimeConfig::test_without_mutation(|| { + assert_eq!(add_after_block(), 3); + }) + } + #[test] + fn add_after_block_active1() { + MutagenRuntimeConfig::test_with_mutation_id(1, || { + assert_eq!(add_after_block(), -1); + }) + } +} + +mod test_mul_i64_with_tempvar { + + use ::mutagen::mutate; + use ::mutagen::MutagenRuntimeConfig; + + // multiplies two numbers, the first one is a temporary variable + #[mutate(conf = local(expected_mutations = 1), mutators = only(binop_num))] + pub fn mul_with_tempvar() -> i64 { + let x = 4; + x * 2 + } + + #[test] + fn mul_with_tempvar_inactive() { + MutagenRuntimeConfig::test_without_mutation(|| { + assert_eq!(mul_with_tempvar(), 8); + }) + } + #[test] + fn mul_with_tempvar_active1() { + MutagenRuntimeConfig::test_with_mutation_id(1, || { + assert_eq!(mul_with_tempvar(), 2); + }) + } +} diff --git a/mutagen-selftest/src/mutator/test_unop_not.rs b/mutagen-selftest/src/mutator/test_unop_not.rs index 42a0cd2..2370446 100644 --- a/mutagen-selftest/src/mutator/test_unop_not.rs +++ b/mutagen-selftest/src/mutator/test_unop_not.rs @@ -27,7 +27,7 @@ mod test_boolnot { mod test_optimistic_incorrect { use ::mutagen::mutate; - use ::mutagen::mutator::mutator_unop_not::optimistc_types::{ + use ::mutagen::mutator::mutator_unop_not::optimistic_types::{ TypeWithNotOtherOutput, TypeWithNotTarget, }; use ::mutagen::MutagenRuntimeConfig;