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