blob: 254be5f49b2ef7e0899d233084200e2e382b22b4 [file] [log] [blame]
/* Error and warning routines.
Copyright (C) 2001-2023 J. Marcel van der Veer.
Copyright (C) 2025 Jose E. Marchesi.
Original implementation by J. Marcel van der Veer.
Adapted and expanded for GCC by Jose E. Marchesi.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#define INCLUDE_MEMORY
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "diagnostic.h"
#include "a68.h"
/*
* Error handling routines.
*/
#define TABULATE(n) (8 * (n / 8 + 1) - n)
/* Severities handled by the DIAGNOSTIC function defined below. */
#define A68_ERROR 0
#define A68_WARNING 1
#define A68_FATAL 2
#define A68_SCAN_ERROR 3
#define A68_INFORM 4
/* Auxiliary function used to grow an obstack by the contents of some given
string. */
static void
obstack_append_str (obstack *b, const char *str)
{
obstack_grow (b, str, strlen (str));
}
/* Give a diagnostic message. */
#if __GNUC__ >= 10
#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
#endif
static bool
diagnostic (int sev, int opt,
NODE_T *p,
LINE_T *line,
char *pos,
const char *loc_str, va_list args)
{
int res = 0;
MOID_T *moid = NO_MOID;
const char *t = loc_str;
obstack b;
/*
* Synthesize diagnostic message.
*
* Legend for special symbols:
* * as first character, copy rest of string literally
* @ AST node
* A AST node attribute
* B keyword
* C context
* L line number
* M moid - if error mode return without giving a message
* O moid - operand
* S quoted symbol, when possible with typographical display features
* X expected attribute
* Y string literal.
* Z quoted string. */
static va_list argp; /* Note this is empty. */
gcc_obstack_init (&b);
if (t[0] == '*')
obstack_append_str (&b, t + 1);
else
while (t[0] != '\0')
{
if (t[0] == '@')
{
const char *nt = a68_attribute_name (ATTRIBUTE (p));
if (t != NO_TEXT)
obstack_append_str (&b, nt);
else
obstack_append_str (&b, "construct");
}
else if (t[0] == 'A')
{
enum a68_attribute att = (enum a68_attribute) va_arg (args, int);
const char *nt = a68_attribute_name (att);
if (nt != NO_TEXT)
obstack_append_str (&b, nt);
else
obstack_append_str (&b, "construct");
}
else if (t[0] == 'B')
{
enum a68_attribute att = (enum a68_attribute) va_arg (args, int);
KEYWORD_T *nt = a68_find_keyword_from_attribute (A68 (top_keyword), att);
if (nt != NO_KEYWORD)
{
const char *strop_keyword = a68_strop_keyword (TEXT (nt));
obstack_append_str (&b, "%<");
obstack_append_str (&b, strop_keyword);
obstack_append_str (&b, "%>");
}
else
obstack_append_str (&b, "keyword");
}
else if (t[0] == 'C')
{
int att = va_arg (args, int);
const char *sort = NULL;
switch (att)
{
case NO_SORT: sort = "this"; break;
case SOFT: sort = "a soft"; break;
case WEAK: sort = "a weak"; break;
case MEEK: sort = "a meek"; break;
case FIRM: sort = "a firm"; break;
case STRONG: sort = "a strong"; break;
default:
gcc_unreachable ();
}
obstack_append_str (&b, sort);
}
else if (t[0] == 'L')
{
LINE_T *a = va_arg (args, LINE_T *);
gcc_assert (a != NO_LINE);
if (NUMBER (a) == 0)
obstack_append_str (&b, "in standard environment");
else if (p != NO_NODE && NUMBER (a) == LINE_NUMBER (p))
obstack_append_str (&b, "in this line");
else
{
char d[18];
if (snprintf (d, 18, "in line %d", NUMBER (a)) < 0)
gcc_unreachable ();
obstack_append_str (&b, d);
}
}
else if (t[0] == 'M')
{
const char *moidstr = NULL;
moid = va_arg (args, MOID_T *);
if (moid == NO_MOID || moid == M_ERROR)
moid = M_UNDEFINED;
if (IS (moid, SERIES_MODE))
{
if (PACK (moid) != NO_PACK && NEXT (PACK (moid)) == NO_PACK)
moidstr = a68_moid_to_string (MOID (PACK (moid)),
MOID_ERROR_WIDTH, p);
else
moidstr = a68_moid_to_string (moid, MOID_ERROR_WIDTH, p);
}
else
moidstr = a68_moid_to_string (moid, MOID_ERROR_WIDTH, p);
obstack_append_str (&b, "%<");
obstack_append_str (&b, moidstr);
obstack_append_str (&b, "%>");
}
else if (t[0] == 'O')
{
moid = va_arg (args, MOID_T *);
if (moid == NO_MOID || moid == M_ERROR)
moid = M_UNDEFINED;
if (moid == M_VOID)
obstack_append_str (&b, "UNION (VOID, ..)");
else if (IS (moid, SERIES_MODE))
{
const char *moidstr = NULL;
if (PACK (moid) != NO_PACK && NEXT (PACK (moid)) == NO_PACK)
moidstr = a68_moid_to_string (MOID (PACK (moid)), MOID_ERROR_WIDTH, p);
else
moidstr = a68_moid_to_string (moid, MOID_ERROR_WIDTH, p);
obstack_append_str (&b, moidstr);
}
else
{
const char *moidstr = a68_moid_to_string (moid, MOID_ERROR_WIDTH, p);
obstack_append_str (&b, moidstr);
}
}
else if (t[0] == 'S')
{
if (p != NO_NODE && NSYMBOL (p) != NO_TEXT)
{
const char *txt = NSYMBOL (p);
char *sym = NCHAR_IN_LINE (p);
int n = 0, size = (int) strlen (txt);
obstack_append_str (&b, "%<");
if (txt[0] != sym[0] || (int) strlen (sym) < size)
obstack_append_str (&b, txt);
else
{
while (n < size)
{
if (ISPRINT (sym[0]))
obstack_1grow (&b, sym[0]);
if (TOLOWER (txt[0]) == TOLOWER (sym[0]))
{
txt++;
n++;
}
sym++;
}
}
obstack_append_str (&b, "%>");
}
else
obstack_append_str (&b, "symbol");
}
else if (t[0] == 'X')
{
enum a68_attribute att = (enum a68_attribute) (va_arg (args, int));
const char *att_name = a68_attribute_name (att);
obstack_append_str (&b, att_name);
}
else if (t[0] == 'Y')
{
char *loc_string = va_arg (args, char *);
obstack_append_str (&b, loc_string);
}
else if (t[0] == 'Z')
{
char *str = va_arg (args, char *);
obstack_append_str (&b, "%<");
obstack_append_str (&b, str);
obstack_append_str (&b, "%>");
}
else
obstack_1grow (&b, t[0]);
t++;
}
obstack_1grow (&b, '\0');
char *format = (char *) obstack_finish (&b);
/* Construct a diagnostic message. */
if (sev == A68_WARNING)
WARNING_COUNT (&A68_JOB)++;
else if (sev != A68_INFORM)
ERROR_COUNT (&A68_JOB)++;
/* Emit the corresponding GCC diagnostic at the proper location. */
location_t loc = UNKNOWN_LOCATION;
if (p != NO_NODE)
loc = a68_get_node_location (p);
else if (line != NO_LINE)
{
if (pos == NO_TEXT)
pos = STRING (line);
loc = a68_get_line_location (line, pos);
}
/* Prepare rich location and diagnostics. */
rich_location rich_loc (line_table, loc);
diagnostics::diagnostic_info diagnostic;
enum diagnostics::kind kind;
switch (sev)
{
case A68_FATAL:
kind = diagnostics::kind::fatal;
break;
case A68_INFORM:
kind = diagnostics::kind::note;
break;
case A68_WARNING:
kind = diagnostics::kind::warning;
break;
case A68_SCAN_ERROR:
case A68_ERROR:
kind = diagnostics::kind::error;
break;
default:
gcc_unreachable ();
}
diagnostic_set_info (&diagnostic, format,
&argp,
&rich_loc, kind);
if (opt != 0)
diagnostic.m_option_id = opt;
res = diagnostic_report_diagnostic (global_dc, &diagnostic);
if (sev == A68_SCAN_ERROR)
exit (FATAL_EXIT_CODE);
return res;
}
/* Give an intelligible error and exit. A line is provided rather than a
node so this can be used at scanning time. */
void
a68_scan_error (LINE_T * u, char *v, const char *txt, ...)
{
va_list args;
va_start (args, txt);
diagnostic (A68_SCAN_ERROR, 0, NO_NODE, u, v, txt, args);
va_end (args);
}
/* Report a compilation error. */
void
a68_error (NODE_T *p, const char *loc_str, ...)
{
va_list args;
va_start (args, loc_str);
diagnostic (A68_ERROR, 0, p, NO_LINE, NO_TEXT, loc_str, args);
va_end (args);
}
/* Report a compilation error in a node's pragmat. */
void
a68_error_in_pragmat (NODE_T *p, size_t off,
const char *loc_str, ...)
{
va_list args;
LINE_T *line = NPRAGMAT_LINE (p);
char *pos = NPRAGMAT_CHAR_IN_LINE (p) + off;
va_start (args, loc_str);
diagnostic (A68_ERROR, 0, NO_NODE, line, pos, loc_str, args);
va_end (args);
a68_inform (p, "pragmat applies to this construct");
}
/* Report a compilation warning.
This function returns a boolean indicating whether a warning was
emitted. */
bool
a68_warning (NODE_T *p, int opt,
const char *loc_str, ...)
{
bool res;
va_list args;
va_start (args, loc_str);
res = diagnostic (A68_WARNING, opt, p, NO_LINE, NO_TEXT, loc_str, args);
va_end (args);
return res;
}
/* Report a compilation note. */
void
a68_inform (NODE_T *p, const char *loc_str, ...)
{
va_list args;
va_start (args, loc_str);
diagnostic (A68_INFORM, 0, p, NO_LINE, NO_TEXT, loc_str, args);
va_end (args);
}