]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/edit-context.c
libgo: update to Go 1.12.2
[thirdparty/gcc.git] / gcc / edit-context.c
index 515833c1af3fd2bb3ba496fbfb346de04b7ed647..d3246ab03348368d9fa25b2800b175d969da6128 100644 (file)
@@ -1,5 +1,5 @@
 /* Determining the results of applying fix-it hints.
-   Copyright (C) 2016 Free Software Foundation, Inc.
+   Copyright (C) 2016-2019 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -45,8 +45,6 @@ class edit_context;
 class edited_file;
 class edited_line;
 class line_event;
-  class insert_event;
-  class replace_event;
 
 /* A struct to hold the params of a print_diff call.  */
 
@@ -71,11 +69,10 @@ class edited_file
   const char *get_filename () const { return m_filename; }
   char *get_content ();
 
-  bool apply_insert (int line, int column, const char *str, int len);
-  bool apply_replace (int line, int start_column,
-                     int finish_column,
-                     const char *replacement_str,
-                     int replacement_len);
+  bool apply_fixit (int line, int start_column,
+                   int next_column,
+                   const char *replacement_str,
+                   int replacement_len);
   int get_effective_column (int line, int column);
 
   static int call_print_diff (const char *, edited_file *file,
@@ -89,19 +86,52 @@ class edited_file
  private:
   bool print_content (pretty_printer *pp);
   void print_diff (pretty_printer *pp, bool show_filenames);
+  int print_diff_hunk (pretty_printer *pp, int old_start_of_hunk,
+                      int old_end_of_hunk, int new_start_of_hunk);
   edited_line *get_line (int line);
   edited_line *get_or_insert_line (int line);
   int get_num_lines (bool *missing_trailing_newline);
 
+  int get_effective_line_count (int old_start_of_hunk,
+                               int old_end_of_hunk);
+
+  void print_run_of_changed_lines (pretty_printer *pp,
+                                  int start_of_run,
+                                  int end_of_run);
+
   const char *m_filename;
   typed_splay_tree<int, edited_line *> m_edited_lines;
   int m_num_lines;
 };
 
+/* A line added before an edited_line.  */
+
+class added_line
+{
+ public:
+  added_line (const char *content, int len)
+  : m_content (xstrndup (content, len)), m_len (len) {}
+  ~added_line () { free (m_content); }
+
+  const char *get_content () const { return m_content; }
+  int get_len () const { return m_len; }
+
+ private:
+  char *m_content;
+  int m_len;
+};
+
 /* The state of one edited line within an edited_file.
    As well as the current content of the line, it contains a record of
    the changes, so that further changes can be applied in the correct
-   place.  */
+   place.
+
+   When handling fix-it hints containing newlines, new lines are added
+   as added_line predecessors to an edited_line.  Hence it's possible
+   for an "edited_line" to not actually have been changed, but to merely
+   be a placeholder for the lines added before it.  This can be tested
+   for with actuall_edited_p, and has a slight effect on how diff hunks
+   are generated.  */
 
 class edited_line
 {
@@ -112,72 +142,49 @@ class edited_line
 
   int get_line_num () const { return m_line_num; }
   const char *get_content () const { return m_content; }
+  int get_len () const { return m_len; }
 
   int get_effective_column (int orig_column) const;
-  bool apply_insert (int column, const char *str, int len);
-  bool apply_replace (int start_column,
-                     int finish_column,
-                     const char *replacement_str,
-                     int replacement_len);
+  bool apply_fixit (int start_column,
+                   int next_column,
+                   const char *replacement_str,
+                   int replacement_len);
+
+  int get_effective_line_count () const;
+
+  /* Has the content of this line actually changed, or are we merely
+     recording predecessor added_lines?  */
+  bool actually_edited_p () const { return m_line_events.length () > 0; }
+
+  void print_content (pretty_printer *pp) const;
+  void print_diff_lines (pretty_printer *pp) const;
 
  private:
   void ensure_capacity (int len);
   void ensure_terminated ();
-  void print_content (pretty_printer *pp) const;
 
   int m_line_num;
   char *m_content;
   int m_len;
   int m_alloc_sz;
-  auto_vec <line_event *> m_line_events;
-};
-
-/* Abstract base class for representing events that have occurred
-   on one line of one file.  */
-
-class line_event
-{
- public:
-  virtual ~line_event () {}
-  virtual int get_effective_column (int orig_column) const = 0;
-};
-
-/* Concrete subclass of line_event: an insertion of some text
-   at some column on the line.
-
-   Subsequent events will need their columns adjusting if they're
-   are on this line and their column is >= the insertion point.  */
-
-class insert_event : public line_event
-{
- public:
-  insert_event (int column, int len) : m_column (column), m_len (len) {}
-  int get_effective_column (int orig_column) const FINAL OVERRIDE
-  {
-    if (orig_column >= m_column)
-      return orig_column + m_len;
-    else
-      return orig_column;
-  }
-
- private:
-  int m_column;
-  int m_len;
+  auto_vec <line_event> m_line_events;
+  auto_vec <added_line *> m_predecessors;
 };
 
-/* Concrete subclass of line_event: the replacement of some text
-   betweeen some columns on the line.
+/* Class for representing edit events that have occurred on one line of
+   one file: the replacement of some text betweeen some columns
+   on the line.
 
    Subsequent events will need their columns adjusting if they're
-   are on this line and their column is >= the finish point.  */
+   are on this line and their column is >= the start point.  */
 
-class replace_event : public line_event
+class line_event
 {
  public:
-  replace_event (int start, int finish, int len) : m_start (start),
-    m_finish (finish), m_delta (len - (finish + 1 - start)) {}
+  line_event (int start, int next, int len) : m_start (start),
+    m_delta (len - (next - start)) {}
 
-  int get_effective_column (int orig_column) const FINAL OVERRIDE
+  int get_effective_column (int orig_column) const
   {
     if (orig_column >= m_start)
       return orig_column += m_delta;
@@ -187,10 +194,15 @@ class replace_event : public line_event
 
  private:
   int m_start;
-  int m_finish;
   int m_delta;
 };
 
+/* Forward decls.  */
+
+static void
+print_diff_line (pretty_printer *pp, char prefix_char,
+                const char *line, int line_size);
+
 /* Implementation of class edit_context.  */
 
 /* edit_context's ctor.  */
@@ -216,27 +228,8 @@ edit_context::add_fixits (rich_location *richloc)
   for (unsigned i = 0; i < richloc->get_num_fixit_hints (); i++)
     {
       const fixit_hint *hint = richloc->get_fixit_hint (i);
-      switch (hint->get_kind ())
-       {
-       case fixit_hint::INSERT:
-         if (!apply_insert ((const fixit_insert *)hint))
-           {
-             /* Failure.  */
-             m_valid = false;
-             return;
-           }
-         break;
-       case fixit_hint::REPLACE:
-         if (!apply_replace ((const fixit_replace *)hint))
-           {
-             /* Failure.  */
-             m_valid = false;
-             return;
-           }
-         break;
-       default:
-         gcc_unreachable ();
-       }
+      if (!apply_fixit (hint))
+       m_valid = false;
     }
 }
 
@@ -297,45 +290,25 @@ edit_context::print_diff (pretty_printer *pp, bool show_filenames)
    applied, or false otherwise.  */
 
 bool
-edit_context::apply_insert (const fixit_insert *insert)
-{
-  expanded_location exploc = expand_location (insert->get_location ());
-
-  if (exploc.column == 0)
-    return false;
-
-  edited_file &file = get_or_insert_file (exploc.file);
-  if (!m_valid)
-    return false;
-  return file.apply_insert (exploc.line, exploc.column, insert->get_string (),
-                           insert->get_length ());
-}
-
-/* Attempt to apply the given fixit.  Return true if it can be
-   applied, or false otherwise.  */
-
-bool
-edit_context::apply_replace (const fixit_replace *replace)
+edit_context::apply_fixit (const fixit_hint *hint)
 {
-  source_range range = replace->get_range ();
-
-  expanded_location start = expand_location (range.m_start);
-  expanded_location finish = expand_location (range.m_finish);
-  if (start.file != finish.file)
+  expanded_location start = expand_location (hint->get_start_loc ());
+  expanded_location next_loc = expand_location (hint->get_next_loc ());
+  if (start.file != next_loc.file)
     return false;
-  if (start.line != finish.line)
+  if (start.line != next_loc.line)
     return false;
   if (start.column == 0)
     return false;
-  if (finish.column == 0)
+  if (next_loc.column == 0)
     return false;
 
   edited_file &file = get_or_insert_file (start.file);
   if (!m_valid)
     return false;
-  return file.apply_replace (start.line, start.column, finish.column,
-                            replace->get_string (),
-                            replace->get_length ());
+  return file.apply_fixit (start.line, start.column, next_loc.column,
+                          hint->get_string (),
+                          hint->get_length ());
 }
 
 /* Locate the edited_file * for FILENAME, if any
@@ -404,37 +377,21 @@ edited_file::get_content ()
   return xstrdup (pp_formatted_text (&pp));
 }
 
-/* Attempt to insert the string INSERT_STR with length INSERT_LEN
-   at LINE and COLUMN, updating the in-memory copy of the line, and
-   the record of edits to the line.  */
-
-bool
-edited_file::apply_insert (int line, int column,
-                          const char *insert_str,
-                          int insert_len)
-{
-  edited_line *el = get_or_insert_line (line);
-  if (!el)
-    return false;
-  return el->apply_insert (column, insert_str, insert_len);
-}
-
-/* Attempt to replace columns START_COLUMN through FINISH_COLUMN of LINE
-   with the string REPLACEMENT_STR of length REPLACEMENT_LEN,
+/* Attempt to replace columns START_COLUMN up to but not including NEXT_COLUMN
+   of LINE with the string REPLACEMENT_STR of length REPLACEMENT_LEN,
    updating the in-memory copy of the line, and the record of edits to
    the line.  */
 
 bool
-edited_file::apply_replace (int line, int start_column,
-                           int finish_column,
-                           const char *replacement_str,
-                           int replacement_len)
+edited_file::apply_fixit (int line, int start_column, int next_column,
+                         const char *replacement_str,
+                         int replacement_len)
 {
   edited_line *el = get_or_insert_line (line);
   if (!el)
     return false;
-  return el->apply_replace (start_column, finish_column, replacement_str,
-                           replacement_len);
+  return el->apply_fixit (start_column, next_column, replacement_str,
+                         replacement_len);
 }
 
 /* Given line LINE, map from COLUMN in the input file to its current
@@ -461,15 +418,13 @@ edited_file::print_content (pretty_printer *pp)
     {
       edited_line *el = get_line (line_num);
       if (el)
-       pp_string (pp, el->get_content ());
+       el->print_content (pp);
       else
        {
-         int len;
-         const char *line
-           = location_get_source_line (m_filename, line_num, &len);
+         char_span line = location_get_source_line (m_filename, line_num);
          if (!line)
            return false;
-         for (int i = 0; i < len; i++)
+         for (size_t i = 0; i < line.length (); i++)
            pp_character (pp, line[i]);
        }
       if (line_num < line_count)
@@ -503,6 +458,10 @@ edited_file::print_diff (pretty_printer *pp, bool show_filenames)
 
   const int context_lines = 3;
 
+  /* Track new line numbers minus old line numbers.  */
+
+  int line_delta = 0;
+
   while (el)
     {
       int start_of_hunk = el->get_line_num ();
@@ -518,91 +477,152 @@ edited_file::print_diff (pretty_printer *pp, bool show_filenames)
            = m_edited_lines.successor (el->get_line_num ());
          if (!next_el)
            break;
-         if (el->get_line_num () + context_lines
+
+         int end_of_printed_hunk = el->get_line_num () + context_lines;
+         if (!el->actually_edited_p ())
+           end_of_printed_hunk--;
+
+         if (end_of_printed_hunk
              >= next_el->get_line_num () - context_lines)
            el = next_el;
          else
            break;
        }
+
       int end_of_hunk = el->get_line_num ();
       end_of_hunk += context_lines;
+      if (!el->actually_edited_p ())
+       end_of_hunk--;
       if (end_of_hunk > line_count)
        end_of_hunk = line_count;
 
-      int num_lines = end_of_hunk - start_of_hunk + 1;
+      int new_start_of_hunk = start_of_hunk + line_delta;
+      line_delta += print_diff_hunk (pp, start_of_hunk, end_of_hunk,
+                                    new_start_of_hunk);
+      el = m_edited_lines.successor (el->get_line_num ());
+    }
+}
 
-      pp_string (pp, colorize_start (pp_show_color (pp), "diff-hunk"));
-      pp_printf (pp, "@@ -%i,%i +%i,%i @@\n", start_of_hunk, num_lines,
-                start_of_hunk, num_lines);
-      pp_string (pp, colorize_stop (pp_show_color (pp)));
+/* Print one hunk within a unified diff to PP, covering the
+   given range of lines.  OLD_START_OF_HUNK and OLD_END_OF_HUNK are
+   line numbers in the unedited version of the file.
+   NEW_START_OF_HUNK is a line number in the edited version of the file.
+   Return the change in the line count within the hunk.  */
+
+int
+edited_file::print_diff_hunk (pretty_printer *pp, int old_start_of_hunk,
+                             int old_end_of_hunk, int new_start_of_hunk)
+{
+  int old_num_lines = old_end_of_hunk - old_start_of_hunk + 1;
+  int new_num_lines
+    = get_effective_line_count (old_start_of_hunk, old_end_of_hunk);
 
-      int line_num = start_of_hunk;
-      while (line_num <= end_of_hunk)
+  pp_string (pp, colorize_start (pp_show_color (pp), "diff-hunk"));
+  pp_printf (pp, "@@ -%i,%i +%i,%i @@\n", old_start_of_hunk, old_num_lines,
+            new_start_of_hunk, new_num_lines);
+  pp_string (pp, colorize_stop (pp_show_color (pp)));
+
+  int line_num = old_start_of_hunk;
+  while (line_num <= old_end_of_hunk)
+    {
+      edited_line *el = get_line (line_num);
+      if (el)
        {
-         edited_line *el = get_line (line_num);
-         if (el)
-           {
-             /* We have an edited line.
-                Consolidate into runs of changed lines.  */
-             const int first_changed_line_in_run = line_num;
-             while (get_line (line_num))
-               line_num++;
-             const int last_changed_line_in_run = line_num - 1;
-
-             pp_string (pp, colorize_start (pp_show_color (pp),
-                                            "diff-delete"));
-
-             /* Show old version of lines.  */
-             for (line_num = first_changed_line_in_run;
-                  line_num <= last_changed_line_in_run;
-                  line_num++)
-               {
-                 int line_size;
-                 const char *old_line
-                   = location_get_source_line (m_filename, line_num,
-                                               &line_size);
-                 pp_character (pp, '-');
-                 for (int i = 0; i < line_size; i++)
-                   pp_character (pp, old_line[i]);
-                 pp_character (pp, '\n');
-               }
-
-             pp_string (pp, colorize_stop (pp_show_color (pp)));
-
-             pp_string (pp, colorize_start (pp_show_color (pp),
-                                            "diff-insert"));
-
-             /* Show new version of lines.  */
-             for (line_num = first_changed_line_in_run;
-                  line_num <= last_changed_line_in_run;
-                  line_num++)
-               {
-                 edited_line *el_in_run = get_line (line_num);
-                 gcc_assert (el_in_run);
-                 pp_character (pp, '+');
-                 pp_string (pp, el_in_run->get_content ());
-                 pp_character (pp, '\n');
-               }
-
-             pp_string (pp, colorize_stop (pp_show_color (pp)));
-           }
-         else
-           {
-             /* Unchanged line.  */
-             int line_size;
-             const char *old_line
-               = location_get_source_line (m_filename, line_num,
-                                           &line_size);
-             pp_character (pp, ' ');
-             for (int i = 0; i < line_size; i++)
-               pp_character (pp, old_line[i]);
-             pp_character (pp, '\n');
-             line_num++;
-           }
+         /* We have an edited line.
+            Consolidate into runs of changed lines.  */
+         const int first_changed_line_in_run = line_num;
+         while (get_line (line_num))
+           line_num++;
+         const int last_changed_line_in_run = line_num - 1;
+         print_run_of_changed_lines (pp, first_changed_line_in_run,
+                                     last_changed_line_in_run);
        }
+      else
+       {
+         /* Unchanged line.  */
+         char_span old_line = location_get_source_line (m_filename, line_num);
+         print_diff_line (pp, ' ', old_line.get_buffer (), old_line.length ());
+         line_num++;
+       }
+    }
 
-      el = m_edited_lines.successor (el->get_line_num ());
+  return new_num_lines - old_num_lines;
+}
+
+/* Subroutine of edited_file::print_diff_hunk: given a run of lines
+   from START_OF_RUN to END_OF_RUN that all have edited_line instances,
+   print the diff to PP.  */
+
+void
+edited_file::print_run_of_changed_lines (pretty_printer *pp,
+                                        int start_of_run,
+                                        int end_of_run)
+{
+  /* Show old version of lines.  */
+  pp_string (pp, colorize_start (pp_show_color (pp),
+                                "diff-delete"));
+  for (int line_num = start_of_run;
+       line_num <= end_of_run;
+       line_num++)
+    {
+      edited_line *el_in_run = get_line (line_num);
+      gcc_assert (el_in_run);
+      if (el_in_run->actually_edited_p ())
+       {
+         char_span old_line = location_get_source_line (m_filename, line_num);
+         print_diff_line (pp, '-', old_line.get_buffer (),
+                          old_line.length ());
+       }
+    }
+  pp_string (pp, colorize_stop (pp_show_color (pp)));
+
+  /* Show new version of lines.  */
+  pp_string (pp, colorize_start (pp_show_color (pp),
+                                "diff-insert"));
+  for (int line_num = start_of_run;
+       line_num <= end_of_run;
+       line_num++)
+    {
+      edited_line *el_in_run = get_line (line_num);
+      gcc_assert (el_in_run);
+      el_in_run->print_diff_lines (pp);
     }
+  pp_string (pp, colorize_stop (pp_show_color (pp)));
+}
+
+/* Print one line within a diff, starting with PREFIX_CHAR,
+   followed by the LINE of content, of length LEN.  LINE is
+   not necessarily 0-terminated.  Print a trailing newline.  */
+
+static void
+print_diff_line (pretty_printer *pp, char prefix_char,
+                const char *line, int len)
+{
+  pp_character (pp, prefix_char);
+  for (int i = 0; i < len; i++)
+    pp_character (pp, line[i]);
+  pp_character (pp, '\n');
+}
+
+/* Determine the number of lines that will be present after
+   editing for the range of lines from OLD_START_OF_HUNK to
+   OLD_END_OF_HUNK inclusive.  */
+
+int
+edited_file::get_effective_line_count (int old_start_of_hunk,
+                                      int old_end_of_hunk)
+{
+  int line_count = 0;
+  for (int old_line_num = old_start_of_hunk; old_line_num <= old_end_of_hunk;
+       old_line_num++)
+    {
+      edited_line *el = get_line (old_line_num);
+      if (el)
+       line_count += el->get_effective_line_count ();
+      else
+       line_count++;
+    }
+  return line_count;
 }
 
 /* Get the state of LINE within the file, or NULL if it is untouched.  */
@@ -645,10 +665,8 @@ edited_file::get_num_lines (bool *missing_trailing_newline)
       m_num_lines = 0;
       while (true)
        {
-         int line_size;
-         const char *line
-           = location_get_source_line (m_filename, m_num_lines + 1,
-                                       &line_size);
+         char_span line
+           = location_get_source_line (m_filename, m_num_lines + 1);
          if (line)
            m_num_lines++;
          else
@@ -666,14 +684,15 @@ edited_file::get_num_lines (bool *missing_trailing_newline)
 edited_line::edited_line (const char *filename, int line_num)
 : m_line_num (line_num),
   m_content (NULL), m_len (0), m_alloc_sz (0),
-  m_line_events ()
+  m_line_events (),
+  m_predecessors ()
 {
-  const char *line = location_get_source_line (filename, line_num,
-                                              &m_len);
+  char_span line = location_get_source_line (filename, line_num);
   if (!line)
     return;
+  m_len = line.length ();
   ensure_capacity (m_len);
-  memcpy (m_content, line, m_len);
+  memcpy (m_content, line.get_buffer (), m_len);
   ensure_terminated ();
 }
 
@@ -681,12 +700,12 @@ edited_line::edited_line (const char *filename, int line_num)
 
 edited_line::~edited_line ()
 {
-  free (m_content);
+  unsigned i;
+  added_line *pred;
 
-  int i;
-  line_event *event;
-  FOR_EACH_VEC_ELT (m_line_events, i, event)
-    delete event;
+  free (m_content);
+  FOR_EACH_VEC_ELT (m_predecessors, i, pred)
+    delete pred;
 }
 
 /* A callback for deleting edited_line *, for use as a
@@ -711,85 +730,52 @@ edited_line::get_effective_column (int orig_column) const
   return orig_column;
 }
 
-/* Attempt to insert the string INSERT_STR with length INSERT_LEN at COLUMN
-   of this line, updating the in-memory copy of the line, and the record
-   of edits to it.
+/* Attempt to replace columns START_COLUMN up to but not including
+   NEXT_COLUMN of the line with the string REPLACEMENT_STR of
+   length REPLACEMENT_LEN, updating the in-memory copy of the line,
+   and the record of edits to the line.
    Return true if successful; false if an error occurred.  */
 
 bool
-edited_line::apply_insert (int column, const char *insert_str,
-                          int insert_len)
+edited_line::apply_fixit (int start_column,
+                         int next_column,
+                         const char *replacement_str,
+                         int replacement_len)
 {
-  column = get_effective_column (column);
+  /* Handle newlines.  They will only ever be at the end of the
+     replacement text, thanks to the filtering in rich_location.  */
+  if (replacement_len > 1)
+    if (replacement_str[replacement_len - 1] == '\n')
+      {
+       /* Stash in m_predecessors, stripping off newline.  */
+       m_predecessors.safe_push (new added_line (replacement_str,
+                                                 replacement_len - 1));
+       return true;
+      }
 
-  int start_offset = column - 1;
-  gcc_assert (start_offset >= 0);
-  if (start_offset > m_len)
-    return false;
-
-  /* Ensure buffer is big enough.  */
-  size_t new_len = m_len + insert_len;
-  ensure_capacity (new_len);
-
-  char *suffix = m_content + start_offset;
-  gcc_assert (suffix <= m_content + m_len);
-  size_t len_suffix = (m_content + m_len) - suffix;
-
-  /* Move successor content into position.  They overlap, so use memmove.  */
-  memmove (m_content + start_offset + insert_len,
-          suffix, len_suffix);
-
-  /* Replace target content.  They don't overlap, so use memcpy.  */
-  memcpy (m_content + start_offset,
-         insert_str,
-         insert_len);
-
-  m_len = new_len;
-
-  ensure_terminated ();
-
-  /* Record the insertion, so that future changes to the line can have
-     their column information adjusted accordingly.  */
-  m_line_events.safe_push (new insert_event (column, insert_len));
-
-  return true;
-}
-
-/* Attempt to replace columns START_COLUMN through FINISH_COLUMN of the line
-   with the string REPLACEMENT_STR of length REPLACEMENT_LEN,
-   updating the in-memory copy of the line, and the record of edits to
-   the line.
-   Return true if successful; false if an error occurred.  */
-
-bool
-edited_line::apply_replace (int start_column,
-                           int finish_column,
-                           const char *replacement_str,
-                           int replacement_len)
-{
   start_column = get_effective_column (start_column);
-  finish_column = get_effective_column (finish_column);
+  next_column = get_effective_column (next_column);
 
   int start_offset = start_column - 1;
-  int end_offset = finish_column - 1;
+  int next_offset = next_column - 1;
 
   gcc_assert (start_offset >= 0);
-  gcc_assert (end_offset >= 0);
+  gcc_assert (next_offset >= 0);
 
-  if (start_column > finish_column)
+  if (start_column > next_column)
     return false;
-  if (start_offset >= m_len)
+  if (start_offset >= (m_len + 1))
     return false;
-  if (end_offset >= m_len)
+  if (next_offset >= (m_len + 1))
     return false;
 
-  size_t victim_len = end_offset - start_offset + 1;
+  size_t victim_len = next_offset - start_offset;
 
   /* Ensure buffer is big enough.  */
   size_t new_len = m_len + replacement_len - victim_len;
   ensure_capacity (new_len);
 
-  char *suffix = m_content + end_offset + 1;
+  char *suffix = m_content + next_offset;
   gcc_assert (suffix <= m_content + m_len);
   size_t len_suffix = (m_content + m_len) - suffix;
 
@@ -808,11 +794,62 @@ edited_line::apply_replace (int start_column,
 
   /* Record the replacement, so that future changes to the line can have
      their column information adjusted accordingly.  */
-  m_line_events.safe_push (new replace_event (start_column, finish_column,
-                                             replacement_len));
+  m_line_events.safe_push (line_event (start_column, next_column,
+                                      replacement_len));
   return true;
 }
 
+/* Determine the number of lines that will be present after
+   editing for this line.  Typically this is just 1, but
+   if newlines have been added before this line, they will
+   also be counted.  */
+
+int
+edited_line::get_effective_line_count () const
+{
+  return m_predecessors.length () + 1;
+}
+
+/* Subroutine of edited_file::print_content.
+   Print this line and any new lines added before it, to PP.  */
+
+void
+edited_line::print_content (pretty_printer *pp) const
+{
+  unsigned i;
+  added_line *pred;
+  FOR_EACH_VEC_ELT (m_predecessors, i, pred)
+    {
+      pp_string (pp, pred->get_content ());
+      pp_newline (pp);
+    }
+  pp_string (pp, m_content);
+}
+
+/* Subroutine of edited_file::print_run_of_changed_lines for
+   printing diff hunks to PP.
+   Print the '+' line for this line, and any newlines added
+   before it.
+   Note that if this edited_line was actually edited, the '-'
+   line has already been printed.  If it wasn't, then we merely
+   have a placeholder edited_line for adding newlines to, and
+   we need to print a ' ' line for the edited_line as we haven't
+   printed it yet.  */
+
+void
+edited_line::print_diff_lines (pretty_printer *pp) const
+{
+  unsigned i;
+  added_line *pred;
+  FOR_EACH_VEC_ELT (m_predecessors, i, pred)
+    print_diff_line (pp, '+', pred->get_content (),
+                    pred->get_len ());
+  if (actually_edited_p ())
+    print_diff_line (pp, '+', m_content, m_len);
+  else
+    print_diff_line (pp, ' ', m_content, m_len);
+}
+
 /* Ensure that the buffer for m_content is at least large enough to hold
    a string of length LEN and its 0-terminator, doubling on repeated
    allocations.  */
@@ -902,10 +939,10 @@ test_get_content ()
   }
 }
 
-/* Test applying an "insert" fixit.  */
+/* Test applying an "insert" fixit, using insert_before.  */
 
 static void
-test_applying_fixits_insert (const line_table_case &case_)
+test_applying_fixits_insert_before (const line_table_case &case_)
 {
   /* Create a tempfile and write some text to it.
      .........................0000000001111111.
@@ -921,7 +958,7 @@ test_applying_fixits_insert (const line_table_case &case_)
   /* Add a comment in front of "bar.field".  */
   location_t start = linemap_position_for_column (line_table, 7);
   rich_location richloc (line_table, start);
-  richloc.add_fixit_insert ("/* inserted */");
+  richloc.add_fixit_insert_before ("/* inserted */");
 
   if (start > LINE_MAP_MAX_LOCATION_WITH_COLS)
     return;
@@ -955,6 +992,193 @@ test_applying_fixits_insert (const line_table_case &case_)
                " /* after */\n", diff);
 }
 
+/* Test applying an "insert" fixit, using insert_after, with
+   a range of length > 1 (to ensure that the end-point of
+   the input range is used).  */
+
+static void
+test_applying_fixits_insert_after (const line_table_case &case_)
+{
+  /* Create a tempfile and write some text to it.
+     .........................0000000001111111.
+     .........................1234567890123456.  */
+  const char *old_content = ("/* before */\n"
+                            "foo = bar.field;\n"
+                            "/* after */\n");
+  temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
+  const char *filename = tmp.get_filename ();
+  line_table_test ltt (case_);
+  linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
+
+  /* Add a comment after "field".  */
+  location_t start = linemap_position_for_column (line_table, 11);
+  location_t finish = linemap_position_for_column (line_table, 15);
+  location_t field = make_location (start, start, finish);
+  rich_location richloc (line_table, field);
+  richloc.add_fixit_insert_after ("/* inserted */");
+
+  if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
+    return;
+
+  /* Verify that the text was inserted after the end of "field". */
+  edit_context edit;
+  edit.add_fixits (&richloc);
+  auto_free <char *> new_content = edit.get_content (filename);
+  ASSERT_STREQ ("/* before */\n"
+               "foo = bar.field/* inserted */;\n"
+               "/* after */\n", new_content);
+
+  /* Verify diff.  */
+  auto_free <char *> diff = edit.generate_diff (false);
+  ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
+               " /* before */\n"
+               "-foo = bar.field;\n"
+               "+foo = bar.field/* inserted */;\n"
+               " /* after */\n", diff);
+}
+
+/* Test applying an "insert" fixit, using insert_after at the end of
+   a line (contrast with test_applying_fixits_insert_after_failure
+   below).  */
+
+static void
+test_applying_fixits_insert_after_at_line_end (const line_table_case &case_)
+{
+  /* Create a tempfile and write some text to it.
+     .........................0000000001111111.
+     .........................1234567890123456.  */
+  const char *old_content = ("/* before */\n"
+                            "foo = bar.field;\n"
+                            "/* after */\n");
+  temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
+  const char *filename = tmp.get_filename ();
+  line_table_test ltt (case_);
+  linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
+
+  /* Add a comment after the semicolon.  */
+  location_t loc = linemap_position_for_column (line_table, 16);
+  rich_location richloc (line_table, loc);
+  richloc.add_fixit_insert_after ("/* inserted */");
+
+  if (loc > LINE_MAP_MAX_LOCATION_WITH_COLS)
+    return;
+
+  edit_context edit;
+  edit.add_fixits (&richloc);
+  auto_free <char *> new_content = edit.get_content (filename);
+  ASSERT_STREQ ("/* before */\n"
+               "foo = bar.field;/* inserted */\n"
+               "/* after */\n", new_content);
+
+  /* Verify diff.  */
+  auto_free <char *> diff = edit.generate_diff (false);
+  ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
+               " /* before */\n"
+               "-foo = bar.field;\n"
+               "+foo = bar.field;/* inserted */\n"
+               " /* after */\n", diff);
+}
+
+/* Test of a failed attempt to apply an "insert" fixit, using insert_after,
+   due to the relevant linemap ending.  Contrast with
+   test_applying_fixits_insert_after_at_line_end above.  */
+
+static void
+test_applying_fixits_insert_after_failure (const line_table_case &case_)
+{
+  /* Create a tempfile and write some text to it.
+     .........................0000000001111111.
+     .........................1234567890123456.  */
+  const char *old_content = ("/* before */\n"
+                            "foo = bar.field;\n"
+                            "/* after */\n");
+  temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
+  const char *filename = tmp.get_filename ();
+  line_table_test ltt (case_);
+  linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
+
+  /* Add a comment after the semicolon.  */
+  location_t loc = linemap_position_for_column (line_table, 16);
+  rich_location richloc (line_table, loc);
+
+  /* We want a failure of linemap_position_for_loc_and_offset.
+     We can do this by starting a new linemap at line 3, so that
+     there is no appropriate location value for the insertion point
+     within the linemap for line 2.  */
+  linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
+
+  /* The failure fails to happen at the transition point from
+     packed ranges to unpacked ranges (where there are some "spare"
+     location_t values).  Skip the test there.  */
+  if (loc >= LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES)
+    return;
+
+  /* Offsetting "loc" should now fail (by returning the input loc. */
+  ASSERT_EQ (loc, linemap_position_for_loc_and_offset (line_table, loc, 1));
+
+  /* Hence attempting to use add_fixit_insert_after at the end of the line
+     should now fail.  */
+  richloc.add_fixit_insert_after ("/* inserted */");
+  ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
+
+  edit_context edit;
+  edit.add_fixits (&richloc);
+  ASSERT_FALSE (edit.valid_p ());
+  ASSERT_EQ (NULL, edit.get_content (filename));
+  ASSERT_EQ (NULL, edit.generate_diff (false));
+}
+
+/* Test applying an "insert" fixit that adds a newline.  */
+
+static void
+test_applying_fixits_insert_containing_newline (const line_table_case &case_)
+{
+  /* Create a tempfile and write some text to it.
+     .........................0000000001111111.
+     .........................1234567890123456.  */
+  const char *old_content = ("    case 'a':\n" /* line 1. */
+                            "      x = a;\n"  /* line 2. */
+                            "    case 'b':\n" /* line 3. */
+                            "      x = b;\n");/* line 4. */
+
+  temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
+  const char *filename = tmp.get_filename ();
+  line_table_test ltt (case_);
+  linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
+
+  /* Add a "break;" on a line by itself before line 3 i.e. before
+     column 1 of line 3. */
+  location_t case_start = linemap_position_for_column (line_table, 5);
+  location_t case_finish = linemap_position_for_column (line_table, 13);
+  location_t case_loc = make_location (case_start, case_start, case_finish);
+  rich_location richloc (line_table, case_loc);
+  location_t line_start = linemap_position_for_column (line_table, 1);
+  richloc.add_fixit_insert_before (line_start, "      break;\n");
+
+  if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
+    return;
+
+  edit_context edit;
+  edit.add_fixits (&richloc);
+  auto_free <char *> new_content = edit.get_content (filename);
+  ASSERT_STREQ (("    case 'a':\n"
+                "      x = a;\n"
+                "      break;\n"
+                "    case 'b':\n"
+                "      x = b;\n"),
+               new_content);
+
+  /* Verify diff.  */
+  auto_free <char *> diff = edit.generate_diff (false);
+  ASSERT_STREQ (("@@ -1,4 +1,5 @@\n"
+                "     case 'a':\n"
+                "       x = a;\n"
+                "+      break;\n"
+                "     case 'b':\n"
+                "       x = b;\n"),
+               diff);
+}
+
 /* Test applying a "replace" fixit that grows the affected line.  */
 
 static void
@@ -1045,6 +1269,44 @@ test_applying_fixits_shrinking_replace (const line_table_case &case_)
     }
 }
 
+/* Replacement fix-it hint containing a newline.  */
+
+static void
+test_applying_fixits_replace_containing_newline (const line_table_case &case_)
+{
+  /* Create a tempfile and write some text to it.
+    .........................0000000001111.
+    .........................1234567890123.  */
+  const char *old_content = "foo = bar ();\n";
+
+  temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
+  const char *filename = tmp.get_filename ();
+  line_table_test ltt (case_);
+  linemap_add (line_table, LC_ENTER, false, filename, 1);
+
+  /* Replace the " = " with "\n  = ", as if we were reformatting an
+     overly long line.  */
+  location_t start = linemap_position_for_column (line_table, 4);
+  location_t finish = linemap_position_for_column (line_table, 6);
+  location_t loc = linemap_position_for_column (line_table, 13);
+  rich_location richloc (line_table, loc);
+  source_range range = source_range::from_locations (start, finish);
+  richloc.add_fixit_replace (range, "\n  = ");
+
+  /* Newlines are only supported within fix-it hints that
+     are at the start of lines (for entirely new lines), hence
+     this fix-it should not be displayed.  */
+  ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
+
+  if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
+    return;
+
+  edit_context edit;
+  edit.add_fixits (&richloc);
+  auto_free <char *> new_content = edit.get_content (filename);
+  //ASSERT_STREQ ("foo\n  = bar ();\n", new_content);
+}
+
 /* Test applying a "remove" fixit.  */
 
 static void
@@ -1119,11 +1381,11 @@ test_applying_fixits_multiple (const line_table_case &case_)
 
   /* Add a comment in front of "bar.field".  */
   rich_location insert_a (line_table, c7);
-  insert_a.add_fixit_insert (c7, "/* alpha */");
+  insert_a.add_fixit_insert_before (c7, "/* alpha */");
 
   /* Add a comment after "bar.field;".  */
   rich_location insert_b (line_table, c17);
-  insert_b.add_fixit_insert (c17, "/* beta */");
+  insert_b.add_fixit_insert_before (c17, "/* beta */");
 
   /* Replace "bar" with "pub".   */
   rich_location replace_a (line_table, c7);
@@ -1187,7 +1449,33 @@ change_line (edit_context &edit, int line_num)
     }
 
   rich_location insert (line_table, loc);
-  insert.add_fixit_insert ("CHANGED: ");
+  insert.add_fixit_insert_before ("CHANGED: ");
+  edit.add_fixits (&insert);
+  return loc;
+}
+
+/* Subroutine of test_applying_fixits_multiple_lines.
+   Add the text "INSERTED\n" in front of the given line.  */
+
+static location_t
+insert_line (edit_context &edit, int line_num)
+{
+  const line_map_ordinary *ord_map
+    = LINEMAPS_LAST_ORDINARY_MAP (line_table);
+  const int column = 1;
+  location_t loc =
+    linemap_position_for_line_and_column (line_table, ord_map,
+                                         line_num, column);
+
+  expanded_location exploc = expand_location (loc);
+  if (loc <= LINE_MAP_MAX_LOCATION_WITH_COLS)
+    {
+      ASSERT_EQ (line_num, exploc.line);
+      ASSERT_EQ (column, exploc.column);
+    }
+
+  rich_location insert (line_table, loc);
+  insert.add_fixit_insert_before ("INSERTED\n");
   edit.add_fixits (&insert);
   return loc;
 }
@@ -1217,6 +1505,7 @@ test_applying_fixits_multiple_lines (const line_table_case &case_)
   change_line (edit, 2);
   change_line (edit, 3);
   change_line (edit, 4);
+  insert_line (edit, 5);
 
   /* A run of nearby lines, within the contextual limit.  */
   change_line (edit, 150);
@@ -1228,7 +1517,7 @@ test_applying_fixits_multiple_lines (const line_table_case &case_)
 
   /* Verify diff.  */
   auto_free <char *> diff = edit.generate_diff (false);
-  ASSERT_STREQ ("@@ -1,7 +1,7 @@\n"
+  ASSERT_STREQ ("@@ -1,7 +1,8 @@\n"
                " line 1\n"
                "-line 2\n"
                "-line 3\n"
@@ -1236,10 +1525,11 @@ test_applying_fixits_multiple_lines (const line_table_case &case_)
                "+CHANGED: line 2\n"
                "+CHANGED: line 3\n"
                "+CHANGED: line 4\n"
+               "+INSERTED\n"
                " line 5\n"
                " line 6\n"
                " line 7\n"
-               "@@ -147,10 +147,10 @@\n"
+               "@@ -147,10 +148,10 @@\n"
                " line 147\n"
                " line 148\n"
                " line 149\n"
@@ -1355,8 +1645,8 @@ test_applying_fixits_unreadable_file ()
   location_t loc = linemap_position_for_column (line_table, 1);
 
   rich_location insert (line_table, loc);
-  insert.add_fixit_insert ("change 1");
-  insert.add_fixit_insert ("change 2");
+  insert.add_fixit_insert_before ("change 1");
+  insert.add_fixit_insert_before ("change 2");
 
   edit_context edit;
   /* Attempting to add the fixits affecting the unreadable file
@@ -1387,7 +1677,7 @@ test_applying_fixits_line_out_of_range ()
   location_t loc = linemap_position_for_column (line_table, 1);
 
   rich_location insert (line_table, loc);
-  insert.add_fixit_insert ("change");
+  insert.add_fixit_insert_before ("change");
 
   /* Verify that attempting the insertion puts an edit_context
      into an invalid state.  */
@@ -1424,7 +1714,7 @@ test_applying_fixits_column_validation (const line_table_case &case_)
   /* Verify inserting at the end of the line.  */
   {
     rich_location richloc (line_table, c11);
-    richloc.add_fixit_insert (c15, " change");
+    richloc.add_fixit_insert_before (c15, " change");
 
     /* Col 15 is at the end of the line, so the insertion
        should succeed.  */
@@ -1440,7 +1730,7 @@ test_applying_fixits_column_validation (const line_table_case &case_)
   /* Verify inserting beyond the end of the line.  */
   {
     rich_location richloc (line_table, c11);
-    richloc.add_fixit_insert (c16, " change");
+    richloc.add_fixit_insert_before (c16, " change");
 
     /* Col 16 is beyond the end of the line, so the insertion
        should fail gracefully.  */
@@ -1494,9 +1784,14 @@ void
 edit_context_c_tests ()
 {
   test_get_content ();
-  for_each_line_table_case (test_applying_fixits_insert);
+  for_each_line_table_case (test_applying_fixits_insert_before);
+  for_each_line_table_case (test_applying_fixits_insert_after);
+  for_each_line_table_case (test_applying_fixits_insert_after_at_line_end);
+  for_each_line_table_case (test_applying_fixits_insert_after_failure);
+  for_each_line_table_case (test_applying_fixits_insert_containing_newline);
   for_each_line_table_case (test_applying_fixits_growing_replace);
   for_each_line_table_case (test_applying_fixits_shrinking_replace);
+  for_each_line_table_case (test_applying_fixits_replace_containing_newline);
   for_each_line_table_case (test_applying_fixits_remove);
   for_each_line_table_case (test_applying_fixits_multiple);
   for_each_line_table_case (test_applying_fixits_multiple_lines);