1 /**
2 Copyright: Copyright (c) 2018, 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.report.compiler;
11 
12 import logger = std.experimental.logger;
13 import std.exception : collectException;
14 import std.path : buildPath;
15 
16 import dextool.type;
17 
18 import dextool.plugin.mutate.backend.database : Database, IterateMutantRow;
19 import dextool.plugin.mutate.backend.generate_mutant : MakeMutationTextResult, makeMutationText;
20 import dextool.plugin.mutate.backend.interface_ : FilesysIO;
21 import dextool.plugin.mutate.backend.mutation_type : toInternal, toUser;
22 import dextool.plugin.mutate.backend.report.type : SimpleWriter, ReportEvent;
23 import dextool.plugin.mutate.backend.report.utility : window, windowSize;
24 import dextool.plugin.mutate.backend.type : Mutation;
25 import dextool.plugin.mutate.backend.utility : Profile;
26 import dextool.plugin.mutate.config : ConfigReport;
27 import dextool.plugin.mutate.type : MutationKind, ReportSection;
28 
29 @safe:
30 
31 void report(ref Database db, const MutationKind[] userKinds, const ConfigReport conf, FilesysIO fio) {
32     import dextool.plugin.mutate.backend.utility;
33 
34     const kinds = toInternal(userKinds);
35 
36     auto a = new ReportCompiler(kinds, conf.reportSection, fio);
37 
38     a.mutationKindEvent(userKinds);
39 
40     {
41         auto profile = Profile("iterate mutants for report");
42         void iter(const ref IterateMutantRow row) {
43             a.locationEvent(db, row);
44         }
45 
46         db.iterateMutants(kinds, &iter);
47     }
48 
49     auto profile = Profile("post process report");
50 }
51 
52 /** Report mutations as gcc would do for compilation warnings with fixit hints.
53  *
54  * #SPC-report_for_tool_integration
55  */
56 final class ReportCompiler {
57     import std.algorithm : each;
58     import std.conv : to;
59     import std.format : format;
60     import dextool.plugin.mutate.backend.utility;
61     import my.set;
62 
63     const Mutation.Kind[] kinds;
64     Set!ReportSection sections;
65     FilesysIO fio;
66 
67     CompilerConsole!SimpleWriter compiler;
68 
69     this(const Mutation.Kind[] kinds, const ReportSection[] sections, FilesysIO fio) {
70         this.kinds = kinds;
71         this.fio = fio;
72         this.sections = sections.toSet;
73     }
74 
75     void mutationKindEvent(const MutationKind[]) {
76         compiler = CompilerConsole!SimpleWriter(delegate(const(char)[] s) @trusted {
77             import std.stdio : stderr, write;
78 
79             stderr.write(s);
80         });
81     }
82 
83     void locationEvent(ref Database db, const ref IterateMutantRow r) @trusted {
84         import dextool.plugin.mutate.backend.generate_mutant : makeMutation;
85 
86         void report() {
87             AbsolutePath abs_path;
88             MakeMutationTextResult mut_txt;
89             try {
90                 abs_path = AbsolutePath(buildPath(fio.getOutputDir, r.file.Path));
91                 mut_txt = makeMutationText(fio.makeInput(abs_path),
92                         r.mutationPoint.offset, r.mutation.kind, r.lang);
93             } catch (Exception e) {
94                 logger.warning(e.msg);
95             }
96 
97             // dfmt off
98             auto b = compiler.build
99                 .file(abs_path)
100                 .start(r.sloc)
101                 .end(r.slocEnd)
102                 .begin("%s: replace '%s' with '%s'", r.mutation.kind.toUser,
103                        window(mut_txt.original, windowSize),
104                        window(mut_txt.mutation, windowSize))
105                 .note("status:%s id:%s", r.mutation.status, r.id.get);
106 
107             if (mut_txt.original.length > windowSize)
108                 b = b.note("replace '%s'", mut_txt.original);
109             if (mut_txt.mutation.length > windowSize)
110                 b = b.note("with '%s'", mut_txt.mutation);
111 
112             b.fixit(mut_txt.mutation).end;
113             // dfmt on
114         }
115 
116         try {
117             if (r.mutation.status == Mutation.Status.alive
118                     && ReportSection.alive in sections || ReportSection.all_mut in sections) {
119                 report();
120             }
121         } catch (Exception e) {
122             logger.trace(e.msg).collectException;
123         }
124     }
125 }
126 
127 private:
128 
129 struct CompilerConsole(Writer) {
130     import std.ascii : newline;
131     import std.format : formattedWrite, format;
132     import std.range : put;
133 
134     private Writer w;
135 
136     this(Writer w) {
137         this.w = w;
138     }
139 
140     auto build() {
141         return CompilerMsgBuilder!Writer(w);
142     }
143 }
144 
145 /** Build a compiler msg that follows how GCC would print to the console.
146  *
147  * Note that it should write to stderr.
148  */
149 struct CompilerMsgBuilder(Writer) {
150     import std.ascii : newline;
151     import std.format : formattedWrite;
152     import std.range : put;
153     import dextool.plugin.mutate.backend.type : SourceLoc;
154 
155     private {
156         Writer w;
157         string file_;
158         SourceLoc start_;
159         SourceLoc end_;
160     }
161 
162     this(Writer w) {
163         this.w = w;
164     }
165 
166     auto file(string a) {
167         file_ = a;
168         return this;
169     }
170 
171     auto start(SourceLoc a) {
172         start_ = a;
173         return this;
174     }
175 
176     auto end(SourceLoc a) {
177         end_ = a;
178         return this;
179     }
180 
181     auto begin(ARGS...)(auto ref ARGS args) @trusted {
182         // for now the severity is hard coded to warning because nothing else
183         // needs to be supported.
184         formattedWrite(w, "%s:%s:%s: warning: ", file_, start_.line, start_.column);
185         formattedWrite(w, args);
186         put(w, newline);
187         return this;
188     }
189 
190     auto note(ARGS...)(auto ref ARGS args) @trusted {
191         formattedWrite(w, "%s:%s:%s: note: ", file_, start_.line, start_.column);
192         formattedWrite(w, args);
193         put(w, newline);
194         return this;
195     }
196 
197     auto fixit(const(char)[] mutation) @trusted {
198         // Example of a fixit hint from gcc:
199         // fix-it:"foo.cpp":{5:12-5:17}:"argc"
200         // the second value in the location is bytes (starting from 1) from the
201         // start of the line.
202         formattedWrite(w, `fix-it:"%s":{%s:%s-%s:%s}:"%s"`, file_, start_.line,
203                 start_.column, end_.line, end_.column, mutation);
204         put(w, newline);
205         return this;
206     }
207 
208     void end() {
209     }
210 }