diff --git a/NEWS b/NEWS index 194fc25dc..64811a97d 100644 --- a/NEWS +++ b/NEWS @@ -321,7 +321,7 @@ NEWS - user visible changes -*- outline -*- original version; note: their use will be adjusted where they don't match GCC's same options in later versions, including addition of -M and -MD -** New -std options: +** new -std options: gcos GCOS compatibility gcos-strict GCOS compatibility - strict @@ -330,6 +330,14 @@ NEWS - user visible changes -*- outline -*- dialect configuration options accompanying each specificity introduced by the dialect. +** new diagnostic format for errors: the diagnostics now print the source + code context with a left margin showing line numbers, configurable with + -fno-diagnostics-show-line-numbers, and possible to disable completely + with -fno-diagnostics-show-caret; + + the option -fdiagnostics-plain-output was added to request that diagnostic + output look as plain as possible and stay more stable over time + * Important Bugfixes: ** for dialects other than the GnuCOBOL default different reserved "alias" words diff --git a/cobc/ChangeLog b/cobc/ChangeLog index cf9ce9ecd..16af69cba 100644 --- a/cobc/ChangeLog +++ b/cobc/ChangeLog @@ -1,4 +1,18 @@ +2023-02-16 Fabrice Le Fessant <fabrice.le_fessant@ocamlpro.com> + + * flag.def (cb_diagnostics_show_caret), error.c + (diagnostics_show_caret): add argument -fno-diagnostics-show-caret + to disable the display of the source context of the error/warning, + 2 lines before and after the location; add argument + -fno-diagnostics-show-line-numbers to disable printing of + line numbers in carets; rename cb_diagnostic_show_option into + cb_diagnostics_show_option to match the argument name. + * error.c (cb_error_kind): replace all occurrences of a + error/warning/note string by a symbolic enum. + * cobc.c: new argument -fdiagnostics-plain-output to make + output as plain as possible (disabling carets for example) + 2023-05-31 Simon Sobisch <simonsobisch@gnu.org> * codegen.c (output_init_comment_and_source_ref) [NO_INIT_SOURCE_LOC]: diff --git a/cobc/cobc.c b/cobc/cobc.c index 336ac60bc..fe9095025 100644 --- a/cobc/cobc.c +++ b/cobc/cobc.c @@ -589,6 +589,7 @@ static const struct option long_options[] = { {"P", CB_OP_ARG, NULL, 'P'}, {"Xref", CB_NO_ARG, NULL, 'X'}, {"use-extfh", CB_RQ_ARG, NULL, 9}, /* this is used by COBOL-IT; Same is -fcallfh= */ + {"fdiagnostics-plain-output", CB_NO_ARG, NULL, '/'}, {"Wall", CB_NO_ARG, NULL, 'W'}, {"Wextra", CB_NO_ARG, NULL, 'Y'}, /* this option used to be called -W */ #if 1 @@ -3459,6 +3460,12 @@ process_command_line (const int argc, char **argv) } break; + case '/': + /* -fdiagnostics-plain-output */ + cb_diagnostics_show_caret = 0 ; + cb_diagnostics_show_line_numbers = 0; + break; + case 'P': /* -P : Generate preproc listing */ if (cob_optarg) { diff --git a/cobc/error.c b/cobc/error.c index 09a62ca5d..941ab6f67 100644 --- a/cobc/error.c +++ b/cobc/error.c @@ -32,6 +32,13 @@ #include "cobc.h" #include "tree.h" +enum cb_error_kind { + CB_KIND_ERROR, + CB_KIND_WARNING, + CB_KIND_NOTE, + CB_KIND_GENERAL +}; + static char *errnamebuff = NULL; static struct cb_label *last_section = NULL; static struct cb_label *last_paragraph = NULL; @@ -63,10 +70,80 @@ print_error_prefix (const char *file, int line, const char *prefix) } } +/* Display a context around the location of the error/warning, only if + * cb_diagnostics_show_caret is true + + Only display two lines before and after. No caret yet for the column as + we only have the line. Since we directly use the file, source is printed + before any REPLACE. + */ + +#define CARET_MAX_COLS 73 +static void +diagnostics_show_caret (const char *file, int line) +{ + FILE* fd = fopen(file, "r"); + if (fd == NULL) return; + char buffer[ CARET_MAX_COLS+1 ]; + int line_pos = 1; + int char_pos = 0; + int printed = 0; /* nothing printed */ + while(1){ + int c = fgetc (fd); + if ( c == EOF ){ + if (printed){ + fprintf(stderr, "\n"); + } + fclose(fd); + return ; + } + buffer[char_pos] = c ; + if (c == '\n' || char_pos == CARET_MAX_COLS){ + buffer[char_pos] = 0; + if (line_pos > line-3 && line_pos < line+3){ + if (line_pos == line-2) fprintf(stderr, "\n"); + printed = 1; + fprintf (stderr, " "); + if (cb_diagnostics_show_line_numbers){ + fprintf (stderr, "%04d ", + line_pos); + } + fprintf (stderr, "%c %s%s\n", + line == line_pos ? '>' : ' ', + c == '\n' ? "" : ".." , + buffer); + if (line_pos == line+2){ + fprintf(stderr, "\n"); + fclose(fd); + return; + } + } + while (c != '\n'){ + /* skip end of line too long */ + c = fgetc (fd); + if( c == EOF ) { fclose(fd); return ; } + } + line_pos++; + char_pos=0; + } else { + char_pos++; + } + } +} + static void -print_error (const char *file, int line, const char *prefix, +print_error (const char *file, int line, enum cb_error_kind kind, const char *fmt, va_list ap, const char *diagnostic_option) { + const char *prefix; + + switch( kind ){ + case CB_KIND_ERROR: prefix = _("error: "); break; + case CB_KIND_WARNING: prefix = _("warning: "); break; + case CB_KIND_NOTE: prefix = _("note: "); break; + case CB_KIND_GENERAL: prefix = ""; break; + } + if (!file) { file = cb_source_file; } @@ -119,12 +196,26 @@ print_error (const char *file, int line, const char *prefix, } cb_add_error_to_listing (file, line, prefix, errmsg); } + + static const char* last_caret_file = NULL ; + static int last_caret_line = -1 ; + if (cb_diagnostics_show_caret + && file != NULL + && strcmp (file, COB_DASH) != 0 + && line + && (last_caret_file != file || last_caret_line != line) + ){ + /* remember last printed location to avoid reprinting it */ + last_caret_file = file; + last_caret_line = line; + diagnostics_show_caret (file, line); + } } static void cobc_too_many_errors (void) { - if (!cb_diagnostic_show_option) { + if (!cb_diagnostics_show_option) { fprintf (stderr, "cobc: %s\n", _("too many errors")); } else @@ -306,7 +397,7 @@ static char *warning_option_text (const enum cb_warn_opt opt, const enum cb_warn { const char *opt_name; - if (!cb_diagnostic_show_option) { + if (!cb_diagnostics_show_option) { return NULL; } switch (opt) { @@ -343,9 +434,9 @@ cb_warning_internal (const enum cb_warn_opt opt, const char *fmt, va_list ap) } if (pref != COBC_WARN_AS_ERROR) { - print_error (NULL, 0, _("warning: "), fmt, ap, warning_option_text (opt, pref)); + print_error (NULL, 0, CB_KIND_WARNING, fmt, ap, warning_option_text (opt, pref)); } else { - print_error (NULL, 0, _("error: "), fmt, ap, warning_option_text (opt, pref)); + print_error (NULL, 0, CB_KIND_ERROR, fmt, ap, warning_option_text (opt, pref)); } if (sav_lst_file) { @@ -379,7 +470,7 @@ cb_error_always (const char *fmt, ...) cobc_in_repository = 0; va_start (ap, fmt); - print_error (NULL, 0, _("error: "), fmt, ap, NULL); + print_error (NULL, 0, CB_KIND_ERROR, fmt, ap, NULL); va_end (ap); if (sav_lst_file) { @@ -405,12 +496,12 @@ cb_error_internal (const char *fmt, va_list ap) } if (!ignore_error) { - print_error (NULL, 0, _("error: "), fmt, ap, NULL); + print_error (NULL, 0, CB_KIND_ERROR, fmt, ap, NULL); ret = COBC_WARN_AS_ERROR; } else if (pref == COBC_WARN_AS_ERROR) { - print_error (NULL, 0, _("error: "), fmt, ap, warning_option_text (opt, pref)); + print_error (NULL, 0, CB_KIND_ERROR, fmt, ap, warning_option_text (opt, pref)); } else { - print_error (NULL, 0, _("warning: "), fmt, ap, warning_option_text (opt, pref)); + print_error (NULL, 0, CB_KIND_WARNING, fmt, ap, warning_option_text (opt, pref)); } if (sav_lst_file) { @@ -447,7 +538,7 @@ cb_perror (const int config_error, const char *fmt, ...) } va_start (ap, fmt); - print_error (NULL, 0, "", fmt, ap, NULL); + print_error (NULL, 0, CB_KIND_GENERAL, fmt, ap, NULL); va_end (ap); @@ -472,9 +563,9 @@ cb_plex_warning (const enum cb_warn_opt opt, const size_t sline, const char *fmt va_start (ap, fmt); if (pref != COBC_WARN_AS_ERROR) { - print_error (NULL, cb_source_line + (int)sline, _("warning: "), fmt, ap, warning_option_text (opt, pref)); + print_error (NULL, cb_source_line + (int)sline, CB_KIND_WARNING, fmt, ap, warning_option_text (opt, pref)); } else { - print_error (NULL, cb_source_line + (int)sline, _("error: "), fmt, ap, warning_option_text (opt, pref)); + print_error (NULL, cb_source_line + (int)sline, CB_KIND_ERROR, fmt, ap, warning_option_text (opt, pref)); } va_end (ap); @@ -496,7 +587,7 @@ cb_plex_error (const size_t sline, const char *fmt, ...) va_list ap; va_start (ap, fmt); - print_error (NULL, cb_source_line + (int)sline, ("error: "), fmt, ap, NULL); + print_error (NULL, cb_source_line + (int)sline, CB_KIND_ERROR, fmt, ap, NULL); va_end (ap); if (sav_lst_file) { @@ -628,7 +719,7 @@ cb_warning_x_internal (const enum cb_warn_opt opt, cb_tree x, const char *fmt, v } print_error (x->source_file, x->source_line, - pref == COBC_WARN_AS_ERROR ? _("error: ") : _("warning: "), + pref == COBC_WARN_AS_ERROR ? CB_KIND_ERROR : CB_KIND_WARNING, fmt, ap, warning_option_text (opt, pref)); if (sav_lst_file) { @@ -672,7 +763,7 @@ cb_warning_dialect_x (const enum cb_support tag, cb_tree x, const char *fmt, ... va_start (ap, fmt); print_error (x->source_file, x->source_line, - ret == COBC_WARN_AS_ERROR ? _("error: ") : _("warning: "), + ret == COBC_WARN_AS_ERROR ? CB_KIND_ERROR : CB_KIND_WARNING, fmt, ap, NULL); va_end (ap); @@ -725,10 +816,10 @@ cb_note_x (const enum cb_warn_opt opt, cb_tree x, const char *fmt, ...) listprint_suppress (); va_start (ap, fmt); if (opt != COB_WARNOPT_NONE) { - print_error (x->source_file, x->source_line, _("note: "), + print_error (x->source_file, x->source_line, CB_KIND_NOTE, fmt, ap, warning_option_text (opt, pref)); } else { - print_error (x->source_file, x->source_line, _("note: "), + print_error (x->source_file, x->source_line, CB_KIND_NOTE, fmt, ap, NULL); } va_end (ap); @@ -752,10 +843,10 @@ cb_note (const enum cb_warn_opt opt, const int suppress_listing, const char *fmt } va_start (ap, fmt); if (opt != COB_WARNOPT_NONE) { - print_error (NULL, 0, _("note: "), + print_error (NULL, 0, CB_KIND_NOTE, fmt, ap, warning_option_text (opt, pref)); } else { - print_error (NULL, 0, _("note: "), + print_error (NULL, 0, CB_KIND_NOTE, fmt, ap, NULL); } va_end (ap); @@ -776,13 +867,13 @@ cb_error_x_internal (cb_tree x, const char *fmt, va_list ap) } if (!ignore_error) { - print_error (x->source_file, x->source_line, _("error: "), + print_error (x->source_file, x->source_line, CB_KIND_ERROR, fmt, ap, NULL); } else if (pref == COBC_WARN_AS_ERROR) { - print_error (x->source_file, x->source_line, _("error: "), + print_error (x->source_file, x->source_line, CB_KIND_ERROR, fmt, ap, warning_option_text (opt, pref)); } else { - print_error (x->source_file, x->source_line, _("warning: "), + print_error (x->source_file, x->source_line, CB_KIND_WARNING, fmt, ap, warning_option_text (opt, pref)); ret = COBC_WARN_ENABLED; } diff --git a/cobc/flag.def b/cobc/flag.def index 80d96fe20..a98f48346 100644 --- a/cobc/flag.def +++ b/cobc/flag.def @@ -232,6 +232,12 @@ CB_FLAG (cb_listing_symbols, 1, "tsymbols", CB_FLAG (cb_listing_cmd, 1, "tcmd", _(" -ftcmd specify command line in listing")) -CB_FLAG_ON (cb_diagnostic_show_option, 1, "diagnostics-show-option", +CB_FLAG_ON (cb_diagnostics_show_option, 1, "diagnostics-show-option", _(" -fno-diagnostics-show-option\tsuppress output of option that directly\n" " controls the diagnostic")) + +CB_FLAG_ON (cb_diagnostics_show_caret, 1, "diagnostics-show-caret", + _(" -fno-diagnostics-show-caret\tdo not display source context on warning/error diagnostic")) + +CB_FLAG_ON (cb_diagnostics_show_line_numbers, 1, "diagnostics-show-line-numbers", + _(" -fno-diagnostics-show-line-numbers\tsuppress display of line numbers in diagnostics")) diff --git a/cobc/help.c b/cobc/help.c index 0cde1f29d..4f4dda32b 100644 --- a/cobc/help.c +++ b/cobc/help.c @@ -162,6 +162,7 @@ cobc_print_usage_warnings (void) #undef CB_ONWARNDEF #undef CB_NOWARNDEF #undef CB_ERRWARNDEF + puts (_(" -fdiagnostics-plain-output\tmake diagnostic output as plain as possible")); puts (_(" -Werror treat all warnings as errors")); puts (_(" -Wno-error don't treat warnings as errors")); puts (_(" -Werror=<warning> treat specified <warning> as error")); diff --git a/tests/atlocal.in b/tests/atlocal.in index 7213bf931..405c24a12 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -72,6 +72,7 @@ fi # FLAGS="-debug -Wall ${COBOL_FLAGS}" FLAGS="-debug -Wall ${COBOL_FLAGS} -fno-diagnostics-show-option" +COBC="${COBC} -fdiagnostics-plain-output" COMPILE="${COBC} -x ${FLAGS}" COMPILE_ONLY="${COBC} -fsyntax-only ${FLAGS} -Wno-unsupported" COMPILE_MODULE="${COBC} -m ${FLAGS}" diff --git a/tests/testsuite.src/listings.at b/tests/testsuite.src/listings.at index a6d43eaba..df3c6c106 100644 --- a/tests/testsuite.src/listings.at +++ b/tests/testsuite.src/listings.at @@ -2143,8 +2143,8 @@ LINE PG/LN A...B............................................................ GnuCOBOL V.R.P prog.cob DDD MMM dd HH:MM:SS YYYY Page 0002 command line: - cobc -q -std=default -Wall -fno-tmessages -fsyntax-only -t prog.lst -+ -fno-tsymbols -ftcmd prog.cob + cobc -fdiagnostics-plain-output -q -std=default -Wall -fno-tmessages ++ -fsyntax-only -t prog.lst -fno-tsymbols -ftcmd prog.cob ]) AT_CHECK([$UNIFY_LISTING prog.lst prog.lis], [0], [], []) diff --git a/tests/testsuite.src/used_binaries.at b/tests/testsuite.src/used_binaries.at index 3d42db4b7..83fa09b07 100644 --- a/tests/testsuite.src/used_binaries.at +++ b/tests/testsuite.src/used_binaries.at @@ -918,3 +918,65 @@ AT_CHECK([cat prog.cob | $COMPILE_MODULE -j -], [0], [job], [], ) AT_CLEANUP + + +AT_SETUP([cobc diagnostics show caret]) +# promoted on 2023-06-01T09:57 +AT_KEYWORDS([cobc diagnostics]) +AT_DATA([prog.cob],[ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 TEST-VAR PIC 9(2) VALUE 'A'. + COPY 'CRUD.CPY'. + PROCEDURE DIVISION. + DISPLAY TEST-VAR NO ADVANCING + END-DISPLAY + MOVE 12 TO TEST-VAR + DISPLAY TEST-VAR NO ADVANCING + END-DISPLAY + STOP RUN. +]) +AT_CHECK([$COBC -Wall -fsyntax-only prog.cob], [1], [], +[prog.cob:7: error: CRUD.CPY: No such file or directory +prog.cob:6: warning: numeric value is expected @<:@-Wothers@:>@ +]) +AT_CHECK([$COMPILE -fdiagnostics-show-caret -fdiagnostics-show-line-numbers -j prog.cob], [1], [], +[prog.cob:7: error: CRUD.CPY: No such file or directory + + 0005 WORKING-STORAGE SECTION. + 0006 01 TEST-VAR PIC 9(2) VALUE 'A'. + 0007 > COPY 'CRUD.CPY'. + 0008 PROCEDURE DIVISION. + 0009 DISPLAY TEST-VAR NO ADVANCING + +prog.cob:6: warning: numeric value is expected + + 0004 DATA DIVISION. + 0005 WORKING-STORAGE SECTION. + 0006 > 01 TEST-VAR PIC 9(2) VALUE 'A'. + 0007 COPY 'CRUD.CPY'. + 0008 PROCEDURE DIVISION. + +]) +AT_CHECK([$COMPILE -fdiagnostics-show-caret -j prog.cob],[1], [], +[prog.cob:7: error: CRUD.CPY: No such file or directory + + WORKING-STORAGE SECTION. + 01 TEST-VAR PIC 9(2) VALUE 'A'. + > COPY 'CRUD.CPY'. + PROCEDURE DIVISION. + DISPLAY TEST-VAR NO ADVANCING + +prog.cob:6: warning: numeric value is expected + + DATA DIVISION. + WORKING-STORAGE SECTION. + > 01 TEST-VAR PIC 9(2) VALUE 'A'. + COPY 'CRUD.CPY'. + PROCEDURE DIVISION. + +]) +AT_CLEANUP +