/* Declaration for error-reporting function for Bison.
Copyright (C) 2000-2002, 2004-2006, 2009-2013 Free Software
Foundation, Inc.
This program 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 of the License, or
(at your option) any later version.
This program 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 this program. If not, see . */
/* Based on error.c and error.h,
written by David MacKenzie . */
#include
#include "system.h"
#include
#include
#include
#include "complain.h"
#include "files.h"
#include "getargs.h"
#include "quote.h"
err_status complaint_status = status_none;
bool warnings_are_errors = false;
/** Whether -Werror/-Wno-error was applied to a warning. */
typedef enum
{
errority_unset = 0, /** No explict status. */
errority_disabled = 1, /** Explictly disabled with -Wno-error=foo. */
errority_enabled = 2 /** Explictly enabled with -Werror=foo. */
} errority;
/** For each warning type, its errority. */
static errority errority_flag[warnings_size];
/** Diagnostics severity. */
typedef enum
{
severity_disabled = 0, /**< Explicitly disabled via -Wno-foo. */
severity_unset = 1, /**< Unspecified status. */
severity_warning = 2, /**< A warning. */
severity_error = 3, /**< An error (continue, but die soon). */
severity_fatal = 4 /**< Fatal error (die now). */
} severity;
/** For each warning type, its severity. */
static severity warnings_flag[warnings_size];
static unsigned *indent_ptr = 0;
/*------------------------.
| --warnings's handling. |
`------------------------*/
static const char * const warnings_args[] =
{
"none",
"midrule-values",
"yacc",
"conflicts-sr",
"conflicts-rr",
"deprecated",
"empty-rule",
"precedence",
"other",
"all",
"error",
"everything",
0
};
static const int warnings_types[] =
{
Wnone,
Wmidrule_values,
Wyacc,
Wconflicts_sr,
Wconflicts_rr,
Wdeprecated,
Wempty_rule,
Wprecedence,
Wother,
Wall,
Werror,
Weverything
};
ARGMATCH_VERIFY (warnings_args, warnings_types);
void
warning_argmatch (char const *arg, size_t no, size_t err)
{
int value = XARGMATCH ("--warning", arg + no + err,
warnings_args, warnings_types);
/* -Wnone == -Wno-everything, and -Wno-none == -Weverything. */
if (!value)
{
value = Weverything;
no = !no;
}
size_t b;
for (b = 0; b < warnings_size; ++b)
if (value & 1 << b)
{
if (err && no)
/* -Wno-error=foo. */
errority_flag[b] = errority_disabled;
else if (err && !no)
{
/* -Werror=foo: enables -Wfoo. */
errority_flag[b] = errority_enabled;
warnings_flag[b] = severity_warning;
}
else if (no)
/* -Wno-foo. */
warnings_flag[b] = severity_disabled;
else
/* -Wfoo. */
warnings_flag[b] = severity_warning;
}
}
/** Decode a comma-separated list of arguments from -W.
*
* \param args comma separated list of effective subarguments to decode.
* If 0, then activate all the flags.
*/
void
warnings_argmatch (char *args)
{
if (args)
for (args = strtok (args, ","); args; args = strtok (NULL, ","))
if (STREQ (args, "error"))
warnings_are_errors = true;
else if (STREQ (args, "no-error"))
warnings_are_errors = false;
else
{
// The length of the possible 'no-' prefix: 3, or 0.
size_t no = STRPREFIX_LIT ("no-", args) ? 3 : 0;
// The length of the possible 'error=' (possibly after
// 'no-') prefix: 6, or 0.
size_t err = STRPREFIX_LIT ("error=", args + no) ? 6 : 0;
warning_argmatch (args, no, err);
}
else
warning_argmatch ("all", 0, 0);
}
/*-----------.
| complain. |
`-----------*/
void
complain_init (void)
{
warnings warnings_default =
Wconflicts_sr | Wconflicts_rr | Wdeprecated | Wother;
size_t b;
for (b = 0; b < warnings_size; ++b)
{
warnings_flag[b] = (1 << b & warnings_default
? severity_warning
: severity_unset);
errority_flag[b] = errority_unset;
}
}
/* A diagnostic with FLAGS is about to be issued. With what severity?
(severity_fatal, severity_error, severity_disabled, or
severity_warning.) */
static severity
warning_severity (warnings flags)
{
if (flags & fatal)
/* Diagnostics about fatal errors. */
return severity_fatal;
else if (flags & complaint)
/* Diagnostics about errors. */
return severity_error;
else
{
/* Diagnostics about warnings. */
severity res = severity_disabled;
size_t b;
for (b = 0; b < warnings_size; ++b)
if (flags & 1 << b)
{
res = res < warnings_flag[b] ? warnings_flag[b] : res;
/* If the diagnostic is enabled, and -Werror is enabled,
and -Wno-error=foo was not explicitly requested, this
is an error. */
if (res == severity_warning
&& (errority_flag[b] == errority_enabled
|| (warnings_are_errors
&& errority_flag[b] != errority_disabled)))
res = severity_error;
}
return res;
}
}
bool
warning_is_unset (warnings flags)
{
size_t b;
for (b = 0; b < warnings_size; ++b)
if (flags & 1 << b && warnings_flag[b] != severity_unset)
return false;
return true;
}
/** Display a "[-Wyacc]" like message on \a f. */
static void
warnings_print_categories (warnings warn_flags, FILE *f)
{
/* Display only the first match, the second is "-Wall". */
size_t i;
for (i = 0; warnings_args[i]; ++i)
if (warn_flags & warnings_types[i])
{
severity s = warning_severity (warnings_types[i]);
fprintf (f, " [-W%s%s]",
s == severity_error ? "error=" : "",
warnings_args[i]);
return;
}
}
/** Report an error message.
*
* \param loc the location, defaulting to the current file,
* or the program name.
* \param flags the category for this message.
* \param prefix put before the message (e.g., "warning").
* \param message the error message, a printf format string. Iff it
* ends with ": ", then no trailing newline is printed,
* and the caller should print the remaining
* newline-terminated message to stderr.
* \param args the arguments of the format string.
*/
static
void
error_message (const location *loc, warnings flags, const char *prefix,
const char *message, va_list args)
{
unsigned pos = 0;
if (loc)
pos += location_print (*loc, stderr);
else
pos += fprintf (stderr, "%s", current_file ? current_file : program_name);
pos += fprintf (stderr, ": ");
if (indent_ptr)
{
if (*indent_ptr)
prefix = NULL;
if (!*indent_ptr)
*indent_ptr = pos;
else if (*indent_ptr > pos)
fprintf (stderr, "%*s", *indent_ptr - pos, "");
indent_ptr = 0;
}
if (prefix)
fprintf (stderr, "%s: ", prefix);
vfprintf (stderr, message, args);
if (! (flags & silent))
warnings_print_categories (flags, stderr);
{
size_t l = strlen (message);
if (l < 2 || message[l - 2] != ':' || message[l - 1] != ' ')
{
putc ('\n', stderr);
fflush (stderr);
if (loc && feature_flag & feature_caret && !(flags & no_caret))
location_caret (*loc, stderr);
}
}
fflush (stderr);
}
/** Raise a complaint. That can be a fatal error, an error or just a
warning. */
static void
complains (const location *loc, warnings flags, const char *message,
va_list args)
{
severity s = warning_severity (flags);
if ((flags & complaint) && complaint_status < status_complaint)
complaint_status = status_complaint;
if (severity_warning <= s)
{
const char* prefix =
s == severity_fatal ? _("fatal error")
: s == severity_error ? _("error")
: _("warning");
if (severity_error <= s && ! complaint_status)
complaint_status = status_warning_as_error;
error_message (loc, flags, prefix, message, args);
}
if (flags & fatal)
exit (EXIT_FAILURE);
}
void
complain (location const *loc, warnings flags, const char *message, ...)
{
va_list args;
va_start (args, message);
complains (loc, flags, message, args);
va_end (args);
}
void
complain_indent (location const *loc, warnings flags, unsigned *indent,
const char *message, ...)
{
va_list args;
indent_ptr = indent;
va_start (args, message);
complains (loc, flags, message, args);
va_end (args);
}
void
complain_args (location const *loc, warnings w, unsigned *indent,
int argc, char *argv[])
{
switch (argc)
{
case 1:
complain_indent (loc, w, indent, "%s", _(argv[0]));
break;
case 2:
complain_indent (loc, w, indent, _(argv[0]), argv[1]);
break;
case 3:
complain_indent (loc, w, indent, _(argv[0]), argv[1], argv[2]);
break;
case 4:
complain_indent (loc, w, indent, _(argv[0]), argv[1], argv[2], argv[3]);
break;
case 5:
complain_indent (loc, w, indent, _(argv[0]), argv[1], argv[2], argv[3],
argv[4]);
break;
default:
complain (loc, fatal, "too many arguments for complains");
break;
}
}
void
deprecated_directive (location const *loc, char const *old, char const *upd)
{
if (feature_flag & feature_caret)
complain (loc, Wdeprecated,
_("deprecated directive, use %s"),
quote_n (1, upd));
else
complain (loc, Wdeprecated,
_("deprecated directive: %s, use %s"),
quote (old), quote_n (1, upd));
}
void
duplicate_directive (char const *directive,
location first, location second)
{
unsigned i = 0;
complain (&second, complaint, _("only one %s allowed per rule"), directive);
i += SUB_INDENT;
complain_indent (&first, complaint, &i, _("previous declaration"));
}