1 /** 2 Copyright: Copyright (c) 2020, 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.analyze.pass_coverage; 11 12 import logger = std.experimental.logger; 13 import std.algorithm : among, map, sort, filter; 14 import std.array : appender, empty, array, Appender; 15 import std.exception : collectException; 16 import std.format : formattedWrite; 17 import std.range : retro, ElementType; 18 import std.typecons : tuple, Tuple, scoped; 19 20 import dextool.type : AbsolutePath, Path; 21 22 import dextool.plugin.mutate.backend.analyze.ast; 23 import dextool.plugin.mutate.backend.analyze.utility; 24 import dextool.plugin.mutate.backend.interface_ : ValidateLoc, FilesysIO; 25 import dextool.plugin.mutate.backend.type : Offset, Mutation, SourceLocRange, Token; 26 27 @safe: 28 29 CoverageResult toCoverage(Ast* ast, FilesysIO fio, ValidateLoc vloc) { 30 scope visitor = new CoverageVisitor(ast, fio, vloc); 31 () @trusted { ast.accept(visitor); }(); 32 return visitor.result; 33 } 34 35 /// Find all places to instrument. 36 class CoverageResult { 37 Interval[][AbsolutePath] points; 38 39 private { 40 FilesysIO fio; 41 ValidateLoc vloc; 42 } 43 44 this(FilesysIO fio, ValidateLoc vloc) { 45 this.fio = fio; 46 this.vloc = vloc; 47 } 48 49 override string toString() @safe { 50 import std.format : formattedWrite; 51 import std.range : put; 52 53 auto w = appender!string; 54 55 put(w, "CoverageResult\n"); 56 foreach (f; points.byKeyValue) { 57 formattedWrite(w, "%s\n", f.key); 58 foreach (p; f.value) { 59 formattedWrite(w, " %s\n", p); 60 } 61 } 62 63 return w.data; 64 } 65 66 private void put(const AbsolutePath p) { 67 if (!vloc.shouldMutate(p) || p in points) 68 return; 69 points[p] = Interval[].init; 70 } 71 72 private void put(const AbsolutePath p, const Interval i) { 73 if (auto v = p in points) { 74 *v ~= i; 75 } 76 } 77 } 78 79 private: 80 81 class CoverageVisitor : DepthFirstVisitor { 82 Ast* ast; 83 CoverageResult result; 84 85 private { 86 uint depth; 87 Stack!(Kind) visited; 88 } 89 90 override void visitPush(Node n) { 91 visited.put(n.kind, ++depth); 92 } 93 94 override void visitPop(Node n) { 95 visited.pop; 96 --depth; 97 } 98 99 alias visit = DepthFirstVisitor.visit; 100 101 this(Ast* ast, FilesysIO fio, ValidateLoc vloc) { 102 this.ast = ast; 103 result = new CoverageResult(fio, vloc); 104 105 // by adding the locations here the rest of the visitor do not have to 106 // be concerned about adding files. 107 foreach (ref l; ast.locs.byValue) { 108 result.put(l.file); 109 } 110 } 111 112 override void visit(Function n) { 113 if (n.blacklist || n.covBlacklist) 114 return; 115 accept(n, this); 116 } 117 118 override void visit(Block n) { 119 if (visited[$ - 1].data != Kind.Function || n.covBlacklist) 120 return; 121 122 const l = ast.location(n); 123 // skip empty bodies. it is both not needed to instrument them because 124 // they do not contain any mutants and there is a off by one bug that 125 // sometimes occur wherein the instrumented function call is injected 126 // before the braket, {. 127 if (l.interval.begin == l.interval.end) { 128 return; 129 } 130 result.put(l.file, l.interval); 131 } 132 }