(root)/
bison-3.8.2/
src/
fixits.c
       1  /* Support for fixing grammar files.
       2  
       3     Copyright (C) 2019-2021 Free Software Foundation, Inc.
       4  
       5     This file is part of Bison, the GNU Compiler Compiler.
       6  
       7     This program is free software: you can redistribute it and/or modify
       8     it under the terms of the GNU General Public License as published by
       9     the Free Software Foundation, either version 3 of the License, or
      10     (at your option) any later version.
      11  
      12     This program is distributed in the hope that it will be useful,
      13     but WITHOUT ANY WARRANTY; without even the implied warranty of
      14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15     GNU General Public License for more details.
      16  
      17     You should have received a copy of the GNU General Public License
      18     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      19  
      20  #include <config.h>
      21  
      22  #include "fixits.h"
      23  
      24  #include <error.h>
      25  #include <get-errno.h>
      26  #include <gl_array_list.h>
      27  #include <gl_xlist.h>
      28  #include <progname.h>
      29  #include <quote.h>
      30  #include <quotearg.h>
      31  #include <vasnprintf.h>
      32  
      33  #include "system.h"
      34  
      35  #include "files.h"
      36  #include "getargs.h"
      37  
      38  typedef struct
      39  {
      40    location location;
      41    char *fix;
      42  } fixit;
      43  
      44  gl_list_t fixits = NULL;
      45  
      46  static fixit *
      47  fixit_new (location const *loc, char const* fix)
      48  {
      49    fixit *res = xmalloc (sizeof *res);
      50    res->location = *loc;
      51    res->fix = xstrdup (fix);
      52    return res;
      53  }
      54  
      55  static int
      56  fixit_cmp (const  fixit *a, const fixit *b)
      57  {
      58    return location_cmp (a->location, b->location);
      59  }
      60  
      61  static void
      62  fixit_free (fixit *f)
      63  {
      64    free (f->fix);
      65    free (f);
      66  }
      67  
      68  
      69  /* GCC and Clang follow the same pattern.
      70     https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Message-Formatting-Options.html
      71     https://clang.llvm.org/docs/UsersManual.html#cmdoption-fdiagnostics-parseable-fixits */
      72  static void
      73  fixit_print (fixit const *f, FILE *out)
      74  {
      75    fprintf (out, "fix-it:%s:{%d:%d-%d:%d}:%s\n",
      76             quotearg_n_style (1, c_quoting_style, f->location.start.file),
      77             f->location.start.line, f->location.start.byte,
      78             f->location.end.line, f->location.end.byte,
      79             quotearg_n_style (2, c_quoting_style, f->fix));
      80  }
      81  
      82  
      83  void
      84  fixits_register (location const *loc, char const* fix)
      85  {
      86    if (!fixits)
      87      fixits = gl_list_create_empty (GL_ARRAY_LIST,
      88                                     /* equals */ NULL,
      89                                     /* hashcode */ NULL,
      90                                     (gl_listelement_dispose_fn) fixit_free,
      91                                     true);
      92    fixit *f = fixit_new (loc, fix);
      93    gl_sortedlist_add (fixits, (gl_listelement_compar_fn) fixit_cmp, f);
      94    if (feature_flag & feature_fixit)
      95      fixit_print (f, stderr);
      96  }
      97  
      98  
      99  bool
     100  fixits_empty (void)
     101  {
     102    return !fixits;
     103  }
     104  
     105  
     106  void
     107  fixits_run (void)
     108  {
     109    if (!fixits)
     110      return;
     111  
     112    /* This is not unlike what is done in location_caret.  */
     113    uniqstr input = ((fixit *) gl_list_get_at (fixits, 0))->location.start.file;
     114    /* Backup the file. */
     115    char buf[256];
     116    size_t len = sizeof (buf);
     117    char *backup = asnprintf (buf, &len, "%s~", input);
     118    if (!backup)
     119      xalloc_die ();
     120    if (rename (input, backup))
     121      error (EXIT_FAILURE, get_errno (),
     122             _("%s: cannot backup"), quotearg_colon (input));
     123    FILE *in = xfopen (backup, "r");
     124    FILE *out = xfopen (input, "w");
     125    size_t line = 1;
     126    size_t offset = 1;
     127    void const *p = NULL;
     128    gl_list_iterator_t iter = gl_list_iterator (fixits);
     129    while (gl_list_iterator_next (&iter, &p, NULL))
     130      {
     131        fixit const *f = p;
     132        /* Look for the correct line. */
     133        while (line < f->location.start.line)
     134          {
     135            int c = getc (in);
     136            if (c == EOF)
     137              break;
     138            if (c == '\n')
     139              {
     140                ++line;
     141                offset = 1;
     142              }
     143            putc (c, out);
     144          }
     145  
     146        /* Look for the right offset. */
     147        bool need_eol = false;
     148        while (offset < f->location.start.byte)
     149          {
     150            int c = getc (in);
     151            if (c == EOF)
     152              break;
     153            ++offset;
     154            if (c == '\n')
     155              /* The position we are asked for is beyond the actual
     156                 line: pad with spaces, and remember we need a \n.  */
     157              need_eol = true;
     158            putc (need_eol ? ' ' : c, out);
     159          }
     160  
     161        /* Paste the fix instead. */
     162        fputs (f->fix, out);
     163  
     164        /* Maybe install the eol afterwards.  */
     165        if (need_eol)
     166          putc ('\n', out);
     167  
     168        /* Skip the bad input. */
     169        while (line < f->location.end.line)
     170          {
     171            int c = getc (in);
     172            if (c == EOF)
     173              break;
     174            if (c == '\n')
     175              {
     176                ++line;
     177                offset = 1;
     178              }
     179          }
     180        while (offset < f->location.end.byte)
     181          {
     182            int c = getc (in);
     183            if (c == EOF)
     184              break;
     185            ++offset;
     186          }
     187  
     188        /* If erasing the content of a full line, also remove the
     189           end-of-line. */
     190        if (f->fix[0] == 0 && f->location.start.byte == 1)
     191          {
     192            int c = getc (in);
     193            if (c == EOF)
     194              break;
     195            else if (c == '\n')
     196              {
     197                ++line;
     198                offset = 1;
     199              }
     200            else
     201              ungetc (c, in);
     202          }
     203      }
     204    /* Paste the rest of the file.  */
     205    {
     206      int c;
     207      while ((c = getc (in)) != EOF)
     208        putc (c, out);
     209    }
     210  
     211    gl_list_iterator_free (&iter);
     212    xfclose (out);
     213    xfclose (in);
     214    fprintf (stderr, "%s: file %s was updated (backup: %s)\n",
     215             program_name, quote_n (0, input), quote_n (1, backup));
     216    if (backup != buf)
     217      free (backup);
     218  }
     219  
     220  
     221  /* Free the registered fixits.  */
     222  void fixits_free (void)
     223  {
     224    if (fixits)
     225      {
     226        gl_list_free (fixits);
     227        fixits = NULL;
     228      }
     229  }