2016-10-28 Steven G. Kargl <kargl@gcc.gnu.org>
[official-gcc.git] / gcc / edit-context.c
blob5945f423d29c7091866243444de1669a06d7c8c9
1 /* Determining the results of applying fix-it hints.
2 Copyright (C) 2016 Free Software Foundation, Inc.
4 This file is part of GCC.
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.
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.
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/>. */
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"
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.
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.
39 The "filename" params are required to outlive the edit_context (no
40 copy of the underlying str is taken, just the ptr). */
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 class insert_event;
49 class replace_event;
51 /* A struct to hold the params of a print_diff call. */
53 struct diff
55 diff (pretty_printer *pp, bool show_filenames)
56 : m_pp (pp), m_show_filenames (show_filenames) {}
58 pretty_printer *m_pp;
59 bool m_show_filenames;
62 /* The state of one named file within an edit_context: the filename,
63 and the lines that have been edited so far. */
65 class edited_file
67 public:
68 edited_file (const char *filename);
69 static void delete_cb (edited_file *file);
71 const char *get_filename () const { return m_filename; }
72 char *get_content ();
74 bool apply_insert (int line, int column, const char *str, int len);
75 bool apply_replace (int line, int start_column,
76 int finish_column,
77 const char *replacement_str,
78 int replacement_len);
79 int get_effective_column (int line, int column);
81 static int call_print_diff (const char *, edited_file *file,
82 void *user_data)
84 diff *d = (diff *)user_data;
85 file->print_diff (d->m_pp, d->m_show_filenames);
86 return 0;
89 private:
90 bool print_content (pretty_printer *pp);
91 void print_diff (pretty_printer *pp, bool show_filenames);
92 void print_diff_hunk (pretty_printer *pp, int start_of_hunk,
93 int end_of_hunk);
94 void print_diff_line (pretty_printer *pp, char prefix_char,
95 const char *line, int line_size);
96 edited_line *get_line (int line);
97 edited_line *get_or_insert_line (int line);
98 int get_num_lines (bool *missing_trailing_newline);
100 const char *m_filename;
101 typed_splay_tree<int, edited_line *> m_edited_lines;
102 int m_num_lines;
105 /* The state of one edited line within an edited_file.
106 As well as the current content of the line, it contains a record of
107 the changes, so that further changes can be applied in the correct
108 place. */
110 class edited_line
112 public:
113 edited_line (const char *filename, int line_num);
114 ~edited_line ();
115 static void delete_cb (edited_line *el);
117 int get_line_num () const { return m_line_num; }
118 const char *get_content () const { return m_content; }
119 int get_len () const { return m_len; }
121 int get_effective_column (int orig_column) const;
122 bool apply_insert (int column, const char *str, int len);
123 bool apply_replace (int start_column,
124 int finish_column,
125 const char *replacement_str,
126 int replacement_len);
128 private:
129 void ensure_capacity (int len);
130 void ensure_terminated ();
131 void print_content (pretty_printer *pp) const;
133 int m_line_num;
134 char *m_content;
135 int m_len;
136 int m_alloc_sz;
137 auto_vec <line_event *> m_line_events;
140 /* Abstract base class for representing events that have occurred
141 on one line of one file. */
143 class line_event
145 public:
146 virtual ~line_event () {}
147 virtual int get_effective_column (int orig_column) const = 0;
150 /* Concrete subclass of line_event: an insertion of some text
151 at some column on the line.
153 Subsequent events will need their columns adjusting if they're
154 are on this line and their column is >= the insertion point. */
156 class insert_event : public line_event
158 public:
159 insert_event (int column, int len) : m_column (column), m_len (len) {}
160 int get_effective_column (int orig_column) const FINAL OVERRIDE
162 if (orig_column >= m_column)
163 return orig_column + m_len;
164 else
165 return orig_column;
168 private:
169 int m_column;
170 int m_len;
173 /* Concrete subclass of line_event: the replacement of some text
174 betweeen some columns on the line.
176 Subsequent events will need their columns adjusting if they're
177 are on this line and their column is >= the finish point. */
179 class replace_event : public line_event
181 public:
182 replace_event (int start, int finish, int len) : m_start (start),
183 m_finish (finish), m_delta (len - (finish + 1 - start)) {}
185 int get_effective_column (int orig_column) const FINAL OVERRIDE
187 if (orig_column >= m_start)
188 return orig_column += m_delta;
189 else
190 return orig_column;
193 private:
194 int m_start;
195 int m_finish;
196 int m_delta;
199 /* Implementation of class edit_context. */
201 /* edit_context's ctor. */
203 edit_context::edit_context ()
204 : m_valid (true),
205 m_files (strcmp, NULL, edited_file::delete_cb)
208 /* Add any fixits within RICHLOC to this context, recording the
209 changes that they make. */
211 void
212 edit_context::add_fixits (rich_location *richloc)
214 if (!m_valid)
215 return;
216 if (richloc->seen_impossible_fixit_p ())
218 m_valid = false;
219 return;
221 for (unsigned i = 0; i < richloc->get_num_fixit_hints (); i++)
223 const fixit_hint *hint = richloc->get_fixit_hint (i);
224 switch (hint->get_kind ())
226 case fixit_hint::INSERT:
227 if (!apply_insert ((const fixit_insert *)hint))
229 /* Failure. */
230 m_valid = false;
231 return;
233 break;
234 case fixit_hint::REPLACE:
235 if (!apply_replace ((const fixit_replace *)hint))
237 /* Failure. */
238 m_valid = false;
239 return;
241 break;
242 default:
243 gcc_unreachable ();
248 /* Get the content of the given file, with fix-its applied.
249 If any errors occurred in this edit_context, return NULL.
250 The ptr should be freed by the caller. */
252 char *
253 edit_context::get_content (const char *filename)
255 if (!m_valid)
256 return NULL;
257 edited_file &file = get_or_insert_file (filename);
258 return file.get_content ();
261 /* Map a location before the edits to a column number after the edits.
262 This method is for the selftests. */
265 edit_context::get_effective_column (const char *filename, int line,
266 int column)
268 edited_file *file = get_file (filename);
269 if (!file)
270 return column;
271 return file->get_effective_column (line, column);
274 /* Generate a unified diff. The resulting string should be freed by the
275 caller. Primarily for selftests.
276 If any errors occurred in this edit_context, return NULL. */
278 char *
279 edit_context::generate_diff (bool show_filenames)
281 if (!m_valid)
282 return NULL;
284 pretty_printer pp;
285 print_diff (&pp, show_filenames);
286 return xstrdup (pp_formatted_text (&pp));
289 /* Print a unified diff to PP, showing the changes made within the
290 context. */
292 void
293 edit_context::print_diff (pretty_printer *pp, bool show_filenames)
295 if (!m_valid)
296 return;
297 diff d (pp, show_filenames);
298 m_files.foreach (edited_file::call_print_diff, &d);
301 /* Attempt to apply the given fixit. Return true if it can be
302 applied, or false otherwise. */
304 bool
305 edit_context::apply_insert (const fixit_insert *insert)
307 expanded_location exploc = expand_location (insert->get_location ());
309 if (exploc.column == 0)
310 return false;
312 edited_file &file = get_or_insert_file (exploc.file);
313 if (!m_valid)
314 return false;
315 return file.apply_insert (exploc.line, exploc.column, insert->get_string (),
316 insert->get_length ());
319 /* Attempt to apply the given fixit. Return true if it can be
320 applied, or false otherwise. */
322 bool
323 edit_context::apply_replace (const fixit_replace *replace)
325 source_range range = replace->get_range ();
327 expanded_location start = expand_location (range.m_start);
328 expanded_location finish = expand_location (range.m_finish);
329 if (start.file != finish.file)
330 return false;
331 if (start.line != finish.line)
332 return false;
333 if (start.column == 0)
334 return false;
335 if (finish.column == 0)
336 return false;
338 edited_file &file = get_or_insert_file (start.file);
339 if (!m_valid)
340 return false;
341 return file.apply_replace (start.line, start.column, finish.column,
342 replace->get_string (),
343 replace->get_length ());
346 /* Locate the edited_file * for FILENAME, if any
347 Return NULL if there isn't one. */
349 edited_file *
350 edit_context::get_file (const char *filename)
352 gcc_assert (filename);
353 return m_files.lookup (filename);
356 /* Locate the edited_file for FILENAME, adding one if there isn't one. */
358 edited_file &
359 edit_context::get_or_insert_file (const char *filename)
361 gcc_assert (filename);
363 edited_file *file = get_file (filename);
364 if (file)
365 return *file;
367 /* Not found. */
368 file = new edited_file (filename);
369 m_files.insert (filename, file);
370 return *file;
373 /* Implementation of class edited_file. */
375 /* Callback for m_edited_lines, for comparing line numbers. */
377 static int line_comparator (int a, int b)
379 return a - b;
382 /* edited_file's constructor. */
384 edited_file::edited_file (const char *filename)
385 : m_filename (filename),
386 m_edited_lines (line_comparator, NULL, edited_line::delete_cb),
387 m_num_lines (-1)
391 /* A callback for deleting edited_file *, for use as a
392 delete_value_fn for edit_context::m_files. */
394 void
395 edited_file::delete_cb (edited_file *file)
397 delete file;
400 /* Get the content of the file, with fix-its applied.
401 The ptr should be freed by the caller. */
403 char *
404 edited_file::get_content ()
406 pretty_printer pp;
407 if (!print_content (&pp))
408 return NULL;
409 return xstrdup (pp_formatted_text (&pp));
412 /* Attempt to insert the string INSERT_STR with length INSERT_LEN
413 at LINE and COLUMN, updating the in-memory copy of the line, and
414 the record of edits to the line. */
416 bool
417 edited_file::apply_insert (int line, int column,
418 const char *insert_str,
419 int insert_len)
421 edited_line *el = get_or_insert_line (line);
422 if (!el)
423 return false;
424 return el->apply_insert (column, insert_str, insert_len);
427 /* Attempt to replace columns START_COLUMN through FINISH_COLUMN of LINE
428 with the string REPLACEMENT_STR of length REPLACEMENT_LEN,
429 updating the in-memory copy of the line, and the record of edits to
430 the line. */
432 bool
433 edited_file::apply_replace (int line, int start_column,
434 int finish_column,
435 const char *replacement_str,
436 int replacement_len)
438 edited_line *el = get_or_insert_line (line);
439 if (!el)
440 return false;
441 return el->apply_replace (start_column, finish_column, replacement_str,
442 replacement_len);
445 /* Given line LINE, map from COLUMN in the input file to its current
446 column after edits have been applied. */
449 edited_file::get_effective_column (int line, int column)
451 const edited_line *el = get_line (line);
452 if (!el)
453 return column;
454 return el->get_effective_column (column);
457 /* Attempt to print the content of the file to PP, with edits applied.
458 Return true if successful, false otherwise. */
460 bool
461 edited_file::print_content (pretty_printer *pp)
463 bool missing_trailing_newline;
464 int line_count = get_num_lines (&missing_trailing_newline);
465 for (int line_num = 1; line_num <= line_count; line_num++)
467 edited_line *el = get_line (line_num);
468 if (el)
469 pp_string (pp, el->get_content ());
470 else
472 int len;
473 const char *line
474 = location_get_source_line (m_filename, line_num, &len);
475 if (!line)
476 return false;
477 for (int i = 0; i < len; i++)
478 pp_character (pp, line[i]);
480 if (line_num < line_count)
481 pp_character (pp, '\n');
484 if (!missing_trailing_newline)
485 pp_character (pp, '\n');
487 return true;
490 /* Print a unified diff to PP, showing any changes that have occurred
491 to this file. */
493 void
494 edited_file::print_diff (pretty_printer *pp, bool show_filenames)
496 if (show_filenames)
498 pp_string (pp, colorize_start (pp_show_color (pp), "diff-filename"));
499 pp_printf (pp, "--- %s\n", m_filename);
500 pp_printf (pp, "+++ %s\n", m_filename);
501 pp_string (pp, colorize_stop (pp_show_color (pp)));
504 edited_line *el = m_edited_lines.min ();
506 bool missing_trailing_newline;
507 int line_count = get_num_lines (&missing_trailing_newline);
509 const int context_lines = 3;
511 while (el)
513 int start_of_hunk = el->get_line_num ();
514 start_of_hunk -= context_lines;
515 if (start_of_hunk < 1)
516 start_of_hunk = 1;
518 /* Locate end of hunk, merging in changed lines
519 that are sufficiently close. */
520 while (true)
522 edited_line *next_el
523 = m_edited_lines.successor (el->get_line_num ());
524 if (!next_el)
525 break;
526 if (el->get_line_num () + context_lines
527 >= next_el->get_line_num () - context_lines)
528 el = next_el;
529 else
530 break;
532 int end_of_hunk = el->get_line_num ();
533 end_of_hunk += context_lines;
534 if (end_of_hunk > line_count)
535 end_of_hunk = line_count;
537 print_diff_hunk (pp, start_of_hunk, end_of_hunk);
539 el = m_edited_lines.successor (el->get_line_num ());
543 /* Print one hunk within a unified diff to PP, covering the
544 given range of lines. */
546 void
547 edited_file::print_diff_hunk (pretty_printer *pp, int start_of_hunk,
548 int end_of_hunk)
550 int num_lines = end_of_hunk - start_of_hunk + 1;
552 pp_string (pp, colorize_start (pp_show_color (pp), "diff-hunk"));
553 pp_printf (pp, "@@ -%i,%i +%i,%i @@\n", start_of_hunk, num_lines,
554 start_of_hunk, num_lines);
555 pp_string (pp, colorize_stop (pp_show_color (pp)));
557 int line_num = start_of_hunk;
558 while (line_num <= end_of_hunk)
560 edited_line *el = get_line (line_num);
561 if (el)
563 /* We have an edited line.
564 Consolidate into runs of changed lines. */
565 const int first_changed_line_in_run = line_num;
566 while (get_line (line_num))
567 line_num++;
568 const int last_changed_line_in_run = line_num - 1;
570 /* Show old version of lines. */
571 pp_string (pp, colorize_start (pp_show_color (pp),
572 "diff-delete"));
573 for (line_num = first_changed_line_in_run;
574 line_num <= last_changed_line_in_run;
575 line_num++)
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);
582 pp_string (pp, colorize_stop (pp_show_color (pp)));
584 /* Show new version of lines. */
585 pp_string (pp, colorize_start (pp_show_color (pp),
586 "diff-insert"));
587 for (line_num = first_changed_line_in_run;
588 line_num <= last_changed_line_in_run;
589 line_num++)
591 edited_line *el_in_run = get_line (line_num);
592 gcc_assert (el_in_run);
593 print_diff_line (pp, '+', el_in_run->get_content (),
594 el_in_run->get_len ());
596 pp_string (pp, colorize_stop (pp_show_color (pp)));
598 else
600 /* Unchanged line. */
601 int line_len;
602 const char *old_line
603 = location_get_source_line (m_filename, line_num, &line_len);
604 print_diff_line (pp, ' ', old_line, line_len);
605 line_num++;
610 /* Print one line within a diff, starting with PREFIX_CHAR,
611 followed by the LINE of content, of length LEN. LINE is
612 not necessarily 0-terminated. Print a trailing newline. */
614 void
615 edited_file::print_diff_line (pretty_printer *pp, char prefix_char,
616 const char *line, int len)
618 pp_character (pp, prefix_char);
619 for (int i = 0; i < len; i++)
620 pp_character (pp, line[i]);
621 pp_character (pp, '\n');
624 /* Get the state of LINE within the file, or NULL if it is untouched. */
626 edited_line *
627 edited_file::get_line (int line)
629 return m_edited_lines.lookup (line);
632 /* Get the state of LINE within the file, creating a state for it
633 if necessary. Return NULL if an error occurs. */
635 edited_line *
636 edited_file::get_or_insert_line (int line)
638 edited_line *el = get_line (line);
639 if (el)
640 return el;
641 el = new edited_line (m_filename, line);
642 if (el->get_content () == NULL)
644 delete el;
645 return NULL;
647 m_edited_lines.insert (line, el);
648 return el;
651 /* Get the total number of lines in m_content, writing
652 true to *MISSING_TRAILING_NEWLINE if the final line
653 if missing a newline, false otherwise. */
656 edited_file::get_num_lines (bool *missing_trailing_newline)
658 gcc_assert (missing_trailing_newline);
659 if (m_num_lines == -1)
661 m_num_lines = 0;
662 while (true)
664 int line_size;
665 const char *line
666 = location_get_source_line (m_filename, m_num_lines + 1,
667 &line_size);
668 if (line)
669 m_num_lines++;
670 else
671 break;
674 *missing_trailing_newline = location_missing_trailing_newline (m_filename);
675 return m_num_lines;
678 /* Implementation of class edited_line. */
680 /* edited_line's ctor. */
682 edited_line::edited_line (const char *filename, int line_num)
683 : m_line_num (line_num),
684 m_content (NULL), m_len (0), m_alloc_sz (0),
685 m_line_events ()
687 const char *line = location_get_source_line (filename, line_num,
688 &m_len);
689 if (!line)
690 return;
691 ensure_capacity (m_len);
692 memcpy (m_content, line, m_len);
693 ensure_terminated ();
696 /* edited_line's dtor. */
698 edited_line::~edited_line ()
700 free (m_content);
702 int i;
703 line_event *event;
704 FOR_EACH_VEC_ELT (m_line_events, i, event)
705 delete event;
708 /* A callback for deleting edited_line *, for use as a
709 delete_value_fn for edited_file::m_edited_lines. */
711 void
712 edited_line::delete_cb (edited_line *el)
714 delete el;
717 /* Map a location before the edits to a column number after the edits,
718 within a specific line. */
721 edited_line::get_effective_column (int orig_column) const
723 int i;
724 line_event *event;
725 FOR_EACH_VEC_ELT (m_line_events, i, event)
726 orig_column = event->get_effective_column (orig_column);
727 return orig_column;
730 /* Attempt to insert the string INSERT_STR with length INSERT_LEN at COLUMN
731 of this line, updating the in-memory copy of the line, and the record
732 of edits to it.
733 Return true if successful; false if an error occurred. */
735 bool
736 edited_line::apply_insert (int column, const char *insert_str,
737 int insert_len)
739 column = get_effective_column (column);
741 int start_offset = column - 1;
742 gcc_assert (start_offset >= 0);
743 if (start_offset > m_len)
744 return false;
746 /* Ensure buffer is big enough. */
747 size_t new_len = m_len + insert_len;
748 ensure_capacity (new_len);
750 char *suffix = m_content + start_offset;
751 gcc_assert (suffix <= m_content + m_len);
752 size_t len_suffix = (m_content + m_len) - suffix;
754 /* Move successor content into position. They overlap, so use memmove. */
755 memmove (m_content + start_offset + insert_len,
756 suffix, len_suffix);
758 /* Replace target content. They don't overlap, so use memcpy. */
759 memcpy (m_content + start_offset,
760 insert_str,
761 insert_len);
763 m_len = new_len;
765 ensure_terminated ();
767 /* Record the insertion, so that future changes to the line can have
768 their column information adjusted accordingly. */
769 m_line_events.safe_push (new insert_event (column, insert_len));
771 return true;
774 /* Attempt to replace columns START_COLUMN through FINISH_COLUMN of the line
775 with the string REPLACEMENT_STR of length REPLACEMENT_LEN,
776 updating the in-memory copy of the line, and the record of edits to
777 the line.
778 Return true if successful; false if an error occurred. */
780 bool
781 edited_line::apply_replace (int start_column,
782 int finish_column,
783 const char *replacement_str,
784 int replacement_len)
786 start_column = get_effective_column (start_column);
787 finish_column = get_effective_column (finish_column);
789 int start_offset = start_column - 1;
790 int end_offset = finish_column - 1;
792 gcc_assert (start_offset >= 0);
793 gcc_assert (end_offset >= 0);
795 if (start_column > finish_column)
796 return false;
797 if (start_offset >= m_len)
798 return false;
799 if (end_offset >= m_len)
800 return false;
802 size_t victim_len = end_offset - start_offset + 1;
804 /* Ensure buffer is big enough. */
805 size_t new_len = m_len + replacement_len - victim_len;
806 ensure_capacity (new_len);
808 char *suffix = m_content + end_offset + 1;
809 gcc_assert (suffix <= m_content + m_len);
810 size_t len_suffix = (m_content + m_len) - suffix;
812 /* Move successor content into position. They overlap, so use memmove. */
813 memmove (m_content + start_offset + replacement_len,
814 suffix, len_suffix);
816 /* Replace target content. They don't overlap, so use memcpy. */
817 memcpy (m_content + start_offset,
818 replacement_str,
819 replacement_len);
821 m_len = new_len;
823 ensure_terminated ();
825 /* Record the replacement, so that future changes to the line can have
826 their column information adjusted accordingly. */
827 m_line_events.safe_push (new replace_event (start_column, finish_column,
828 replacement_len));
829 return true;
832 /* Ensure that the buffer for m_content is at least large enough to hold
833 a string of length LEN and its 0-terminator, doubling on repeated
834 allocations. */
836 void
837 edited_line::ensure_capacity (int len)
839 /* Allow 1 extra byte for 0-termination. */
840 if (m_alloc_sz < (len + 1))
842 size_t new_alloc_sz = (len + 1) * 2;
843 m_content = (char *)xrealloc (m_content, new_alloc_sz);
844 m_alloc_sz = new_alloc_sz;
848 /* Ensure that m_content is 0-terminated. */
850 void
851 edited_line::ensure_terminated ()
853 /* 0-terminate the buffer. */
854 gcc_assert (m_len < m_alloc_sz);
855 m_content[m_len] = '\0';
858 #if CHECKING_P
860 /* Selftests of code-editing. */
862 namespace selftest {
864 /* A wrapper class for ensuring that the underlying pointer is freed. */
866 template <typename POINTER_T>
867 class auto_free
869 public:
870 auto_free (POINTER_T p) : m_ptr (p) {}
871 ~auto_free () { free (m_ptr); }
873 operator POINTER_T () { return m_ptr; }
875 private:
876 POINTER_T m_ptr;
879 /* Verify that edit_context::get_content works for unedited files. */
881 static void
882 test_get_content ()
884 /* Test of empty file. */
886 const char *content = ("");
887 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
888 edit_context edit;
889 auto_free <char *> result = edit.get_content (tmp.get_filename ());
890 ASSERT_STREQ ("", result);
893 /* Test of simple content. */
895 const char *content = ("/* before */\n"
896 "foo = bar.field;\n"
897 "/* after */\n");
898 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
899 edit_context edit;
900 auto_free <char *> result = edit.get_content (tmp.get_filename ());
901 ASSERT_STREQ ("/* before */\n"
902 "foo = bar.field;\n"
903 "/* after */\n", result);
906 /* Test of omitting the trailing newline on the final line. */
908 const char *content = ("/* before */\n"
909 "foo = bar.field;\n"
910 "/* after */");
911 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
912 edit_context edit;
913 auto_free <char *> result = edit.get_content (tmp.get_filename ());
914 /* We should respect the omitted trailing newline. */
915 ASSERT_STREQ ("/* before */\n"
916 "foo = bar.field;\n"
917 "/* after */", result);
921 /* Test applying an "insert" fixit, using insert_before. */
923 static void
924 test_applying_fixits_insert_before (const line_table_case &case_)
926 /* Create a tempfile and write some text to it.
927 .........................0000000001111111.
928 .........................1234567890123456. */
929 const char *old_content = ("/* before */\n"
930 "foo = bar.field;\n"
931 "/* after */\n");
932 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
933 const char *filename = tmp.get_filename ();
934 line_table_test ltt (case_);
935 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
937 /* Add a comment in front of "bar.field". */
938 location_t start = linemap_position_for_column (line_table, 7);
939 rich_location richloc (line_table, start);
940 richloc.add_fixit_insert_before ("/* inserted */");
942 if (start > LINE_MAP_MAX_LOCATION_WITH_COLS)
943 return;
945 edit_context edit;
946 edit.add_fixits (&richloc);
947 auto_free <char *> new_content = edit.get_content (filename);
948 if (start <= LINE_MAP_MAX_LOCATION_WITH_COLS)
949 ASSERT_STREQ ("/* before */\n"
950 "foo = /* inserted */bar.field;\n"
951 "/* after */\n", new_content);
953 /* Verify that locations on other lines aren't affected by the change. */
954 ASSERT_EQ (100, edit.get_effective_column (filename, 1, 100));
955 ASSERT_EQ (100, edit.get_effective_column (filename, 3, 100));
957 /* Verify locations on the line before the change. */
958 ASSERT_EQ (1, edit.get_effective_column (filename, 2, 1));
959 ASSERT_EQ (6, edit.get_effective_column (filename, 2, 6));
961 /* Verify locations on the line at and after the change. */
962 ASSERT_EQ (21, edit.get_effective_column (filename, 2, 7));
963 ASSERT_EQ (22, edit.get_effective_column (filename, 2, 8));
965 /* Verify diff. */
966 auto_free <char *> diff = edit.generate_diff (false);
967 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
968 " /* before */\n"
969 "-foo = bar.field;\n"
970 "+foo = /* inserted */bar.field;\n"
971 " /* after */\n", diff);
974 /* Test applying an "insert" fixit, using insert_after, with
975 a range of length > 1 (to ensure that the end-point of
976 the input range is used). */
978 static void
979 test_applying_fixits_insert_after (const line_table_case &case_)
981 /* Create a tempfile and write some text to it.
982 .........................0000000001111111.
983 .........................1234567890123456. */
984 const char *old_content = ("/* before */\n"
985 "foo = bar.field;\n"
986 "/* after */\n");
987 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
988 const char *filename = tmp.get_filename ();
989 line_table_test ltt (case_);
990 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
992 /* Add a comment after "field". */
993 location_t start = linemap_position_for_column (line_table, 11);
994 location_t finish = linemap_position_for_column (line_table, 15);
995 location_t field = make_location (start, start, finish);
996 rich_location richloc (line_table, field);
997 richloc.add_fixit_insert_after ("/* inserted */");
999 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
1000 return;
1002 /* Verify that the text was inserted after the end of "field". */
1003 edit_context edit;
1004 edit.add_fixits (&richloc);
1005 auto_free <char *> new_content = edit.get_content (filename);
1006 ASSERT_STREQ ("/* before */\n"
1007 "foo = bar.field/* inserted */;\n"
1008 "/* after */\n", new_content);
1010 /* Verify diff. */
1011 auto_free <char *> diff = edit.generate_diff (false);
1012 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1013 " /* before */\n"
1014 "-foo = bar.field;\n"
1015 "+foo = bar.field/* inserted */;\n"
1016 " /* after */\n", diff);
1019 /* Test applying an "insert" fixit, using insert_after at the end of
1020 a line (contrast with test_applying_fixits_insert_after_failure
1021 below). */
1023 static void
1024 test_applying_fixits_insert_after_at_line_end (const line_table_case &case_)
1026 /* Create a tempfile and write some text to it.
1027 .........................0000000001111111.
1028 .........................1234567890123456. */
1029 const char *old_content = ("/* before */\n"
1030 "foo = bar.field;\n"
1031 "/* after */\n");
1032 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1033 const char *filename = tmp.get_filename ();
1034 line_table_test ltt (case_);
1035 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
1037 /* Add a comment after the semicolon. */
1038 location_t loc = linemap_position_for_column (line_table, 16);
1039 rich_location richloc (line_table, loc);
1040 richloc.add_fixit_insert_after ("/* inserted */");
1042 if (loc > LINE_MAP_MAX_LOCATION_WITH_COLS)
1043 return;
1045 edit_context edit;
1046 edit.add_fixits (&richloc);
1047 auto_free <char *> new_content = edit.get_content (filename);
1048 ASSERT_STREQ ("/* before */\n"
1049 "foo = bar.field;/* inserted */\n"
1050 "/* after */\n", new_content);
1052 /* Verify diff. */
1053 auto_free <char *> diff = edit.generate_diff (false);
1054 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1055 " /* before */\n"
1056 "-foo = bar.field;\n"
1057 "+foo = bar.field;/* inserted */\n"
1058 " /* after */\n", diff);
1061 /* Test of a failed attempt to apply an "insert" fixit, using insert_after,
1062 due to the relevant linemap ending. Contrast with
1063 test_applying_fixits_insert_after_at_line_end above. */
1065 static void
1066 test_applying_fixits_insert_after_failure (const line_table_case &case_)
1068 /* Create a tempfile and write some text to it.
1069 .........................0000000001111111.
1070 .........................1234567890123456. */
1071 const char *old_content = ("/* before */\n"
1072 "foo = bar.field;\n"
1073 "/* after */\n");
1074 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1075 const char *filename = tmp.get_filename ();
1076 line_table_test ltt (case_);
1077 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
1079 /* Add a comment after the semicolon. */
1080 location_t loc = linemap_position_for_column (line_table, 16);
1081 rich_location richloc (line_table, loc);
1083 /* We want a failure of linemap_position_for_loc_and_offset.
1084 We can do this by starting a new linemap at line 3, so that
1085 there is no appropriate location value for the insertion point
1086 within the linemap for line 2. */
1087 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
1089 /* The failure fails to happen at the transition point from
1090 packed ranges to unpacked ranges (where there are some "spare"
1091 location_t values). Skip the test there. */
1092 if (loc >= LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES)
1093 return;
1095 /* Offsetting "loc" should now fail (by returning the input loc. */
1096 ASSERT_EQ (loc, linemap_position_for_loc_and_offset (line_table, loc, 1));
1098 /* Hence attempting to use add_fixit_insert_after at the end of the line
1099 should now fail. */
1100 richloc.add_fixit_insert_after ("/* inserted */");
1101 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
1103 edit_context edit;
1104 edit.add_fixits (&richloc);
1105 ASSERT_FALSE (edit.valid_p ());
1106 ASSERT_EQ (NULL, edit.get_content (filename));
1107 ASSERT_EQ (NULL, edit.generate_diff (false));
1110 /* Test applying a "replace" fixit that grows the affected line. */
1112 static void
1113 test_applying_fixits_growing_replace (const line_table_case &case_)
1115 /* Create a tempfile and write some text to it.
1116 .........................0000000001111111.
1117 .........................1234567890123456. */
1118 const char *old_content = ("/* before */\n"
1119 "foo = bar.field;\n"
1120 "/* after */\n");
1121 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1122 const char *filename = tmp.get_filename ();
1123 line_table_test ltt (case_);
1124 linemap_add (line_table, LC_ENTER, false, filename, 2);
1126 /* Replace "field" with "m_field". */
1127 location_t start = linemap_position_for_column (line_table, 11);
1128 location_t finish = linemap_position_for_column (line_table, 15);
1129 location_t field = make_location (start, start, finish);
1130 rich_location richloc (line_table, field);
1131 richloc.add_fixit_replace ("m_field");
1133 edit_context edit;
1134 edit.add_fixits (&richloc);
1135 auto_free <char *> new_content = edit.get_content (filename);
1136 if (finish <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1138 ASSERT_STREQ ("/* before */\n"
1139 "foo = bar.m_field;\n"
1140 "/* after */\n", new_content);
1142 /* Verify location of ";" after the change. */
1143 ASSERT_EQ (18, edit.get_effective_column (filename, 2, 16));
1145 /* Verify diff. */
1146 auto_free <char *> diff = edit.generate_diff (false);
1147 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1148 " /* before */\n"
1149 "-foo = bar.field;\n"
1150 "+foo = bar.m_field;\n"
1151 " /* after */\n", diff);
1155 /* Test applying a "replace" fixit that shrinks the affected line. */
1157 static void
1158 test_applying_fixits_shrinking_replace (const line_table_case &case_)
1160 /* Create a tempfile and write some text to it.
1161 .........................000000000111111111.
1162 .........................123456789012345678. */
1163 const char *old_content = ("/* before */\n"
1164 "foo = bar.m_field;\n"
1165 "/* after */\n");
1166 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1167 const char *filename = tmp.get_filename ();
1168 line_table_test ltt (case_);
1169 linemap_add (line_table, LC_ENTER, false, filename, 2);
1171 /* Replace "field" with "m_field". */
1172 location_t start = linemap_position_for_column (line_table, 11);
1173 location_t finish = linemap_position_for_column (line_table, 17);
1174 location_t m_field = make_location (start, start, finish);
1175 rich_location richloc (line_table, m_field);
1176 richloc.add_fixit_replace ("field");
1178 edit_context edit;
1179 edit.add_fixits (&richloc);
1180 auto_free <char *> new_content = edit.get_content (filename);
1181 if (finish <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1183 ASSERT_STREQ ("/* before */\n"
1184 "foo = bar.field;\n"
1185 "/* after */\n", new_content);
1187 /* Verify location of ";" after the change. */
1188 ASSERT_EQ (16, edit.get_effective_column (filename, 2, 18));
1190 /* Verify diff. */
1191 auto_free <char *> diff = edit.generate_diff (false);
1192 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1193 " /* before */\n"
1194 "-foo = bar.m_field;\n"
1195 "+foo = bar.field;\n"
1196 " /* after */\n", diff);
1200 /* Test applying a "remove" fixit. */
1202 static void
1203 test_applying_fixits_remove (const line_table_case &case_)
1205 /* Create a tempfile and write some text to it.
1206 .........................000000000111111111.
1207 .........................123456789012345678. */
1208 const char *old_content = ("/* before */\n"
1209 "foo = bar.m_field;\n"
1210 "/* after */\n");
1211 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1212 const char *filename = tmp.get_filename ();
1213 line_table_test ltt (case_);
1214 linemap_add (line_table, LC_ENTER, false, filename, 2);
1216 /* Remove ".m_field". */
1217 location_t start = linemap_position_for_column (line_table, 10);
1218 location_t finish = linemap_position_for_column (line_table, 17);
1219 rich_location richloc (line_table, start);
1220 source_range range;
1221 range.m_start = start;
1222 range.m_finish = finish;
1223 richloc.add_fixit_remove (range);
1225 edit_context edit;
1226 edit.add_fixits (&richloc);
1227 auto_free <char *> new_content = edit.get_content (filename);
1228 if (finish <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1230 ASSERT_STREQ ("/* before */\n"
1231 "foo = bar;\n"
1232 "/* after */\n", new_content);
1234 /* Verify location of ";" after the change. */
1235 ASSERT_EQ (10, edit.get_effective_column (filename, 2, 18));
1237 /* Verify diff. */
1238 auto_free <char *> diff = edit.generate_diff (false);
1239 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1240 " /* before */\n"
1241 "-foo = bar.m_field;\n"
1242 "+foo = bar;\n"
1243 " /* after */\n", diff);
1247 /* Test applying multiple fixits to one line. */
1249 static void
1250 test_applying_fixits_multiple (const line_table_case &case_)
1252 /* Create a tempfile and write some text to it.
1253 .........................00000000011111111.
1254 .........................12345678901234567. */
1255 const char *old_content = ("/* before */\n"
1256 "foo = bar.field;\n"
1257 "/* after */\n");
1258 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1259 const char *filename = tmp.get_filename ();
1260 line_table_test ltt (case_);
1261 linemap_add (line_table, LC_ENTER, false, filename, 2);
1263 location_t c7 = linemap_position_for_column (line_table, 7);
1264 location_t c9 = linemap_position_for_column (line_table, 9);
1265 location_t c11 = linemap_position_for_column (line_table, 11);
1266 location_t c15 = linemap_position_for_column (line_table, 15);
1267 location_t c17 = linemap_position_for_column (line_table, 17);
1269 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1270 return;
1272 /* Add a comment in front of "bar.field". */
1273 rich_location insert_a (line_table, c7);
1274 insert_a.add_fixit_insert_before (c7, "/* alpha */");
1276 /* Add a comment after "bar.field;". */
1277 rich_location insert_b (line_table, c17);
1278 insert_b.add_fixit_insert_before (c17, "/* beta */");
1280 /* Replace "bar" with "pub". */
1281 rich_location replace_a (line_table, c7);
1282 replace_a.add_fixit_replace (source_range::from_locations (c7, c9),
1283 "pub");
1285 /* Replace "field" with "meadow". */
1286 rich_location replace_b (line_table, c7);
1287 replace_b.add_fixit_replace (source_range::from_locations (c11, c15),
1288 "meadow");
1290 edit_context edit;
1291 edit.add_fixits (&insert_a);
1292 ASSERT_EQ (100, edit.get_effective_column (filename, 1, 100));
1293 ASSERT_EQ (1, edit.get_effective_column (filename, 2, 1));
1294 ASSERT_EQ (6, edit.get_effective_column (filename, 2, 6));
1295 ASSERT_EQ (18, edit.get_effective_column (filename, 2, 7));
1296 ASSERT_EQ (27, edit.get_effective_column (filename, 2, 16));
1297 ASSERT_EQ (100, edit.get_effective_column (filename, 3, 100));
1299 edit.add_fixits (&insert_b);
1300 edit.add_fixits (&replace_a);
1301 edit.add_fixits (&replace_b);
1303 if (c17 <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1305 auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
1306 ASSERT_STREQ ("/* before */\n"
1307 "foo = /* alpha */pub.meadow;/* beta */\n"
1308 "/* after */\n",
1309 new_content);
1311 /* Verify diff. */
1312 auto_free <char *> diff = edit.generate_diff (false);
1313 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1314 " /* before */\n"
1315 "-foo = bar.field;\n"
1316 "+foo = /* alpha */pub.meadow;/* beta */\n"
1317 " /* after */\n", diff);
1321 /* Subroutine of test_applying_fixits_multiple_lines.
1322 Add the text "CHANGED: " to the front of the given line. */
1324 static location_t
1325 change_line (edit_context &edit, int line_num)
1327 const line_map_ordinary *ord_map
1328 = LINEMAPS_LAST_ORDINARY_MAP (line_table);
1329 const int column = 1;
1330 location_t loc =
1331 linemap_position_for_line_and_column (line_table, ord_map,
1332 line_num, column);
1334 expanded_location exploc = expand_location (loc);
1335 if (loc <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1337 ASSERT_EQ (line_num, exploc.line);
1338 ASSERT_EQ (column, exploc.column);
1341 rich_location insert (line_table, loc);
1342 insert.add_fixit_insert_before ("CHANGED: ");
1343 edit.add_fixits (&insert);
1344 return loc;
1347 /* Test of editing multiple lines within a long file,
1348 to ensure that diffs are generated as expected. */
1350 static void
1351 test_applying_fixits_multiple_lines (const line_table_case &case_)
1353 /* Create a tempfile and write many lines of text to it. */
1354 named_temp_file tmp (".txt");
1355 const char *filename = tmp.get_filename ();
1356 FILE *f = fopen (filename, "w");
1357 ASSERT_NE (f, NULL);
1358 for (int i = 1; i <= 1000; i++)
1359 fprintf (f, "line %i\n", i);
1360 fclose (f);
1362 line_table_test ltt (case_);
1363 linemap_add (line_table, LC_ENTER, false, filename, 1);
1364 linemap_position_for_column (line_table, 127);
1366 edit_context edit;
1368 /* A run of consecutive lines. */
1369 change_line (edit, 2);
1370 change_line (edit, 3);
1371 change_line (edit, 4);
1373 /* A run of nearby lines, within the contextual limit. */
1374 change_line (edit, 150);
1375 change_line (edit, 151);
1376 location_t last_loc = change_line (edit, 153);
1378 if (last_loc > LINE_MAP_MAX_LOCATION_WITH_COLS)
1379 return;
1381 /* Verify diff. */
1382 auto_free <char *> diff = edit.generate_diff (false);
1383 ASSERT_STREQ ("@@ -1,7 +1,7 @@\n"
1384 " line 1\n"
1385 "-line 2\n"
1386 "-line 3\n"
1387 "-line 4\n"
1388 "+CHANGED: line 2\n"
1389 "+CHANGED: line 3\n"
1390 "+CHANGED: line 4\n"
1391 " line 5\n"
1392 " line 6\n"
1393 " line 7\n"
1394 "@@ -147,10 +147,10 @@\n"
1395 " line 147\n"
1396 " line 148\n"
1397 " line 149\n"
1398 "-line 150\n"
1399 "-line 151\n"
1400 "+CHANGED: line 150\n"
1401 "+CHANGED: line 151\n"
1402 " line 152\n"
1403 "-line 153\n"
1404 "+CHANGED: line 153\n"
1405 " line 154\n"
1406 " line 155\n"
1407 " line 156\n", diff);
1409 /* Ensure tmp stays alive until this point, so that the tempfile
1410 persists until after the generate_diff call. */
1411 tmp.get_filename ();
1414 /* Test of converting an initializer for a named field from
1415 the old GCC extension to C99 syntax.
1416 Exercises a shrinking replacement followed by a growing
1417 replacement on the same line. */
1419 static void
1420 test_applying_fixits_modernize_named_init (const line_table_case &case_)
1422 /* Create a tempfile and write some text to it.
1423 .........................00000000011111111.
1424 .........................12345678901234567. */
1425 const char *old_content = ("/* before */\n"
1426 "bar : 1,\n"
1427 "/* after */\n");
1428 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1429 const char *filename = tmp.get_filename ();
1430 line_table_test ltt (case_);
1431 linemap_add (line_table, LC_ENTER, false, filename, 2);
1433 location_t c1 = linemap_position_for_column (line_table, 1);
1434 location_t c3 = linemap_position_for_column (line_table, 3);
1435 location_t c8 = linemap_position_for_column (line_table, 8);
1437 if (c8 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1438 return;
1440 /* Replace "bar" with ".". */
1441 rich_location r1 (line_table, c8);
1442 r1.add_fixit_replace (source_range::from_locations (c1, c3),
1443 ".");
1445 /* Replace ":" with "bar =". */
1446 rich_location r2 (line_table, c8);
1447 r2.add_fixit_replace (source_range::from_locations (c8, c8),
1448 "bar =");
1450 /* The order should not matter. Do r1 then r2. */
1452 edit_context edit;
1453 edit.add_fixits (&r1);
1455 /* Verify state after first replacement. */
1457 auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
1458 /* We should now have:
1459 ............00000000011.
1460 ............12345678901. */
1461 ASSERT_STREQ ("/* before */\n"
1462 ". : 1,\n"
1463 "/* after */\n",
1464 new_content);
1465 /* Location of the "1". */
1466 ASSERT_EQ (6, edit.get_effective_column (filename, 2, 8));
1467 /* Location of the ",". */
1468 ASSERT_EQ (9, edit.get_effective_column (filename, 2, 11));
1471 edit.add_fixits (&r2);
1473 auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
1474 /* Verify state after second replacement.
1475 ............00000000011111111.
1476 ............12345678901234567. */
1477 ASSERT_STREQ ("/* before */\n"
1478 ". bar = 1,\n"
1479 "/* after */\n",
1480 new_content);
1483 /* Try again, doing r2 then r1; the new_content should be the same. */
1485 edit_context edit;
1486 edit.add_fixits (&r2);
1487 edit.add_fixits (&r1);
1488 auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
1489 /*.............00000000011111111.
1490 .............12345678901234567. */
1491 ASSERT_STREQ ("/* before */\n"
1492 ". bar = 1,\n"
1493 "/* after */\n",
1494 new_content);
1498 /* Test of a fixit affecting a file that can't be read. */
1500 static void
1501 test_applying_fixits_unreadable_file ()
1503 const char *filename = "this-does-not-exist.txt";
1504 line_table_test ltt ();
1505 linemap_add (line_table, LC_ENTER, false, filename, 1);
1507 location_t loc = linemap_position_for_column (line_table, 1);
1509 rich_location insert (line_table, loc);
1510 insert.add_fixit_insert_before ("change 1");
1511 insert.add_fixit_insert_before ("change 2");
1513 edit_context edit;
1514 /* Attempting to add the fixits affecting the unreadable file
1515 should transition the edit from valid to invalid. */
1516 ASSERT_TRUE (edit.valid_p ());
1517 edit.add_fixits (&insert);
1518 ASSERT_FALSE (edit.valid_p ());
1519 ASSERT_EQ (NULL, edit.get_content (filename));
1520 ASSERT_EQ (NULL, edit.generate_diff (false));
1523 /* Verify that we gracefully handle an attempt to edit a line
1524 that's beyond the end of the file. */
1526 static void
1527 test_applying_fixits_line_out_of_range ()
1529 /* Create a tempfile and write some text to it.
1530 ........................00000000011111111.
1531 ........................12345678901234567. */
1532 const char *old_content = "One-liner file\n";
1533 temp_source_file tmp (SELFTEST_LOCATION, ".txt", old_content);
1534 const char *filename = tmp.get_filename ();
1535 line_table_test ltt ();
1536 linemap_add (line_table, LC_ENTER, false, filename, 2);
1538 /* Try to insert a string in line 2. */
1539 location_t loc = linemap_position_for_column (line_table, 1);
1541 rich_location insert (line_table, loc);
1542 insert.add_fixit_insert_before ("change");
1544 /* Verify that attempting the insertion puts an edit_context
1545 into an invalid state. */
1546 edit_context edit;
1547 ASSERT_TRUE (edit.valid_p ());
1548 edit.add_fixits (&insert);
1549 ASSERT_FALSE (edit.valid_p ());
1550 ASSERT_EQ (NULL, edit.get_content (filename));
1551 ASSERT_EQ (NULL, edit.generate_diff (false));
1554 /* Verify the boundary conditions of column values in fix-it
1555 hints applied to edit_context instances. */
1557 static void
1558 test_applying_fixits_column_validation (const line_table_case &case_)
1560 /* Create a tempfile and write some text to it.
1561 ........................00000000011111111.
1562 ........................12345678901234567. */
1563 const char *old_content = "One-liner file\n";
1564 temp_source_file tmp (SELFTEST_LOCATION, ".txt", old_content);
1565 const char *filename = tmp.get_filename ();
1566 line_table_test ltt (case_);
1567 linemap_add (line_table, LC_ENTER, false, filename, 1);
1569 location_t c11 = linemap_position_for_column (line_table, 11);
1570 location_t c14 = linemap_position_for_column (line_table, 14);
1571 location_t c15 = linemap_position_for_column (line_table, 15);
1572 location_t c16 = linemap_position_for_column (line_table, 16);
1574 /* Verify limits of valid columns in insertion fixits. */
1576 /* Verify inserting at the end of the line. */
1578 rich_location richloc (line_table, c11);
1579 richloc.add_fixit_insert_before (c15, " change");
1581 /* Col 15 is at the end of the line, so the insertion
1582 should succeed. */
1583 edit_context edit;
1584 edit.add_fixits (&richloc);
1585 auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
1586 if (c15 <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1587 ASSERT_STREQ ("One-liner file change\n", new_content);
1588 else
1589 ASSERT_EQ (NULL, new_content);
1592 /* Verify inserting beyond the end of the line. */
1594 rich_location richloc (line_table, c11);
1595 richloc.add_fixit_insert_before (c16, " change");
1597 /* Col 16 is beyond the end of the line, so the insertion
1598 should fail gracefully. */
1599 edit_context edit;
1600 ASSERT_TRUE (edit.valid_p ());
1601 edit.add_fixits (&richloc);
1602 ASSERT_FALSE (edit.valid_p ());
1603 ASSERT_EQ (NULL, edit.get_content (filename));
1604 ASSERT_EQ (NULL, edit.generate_diff (false));
1607 /* Verify limits of valid columns in replacement fixits. */
1609 /* Verify replacing the end of the line. */
1611 rich_location richloc (line_table, c11);
1612 source_range range = source_range::from_locations (c11, c14);
1613 richloc.add_fixit_replace (range, "change");
1615 /* Col 14 is at the end of the line, so the replacement
1616 should succeed. */
1617 edit_context edit;
1618 edit.add_fixits (&richloc);
1619 auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
1620 if (c14 <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1621 ASSERT_STREQ ("One-liner change\n", new_content);
1622 else
1623 ASSERT_EQ (NULL, new_content);
1626 /* Verify going beyond the end of the line. */
1628 rich_location richloc (line_table, c11);
1629 source_range range = source_range::from_locations (c11, c15);
1630 richloc.add_fixit_replace (range, "change");
1632 /* Col 15 is after the end of the line, so the replacement
1633 should fail; verify that the attempt fails gracefully. */
1634 edit_context edit;
1635 ASSERT_TRUE (edit.valid_p ());
1636 edit.add_fixits (&richloc);
1637 ASSERT_FALSE (edit.valid_p ());
1638 ASSERT_EQ (NULL, edit.get_content (filename));
1639 ASSERT_EQ (NULL, edit.generate_diff (false));
1643 /* Run all of the selftests within this file. */
1645 void
1646 edit_context_c_tests ()
1648 test_get_content ();
1649 for_each_line_table_case (test_applying_fixits_insert_before);
1650 for_each_line_table_case (test_applying_fixits_insert_after);
1651 for_each_line_table_case (test_applying_fixits_insert_after_at_line_end);
1652 for_each_line_table_case (test_applying_fixits_insert_after_failure);
1653 for_each_line_table_case (test_applying_fixits_growing_replace);
1654 for_each_line_table_case (test_applying_fixits_shrinking_replace);
1655 for_each_line_table_case (test_applying_fixits_remove);
1656 for_each_line_table_case (test_applying_fixits_multiple);
1657 for_each_line_table_case (test_applying_fixits_multiple_lines);
1658 for_each_line_table_case (test_applying_fixits_modernize_named_init);
1659 test_applying_fixits_unreadable_file ();
1660 test_applying_fixits_line_out_of_range ();
1661 for_each_line_table_case (test_applying_fixits_column_validation);
1664 } // namespace selftest
1666 #endif /* CHECKING_P */