1 /**
2 Copyright: Copyright (c) 2018, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 
10 #SPC-mutation_ror
11 */
12 module dextool.plugin.mutate.backend.mutation_type.ror;
13 
14 import std.array : empty;
15 
16 import dextool.plugin.mutate.backend.type;
17 
18 import dextool.plugin.mutate.backend.analyze.ast;
19 
20 @safe:
21 
22 /// Information used to intelligently generate ror mutants;
23 struct RorInfo {
24     Kind operator;
25     Type lhs;
26     Symbol lhsSym;
27     Type rhs;
28     Symbol rhsSym;
29 }
30 
31 /** Produce the mutations that can be applied on an operator.
32  *
33  * The resulting schema is intended to be:
34  * normal + pointer - float - enum - bool
35  *
36  * The schema when used is:
37  * ror = normal - float - enum - bool
38  * rorp = pointer - float - enum - bool
39  *
40  * See SPC-mutation_ror for the subsumed table.
41  */
42 auto rorMutations(RorInfo info) {
43     import std.typecons : Tuple;
44     import std.algorithm : among;
45 
46     alias Rval = Tuple!(Mutation.Kind[], "op", Mutation.Kind[], "expr");
47     Rval rval;
48 
49     // initialize rval with the basic ROR schema
50     switch (info.operator) with (Mutation.Kind) {
51     case Kind.OpLess:
52         rval = Rval([rorLE, rorpLE, rorNE, rorpNE], [rorFalse]);
53         break;
54     case Kind.OpGreater:
55         rval = Rval([rorGE, rorpGE, rorNE, rorpNE], [rorFalse]);
56         break;
57     case Kind.OpLessEq:
58         rval = Rval([rorLT, rorpLT, rorEQ, rorpEQ], [rorTrue]);
59         break;
60     case Kind.OpGreaterEq:
61         rval = Rval([rorGT, rorpGT, rorEQ, rorpEQ], [rorTrue]);
62         break;
63     case Kind.OpEqual:
64         rval = Rval([rorLE, rorpLE, rorGE, rorpGE], [rorFalse]);
65         break;
66     case Kind.OpNotEqual:
67         rval = Rval([rorLT, rorpLT, rorGT, rorpGT], [rorTrue]);
68         break;
69     default:
70     }
71 
72     // in case the operator isn't a ROR operator.
73     if (rval.op.empty && rval.expr.empty)
74         return rval;
75 
76     // #SPC-mutation_ror_enum
77     void discreteSchema() {
78         // information about the type and the concrete value of lhs is available
79         if (info.lhs !is null && info.lhsSym !is null)
80             with (Mutation.Kind) {
81                 const c = info.lhs.range.compare(info.lhsSym.value);
82 
83                 if (c == Range.CompareResult.inside) {
84                     // do nothing because lhs is inside thus all ROR mutants are possible
85                 } else if (info.operator.among(Kind.OpEqual, Kind.OpNotEqual)
86                         && c.among(Range.CompareResult.onLowerBound,
87                             Range.CompareResult.onUpperBound)) {
88                     rval = Rval(null, [rorTrue, rorFalse]);
89                 }
90             }
91 
92         if (info.rhs !is null && info.rhsSym !is null)
93             with (Mutation.Kind) {
94                 const c = info.rhs.range.compare(info.rhsSym.value);
95 
96                 if (c == Range.CompareResult.inside) {
97                     // do nothing because lhs is inside thus all ROR mutants are possible
98                 } else if (info.operator.among(Kind.OpEqual, Kind.OpNotEqual)
99                         && c.among(Range.CompareResult.onLowerBound,
100                             Range.CompareResult.onUpperBound)) {
101                     rval = Rval(null, [rorTrue, rorFalse]);
102                 }
103             }
104     }
105 
106     // #SPC-mutation_ror_float
107     void continuesSchema() {
108         switch (info.operator) with (Mutation.Kind) {
109         case Kind.OpLess:
110             rval.op = [rorGT, rorpGT];
111             break;
112         case Kind.OpGreater:
113             rval.op = [rorLT, rorpLT];
114             break;
115         case Kind.OpLessEq:
116             rval.op = [rorGT, rorpGT];
117             break;
118         case Kind.OpGreaterEq:
119             rval.op = [rorLT, rorpLT];
120             break;
121         default:
122         }
123     }
124 
125     // #SPC-mutation_ror_bool
126     void boolSchema() {
127         switch (info.operator) with (Mutation.Kind) {
128         case Kind.OpEqual:
129             rval.op = [rorNE, rorpNE];
130             break;
131         case Kind.OpNotEqual:
132             rval.op = [rorEQ, rorpEQ];
133             break;
134         default:
135         }
136     }
137 
138     // #SPC-mutation_ror_ptr
139     void unorderedSchema() {
140         switch (info.operator) with (Mutation.Kind) {
141         case Kind.OpEqual:
142             rval.op = [rorLE, rorGE, rorpNE];
143             break;
144         case Kind.OpNotEqual:
145             rval.op = [rorLT, rorGT, rorpEQ];
146             break;
147         default:
148         }
149     }
150 
151     // Returns: true if either the type for lhs or rhs match `k`.
152     bool isAny(TypeKind k) {
153         if (info.lhs !is null && info.lhs.kind == k)
154             return true;
155         if (info.rhs !is null && info.rhs.kind == k)
156             return true;
157         return false;
158     }
159 
160     if (isAny(TypeKind.unordered))
161         unorderedSchema;
162     else if (isAny(TypeKind.boolean))
163         boolSchema;
164     else if (isAny(TypeKind.continues))
165         continuesSchema;
166     else if (isAny(TypeKind.discrete))
167         discreteSchema;
168 
169     return rval;
170 }
171 
172 immutable Mutation.Kind[] rorMutationsAll;
173 immutable Mutation.Kind[] rorpMutationsAll;
174 
175 shared static this() {
176     with (Mutation.Kind) {
177         rorMutationsAll = [
178             rorLT, rorLE, rorGT, rorGE, rorEQ, rorNE, rorTrue, rorFalse
179         ];
180         rorpMutationsAll = [
181             rorpLT, rorpLE, rorpGT, rorpGE, rorpEQ, rorpNE, rorTrue, rorFalse
182         ];
183     }
184 }