1 /*
2 * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 /** \file
19 * \brief Error handling and reporting module.
20 */
21 #include "error.h"
22 #include "global.h"
23 #include "version.h"
24 #include "main.h"
25 #include "symtab.h"
26 #ifdef FE90
27 #include "ast.h"
28 #include "scan.h"
29 #include <string.h>
30 #endif
31 #include <stdarg.h>
32
33 /* second time -- include error text definitions from errmsg utility */
34 #define ERRMSG_GET_ERRTXT_TABLE 1
35 #include "errmsgdf.h"
36 #undef ERRMSG_GET_ERRTXT_TABLE
37
38 static int ndiags[ERR_SEVERITY_SIZE];
39 static enum error_severity maxfilsev; /* max severity for entire source file */
40 static int totaldiags = 0; /* total number of messages for this source
41 * file */
42 static int emit_errmsg = 1;
43
44 /** \brief Expand error message template: replace '$' with text of the other
45 * two operands
46 *
47 * \param intxt error message template
48 * \param op1 first operand for substitution
49 * \param op2 second operand for substitution
50 * \return pointer to result
51 */
52 static char *
errfill(const char * intxt,const char * op1,const char * op2)53 errfill(const char *intxt, const char *op1, const char *op2)
54 {
55 static char outtxt[200]; /* holds result string */
56 char *p; /* points into outtxt */
57 const char *op;
58
59 /* calculate length of txt and operands to avoid overflow */
60 int intxt_len;
61 int op_len, op1_len, op2_len;
62 int op_adj_len, op2_adj_len;
63 int buf_len, tot_len, len_left;
64
65 buf_len = 200;
66 op_adj_len = op2_adj_len = 0;
67 intxt_len = (intxt != NULL) ? strlen(intxt) : 0;
68 op1_len = op_len = (op1 != NULL) ? strlen(op1) : 0;
69 op2_len = (op2 != NULL) ? strlen(op2) : 0;
70 tot_len = intxt_len + op1_len + op2_len;
71 len_left = buf_len;
72 if (tot_len > buf_len) {
73 len_left = buf_len - intxt_len;
74 if (!op2_len)
75 op_adj_len = len_left;
76 else {
77 if (op_len > len_left / 2) {
78 if (op2_len > len_left / 2)
79 op_adj_len = op2_adj_len = len_left / 2;
80 else
81 op_adj_len = len_left - op2_len;
82 } else
83 op2_adj_len = len_left - op1_len;
84 }
85 }
86
87 p = outtxt;
88 op = op1;
89
90 while ((*p = *intxt++) != 0) {
91 if (*p++ == '$') {
92 p--;
93 if (op == 0)
94 op = "";
95 if (tot_len > buf_len) {
96 if (op_adj_len && (op_len != op_adj_len)) {
97 strncpy(p, op, op_adj_len - 3);
98 p += op_adj_len - 3;
99 strcpy(p, "...");
100 p += 3;
101 } else {
102 strncpy(p, op, op_len);
103 p += op_len;
104 }
105 op_len = op2_len;
106 op_adj_len = op2_adj_len;
107 } else {
108 strcpy(p, op);
109 p += strlen(op);
110 }
111 op = op2;
112 }
113 }
114 return outtxt;
115 }
116
117 static void
display_error(error_code_t ecode,enum error_severity sev,int eline,const char * op1,const char * op2,int col,const char * srcFile)118 display_error(error_code_t ecode, enum error_severity sev, int eline,
119 const char *op1, const char *op2, int col, const char *srcFile)
120 {
121 static char sevlett[5] = {'X', 'I', 'W', 'S', 'F'};
122 char *formatstr;
123 char buff[400];
124 int lastmsg;
125 char *msgstr;
126
127 if (sev < ERR_Informational || sev > ERR_Fatal)
128 sev = ERR_Fatal;
129 /* check if informationals and warnings are inhibited */
130 if (gbl.nowarn && sev <= ERR_Warning)
131 return;
132 /* don't count informs if -inform warn */
133 if (sev > ERR_Informational || sev >= flg.inform)
134 ndiags[sev]++;
135
136 if ((sev > ERR_Informational || sev >= flg.inform) && sev > gbl.maxsev) {
137 gbl.maxsev = sev;
138 if (sev > maxfilsev)
139 maxfilsev = sev;
140 }
141
142 if (sev >= flg.inform) {
143 if (gbl.curr_file != NULL || srcFile != NULL) {
144 if (eline) {
145 if (col > 0)
146 formatstr = "%s-%c-%04d-%s (%s: %d.%d)";
147 else
148 formatstr = "%s-%c-%04d-%s (%s: %d)";
149 } else
150 formatstr = "%s-%c-%04d-%s (%s)";
151 } else
152 formatstr = "%s-%c-%04d-%s";
153
154 lastmsg = sizeof(errtxt) / sizeof(char *);
155 if (ecode < lastmsg) {
156 msgstr = errtxt[ecode];
157 } else {
158 msgstr = "Unknown error code";
159 }
160
161 if (!XBIT(0, 0x40000000) && col <= 0 && srcFile == NULL)
162 snprintf(&buff[1], sizeof(buff) - 1, formatstr, version.lang,
163 sevlett[sev], ecode, errfill(msgstr, op1, op2), gbl.curr_file,
164 eline);
165 else {
166 static char *sevtext[5] = {"X", "info", "warning", "error", "error"};
167 if (col > 0 && (srcFile != NULL || gbl.curr_file != NULL)) {
168 snprintf(&buff[1], sizeof(buff) - 1, "\n%s:%d:%d: %s %c%04d: %s",
169 (srcFile != NULL) ? srcFile : gbl.curr_file, eline, col,
170 sevtext[sev], sevlett[sev], ecode, errfill(msgstr, op1, op2));
171 } else if (srcFile != NULL) {
172 snprintf(&buff[1], sizeof(buff) - 1, "\n%s:%d: %s %c%04d: %s", srcFile,
173 eline, sevtext[sev], sevlett[sev], ecode,
174 errfill(msgstr, op1, op2));
175 } else if (gbl.curr_file != NULL) {
176 snprintf(&buff[1], sizeof(buff) - 1, "%s(%d) : %s %c%04d : %s",
177 gbl.curr_file, eline, sevtext[sev], sevlett[sev], ecode,
178 errfill(msgstr, op1, op2));
179 } else
180 snprintf(&buff[1], sizeof(buff) - 1, "%s : %s %c%04d : %s", "",
181 sevtext[sev], sevlett[sev], ecode, errfill(msgstr, op1, op2));
182 }
183 if (emit_errmsg)
184 fprintf(stderr, "%s\n", &buff[1]);
185 #if DEBUG
186 if (DBGBIT(0, 2))
187 fprintf(gbl.dbgfil, "%s\n", &buff[1]);
188 #endif
189 if (flg.list || flg.code || flg.xref) {
190 if (flg.dbg[14]) {
191 buff[0] = '#'; /* make sure listing is assembleable */
192 list_line(buff);
193 } else {
194 list_line(&buff[1]);
195 }
196 }
197 }
198
199 if (sev == ERR_Fatal) {
200 #ifdef FE90
201 #if DEBUG
202 if (ecode == 7 && DBGBIT(0, 512))
203 dump_stg_stat("- subprogram too large");
204 #endif
205 #endif
206 if (col <= 0 || (srcFile == NULL && gbl.curr_file == NULL)) {
207 finish();
208 }
209 }
210
211 if (sev >= ERR_Severe)
212 totaldiags++;
213
214 if (totaldiags >= flg.errorlimit && !DBGBIT(0, 64))
215 errfatal(F_0008_Error_limit_exceeded);
216 }
217
218 void
errini(void)219 errini(void)
220 {
221 ndiags[1] = ndiags[2] = ndiags[3] = ndiags[4] = gbl.maxsev = totaldiags = 0;
222 }
223
224 void
errversion(void)225 errversion(void)
226 {
227 fprintf(stderr, "%s/%s %s %s%s%s\n", version.lang, version.target,
228 version.host, version.vsn, version.product, version.bld);
229 fprintf(stderr, "%s\n", version.copyright);
230 }
231
232 void
error(error_code_t ecode,enum error_severity sev,int eline,const char * op1,const char * op2)233 error(error_code_t ecode, enum error_severity sev, int eline, const char *op1,
234 const char *op2)
235 {
236 display_error(ecode, sev, eline, op1, op2, 0, NULL);
237 }
238
239 void
errlabel(error_code_t ecode,enum error_severity sev,int eline,char * nm,char * op2)240 errlabel(error_code_t ecode, enum error_severity sev, int eline, char *nm,
241 char *op2)
242 {
243 nm += 2; /* skip past .L */
244 while (*nm == '0')
245 nm++; /* skip over leading 0's */
246 if (*nm == 0)
247 nm--;
248 error(ecode, sev, eline, nm, op2);
249 }
250
251 /* Do printf-style formatting of the message by: compute the size of the
252 * required buffer, allocate it, sprintf into it, then free it. */
253 void
interrf(enum error_severity sev,const char * fmt,...)254 interrf(enum error_severity sev, const char *fmt, ...)
255 {
256 size_t size;
257 char *buffer;
258 va_list ap;
259
260 #if !DEBUG
261 if (sev == ERR_Informational)
262 return;
263 #endif
264 va_start(ap, fmt);
265 size = vsnprintf(NULL, 0, fmt, ap);
266 va_end(ap);
267 NEW(buffer, char, size + 1);
268 va_start(ap, fmt);
269 vsprintf(buffer, fmt, ap);
270 va_end(ap);
271 error(V_0000_Internal_compiler_error_OP1_OP2, sev, gbl.lineno, buffer, 0);
272 FREE(buffer);
273 }
274
275 void
interr(const char * txt,int val,enum error_severity sev)276 interr(const char *txt, int val, enum error_severity sev)
277 {
278 interrf(sev, "%s %7d", txt, val);
279 }
280
281 #if DEBUG
282 void
dassert_err(const char * filename,int line,const char * expr,const char * txt)283 dassert_err(const char *filename, int line, const char *expr, const char *txt)
284 {
285 /* Since we reach here only in DEBUG mode, there's no point in
286 being clever about creating a single string to pass to interr.
287 Just get the information to the compiler developer via stderr. */
288 (void)fprintf(stderr, "%s:%d: DEBUG_ASSERT %s failed\n", filename, line,
289 expr);
290 interr(txt, 0, error_max_severity());
291 }
292
293 void
asrt_failed(const char * filename,int line)294 asrt_failed(const char *filename, int line)
295 {
296 fprintf(stderr, "asrt failed. line %d, file %s\n", line, filename);
297 /* Call interr so that we have a common place to set a breakpoint when
298 running under a debugger. */
299 interr("asrt failed", 0, ERR_Warning);
300 }
301 #endif
302
303 char *
errnum(int num)304 errnum(int num)
305 {
306 static char n[20];
307 sprintf(n, "%d", num);
308 return n;
309 } /* errnum */
310
311 void
errinfo(error_code_t ecode)312 errinfo(error_code_t ecode)
313 {
314 error(ecode, ERR_Informational, gbl.lineno, CNULL, CNULL);
315 }
316
317 void
errwarn(error_code_t ecode)318 errwarn(error_code_t ecode)
319 {
320 error(ecode, ERR_Warning, gbl.lineno, CNULL, CNULL);
321 }
322
323 void
errsev(error_code_t ecode)324 errsev(error_code_t ecode)
325 {
326 error(ecode, ERR_Severe, gbl.lineno, CNULL, CNULL);
327 }
328
329 void
errfatal(error_code_t ecode)330 errfatal(error_code_t ecode)
331 {
332 error(ecode, ERR_Fatal, gbl.lineno, CNULL, CNULL);
333 }
334
335 int
summary(bool final,int ipafollows)336 summary(bool final, int ipafollows)
337 {
338 static char *t[5] = {
339 "%s/%s %s %s%s%s: compilation successful\n",
340 "%s/%s %s %s%s%s: compilation completed with informational messages\n",
341 "%s/%s %s %s%s%s: compilation completed with warnings\n",
342 "%s/%s %s %s%s%s: compilation completed with severe errors\n",
343 "%s/%s %s %s%s%s: compilation aborted\n"};
344 static bool empty_file = true;
345
346 if (!final) {
347 if (!flg.terse || gbl.maxsev > 1)
348 fprintf(stderr,
349 "%3d inform, %3d warnings, %3d severes, %1d fatal for %s\n",
350 ndiags[1], ndiags[2], ndiags[3], ndiags[4], SYMNAME(gbl.currsub));
351 empty_file = false;
352 } else if (!empty_file || !ipafollows) {
353 if (empty_file && maxfilsev < 3)
354 errwarn(S_0006_Input_file_empty);
355 if (!flg.terse || gbl.maxsev > 1)
356 fprintf(stderr, t[maxfilsev], version.lang, version.target, version.host,
357 version.vsn, version.product, version.bld);
358 }
359 return maxfilsev;
360 }
361
362 void
erremit(int x)363 erremit(int x)
364 {
365 emit_errmsg = x;
366 }
367
368 void
fperror(int errcode)369 fperror(int errcode)
370 {
371 /* floating point error codes */
372 static struct {
373 int ovf;
374 int unf;
375 int invop;
376 } lineno = {-1, -1, -1};
377
378 gbl.fperror_status = errcode;
379 if (gbl.nofperror)
380 return;
381 switch (errcode) {
382 case FPE_NOERR:
383 break;
384 case FPE_FPOVF: /* floating point overflow */
385 if (lineno.ovf == gbl.lineno)
386 break;
387 lineno.ovf = gbl.lineno;
388 errwarn((enum error_code)129); // FIXME: different enum names per target
389 break;
390 case FPE_FPUNF: /* floating point underflow */
391 if (lineno.unf == gbl.lineno)
392 break;
393 lineno.unf = gbl.lineno;
394 errwarn((enum error_code)130); // FIXME: different enum names per target
395 break;
396 case FPE_INVOP: /* invalid operand */
397 if (lineno.invop == gbl.lineno)
398 break;
399 lineno.invop = gbl.lineno;
400 errwarn((enum error_code)132); // FIXME: different enum names per target
401 break;
402 default:
403 interr("invalid floating point error code", (int)errcode, ERR_Severe);
404 }
405 }
406
407 enum error_severity
error_max_severity(void)408 error_max_severity(void)
409 {
410 return maxfilsev;
411 }
412
413 #ifdef FE90
414
415 /** \brief Returns the last substring of a string.
416 *
417 * This function is used by callers of errWithSrc() below to obtain the
418 * last substring of a string. In some cases the operand of an error message
419 * has extra words. This is the case with syntax errors where we call the
420 * function prettytoken() prior to generating the syntax error. The offending
421 * token is typically the last substring. Each substring is separated by one
422 * space.
423 *
424 * We use this substring in column deduction in errWithSrc().
425 *
426 * \param ptoken is the token string we are processing.
427 *
428 * \return the last substring, else NULL
429 */
430 char *
getDeduceStr(char * ptoken)431 getDeduceStr(char *ptoken)
432 {
433 char *lastToken;
434 if (ptoken != NULL) {
435 lastToken = strrchr(ptoken, ' ');
436 if (lastToken != NULL) {
437 lastToken++;
438 }
439 } else {
440 lastToken = NULL;
441 }
442 return lastToken;
443 }
444
445 /** \brief Construct and issue an "enhanced" error message.
446 *
447 * Construct error message and issue it to user terminal and to listing file
448 * if appropriate. This is an "enhanced" error message which means we will
449 * also display the source line, column number, and location of the error.
450 *
451 * Note: First five arguments are the same as function error().
452 *
453 * \param ecode Error number
454 *
455 * \param sev Error severity (a value in the err_severity enum)
456 *
457 * \param eline Source file line number
458 *
459 * \param op1 String to be expanded into error message * or 0
460 *
461 * \param op2 String to be expanded into error message * or 0
462 *
463 * \param col The column number where the error occurred at if
464 * available, else 0.
465 *
466 * \param deduceCol The operand to use (1 for op1, 2 for op2) to deduce the
467 * the column number when the col argument is not available.
468 * Setting this to 0 disables column deduction.
469 *
470 * \param uniqDeduct If set, this function will only deduce the column if
471 * the operand specified in deduceCol only occurs once
472 * in the source line. Otherwise, it will use the first
473 * occurrence of the operand in the source line.
474 *
475 * \param deduceVal If this is a non-NULL character pointer, then use this
476 * string for column deduction instead of op1 or op2.
477 *
478 */
479 void
errWithSrc(error_code_t ecode,enum error_severity sev,int eline,const char * op1,const char * op2,int col,int deduceCol,bool uniqDeduct,const char * deduceVal)480 errWithSrc(error_code_t ecode, enum error_severity sev, int eline,
481 const char *op1, const char *op2, int col, int deduceCol,
482 bool uniqDeduct, const char *deduceVal)
483 {
484 int i, len;
485 char *srcFile = NULL;
486 char *srcLine = NULL;
487 int srcCol = 0;
488 int contNo = 0;
489
490 if (!XBIT(1, 1)) {
491 /* Generate old error messages */
492 display_error(ecode, sev, eline, op1, op2, 0, NULL);
493 return;
494 }
495 if (eline > 0) {
496 srcLine = get_src_line(eline, &srcFile, col, &srcCol, &contNo);
497 if (srcFile && (len = strlen(srcFile)) > 0) {
498 /* trim trailing whitespace on srcFile */
499 char *cp;
500 for (cp = (srcFile + (len - 1)); cp != srcFile; --cp) {
501 if (!isspace(*cp))
502 break;
503 }
504 if (cp != srcFile) {
505 *(cp + 1) = '\0';
506 }
507 }
508 if (deduceCol > 0) {
509 /* try to deduce column number */
510 char *op;
511 char *srcLC = strdup(srcLine);
512 char *p;
513 if (deduceVal != NULL) {
514 op = strdup(deduceVal);
515 } else {
516 op = strdup((deduceCol == 1) ? op1 : op2);
517 }
518 len = strlen(srcLC);
519 for (i = 0; i < len; ++i) {
520 srcLC[i] = tolower(srcLC[i]);
521 }
522 len = strlen(op);
523 for (i = 0; i < len; ++i) {
524 op[i] = tolower(op[i]);
525 }
526 p = srcCol == 0 ? strstr(srcLC, op) : strstr(srcLC + (srcCol-1), op);
527 col = 0;
528 if (p != NULL) {
529 if (uniqDeduct) {
530 char *q = strstr(p + 1, op);
531 if (q == NULL) {
532 /* op only occurs once in srcLine, so we can deduce col */
533 col = (int)(p - srcLC) + 1;
534 }
535 } else {
536 /* found op in srcLine, so we can deduce col */
537 col = (int)(p - srcLC) + 1;
538 }
539 }
540 FREE(op);
541 FREE(srcLC);
542 }
543 }
544 if (!deduceCol || col == 0)
545 col = srcCol;
546 display_error(ecode, sev, contNo + eline, op1, op2, col, srcFile);
547 if (col > 0 && srcLine != NULL) {
548 bool isLeadingChars;
549 int numLeadingTabs;
550 len = strlen(srcLine);
551 for (numLeadingTabs = i = 0, isLeadingChars = true; i < len; ++i) {
552 if (i == (col - 1)) {
553 isLeadingChars = false;
554 }
555 if (isLeadingChars && srcLine[i] == '\t') {
556 /* Keep track of tabs that appear before column number. */
557 fputc('\t', stderr);
558 ++numLeadingTabs;
559 } else if (srcLine[i] == '\n') {
560 break;
561 } else {
562 fputc(srcLine[i], stderr);
563 }
564 }
565 fputc('\n', stderr);
566
567 /* When we first computed col, we counted a tab as one space. So, we need
568 * to subtract one from col as we print out the leading tabs.
569 */
570 for (i = 0; i < numLeadingTabs; ++i) {
571 fputc('\t', stderr);
572 }
573 col -= numLeadingTabs;
574
575 for (i = 0; i < (col - 1); ++i)
576 fputc(' ', stderr);
577 fputs("^\n", stderr);
578 } else {
579 fputc('\n', stderr);
580 }
581 FREE(srcLine);
582 FREE(srcFile);
583 if (sev == ERR_Fatal) {
584 finish();
585 }
586 }
587 #endif
588