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