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