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