1 /* ------------------------------------------------------------------------- */
2 /* "errors" : Warnings, errors and fatal errors */
3 /* (with error throwback code for RISC OS machines) */
5 /* Part of Inform 6.42 */
6 /* copyright (c) Graham Nelson 1993 - 2024 */
8 /* Inform is free software: you can redistribute it and/or modify */
9 /* it under the terms of the GNU General Public License as published by */
10 /* the Free Software Foundation, either version 3 of the License, or */
11 /* (at your option) any later version. */
13 /* Inform is distributed in the hope that it will be useful, */
14 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
15 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
16 /* GNU General Public License for more details. */
18 /* You should have received a copy of the GNU General Public License */
19 /* along with Inform. If not, see https://gnu.org/licenses/ */
21 /* ------------------------------------------------------------------------- */
25 #define ERROR_BUFLEN (256)
26 static char error_message_buff[ERROR_BUFLEN+4]; /* room for ellipsis */
28 /* ------------------------------------------------------------------------- */
29 /* Error preamble printing. */
30 /* ------------------------------------------------------------------------- */
32 ErrorPosition ErrorReport; /* Maintained by "lexer.c" */
34 static char other_pos_buff[ERROR_BUFLEN+1]; /* Used by location_text() */
36 static void print_preamble(void)
38 /* Only really prints the preamble to an error or warning message:
40 e.g. "jigsaw.apollo", line 24:
42 The format is controllable (from an ICL switch) since this assists
43 the working of some development environments. */
45 int j, with_extension_flag = FALSE; char *p;
47 j = ErrorReport.file_number;
48 if (j <= 0 || j > total_files) p = ErrorReport.source;
49 else p = InputFiles[j-1].filename;
55 case 0: /* RISC OS error message format */
57 if (!(ErrorReport.main_flag)) printf("\"%s\", ", p);
58 printf("line %d: ", ErrorReport.line_number);
60 if (ErrorReport.orig_file) {
62 if (ErrorReport.orig_file <= 0 || ErrorReport.orig_file > total_files)
63 op = ErrorReport.orig_source;
65 op = InputFiles[ErrorReport.orig_file-1].filename;
66 printf("(\"%s\"", op);
67 if (ErrorReport.orig_line) {
68 printf(", %d", ErrorReport.orig_line);
69 if (ErrorReport.orig_char) {
70 printf(":%d", ErrorReport.orig_char);
77 case 1: /* Microsoft error message format */
79 for (j=0; p[j]!=0; j++)
80 { if (p[j] == FN_SEP) with_extension_flag = TRUE;
81 if (p[j] == '.') with_extension_flag = FALSE;
84 if (with_extension_flag) printf("%s", Source_Extension);
85 printf("(%d)", ErrorReport.line_number);
87 if (ErrorReport.orig_file) {
89 if (ErrorReport.orig_file <= 0 || ErrorReport.orig_file > total_files)
90 op = ErrorReport.orig_source;
92 op = InputFiles[ErrorReport.orig_file-1].filename;
94 if (ErrorReport.orig_line) {
95 printf("(%d", ErrorReport.orig_line);
96 if (ErrorReport.orig_char) {
97 printf(":%d", ErrorReport.orig_char);
106 case 2: /* Macintosh Programmer's Workshop error message format */
108 printf("File \"%s\"; Line %d", p, ErrorReport.line_number);
110 if (ErrorReport.orig_file) {
112 if (ErrorReport.orig_file <= 0 || ErrorReport.orig_file > total_files)
113 op = ErrorReport.orig_source;
115 op = InputFiles[ErrorReport.orig_file-1].filename;
116 printf(": (\"%s\"", op);
117 if (ErrorReport.orig_line) {
118 printf("; Line %d", ErrorReport.orig_line);
119 if (ErrorReport.orig_char) {
120 printf("; Char %d", ErrorReport.orig_char);
131 static char *location_text(brief_location report_line)
137 /* Convert the location to a brief string.
138 (Some error messages need to report a secondary location.)
139 This uses the static buffer other_pos_buff. */
141 ErrorPosition errpos;
142 errpos.file_number = -1;
143 errpos.source = NULL;
144 errpos.line_number = 0;
145 errpos.main_flag = 0;
146 errpos.orig_source = NULL;
147 export_brief_location(report_line, &errpos);
149 j = errpos.file_number;
150 if (j <= 0 || j > total_files) p = errpos.source;
151 else p = InputFiles[j-1].filename;
153 if (!p && errpos.line_number == 0) {
155 strcpy(other_pos_buff, "compiler setup");
156 return other_pos_buff;
163 if (!(errpos.main_flag)) {
164 snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
166 len = strlen(other_pos_buff);
168 snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
169 "line %d", errpos.line_number);
171 return other_pos_buff;
174 char *current_location_text(void)
176 /* Convert the current lexer location to a brief string.
177 (Called by some trace messages.)
178 This uses the static buffer other_pos_buff. */
179 return location_text(get_brief_location(&ErrorReport));
182 static void ellipsize_error_message_buff(void)
184 /* If the error buffer was actually filled up by a message, it was
185 probably truncated too. Add an ellipsis, for which we left
186 extra room. (Yes, yes; errors that are *exactly* 255 characters
187 long will suffer an unnecessary ellipsis.) */
188 if (strlen(error_message_buff) == ERROR_BUFLEN-1)
189 strcat(error_message_buff, "...");
192 /* ------------------------------------------------------------------------- */
193 /* Fatal errors (which have style 0) */
194 /* ------------------------------------------------------------------------- */
196 extern void fatalerror(char *s)
199 printf("Fatal error: %s\n",s);
200 if (no_compiler_errors > 0) print_sorry_message();
208 abort_transcript_file();
210 longjmp(g_fallback, 1);
215 extern void fatalerror_fmt(const char *format, ...)
217 va_list argument_pointer;
218 va_start(argument_pointer, format);
219 vsnprintf(error_message_buff, ERROR_BUFLEN, format, argument_pointer);
220 va_end(argument_pointer);
221 ellipsize_error_message_buff();
222 fatalerror(error_message_buff);
225 extern void fatalerror_named(char *m, char *fn)
226 { snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\"", m, fn);
227 ellipsize_error_message_buff();
228 fatalerror(error_message_buff);
231 extern void fatalerror_memory_out(int32 size, int32 howmany, char *name)
233 snprintf(error_message_buff, ERROR_BUFLEN,
234 "Run out of memory allocating %d bytes for %s", size, name);
236 snprintf(error_message_buff, ERROR_BUFLEN,
237 "Run out of memory allocating array of %dx%d bytes for %s",
238 howmany, size, name);
239 ellipsize_error_message_buff();
240 fatalerror(error_message_buff);
243 /* ------------------------------------------------------------------------- */
244 /* Survivable diagnostics: */
245 /* compilation errors style 1 */
246 /* warnings style 2 */
247 /* linkage errors style 3 (no longer used) */
248 /* compiler errors style 4 (these should never happen and */
249 /* indicate a bug in Inform) */
250 /* ------------------------------------------------------------------------- */
252 int no_errors, no_warnings, no_suppressed_warnings, no_compiler_errors;
254 char *forerrors_buff;
255 int forerrors_pointer;
257 static void message(int style, char *s)
259 if (hash_printed_since_newline) printf("\n");
260 hash_printed_since_newline = FALSE;
263 { case 1: printf("Error: "); no_errors++; break;
264 case 2: printf("Warning: "); no_warnings++; break;
265 case 3: printf("Error: [linking] "); no_errors++; break;
266 case 4: printf("*** Compiler error: ");
267 no_compiler_errors++; break;
271 throwback(((style <= 2) ? style : 1), s);
274 ProcessEvents (&g_proc);
278 abort_transcript_file();
279 longjmp (g_fallback, 1);
282 if ((!concise_switch) && (forerrors_pointer > 0) && (style <= 2))
283 { forerrors_buff[forerrors_pointer] = 0;
284 sprintf(forerrors_buff+68," ...etc");
285 printf("> %s\n",forerrors_buff);
289 /* ------------------------------------------------------------------------- */
290 /* Style 1: Error message routines */
291 /* ------------------------------------------------------------------------- */
293 extern void error(char *s)
294 { if (no_errors == MAX_ERRORS)
295 fatalerror("Too many errors: giving up");
299 extern void error_fmt(const char *format, ...)
301 va_list argument_pointer;
302 va_start(argument_pointer, format);
303 vsnprintf(error_message_buff, ERROR_BUFLEN, format, argument_pointer);
304 va_end(argument_pointer);
305 ellipsize_error_message_buff();
306 error(error_message_buff);
309 extern void error_named(char *s1, char *s2)
310 { snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
311 ellipsize_error_message_buff();
312 error(error_message_buff);
315 extern void error_named_at(char *s1, char *s2, brief_location report_line)
318 ErrorPosition E = ErrorReport;
319 export_brief_location(report_line, &ErrorReport);
321 snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
322 ellipsize_error_message_buff();
324 i = concise_switch; concise_switch = TRUE;
325 error(error_message_buff);
326 ErrorReport = E; concise_switch = i;
329 extern void ebf_error(char *s1, char *s2)
330 { snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found %s", s1, s2);
331 ellipsize_error_message_buff();
332 error(error_message_buff);
335 extern void ebf_curtoken_error(char *s)
337 /* This is "Expected (s) but found (the current token_text)". We use
338 token_type as a hint for how to display token_text. */
340 if (token_type == DQ_TT) {
341 snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found string \"%s\"", s, token_text);
343 else if (token_type == SQ_TT && strlen(token_text)==1) {
344 snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found char '%s'", s, token_text);
346 else if (token_type == SQ_TT) {
347 snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found dict word '%s'", s, token_text);
350 /* Symbols, unquoted strings, and numbers can be printed directly. EOF will have "<end of file>" in token_text. */
351 snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found %s", s, token_text);
354 ellipsize_error_message_buff();
355 error(error_message_buff);
358 extern void ebf_symbol_error(char *s1, char *name, char *type, brief_location report_line)
359 { snprintf(error_message_buff, ERROR_BUFLEN, "\"%s\" is a name already in use and may not be used as a %s (%s \"%s\" was defined at %s)", name, s1, type, name, location_text(report_line));
360 ellipsize_error_message_buff();
361 error(error_message_buff);
364 extern void char_error(char *s, int ch)
367 uni = iso_to_unicode(ch);
369 if (character_set_unicode)
370 snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
371 else if (uni >= 0x100)
372 { snprintf(error_message_buff, ERROR_BUFLEN,
373 "%s (unicode) $%04x = (ISO %s) $%02x", s, uni,
374 name_of_iso_set(character_set_setting), ch);
377 snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
379 /* If the character set is set to Latin-1, and the char in question
380 is a printable Latin-1 character, we print it in the error message.
381 This conflates the source-text charset with the terminal charset,
382 really, but it's not a big deal. */
384 if (((uni>=32) && (uni<127))
385 || (((uni >= 0xa1) && (uni <= 0xff))
386 && (character_set_setting==1) && (!character_set_unicode)))
387 { int curlen = strlen(error_message_buff);
388 snprintf(error_message_buff+curlen, ERROR_BUFLEN-curlen,
389 ", i.e., '%c'", uni);
392 ellipsize_error_message_buff();
393 error(error_message_buff);
396 extern void unicode_char_error(char *s, int32 uni)
399 snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
401 snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
403 /* See comment above. */
405 if (((uni>=32) && (uni<127))
406 || (((uni >= 0xa1) && (uni <= 0xff))
407 && (character_set_setting==1) && (!character_set_unicode)))
408 { int curlen = strlen(error_message_buff);
409 snprintf(error_message_buff+curlen, ERROR_BUFLEN-curlen,
410 ", i.e., '%c'", uni);
413 ellipsize_error_message_buff();
414 error(error_message_buff);
417 extern void error_max_dynamic_strings(int index)
419 if (index >= 96 && !glulx_mode)
420 snprintf(error_message_buff, ERROR_BUFLEN, "Only dynamic strings @(00) to @(95) may be used in Z-code");
421 else if (MAX_DYNAMIC_STRINGS == 0)
422 snprintf(error_message_buff, ERROR_BUFLEN, "Dynamic strings may not be used, because $MAX_DYNAMIC_STRINGS has been set to 0. Increase MAX_DYNAMIC_STRINGS.");
423 else if (MAX_DYNAMIC_STRINGS == 32 && !glulx_mode)
424 snprintf(error_message_buff, ERROR_BUFLEN, "Only dynamic strings @(00) to @(%02d) may be used, because $MAX_DYNAMIC_STRINGS has its default value of %d. Increase MAX_DYNAMIC_STRINGS.", MAX_DYNAMIC_STRINGS-1, MAX_DYNAMIC_STRINGS);
426 snprintf(error_message_buff, ERROR_BUFLEN, "Only dynamic strings @(00) to @(%02d) may be used, because $MAX_DYNAMIC_STRINGS has been set to %d. Increase MAX_DYNAMIC_STRINGS.", MAX_DYNAMIC_STRINGS-1, MAX_DYNAMIC_STRINGS);
428 ellipsize_error_message_buff();
429 error(error_message_buff);
432 extern void error_max_abbreviations(int index)
434 /* This is only called for Z-code. */
436 error("The number of abbreviations has exceeded 96, the limit in Z-code");
438 error("The number of abbreviations has exceeded MAX_ABBREVS. Increase MAX_ABBREVS.");
441 /* ------------------------------------------------------------------------- */
442 /* Style 2: Warning message routines */
443 /* ------------------------------------------------------------------------- */
445 extern void warning(char *s1)
446 { if (nowarnings_switch) { no_suppressed_warnings++; return; }
450 extern void warning_fmt(const char *format, ...)
452 va_list argument_pointer;
453 if (nowarnings_switch) { no_suppressed_warnings++; return; }
454 va_start(argument_pointer, format);
455 vsnprintf(error_message_buff, ERROR_BUFLEN, format, argument_pointer);
456 va_end(argument_pointer);
457 ellipsize_error_message_buff();
458 message(2,error_message_buff);
461 extern void warning_named(char *s1, char *s2)
463 if (nowarnings_switch) { no_suppressed_warnings++; return; }
464 snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"", s1, s2);
465 ellipsize_error_message_buff();
466 message(2,error_message_buff);
469 extern void warning_at(char *name, brief_location report_line)
471 ErrorPosition E = ErrorReport;
472 if (nowarnings_switch) { no_suppressed_warnings++; return; }
473 export_brief_location(report_line, &ErrorReport);
474 snprintf(error_message_buff, ERROR_BUFLEN, "%s", name);
475 ellipsize_error_message_buff();
476 i = concise_switch; concise_switch = TRUE;
477 message(2,error_message_buff);
482 extern void symtype_warning(char *context, char *name, char *type, char *wanttype)
484 if (nowarnings_switch) { no_suppressed_warnings++; return; }
486 snprintf(error_message_buff, ERROR_BUFLEN, "In %s, expected %s but found %s \"%s\"", context, wanttype, type, name);
488 snprintf(error_message_buff, ERROR_BUFLEN, "In %s, expected %s but found %s", context, wanttype, type);
489 ellipsize_error_message_buff();
490 message(2,error_message_buff);
493 extern void dbnu_warning(char *type, char *name, brief_location report_line)
495 ErrorPosition E = ErrorReport;
496 if (nowarnings_switch) { no_suppressed_warnings++; return; }
497 export_brief_location(report_line, &ErrorReport);
498 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" declared but not used", type, name);
499 ellipsize_error_message_buff();
500 i = concise_switch; concise_switch = TRUE;
501 message(2,error_message_buff);
506 extern void uncalled_routine_warning(char *type, char *name, brief_location report_line)
508 /* This is called for functions which have been detected by the
509 track-unused-routines module. These will often (but not always)
510 be also caught by dbnu_warning(), which tracks symbols rather
511 than routine addresses. */
512 ErrorPosition E = ErrorReport;
513 if (nowarnings_switch) { no_suppressed_warnings++; return; }
514 export_brief_location(report_line, &ErrorReport);
515 if (OMIT_UNUSED_ROUTINES)
516 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" unused and omitted", type, name);
518 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" unused (not omitted)", type, name);
519 ellipsize_error_message_buff();
520 i = concise_switch; concise_switch = TRUE;
521 message(2,error_message_buff);
526 extern void obsolete_warning(char *s1)
527 { if (is_systemfile()==1) return;
528 if (obsolete_switch || nowarnings_switch)
529 { no_suppressed_warnings++; return; }
530 snprintf(error_message_buff, ERROR_BUFLEN, "Obsolete usage: %s",s1);
531 ellipsize_error_message_buff();
532 message(2,error_message_buff);
535 /* ------------------------------------------------------------------------- */
536 /* Style 4: Compiler error message routines */
537 /* ------------------------------------------------------------------------- */
539 extern void print_sorry_message(void)
541 "***********************************************************************\n\
542 * 'Compiler errors' should never occur if Inform is working properly. *\n\
543 * Check to see if there is a more recent version available, from which *\n\
544 * the problem may have been removed. If not, please report this fault *\n\
545 * and if at all possible, please include your source code, as faults *\n\
546 * such as these are rare and often difficult to reproduce. Sorry. *\n\
547 ***********************************************************************\n");
550 extern int compiler_error(char *s)
552 if (no_errors > 0) return FALSE;
553 if (no_compiler_errors==MAX_ERRORS)
554 fatalerror("Too many compiler errors: giving up");
559 extern int compiler_error_named(char *s1, char *s2)
561 if (no_errors > 0) return FALSE;
562 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\"",s1,s2);
563 ellipsize_error_message_buff();
564 compiler_error(error_message_buff);
568 /* ------------------------------------------------------------------------- */
569 /* Code for the Acorn RISC OS operating system, donated by Robin Watts, */
570 /* to provide error throwback under the DDE environment */
571 /* ------------------------------------------------------------------------- */
575 #define DDEUtils_ThrowbackStart 0x42587
576 #define DDEUtils_ThrowbackSend 0x42588
577 #define DDEUtils_ThrowbackEnd 0x42589
581 extern void throwback_start(void)
582 { _kernel_swi_regs regs;
583 if (throwback_switch)
584 _kernel_swi(DDEUtils_ThrowbackStart, ®s, ®s);
587 extern void throwback_end(void)
588 { _kernel_swi_regs regs;
589 if (throwback_switch)
590 _kernel_swi(DDEUtils_ThrowbackEnd, ®s, ®s);
593 int throwback_started = FALSE;
595 extern void throwback(int severity, char * error)
596 { _kernel_swi_regs regs;
597 if (!throwback_started)
598 { throwback_started = TRUE;
601 if (throwback_switch)
603 if ((ErrorReport.file_number == -1)
604 || (ErrorReport.file_number == 0))
605 regs.r[2] = (int) (InputFiles[0].filename);
606 else regs.r[2] = (int) (InputFiles[ErrorReport.file_number-1].filename);
607 regs.r[3] = ErrorReport.line_number;
608 regs.r[4] = (2-severity);
609 regs.r[5] = (int) error;
610 _kernel_swi(DDEUtils_ThrowbackSend, ®s, ®s);
616 /* ========================================================================= */
617 /* Data structure management routines */
618 /* ------------------------------------------------------------------------- */
620 extern void init_errors_vars(void)
621 { forerrors_buff = NULL;
622 no_errors = 0; no_warnings = 0; no_suppressed_warnings = 0;
623 no_compiler_errors = 0;
626 extern void errors_begin_pass(void)
627 { ErrorReport.line_number = 0;
628 ErrorReport.file_number = -1;
629 ErrorReport.source = "<no text read yet>";
630 ErrorReport.main_flag = FALSE;
631 ErrorReport.orig_source = NULL;
632 ErrorReport.orig_file = 0;
633 ErrorReport.orig_line = 0;
634 ErrorReport.orig_char = 0;
637 extern void errors_allocate_arrays(void)
638 { forerrors_buff = my_malloc(FORERRORS_SIZE, "errors buffer");
641 extern void errors_free_arrays(void)
642 { my_free(&forerrors_buff, "errors buffer");
645 /* ========================================================================= */