Line data Source code
1 : /* valareport.vala
2 : *
3 : * Copyright (C) 2006-2010 Jürg Billeter
4 : *
5 : * This library is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU Lesser General Public
7 : * License as published by the Free Software Foundation; either
8 : * version 2.1 of the License, or (at your option) any later version.
9 :
10 : * This library is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : * Lesser General Public License for more details.
14 :
15 : * You should have received a copy of the GNU Lesser General Public
16 : * License along with this library; if not, write to the Free Software
17 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 : *
19 : * Author:
20 : * Jürg Billeter <j@bitron.ch>
21 : */
22 :
23 : using GLib;
24 :
25 :
26 : /**
27 : * Namespace to centralize reporting warnings and errors.
28 : */
29 9228 : public class Vala.Report {
30 : public enum Colored {
31 : AUTO,
32 : NEVER,
33 : ALWAYS
34 : }
35 :
36 : /**
37 : * SGR (Select Graphic Rendition) end tag
38 : */
39 : private const string ANSI_COLOR_END = "\x1b[0m";
40 :
41 : /**
42 : * SGR (Select Graphic Rendition) start tag for source location
43 : */
44 3076 : private string locus_color_start = "";
45 :
46 : /**
47 : * SGR (Select Graphic Rendition) end tag for source location
48 : */
49 1538 : private unowned string locus_color_end = "";
50 :
51 : /**
52 : * SGR (Select Graphic Rendition) start tag for warning titles
53 : */
54 3076 : private string warning_color_start = "";
55 :
56 : /**
57 : * SGR (Select Graphic Rendition) end tag for warning titles
58 : */
59 1538 : private unowned string warning_color_end = "";
60 :
61 : /**
62 : * SGR (Select Graphic Rendition) start tag for error titles
63 : */
64 3076 : private string error_color_start = "";
65 :
66 : /**
67 : * SGR (Select Graphic Rendition) end tag for error titles
68 : */
69 1538 : private unowned string error_color_end = "";
70 :
71 : /**
72 : * SGR (Select Graphic Rendition) start tag for note titles
73 : */
74 3076 : private string note_color_start = "";
75 :
76 : /**
77 : * SGR (Select Graphic Rendition) end tag for note titles
78 : */
79 1538 : private unowned string note_color_end = "";
80 :
81 : /**
82 : * SGR (Select Graphic Rendition) start tag for caret line (^^^)
83 : */
84 3076 : private string caret_color_start = "";
85 :
86 : /**
87 : * SGR (Select Graphic Rendition) end tag for caret line (^^^)
88 : */
89 1538 : private unowned string caret_color_end = "";
90 :
91 : /**
92 : * SGR (Select Graphic Rendition) start tag for quotes line ('...', `...`, `...')
93 : */
94 3076 : private string quote_color_start = "";
95 :
96 : /**
97 : * SGR (Select Graphic Rendition) end tag for quotes line ('...', `...`, `...')
98 : */
99 1538 : private unowned string quote_color_end = "";
100 :
101 :
102 : protected int warnings;
103 : protected int errors;
104 :
105 : private bool verbose_errors;
106 :
107 1538 : public bool enable_warnings { get; set; default = true; }
108 :
109 : static GLib.Regex val_regex;
110 :
111 : /**
112 : * Set all colors by string
113 : *
114 : * {{{
115 : * "error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01"
116 : * }}}
117 : */
118 1467 : public bool set_colors (string str, Report.Colored colored_output = Report.Colored.AUTO) {
119 0 : try {
120 1467 : if (val_regex == null)
121 1467 : val_regex = new Regex ("^\\s*[0-9]+(;[0-9]*)*\\s*$");
122 : } catch (RegexError e) {
123 1467 : assert_not_reached ();
124 : }
125 :
126 1467 : string error_color = null;
127 1467 : string warning_color = null;
128 1467 : string note_color = null;
129 1467 : string caret_color = null;
130 1467 : string locus_color = null;
131 1467 : string quote_color = null;
132 :
133 1467 : string[] fragments = str.split (":");
134 19071 : foreach (unowned string fragment in fragments) {
135 8802 : string[] eq = fragment.split ("=", 2);
136 8802 : if (eq.length != 2) {
137 0 : return false;
138 : }
139 :
140 8802 : if (!val_regex.match (eq[1])) {
141 0 : return false;
142 : }
143 :
144 :
145 8802 : unowned string checked_value = eq[1]._strip ();
146 24939 : switch (eq[0]._strip ()) {
147 : case "error":
148 1467 : error_color = checked_value;
149 1467 : break;
150 :
151 : case "warning":
152 1467 : warning_color = checked_value;
153 1467 : break;
154 :
155 : case "note":
156 1467 : note_color = checked_value;
157 1467 : break;
158 :
159 : case "caret":
160 1467 : caret_color = checked_value;
161 1467 : break;
162 :
163 : case "locus":
164 1467 : locus_color = checked_value;
165 1467 : break;
166 :
167 : case "quote":
168 1467 : quote_color = checked_value;
169 1467 : break;
170 :
171 : default:
172 0 : return false;
173 : }
174 : }
175 :
176 1467 : if (colored_output == Report.Colored.ALWAYS || (colored_output == Report.Colored.AUTO && Log.writer_supports_color (stderr.fileno ()))) {
177 0 : if (error_color != null) {
178 0 : this.error_color_start = "\x1b[0" + error_color + "m";
179 0 : this.error_color_end = ANSI_COLOR_END;
180 : }
181 :
182 0 : if (warning_color != null) {
183 0 : this.warning_color_start = "\x1b[0" + warning_color + "m";
184 0 : this.warning_color_end = ANSI_COLOR_END;
185 : }
186 :
187 0 : if (note_color != null) {
188 0 : this.note_color_start = "\x1b[0" + note_color + "m";
189 0 : this.note_color_end = ANSI_COLOR_END;
190 : }
191 :
192 0 : if (caret_color != null) {
193 0 : this.caret_color_start = "\x1b[0" + caret_color + "m";
194 0 : this.caret_color_end = ANSI_COLOR_END;
195 : }
196 :
197 0 : if (locus_color != null) {
198 0 : this.locus_color_start = "\x1b[0" + locus_color + "m";
199 0 : this.locus_color_end = ANSI_COLOR_END;
200 : }
201 :
202 0 : if (quote_color != null) {
203 0 : this.quote_color_start = "\x1b[0" + quote_color + "m";
204 0 : this.quote_color_end = ANSI_COLOR_END;
205 : }
206 : }
207 2934 : return true;
208 : }
209 :
210 : /**
211 : * Set the error verbosity.
212 : */
213 1538 : public void set_verbose_errors (bool verbose) {
214 1538 : verbose_errors = verbose;
215 : }
216 :
217 : /**
218 : * Returns the total number of warnings reported.
219 : */
220 1542 : public int get_warnings () {
221 1542 : return warnings;
222 : }
223 :
224 : /**
225 : * Returns the total number of errors reported.
226 : */
227 16748 : public int get_errors () {
228 16748 : return errors;
229 : }
230 :
231 : /**
232 : * Pretty-print the actual line of offending code if possible.
233 : */
234 748 : private void report_source (SourceReference source) {
235 1514 : for (int idx = source.begin.line; idx <= source.end.line; idx++) {
236 767 : string? offending_line = source.file.get_source_line (idx);
237 767 : if (offending_line == null) {
238 1 : break;
239 : }
240 766 : printerr ("%5d | %s\n", idx, offending_line);
241 766 : printerr (" | ");
242 766 : stderr.puts (caret_color_start);
243 19776 : for (int jdx = 0; jdx < offending_line.length; jdx++) {
244 19010 : if (offending_line[jdx] == '\t') {
245 656 : stderr.putc ('\t');
246 656 : continue;
247 : }
248 18354 : bool caret = false;
249 18354 : unowned SourceLocation begin = source.begin;
250 18354 : unowned SourceLocation end = source.end;
251 18354 : if (begin.line == idx && end.line == idx) {
252 17865 : if (begin.column <= jdx + 1 <= end.column) {
253 : caret = true;
254 : }
255 489 : } else if (begin.line == idx && begin.column <= jdx + 1) {
256 : caret = true;
257 341 : } else if (begin.line < idx < end.line) {
258 : caret = true;
259 145 : } else if (end.line == idx && end.column >= jdx + 1) {
260 : caret = true;
261 : }
262 81 : if (caret) {
263 8518 : if (begin.line == idx && begin.column == jdx + 1) {
264 739 : stderr.putc ('^');
265 : } else {
266 7779 : stderr.putc ('~');
267 : }
268 : } else {
269 9836 : stderr.putc (' ');
270 : }
271 : }
272 766 : stderr.puts (caret_color_end);
273 766 : stderr.putc ('\n');
274 : }
275 : }
276 :
277 749 : private void print_highlighted_message (string message) {
278 : int start = 0;
279 : int cur = 0;
280 :
281 34809 : while (message[cur] != '\0') {
282 34060 : if (message[cur] == '\'' || message[cur] == '`') {
283 679 : unowned string end_chars = (message[cur] == '`')? "`'" : "'";
284 679 : stderr.puts (message.substring (start, cur - start));
285 679 : start = cur;
286 679 : cur++;
287 :
288 5726 : while (message[cur] != '\0' && end_chars.index_of_char (message[cur]) < 0) {
289 5047 : cur++;
290 : }
291 679 : if (message[cur] == '\0') {
292 3 : stderr.puts (message.substring (start, cur - start));
293 3 : start = cur;
294 : } else {
295 676 : cur++;
296 676 : printerr ("%s%s%s", quote_color_start, message.substring (start, cur - start), quote_color_end);
297 676 : start = cur;
298 : }
299 : } else {
300 33381 : cur++;
301 : }
302 : }
303 :
304 749 : stderr.puts (message.offset (start));
305 : }
306 :
307 749 : private void print_message (SourceReference? source, string type, string type_color_start, string type_color_end, string message, bool do_report_source) {
308 749 : if (source != null) {
309 748 : printerr ("%s%s:%s ", locus_color_start, source.to_string (), locus_color_end);
310 : }
311 :
312 749 : printerr ("%s%s:%s ", type_color_start, type, type_color_end);
313 :
314 : // highlight '', `', ``
315 749 : print_highlighted_message (message);
316 749 : stderr.putc ('\n');
317 :
318 749 : if (do_report_source && source != null) {
319 748 : report_source (source);
320 : }
321 : }
322 :
323 : /**
324 : * Reports the specified message as note.
325 : *
326 : * @param source reference to source code
327 : * @param message note message
328 : */
329 22 : public virtual void note (SourceReference? source, string message) {
330 11 : if (!enable_warnings) {
331 : return;
332 : }
333 :
334 1 : print_message (source, "note", note_color_start, note_color_end, message, verbose_errors);
335 : }
336 :
337 : /**
338 : * Reports the specified message as deprecation warning.
339 : *
340 : * @param source reference to source code
341 : * @param message warning message
342 : */
343 112 : public virtual void depr (SourceReference? source, string message) {
344 56 : if (!enable_warnings) {
345 : return;
346 : }
347 :
348 20 : warnings++;
349 :
350 20 : print_message (source, "warning", warning_color_start, warning_color_end, message, verbose_errors);
351 : }
352 :
353 : /**
354 : * Reports the specified message as warning.
355 : *
356 : * @param source reference to source code
357 : * @param message warning message
358 : */
359 4282 : public virtual void warn (SourceReference? source, string message) {
360 2141 : if (!enable_warnings) {
361 : return;
362 : }
363 :
364 22 : warnings++;
365 :
366 22 : print_message (source, "warning", warning_color_start, warning_color_end, message, verbose_errors);
367 : }
368 :
369 : /**
370 : * Reports the specified message as error.
371 : *
372 : * @param source reference to source code
373 : * @param message error message
374 : */
375 1412 : public virtual void err (SourceReference? source, string message) {
376 706 : errors++;
377 :
378 706 : print_message (source, "error", error_color_start, error_color_end, message, verbose_errors);
379 : }
380 :
381 : /* Convenience methods calling warn and err on correct instance */
382 : [PrintfFormat]
383 11 : public static void notice (SourceReference? source, string msg_format, ...) {
384 11 : CodeContext.get ().report.note (source, msg_format.vprintf (va_list ()));
385 : }
386 : [PrintfFormat]
387 56 : public static void deprecated (SourceReference? source, string msg_format, ...) {
388 56 : CodeContext.get ().report.depr (source, msg_format.vprintf (va_list ()));
389 : }
390 : [PrintfFormat]
391 0 : public static void experimental (SourceReference? source, string msg_format, ...) {
392 0 : CodeContext.get ().report.depr (source, msg_format.vprintf (va_list ()));
393 : }
394 : [PrintfFormat]
395 2141 : public static void warning (SourceReference? source, string msg_format, ...) {
396 2141 : CodeContext.get ().report.warn (source, msg_format.vprintf (va_list ()));
397 : }
398 : [PrintfFormat]
399 706 : public static void error (SourceReference? source, string msg_format, ...) {
400 706 : CodeContext.get ().report.err (source, msg_format.vprintf (va_list ()));
401 : }
402 : }
|