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