1 /* Determining the results of applying fix-it hints.
2 Copyright (C) 2016-2018 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
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
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/>. */
22 #include "coretypes.h"
24 #include "edit-context.h"
25 #include "pretty-print.h"
26 #include "diagnostic-color.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
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. */
49 /* A struct to hold the params of a print_diff call. */
53 diff (pretty_printer
*pp
, bool show_filenames
)
54 : m_pp (pp
), m_show_filenames (show_filenames
) {}
57 bool m_show_filenames
;
60 /* The state of one named file within an edit_context: the filename,
61 and the lines that have been edited so far. */
66 edited_file (const char *filename
);
67 static void delete_cb (edited_file
*file
);
69 const char *get_filename () const { return m_filename
; }
72 bool apply_fixit (int line
, int start_column
,
74 const char *replacement_str
,
76 int get_effective_column (int line
, int column
);
78 static int call_print_diff (const char *, edited_file
*file
,
81 diff
*d
= (diff
*)user_data
;
82 file
->print_diff (d
->m_pp
, d
->m_show_filenames
);
87 bool print_content (pretty_printer
*pp
);
88 void print_diff (pretty_printer
*pp
, bool show_filenames
);
89 int print_diff_hunk (pretty_printer
*pp
, int old_start_of_hunk
,
90 int old_end_of_hunk
, int new_start_of_hunk
);
91 edited_line
*get_line (int line
);
92 edited_line
*get_or_insert_line (int line
);
93 int get_num_lines (bool *missing_trailing_newline
);
95 int get_effective_line_count (int old_start_of_hunk
,
98 void print_run_of_changed_lines (pretty_printer
*pp
,
102 const char *m_filename
;
103 typed_splay_tree
<int, edited_line
*> m_edited_lines
;
107 /* A line added before an edited_line. */
112 added_line (const char *content
, int len
)
113 : m_content (xstrndup (content
, len
)), m_len (len
) {}
114 ~added_line () { free (m_content
); }
116 const char *get_content () const { return m_content
; }
117 int get_len () const { return m_len
; }
124 /* The state of one edited line within an edited_file.
125 As well as the current content of the line, it contains a record of
126 the changes, so that further changes can be applied in the correct
129 When handling fix-it hints containing newlines, new lines are added
130 as added_line predecessors to an edited_line. Hence it's possible
131 for an "edited_line" to not actually have been changed, but to merely
132 be a placeholder for the lines added before it. This can be tested
133 for with actuall_edited_p, and has a slight effect on how diff hunks
139 edited_line (const char *filename
, int line_num
);
141 static void delete_cb (edited_line
*el
);
143 int get_line_num () const { return m_line_num
; }
144 const char *get_content () const { return m_content
; }
145 int get_len () const { return m_len
; }
147 int get_effective_column (int orig_column
) const;
148 bool apply_fixit (int start_column
,
150 const char *replacement_str
,
151 int replacement_len
);
153 int get_effective_line_count () const;
155 /* Has the content of this line actually changed, or are we merely
156 recording predecessor added_lines? */
157 bool actually_edited_p () const { return m_line_events
.length () > 0; }
159 void print_content (pretty_printer
*pp
) const;
160 void print_diff_lines (pretty_printer
*pp
) const;
163 void ensure_capacity (int len
);
164 void ensure_terminated ();
170 auto_vec
<line_event
> m_line_events
;
171 auto_vec
<added_line
*> m_predecessors
;
174 /* Class for representing edit events that have occurred on one line of
175 one file: the replacement of some text betweeen some columns
178 Subsequent events will need their columns adjusting if they're
179 are on this line and their column is >= the start point. */
184 line_event (int start
, int next
, int len
) : m_start (start
),
185 m_delta (len
- (next
- start
)) {}
187 int get_effective_column (int orig_column
) const
189 if (orig_column
>= m_start
)
190 return orig_column
+= m_delta
;
203 print_diff_line (pretty_printer
*pp
, char prefix_char
,
204 const char *line
, int line_size
);
206 /* Implementation of class edit_context. */
208 /* edit_context's ctor. */
210 edit_context::edit_context ()
212 m_files (strcmp
, NULL
, edited_file::delete_cb
)
215 /* Add any fixits within RICHLOC to this context, recording the
216 changes that they make. */
219 edit_context::add_fixits (rich_location
*richloc
)
223 if (richloc
->seen_impossible_fixit_p ())
228 for (unsigned i
= 0; i
< richloc
->get_num_fixit_hints (); i
++)
230 const fixit_hint
*hint
= richloc
->get_fixit_hint (i
);
231 if (!apply_fixit (hint
))
236 /* Get the content of the given file, with fix-its applied.
237 If any errors occurred in this edit_context, return NULL.
238 The ptr should be freed by the caller. */
241 edit_context::get_content (const char *filename
)
245 edited_file
&file
= get_or_insert_file (filename
);
246 return file
.get_content ();
249 /* Map a location before the edits to a column number after the edits.
250 This method is for the selftests. */
253 edit_context::get_effective_column (const char *filename
, int line
,
256 edited_file
*file
= get_file (filename
);
259 return file
->get_effective_column (line
, column
);
262 /* Generate a unified diff. The resulting string should be freed by the
263 caller. Primarily for selftests.
264 If any errors occurred in this edit_context, return NULL. */
267 edit_context::generate_diff (bool show_filenames
)
273 print_diff (&pp
, show_filenames
);
274 return xstrdup (pp_formatted_text (&pp
));
277 /* Print a unified diff to PP, showing the changes made within the
281 edit_context::print_diff (pretty_printer
*pp
, bool show_filenames
)
285 diff
d (pp
, show_filenames
);
286 m_files
.foreach (edited_file::call_print_diff
, &d
);
289 /* Attempt to apply the given fixit. Return true if it can be
290 applied, or false otherwise. */
293 edit_context::apply_fixit (const fixit_hint
*hint
)
295 expanded_location start
= expand_location (hint
->get_start_loc ());
296 expanded_location next_loc
= expand_location (hint
->get_next_loc ());
297 if (start
.file
!= next_loc
.file
)
299 if (start
.line
!= next_loc
.line
)
301 if (start
.column
== 0)
303 if (next_loc
.column
== 0)
306 edited_file
&file
= get_or_insert_file (start
.file
);
309 return file
.apply_fixit (start
.line
, start
.column
, next_loc
.column
,
311 hint
->get_length ());
314 /* Locate the edited_file * for FILENAME, if any
315 Return NULL if there isn't one. */
318 edit_context::get_file (const char *filename
)
320 gcc_assert (filename
);
321 return m_files
.lookup (filename
);
324 /* Locate the edited_file for FILENAME, adding one if there isn't one. */
327 edit_context::get_or_insert_file (const char *filename
)
329 gcc_assert (filename
);
331 edited_file
*file
= get_file (filename
);
336 file
= new edited_file (filename
);
337 m_files
.insert (filename
, file
);
341 /* Implementation of class edited_file. */
343 /* Callback for m_edited_lines, for comparing line numbers. */
345 static int line_comparator (int a
, int b
)
350 /* edited_file's constructor. */
352 edited_file::edited_file (const char *filename
)
353 : m_filename (filename
),
354 m_edited_lines (line_comparator
, NULL
, edited_line::delete_cb
),
359 /* A callback for deleting edited_file *, for use as a
360 delete_value_fn for edit_context::m_files. */
363 edited_file::delete_cb (edited_file
*file
)
368 /* Get the content of the file, with fix-its applied.
369 The ptr should be freed by the caller. */
372 edited_file::get_content ()
375 if (!print_content (&pp
))
377 return xstrdup (pp_formatted_text (&pp
));
380 /* Attempt to replace columns START_COLUMN up to but not including NEXT_COLUMN
381 of LINE with the string REPLACEMENT_STR of length REPLACEMENT_LEN,
382 updating the in-memory copy of the line, and the record of edits to
386 edited_file::apply_fixit (int line
, int start_column
, int next_column
,
387 const char *replacement_str
,
390 edited_line
*el
= get_or_insert_line (line
);
393 return el
->apply_fixit (start_column
, next_column
, replacement_str
,
397 /* Given line LINE, map from COLUMN in the input file to its current
398 column after edits have been applied. */
401 edited_file::get_effective_column (int line
, int column
)
403 const edited_line
*el
= get_line (line
);
406 return el
->get_effective_column (column
);
409 /* Attempt to print the content of the file to PP, with edits applied.
410 Return true if successful, false otherwise. */
413 edited_file::print_content (pretty_printer
*pp
)
415 bool missing_trailing_newline
;
416 int line_count
= get_num_lines (&missing_trailing_newline
);
417 for (int line_num
= 1; line_num
<= line_count
; line_num
++)
419 edited_line
*el
= get_line (line_num
);
421 el
->print_content (pp
);
424 char_span line
= location_get_source_line (m_filename
, line_num
);
427 for (size_t i
= 0; i
< line
.length (); i
++)
428 pp_character (pp
, line
[i
]);
430 if (line_num
< line_count
)
431 pp_character (pp
, '\n');
434 if (!missing_trailing_newline
)
435 pp_character (pp
, '\n');
440 /* Print a unified diff to PP, showing any changes that have occurred
444 edited_file::print_diff (pretty_printer
*pp
, bool show_filenames
)
448 pp_string (pp
, colorize_start (pp_show_color (pp
), "diff-filename"));
449 pp_printf (pp
, "--- %s\n", m_filename
);
450 pp_printf (pp
, "+++ %s\n", m_filename
);
451 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
454 edited_line
*el
= m_edited_lines
.min ();
456 bool missing_trailing_newline
;
457 int line_count
= get_num_lines (&missing_trailing_newline
);
459 const int context_lines
= 3;
461 /* Track new line numbers minus old line numbers. */
467 int start_of_hunk
= el
->get_line_num ();
468 start_of_hunk
-= context_lines
;
469 if (start_of_hunk
< 1)
472 /* Locate end of hunk, merging in changed lines
473 that are sufficiently close. */
477 = m_edited_lines
.successor (el
->get_line_num ());
481 int end_of_printed_hunk
= el
->get_line_num () + context_lines
;
482 if (!el
->actually_edited_p ())
483 end_of_printed_hunk
--;
485 if (end_of_printed_hunk
486 >= next_el
->get_line_num () - context_lines
)
492 int end_of_hunk
= el
->get_line_num ();
493 end_of_hunk
+= context_lines
;
494 if (!el
->actually_edited_p ())
496 if (end_of_hunk
> line_count
)
497 end_of_hunk
= line_count
;
499 int new_start_of_hunk
= start_of_hunk
+ line_delta
;
500 line_delta
+= print_diff_hunk (pp
, start_of_hunk
, end_of_hunk
,
502 el
= m_edited_lines
.successor (el
->get_line_num ());
506 /* Print one hunk within a unified diff to PP, covering the
507 given range of lines. OLD_START_OF_HUNK and OLD_END_OF_HUNK are
508 line numbers in the unedited version of the file.
509 NEW_START_OF_HUNK is a line number in the edited version of the file.
510 Return the change in the line count within the hunk. */
513 edited_file::print_diff_hunk (pretty_printer
*pp
, int old_start_of_hunk
,
514 int old_end_of_hunk
, int new_start_of_hunk
)
516 int old_num_lines
= old_end_of_hunk
- old_start_of_hunk
+ 1;
518 = get_effective_line_count (old_start_of_hunk
, old_end_of_hunk
);
520 pp_string (pp
, colorize_start (pp_show_color (pp
), "diff-hunk"));
521 pp_printf (pp
, "@@ -%i,%i +%i,%i @@\n", old_start_of_hunk
, old_num_lines
,
522 new_start_of_hunk
, new_num_lines
);
523 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
525 int line_num
= old_start_of_hunk
;
526 while (line_num
<= old_end_of_hunk
)
528 edited_line
*el
= get_line (line_num
);
531 /* We have an edited line.
532 Consolidate into runs of changed lines. */
533 const int first_changed_line_in_run
= line_num
;
534 while (get_line (line_num
))
536 const int last_changed_line_in_run
= line_num
- 1;
537 print_run_of_changed_lines (pp
, first_changed_line_in_run
,
538 last_changed_line_in_run
);
542 /* Unchanged line. */
543 char_span old_line
= location_get_source_line (m_filename
, line_num
);
544 print_diff_line (pp
, ' ', old_line
.get_buffer (), old_line
.length ());
549 return new_num_lines
- old_num_lines
;
552 /* Subroutine of edited_file::print_diff_hunk: given a run of lines
553 from START_OF_RUN to END_OF_RUN that all have edited_line instances,
554 print the diff to PP. */
557 edited_file::print_run_of_changed_lines (pretty_printer
*pp
,
561 /* Show old version of lines. */
562 pp_string (pp
, colorize_start (pp_show_color (pp
),
564 for (int line_num
= start_of_run
;
565 line_num
<= end_of_run
;
568 edited_line
*el_in_run
= get_line (line_num
);
569 gcc_assert (el_in_run
);
570 if (el_in_run
->actually_edited_p ())
572 char_span old_line
= location_get_source_line (m_filename
, line_num
);
573 print_diff_line (pp
, '-', old_line
.get_buffer (),
577 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
579 /* Show new version of lines. */
580 pp_string (pp
, colorize_start (pp_show_color (pp
),
582 for (int line_num
= start_of_run
;
583 line_num
<= end_of_run
;
586 edited_line
*el_in_run
= get_line (line_num
);
587 gcc_assert (el_in_run
);
588 el_in_run
->print_diff_lines (pp
);
590 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
593 /* Print one line within a diff, starting with PREFIX_CHAR,
594 followed by the LINE of content, of length LEN. LINE is
595 not necessarily 0-terminated. Print a trailing newline. */
598 print_diff_line (pretty_printer
*pp
, char prefix_char
,
599 const char *line
, int len
)
601 pp_character (pp
, prefix_char
);
602 for (int i
= 0; i
< len
; i
++)
603 pp_character (pp
, line
[i
]);
604 pp_character (pp
, '\n');
607 /* Determine the number of lines that will be present after
608 editing for the range of lines from OLD_START_OF_HUNK to
609 OLD_END_OF_HUNK inclusive. */
612 edited_file::get_effective_line_count (int old_start_of_hunk
,
616 for (int old_line_num
= old_start_of_hunk
; old_line_num
<= old_end_of_hunk
;
619 edited_line
*el
= get_line (old_line_num
);
621 line_count
+= el
->get_effective_line_count ();
628 /* Get the state of LINE within the file, or NULL if it is untouched. */
631 edited_file::get_line (int line
)
633 return m_edited_lines
.lookup (line
);
636 /* Get the state of LINE within the file, creating a state for it
637 if necessary. Return NULL if an error occurs. */
640 edited_file::get_or_insert_line (int line
)
642 edited_line
*el
= get_line (line
);
645 el
= new edited_line (m_filename
, line
);
646 if (el
->get_content () == NULL
)
651 m_edited_lines
.insert (line
, el
);
655 /* Get the total number of lines in m_content, writing
656 true to *MISSING_TRAILING_NEWLINE if the final line
657 if missing a newline, false otherwise. */
660 edited_file::get_num_lines (bool *missing_trailing_newline
)
662 gcc_assert (missing_trailing_newline
);
663 if (m_num_lines
== -1)
669 = location_get_source_line (m_filename
, m_num_lines
+ 1);
676 *missing_trailing_newline
= location_missing_trailing_newline (m_filename
);
680 /* Implementation of class edited_line. */
682 /* edited_line's ctor. */
684 edited_line::edited_line (const char *filename
, int line_num
)
685 : m_line_num (line_num
),
686 m_content (NULL
), m_len (0), m_alloc_sz (0),
690 char_span line
= location_get_source_line (filename
, line_num
);
693 m_len
= line
.length ();
694 ensure_capacity (m_len
);
695 memcpy (m_content
, line
.get_buffer (), m_len
);
696 ensure_terminated ();
699 /* edited_line's dtor. */
701 edited_line::~edited_line ()
707 FOR_EACH_VEC_ELT (m_predecessors
, i
, pred
)
711 /* A callback for deleting edited_line *, for use as a
712 delete_value_fn for edited_file::m_edited_lines. */
715 edited_line::delete_cb (edited_line
*el
)
720 /* Map a location before the edits to a column number after the edits,
721 within a specific line. */
724 edited_line::get_effective_column (int orig_column
) const
728 FOR_EACH_VEC_ELT (m_line_events
, i
, event
)
729 orig_column
= event
->get_effective_column (orig_column
);
733 /* Attempt to replace columns START_COLUMN up to but not including
734 NEXT_COLUMN of the line with the string REPLACEMENT_STR of
735 length REPLACEMENT_LEN, updating the in-memory copy of the line,
736 and the record of edits to the line.
737 Return true if successful; false if an error occurred. */
740 edited_line::apply_fixit (int start_column
,
742 const char *replacement_str
,
745 /* Handle newlines. They will only ever be at the end of the
746 replacement text, thanks to the filtering in rich_location. */
747 if (replacement_len
> 1)
748 if (replacement_str
[replacement_len
- 1] == '\n')
750 /* Stash in m_predecessors, stripping off newline. */
751 m_predecessors
.safe_push (new added_line (replacement_str
,
752 replacement_len
- 1));
756 start_column
= get_effective_column (start_column
);
757 next_column
= get_effective_column (next_column
);
759 int start_offset
= start_column
- 1;
760 int next_offset
= next_column
- 1;
762 gcc_assert (start_offset
>= 0);
763 gcc_assert (next_offset
>= 0);
765 if (start_column
> next_column
)
767 if (start_offset
>= (m_len
+ 1))
769 if (next_offset
>= (m_len
+ 1))
772 size_t victim_len
= next_offset
- start_offset
;
774 /* Ensure buffer is big enough. */
775 size_t new_len
= m_len
+ replacement_len
- victim_len
;
776 ensure_capacity (new_len
);
778 char *suffix
= m_content
+ next_offset
;
779 gcc_assert (suffix
<= m_content
+ m_len
);
780 size_t len_suffix
= (m_content
+ m_len
) - suffix
;
782 /* Move successor content into position. They overlap, so use memmove. */
783 memmove (m_content
+ start_offset
+ replacement_len
,
786 /* Replace target content. They don't overlap, so use memcpy. */
787 memcpy (m_content
+ start_offset
,
793 ensure_terminated ();
795 /* Record the replacement, so that future changes to the line can have
796 their column information adjusted accordingly. */
797 m_line_events
.safe_push (line_event (start_column
, next_column
,
802 /* Determine the number of lines that will be present after
803 editing for this line. Typically this is just 1, but
804 if newlines have been added before this line, they will
808 edited_line::get_effective_line_count () const
810 return m_predecessors
.length () + 1;
813 /* Subroutine of edited_file::print_content.
814 Print this line and any new lines added before it, to PP. */
817 edited_line::print_content (pretty_printer
*pp
) const
821 FOR_EACH_VEC_ELT (m_predecessors
, i
, pred
)
823 pp_string (pp
, pred
->get_content ());
826 pp_string (pp
, m_content
);
829 /* Subroutine of edited_file::print_run_of_changed_lines for
830 printing diff hunks to PP.
831 Print the '+' line for this line, and any newlines added
833 Note that if this edited_line was actually edited, the '-'
834 line has already been printed. If it wasn't, then we merely
835 have a placeholder edited_line for adding newlines to, and
836 we need to print a ' ' line for the edited_line as we haven't
840 edited_line::print_diff_lines (pretty_printer
*pp
) const
844 FOR_EACH_VEC_ELT (m_predecessors
, i
, pred
)
845 print_diff_line (pp
, '+', pred
->get_content (),
847 if (actually_edited_p ())
848 print_diff_line (pp
, '+', m_content
, m_len
);
850 print_diff_line (pp
, ' ', m_content
, m_len
);
853 /* Ensure that the buffer for m_content is at least large enough to hold
854 a string of length LEN and its 0-terminator, doubling on repeated
858 edited_line::ensure_capacity (int len
)
860 /* Allow 1 extra byte for 0-termination. */
861 if (m_alloc_sz
< (len
+ 1))
863 size_t new_alloc_sz
= (len
+ 1) * 2;
864 m_content
= (char *)xrealloc (m_content
, new_alloc_sz
);
865 m_alloc_sz
= new_alloc_sz
;
869 /* Ensure that m_content is 0-terminated. */
872 edited_line::ensure_terminated ()
874 /* 0-terminate the buffer. */
875 gcc_assert (m_len
< m_alloc_sz
);
876 m_content
[m_len
] = '\0';
881 /* Selftests of code-editing. */
885 /* A wrapper class for ensuring that the underlying pointer is freed. */
887 template <typename POINTER_T
>
891 auto_free (POINTER_T p
) : m_ptr (p
) {}
892 ~auto_free () { free (m_ptr
); }
894 operator POINTER_T () { return m_ptr
; }
900 /* Verify that edit_context::get_content works for unedited files. */
905 /* Test of empty file. */
907 const char *content
= ("");
908 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
910 auto_free
<char *> result
= edit
.get_content (tmp
.get_filename ());
911 ASSERT_STREQ ("", result
);
914 /* Test of simple content. */
916 const char *content
= ("/* before */\n"
919 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
921 auto_free
<char *> result
= edit
.get_content (tmp
.get_filename ());
922 ASSERT_STREQ ("/* before */\n"
924 "/* after */\n", result
);
927 /* Test of omitting the trailing newline on the final line. */
929 const char *content
= ("/* before */\n"
932 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
934 auto_free
<char *> result
= edit
.get_content (tmp
.get_filename ());
935 /* We should respect the omitted trailing newline. */
936 ASSERT_STREQ ("/* before */\n"
938 "/* after */", result
);
942 /* Test applying an "insert" fixit, using insert_before. */
945 test_applying_fixits_insert_before (const line_table_case
&case_
)
947 /* Create a tempfile and write some text to it.
948 .........................0000000001111111.
949 .........................1234567890123456. */
950 const char *old_content
= ("/* before */\n"
953 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
954 const char *filename
= tmp
.get_filename ();
955 line_table_test
ltt (case_
);
956 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 2);
958 /* Add a comment in front of "bar.field". */
959 location_t start
= linemap_position_for_column (line_table
, 7);
960 rich_location
richloc (line_table
, start
);
961 richloc
.add_fixit_insert_before ("/* inserted */");
963 if (start
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
967 edit
.add_fixits (&richloc
);
968 auto_free
<char *> new_content
= edit
.get_content (filename
);
969 if (start
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
970 ASSERT_STREQ ("/* before */\n"
971 "foo = /* inserted */bar.field;\n"
972 "/* after */\n", new_content
);
974 /* Verify that locations on other lines aren't affected by the change. */
975 ASSERT_EQ (100, edit
.get_effective_column (filename
, 1, 100));
976 ASSERT_EQ (100, edit
.get_effective_column (filename
, 3, 100));
978 /* Verify locations on the line before the change. */
979 ASSERT_EQ (1, edit
.get_effective_column (filename
, 2, 1));
980 ASSERT_EQ (6, edit
.get_effective_column (filename
, 2, 6));
982 /* Verify locations on the line at and after the change. */
983 ASSERT_EQ (21, edit
.get_effective_column (filename
, 2, 7));
984 ASSERT_EQ (22, edit
.get_effective_column (filename
, 2, 8));
987 auto_free
<char *> diff
= edit
.generate_diff (false);
988 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
990 "-foo = bar.field;\n"
991 "+foo = /* inserted */bar.field;\n"
992 " /* after */\n", diff
);
995 /* Test applying an "insert" fixit, using insert_after, with
996 a range of length > 1 (to ensure that the end-point of
997 the input range is used). */
1000 test_applying_fixits_insert_after (const line_table_case
&case_
)
1002 /* Create a tempfile and write some text to it.
1003 .........................0000000001111111.
1004 .........................1234567890123456. */
1005 const char *old_content
= ("/* before */\n"
1006 "foo = bar.field;\n"
1008 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1009 const char *filename
= tmp
.get_filename ();
1010 line_table_test
ltt (case_
);
1011 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 2);
1013 /* Add a comment after "field". */
1014 location_t start
= linemap_position_for_column (line_table
, 11);
1015 location_t finish
= linemap_position_for_column (line_table
, 15);
1016 location_t field
= make_location (start
, start
, finish
);
1017 rich_location
richloc (line_table
, field
);
1018 richloc
.add_fixit_insert_after ("/* inserted */");
1020 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1023 /* Verify that the text was inserted after the end of "field". */
1025 edit
.add_fixits (&richloc
);
1026 auto_free
<char *> new_content
= edit
.get_content (filename
);
1027 ASSERT_STREQ ("/* before */\n"
1028 "foo = bar.field/* inserted */;\n"
1029 "/* after */\n", new_content
);
1032 auto_free
<char *> diff
= edit
.generate_diff (false);
1033 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1035 "-foo = bar.field;\n"
1036 "+foo = bar.field/* inserted */;\n"
1037 " /* after */\n", diff
);
1040 /* Test applying an "insert" fixit, using insert_after at the end of
1041 a line (contrast with test_applying_fixits_insert_after_failure
1045 test_applying_fixits_insert_after_at_line_end (const line_table_case
&case_
)
1047 /* Create a tempfile and write some text to it.
1048 .........................0000000001111111.
1049 .........................1234567890123456. */
1050 const char *old_content
= ("/* before */\n"
1051 "foo = bar.field;\n"
1053 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1054 const char *filename
= tmp
.get_filename ();
1055 line_table_test
ltt (case_
);
1056 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 2);
1058 /* Add a comment after the semicolon. */
1059 location_t loc
= linemap_position_for_column (line_table
, 16);
1060 rich_location
richloc (line_table
, loc
);
1061 richloc
.add_fixit_insert_after ("/* inserted */");
1063 if (loc
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1067 edit
.add_fixits (&richloc
);
1068 auto_free
<char *> new_content
= edit
.get_content (filename
);
1069 ASSERT_STREQ ("/* before */\n"
1070 "foo = bar.field;/* inserted */\n"
1071 "/* after */\n", new_content
);
1074 auto_free
<char *> diff
= edit
.generate_diff (false);
1075 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1077 "-foo = bar.field;\n"
1078 "+foo = bar.field;/* inserted */\n"
1079 " /* after */\n", diff
);
1082 /* Test of a failed attempt to apply an "insert" fixit, using insert_after,
1083 due to the relevant linemap ending. Contrast with
1084 test_applying_fixits_insert_after_at_line_end above. */
1087 test_applying_fixits_insert_after_failure (const line_table_case
&case_
)
1089 /* Create a tempfile and write some text to it.
1090 .........................0000000001111111.
1091 .........................1234567890123456. */
1092 const char *old_content
= ("/* before */\n"
1093 "foo = bar.field;\n"
1095 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1096 const char *filename
= tmp
.get_filename ();
1097 line_table_test
ltt (case_
);
1098 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 2);
1100 /* Add a comment after the semicolon. */
1101 location_t loc
= linemap_position_for_column (line_table
, 16);
1102 rich_location
richloc (line_table
, loc
);
1104 /* We want a failure of linemap_position_for_loc_and_offset.
1105 We can do this by starting a new linemap at line 3, so that
1106 there is no appropriate location value for the insertion point
1107 within the linemap for line 2. */
1108 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 3);
1110 /* The failure fails to happen at the transition point from
1111 packed ranges to unpacked ranges (where there are some "spare"
1112 location_t values). Skip the test there. */
1113 if (loc
>= LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES
)
1116 /* Offsetting "loc" should now fail (by returning the input loc. */
1117 ASSERT_EQ (loc
, linemap_position_for_loc_and_offset (line_table
, loc
, 1));
1119 /* Hence attempting to use add_fixit_insert_after at the end of the line
1121 richloc
.add_fixit_insert_after ("/* inserted */");
1122 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
1125 edit
.add_fixits (&richloc
);
1126 ASSERT_FALSE (edit
.valid_p ());
1127 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1128 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1131 /* Test applying an "insert" fixit that adds a newline. */
1134 test_applying_fixits_insert_containing_newline (const line_table_case
&case_
)
1136 /* Create a tempfile and write some text to it.
1137 .........................0000000001111111.
1138 .........................1234567890123456. */
1139 const char *old_content
= (" case 'a':\n" /* line 1. */
1140 " x = a;\n" /* line 2. */
1141 " case 'b':\n" /* line 3. */
1142 " x = b;\n");/* line 4. */
1144 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1145 const char *filename
= tmp
.get_filename ();
1146 line_table_test
ltt (case_
);
1147 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 3);
1149 /* Add a "break;" on a line by itself before line 3 i.e. before
1150 column 1 of line 3. */
1151 location_t case_start
= linemap_position_for_column (line_table
, 5);
1152 location_t case_finish
= linemap_position_for_column (line_table
, 13);
1153 location_t case_loc
= make_location (case_start
, case_start
, case_finish
);
1154 rich_location
richloc (line_table
, case_loc
);
1155 location_t line_start
= linemap_position_for_column (line_table
, 1);
1156 richloc
.add_fixit_insert_before (line_start
, " break;\n");
1158 if (case_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1162 edit
.add_fixits (&richloc
);
1163 auto_free
<char *> new_content
= edit
.get_content (filename
);
1164 ASSERT_STREQ ((" case 'a':\n"
1172 auto_free
<char *> diff
= edit
.generate_diff (false);
1173 ASSERT_STREQ (("@@ -1,4 +1,5 @@\n"
1182 /* Test applying a "replace" fixit that grows the affected line. */
1185 test_applying_fixits_growing_replace (const line_table_case
&case_
)
1187 /* Create a tempfile and write some text to it.
1188 .........................0000000001111111.
1189 .........................1234567890123456. */
1190 const char *old_content
= ("/* before */\n"
1191 "foo = bar.field;\n"
1193 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1194 const char *filename
= tmp
.get_filename ();
1195 line_table_test
ltt (case_
);
1196 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1198 /* Replace "field" with "m_field". */
1199 location_t start
= linemap_position_for_column (line_table
, 11);
1200 location_t finish
= linemap_position_for_column (line_table
, 15);
1201 location_t field
= make_location (start
, start
, finish
);
1202 rich_location
richloc (line_table
, field
);
1203 richloc
.add_fixit_replace ("m_field");
1206 edit
.add_fixits (&richloc
);
1207 auto_free
<char *> new_content
= edit
.get_content (filename
);
1208 if (finish
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1210 ASSERT_STREQ ("/* before */\n"
1211 "foo = bar.m_field;\n"
1212 "/* after */\n", new_content
);
1214 /* Verify location of ";" after the change. */
1215 ASSERT_EQ (18, edit
.get_effective_column (filename
, 2, 16));
1218 auto_free
<char *> diff
= edit
.generate_diff (false);
1219 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1221 "-foo = bar.field;\n"
1222 "+foo = bar.m_field;\n"
1223 " /* after */\n", diff
);
1227 /* Test applying a "replace" fixit that shrinks the affected line. */
1230 test_applying_fixits_shrinking_replace (const line_table_case
&case_
)
1232 /* Create a tempfile and write some text to it.
1233 .........................000000000111111111.
1234 .........................123456789012345678. */
1235 const char *old_content
= ("/* before */\n"
1236 "foo = bar.m_field;\n"
1238 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1239 const char *filename
= tmp
.get_filename ();
1240 line_table_test
ltt (case_
);
1241 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1243 /* Replace "field" with "m_field". */
1244 location_t start
= linemap_position_for_column (line_table
, 11);
1245 location_t finish
= linemap_position_for_column (line_table
, 17);
1246 location_t m_field
= make_location (start
, start
, finish
);
1247 rich_location
richloc (line_table
, m_field
);
1248 richloc
.add_fixit_replace ("field");
1251 edit
.add_fixits (&richloc
);
1252 auto_free
<char *> new_content
= edit
.get_content (filename
);
1253 if (finish
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1255 ASSERT_STREQ ("/* before */\n"
1256 "foo = bar.field;\n"
1257 "/* after */\n", new_content
);
1259 /* Verify location of ";" after the change. */
1260 ASSERT_EQ (16, edit
.get_effective_column (filename
, 2, 18));
1263 auto_free
<char *> diff
= edit
.generate_diff (false);
1264 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1266 "-foo = bar.m_field;\n"
1267 "+foo = bar.field;\n"
1268 " /* after */\n", diff
);
1272 /* Replacement fix-it hint containing a newline. */
1275 test_applying_fixits_replace_containing_newline (const line_table_case
&case_
)
1277 /* Create a tempfile and write some text to it.
1278 .........................0000000001111.
1279 .........................1234567890123. */
1280 const char *old_content
= "foo = bar ();\n";
1282 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1283 const char *filename
= tmp
.get_filename ();
1284 line_table_test
ltt (case_
);
1285 linemap_add (line_table
, LC_ENTER
, false, filename
, 1);
1287 /* Replace the " = " with "\n = ", as if we were reformatting an
1288 overly long line. */
1289 location_t start
= linemap_position_for_column (line_table
, 4);
1290 location_t finish
= linemap_position_for_column (line_table
, 6);
1291 location_t loc
= linemap_position_for_column (line_table
, 13);
1292 rich_location
richloc (line_table
, loc
);
1293 source_range range
= source_range::from_locations (start
, finish
);
1294 richloc
.add_fixit_replace (range
, "\n = ");
1296 /* Newlines are only supported within fix-it hints that
1297 are at the start of lines (for entirely new lines), hence
1298 this fix-it should not be displayed. */
1299 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
1301 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1305 edit
.add_fixits (&richloc
);
1306 auto_free
<char *> new_content
= edit
.get_content (filename
);
1307 //ASSERT_STREQ ("foo\n = bar ();\n", new_content);
1310 /* Test applying a "remove" fixit. */
1313 test_applying_fixits_remove (const line_table_case
&case_
)
1315 /* Create a tempfile and write some text to it.
1316 .........................000000000111111111.
1317 .........................123456789012345678. */
1318 const char *old_content
= ("/* before */\n"
1319 "foo = bar.m_field;\n"
1321 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1322 const char *filename
= tmp
.get_filename ();
1323 line_table_test
ltt (case_
);
1324 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1326 /* Remove ".m_field". */
1327 location_t start
= linemap_position_for_column (line_table
, 10);
1328 location_t finish
= linemap_position_for_column (line_table
, 17);
1329 rich_location
richloc (line_table
, start
);
1331 range
.m_start
= start
;
1332 range
.m_finish
= finish
;
1333 richloc
.add_fixit_remove (range
);
1336 edit
.add_fixits (&richloc
);
1337 auto_free
<char *> new_content
= edit
.get_content (filename
);
1338 if (finish
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1340 ASSERT_STREQ ("/* before */\n"
1342 "/* after */\n", new_content
);
1344 /* Verify location of ";" after the change. */
1345 ASSERT_EQ (10, edit
.get_effective_column (filename
, 2, 18));
1348 auto_free
<char *> diff
= edit
.generate_diff (false);
1349 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1351 "-foo = bar.m_field;\n"
1353 " /* after */\n", diff
);
1357 /* Test applying multiple fixits to one line. */
1360 test_applying_fixits_multiple (const line_table_case
&case_
)
1362 /* Create a tempfile and write some text to it.
1363 .........................00000000011111111.
1364 .........................12345678901234567. */
1365 const char *old_content
= ("/* before */\n"
1366 "foo = bar.field;\n"
1368 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1369 const char *filename
= tmp
.get_filename ();
1370 line_table_test
ltt (case_
);
1371 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1373 location_t c7
= linemap_position_for_column (line_table
, 7);
1374 location_t c9
= linemap_position_for_column (line_table
, 9);
1375 location_t c11
= linemap_position_for_column (line_table
, 11);
1376 location_t c15
= linemap_position_for_column (line_table
, 15);
1377 location_t c17
= linemap_position_for_column (line_table
, 17);
1379 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1382 /* Add a comment in front of "bar.field". */
1383 rich_location
insert_a (line_table
, c7
);
1384 insert_a
.add_fixit_insert_before (c7
, "/* alpha */");
1386 /* Add a comment after "bar.field;". */
1387 rich_location
insert_b (line_table
, c17
);
1388 insert_b
.add_fixit_insert_before (c17
, "/* beta */");
1390 /* Replace "bar" with "pub". */
1391 rich_location
replace_a (line_table
, c7
);
1392 replace_a
.add_fixit_replace (source_range::from_locations (c7
, c9
),
1395 /* Replace "field" with "meadow". */
1396 rich_location
replace_b (line_table
, c7
);
1397 replace_b
.add_fixit_replace (source_range::from_locations (c11
, c15
),
1401 edit
.add_fixits (&insert_a
);
1402 ASSERT_EQ (100, edit
.get_effective_column (filename
, 1, 100));
1403 ASSERT_EQ (1, edit
.get_effective_column (filename
, 2, 1));
1404 ASSERT_EQ (6, edit
.get_effective_column (filename
, 2, 6));
1405 ASSERT_EQ (18, edit
.get_effective_column (filename
, 2, 7));
1406 ASSERT_EQ (27, edit
.get_effective_column (filename
, 2, 16));
1407 ASSERT_EQ (100, edit
.get_effective_column (filename
, 3, 100));
1409 edit
.add_fixits (&insert_b
);
1410 edit
.add_fixits (&replace_a
);
1411 edit
.add_fixits (&replace_b
);
1413 if (c17
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1415 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1416 ASSERT_STREQ ("/* before */\n"
1417 "foo = /* alpha */pub.meadow;/* beta */\n"
1422 auto_free
<char *> diff
= edit
.generate_diff (false);
1423 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1425 "-foo = bar.field;\n"
1426 "+foo = /* alpha */pub.meadow;/* beta */\n"
1427 " /* after */\n", diff
);
1431 /* Subroutine of test_applying_fixits_multiple_lines.
1432 Add the text "CHANGED: " to the front of the given line. */
1435 change_line (edit_context
&edit
, int line_num
)
1437 const line_map_ordinary
*ord_map
1438 = LINEMAPS_LAST_ORDINARY_MAP (line_table
);
1439 const int column
= 1;
1441 linemap_position_for_line_and_column (line_table
, ord_map
,
1444 expanded_location exploc
= expand_location (loc
);
1445 if (loc
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1447 ASSERT_EQ (line_num
, exploc
.line
);
1448 ASSERT_EQ (column
, exploc
.column
);
1451 rich_location
insert (line_table
, loc
);
1452 insert
.add_fixit_insert_before ("CHANGED: ");
1453 edit
.add_fixits (&insert
);
1457 /* Subroutine of test_applying_fixits_multiple_lines.
1458 Add the text "INSERTED\n" in front of the given line. */
1461 insert_line (edit_context
&edit
, int line_num
)
1463 const line_map_ordinary
*ord_map
1464 = LINEMAPS_LAST_ORDINARY_MAP (line_table
);
1465 const int column
= 1;
1467 linemap_position_for_line_and_column (line_table
, ord_map
,
1470 expanded_location exploc
= expand_location (loc
);
1471 if (loc
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1473 ASSERT_EQ (line_num
, exploc
.line
);
1474 ASSERT_EQ (column
, exploc
.column
);
1477 rich_location
insert (line_table
, loc
);
1478 insert
.add_fixit_insert_before ("INSERTED\n");
1479 edit
.add_fixits (&insert
);
1483 /* Test of editing multiple lines within a long file,
1484 to ensure that diffs are generated as expected. */
1487 test_applying_fixits_multiple_lines (const line_table_case
&case_
)
1489 /* Create a tempfile and write many lines of text to it. */
1490 named_temp_file
tmp (".txt");
1491 const char *filename
= tmp
.get_filename ();
1492 FILE *f
= fopen (filename
, "w");
1493 ASSERT_NE (f
, NULL
);
1494 for (int i
= 1; i
<= 1000; i
++)
1495 fprintf (f
, "line %i\n", i
);
1498 line_table_test
ltt (case_
);
1499 linemap_add (line_table
, LC_ENTER
, false, filename
, 1);
1500 linemap_position_for_column (line_table
, 127);
1504 /* A run of consecutive lines. */
1505 change_line (edit
, 2);
1506 change_line (edit
, 3);
1507 change_line (edit
, 4);
1508 insert_line (edit
, 5);
1510 /* A run of nearby lines, within the contextual limit. */
1511 change_line (edit
, 150);
1512 change_line (edit
, 151);
1513 location_t last_loc
= change_line (edit
, 153);
1515 if (last_loc
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1519 auto_free
<char *> diff
= edit
.generate_diff (false);
1520 ASSERT_STREQ ("@@ -1,7 +1,8 @@\n"
1525 "+CHANGED: line 2\n"
1526 "+CHANGED: line 3\n"
1527 "+CHANGED: line 4\n"
1532 "@@ -147,10 +148,10 @@\n"
1538 "+CHANGED: line 150\n"
1539 "+CHANGED: line 151\n"
1542 "+CHANGED: line 153\n"
1545 " line 156\n", diff
);
1547 /* Ensure tmp stays alive until this point, so that the tempfile
1548 persists until after the generate_diff call. */
1549 tmp
.get_filename ();
1552 /* Test of converting an initializer for a named field from
1553 the old GCC extension to C99 syntax.
1554 Exercises a shrinking replacement followed by a growing
1555 replacement on the same line. */
1558 test_applying_fixits_modernize_named_init (const line_table_case
&case_
)
1560 /* Create a tempfile and write some text to it.
1561 .........................00000000011111111.
1562 .........................12345678901234567. */
1563 const char *old_content
= ("/* before */\n"
1566 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1567 const char *filename
= tmp
.get_filename ();
1568 line_table_test
ltt (case_
);
1569 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1571 location_t c1
= linemap_position_for_column (line_table
, 1);
1572 location_t c3
= linemap_position_for_column (line_table
, 3);
1573 location_t c8
= linemap_position_for_column (line_table
, 8);
1575 if (c8
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1578 /* Replace "bar" with ".". */
1579 rich_location
r1 (line_table
, c8
);
1580 r1
.add_fixit_replace (source_range::from_locations (c1
, c3
),
1583 /* Replace ":" with "bar =". */
1584 rich_location
r2 (line_table
, c8
);
1585 r2
.add_fixit_replace (source_range::from_locations (c8
, c8
),
1588 /* The order should not matter. Do r1 then r2. */
1591 edit
.add_fixits (&r1
);
1593 /* Verify state after first replacement. */
1595 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1596 /* We should now have:
1597 ............00000000011.
1598 ............12345678901. */
1599 ASSERT_STREQ ("/* before */\n"
1603 /* Location of the "1". */
1604 ASSERT_EQ (6, edit
.get_effective_column (filename
, 2, 8));
1605 /* Location of the ",". */
1606 ASSERT_EQ (9, edit
.get_effective_column (filename
, 2, 11));
1609 edit
.add_fixits (&r2
);
1611 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1612 /* Verify state after second replacement.
1613 ............00000000011111111.
1614 ............12345678901234567. */
1615 ASSERT_STREQ ("/* before */\n"
1621 /* Try again, doing r2 then r1; the new_content should be the same. */
1624 edit
.add_fixits (&r2
);
1625 edit
.add_fixits (&r1
);
1626 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1627 /*.............00000000011111111.
1628 .............12345678901234567. */
1629 ASSERT_STREQ ("/* before */\n"
1636 /* Test of a fixit affecting a file that can't be read. */
1639 test_applying_fixits_unreadable_file ()
1641 const char *filename
= "this-does-not-exist.txt";
1642 line_table_test
ltt ();
1643 linemap_add (line_table
, LC_ENTER
, false, filename
, 1);
1645 location_t loc
= linemap_position_for_column (line_table
, 1);
1647 rich_location
insert (line_table
, loc
);
1648 insert
.add_fixit_insert_before ("change 1");
1649 insert
.add_fixit_insert_before ("change 2");
1652 /* Attempting to add the fixits affecting the unreadable file
1653 should transition the edit from valid to invalid. */
1654 ASSERT_TRUE (edit
.valid_p ());
1655 edit
.add_fixits (&insert
);
1656 ASSERT_FALSE (edit
.valid_p ());
1657 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1658 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1661 /* Verify that we gracefully handle an attempt to edit a line
1662 that's beyond the end of the file. */
1665 test_applying_fixits_line_out_of_range ()
1667 /* Create a tempfile and write some text to it.
1668 ........................00000000011111111.
1669 ........................12345678901234567. */
1670 const char *old_content
= "One-liner file\n";
1671 temp_source_file
tmp (SELFTEST_LOCATION
, ".txt", old_content
);
1672 const char *filename
= tmp
.get_filename ();
1673 line_table_test
ltt ();
1674 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1676 /* Try to insert a string in line 2. */
1677 location_t loc
= linemap_position_for_column (line_table
, 1);
1679 rich_location
insert (line_table
, loc
);
1680 insert
.add_fixit_insert_before ("change");
1682 /* Verify that attempting the insertion puts an edit_context
1683 into an invalid state. */
1685 ASSERT_TRUE (edit
.valid_p ());
1686 edit
.add_fixits (&insert
);
1687 ASSERT_FALSE (edit
.valid_p ());
1688 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1689 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1692 /* Verify the boundary conditions of column values in fix-it
1693 hints applied to edit_context instances. */
1696 test_applying_fixits_column_validation (const line_table_case
&case_
)
1698 /* Create a tempfile and write some text to it.
1699 ........................00000000011111111.
1700 ........................12345678901234567. */
1701 const char *old_content
= "One-liner file\n";
1702 temp_source_file
tmp (SELFTEST_LOCATION
, ".txt", old_content
);
1703 const char *filename
= tmp
.get_filename ();
1704 line_table_test
ltt (case_
);
1705 linemap_add (line_table
, LC_ENTER
, false, filename
, 1);
1707 location_t c11
= linemap_position_for_column (line_table
, 11);
1708 location_t c14
= linemap_position_for_column (line_table
, 14);
1709 location_t c15
= linemap_position_for_column (line_table
, 15);
1710 location_t c16
= linemap_position_for_column (line_table
, 16);
1712 /* Verify limits of valid columns in insertion fixits. */
1714 /* Verify inserting at the end of the line. */
1716 rich_location
richloc (line_table
, c11
);
1717 richloc
.add_fixit_insert_before (c15
, " change");
1719 /* Col 15 is at the end of the line, so the insertion
1722 edit
.add_fixits (&richloc
);
1723 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1724 if (c15
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1725 ASSERT_STREQ ("One-liner file change\n", new_content
);
1727 ASSERT_EQ (NULL
, new_content
);
1730 /* Verify inserting beyond the end of the line. */
1732 rich_location
richloc (line_table
, c11
);
1733 richloc
.add_fixit_insert_before (c16
, " change");
1735 /* Col 16 is beyond the end of the line, so the insertion
1736 should fail gracefully. */
1738 ASSERT_TRUE (edit
.valid_p ());
1739 edit
.add_fixits (&richloc
);
1740 ASSERT_FALSE (edit
.valid_p ());
1741 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1742 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1745 /* Verify limits of valid columns in replacement fixits. */
1747 /* Verify replacing the end of the line. */
1749 rich_location
richloc (line_table
, c11
);
1750 source_range range
= source_range::from_locations (c11
, c14
);
1751 richloc
.add_fixit_replace (range
, "change");
1753 /* Col 14 is at the end of the line, so the replacement
1756 edit
.add_fixits (&richloc
);
1757 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1758 if (c14
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1759 ASSERT_STREQ ("One-liner change\n", new_content
);
1761 ASSERT_EQ (NULL
, new_content
);
1764 /* Verify going beyond the end of the line. */
1766 rich_location
richloc (line_table
, c11
);
1767 source_range range
= source_range::from_locations (c11
, c15
);
1768 richloc
.add_fixit_replace (range
, "change");
1770 /* Col 15 is after the end of the line, so the replacement
1771 should fail; verify that the attempt fails gracefully. */
1773 ASSERT_TRUE (edit
.valid_p ());
1774 edit
.add_fixits (&richloc
);
1775 ASSERT_FALSE (edit
.valid_p ());
1776 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1777 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1781 /* Run all of the selftests within this file. */
1784 edit_context_c_tests ()
1786 test_get_content ();
1787 for_each_line_table_case (test_applying_fixits_insert_before
);
1788 for_each_line_table_case (test_applying_fixits_insert_after
);
1789 for_each_line_table_case (test_applying_fixits_insert_after_at_line_end
);
1790 for_each_line_table_case (test_applying_fixits_insert_after_failure
);
1791 for_each_line_table_case (test_applying_fixits_insert_containing_newline
);
1792 for_each_line_table_case (test_applying_fixits_growing_replace
);
1793 for_each_line_table_case (test_applying_fixits_shrinking_replace
);
1794 for_each_line_table_case (test_applying_fixits_replace_containing_newline
);
1795 for_each_line_table_case (test_applying_fixits_remove
);
1796 for_each_line_table_case (test_applying_fixits_multiple
);
1797 for_each_line_table_case (test_applying_fixits_multiple_lines
);
1798 for_each_line_table_case (test_applying_fixits_modernize_named_init
);
1799 test_applying_fixits_unreadable_file ();
1800 test_applying_fixits_line_out_of_range ();
1801 for_each_line_table_case (test_applying_fixits_column_validation
);
1804 } // namespace selftest
1806 #endif /* CHECKING_P */