Closed JonathanFoo0523 closed 1 month ago
I wonder if it comes from here:
Perhaps a switch statement is a kind of compound statement.
If that's the case, then clearly the sub-statement is not considered for removal.
I believe this is a bug. Perhaps a better example is:
int main() {
int x = 10;
switch (x) {
case 2:
printf("bar\n");
printf("buz\n");
default:
printf("baz\n");
}
}
which dredd mutate to:
int main() {
int x = __dredd_replace_expr_int_constant(10, 0);
if (!__dredd_enabled_mutation(26)) { switch (__dredd_replace_expr_int(__dredd_replace_expr_int_lvalue(&(x), 5), 7)) {
case 2:
__dredd_replace_expr_int(printf("bar\n"), 13);
if (!__dredd_enabled_mutation(19)) { printf("buz\n"); }
default:
__dredd_replace_expr_int(printf("baz\n"), 20);
} }
}
Note that:
(a) The first statement under case
/default
is not considered for removal
(b) The printf
statement under case
/default
is treated as int expression
Interesting.
First, printf
does have return type int
, so it's correct in principle to wrap a printf
call in __dredd_replace_expr_int
. The reason this doesn't happen to printf("buz\n")
appears to be due to an optimisation performed by Dredd. If I run with --no-mutation-opts
I get this code:
int main() {
int x = __dredd_replace_expr_int(10, 0);
if (!__dredd_enabled_mutation(33)) { switch (__dredd_replace_expr_int(__dredd_replace_expr_int_lvalue(&(x), 6), 8)) {
case 2:
__dredd_replace_expr_int(printf("bar\n"), 14);
if (!__dredd_enabled_mutation(26)) { __dredd_replace_expr_int(printf("buz\n"), 20); }
default:
__dredd_replace_expr_int(printf("baz\n"), 27);
} }
}
In this case, all of the printf
statements are getting wrapped in __dredd_replace_expr_int
.
What is confusing then is why the first statements in each case
and default
are getting treated differently from the rest.
I think I can see what is going on, via the --dump-asts
option, which gives this:
`-FunctionDecl 0x57061dd005f0 </home/afd/dev/dredd/tomutate.c:3:1, line:12:1> line:3:5 main 'int ()'
`-CompoundStmt 0x57061dd00b10 <col:12, line:12:1>
|-DeclStmt 0x57061dd00738 <line:4:3, col:13>
| `-VarDecl 0x57061dd006b0 <col:3, col:11> col:7 used x 'int' cinit
| `-IntegerLiteral 0x57061dd00718 <col:11> 'int' 10
`-SwitchStmt 0x57061dd00788 <line:5:3, line:11:3>
|-ImplicitCastExpr 0x57061dd00770 <line:5:11> 'int' <LValueToRValue>
| `-DeclRefExpr 0x57061dd00750 <col:11> 'int' lvalue Var 0x57061dd006b0 'x' 'int'
`-CompoundStmt 0x57061dd00ae8 <col:14, line:11:3>
|-CaseStmt 0x57061dd007e8 <line:6:3, line:7:19>
| |-ConstantExpr 0x57061dd007d0 <line:6:8> 'int'
| | `-IntegerLiteral 0x57061dd007b0 <col:8> 'int' 2
| `-CallExpr 0x57061dd008e0 <line:7:5, col:19> 'int'
| |-ImplicitCastExpr 0x57061dd008c8 <col:5> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x57061dd00810 <col:5> 'int (const char *, ...)' Function 0x57061dce8200 'printf' 'int (const char *, ...)'
| `-ImplicitCastExpr 0x57061dd00920 <col:12> 'const char *' <NoOp>
| `-ImplicitCastExpr 0x57061dd00908 <col:12> 'char *' <ArrayToPointerDecay>
| `-StringLiteral 0x57061dd00868 <col:12> 'char[5]' lvalue "bar\n"
|-CallExpr 0x57061dd009a8 <line:8:5, col:19> 'int'
| |-ImplicitCastExpr 0x57061dd00990 <col:5> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x57061dd00938 <col:5> 'int (const char *, ...)' Function 0x57061dce8200 'printf' 'int (const char *, ...)'
| `-ImplicitCastExpr 0x57061dd009e8 <col:12> 'const char *' <NoOp>
| `-ImplicitCastExpr 0x57061dd009d0 <col:12> 'char *' <ArrayToPointerDecay>
| `-StringLiteral 0x57061dd00958 <col:12> 'char[5]' lvalue "buz\n"
`-DefaultStmt 0x57061dd00ac8 <line:9:3, line:10:19>
`-CallExpr 0x57061dd00a70 <col:5, col:19> 'int'
|-ImplicitCastExpr 0x57061dd00a58 <col:5> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
| `-DeclRefExpr 0x57061dd00a00 <col:5> 'int (const char *, ...)' Function 0x57061dce8200 'printf' 'int (const char *, ...)'
`-ImplicitCastExpr 0x57061dd00ab0 <col:12> 'const char *' <NoOp>
`-ImplicitCastExpr 0x57061dd00a98 <col:12> 'char *' <ArrayToPointerDecay>
`-StringLiteral 0x57061dd00a20 <col:12> 'char[5]' lvalue "baz\n"
I'm going to do some more investigation, but it seems like case and default statements are not being wrapped due to code at line 543 of mutate_visitor.cc.
I'll do some more digging.
I noted that dredd doesn't apply delete-statement mutation for the first statement of case/default in a switch statement.
This function:
is mutated to something as:
Is there any reason we don't mutate it to something as follow?