(root)/
bison-3.8.2/
src/
complain.c
       1  /* Declaration for error-reporting function for Bison.
       2  
       3     Copyright (C) 2000-2002, 2004-2006, 2009-2015, 2018-2021 Free
       4     Software Foundation, Inc.
       5  
       6     This program is free software: you can redistribute it and/or modify
       7     it under the terms of the GNU General Public License as published by
       8     the Free Software Foundation, either version 3 of the License, or
       9     (at your option) any later version.
      10  
      11     This program is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU General Public License for more details.
      15  
      16     You should have received a copy of the GNU General Public License
      17     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  /* Based on error.c and error.h,
      20     written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
      21  
      22  #include <config.h>
      23  #include "system.h"
      24  
      25  #include <argmatch.h>
      26  #include <c-ctype.h>
      27  #include <progname.h>
      28  #include <stdarg.h>
      29  #include <sys/stat.h>
      30  #include <textstyle.h>
      31  
      32  #include "complain.h"
      33  #include "files.h"
      34  #include "fixits.h"
      35  #include "getargs.h"
      36  #include "quote.h"
      37  
      38  // The URL of the manual page about diagnostics.  Use the per-node
      39  // manual, to avoid downloading repeatedly the whole manual over the
      40  // Internet.
      41  static const char *diagnostics_url
      42    = "https://www.gnu.org/software/bison/manual/html_node/Diagnostics.html";
      43  
      44  
      45  err_status complaint_status = status_none;
      46  
      47  bool warnings_are_errors = false;
      48  
      49  /** Whether -Werror/-Wno-error was applied to a warning.  */
      50  typedef enum
      51    {
      52      errority_unset = 0,     /** No explicit status.  */
      53      errority_disabled = 1,  /** Explicitly disabled with -Wno-error=foo.  */
      54      errority_enabled = 2    /** Explicitly enabled with -Werror=foo. */
      55    } errority;
      56  
      57  /** For each warning type, its errority.  */
      58  static errority errority_flag[warnings_size];
      59  
      60  /** Diagnostics severity.  */
      61  typedef enum
      62    {
      63      severity_disabled = 0, /**< Explicitly disabled via -Wno-foo.  */
      64      severity_unset = 1,    /**< Unspecified status.  */
      65      severity_warning = 2,  /**< A warning.  */
      66      severity_error = 3,    /**< An error (continue, but die soon).  */
      67      severity_fatal = 4     /**< Fatal error (die now).  */
      68    } severity;
      69  
      70  
      71  /** For each warning type, its severity.  */
      72  static severity warnings_flag[warnings_size];
      73  
      74  styled_ostream_t errstream = NULL;
      75  
      76  void
      77  begin_use_class (const char *s, FILE *out)
      78  {
      79    if (out == stderr)
      80      {
      81        if (color_debug)
      82          fprintf (out, "<%s>", s);
      83        else
      84          {
      85            styled_ostream_begin_use_class (errstream, s);
      86            styled_ostream_flush_to_current_style (errstream);
      87          }
      88      }
      89  }
      90  
      91  void
      92  end_use_class (const char *s, FILE *out)
      93  {
      94    if (out == stderr)
      95      {
      96        if (color_debug)
      97          fprintf (out, "</%s>", s);
      98        else
      99          {
     100            styled_ostream_end_use_class (errstream, s);
     101            styled_ostream_flush_to_current_style (errstream);
     102          }
     103      }
     104  }
     105  
     106  static void
     107  begin_hyperlink (FILE *out, const char *ref)
     108  {
     109    if (out == stderr)
     110      styled_ostream_set_hyperlink (errstream, ref, NULL);
     111  }
     112  
     113  static void
     114  end_hyperlink (FILE *out)
     115  {
     116    if (out == stderr)
     117      styled_ostream_set_hyperlink (errstream, NULL, NULL);
     118  }
     119  
     120  void
     121  flush (FILE *out)
     122  {
     123    if (out == stderr)
     124      ostream_flush (errstream, FLUSH_THIS_STREAM);
     125    fflush (out);
     126  }
     127  
     128  bool
     129  is_styled (FILE *out)
     130  {
     131    if (out != stderr)
     132      return false;
     133    if (color_debug)
     134      return true;
     135  #if HAVE_LIBTEXTSTYLE
     136    return (color_mode == color_yes
     137            || color_mode == color_html
     138            || (color_mode == color_tty && isatty (STDERR_FILENO)));
     139  #else
     140    return false;
     141  #endif
     142  }
     143  
     144  
     145  /*------------------------.
     146  | --warnings's handling.  |
     147  `------------------------*/
     148  
     149  ARGMATCH_DEFINE_GROUP (warning, warnings)
     150  
     151  static const argmatch_warning_doc argmatch_warning_docs[] =
     152  {
     153    { "conflicts-sr",     N_("S/R conflicts (enabled by default)") },
     154    { "conflicts-rr",     N_("R/R conflicts (enabled by default)") },
     155    { "counterexamples",  N_("generate conflict counterexamples") },
     156    { "dangling-alias",   N_("string aliases not attached to a symbol") },
     157    { "deprecated",       N_("obsolete constructs") },
     158    { "empty-rule",       N_("empty rules without %empty") },
     159    { "midrule-values",   N_("unset or unused midrule values") },
     160    { "precedence",       N_("useless precedence and associativity") },
     161    { "yacc",             N_("incompatibilities with POSIX Yacc") },
     162    { "other",            N_("all other warnings (enabled by default)") },
     163    { "all",              N_("all the warnings except 'counterexamples', 'dangling-alias' and 'yacc'") },
     164    { "no-CATEGORY",      N_("turn off warnings in CATEGORY") },
     165    { "none",             N_("turn off all the warnings") },
     166    { "error[=CATEGORY]", N_("treat warnings as errors") },
     167    { NULL, NULL }
     168  };
     169  
     170  static const argmatch_warning_arg argmatch_warning_args[] =
     171  {
     172    { "all",             Wall },
     173    { "conflicts-rr",    Wconflicts_rr },
     174    { "conflicts-sr",    Wconflicts_sr },
     175    { "counterexamples", Wcounterexamples }, { "cex", Wcounterexamples }, // Show cex second.
     176    { "dangling-alias",  Wdangling_alias },
     177    { "deprecated",      Wdeprecated },
     178    { "empty-rule",      Wempty_rule },
     179    { "everything",      Weverything },
     180    { "midrule-values",  Wmidrule_values },
     181    { "none",            Wnone },
     182    { "other",           Wother },
     183    { "precedence",      Wprecedence },
     184    { "yacc",            Wyacc },
     185    { NULL, Wnone }
     186  };
     187  
     188  const argmatch_warning_group_type argmatch_warning_group =
     189  {
     190    argmatch_warning_args,
     191    argmatch_warning_docs,
     192    N_("Warning categories include:"),
     193    NULL
     194  };
     195  
     196  void
     197  warning_usage (FILE *out)
     198  {
     199    argmatch_warning_usage (out);
     200  }
     201  
     202  void
     203  warning_argmatch (char const *arg, size_t no, size_t err)
     204  {
     205    int value = *argmatch_warning_value ("--warning", arg + no + err);
     206  
     207    /* -Wnone == -Wno-everything, and -Wno-none == -Weverything.  */
     208    if (!value)
     209      {
     210        value = Weverything;
     211        no = !no;
     212      }
     213  
     214    for (size_t b = 0; b < warnings_size; ++b)
     215      if (value & 1 << b)
     216        {
     217          if (err && no)
     218            /* -Wno-error=foo.  */
     219            errority_flag[b] = errority_disabled;
     220          else if (err && !no)
     221            {
     222              /* -Werror=foo: enables -Wfoo. */
     223              errority_flag[b] = errority_enabled;
     224              warnings_flag[b] = severity_warning;
     225            }
     226          else if (no)
     227            /* -Wno-foo.  */
     228            warnings_flag[b] = severity_disabled;
     229          else
     230            /* -Wfoo. */
     231            warnings_flag[b] = severity_warning;
     232        }
     233  }
     234  
     235  /** Decode a comma-separated list of arguments from -W.
     236   *
     237   *  \param args     comma separated list of effective subarguments to decode.
     238   *                  If 0, then activate all the flags.
     239   */
     240  
     241  void
     242  warnings_argmatch (char *args)
     243  {
     244    if (!args)
     245      warning_argmatch ("all", 0, 0);
     246    else if (STREQ (args, "help"))
     247      {
     248        warning_usage (stdout);
     249        exit (EXIT_SUCCESS);
     250      }
     251    else
     252      for (args = strtok (args, ","); args; args = strtok (NULL, ","))
     253        if (STREQ (args, "error"))
     254          warnings_are_errors = true;
     255        else if (STREQ (args, "no-error"))
     256          warnings_are_errors = false;
     257        else
     258          {
     259            /* The length of the possible 'no-' prefix: 3, or 0.  */
     260            size_t no = STRPREFIX_LIT ("no-", args) ? 3 : 0;
     261            /* The length of the possible 'error=' (possibly after
     262               'no-') prefix: 6, or 0. */
     263            size_t err = STRPREFIX_LIT ("error=", args + no) ? 6 : 0;
     264  
     265            warning_argmatch (args, no, err);
     266          }
     267  }
     268  
     269  /* Color style for this type of message.  */
     270  static const char*
     271  severity_style (severity s)
     272  {
     273    switch (s)
     274      {
     275      case severity_disabled:
     276      case severity_unset:
     277        return "note";
     278      case severity_warning:
     279        return "warning";
     280      case severity_error:
     281      case severity_fatal:
     282        return "error";
     283      }
     284    abort ();
     285  }
     286  
     287  /* Prefix for this type of message.  */
     288  static const char*
     289  severity_prefix (severity s)
     290  {
     291    switch (s)
     292      {
     293      case severity_disabled:
     294      case severity_unset:
     295        return "";
     296      case severity_warning:
     297        return _("warning");
     298      case severity_error:
     299        return  _("error");
     300      case severity_fatal:
     301        return _("fatal error");
     302      }
     303    abort ();
     304  }
     305  
     306  
     307  static void
     308  severity_print (severity s, FILE *out)
     309  {
     310    if (s != severity_disabled)
     311      {
     312        const char* style = severity_style (s);
     313        begin_use_class (style, out);
     314        fprintf (out, "%s:", severity_prefix (s));
     315        end_use_class (style, out);
     316        fputc (' ', out);
     317      }
     318  }
     319  
     320  
     321  /*-----------.
     322  | complain.  |
     323  `-----------*/
     324  
     325  void
     326  complain_init_color (void)
     327  {
     328  #if HAVE_LIBTEXTSTYLE
     329    if (is_styled (stderr))
     330      {
     331        style_file_prepare ("BISON_STYLE", "BISON_STYLEDIR", pkgdatadir (),
     332                            "bison-default.css");
     333        /* As a fallback, use the default in the current directory.  */
     334        struct stat statbuf;
     335        if ((style_file_name == NULL || stat (style_file_name, &statbuf) < 0)
     336            && stat ("bison-default.css", &statbuf) == 0)
     337          style_file_name = "bison-default.css";
     338      }
     339    else
     340      /* No styling.  */
     341      style_file_name = NULL;
     342  #endif
     343  
     344    /* Workaround clang's warning (starting at Clang 3.5) about the stub
     345       code of html_styled_ostream_create:
     346  
     347       | src/complain.c:274:7: error: code will never be executed [-Werror,-Wunreachable-code]
     348       |     ? html_styled_ostream_create (file_ostream_create (stderr),
     349       |       ^~~~~~~~~~~~~~~~~~~~~~~~~~ */
     350  #if defined __clang__
     351  # pragma clang diagnostic push
     352  # pragma clang diagnostic ignored "-Wunreachable-code"
     353  #endif
     354    errstream =
     355      color_mode == color_html
     356      ? html_styled_ostream_create (file_ostream_create (stderr),
     357                                    style_file_name)
     358      : styled_ostream_create (STDERR_FILENO, "(stderr)", TTYCTL_AUTO,
     359                               style_file_name);
     360  #if defined __clang__
     361  # pragma clang diagnostic pop
     362  #endif
     363  }
     364  
     365  void
     366  complain_init (void)
     367  {
     368    caret_init ();
     369  
     370    warnings warnings_default =
     371      Wconflicts_sr | Wconflicts_rr | Wdeprecated | Wother;
     372  
     373    for (size_t b = 0; b < warnings_size; ++b)
     374      {
     375        warnings_flag[b] = (1 << b & warnings_default
     376                            ? severity_warning
     377                            : severity_unset);
     378        errority_flag[b] = errority_unset;
     379      }
     380  }
     381  
     382  void
     383  complain_free (void)
     384  {
     385    caret_free ();
     386    styled_ostream_free (errstream);
     387  }
     388  
     389  /* A diagnostic with FLAGS is about to be issued.  With what severity?
     390     (severity_fatal, severity_error, severity_disabled, or
     391     severity_warning.) */
     392  
     393  static severity
     394  warning_severity (warnings flags)
     395  {
     396    if (flags & fatal)
     397      /* Diagnostics about fatal errors.  */
     398      return severity_fatal;
     399    else if (flags & complaint)
     400      /* Diagnostics about errors.  */
     401      return severity_error;
     402    else
     403      {
     404        /* Diagnostics about warnings.  */
     405        severity res = severity_disabled;
     406        for (size_t b = 0; b < warnings_size; ++b)
     407          if (flags & 1 << b)
     408            {
     409              res = res < warnings_flag[b] ? warnings_flag[b] : res;
     410              /* If the diagnostic is enabled, and -Werror is enabled,
     411                 and -Wno-error=foo was not explicitly requested, this
     412                 is an error. */
     413              if (res == severity_warning
     414                  && (errority_flag[b] == errority_enabled
     415                      || (warnings_are_errors
     416                          && errority_flag[b] != errority_disabled)))
     417                res = severity_error;
     418            }
     419        return res;
     420      }
     421  }
     422  
     423  bool
     424  warning_is_unset (warnings flags)
     425  {
     426    for (size_t b = 0; b < warnings_size; ++b)
     427      if (flags & 1 << b && warnings_flag[b] != severity_unset)
     428        return false;
     429    return true;
     430  }
     431  
     432  bool
     433  warning_is_enabled (warnings flags)
     434  {
     435    return severity_warning <= warning_severity (flags);
     436  }
     437  
     438  /** Display a "[-Wyacc]" like message on \a out.  */
     439  
     440  static void
     441  warnings_print_categories (warnings warn_flags, FILE *out)
     442  {
     443    for (int wbit = 0; wbit < warnings_size; ++wbit)
     444      if (warn_flags & (1 << wbit))
     445        {
     446          warnings w = 1 << wbit;
     447          severity s = warning_severity (w);
     448          const char* style = severity_style (s);
     449          fputs (" [", out);
     450          begin_use_class (style, out);
     451          // E.g., "counterexamples".
     452          const char *warning = argmatch_warning_argument (&w);
     453          char ref[200];
     454          snprintf (ref, sizeof ref,
     455                    "%s#W%s", diagnostics_url, warning);
     456          begin_hyperlink (out, ref);
     457          ostream_printf (errstream,
     458                          "-W%s%s",
     459                          s == severity_error ? "error=" : "",
     460                          warning);
     461          end_hyperlink (out);
     462          // Because we mix stdio with ostream I/O, we need to flush
     463          // here for sake of color == debug.
     464          flush (out);
     465          end_use_class (style, out);
     466          fputc (']', out);
     467          /* Display only the first match, the second is "-Wall".  */
     468          return;
     469        }
     470  }
     471  
     472  /** Report an error message.
     473   *
     474   * \param loc     the location, defaulting to the current file,
     475   *                or the program name.
     476   * \param flags   the category for this message.
     477   * \param sever   to decide the prefix to put before the message
     478   *                (e.g., "warning").
     479   * \param message the error message, a printf format string.  Iff it
     480   *                ends with ": ", then no trailing newline is printed,
     481   *                and the caller should print the remaining
     482   *                newline-terminated message to stderr.
     483   * \param args    the arguments of the format string.
     484   */
     485  static
     486  void
     487  error_message (const location *loc, warnings flags,
     488                 severity sever, const char *message, va_list args)
     489  {
     490    const char* style = flags & note ? "note" : severity_style (sever);
     491  
     492    if (loc)
     493      location_print (*loc, stderr);
     494    else
     495      fprintf (stderr, "%s", grammar_file ? grammar_file : program_name);
     496    fprintf (stderr, ": ");
     497  
     498    if (sever != severity_disabled)
     499      {
     500        begin_use_class (style, stderr);
     501        fprintf (stderr, "%s:", flags & note ? _("note") : severity_prefix (sever));
     502        end_use_class (style, stderr);
     503        fputc (' ', stderr);
     504      }
     505  
     506    vfprintf (stderr, message, args);
     507    /* Print the type of warning, only if this is not a sub message
     508       (in which case the prefix is null).  */
     509    if (! (flags & silent) && sever != severity_disabled)
     510      warnings_print_categories (flags, stderr);
     511  
     512    size_t l = strlen (message);
     513    if (l < 2 || message[l - 2] != ':' || message[l - 1] != ' ')
     514      {
     515        putc ('\n', stderr);
     516        flush (stderr);
     517        if (loc && !(flags & no_caret))
     518          location_caret (*loc, style, stderr);
     519      }
     520    flush (stderr);
     521  }
     522  
     523  /** Raise a complaint (fatal error, error or just warning).  */
     524  
     525  static void
     526  complains (const location *loc, warnings flags,
     527             const char *message, va_list args)
     528  {
     529    if ((flags & complaint) && complaint_status < status_complaint)
     530      complaint_status = status_complaint;
     531  
     532    severity s = warning_severity (flags);
     533    if (severity_warning <= s)
     534      {
     535        if (severity_error <= s && ! complaint_status)
     536          complaint_status = status_warning_as_error;
     537        error_message (loc, flags, s, message, args);
     538      }
     539  
     540    if (flags & fatal)
     541      exit (EXIT_FAILURE);
     542  }
     543  
     544  void
     545  complain (location const *loc, warnings flags, const char *message, ...)
     546  {
     547    va_list args;
     548    va_start (args, message);
     549    complains (loc, flags, message, args);
     550    va_end (args);
     551  }
     552  
     553  void
     554  subcomplain (location const *loc, warnings flags, const char *message, ...)
     555  {
     556    va_list args;
     557    va_start (args, message);
     558    complains (loc, flags | note | silent, message, args);
     559    va_end (args);
     560  }
     561  
     562  void
     563  complain_args (location const *loc, warnings w,
     564                 int argc, char *argv[])
     565  {
     566    switch (argc)
     567    {
     568    case 1:
     569      complain (loc, w, "%s", _(argv[0]));
     570      break;
     571    case 2:
     572      complain (loc, w, _(argv[0]), argv[1]);
     573      break;
     574    case 3:
     575      complain (loc, w, _(argv[0]), argv[1], argv[2]);
     576      break;
     577    case 4:
     578      complain (loc, w, _(argv[0]), argv[1], argv[2], argv[3]);
     579      break;
     580    case 5:
     581      complain (loc, w, _(argv[0]), argv[1], argv[2], argv[3], argv[4]);
     582      break;
     583    default:
     584      complain (loc, fatal, "too many arguments for complains");
     585      break;
     586    }
     587  }
     588  
     589  
     590  void
     591  bison_directive (location const *loc, char const *directive)
     592  {
     593    complain (loc, Wyacc,
     594              _("POSIX Yacc does not support %s"), directive);
     595  }
     596  
     597  void
     598  deprecated_directive (location const *loc, char const *old, char const *upd)
     599  {
     600    if (warning_is_enabled (Wdeprecated))
     601      {
     602        complain (loc, Wdeprecated,
     603                  _("deprecated directive: %s, use %s"),
     604                  quote (old), quote_n (1, upd));
     605        location_caret_suggestion (*loc, upd, stderr);
     606        /* Register updates only if -Wdeprecated is enabled.  */
     607        fixits_register (loc, upd);
     608      }
     609  }
     610  
     611  void
     612  duplicate_directive (char const *directive,
     613                       location first, location second)
     614  {
     615    if (feature_flag & feature_caret)
     616      complain (&second, Wother, _("duplicate directive"));
     617    else
     618      complain (&second, Wother, _("duplicate directive: %s"), quote (directive));
     619    subcomplain (&first, Wother, _("previous declaration"));
     620    fixits_register (&second, "");
     621  }
     622  
     623  void
     624  duplicate_rule_directive (char const *directive,
     625                            location first, location second)
     626  {
     627    complain (&second, complaint, _("only one %s allowed per rule"), directive);
     628    subcomplain (&first, complaint, _("previous declaration"));
     629    fixits_register (&second, "");
     630  }
     631  
     632  void
     633  syntax_error (location loc,
     634                int argc, const char* argv[])
     635  {
     636    if (complaint_status < status_complaint)
     637      complaint_status = status_complaint;
     638    assert (argc <= 5);
     639    const char *format = NULL;
     640    switch (argc)
     641      {
     642  #define CASE(N, S)                          \
     643        case N:                               \
     644          format = S;                         \
     645          break
     646      default: /* Avoid compiler warnings. */
     647        CASE (0, _("syntax error"));
     648        CASE (1, _("unexpected %0$s"));
     649        CASE (2, _("expected %1$s before %0$s"));
     650        CASE (3, _("expected %1$s or %2$s before %0$s"));
     651        CASE (4, _("expected %1$s or %2$s or %3$s before %0$s"));
     652        CASE (5, _("expected %1$s or %2$s or %3$s or %4$s before %0$s"));
     653  #undef CASE
     654      }
     655    location_print (loc, stderr);
     656    fputs (": ", stderr);
     657    severity_print (severity_error, stderr);
     658  
     659    while (*format)
     660      if (format[0] == '%'
     661          && c_isdigit (format[1])
     662          && format[2] == '$'
     663          && format[3] == 's'
     664          && (format[1] - '0') < argc)
     665        {
     666          int i = format[1] - '0';
     667          const char *style = i == 0 ? "unexpected" : "expected";
     668          begin_use_class (style, stderr);
     669          fputs (argv[i], stderr);
     670          end_use_class (style, stderr);
     671          format += 4;
     672        }
     673      else
     674        {
     675          fputc (*format, stderr);
     676          ++format;
     677        }
     678    fputc ('\n', stderr);
     679    location_caret (loc, "error", stderr);
     680  }