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