1 /**
2 Copyright: Copyright (c) 2017, 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 module dextool.plugin.mutate.backend.type;
11 
12 import dextool.hash : Checksum128, BuildChecksum128, toBytes, toChecksum128;
13 public import dextool.plugin.mutate.backend.database.type : MutantAttr, MutantMetaData;
14 
15 @safe:
16 
17 alias Checksum = Checksum128;
18 
19 /// Used to replace invalid UTF-8 characters.
20 immutable invalidUtf8 = "[invalid utf8]";
21 
22 /** A mutation point for a specific file.
23  *
24  * TODO: shouldn't this have the file ID?
25  *
26  * See: definitions.md for more information
27  */
28 struct MutationPoint {
29     Offset offset;
30     Mutation[] mutations;
31 
32     bool opEquals()(auto ref const S s) const {
33         return offset == s.offset && mutations == s.mutations;
34     }
35 }
36 
37 /// Offset range. It is a  closed->open set.
38 struct Offset {
39     uint begin;
40     uint end;
41 }
42 
43 /// Location in the source code.
44 struct SourceLoc {
45     uint line;
46     uint column;
47 }
48 
49 /// A possible mutation.
50 struct Mutation {
51     /// States what kind of mutations that can be performed on this mutation point.
52     // ONLY ADD NEW ITEMS TO THE END
53     enum Kind : uint {
54         /// the kind is not initialized thus can only ignore the point
55         none,
56         /// Relational operator replacement
57         rorLT,
58         rorLE,
59         rorGT,
60         rorGE,
61         rorEQ,
62         rorNE,
63         /// Logical connector replacement
64         lcrAnd,
65         lcrOr,
66         /// Arithmetic operator replacement
67         aorMul,
68         aorDiv,
69         aorRem,
70         aorAdd,
71         aorSub,
72         aorMulAssign,
73         aorDivAssign,
74         aorRemAssign,
75         aorAddAssign,
76         aorSubAssign,
77         /// Unary operator insert on an lvalue
78         uoiPostInc,
79         uoiPostDec,
80         // these work for rvalue
81         uoiPreInc,
82         uoiPreDec,
83         uoiAddress,
84         uoiIndirection,
85         uoiPositive,
86         uoiNegative,
87         uoiComplement,
88         uoiNegation,
89         uoiSizeof_,
90         /// Absolute value replacement
91         absPos,
92         absNeg,
93         absZero,
94         /// statement deletion
95         stmtDel,
96         /// Conditional Operator Replacement (reduced set)
97         corAnd,
98         corOr,
99         corFalse,
100         corLhs,
101         corRhs,
102         corEQ,
103         corNE,
104         corTrue,
105         /// Relational operator replacement
106         rorTrue,
107         rorFalse,
108         /// Decision/Condition Coverage
109         dccTrue,
110         dccFalse,
111         dccBomb,
112         /// Decision/Condition Requirement
113         dcrCaseDel,
114         /// Relational operator replacement for pointers
115         rorpLT,
116         rorpLE,
117         rorpGT,
118         rorpGE,
119         rorpEQ,
120         rorpNE,
121         /// Logical Operator Replacement Bit-wise (lcrb)
122         lcrbAnd,
123         lcrbOr,
124         lcrbAndAssign,
125         lcrbOrAssign,
126         lcrbLhs,
127         lcrbRhs,
128         /// Logical connector replacement
129         lcrLhs,
130         lcrRhs,
131         lcrTrue,
132         lcrFalse,
133         /// Arithmetic operator replacement
134         aorLhs,
135         aorRhs,
136     }
137 
138     /// The status of a mutant.
139     enum Status : ubyte {
140         /// the mutation isn't tested
141         unknown,
142         /// killed by the test suite
143         killed,
144         /// not killed by the test suite
145         alive,
146         /// the mutation resulted in invalid code that didn't compile
147         killedByCompiler,
148         /// the mutant resulted in the test suite/sut reaching the timeout threshold
149         timeout,
150     }
151 
152     Kind kind;
153     Status status;
154 }
155 
156 /** The checksum that uniquely identify the mutation done in the source code.
157  *
158  * Multiple mutants can end up resulting in the same change in the source code.
159  */
160 struct CodeChecksum {
161     Checksum value;
162     alias value this;
163 }
164 
165 /// The mutant coupled to the source code mutant that is injected.
166 struct CodeMutant {
167     CodeChecksum id;
168     Mutation mut;
169 }
170 
171 /// Deducted type information for expressions on the sides of a relational operator
172 enum OpTypeInfo {
173     none,
174     /// Both sides are floating points
175     floatingPoint,
176     /// Either side is a pointer
177     pointer,
178     /// Both sides are bools
179     boolean,
180     /// lhs and rhs sides are the same enum decl
181     enumLhsRhsIsSame,
182     /// lhs is the minimal representation in the enum type
183     enumLhsIsMin,
184     /// lhs is the maximum representation in the enum type
185     enumLhsIsMax,
186     /// rhs is the minimum representation in the enum type
187     enumRhsIsMin,
188     /// rhs is the maximum representation in the enum type
189     enumRhsIsMax,
190 }
191 
192 /// A test case from the test suite that is executed on mutants.
193 struct TestCase {
194     /// The name of the test case as extracted from the test suite.
195     string name;
196 
197     /// A location identifier intended to be presented to the user.
198     string location;
199 
200     this(string name) @safe pure nothrow @nogc scope {
201         this(name, null);
202     }
203 
204     this(string name, string loc) @safe pure nothrow @nogc scope {
205         this.name = name;
206         this.location = loc;
207     }
208 
209     int opCmp(ref const typeof(this) s) @safe pure nothrow const @nogc scope {
210         if (name < s.name)
211             return -1;
212         else if (name > s.name)
213             return 1;
214         else if (location < s.location)
215             return -1;
216         else if (location > s.location)
217             return 1;
218 
219         return 0;
220     }
221 
222     bool opEquals(ref const typeof(this) s) @safe pure nothrow const @nogc scope {
223         return name == s.name && location == s.location;
224     }
225 
226     size_t toHash() @safe nothrow const {
227         return typeid(string).getHash(&name) + typeid(string).getHash(&location);
228     }
229 
230     string toString() @safe pure const nothrow {
231         import std.array : appender;
232 
233         auto buf = appender!string;
234         toString(buf);
235         return buf.data;
236     }
237 
238     import std.range : isOutputRange;
239 
240     void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, char)) {
241         import std.range : put;
242 
243         if (location.length != 0) {
244             put(w, location);
245             put(w, ":");
246         }
247         put(w, name);
248     }
249 }
250 
251 /// The language a file or mutant is.
252 enum Language {
253     /// the default is assumed to be c++
254     assumeCpp,
255     ///
256     cpp,
257     ///
258     c
259 }
260 
261 /// Test Group criterias.
262 struct TestGroup {
263     import std.regex : Regex, regex;
264 
265     string description;
266     string name;
267 
268     /// What the user configured as regex. Useful when e.g. generating reports
269     /// for a user.
270     string userInput;
271     /// The compiled regex.
272     Regex!char re;
273 
274     this(string name, string desc, string r) {
275         this.name = name;
276         description = desc;
277         userInput = r;
278         re = regex(r);
279     }
280 
281     string toString() @safe pure const {
282         import std.format : format;
283 
284         return format("TestGroup(%s, %s, %s)", name, description, userInput);
285     }
286 
287     import std.range : isOutputRange;
288 
289     void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, char)) {
290         import std.format : formattedWrite;
291 
292         formattedWrite(w, "TestGroup(%s, %s, %s)", name, description, userInput);
293     }
294 }
295 
296 /// Number of times a mutant has been tested.
297 struct MutantTestCount {
298     long value;
299     alias value this;
300 }
301 
302 /** A source code token.
303  *
304  * The source can contain invalid UTF-8 chars therefor every token has to be
305  * validated. Otherwise it isn't possible to generate a report.
306  */
307 struct Token {
308     import std.format : format;
309     import clang.c.Index : CXTokenKind;
310 
311     // TODO: this should be a language agnostic type when more languages are
312     // added in the future.
313     CXTokenKind kind;
314     Offset offset;
315     SourceLoc loc;
316     SourceLoc locEnd;
317     string spelling;
318 
319     this(CXTokenKind kind, Offset offset, SourceLoc loc, SourceLoc locEnd, string spelling) {
320         this.kind = kind;
321         this.offset = offset;
322         this.loc = loc;
323         this.locEnd = locEnd;
324 
325         try {
326             import std.utf : validate;
327 
328             validate(spelling);
329             this.spelling = spelling;
330         } catch (Exception e) {
331             this.spelling = invalidUtf8;
332         }
333     }
334 
335     string toId() @safe const {
336         return format("%s-%s", offset.begin, offset.end);
337     }
338 
339     string toName() @safe const {
340         import std.conv : to;
341 
342         return kind.to!string;
343     }
344 
345     int opCmp(ref const typeof(this) s) const @safe {
346         if (offset.begin > s.offset.begin)
347             return 1;
348         if (offset.begin < s.offset.begin)
349             return -1;
350         if (offset.end > s.offset.end)
351             return 1;
352         if (offset.end < s.offset.end)
353             return -1;
354         return 0;
355     }
356 }
357 
358 @("shall be possible to construct in @safe")
359 @safe unittest {
360     import clang.c.Index : CXTokenKind;
361 
362     auto tok = Token(CXTokenKind.comment, Offset(1, 2), SourceLoc(1, 2), SourceLoc(1, 2), "smurf");
363 }