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 }