LCOV - code coverage report
Current view: top level - gcc/diagnostics - changes.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 99.7 % 788 786
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 55 55
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Determining the results of applying fix-it hints.
       2              :    Copyright (C) 2016-2026 Free Software Foundation, Inc.
       3              : 
       4              : This file is part of GCC.
       5              : 
       6              : GCC is free software; you can redistribute it and/or modify it under
       7              : the terms of the GNU General Public License as published by the Free
       8              : Software Foundation; either version 3, or (at your option) any later
       9              : version.
      10              : 
      11              : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      12              : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13              : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14              : for more details.
      15              : 
      16              : You should have received a copy of the GNU General Public License
      17              : along with GCC; see the file COPYING3.  If not see
      18              : <http://www.gnu.org/licenses/>.  */
      19              : 
      20              : #include "config.h"
      21              : #include "system.h"
      22              : #include "coretypes.h"
      23              : #include "line-map.h"
      24              : #include "diagnostics/changes.h"
      25              : #include "pretty-print.h"
      26              : #include "diagnostics/color.h"
      27              : #include "diagnostics/file-cache.h"
      28              : #include "selftest.h"
      29              : 
      30              : namespace diagnostics {
      31              : namespace changes {
      32              : 
      33              : /* This file implements a way to track the effect of fix-its,
      34              :    via a class change_set; the other classes are support classes for
      35              :    change_set.
      36              : 
      37              :    A complication here is that fix-its are expressed relative to coordinates
      38              :    in the file when it was parsed, before any changes have been made, and
      39              :    so if there's more that one fix-it to be applied, we have to adjust
      40              :    later fix-its to allow for the changes made by earlier ones.  This
      41              :    is done by the various "get_effective_column" methods.
      42              : 
      43              :    The "filename" params are required to outlive the change_set (no
      44              :    copy of the underlying str is taken, just the ptr).  */
      45              : 
      46              : /* Forward decls.  class change_set is declared within changes.h.
      47              :    The other types are declared here.  */
      48              : class change_set;
      49              : class changed_file;
      50              : class changed_line;
      51              : class line_event;
      52              : 
      53              : /* A struct to hold the params of a print_diff call.  */
      54              : 
      55              : class diff
      56              : {
      57              : public:
      58         2717 :   diff (pretty_printer *pp, bool show_filenames)
      59         2717 :   : m_pp (pp), m_show_filenames (show_filenames) {}
      60              : 
      61              :   pretty_printer *m_pp;
      62              :   bool m_show_filenames;
      63              : };
      64              : 
      65              : /* The state of one named file within an change_set: the filename,
      66              :    and the lines that have been edited so far.  */
      67              : 
      68         3611 : class changed_file
      69              : {
      70              :  public:
      71              :   changed_file (change_set &ec, const char *filename);
      72              :   static void delete_cb (changed_file *file);
      73              : 
      74              :   const char *get_filename () const { return m_filename; }
      75              :   char *get_content ();
      76              : 
      77              :   bool apply_fixit (int line, int start_column,
      78              :                     int next_column,
      79              :                     const char *replacement_str,
      80              :                     int replacement_len);
      81              :   int get_effective_column (int line, int column);
      82              : 
      83         2645 :   static int call_print_diff (const char *, changed_file *file,
      84              :                               void *user_data)
      85              :   {
      86         2645 :     diff *d = (diff *)user_data;
      87         2645 :     file->print_diff (d->m_pp, d->m_show_filenames);
      88         2645 :     return 0;
      89              :   }
      90              : 
      91        89180 :   file_cache &get_file_cache () const
      92              :   {
      93        89180 :     return m_change_set.get_file_cache ();
      94              :   }
      95              : 
      96              :  private:
      97              :   bool print_content (pretty_printer *pp);
      98              :   void print_diff (pretty_printer *pp, bool show_filenames);
      99              :   int print_diff_hunk (pretty_printer *pp, int old_start_of_hunk,
     100              :                        int old_end_of_hunk, int new_start_of_hunk);
     101              :   changed_line *get_line (int line);
     102              :   changed_line *get_or_insert_line (int line);
     103              :   int get_num_lines (bool *missing_trailing_newline);
     104              : 
     105              :   int get_effective_line_count (int old_start_of_hunk,
     106              :                                 int old_end_of_hunk);
     107              : 
     108              :   void print_run_of_changed_lines (pretty_printer *pp,
     109              :                                    int start_of_run,
     110              :                                    int end_of_run);
     111              : 
     112              :   change_set &m_change_set;
     113              :   const char *m_filename;
     114              :   typed_splay_tree<int, changed_line *> m_changed_lines;
     115              :   int m_num_lines;
     116              : };
     117              : 
     118              : /* A line added before an changed_line.  */
     119              : 
     120              : class added_line
     121              : {
     122              :  public:
     123          142 :   added_line (const char *content, int len)
     124          284 :   : m_content (xstrndup (content, len)), m_len (len) {}
     125          142 :   ~added_line () { free (m_content); }
     126              : 
     127          206 :   const char *get_content () const { return m_content; }
     128          142 :   int get_len () const { return m_len; }
     129              : 
     130              :  private:
     131              :   char *m_content;
     132              :   int m_len;
     133              : };
     134              : 
     135              : /* Class for representing edit events that have occurred on one line of
     136              :    one file: the replacement of some text between some columns
     137              :    on the line.
     138              : 
     139              :    Subsequent events will need their columns adjusting if they're
     140              :    are on this line and their column is >= the start point.  */
     141              : 
     142              : class line_event
     143              : {
     144              :  public:
     145         3433 :   line_event (int start, int next, int len) : m_start (start),
     146         3433 :     m_delta (len - (next - start)) {}
     147              : 
     148         1860 :   int get_effective_column (int orig_column) const
     149              :   {
     150         1860 :     if (orig_column >= m_start)
     151         1220 :       return orig_column += m_delta;
     152              :     else
     153              :       return orig_column;
     154              :   }
     155              : 
     156              :  private:
     157              :   int m_start;
     158              :   int m_delta;
     159              : };
     160              : 
     161              : /* The state of one edited line within an changed_file.
     162              :    As well as the current content of the line, it contains a record of
     163              :    the changes, so that further changes can be applied in the correct
     164              :    place.
     165              : 
     166              :    When handling fix-it hints containing newlines, new lines are added
     167              :    as added_line predecessors to an changed_line.  Hence it's possible
     168              :    for an "changed_line" to not actually have been changed, but to merely
     169              :    be a placeholder for the lines added before it.  This can be tested
     170              :    for with actuall_edited_p, and has a slight effect on how diff hunks
     171              :    are generated.  */
     172              : 
     173              : class changed_line
     174              : {
     175              :  public:
     176              :   changed_line (file_cache &fc, const char *filename, int line_num);
     177              :   ~changed_line ();
     178              :   static void delete_cb (changed_line *el);
     179              : 
     180        11786 :   int get_line_num () const { return m_line_num; }
     181         3940 :   const char *get_content () const { return m_content; }
     182              :   int get_len () const { return m_len; }
     183              : 
     184              :   int get_effective_column (int orig_column) const;
     185              :   bool apply_fixit (int start_column,
     186              :                     int next_column,
     187              :                     const char *replacement_str,
     188              :                     int replacement_len);
     189              : 
     190              :   int get_effective_line_count () const;
     191              : 
     192              :   /* Has the content of this line actually changed, or are we merely
     193              :      recording predecessor added_lines?  */
     194        20885 :   bool actually_edited_p () const { return m_line_events.length () > 0; }
     195              : 
     196              :   void print_content (pretty_printer *pp) const;
     197              :   void print_diff_lines (pretty_printer *pp) const;
     198              : 
     199              :  private:
     200              :   void ensure_capacity (int len);
     201              :   void ensure_terminated ();
     202              : 
     203              :   int m_line_num;
     204              :   char *m_content;
     205              :   int m_len;
     206              :   int m_alloc_sz;
     207              :   auto_vec <line_event> m_line_events;
     208              :   auto_vec <added_line *> m_predecessors;
     209              : };
     210              : 
     211              : /* Forward decls.  */
     212              : 
     213              : static void
     214              : print_diff_line (pretty_printer *pp, char prefix_char,
     215              :                  const char *line, int line_size);
     216              : 
     217              : /* Implementation of class change_set.  */
     218              : 
     219              : /* change_set's ctor.  */
     220              : 
     221        10443 : change_set::change_set (file_cache &fc)
     222        10443 : : m_file_cache (fc),
     223        10443 :   m_valid (true),
     224        10443 :   m_files (strcmp, NULL, changed_file::delete_cb)
     225        10443 : {}
     226              : 
     227              : /* Add any fixits within RICHLOC to this context, recording the
     228              :    changes that they make.  */
     229              : 
     230              : void
     231        11368 : change_set::add_fixits (rich_location *richloc)
     232              : {
     233        11368 :   if (!m_valid)
     234              :     return;
     235        11136 :   if (richloc->seen_impossible_fixit_p ())
     236              :     {
     237         6768 :       m_valid = false;
     238         6768 :       return;
     239              :     }
     240         8633 :   for (unsigned i = 0; i < richloc->get_num_fixit_hints (); i++)
     241              :     {
     242         4265 :       const fixit_hint *hint = richloc->get_fixit_hint (i);
     243         4265 :       if (!apply_fixit (hint))
     244          690 :         m_valid = false;
     245              :     }
     246              : }
     247              : 
     248              : /* Get the content of the given file, with fix-its applied.
     249              :    If any errors occurred in this change_set, return NULL.
     250              :    The ptr should be freed by the caller.  */
     251              : 
     252              : char *
     253         1288 : change_set::get_content (const char *filename)
     254              : {
     255         1288 :   if (!m_valid)
     256              :     return NULL;
     257          844 :   changed_file &file = get_or_insert_file (filename);
     258          844 :   return file.get_content ();
     259              : }
     260              : 
     261              : /* Map a location before the edits to a column number after the edits.
     262              :    This method is for the selftests.  */
     263              : 
     264              : int
     265         1088 : change_set::get_effective_column (const char *filename, int line,
     266              :                                     int column)
     267              : {
     268         1088 :   changed_file *file = get_file (filename);
     269         1088 :   if (!file)
     270              :     return column;
     271         1088 :   return file->get_effective_column (line, column);
     272              : }
     273              : 
     274              : /* Generate a unified diff.  The resulting string should be freed by the
     275              :    caller.  Primarily for selftests.
     276              :    If any errors occurred in this change_set, return NULL.  */
     277              : 
     278              : char *
     279         9894 : change_set::generate_diff (bool show_filenames)
     280              : {
     281         9894 :   if (!m_valid)
     282              :     return NULL;
     283              : 
     284         2700 :   pretty_printer pp;
     285         2700 :   print_diff (&pp, show_filenames);
     286         2700 :   return xstrdup (pp_formatted_text (&pp));
     287         2700 : }
     288              : 
     289              : /* Print a unified diff to PP, showing the changes made within the
     290              :    context.  */
     291              : 
     292              : void
     293         2717 : change_set::print_diff (pretty_printer *pp, bool show_filenames)
     294              : {
     295         2717 :   if (!m_valid)
     296            0 :     return;
     297         2717 :   diff d (pp, show_filenames);
     298         2717 :   m_files.foreach (changed_file::call_print_diff, &d);
     299              : }
     300              : 
     301              : /* Attempt to apply the given fixit.  Return true if it can be
     302              :    applied, or false otherwise.  */
     303              : 
     304              : bool
     305         4265 : change_set::apply_fixit (const fixit_hint *hint)
     306              : {
     307         4265 :   expanded_location start = expand_location (hint->get_start_loc ());
     308         4265 :   expanded_location next_loc = expand_location (hint->get_next_loc ());
     309         4265 :   if (start.file != next_loc.file)
     310              :     return false;
     311         4265 :   if (start.line != next_loc.line)
     312              :     return false;
     313         4265 :   if (start.column == 0)
     314              :     return false;
     315         4265 :   if (next_loc.column == 0)
     316              :     return false;
     317              : 
     318         4265 :   changed_file &file = get_or_insert_file (start.file);
     319         4265 :   if (!m_valid)
     320              :     return false;
     321         4265 :   return file.apply_fixit (start.line, start.column, next_loc.column,
     322              :                            hint->get_string (),
     323         4265 :                            hint->get_length ());
     324              : }
     325              : 
     326              : /* Locate the changed_file * for FILENAME, if any
     327              :    Return NULL if there isn't one.  */
     328              : 
     329              : changed_file *
     330         6197 : change_set::get_file (const char *filename)
     331              : {
     332         6197 :   gcc_assert (filename);
     333         6197 :   return m_files.lookup (filename);
     334              : }
     335              : 
     336              : /* Locate the changed_file for FILENAME, adding one if there isn't one.  */
     337              : 
     338              : changed_file &
     339         5109 : change_set::get_or_insert_file (const char *filename)
     340              : {
     341         5109 :   gcc_assert (filename);
     342              : 
     343         5109 :   changed_file *file = get_file (filename);
     344         5109 :   if (file)
     345              :     return *file;
     346              : 
     347              :   /* Not found.  */
     348         3611 :   file = new changed_file (*this, filename);
     349         3611 :   m_files.insert (filename, file);
     350         3611 :   return *file;
     351              : }
     352              : 
     353              : /* Implementation of class changed_file.  */
     354              : 
     355              : /* Callback for m_changed_lines, for comparing line numbers.  */
     356              : 
     357        67131 : static int line_comparator (int a, int b)
     358              : {
     359        67131 :   return a - b;
     360              : }
     361              : 
     362              : /* changed_file's constructor.  */
     363              : 
     364         3611 : changed_file::changed_file (change_set &ec, const char *filename)
     365         3611 : : m_change_set (ec),
     366         3611 :   m_filename (filename),
     367         3611 :   m_changed_lines (line_comparator, NULL, changed_line::delete_cb),
     368         3611 :   m_num_lines (-1)
     369              : {
     370         3611 : }
     371              : 
     372              : /* A callback for deleting changed_file *, for use as a
     373              :    delete_value_fn for change_set::m_files.  */
     374              : 
     375              : void
     376         3611 : changed_file::delete_cb (changed_file *file)
     377              : {
     378         7222 :   delete file;
     379         3611 : }
     380              : 
     381              : /* Get the content of the file, with fix-its applied.
     382              :    The ptr should be freed by the caller.  */
     383              : 
     384              : char *
     385          844 : changed_file::get_content ()
     386              : {
     387          844 :   pretty_printer pp;
     388          844 :   if (!print_content (&pp))
     389              :     return NULL;
     390          844 :   return xstrdup (pp_formatted_text (&pp));
     391          844 : }
     392              : 
     393              : /* Attempt to replace columns START_COLUMN up to but not including NEXT_COLUMN
     394              :    of LINE with the string REPLACEMENT_STR of length REPLACEMENT_LEN,
     395              :    updating the in-memory copy of the line, and the record of edits to
     396              :    the line.  */
     397              : 
     398              : bool
     399         4265 : changed_file::apply_fixit (int line, int start_column, int next_column,
     400              :                           const char *replacement_str,
     401              :                           int replacement_len)
     402              : {
     403         4265 :   changed_line *el = get_or_insert_line (line);
     404         4265 :   if (!el)
     405              :     return false;
     406         4161 :   return el->apply_fixit (start_column, next_column, replacement_str,
     407         4161 :                           replacement_len);
     408              : }
     409              : 
     410              : /* Given line LINE, map from COLUMN in the input file to its current
     411              :    column after edits have been applied.  */
     412              : 
     413              : int
     414         1088 : changed_file::get_effective_column (int line, int column)
     415              : {
     416         1088 :   const changed_line *el = get_line (line);
     417         1088 :   if (!el)
     418              :     return column;
     419          832 :   return el->get_effective_column (column);
     420              : }
     421              : 
     422              : /* Attempt to print the content of the file to PP, with edits applied.
     423              :    Return true if successful, false otherwise.  */
     424              : 
     425              : bool
     426          844 : changed_file::print_content (pretty_printer *pp)
     427              : {
     428          844 :   bool missing_trailing_newline;
     429          844 :   int line_count = get_num_lines (&missing_trailing_newline);
     430         3172 :   for (int line_num = 1; line_num <= line_count; line_num++)
     431              :     {
     432         2328 :       changed_line *el = get_line (line_num);
     433         2328 :       if (el)
     434          832 :         el->print_content (pp);
     435              :       else
     436              :         {
     437         1496 :           char_span line
     438         1496 :             = get_file_cache ().get_source_line (m_filename, line_num);
     439         1496 :           if (!line)
     440            0 :             return false;
     441        18896 :           for (size_t i = 0; i < line.length (); i++)
     442        17400 :             pp_character (pp, line[i]);
     443              :         }
     444         2328 :       if (line_num < line_count)
     445         1488 :         pp_character (pp, '\n');
     446              :     }
     447              : 
     448          844 :   if (!missing_trailing_newline)
     449          836 :     pp_character (pp, '\n');
     450              : 
     451              :   return true;
     452              : }
     453              : 
     454              : /* Print a unified diff to PP, showing any changes that have occurred
     455              :    to this file.  */
     456              : 
     457              : void
     458         2645 : changed_file::print_diff (pretty_printer *pp, bool show_filenames)
     459              : {
     460         2645 :   if (show_filenames)
     461              :     {
     462         2077 :       pp_string (pp, colorize_start (pp_show_color (pp), "diff-filename"));
     463              :       /* Avoid -Wformat-diag in non-diagnostic output.  */
     464         2077 :       pp_string (pp, "--- ");
     465         2077 :       pp_string (pp, m_filename);
     466         2077 :       pp_newline (pp);
     467         2077 :       pp_string (pp, "+++ ");
     468         2077 :       pp_string (pp, m_filename);
     469         2077 :       pp_newline (pp);
     470         2077 :       pp_string (pp, colorize_stop (pp_show_color (pp)));
     471              :     }
     472              : 
     473         2645 :   changed_line *el = m_changed_lines.min ();
     474              : 
     475         2645 :   bool missing_trailing_newline;
     476         2645 :   int line_count = get_num_lines (&missing_trailing_newline);
     477              : 
     478         2645 :   const int context_lines = 3;
     479              : 
     480              :   /* Track new line numbers minus old line numbers.  */
     481              : 
     482         2645 :   int line_delta = 0;
     483              : 
     484         2645 :   while (el)
     485              :     {
     486         2706 :       int start_of_hunk = el->get_line_num ();
     487         2706 :       start_of_hunk -= context_lines;
     488         2706 :       if (start_of_hunk < 1)
     489              :         start_of_hunk = 1;
     490              : 
     491              :       /* Locate end of hunk, merging in changed lines
     492              :          that are sufficiently close.  */
     493         2986 :       while (true)
     494              :         {
     495         2986 :           changed_line *next_el
     496         2986 :             = m_changed_lines.successor (el->get_line_num ());
     497          341 :           if (!next_el)
     498              :             break;
     499              : 
     500          341 :           int end_of_printed_hunk = el->get_line_num () + context_lines;
     501          341 :           if (!el->actually_edited_p ())
     502           57 :             end_of_printed_hunk--;
     503              : 
     504          341 :           if (end_of_printed_hunk
     505          341 :               >= next_el->get_line_num () - context_lines)
     506              :             el = next_el;
     507              :           else
     508              :             break;
     509              :         }
     510              : 
     511         2706 :       int end_of_hunk = el->get_line_num ();
     512         2706 :       end_of_hunk += context_lines;
     513         2706 :       if (!el->actually_edited_p ())
     514          139 :         end_of_hunk--;
     515         2706 :       if (end_of_hunk > line_count)
     516              :         end_of_hunk = line_count;
     517              : 
     518         2706 :       int new_start_of_hunk = start_of_hunk + line_delta;
     519         2706 :       line_delta += print_diff_hunk (pp, start_of_hunk, end_of_hunk,
     520              :                                      new_start_of_hunk);
     521         8057 :       el = m_changed_lines.successor (el->get_line_num ());
     522              :     }
     523         2645 : }
     524              : 
     525              : /* Print one hunk within a unified diff to PP, covering the
     526              :    given range of lines.  OLD_START_OF_HUNK and OLD_END_OF_HUNK are
     527              :    line numbers in the unedited version of the file.
     528              :    NEW_START_OF_HUNK is a line number in the edited version of the file.
     529              :    Return the change in the line count within the hunk.  */
     530              : 
     531              : int
     532         2706 : changed_file::print_diff_hunk (pretty_printer *pp, int old_start_of_hunk,
     533              :                               int old_end_of_hunk, int new_start_of_hunk)
     534              : {
     535         2706 :   int old_num_lines = old_end_of_hunk - old_start_of_hunk + 1;
     536         2706 :   int new_num_lines
     537         2706 :     = get_effective_line_count (old_start_of_hunk, old_end_of_hunk);
     538              : 
     539         2706 :   pp_string (pp, colorize_start (pp_show_color (pp), "diff-hunk"));
     540         2706 :   pp_printf (pp, "%s -%i,%i +%i,%i %s",
     541              :              "@@", old_start_of_hunk, old_num_lines,
     542              :              new_start_of_hunk, new_num_lines, "@@\n");
     543         2706 :   pp_string (pp, colorize_stop (pp_show_color (pp)));
     544              : 
     545         2706 :   int line_num = old_start_of_hunk;
     546        10100 :   while (line_num <= old_end_of_hunk)
     547              :     {
     548         4688 :       changed_line *el = get_line (line_num);
     549         4688 :       if (el)
     550              :         {
     551              :           /* We have an edited line.
     552              :              Consolidate into runs of changed lines.  */
     553              :           const int first_changed_line_in_run = line_num;
     554         5748 :           while (get_line (line_num))
     555         2986 :             line_num++;
     556         2762 :           const int last_changed_line_in_run = line_num - 1;
     557         2762 :           print_run_of_changed_lines (pp, first_changed_line_in_run,
     558              :                                       last_changed_line_in_run);
     559              :         }
     560              :       else
     561              :         {
     562              :           /* Unchanged line.  */
     563         1926 :           char_span old_line
     564         1926 :             = get_file_cache ().get_source_line (m_filename, line_num);
     565         1926 :           print_diff_line (pp, ' ', old_line.get_buffer (), old_line.length ());
     566         1926 :           line_num++;
     567              :         }
     568              :     }
     569              : 
     570         2706 :   return new_num_lines - old_num_lines;
     571              : }
     572              : 
     573              : /* Subroutine of changed_file::print_diff_hunk: given a run of lines
     574              :    from START_OF_RUN to END_OF_RUN that all have changed_line instances,
     575              :    print the diff to PP.  */
     576              : 
     577              : void
     578         2762 : changed_file::print_run_of_changed_lines (pretty_printer *pp,
     579              :                                          int start_of_run,
     580              :                                          int end_of_run)
     581              : {
     582              :   /* Show old version of lines.  */
     583         2762 :   pp_string (pp, colorize_start (pp_show_color (pp),
     584              :                                  "diff-delete"));
     585         2762 :   for (int line_num = start_of_run;
     586         5748 :        line_num <= end_of_run;
     587              :        line_num++)
     588              :     {
     589         2986 :       changed_line *el_in_run = get_line (line_num);
     590         2986 :       gcc_assert (el_in_run);
     591         5833 :       if (el_in_run->actually_edited_p ())
     592              :         {
     593         2847 :           char_span old_line
     594         2847 :             = get_file_cache ().get_source_line (m_filename, line_num);
     595         2847 :           print_diff_line (pp, '-', old_line.get_buffer (),
     596         2847 :                            old_line.length ());
     597              :         }
     598              :     }
     599         2762 :   pp_string (pp, colorize_stop (pp_show_color (pp)));
     600              : 
     601              :   /* Show new version of lines.  */
     602         2762 :   pp_string (pp, colorize_start (pp_show_color (pp),
     603              :                                  "diff-insert"));
     604         5748 :   for (int line_num = start_of_run;
     605         5748 :        line_num <= end_of_run;
     606              :        line_num++)
     607              :     {
     608         2986 :       changed_line *el_in_run = get_line (line_num);
     609         2986 :       gcc_assert (el_in_run);
     610         2986 :       el_in_run->print_diff_lines (pp);
     611              :     }
     612         2762 :   pp_string (pp, colorize_stop (pp_show_color (pp)));
     613         2762 : }
     614              : 
     615              : /* Print one line within a diff, starting with PREFIX_CHAR,
     616              :    followed by the LINE of content, of length LEN.  LINE is
     617              :    not necessarily 0-terminated.  Print a trailing newline.  */
     618              : 
     619              : static void
     620         7901 : print_diff_line (pretty_printer *pp, char prefix_char,
     621              :                  const char *line, int len)
     622              : {
     623         7901 :   pp_character (pp, prefix_char);
     624     17385105 :   for (int i = 0; i < len; i++)
     625     17377204 :     pp_character (pp, line[i]);
     626         7901 :   pp_character (pp, '\n');
     627         7901 : }
     628              : 
     629              : /* Determine the number of lines that will be present after
     630              :    editing for the range of lines from OLD_START_OF_HUNK to
     631              :    OLD_END_OF_HUNK inclusive.  */
     632              : 
     633              : int
     634         2706 : changed_file::get_effective_line_count (int old_start_of_hunk,
     635              :                                        int old_end_of_hunk)
     636              : {
     637         2706 :   int line_count = 0;
     638         7618 :   for (int old_line_num = old_start_of_hunk; old_line_num <= old_end_of_hunk;
     639              :        old_line_num++)
     640              :     {
     641         4912 :       changed_line *el = get_line (old_line_num);
     642         4912 :       if (el)
     643         2986 :         line_count += el->get_effective_line_count ();
     644              :       else
     645         1926 :         line_count++;
     646              :     }
     647         2706 :   return line_count;
     648              : }
     649              : 
     650              : /* Get the state of LINE within the file, or NULL if it is untouched.  */
     651              : 
     652              : changed_line *
     653        29001 : changed_file::get_line (int line)
     654              : {
     655        29001 :   return m_changed_lines.lookup (line);
     656              : }
     657              : 
     658              : /* Get the state of LINE within the file, creating a state for it
     659              :    if necessary.  Return NULL if an error occurs.  */
     660              : 
     661              : changed_line *
     662         4265 : changed_file::get_or_insert_line (int line)
     663              : {
     664         4265 :   changed_line *el = get_line (line);
     665         4265 :   if (el)
     666              :     return el;
     667         3940 :   el = new changed_line (get_file_cache (), m_filename, line);
     668         3940 :   if (el->get_content () == NULL)
     669              :     {
     670          104 :       delete el;
     671          104 :       return NULL;
     672              :     }
     673         3836 :   m_changed_lines.insert (line, el);
     674         3836 :   return el;
     675              : }
     676              : 
     677              : /* Get the total number of lines in m_content, writing
     678              :    true to *MISSING_TRAILING_NEWLINE if the final line
     679              :    if missing a newline, false otherwise.  */
     680              : 
     681              : int
     682         3489 : changed_file::get_num_lines (bool *missing_trailing_newline)
     683              : {
     684         3489 :   gcc_assert (missing_trailing_newline);
     685         3489 :   if (m_num_lines == -1)
     686              :     {
     687         2913 :       m_num_lines = 0;
     688       148051 :       while (true)
     689              :         {
     690        75482 :           char_span line
     691        75482 :             = get_file_cache ().get_source_line (m_filename, m_num_lines + 1);
     692        75482 :           if (line)
     693        72569 :             m_num_lines++;
     694              :           else
     695              :             break;
     696        72569 :         }
     697              :     }
     698         3489 :   *missing_trailing_newline
     699         3489 :     = get_file_cache ().missing_trailing_newline_p (m_filename);
     700         3489 :   return m_num_lines;
     701              : }
     702              : 
     703              : /* Implementation of class changed_line.  */
     704              : 
     705              : /* changed_line's ctor.  */
     706              : 
     707         3940 : changed_line::changed_line (file_cache &fc, const char *filename, int line_num)
     708         3940 : : m_line_num (line_num),
     709         3940 :   m_content (NULL), m_len (0), m_alloc_sz (0),
     710         3940 :   m_line_events (),
     711         3940 :   m_predecessors ()
     712              : {
     713         3940 :   char_span line = fc.get_source_line (filename, line_num);
     714         3940 :   if (!line)
     715          104 :     return;
     716         3836 :   m_len = line.length ();
     717         3836 :   ensure_capacity (m_len);
     718         3836 :   memcpy (m_content, line.get_buffer (), m_len);
     719         3836 :   ensure_terminated ();
     720              : }
     721              : 
     722              : /* changed_line's dtor.  */
     723              : 
     724         3940 : changed_line::~changed_line ()
     725              : {
     726         3940 :   unsigned i;
     727         3940 :   added_line *pred;
     728              : 
     729         3940 :   free (m_content);
     730         4082 :   FOR_EACH_VEC_ELT (m_predecessors, i, pred)
     731          142 :     delete pred;
     732         3940 : }
     733              : 
     734              : /* A callback for deleting changed_line *, for use as a
     735              :    delete_value_fn for changed_file::m_changed_lines.  */
     736              : 
     737              : void
     738         3836 : changed_line::delete_cb (changed_line *el)
     739              : {
     740         3836 :   delete el;
     741         3836 : }
     742              : 
     743              : /* Map a location before the edits to a column number after the edits,
     744              :    within a specific line.  */
     745              : 
     746              : int
     747         8870 : changed_line::get_effective_column (int orig_column) const
     748              : {
     749         8870 :   int i;
     750         8870 :   line_event *event;
     751        10730 :   FOR_EACH_VEC_ELT (m_line_events, i, event)
     752         3080 :     orig_column = event->get_effective_column (orig_column);
     753         8870 :   return orig_column;
     754              : }
     755              : 
     756              : /* Attempt to replace columns START_COLUMN up to but not including
     757              :    NEXT_COLUMN of the line with the string REPLACEMENT_STR of
     758              :    length REPLACEMENT_LEN, updating the in-memory copy of the line,
     759              :    and the record of edits to the line.
     760              :    Return true if successful; false if an error occurred.  */
     761              : 
     762              : bool
     763         4161 : changed_line::apply_fixit (int start_column,
     764              :                           int next_column,
     765              :                           const char *replacement_str,
     766              :                           int replacement_len)
     767              : {
     768              :   /* Handle newlines.  They will only ever be at the end of the
     769              :      replacement text, thanks to the filtering in rich_location.  */
     770         4161 :   if (replacement_len > 1)
     771         3430 :     if (replacement_str[replacement_len - 1] == '\n')
     772              :       {
     773              :         /* Stash in m_predecessors, stripping off newline.  */
     774          426 :         m_predecessors.safe_push (new added_line (replacement_str,
     775          142 :                                                   replacement_len - 1));
     776          142 :         return true;
     777              :       }
     778              : 
     779         4019 :   start_column = get_effective_column (start_column);
     780         4019 :   next_column = get_effective_column (next_column);
     781              : 
     782         4019 :   int start_offset = start_column - 1;
     783         4019 :   int next_offset = next_column - 1;
     784              : 
     785         4019 :   gcc_assert (start_offset >= 0);
     786         4019 :   gcc_assert (next_offset >= 0);
     787              : 
     788         4019 :   if (start_column > next_column)
     789              :     return false;
     790         4019 :   if (start_offset >= (m_len + 1))
     791              :     return false;
     792         3637 :   if (next_offset >= (m_len + 1))
     793              :     return false;
     794              : 
     795         3433 :   size_t victim_len = next_offset - start_offset;
     796              : 
     797              :   /* Ensure buffer is big enough.  */
     798         3433 :   size_t new_len = m_len + replacement_len - victim_len;
     799         3433 :   ensure_capacity (new_len);
     800              : 
     801         3433 :   char *suffix = m_content + next_offset;
     802         3433 :   gcc_assert (suffix <= m_content + m_len);
     803         3433 :   size_t len_suffix = (m_content + m_len) - suffix;
     804              : 
     805              :   /* Move successor content into position.  They overlap, so use memmove.  */
     806         3433 :   memmove (m_content + start_offset + replacement_len,
     807              :            suffix, len_suffix);
     808              : 
     809              :   /* Replace target content.  They don't overlap, so use memcpy.  */
     810         3433 :   memcpy (m_content + start_offset,
     811              :           replacement_str,
     812              :           replacement_len);
     813              : 
     814         3433 :   m_len = new_len;
     815              : 
     816         3433 :   ensure_terminated ();
     817              : 
     818              :   /* Record the replacement, so that future changes to the line can have
     819              :      their column information adjusted accordingly.  */
     820         6866 :   m_line_events.safe_push (line_event (start_column, next_column,
     821         3433 :                                        replacement_len));
     822         3433 :   return true;
     823              : }
     824              : 
     825              : /* Determine the number of lines that will be present after
     826              :    editing for this line.  Typically this is just 1, but
     827              :    if newlines have been added before this line, they will
     828              :    also be counted.  */
     829              : 
     830              : int
     831         2986 : changed_line::get_effective_line_count () const
     832              : {
     833         2986 :   return m_predecessors.length () + 1;
     834              : }
     835              : 
     836              : /* Subroutine of changed_file::print_content.
     837              :    Print this line and any new lines added before it, to PP.  */
     838              : 
     839              : void
     840          832 : changed_line::print_content (pretty_printer *pp) const
     841              : {
     842          832 :   unsigned i;
     843          832 :   added_line *pred;
     844          896 :   FOR_EACH_VEC_ELT (m_predecessors, i, pred)
     845              :     {
     846           64 :       pp_string (pp, pred->get_content ());
     847           64 :       pp_newline (pp);
     848              :     }
     849          832 :   pp_string (pp, m_content);
     850          832 : }
     851              : 
     852              : /* Subroutine of changed_file::print_run_of_changed_lines for
     853              :    printing diff hunks to PP.
     854              :    Print the '+' line for this line, and any newlines added
     855              :    before it.
     856              :    Note that if this changed_line was actually edited, the '-'
     857              :    line has already been printed.  If it wasn't, then we merely
     858              :    have a placeholder changed_line for adding newlines to, and
     859              :    we need to print a ' ' line for the changed_line as we haven't
     860              :    printed it yet.  */
     861              : 
     862              : void
     863         2986 : changed_line::print_diff_lines (pretty_printer *pp) const
     864              : {
     865         2986 :   unsigned i;
     866         2986 :   added_line *pred;
     867         3128 :   FOR_EACH_VEC_ELT (m_predecessors, i, pred)
     868          142 :     print_diff_line (pp, '+', pred->get_content (),
     869              :                      pred->get_len ());
     870         2986 :   if (actually_edited_p ())
     871         2847 :     print_diff_line (pp, '+', m_content, m_len);
     872              :   else
     873          139 :     print_diff_line (pp, ' ', m_content, m_len);
     874         2986 : }
     875              : 
     876              : /* Ensure that the buffer for m_content is at least large enough to hold
     877              :    a string of length LEN and its 0-terminator, doubling on repeated
     878              :    allocations.  */
     879              : 
     880              : void
     881         7269 : changed_line::ensure_capacity (int len)
     882              : {
     883              :   /* Allow 1 extra byte for 0-termination.  */
     884         7269 :   if (m_alloc_sz < (len + 1))
     885              :     {
     886         4341 :       size_t new_alloc_sz = (len + 1) * 2;
     887         4341 :       m_content = (char *)xrealloc (m_content, new_alloc_sz);
     888         4341 :       m_alloc_sz = new_alloc_sz;
     889              :     }
     890         7269 : }
     891              : 
     892              : /* Ensure that m_content is 0-terminated.  */
     893              : 
     894              : void
     895         7269 : changed_line::ensure_terminated ()
     896              : {
     897              :   /* 0-terminate the buffer.  */
     898         7269 :   gcc_assert (m_len < m_alloc_sz);
     899         7269 :   m_content[m_len] = '\0';
     900         7269 : }
     901              : 
     902              : #if CHECKING_P
     903              : 
     904              : /* Selftests of code-editing.  */
     905              : 
     906              : namespace selftest {
     907              : 
     908              : using line_table_case = ::selftest::line_table_case;
     909              : using line_table_test = ::selftest::line_table_test;
     910              : using temp_source_file = ::selftest::temp_source_file;
     911              : using named_temp_file = ::selftest::named_temp_file;
     912              : 
     913              : /* A wrapper class for ensuring that the underlying pointer is freed.  */
     914              : 
     915              : template <typename POINTER_T>
     916              : class auto_free
     917              : {
     918              :  public:
     919         1636 :   auto_free (POINTER_T p) : m_ptr (p) {}
     920         1316 :   ~auto_free () { free (m_ptr); }
     921              : 
     922              :   operator POINTER_T () { return m_ptr; }
     923              : 
     924              :  private:
     925              :   POINTER_T m_ptr;
     926              : };
     927              : 
     928              : /* Verify that change_set::get_content works for unedited files.  */
     929              : 
     930              : static void
     931            4 : test_get_content ()
     932              : {
     933              :   /* Test of empty file.  */
     934            4 :   {
     935            4 :     const char *content = ("");
     936            4 :     temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
     937            4 :     file_cache fc;
     938            4 :     change_set edit (fc);
     939            4 :     auto_free <char *> result = edit.get_content (tmp.get_filename ());
     940            4 :     ASSERT_STREQ ("", result);
     941            4 :   }
     942              : 
     943              :   /* Test of simple content.  */
     944            4 :   {
     945            4 :     const char *content = ("/* before */\n"
     946              :                            "foo = bar.field;\n"
     947              :                            "/* after */\n");
     948            4 :     temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
     949            4 :     file_cache fc;
     950            4 :     change_set edit (fc);
     951            4 :     auto_free <char *> result = edit.get_content (tmp.get_filename ());
     952            4 :     ASSERT_STREQ ("/* before */\n"
     953              :                   "foo = bar.field;\n"
     954              :                   "/* after */\n", result);
     955            4 :   }
     956              : 
     957              :   /* Test of omitting the trailing newline on the final line.  */
     958            4 :   {
     959            4 :     const char *content = ("/* before */\n"
     960              :                            "foo = bar.field;\n"
     961              :                            "/* after */");
     962            4 :     temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
     963            4 :     file_cache fc;
     964            4 :     change_set edit (fc);
     965            4 :     auto_free <char *> result = edit.get_content (tmp.get_filename ());
     966              :     /* We should respect the omitted trailing newline.  */
     967            4 :     ASSERT_STREQ ("/* before */\n"
     968              :                   "foo = bar.field;\n"
     969              :                   "/* after */", result);
     970            4 :   }
     971            4 : }
     972              : 
     973              : /* Test applying an "insert" fixit, using insert_before.  */
     974              : 
     975              : static void
     976           96 : test_applying_fixits_insert_before (const line_table_case &case_)
     977              : {
     978              :   /* Create a tempfile and write some text to it.
     979              :      .........................0000000001111111.
     980              :      .........................1234567890123456.  */
     981           96 :   const char *old_content = ("/* before */\n"
     982              :                              "foo = bar.field;\n"
     983              :                              "/* after */\n");
     984           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
     985           96 :   const char *filename = tmp.get_filename ();
     986           96 :   line_table_test ltt (case_);
     987           96 :   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
     988              : 
     989              :   /* Add a comment in front of "bar.field".  */
     990           96 :   location_t start = linemap_position_for_column (line_table, 7);
     991           96 :   rich_location richloc (line_table, start);
     992           96 :   richloc.add_fixit_insert_before ("/* inserted */");
     993              : 
     994           96 :   if (start > LINE_MAP_MAX_LOCATION_WITH_COLS)
     995           32 :     return;
     996              : 
     997           64 :   file_cache fc;
     998           64 :   change_set edit (fc);
     999           64 :   edit.add_fixits (&richloc);
    1000           64 :   auto_free <char *> new_content = edit.get_content (filename);
    1001           64 :   if (start <= LINE_MAP_MAX_LOCATION_WITH_COLS)
    1002           64 :     ASSERT_STREQ ("/* before */\n"
    1003              :                   "foo = /* inserted */bar.field;\n"
    1004              :                   "/* after */\n", new_content);
    1005              : 
    1006              :   /* Verify that locations on other lines aren't affected by the change.  */
    1007           64 :   ASSERT_EQ (100, edit.get_effective_column (filename, 1, 100));
    1008           64 :   ASSERT_EQ (100, edit.get_effective_column (filename, 3, 100));
    1009              : 
    1010              :   /* Verify locations on the line before the change.  */
    1011           64 :   ASSERT_EQ (1, edit.get_effective_column (filename, 2, 1));
    1012           64 :   ASSERT_EQ (6, edit.get_effective_column (filename, 2, 6));
    1013              : 
    1014              :   /* Verify locations on the line at and after the change.  */
    1015           64 :   ASSERT_EQ (21, edit.get_effective_column (filename, 2, 7));
    1016           64 :   ASSERT_EQ (22, edit.get_effective_column (filename, 2, 8));
    1017              : 
    1018              :   /* Verify diff.  */
    1019           64 :   auto_free <char *> diff = edit.generate_diff (false);
    1020           64 :   ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
    1021              :                 " /* before */\n"
    1022              :                 "-foo = bar.field;\n"
    1023              :                 "+foo = /* inserted */bar.field;\n"
    1024              :                 " /* after */\n", diff);
    1025           96 : }
    1026              : 
    1027              : /* Test applying an "insert" fixit, using insert_after, with
    1028              :    a range of length > 1 (to ensure that the end-point of
    1029              :    the input range is used).  */
    1030              : 
    1031              : static void
    1032           96 : test_applying_fixits_insert_after (const line_table_case &case_)
    1033              : {
    1034              :   /* Create a tempfile and write some text to it.
    1035              :      .........................0000000001111111.
    1036              :      .........................1234567890123456.  */
    1037           96 :   const char *old_content = ("/* before */\n"
    1038              :                              "foo = bar.field;\n"
    1039              :                              "/* after */\n");
    1040           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    1041           96 :   const char *filename = tmp.get_filename ();
    1042           96 :   line_table_test ltt (case_);
    1043           96 :   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
    1044              : 
    1045              :   /* Add a comment after "field".  */
    1046           96 :   location_t start = linemap_position_for_column (line_table, 11);
    1047           96 :   location_t finish = linemap_position_for_column (line_table, 15);
    1048           96 :   location_t field = make_location (start, start, finish);
    1049           96 :   rich_location richloc (line_table, field);
    1050           96 :   richloc.add_fixit_insert_after ("/* inserted */");
    1051              : 
    1052           96 :   if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
    1053           32 :     return;
    1054              : 
    1055              :   /* Verify that the text was inserted after the end of "field". */
    1056           64 :   file_cache fc;
    1057           64 :   change_set edit (fc);
    1058           64 :   edit.add_fixits (&richloc);
    1059           64 :   auto_free <char *> new_content = edit.get_content (filename);
    1060           64 :   ASSERT_STREQ ("/* before */\n"
    1061              :                 "foo = bar.field/* inserted */;\n"
    1062              :                 "/* after */\n", new_content);
    1063              : 
    1064              :   /* Verify diff.  */
    1065           64 :   auto_free <char *> diff = edit.generate_diff (false);
    1066           64 :   ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
    1067              :                 " /* before */\n"
    1068              :                 "-foo = bar.field;\n"
    1069              :                 "+foo = bar.field/* inserted */;\n"
    1070              :                 " /* after */\n", diff);
    1071           96 : }
    1072              : 
    1073              : /* Test applying an "insert" fixit, using insert_after at the end of
    1074              :    a line (contrast with test_applying_fixits_insert_after_failure
    1075              :    below).  */
    1076              : 
    1077              : static void
    1078           96 : test_applying_fixits_insert_after_at_line_end (const line_table_case &case_)
    1079              : {
    1080              :   /* Create a tempfile and write some text to it.
    1081              :      .........................0000000001111111.
    1082              :      .........................1234567890123456.  */
    1083           96 :   const char *old_content = ("/* before */\n"
    1084              :                              "foo = bar.field;\n"
    1085              :                              "/* after */\n");
    1086           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    1087           96 :   const char *filename = tmp.get_filename ();
    1088           96 :   line_table_test ltt (case_);
    1089           96 :   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
    1090              : 
    1091              :   /* Add a comment after the semicolon.  */
    1092           96 :   location_t loc = linemap_position_for_column (line_table, 16);
    1093           96 :   rich_location richloc (line_table, loc);
    1094           96 :   richloc.add_fixit_insert_after ("/* inserted */");
    1095              : 
    1096           96 :   if (loc > LINE_MAP_MAX_LOCATION_WITH_COLS)
    1097           32 :     return;
    1098              : 
    1099           64 :   file_cache fc;
    1100           64 :   change_set edit (fc);
    1101           64 :   edit.add_fixits (&richloc);
    1102           64 :   auto_free <char *> new_content = edit.get_content (filename);
    1103           64 :   ASSERT_STREQ ("/* before */\n"
    1104              :                 "foo = bar.field;/* inserted */\n"
    1105              :                 "/* after */\n", new_content);
    1106              : 
    1107              :   /* Verify diff.  */
    1108           64 :   auto_free <char *> diff = edit.generate_diff (false);
    1109           64 :   ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
    1110              :                 " /* before */\n"
    1111              :                 "-foo = bar.field;\n"
    1112              :                 "+foo = bar.field;/* inserted */\n"
    1113              :                 " /* after */\n", diff);
    1114           96 : }
    1115              : 
    1116              : /* Test of a failed attempt to apply an "insert" fixit, using insert_after,
    1117              :    due to the relevant linemap ending.  Contrast with
    1118              :    test_applying_fixits_insert_after_at_line_end above.  */
    1119              : 
    1120              : static void
    1121           96 : test_applying_fixits_insert_after_failure (const line_table_case &case_)
    1122              : {
    1123              :   /* Create a tempfile and write some text to it.
    1124              :      .........................0000000001111111.
    1125              :      .........................1234567890123456.  */
    1126           96 :   const char *old_content = ("/* before */\n"
    1127              :                              "foo = bar.field;\n"
    1128              :                              "/* after */\n");
    1129           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    1130           96 :   const char *filename = tmp.get_filename ();
    1131           96 :   line_table_test ltt (case_);
    1132           96 :   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
    1133              : 
    1134              :   /* Add a comment after the semicolon.  */
    1135           96 :   location_t loc = linemap_position_for_column (line_table, 16);
    1136           96 :   rich_location richloc (line_table, loc);
    1137              : 
    1138              :   /* We want a failure of linemap_position_for_loc_and_offset.
    1139              :      We can do this by starting a new linemap at line 3, so that
    1140              :      there is no appropriate location value for the insertion point
    1141              :      within the linemap for line 2.  */
    1142           96 :   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
    1143              : 
    1144              :   /* The failure fails to happen at the transition point from
    1145              :      packed ranges to unpacked ranges (where there are some "spare"
    1146              :      location_t values).  Skip the test there.  */
    1147           96 :   if (loc >= LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES)
    1148           76 :     return;
    1149              : 
    1150              :   /* Offsetting "loc" should now fail (by returning the input loc. */
    1151           20 :   ASSERT_EQ (loc, linemap_position_for_loc_and_offset (line_table, loc, 1));
    1152              : 
    1153              :   /* Hence attempting to use add_fixit_insert_after at the end of the line
    1154              :      should now fail.  */
    1155           20 :   richloc.add_fixit_insert_after ("/* inserted */");
    1156           20 :   ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
    1157              : 
    1158           20 :   file_cache fc;
    1159           20 :   change_set edit (fc);
    1160           20 :   edit.add_fixits (&richloc);
    1161           20 :   ASSERT_FALSE (edit.valid_p ());
    1162           20 :   ASSERT_EQ (NULL, edit.get_content (filename));
    1163           20 :   ASSERT_EQ (NULL, edit.generate_diff (false));
    1164           96 : }
    1165              : 
    1166              : /* Test applying an "insert" fixit that adds a newline.  */
    1167              : 
    1168              : static void
    1169           96 : test_applying_fixits_insert_containing_newline (const line_table_case &case_)
    1170              : {
    1171              :   /* Create a tempfile and write some text to it.
    1172              :      .........................0000000001111111.
    1173              :      .........................1234567890123456.  */
    1174           96 :   const char *old_content = ("    case 'a':\n" /* line 1. */
    1175              :                              "      x = a;\n"  /* line 2. */
    1176              :                              "    case 'b':\n" /* line 3. */
    1177              :                              "      x = b;\n");/* line 4. */
    1178              : 
    1179           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    1180           96 :   const char *filename = tmp.get_filename ();
    1181           96 :   line_table_test ltt (case_);
    1182           96 :   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
    1183              : 
    1184              :   /* Add a "break;" on a line by itself before line 3 i.e. before
    1185              :      column 1 of line 3. */
    1186           96 :   location_t case_start = linemap_position_for_column (line_table, 5);
    1187           96 :   location_t case_finish = linemap_position_for_column (line_table, 13);
    1188           96 :   location_t case_loc = make_location (case_start, case_start, case_finish);
    1189           96 :   rich_location richloc (line_table, case_loc);
    1190           96 :   location_t line_start = linemap_position_for_column (line_table, 1);
    1191           96 :   richloc.add_fixit_insert_before (line_start, "      break;\n");
    1192              : 
    1193           96 :   if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
    1194           32 :     return;
    1195              : 
    1196           64 :   file_cache fc;
    1197           64 :   change_set edit (fc);
    1198           64 :   edit.add_fixits (&richloc);
    1199           64 :   auto_free <char *> new_content = edit.get_content (filename);
    1200           64 :   ASSERT_STREQ (("    case 'a':\n"
    1201              :                  "      x = a;\n"
    1202              :                  "      break;\n"
    1203              :                  "    case 'b':\n"
    1204              :                  "      x = b;\n"),
    1205              :                 new_content);
    1206              : 
    1207              :   /* Verify diff.  */
    1208           64 :   auto_free <char *> diff = edit.generate_diff (false);
    1209           64 :   ASSERT_STREQ (("@@ -1,4 +1,5 @@\n"
    1210              :                  "     case 'a':\n"
    1211              :                  "       x = a;\n"
    1212              :                  "+      break;\n"
    1213              :                  "     case 'b':\n"
    1214              :                  "       x = b;\n"),
    1215              :                 diff);
    1216           96 : }
    1217              : 
    1218              : /* Test applying a "replace" fixit that grows the affected line.  */
    1219              : 
    1220              : static void
    1221           96 : test_applying_fixits_growing_replace (const line_table_case &case_)
    1222              : {
    1223              :   /* Create a tempfile and write some text to it.
    1224              :      .........................0000000001111111.
    1225              :      .........................1234567890123456.  */
    1226           96 :   const char *old_content = ("/* before */\n"
    1227              :                              "foo = bar.field;\n"
    1228              :                              "/* after */\n");
    1229           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    1230           96 :   const char *filename = tmp.get_filename ();
    1231           96 :   line_table_test ltt (case_);
    1232           96 :   linemap_add (line_table, LC_ENTER, false, filename, 2);
    1233              : 
    1234              :   /* Replace "field" with "m_field".  */
    1235           96 :   location_t start = linemap_position_for_column (line_table, 11);
    1236           96 :   location_t finish = linemap_position_for_column (line_table, 15);
    1237           96 :   location_t field = make_location (start, start, finish);
    1238           96 :   rich_location richloc (line_table, field);
    1239           96 :   richloc.add_fixit_replace ("m_field");
    1240              : 
    1241           96 :   file_cache fc;
    1242           96 :   change_set edit (fc);
    1243           96 :   edit.add_fixits (&richloc);
    1244           96 :   auto_free <char *> new_content = edit.get_content (filename);
    1245           96 :   if (finish <= LINE_MAP_MAX_LOCATION_WITH_COLS)
    1246              :     {
    1247           64 :       ASSERT_STREQ ("/* before */\n"
    1248              :                     "foo = bar.m_field;\n"
    1249              :                     "/* after */\n", new_content);
    1250              : 
    1251              :       /* Verify location of ";" after the change.  */
    1252           64 :       ASSERT_EQ (18, edit.get_effective_column (filename, 2, 16));
    1253              : 
    1254              :       /* Verify diff.  */
    1255           64 :       auto_free <char *> diff = edit.generate_diff (false);
    1256           64 :       ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
    1257              :                     " /* before */\n"
    1258              :                     "-foo = bar.field;\n"
    1259              :                     "+foo = bar.m_field;\n"
    1260              :                     " /* after */\n", diff);
    1261           64 :     }
    1262           96 : }
    1263              : 
    1264              : /* Test applying a "replace" fixit that shrinks the affected line.  */
    1265              : 
    1266              : static void
    1267           96 : test_applying_fixits_shrinking_replace (const line_table_case &case_)
    1268              : {
    1269              :   /* Create a tempfile and write some text to it.
    1270              :      .........................000000000111111111.
    1271              :      .........................123456789012345678.  */
    1272           96 :   const char *old_content = ("/* before */\n"
    1273              :                              "foo = bar.m_field;\n"
    1274              :                              "/* after */\n");
    1275           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    1276           96 :   const char *filename = tmp.get_filename ();
    1277           96 :   line_table_test ltt (case_);
    1278           96 :   linemap_add (line_table, LC_ENTER, false, filename, 2);
    1279              : 
    1280              :   /* Replace "field" with "m_field".  */
    1281           96 :   location_t start = linemap_position_for_column (line_table, 11);
    1282           96 :   location_t finish = linemap_position_for_column (line_table, 17);
    1283           96 :   location_t m_field = make_location (start, start, finish);
    1284           96 :   rich_location richloc (line_table, m_field);
    1285           96 :   richloc.add_fixit_replace ("field");
    1286              : 
    1287           96 :   file_cache fc;
    1288           96 :   change_set edit (fc);
    1289           96 :   edit.add_fixits (&richloc);
    1290           96 :   auto_free <char *> new_content = edit.get_content (filename);
    1291           96 :   if (finish <= LINE_MAP_MAX_LOCATION_WITH_COLS)
    1292              :     {
    1293           64 :       ASSERT_STREQ ("/* before */\n"
    1294              :                     "foo = bar.field;\n"
    1295              :                     "/* after */\n", new_content);
    1296              : 
    1297              :       /* Verify location of ";" after the change.  */
    1298           64 :       ASSERT_EQ (16, edit.get_effective_column (filename, 2, 18));
    1299              : 
    1300              :       /* Verify diff.  */
    1301           64 :       auto_free <char *> diff = edit.generate_diff (false);
    1302           64 :       ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
    1303              :                     " /* before */\n"
    1304              :                     "-foo = bar.m_field;\n"
    1305              :                     "+foo = bar.field;\n"
    1306              :                     " /* after */\n", diff);
    1307           64 :     }
    1308           96 : }
    1309              : 
    1310              : /* Replacement fix-it hint containing a newline.  */
    1311              : 
    1312              : static void
    1313           96 : test_applying_fixits_replace_containing_newline (const line_table_case &case_)
    1314              : {
    1315              :   /* Create a tempfile and write some text to it.
    1316              :     .........................0000000001111.
    1317              :     .........................1234567890123.  */
    1318           96 :   const char *old_content = "foo = bar ();\n";
    1319              : 
    1320           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    1321           96 :   const char *filename = tmp.get_filename ();
    1322           96 :   line_table_test ltt (case_);
    1323           96 :   linemap_add (line_table, LC_ENTER, false, filename, 1);
    1324              : 
    1325              :   /* Replace the " = " with "\n  = ", as if we were reformatting an
    1326              :      overly long line.  */
    1327           96 :   location_t start = linemap_position_for_column (line_table, 4);
    1328           96 :   location_t finish = linemap_position_for_column (line_table, 6);
    1329           96 :   location_t loc = linemap_position_for_column (line_table, 13);
    1330           96 :   rich_location richloc (line_table, loc);
    1331           96 :   source_range range = source_range::from_locations (start, finish);
    1332           96 :   richloc.add_fixit_replace (range, "\n  = ");
    1333              : 
    1334              :   /* Newlines are only supported within fix-it hints that
    1335              :      are at the start of lines (for entirely new lines), hence
    1336              :      this fix-it should not be displayed.  */
    1337           96 :   ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
    1338              : 
    1339           96 :   if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
    1340           32 :     return;
    1341              : 
    1342           64 :   file_cache fc;
    1343           64 :   change_set edit (fc);
    1344           64 :   edit.add_fixits (&richloc);
    1345           64 :   auto_free <char *> new_content = edit.get_content (filename);
    1346              :   //ASSERT_STREQ ("foo\n  = bar ();\n", new_content);
    1347           96 : }
    1348              : 
    1349              : /* Test applying a "remove" fixit.  */
    1350              : 
    1351              : static void
    1352           96 : test_applying_fixits_remove (const line_table_case &case_)
    1353              : {
    1354              :   /* Create a tempfile and write some text to it.
    1355              :      .........................000000000111111111.
    1356              :      .........................123456789012345678.  */
    1357           96 :   const char *old_content = ("/* before */\n"
    1358              :                              "foo = bar.m_field;\n"
    1359              :                              "/* after */\n");
    1360           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    1361           96 :   const char *filename = tmp.get_filename ();
    1362           96 :   line_table_test ltt (case_);
    1363           96 :   linemap_add (line_table, LC_ENTER, false, filename, 2);
    1364              : 
    1365              :   /* Remove ".m_field".  */
    1366           96 :   location_t start = linemap_position_for_column (line_table, 10);
    1367           96 :   location_t finish = linemap_position_for_column (line_table, 17);
    1368           96 :   rich_location richloc (line_table, start);
    1369           96 :   source_range range;
    1370           96 :   range.m_start = start;
    1371           96 :   range.m_finish = finish;
    1372           96 :   richloc.add_fixit_remove (range);
    1373              : 
    1374           96 :   file_cache fc;
    1375           96 :   change_set edit (fc);
    1376           96 :   edit.add_fixits (&richloc);
    1377           96 :   auto_free <char *> new_content = edit.get_content (filename);
    1378           96 :   if (finish <= LINE_MAP_MAX_LOCATION_WITH_COLS)
    1379              :     {
    1380           64 :       ASSERT_STREQ ("/* before */\n"
    1381              :                     "foo = bar;\n"
    1382              :                     "/* after */\n", new_content);
    1383              : 
    1384              :       /* Verify location of ";" after the change.  */
    1385           64 :       ASSERT_EQ (10, edit.get_effective_column (filename, 2, 18));
    1386              : 
    1387              :       /* Verify diff.  */
    1388           64 :       auto_free <char *> diff = edit.generate_diff (false);
    1389           64 :       ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
    1390              :                     " /* before */\n"
    1391              :                     "-foo = bar.m_field;\n"
    1392              :                     "+foo = bar;\n"
    1393              :                     " /* after */\n", diff);
    1394           64 :     }
    1395           96 : }
    1396              : 
    1397              : /* Test applying multiple fixits to one line.  */
    1398              : 
    1399              : static void
    1400           96 : test_applying_fixits_multiple (const line_table_case &case_)
    1401              : {
    1402              :   /* Create a tempfile and write some text to it.
    1403              :      .........................00000000011111111.
    1404              :      .........................12345678901234567.  */
    1405           96 :   const char *old_content = ("/* before */\n"
    1406              :                              "foo = bar.field;\n"
    1407              :                              "/* after */\n");
    1408           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    1409           96 :   const char *filename = tmp.get_filename ();
    1410           96 :   line_table_test ltt (case_);
    1411           96 :   linemap_add (line_table, LC_ENTER, false, filename, 2);
    1412              : 
    1413           96 :   location_t c7 = linemap_position_for_column (line_table, 7);
    1414           96 :   location_t c9 = linemap_position_for_column (line_table, 9);
    1415           96 :   location_t c11 = linemap_position_for_column (line_table, 11);
    1416           96 :   location_t c15 = linemap_position_for_column (line_table, 15);
    1417           96 :   location_t c17 = linemap_position_for_column (line_table, 17);
    1418              : 
    1419           96 :   if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
    1420           32 :     return;
    1421              : 
    1422              :   /* Add a comment in front of "bar.field".  */
    1423           64 :   rich_location insert_a (line_table, c7);
    1424           64 :   insert_a.add_fixit_insert_before (c7, "/* alpha */");
    1425              : 
    1426              :   /* Add a comment after "bar.field;".  */
    1427           64 :   rich_location insert_b (line_table, c17);
    1428           64 :   insert_b.add_fixit_insert_before (c17, "/* beta */");
    1429              : 
    1430              :   /* Replace "bar" with "pub".   */
    1431           64 :   rich_location replace_a (line_table, c7);
    1432           64 :   replace_a.add_fixit_replace (source_range::from_locations (c7, c9),
    1433              :                                "pub");
    1434              : 
    1435              :   /* Replace "field" with "meadow".   */
    1436           64 :   rich_location replace_b (line_table, c7);
    1437           64 :   replace_b.add_fixit_replace (source_range::from_locations (c11, c15),
    1438              :                                "meadow");
    1439              : 
    1440           64 :   file_cache fc;
    1441           64 :   change_set edit (fc);
    1442           64 :   edit.add_fixits (&insert_a);
    1443           64 :   ASSERT_EQ (100, edit.get_effective_column (filename, 1, 100));
    1444           64 :   ASSERT_EQ (1, edit.get_effective_column (filename, 2, 1));
    1445           64 :   ASSERT_EQ (6, edit.get_effective_column (filename, 2, 6));
    1446           64 :   ASSERT_EQ (18, edit.get_effective_column (filename, 2, 7));
    1447           64 :   ASSERT_EQ (27, edit.get_effective_column (filename, 2, 16));
    1448           64 :   ASSERT_EQ (100, edit.get_effective_column (filename, 3, 100));
    1449              : 
    1450           64 :   edit.add_fixits (&insert_b);
    1451           64 :   edit.add_fixits (&replace_a);
    1452           64 :   edit.add_fixits (&replace_b);
    1453              : 
    1454           64 :   if (c17 <= LINE_MAP_MAX_LOCATION_WITH_COLS)
    1455              :     {
    1456           64 :       auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
    1457           64 :       ASSERT_STREQ ("/* before */\n"
    1458              :                      "foo = /* alpha */pub.meadow;/* beta */\n"
    1459              :                      "/* after */\n",
    1460              :                     new_content);
    1461              : 
    1462              :       /* Verify diff.  */
    1463           64 :       auto_free <char *> diff = edit.generate_diff (false);
    1464           64 :       ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
    1465              :                     " /* before */\n"
    1466              :                     "-foo = bar.field;\n"
    1467              :                     "+foo = /* alpha */pub.meadow;/* beta */\n"
    1468              :                     " /* after */\n", diff);
    1469           64 :     }
    1470           96 : }
    1471              : 
    1472              : /* Subroutine of test_applying_fixits_multiple_lines.
    1473              :    Add the text "CHANGED: " to the front of the given line.  */
    1474              : 
    1475              : static location_t
    1476          576 : change_line (change_set &edit, int line_num)
    1477              : {
    1478          576 :   const line_map_ordinary *ord_map
    1479          576 :     = LINEMAPS_LAST_ORDINARY_MAP (line_table);
    1480          576 :   const int column = 1;
    1481          576 :   location_t loc =
    1482          576 :     linemap_position_for_line_and_column (line_table, ord_map,
    1483              :                                           line_num, column);
    1484              : 
    1485          576 :   expanded_location exploc = expand_location (loc);
    1486          576 :   if (loc <= LINE_MAP_MAX_LOCATION_WITH_COLS)
    1487              :     {
    1488          344 :       ASSERT_EQ (line_num, exploc.line);
    1489          344 :       ASSERT_EQ (column, exploc.column);
    1490              :     }
    1491              : 
    1492          576 :   rich_location insert (line_table, loc);
    1493          576 :   insert.add_fixit_insert_before ("CHANGED: ");
    1494          576 :   edit.add_fixits (&insert);
    1495         1152 :   return loc;
    1496          576 : }
    1497              : 
    1498              : /* Subroutine of test_applying_fixits_multiple_lines.
    1499              :    Add the text "INSERTED\n" in front of the given line.  */
    1500              : 
    1501              : static location_t
    1502           96 : insert_line (change_set &edit, int line_num)
    1503              : {
    1504           96 :   const line_map_ordinary *ord_map
    1505           96 :     = LINEMAPS_LAST_ORDINARY_MAP (line_table);
    1506           96 :   const int column = 1;
    1507           96 :   location_t loc =
    1508           96 :     linemap_position_for_line_and_column (line_table, ord_map,
    1509              :                                           line_num, column);
    1510              : 
    1511           96 :   expanded_location exploc = expand_location (loc);
    1512           96 :   if (loc <= LINE_MAP_MAX_LOCATION_WITH_COLS)
    1513              :     {
    1514           56 :       ASSERT_EQ (line_num, exploc.line);
    1515           56 :       ASSERT_EQ (column, exploc.column);
    1516              :     }
    1517              : 
    1518           96 :   rich_location insert (line_table, loc);
    1519           96 :   insert.add_fixit_insert_before ("INSERTED\n");
    1520           96 :   edit.add_fixits (&insert);
    1521          192 :   return loc;
    1522           96 : }
    1523              : 
    1524              : /* Test of editing multiple lines within a long file,
    1525              :    to ensure that diffs are generated as expected.  */
    1526              : 
    1527              : static void
    1528           96 : test_applying_fixits_multiple_lines (const line_table_case &case_)
    1529              : {
    1530              :   /* Create a tempfile and write many lines of text to it.  */
    1531           96 :   named_temp_file tmp (".txt");
    1532           96 :   const char *filename = tmp.get_filename ();
    1533           96 :   FILE *f = fopen (filename, "w");
    1534           96 :   ASSERT_NE (f, NULL);
    1535        96096 :   for (int i = 1; i <= 1000; i++)
    1536        96000 :     fprintf (f, "line %i\n", i);
    1537           96 :   fclose (f);
    1538              : 
    1539           96 :   line_table_test ltt (case_);
    1540           96 :   linemap_add (line_table, LC_ENTER, false, filename, 1);
    1541           96 :   linemap_position_for_column (line_table, 127);
    1542              : 
    1543           96 :   file_cache fc;
    1544           96 :   change_set edit (fc);
    1545              : 
    1546              :   /* A run of consecutive lines.  */
    1547           96 :   change_line (edit, 2);
    1548           96 :   change_line (edit, 3);
    1549           96 :   change_line (edit, 4);
    1550           96 :   insert_line (edit, 5);
    1551              : 
    1552              :   /* A run of nearby lines, within the contextual limit.  */
    1553           96 :   change_line (edit, 150);
    1554           96 :   change_line (edit, 151);
    1555           96 :   location_t last_loc = change_line (edit, 153);
    1556              : 
    1557           96 :   if (last_loc > LINE_MAP_MAX_LOCATION_WITH_COLS)
    1558           40 :     return;
    1559              : 
    1560              :   /* Verify diff.  */
    1561           56 :   auto_free <char *> diff = edit.generate_diff (false);
    1562           56 :   ASSERT_STREQ ("@@ -1,7 +1,8 @@\n"
    1563              :                 " line 1\n"
    1564              :                 "-line 2\n"
    1565              :                 "-line 3\n"
    1566              :                 "-line 4\n"
    1567              :                 "+CHANGED: line 2\n"
    1568              :                 "+CHANGED: line 3\n"
    1569              :                 "+CHANGED: line 4\n"
    1570              :                 "+INSERTED\n"
    1571              :                 " line 5\n"
    1572              :                 " line 6\n"
    1573              :                 " line 7\n"
    1574              :                 "@@ -147,10 +148,10 @@\n"
    1575              :                 " line 147\n"
    1576              :                 " line 148\n"
    1577              :                 " line 149\n"
    1578              :                 "-line 150\n"
    1579              :                 "-line 151\n"
    1580              :                 "+CHANGED: line 150\n"
    1581              :                 "+CHANGED: line 151\n"
    1582              :                 " line 152\n"
    1583              :                 "-line 153\n"
    1584              :                 "+CHANGED: line 153\n"
    1585              :                 " line 154\n"
    1586              :                 " line 155\n"
    1587              :                 " line 156\n", diff);
    1588              : 
    1589              :   /* Ensure tmp stays alive until this point, so that the tempfile
    1590              :      persists until after the generate_diff call.  */
    1591           56 :   tmp.get_filename ();
    1592           96 : }
    1593              : 
    1594              : /* Test of converting an initializer for a named field from
    1595              :    the old GCC extension to C99 syntax.
    1596              :    Exercises a shrinking replacement followed by a growing
    1597              :    replacement on the same line.  */
    1598              : 
    1599              : static void
    1600           96 : test_applying_fixits_modernize_named_init (const line_table_case &case_)
    1601              : {
    1602              :   /* Create a tempfile and write some text to it.
    1603              :      .........................00000000011111111.
    1604              :      .........................12345678901234567.  */
    1605           96 :   const char *old_content = ("/* before */\n"
    1606              :                              "bar    : 1,\n"
    1607              :                              "/* after */\n");
    1608           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    1609           96 :   const char *filename = tmp.get_filename ();
    1610           96 :   line_table_test ltt (case_);
    1611           96 :   linemap_add (line_table, LC_ENTER, false, filename, 2);
    1612              : 
    1613           96 :   location_t c1 = linemap_position_for_column (line_table, 1);
    1614           96 :   location_t c3 = linemap_position_for_column (line_table, 3);
    1615           96 :   location_t c8 = linemap_position_for_column (line_table, 8);
    1616              : 
    1617           96 :   if (c8 > LINE_MAP_MAX_LOCATION_WITH_COLS)
    1618           32 :     return;
    1619              : 
    1620              :   /* Replace "bar" with ".".  */
    1621           64 :   rich_location r1 (line_table, c8);
    1622           64 :   r1.add_fixit_replace (source_range::from_locations (c1, c3),
    1623              :                         ".");
    1624              : 
    1625              :   /* Replace ":" with "bar =".   */
    1626           64 :   rich_location r2 (line_table, c8);
    1627           64 :   r2.add_fixit_replace (source_range::from_locations (c8, c8),
    1628              :                         "bar =");
    1629              : 
    1630              :   /* The order should not matter.  Do r1 then r2. */
    1631           64 :   {
    1632           64 :     file_cache fc;
    1633           64 :     change_set edit (fc);
    1634           64 :     edit.add_fixits (&r1);
    1635              : 
    1636              :     /* Verify state after first replacement.  */
    1637           64 :     {
    1638           64 :       auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
    1639              :       /* We should now have:
    1640              :          ............00000000011.
    1641              :          ............12345678901.  */
    1642           64 :       ASSERT_STREQ ("/* before */\n"
    1643              :                     ".    : 1,\n"
    1644              :                     "/* after */\n",
    1645              :                     new_content);
    1646              :       /* Location of the "1".  */
    1647           64 :       ASSERT_EQ (6, edit.get_effective_column (filename, 2, 8));
    1648              :       /* Location of the ",".  */
    1649           64 :       ASSERT_EQ (9, edit.get_effective_column (filename, 2, 11));
    1650           64 :     }
    1651              : 
    1652           64 :     edit.add_fixits (&r2);
    1653              : 
    1654           64 :     auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
    1655              :     /* Verify state after second replacement.
    1656              :        ............00000000011111111.
    1657              :        ............12345678901234567.  */
    1658           64 :     ASSERT_STREQ ("/* before */\n"
    1659              :                   ".    bar = 1,\n"
    1660              :                   "/* after */\n",
    1661              :                   new_content);
    1662           64 :   }
    1663              : 
    1664              :   /* Try again, doing r2 then r1; the new_content should be the same.  */
    1665           64 :   {
    1666           64 :     file_cache fc;
    1667           64 :     change_set edit (fc);
    1668           64 :     edit.add_fixits (&r2);
    1669           64 :     edit.add_fixits (&r1);
    1670           64 :     auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
    1671              :     /*.............00000000011111111.
    1672              :       .............12345678901234567.  */
    1673           64 :     ASSERT_STREQ ("/* before */\n"
    1674              :                   ".    bar = 1,\n"
    1675              :                   "/* after */\n",
    1676              :                   new_content);
    1677           64 :   }
    1678           96 : }
    1679              : 
    1680              : /* Test of a fixit affecting a file that can't be read.  */
    1681              : 
    1682              : static void
    1683            4 : test_applying_fixits_unreadable_file ()
    1684              : {
    1685            4 :   const char *filename = "this-does-not-exist.txt";
    1686            4 :   line_table_test ltt;
    1687            4 :   linemap_add (line_table, LC_ENTER, false, filename, 1);
    1688              : 
    1689            4 :   location_t loc = linemap_position_for_column (line_table, 1);
    1690              : 
    1691            4 :   rich_location insert (line_table, loc);
    1692            4 :   insert.add_fixit_insert_before ("change 1");
    1693            4 :   insert.add_fixit_insert_before ("change 2");
    1694              : 
    1695            4 :   file_cache fc;
    1696            4 :   change_set edit (fc);
    1697              :   /* Attempting to add the fixits affecting the unreadable file
    1698              :      should transition the edit from valid to invalid.  */
    1699            4 :   ASSERT_TRUE (edit.valid_p ());
    1700            4 :   edit.add_fixits (&insert);
    1701            4 :   ASSERT_FALSE (edit.valid_p ());
    1702            4 :   ASSERT_EQ (NULL, edit.get_content (filename));
    1703            4 :   ASSERT_EQ (NULL, edit.generate_diff (false));
    1704            4 : }
    1705              : 
    1706              : /* Verify that we gracefully handle an attempt to edit a line
    1707              :    that's beyond the end of the file.  */
    1708              : 
    1709              : static void
    1710            4 : test_applying_fixits_line_out_of_range ()
    1711              : {
    1712              :   /* Create a tempfile and write some text to it.
    1713              :      ........................00000000011111111.
    1714              :      ........................12345678901234567.  */
    1715            4 :   const char *old_content = "One-liner file\n";
    1716            4 :   temp_source_file tmp (SELFTEST_LOCATION, ".txt", old_content);
    1717            4 :   const char *filename = tmp.get_filename ();
    1718            4 :   line_table_test ltt;
    1719            4 :   linemap_add (line_table, LC_ENTER, false, filename, 2);
    1720              : 
    1721              :   /* Try to insert a string in line 2.  */
    1722            4 :   location_t loc = linemap_position_for_column (line_table, 1);
    1723              : 
    1724            4 :   rich_location insert (line_table, loc);
    1725            4 :   insert.add_fixit_insert_before ("change");
    1726              : 
    1727              :   /* Verify that attempting the insertion puts an change_set
    1728              :      into an invalid state.  */
    1729            4 :   file_cache fc;
    1730            4 :   change_set edit (fc);
    1731            4 :   ASSERT_TRUE (edit.valid_p ());
    1732            4 :   edit.add_fixits (&insert);
    1733            4 :   ASSERT_FALSE (edit.valid_p ());
    1734            4 :   ASSERT_EQ (NULL, edit.get_content (filename));
    1735            4 :   ASSERT_EQ (NULL, edit.generate_diff (false));
    1736            4 : }
    1737              : 
    1738              : /* Verify the boundary conditions of column values in fix-it
    1739              :    hints applied to change_set instances.  */
    1740              : 
    1741              : static void
    1742           96 : test_applying_fixits_column_validation (const line_table_case &case_)
    1743              : {
    1744              :   /* Create a tempfile and write some text to it.
    1745              :      ........................00000000011111111.
    1746              :      ........................12345678901234567.  */
    1747           96 :   const char *old_content = "One-liner file\n";
    1748           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".txt", old_content);
    1749           96 :   const char *filename = tmp.get_filename ();
    1750           96 :   line_table_test ltt (case_);
    1751           96 :   linemap_add (line_table, LC_ENTER, false, filename, 1);
    1752              : 
    1753           96 :   location_t c11 = linemap_position_for_column (line_table, 11);
    1754           96 :   location_t c14 = linemap_position_for_column (line_table, 14);
    1755           96 :   location_t c15 = linemap_position_for_column (line_table, 15);
    1756           96 :   location_t c16 = linemap_position_for_column (line_table, 16);
    1757              : 
    1758              :   /* Verify limits of valid columns in insertion fixits.  */
    1759              : 
    1760              :   /* Verify inserting at the end of the line.  */
    1761           96 :   {
    1762           96 :     rich_location richloc (line_table, c11);
    1763           96 :     richloc.add_fixit_insert_before (c15, " change");
    1764              : 
    1765              :     /* Col 15 is at the end of the line, so the insertion
    1766              :        should succeed.  */
    1767           96 :     file_cache fc;
    1768           96 :     change_set edit (fc);
    1769           96 :     edit.add_fixits (&richloc);
    1770           96 :     auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
    1771           96 :     if (c15 <= LINE_MAP_MAX_LOCATION_WITH_COLS)
    1772           64 :       ASSERT_STREQ ("One-liner file change\n", new_content);
    1773              :     else
    1774           32 :       ASSERT_EQ (NULL, new_content);
    1775           96 :   }
    1776              : 
    1777              :   /* Verify inserting beyond the end of the line.  */
    1778           96 :   {
    1779           96 :     rich_location richloc (line_table, c11);
    1780           96 :     richloc.add_fixit_insert_before (c16, " change");
    1781              : 
    1782              :     /* Col 16 is beyond the end of the line, so the insertion
    1783              :        should fail gracefully.  */
    1784           96 :     file_cache fc;
    1785           96 :     change_set edit (fc);
    1786           96 :     ASSERT_TRUE (edit.valid_p ());
    1787           96 :     edit.add_fixits (&richloc);
    1788           96 :     ASSERT_FALSE (edit.valid_p ());
    1789           96 :     ASSERT_EQ (NULL, edit.get_content (filename));
    1790           96 :     ASSERT_EQ (NULL, edit.generate_diff (false));
    1791           96 :   }
    1792              : 
    1793              :   /* Verify limits of valid columns in replacement fixits.  */
    1794              : 
    1795              :   /* Verify replacing the end of the line.  */
    1796           96 :   {
    1797           96 :     rich_location richloc (line_table, c11);
    1798           96 :     source_range range = source_range::from_locations (c11, c14);
    1799           96 :     richloc.add_fixit_replace (range, "change");
    1800              : 
    1801              :     /* Col 14 is at the end of the line, so the replacement
    1802              :        should succeed.  */
    1803           96 :     file_cache fc;
    1804           96 :     change_set edit (fc);
    1805           96 :     edit.add_fixits (&richloc);
    1806           96 :     auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
    1807           96 :     if (c14 <= LINE_MAP_MAX_LOCATION_WITH_COLS)
    1808           64 :       ASSERT_STREQ ("One-liner change\n", new_content);
    1809              :     else
    1810           32 :       ASSERT_EQ (NULL, new_content);
    1811           96 :   }
    1812              : 
    1813              :   /* Verify going beyond the end of the line.  */
    1814           96 :   {
    1815           96 :     rich_location richloc (line_table, c11);
    1816           96 :     source_range range = source_range::from_locations (c11, c15);
    1817           96 :     richloc.add_fixit_replace (range, "change");
    1818              : 
    1819              :     /* Col 15 is after the end of the line, so the replacement
    1820              :        should fail; verify that the attempt fails gracefully.  */
    1821           96 :     file_cache fc;
    1822           96 :     change_set edit (fc);
    1823           96 :     ASSERT_TRUE (edit.valid_p ());
    1824           96 :     edit.add_fixits (&richloc);
    1825           96 :     ASSERT_FALSE (edit.valid_p ());
    1826           96 :     ASSERT_EQ (NULL, edit.get_content (filename));
    1827           96 :     ASSERT_EQ (NULL, edit.generate_diff (false));
    1828           96 :   }
    1829           96 : }
    1830              : 
    1831              : static void
    1832            4 : run_all_tests ()
    1833              : {
    1834            4 :   test_get_content ();
    1835            4 :   for_each_line_table_case (test_applying_fixits_insert_before);
    1836            4 :   for_each_line_table_case (test_applying_fixits_insert_after);
    1837            4 :   for_each_line_table_case (test_applying_fixits_insert_after_at_line_end);
    1838            4 :   for_each_line_table_case (test_applying_fixits_insert_after_failure);
    1839            4 :   for_each_line_table_case (test_applying_fixits_insert_containing_newline);
    1840            4 :   for_each_line_table_case (test_applying_fixits_growing_replace);
    1841            4 :   for_each_line_table_case (test_applying_fixits_shrinking_replace);
    1842            4 :   for_each_line_table_case (test_applying_fixits_replace_containing_newline);
    1843            4 :   for_each_line_table_case (test_applying_fixits_remove);
    1844            4 :   for_each_line_table_case (test_applying_fixits_multiple);
    1845            4 :   for_each_line_table_case (test_applying_fixits_multiple_lines);
    1846            4 :   for_each_line_table_case (test_applying_fixits_modernize_named_init);
    1847            4 :   test_applying_fixits_unreadable_file ();
    1848            4 :   test_applying_fixits_line_out_of_range ();
    1849            4 :   for_each_line_table_case (test_applying_fixits_column_validation);
    1850            4 : }
    1851              : 
    1852              : } // namespace diagnostics::changes::selftest
    1853              : 
    1854              : #endif /* CHECKING_P */
    1855              : 
    1856              : } // namespace diagnostics::changes
    1857              : 
    1858              : #if CHECKING_P
    1859              : 
    1860              : namespace selftest { // diagnostics::selftest
    1861              : 
    1862              : /* Run all of the selftests within this file.  */
    1863              : 
    1864              : void
    1865            4 : changes_cc_tests ()
    1866              : {
    1867            4 :   diagnostics::changes::selftest::run_all_tests ();
    1868            4 : }
    1869              : 
    1870              : } // namespace selftest
    1871              : 
    1872              : #endif /* CHECKING_P */
    1873              : 
    1874              : } // namespace diagnostics
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.