[gcc r17-2906] fortran: [PR118793] Additional expanded namelist read diagnostics
Jerry DeLisle via Gcc-cvs <[email protected]> Mon, 3 Aug 2026 20:58:23 +0000 (GMT)
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:86b7458e072b0651f8b04bcba64f5e2d1586bdec commit r17-2906-g86b7458e072b0651f8b04bcba64f5e2d1586bdec Author: Jerry DeLisle <[email protected]> Date: Tue Jul 28 19:02:36 2026 -0700 fortran: [PR118793] Additional expanded namelist read diagnostics These changes add expanded diagnostics to error locations not addressed in the original patch. This is done by using a new helper function that saves the error message information for namelist related errors in a buffer and keeping a status bit in the st_parameter_dt structure. PR libfortran/118793 libgfortran/ChangeLog: * io/io.h (NML_ERR_MSG_LEN): New macro. (st_parameter_dt): Add nml_err_pending bit. (gfc_unit): Add nml_err_msg. * io/list_read.c (nml_error): New function. (eat_separator): Use new function (convert_integer): Likewise. (convert_unsigned): Likewise. (parse_repeat): Likewise. Save the position where the repeat count starts and report it. (read_logical): Likewise. (read_integer): Likewise. (parse_real): Likewise. (read_complex): Likewise. (read_real): Likewise. (check_type): Likewise. (list_formatted_read_scalar): Likewise. (read_character): Likewise. Save the initial position of the value (nml_read_obj): Unwind to nml_err_ret on a deferred error. (namelist_read): Store deferred errors in the unit's nml_err_msg buffer. gcc/testsuite/ChangeLog: * gfortran.dg/namelist_101.f90: Fix dg-do directive typo and check the expanded diagnostic. * gfortran.dg/namelist_104.f90: New test. Diff: --- gcc/testsuite/gfortran.dg/namelist_101.f90 | 6 +- gcc/testsuite/gfortran.dg/namelist_104.f90 | 21 +++++ libgfortran/io/io.h | 12 ++- libgfortran/io/list_read.c | 119 ++++++++++++++++++++++------- 4 files changed, 128 insertions(+), 30 deletions(-) diff --git a/gcc/testsuite/gfortran.dg/namelist_101.f90 b/gcc/testsuite/gfortran.dg/namelist_101.f90 index 409c568f53f3..e6b33f876193 100644 --- a/gcc/testsuite/gfortran.dg/namelist_101.f90 +++ b/gcc/testsuite/gfortran.dg/namelist_101.f90 @@ -1,5 +1,7 @@ -! { dg-do run ) +! { dg-do run } ! { dg-shouldfail "Missing quote" } +! +! PR libfortran/118793 program nml_quotes_bug implicit none integer :: unit = 10 @@ -14,3 +16,5 @@ program nml_quotes_bug read (unit ,nml=tovs_obs_chan) close(unit ,status="delete") end program nml_quotes_bug +! { dg-output "Missing quote while reading item 2 at line 3, column 10 in file .*(\r*\n+)" } +! { dg-output " c2 = 2a ,(\r*\n+) \\^(\r*\n+)" } diff --git a/gcc/testsuite/gfortran.dg/namelist_104.f90 b/gcc/testsuite/gfortran.dg/namelist_104.f90 new file mode 100644 index 000000000000..d45c07a762ba --- /dev/null +++ b/gcc/testsuite/gfortran.dg/namelist_104.f90 @@ -0,0 +1,21 @@ +! { dg-do run } +! { dg-shouldfail "Missing quote" } +!! PR118793 - Expanded namelist error messages +! +! Based on a testcase by Harald Anlauf +program namelist_104 + implicit none + integer :: unit = 10 + character(8) :: c1, c2 + namelist /tovs_obs_chan/ c1, c2 + open (unit ,file="nml-quotes-bug-104.nml") + write(unit,*) "&tovs_obs_chan" + write(unit,*) " c1 = '1', c1 = '1'," + write(unit,*) " c1 = '1', c1 = '1', c2 = 2 ," + write(unit,*) "/" + rewind(unit) + read (unit ,nml=tovs_obs_chan) + close(unit ,status="delete") +end program namelist_104 +! { dg-output "Missing quote while reading item 5 at line 3, column 30 in file .*(\r*\n+)" } +! { dg-output " c1 = '1', c1 = '1', c2 = 2 ,(\r*\n+) \\^(\r*\n+)" } diff --git a/libgfortran/io/io.h b/libgfortran/io/io.h index ecf1b779a483..87d32691ee8d 100644 --- a/libgfortran/io/io.h +++ b/libgfortran/io/io.h @@ -37,6 +37,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see /* Used for building error message strings. */ #define IOMSG_LEN 256 +/* Size of the namelist read error message buffer. */ +#define NML_ERR_MSG_LEN 200 + /* POSIX 2008 specifies that the extended locale stuff is found in locale.h, but some systems have them in xlocale.h. */ @@ -553,7 +556,10 @@ typedef struct st_parameter_dt unsigned expanded_read : 1; /* Flag to indicate if the statement has async="YES". */ unsigned async : 1; - /* 12 unused bits. */ + /* A namelist specific flag set when a read error message has + been saved into the unit's nml_err_msg buffer. */ + unsigned nml_err_pending : 1; + /* 11 unused bits. */ int child_saved_iostat; int nml_delim; @@ -659,6 +665,10 @@ typedef struct gfc_unit /* Position information for better diagnostics. */ int line_number, column_number; + /* Error message buffer used while a namelist read is in progress. Valid + only when the transfer's nml_err_pending flag is set. */ + char nml_err_msg[NML_ERR_MSG_LEN]; + enum { NO_ENDFILE, AT_ENDFILE, AFTER_ENDFILE } endfile; diff --git a/libgfortran/io/list_read.c b/libgfortran/io/list_read.c index f2179e2a1d89..b9e45fab7bfd 100644 --- a/libgfortran/io/list_read.c +++ b/libgfortran/io/list_read.c @@ -69,6 +69,24 @@ typedef unsigned char uchar; #define next_char(dtp) ((dtp)->u.p.current_unit->next_char_fn_ptr (dtp)) #define push_char(dtp, c) ((dtp)->u.p.current_unit->push_char_fn_ptr (dtp, c)) +/* During a namelist read, the error message is saved to the namelist error + buffer and flagged as pending. The saved message will be issued from + nml_read_obj. Non namelist errors are issued now. */ + +static void +nml_error (st_parameter_dt *dtp, int errcode, const char *message) +{ + if (dtp->u.p.namelist_mode) + { + snprintf (dtp->u.p.current_unit->nml_err_msg, NML_ERR_MSG_LEN, "%s", + message); + dtp->u.p.nml_err_pending = 1; + return; + } + + generate_error (&dtp->common, errcode, message); +} + /* Worker function to save a default KIND=1 character to a string buffer, enlarging it as necessary. */ @@ -516,8 +534,8 @@ eat_separator (st_parameter_dt *dtp) case ',': if (dtp->u.p.current_unit->decimal_status == DECIMAL_COMMA) { - generate_error (&dtp->common, LIBERROR_READ_VALUE, - "Comma not allowed as separator with DECIMAL='comma'"); + nml_error (dtp, LIBERROR_READ_VALUE, + "Comma not allowed as separator with DECIMAL='comma'"); unget_char (dtp, c); break; } @@ -528,8 +546,8 @@ eat_separator (st_parameter_dt *dtp) case ';': if (dtp->u.p.current_unit->decimal_status == DECIMAL_POINT) { - generate_error (&dtp->common, LIBERROR_READ_VALUE, - "Semicolon not allowed as separator with DECIMAL='point'"); + nml_error (dtp, LIBERROR_READ_VALUE, + "Semicolon not allowed as separator with DECIMAL='point'"); unget_char (dtp, c); break; } @@ -726,7 +744,7 @@ convert_integer (st_parameter_dt *dtp, int length, int negative) snprintf (message, IOMSG_LEN, "Zero repeat count in item %d of list " "input", dtp->u.p.item_count); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); m = 1; } } @@ -743,7 +761,7 @@ convert_integer (st_parameter_dt *dtp, int length, int negative) dtp->u.p.item_count); free_saved (dtp); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); return 1; } @@ -805,7 +823,7 @@ convert_unsigned (st_parameter_dt *dtp, int length, int negative) snprintf (message, IOMSG_LEN, "Zero repeat count in item %d of list input", dtp->u.p.item_count); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); m = 1; } } @@ -824,7 +842,7 @@ convert_unsigned (st_parameter_dt *dtp, int length, int negative) "item %d of list input", dtp->u.p.item_count); free_saved (dtp); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); return 1; } @@ -838,9 +856,16 @@ parse_repeat (st_parameter_dt *dtp) { char message[IOMSG_LEN]; int c, repeat; + int initial_line = dtp->u.p.current_unit->line_number; + int initial_col = dtp->u.p.current_unit->column_number; if ((c = next_char (dtp)) == EOF) goto bad_repeat; + + /* Remember where the repeat count starts, for diagnostics. */ + initial_line = dtp->u.p.current_unit->line_number; + initial_col = dtp->u.p.current_unit->column_number; + switch (c) { CASE_DIGITS: @@ -867,11 +892,16 @@ parse_repeat (st_parameter_dt *dtp) if (repeat > MAX_REPEAT) { + if (dtp->u.p.namelist_mode) + { + dtp->u.p.current_unit->line_number = initial_line; + dtp->u.p.current_unit->column_number = initial_col; + } snprintf (message, IOMSG_LEN, "Repeat count overflow in item %d of list input", dtp->u.p.item_count); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); return 1; } @@ -880,11 +910,16 @@ parse_repeat (st_parameter_dt *dtp) case '*': if (repeat == 0) { + if (dtp->u.p.namelist_mode) + { + dtp->u.p.current_unit->line_number = initial_line; + dtp->u.p.current_unit->column_number = initial_col; + } snprintf (message, IOMSG_LEN, "Zero repeat count in item %d of list input", dtp->u.p.item_count); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); return 1; } @@ -910,9 +945,14 @@ parse_repeat (st_parameter_dt *dtp) } else eat_line (dtp); + if (dtp->u.p.namelist_mode) + { + dtp->u.p.current_unit->line_number = initial_line; + dtp->u.p.current_unit->column_number = initial_col; + } snprintf (message, IOMSG_LEN, "Bad repeat count in item %d of list input", dtp->u.p.item_count); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); return 1; } @@ -1081,7 +1121,7 @@ next: snprintf (message, IOMSG_LEN, "Bad logical value while reading item %d", dtp->u.p.item_count); free_line (dtp); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); return; logical_done: @@ -1254,7 +1294,7 @@ next: dtp->u.p.item_count); free_line (dtp); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); return; @@ -1290,13 +1330,18 @@ static void read_character (st_parameter_dt *dtp, int length __attribute__ ((unused))) { char quote, message[IOMSG_LEN]; - int c; + int c, initial_line, initial_col; quote = ' '; /* Space means no quote character. */ next: if ((c = next_char (dtp)) == EOF) goto eof; + + /* Save diagnostics info. */ + initial_line = dtp->u.p.current_unit->line_number; + initial_col = dtp->u.p.current_unit->column_number; + if (c == ';') { push_char (dtp, c); @@ -1364,9 +1409,15 @@ next: was a string of digits it should have had the closing quote. */ if (dtp->u.p.namelist_mode) { + dtp->u.p.current_unit->line_number = initial_line; + dtp->u.p.current_unit->column_number = initial_col; snprintf (message, IOMSG_LEN, "Missing quote while reading item %d", dtp->u.p.item_count); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); + /* If there is a pending error, exit here to preserve the + position information. */ + if (dtp->u.p.nml_err_pending) + return; } unget_char (dtp, c); goto done; /* String was only digits! */ @@ -1420,9 +1471,14 @@ next: read should have been set. */ if (dtp->u.p.namelist_mode && (quote == ' ')) { + dtp->u.p.current_unit->line_number = initial_line; + dtp->u.p.current_unit->column_number = initial_col; snprintf (message, IOMSG_LEN, "Missing quote while reading item %d", dtp->u.p.item_count); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); + /* Stop consuming input so the reported position stays put. */ + if (dtp->u.p.nml_err_pending) + return; } for (;;) @@ -1493,7 +1549,7 @@ next: free_saved (dtp); snprintf (message, IOMSG_LEN, "Invalid string input in item %d", dtp->u.p.item_count); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); } free_line (dtp); return; @@ -1764,7 +1820,7 @@ parse_real (st_parameter_dt *dtp, void *buffer, int length) snprintf (message, IOMSG_LEN, "Bad complex floating point " "number for item %d", dtp->u.p.item_count); free_line (dtp); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); return 1; } @@ -1880,7 +1936,7 @@ eol_4: snprintf (message, IOMSG_LEN, "Bad complex value in item %d of list input", dtp->u.p.item_count); free_line (dtp); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); } @@ -2360,7 +2416,7 @@ next: snprintf (message, IOMSG_LEN, "Bad real number in item %d of list input", dtp->u.p.item_count); free_line (dtp); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); } @@ -2378,7 +2434,7 @@ check_type (st_parameter_dt *dtp, bt type, int kind) type_name (dtp->u.p.saved_type), type_name (type), dtp->u.p.item_count); free_line (dtp); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); return 1; } @@ -2395,7 +2451,7 @@ check_type (st_parameter_dt *dtp, bt type, int kind) type_name (dtp->u.p.saved_type), kind, dtp->u.p.item_count); free_line (dtp); - generate_error (&dtp->common, LIBERROR_READ_VALUE, message); + nml_error (dtp, LIBERROR_READ_VALUE, message); return 1; } @@ -2579,8 +2635,7 @@ list_formatted_read_scalar (st_parameter_dt *dtp, bt type, void *p, free_line (dtp); fstrcpy (message, child_iomsg_len, child_iomsg, child_iomsg_len); message[child_iomsg_len] = '\0'; - generate_error (&dtp->common, dtp->u.p.child_saved_iostat, - message); + nml_error (dtp, dtp->u.p.child_saved_iostat, message); } } break; @@ -3459,8 +3514,7 @@ nml_read_obj (st_parameter_dt *dtp, namelist_info *nl, index_type offset, child_iomsg_len = string_len_trim (IOMSG_LEN, child_iomsg); fstrcpy (message, child_iomsg_len, child_iomsg, child_iomsg_len); message[child_iomsg_len] = '\0'; - generate_error (&dtp->common, dtp->u.p.child_saved_iostat, - message); + nml_error (dtp, dtp->u.p.child_saved_iostat, message); goto nml_err_ret; } @@ -3473,6 +3527,10 @@ nml_read_obj (st_parameter_dt *dtp, namelist_info *nl, index_type offset, internal_error (&dtp->common, nml_err_msg); goto nml_err_ret; } + + /* If there is a pending error, return now. */ + if (dtp->u.p.nml_err_pending) + return false; } /* The standard permits array data to stop short of the number of @@ -3933,7 +3991,7 @@ void namelist_read (st_parameter_dt *dtp) { int c; - char nml_err_msg[200]; + char *nml_err_msg = dtp->u.p.current_unit->nml_err_msg; /* Initialize the error string buffer just in case we get an unexpected fail somewhere and end up at nml_err_ret. */ @@ -3947,6 +4005,9 @@ namelist_read (st_parameter_dt *dtp) dtp->u.p.input_complete = 0; dtp->u.p.expanded_read = 0; + /* Initialize the pending error flag. */ + dtp->u.p.nml_err_pending = 0; + /* Set the next_char and push_char worker functions. */ set_workers (dtp); @@ -4012,7 +4073,8 @@ find_nml_name: while (!dtp->u.p.input_complete) { - if (!nml_get_obj_data (dtp, &prev_nl, nml_err_msg, sizeof nml_err_msg)) + if (!nml_get_obj_data (dtp, &prev_nl, nml_err_msg, NML_ERR_MSG_LEN) + || dtp->u.p.nml_err_pending) goto nml_err_ret; /* Reset the previous namelist pointer if we know we are not going @@ -4031,6 +4093,7 @@ nml_err_ret: /* All namelist error calls return from here */ free_saved (dtp); free_line (dtp); + dtp->u.p.nml_err_pending = 0; if (dtp->u.p.current_unit) {