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 }