type: Make an assert()ion independent of the last entry of an enum.
[cparser] / diagnostic.c
1 /*
2  * This file is part of cparser.
3  * Copyright (C) 2012 Matthias Braun <matze@braunis.de>
4  */
5 #include <stdarg.h>
6 #include <stdio.h>
7
8 #include "diagnostic.h"
9 #include "adt/error.h"
10 #include "entity_t.h"
11 #include "separator_t.h"
12 #include "symbol_t.h"
13 #include "token_t.h"
14 #include "ast.h"
15 #include "type.h"
16 #include "warning.h"
17
18 /** Number of occurred errors. */
19 unsigned error_count             = 0;
20 /** Number of occurred warnings. */
21 unsigned warning_count           = 0;
22 bool     show_column             = true;
23 bool     diagnostics_show_option = true;
24
25 static const position_t *curr_pos = NULL;
26
27 /**
28  * prints an additional source position
29  */
30 static void print_position(FILE *out, const position_t *pos)
31 {
32         fprintf(out, "at line %u", pos->lineno);
33         if (show_column)
34                 fprintf(out, ":%u", (unsigned)pos->colno);
35         if (curr_pos == NULL || curr_pos->input_name != pos->input_name)
36                 fprintf(out, " of \"%s\"", pos->input_name);
37 }
38
39 static void fpututf32(utf32 const c, FILE *const out)
40 {
41         if (c < 0x80U) {
42                 fputc(c, out);
43         } else if (c < 0x800) {
44                 fputc(0xC0 | (c >> 6), out);
45                 fputc(0x80 | (c & 0x3F), out);
46         } else if (c < 0x10000) {
47                 fputc(0xE0 | ( c >> 12), out);
48                 fputc(0x80 | ((c >>  6) & 0x3F), out);
49                 fputc(0x80 | ( c        & 0x3F), out);
50         } else {
51                 fputc(0xF0 | ( c >> 18), out);
52                 fputc(0x80 | ((c >> 12) & 0x3F), out);
53                 fputc(0x80 | ((c >>  6) & 0x3F), out);
54                 fputc(0x80 | ( c        & 0x3F), out);
55         }
56 }
57
58 /**
59  * Issue a diagnostic message.
60  */
61 static void diagnosticvf(char const *fmt, va_list ap)
62 {
63         for (char const *f; (f = strchr(fmt, '%')); fmt = f) {
64                 fwrite(fmt, sizeof(*fmt), f - fmt, stderr); // Print till '%'.
65                 ++f; // Skip '%'.
66
67                 bool extended  = false;
68                 bool flag_zero = false;
69                 bool flag_long = false;
70                 for (;; ++f) {
71                         switch (*f) {
72                         case '#': extended  = true; break;
73                         case '0': flag_zero = true; break;
74                         case 'l': flag_long = true; break;
75                         default:  goto done_flags;
76                         }
77                 }
78 done_flags:;
79
80                 int field_width = 0;
81                 if (*f == '*') {
82                         ++f;
83                         field_width = va_arg(ap, int);
84                 }
85
86                 switch (*f++) {
87                 case '%':
88                         fputc('%', stderr);
89                         break;
90
91                 case 'c': {
92                         if (flag_long) {
93                                 const utf32 val = va_arg(ap, utf32);
94                                 fpututf32(val, stderr);
95                         } else {
96                                 const unsigned char val = (unsigned char) va_arg(ap, int);
97                                 fputc(val, stderr);
98                         }
99                         break;
100                 }
101
102                 case 'd': {
103                         const int val = va_arg(ap, int);
104                         fprintf(stderr, "%d", val);
105                         break;
106                 }
107
108                 case 's': {
109                         const char* const str = va_arg(ap, const char*);
110                         fputs(str, stderr);
111                         break;
112                 }
113
114                 case 'S': {
115                         const string_t *str = va_arg(ap, const string_t*);
116                         for (size_t i = 0; i < str->size; ++i) {
117                                 fputc(str->begin[i], stderr);
118                         }
119                         break;
120                 }
121
122                 case 'u': {
123                         const unsigned int val = va_arg(ap, unsigned int);
124                         fprintf(stderr, "%u", val);
125                         break;
126                 }
127
128                 case 'X': {
129                         unsigned int const val  = va_arg(ap, unsigned int);
130                         char  const *const xfmt = flag_zero ? "%0*X" : "%*X";
131                         fprintf(stderr, xfmt, field_width, val);
132                         break;
133                 }
134
135                 case 'Y': {
136                         const symbol_t *const symbol = va_arg(ap, const symbol_t*);
137                         if (symbol == NULL)
138                                 fputs("(null)", stderr);
139                         else
140                                 fputs(symbol->string, stderr);
141                         break;
142                 }
143
144                 case 'E': {
145                         const expression_t* const expr = va_arg(ap, const expression_t*);
146                         print_expression(expr);
147                         break;
148                 }
149
150                 case 'Q': {
151                         const unsigned qualifiers = va_arg(ap, unsigned);
152                         print_type_qualifiers(qualifiers, QUAL_SEP_NONE);
153                         break;
154                 }
155
156                 case 'T': {
157                         const type_t* const type = va_arg(ap, const type_t*);
158                         const symbol_t*     sym  = NULL;
159                         if (extended) {
160                                 sym = va_arg(ap, const symbol_t*);
161                         }
162                         print_type_ext(type, sym, NULL);
163                         break;
164                 }
165
166                 case 'K': {
167                         const token_t* const token = va_arg(ap, const token_t*);
168                         print_token(stderr, token);
169                         break;
170                 }
171
172                 case 'k': {
173                         if (extended) {
174                                 va_list* const toks = va_arg(ap, va_list*);
175                                 separator_t    sep  = { "", va_arg(ap, const char*) };
176                                 for (;;) {
177                                         const token_kind_t tok = (token_kind_t)va_arg(*toks, int);
178                                         if (tok == 0)
179                                                 break;
180                                         fputs(sep_next(&sep), stderr);
181                                         print_token_kind(stderr, tok);
182                                 }
183                         } else {
184                                 const token_kind_t token = (token_kind_t)va_arg(ap, int);
185                                 print_token_kind(stderr, token);
186                         }
187                         break;
188                 }
189
190                 case 'N': {
191                         entity_t const *const ent = va_arg(ap, entity_t const*);
192                         if (extended && is_declaration(ent)) {
193                                 print_type_ext(ent->declaration.type, ent->base.symbol, NULL);
194                         } else {
195                                 char     const *const kind = get_entity_kind_name(ent->kind);
196                                 symbol_t const *const sym  = ent->base.symbol;
197                                 if (sym) {
198                                         fprintf(stderr, "%s %s", kind, sym->string);
199                                 } else {
200                                         fprintf(stderr, "anonymous %s", kind);
201                                 }
202                         }
203                         break;
204                 }
205
206                 case 'P': {
207                         const position_t *pos = va_arg(ap, const position_t *);
208                         print_position(stderr, pos);
209                         break;
210                 }
211
212                 default:
213                         panic("unknown format specifier");
214                 }
215         }
216         fputs(fmt, stderr); // Print rest.
217 }
218
219 void diagnosticf(const char *const fmt, ...)
220 {
221         va_list ap;
222         va_start(ap, fmt);
223         curr_pos = NULL;
224         diagnosticvf(fmt, ap);
225         va_end(ap);
226 }
227
228 static void diagnosticposvf(position_t const *const pos, char const *const kind, char const *const fmt, va_list ap)
229 {
230         FILE *const out = stderr;
231         if (pos) {
232                 fprintf(out, "%s:", pos->input_name);
233                 if (pos->lineno != 0) {
234                         fprintf(out, "%u:", pos->lineno);
235                         if (show_column)
236                                 fprintf(out, "%u:", (unsigned)pos->colno);
237                 }
238                 fputc(' ', out);
239         }
240         fprintf(out, "%s: ", kind);
241         curr_pos = pos;
242         diagnosticvf(fmt, ap);
243 }
244
245 static void errorvf(const position_t *pos,
246                     const char *const fmt, va_list ap)
247 {
248         ++error_count;
249         diagnosticposvf(pos, "error", fmt, ap);
250         fputc('\n', stderr);
251         if (is_warn_on(WARN_FATAL_ERRORS))
252                 exit(EXIT_FAILURE);
253 }
254
255 void errorf(const position_t *pos, const char *const fmt, ...)
256 {
257         va_list ap;
258         va_start(ap, fmt);
259         errorvf(pos, fmt, ap);
260         va_end(ap);
261 }
262
263 void warningf(warning_t const warn, position_t const* pos, char const *const fmt, ...)
264 {
265         if (pos->is_system_header && !is_warn_on(WARN_SYSTEM))
266                 return;
267
268         warning_switch_t const *const s = get_warn_switch(warn);
269         switch ((unsigned) s->state) {
270                         char const* kind;
271                 case WARN_STATE_ON:
272                         if (is_warn_on(WARN_ERROR)) {
273                 case WARN_STATE_ON | WARN_STATE_ERROR:
274                                 ++error_count;
275                                 kind = "error";
276                         } else {
277                 case WARN_STATE_ON | WARN_STATE_NO_ERROR:
278                                 ++warning_count;
279                                 kind = "warning";
280                         }
281                         va_list ap;
282                         va_start(ap, fmt);
283                         diagnosticposvf(pos, kind, fmt, ap);
284                         va_end(ap);
285                         if (diagnostics_show_option)
286                                 fprintf(stderr, " [-W%s]\n", s->name);
287                         else
288                                 fputc('\n', stderr);
289                         break;
290
291                 default:
292                         break;
293         }
294 }
295
296 static void internal_errorvf(const position_t *pos,
297                     const char *const fmt, va_list ap)
298 {
299         diagnosticposvf(pos, "internal error", fmt, ap);
300         fputc('\n', stderr);
301 }
302
303 void internal_errorf(const position_t *pos, const char *const fmt, ...)
304 {
305         va_list ap;
306         va_start(ap, fmt);
307         internal_errorvf(pos, fmt, ap);
308         va_end(ap);
309         abort();
310 }