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