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 }