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 }