1 /* Determining the results of applying fix-it hints.
2 Copyright (C) 2016-2020 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. */
54 diff (pretty_printer
*pp
, bool show_filenames
)
55 : m_pp (pp
), m_show_filenames (show_filenames
) {}
58 bool m_show_filenames
;
61 /* The state of one named file within an edit_context: the filename,
62 and the lines that have been edited so far. */
67 edited_file (const char *filename
);
68 static void delete_cb (edited_file
*file
);
70 const char *get_filename () const { return m_filename
; }
73 bool apply_fixit (int line
, int start_column
,
75 const char *replacement_str
,
77 int get_effective_column (int line
, int column
);
79 static int call_print_diff (const char *, edited_file
*file
,
82 diff
*d
= (diff
*)user_data
;
83 file
->print_diff (d
->m_pp
, d
->m_show_filenames
);
88 bool print_content (pretty_printer
*pp
);
89 void print_diff (pretty_printer
*pp
, bool show_filenames
);
90 int print_diff_hunk (pretty_printer
*pp
, int old_start_of_hunk
,
91 int old_end_of_hunk
, int new_start_of_hunk
);
92 edited_line
*get_line (int line
);
93 edited_line
*get_or_insert_line (int line
);
94 int get_num_lines (bool *missing_trailing_newline
);
96 int get_effective_line_count (int old_start_of_hunk
,
99 void print_run_of_changed_lines (pretty_printer
*pp
,
103 const char *m_filename
;
104 typed_splay_tree
<int, edited_line
*> m_edited_lines
;
108 /* A line added before an edited_line. */
113 added_line (const char *content
, int len
)
114 : m_content (xstrndup (content
, len
)), m_len (len
) {}
115 ~added_line () { free (m_content
); }
117 const char *get_content () const { return m_content
; }
118 int get_len () const { return m_len
; }
125 /* The state of one edited line within an edited_file.
126 As well as the current content of the line, it contains a record of
127 the changes, so that further changes can be applied in the correct
130 When handling fix-it hints containing newlines, new lines are added
131 as added_line predecessors to an edited_line. Hence it's possible
132 for an "edited_line" to not actually have been changed, but to merely
133 be a placeholder for the lines added before it. This can be tested
134 for with actuall_edited_p, and has a slight effect on how diff hunks
140 edited_line (const char *filename
, int line_num
);
142 static void delete_cb (edited_line
*el
);
144 int get_line_num () const { return m_line_num
; }
145 const char *get_content () const { return m_content
; }
146 int get_len () const { return m_len
; }
148 int get_effective_column (int orig_column
) const;
149 bool apply_fixit (int start_column
,
151 const char *replacement_str
,
152 int replacement_len
);
154 int get_effective_line_count () const;
156 /* Has the content of this line actually changed, or are we merely
157 recording predecessor added_lines? */
158 bool actually_edited_p () const { return m_line_events
.length () > 0; }
160 void print_content (pretty_printer
*pp
) const;
161 void print_diff_lines (pretty_printer
*pp
) const;
164 void ensure_capacity (int len
);
165 void ensure_terminated ();
171 auto_vec
<line_event
> m_line_events
;
172 auto_vec
<added_line
*> m_predecessors
;
175 /* Class for representing edit events that have occurred on one line of
176 one file: the replacement of some text betweeen some columns
179 Subsequent events will need their columns adjusting if they're
180 are on this line and their column is >= the start point. */
185 line_event (int start
, int next
, int len
) : m_start (start
),
186 m_delta (len
- (next
- start
)) {}
188 int get_effective_column (int orig_column
) const
190 if (orig_column
>= m_start
)
191 return orig_column
+= m_delta
;
204 print_diff_line (pretty_printer
*pp
, char prefix_char
,
205 const char *line
, int line_size
);
207 /* Implementation of class edit_context. */
209 /* edit_context's ctor. */
211 edit_context::edit_context ()
213 m_files (strcmp
, NULL
, edited_file::delete_cb
)
216 /* Add any fixits within RICHLOC to this context, recording the
217 changes that they make. */
220 edit_context::add_fixits (rich_location
*richloc
)
224 if (richloc
->seen_impossible_fixit_p ())
229 for (unsigned i
= 0; i
< richloc
->get_num_fixit_hints (); i
++)
231 const fixit_hint
*hint
= richloc
->get_fixit_hint (i
);
232 if (!apply_fixit (hint
))
237 /* Get the content of the given file, with fix-its applied.
238 If any errors occurred in this edit_context, return NULL.
239 The ptr should be freed by the caller. */
242 edit_context::get_content (const char *filename
)
246 edited_file
&file
= get_or_insert_file (filename
);
247 return file
.get_content ();
250 /* Map a location before the edits to a column number after the edits.
251 This method is for the selftests. */
254 edit_context::get_effective_column (const char *filename
, int line
,
257 edited_file
*file
= get_file (filename
);
260 return file
->get_effective_column (line
, column
);
263 /* Generate a unified diff. The resulting string should be freed by the
264 caller. Primarily for selftests.
265 If any errors occurred in this edit_context, return NULL. */
268 edit_context::generate_diff (bool show_filenames
)
274 print_diff (&pp
, show_filenames
);
275 return xstrdup (pp_formatted_text (&pp
));
278 /* Print a unified diff to PP, showing the changes made within the
282 edit_context::print_diff (pretty_printer
*pp
, bool show_filenames
)
286 diff
d (pp
, show_filenames
);
287 m_files
.foreach (edited_file::call_print_diff
, &d
);
290 /* Attempt to apply the given fixit. Return true if it can be
291 applied, or false otherwise. */
294 edit_context::apply_fixit (const fixit_hint
*hint
)
296 expanded_location start
= expand_location (hint
->get_start_loc ());
297 expanded_location next_loc
= expand_location (hint
->get_next_loc ());
298 if (start
.file
!= next_loc
.file
)
300 if (start
.line
!= next_loc
.line
)
302 if (start
.column
== 0)
304 if (next_loc
.column
== 0)
307 edited_file
&file
= get_or_insert_file (start
.file
);
310 return file
.apply_fixit (start
.line
, start
.column
, next_loc
.column
,
312 hint
->get_length ());
315 /* Locate the edited_file * for FILENAME, if any
316 Return NULL if there isn't one. */
319 edit_context::get_file (const char *filename
)
321 gcc_assert (filename
);
322 return m_files
.lookup (filename
);
325 /* Locate the edited_file for FILENAME, adding one if there isn't one. */
328 edit_context::get_or_insert_file (const char *filename
)
330 gcc_assert (filename
);
332 edited_file
*file
= get_file (filename
);
337 file
= new edited_file (filename
);
338 m_files
.insert (filename
, file
);
342 /* Implementation of class edited_file. */
344 /* Callback for m_edited_lines, for comparing line numbers. */
346 static int line_comparator (int a
, int b
)
351 /* edited_file's constructor. */
353 edited_file::edited_file (const char *filename
)
354 : m_filename (filename
),
355 m_edited_lines (line_comparator
, NULL
, edited_line::delete_cb
),
360 /* A callback for deleting edited_file *, for use as a
361 delete_value_fn for edit_context::m_files. */
364 edited_file::delete_cb (edited_file
*file
)
369 /* Get the content of the file, with fix-its applied.
370 The ptr should be freed by the caller. */
373 edited_file::get_content ()
376 if (!print_content (&pp
))
378 return xstrdup (pp_formatted_text (&pp
));
381 /* Attempt to replace columns START_COLUMN up to but not including NEXT_COLUMN
382 of LINE with the string REPLACEMENT_STR of length REPLACEMENT_LEN,
383 updating the in-memory copy of the line, and the record of edits to
387 edited_file::apply_fixit (int line
, int start_column
, int next_column
,
388 const char *replacement_str
,
391 edited_line
*el
= get_or_insert_line (line
);
394 return el
->apply_fixit (start_column
, next_column
, replacement_str
,
398 /* Given line LINE, map from COLUMN in the input file to its current
399 column after edits have been applied. */
402 edited_file::get_effective_column (int line
, int column
)
404 const edited_line
*el
= get_line (line
);
407 return el
->get_effective_column (column
);
410 /* Attempt to print the content of the file to PP, with edits applied.
411 Return true if successful, false otherwise. */
414 edited_file::print_content (pretty_printer
*pp
)
416 bool missing_trailing_newline
;
417 int line_count
= get_num_lines (&missing_trailing_newline
);
418 for (int line_num
= 1; line_num
<= line_count
; line_num
++)
420 edited_line
*el
= get_line (line_num
);
422 el
->print_content (pp
);
425 char_span line
= location_get_source_line (m_filename
, line_num
);
428 for (size_t i
= 0; i
< line
.length (); i
++)
429 pp_character (pp
, line
[i
]);
431 if (line_num
< line_count
)
432 pp_character (pp
, '\n');
435 if (!missing_trailing_newline
)
436 pp_character (pp
, '\n');
441 /* Print a unified diff to PP, showing any changes that have occurred
445 edited_file::print_diff (pretty_printer
*pp
, bool show_filenames
)
449 pp_string (pp
, colorize_start (pp_show_color (pp
), "diff-filename"));
450 pp_printf (pp
, "--- %s\n", m_filename
);
451 pp_printf (pp
, "+++ %s\n", m_filename
);
452 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
455 edited_line
*el
= m_edited_lines
.min ();
457 bool missing_trailing_newline
;
458 int line_count
= get_num_lines (&missing_trailing_newline
);
460 const int context_lines
= 3;
462 /* Track new line numbers minus old line numbers. */
468 int start_of_hunk
= el
->get_line_num ();
469 start_of_hunk
-= context_lines
;
470 if (start_of_hunk
< 1)
473 /* Locate end of hunk, merging in changed lines
474 that are sufficiently close. */
478 = m_edited_lines
.successor (el
->get_line_num ());
482 int end_of_printed_hunk
= el
->get_line_num () + context_lines
;
483 if (!el
->actually_edited_p ())
484 end_of_printed_hunk
--;
486 if (end_of_printed_hunk
487 >= next_el
->get_line_num () - context_lines
)
493 int end_of_hunk
= el
->get_line_num ();
494 end_of_hunk
+= context_lines
;
495 if (!el
->actually_edited_p ())
497 if (end_of_hunk
> line_count
)
498 end_of_hunk
= line_count
;
500 int new_start_of_hunk
= start_of_hunk
+ line_delta
;
501 line_delta
+= print_diff_hunk (pp
, start_of_hunk
, end_of_hunk
,
503 el
= m_edited_lines
.successor (el
->get_line_num ());
507 /* Print one hunk within a unified diff to PP, covering the
508 given range of lines. OLD_START_OF_HUNK and OLD_END_OF_HUNK are
509 line numbers in the unedited version of the file.
510 NEW_START_OF_HUNK is a line number in the edited version of the file.
511 Return the change in the line count within the hunk. */
514 edited_file::print_diff_hunk (pretty_printer
*pp
, int old_start_of_hunk
,
515 int old_end_of_hunk
, int new_start_of_hunk
)
517 int old_num_lines
= old_end_of_hunk
- old_start_of_hunk
+ 1;
519 = get_effective_line_count (old_start_of_hunk
, old_end_of_hunk
);
521 pp_string (pp
, colorize_start (pp_show_color (pp
), "diff-hunk"));
522 pp_printf (pp
, "@@ -%i,%i +%i,%i @@\n", old_start_of_hunk
, old_num_lines
,
523 new_start_of_hunk
, new_num_lines
);
524 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
526 int line_num
= old_start_of_hunk
;
527 while (line_num
<= old_end_of_hunk
)
529 edited_line
*el
= get_line (line_num
);
532 /* We have an edited line.
533 Consolidate into runs of changed lines. */
534 const int first_changed_line_in_run
= line_num
;
535 while (get_line (line_num
))
537 const int last_changed_line_in_run
= line_num
- 1;
538 print_run_of_changed_lines (pp
, first_changed_line_in_run
,
539 last_changed_line_in_run
);
543 /* Unchanged line. */
544 char_span old_line
= location_get_source_line (m_filename
, line_num
);
545 print_diff_line (pp
, ' ', old_line
.get_buffer (), old_line
.length ());
550 return new_num_lines
- old_num_lines
;
553 /* Subroutine of edited_file::print_diff_hunk: given a run of lines
554 from START_OF_RUN to END_OF_RUN that all have edited_line instances,
555 print the diff to PP. */
558 edited_file::print_run_of_changed_lines (pretty_printer
*pp
,
562 /* Show old version of lines. */
563 pp_string (pp
, colorize_start (pp_show_color (pp
),
565 for (int line_num
= start_of_run
;
566 line_num
<= end_of_run
;
569 edited_line
*el_in_run
= get_line (line_num
);
570 gcc_assert (el_in_run
);
571 if (el_in_run
->actually_edited_p ())
573 char_span old_line
= location_get_source_line (m_filename
, line_num
);
574 print_diff_line (pp
, '-', old_line
.get_buffer (),
578 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
580 /* Show new version of lines. */
581 pp_string (pp
, colorize_start (pp_show_color (pp
),
583 for (int line_num
= start_of_run
;
584 line_num
<= end_of_run
;
587 edited_line
*el_in_run
= get_line (line_num
);
588 gcc_assert (el_in_run
);
589 el_in_run
->print_diff_lines (pp
);
591 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
594 /* Print one line within a diff, starting with PREFIX_CHAR,
595 followed by the LINE of content, of length LEN. LINE is
596 not necessarily 0-terminated. Print a trailing newline. */
599 print_diff_line (pretty_printer
*pp
, char prefix_char
,
600 const char *line
, int len
)
602 pp_character (pp
, prefix_char
);
603 for (int i
= 0; i
< len
; i
++)
604 pp_character (pp
, line
[i
]);
605 pp_character (pp
, '\n');
608 /* Determine the number of lines that will be present after
609 editing for the range of lines from OLD_START_OF_HUNK to
610 OLD_END_OF_HUNK inclusive. */
613 edited_file::get_effective_line_count (int old_start_of_hunk
,
617 for (int old_line_num
= old_start_of_hunk
; old_line_num
<= old_end_of_hunk
;
620 edited_line
*el
= get_line (old_line_num
);
622 line_count
+= el
->get_effective_line_count ();
629 /* Get the state of LINE within the file, or NULL if it is untouched. */
632 edited_file::get_line (int line
)
634 return m_edited_lines
.lookup (line
);
637 /* Get the state of LINE within the file, creating a state for it
638 if necessary. Return NULL if an error occurs. */
641 edited_file::get_or_insert_line (int line
)
643 edited_line
*el
= get_line (line
);
646 el
= new edited_line (m_filename
, line
);
647 if (el
->get_content () == NULL
)
652 m_edited_lines
.insert (line
, el
);
656 /* Get the total number of lines in m_content, writing
657 true to *MISSING_TRAILING_NEWLINE if the final line
658 if missing a newline, false otherwise. */
661 edited_file::get_num_lines (bool *missing_trailing_newline
)
663 gcc_assert (missing_trailing_newline
);
664 if (m_num_lines
== -1)
670 = location_get_source_line (m_filename
, m_num_lines
+ 1);
677 *missing_trailing_newline
= location_missing_trailing_newline (m_filename
);
681 /* Implementation of class edited_line. */
683 /* edited_line's ctor. */
685 edited_line::edited_line (const char *filename
, int line_num
)
686 : m_line_num (line_num
),
687 m_content (NULL
), m_len (0), m_alloc_sz (0),
691 char_span line
= location_get_source_line (filename
, line_num
);
694 m_len
= line
.length ();
695 ensure_capacity (m_len
);
696 memcpy (m_content
, line
.get_buffer (), m_len
);
697 ensure_terminated ();
700 /* edited_line's dtor. */
702 edited_line::~edited_line ()
708 FOR_EACH_VEC_ELT (m_predecessors
, i
, pred
)
712 /* A callback for deleting edited_line *, for use as a
713 delete_value_fn for edited_file::m_edited_lines. */
716 edited_line::delete_cb (edited_line
*el
)
721 /* Map a location before the edits to a column number after the edits,
722 within a specific line. */
725 edited_line::get_effective_column (int orig_column
) const
729 FOR_EACH_VEC_ELT (m_line_events
, i
, event
)
730 orig_column
= event
->get_effective_column (orig_column
);
734 /* Attempt to replace columns START_COLUMN up to but not including
735 NEXT_COLUMN of the line with the string REPLACEMENT_STR of
736 length REPLACEMENT_LEN, updating the in-memory copy of the line,
737 and the record of edits to the line.
738 Return true if successful; false if an error occurred. */
741 edited_line::apply_fixit (int start_column
,
743 const char *replacement_str
,
746 /* Handle newlines. They will only ever be at the end of the
747 replacement text, thanks to the filtering in rich_location. */
748 if (replacement_len
> 1)
749 if (replacement_str
[replacement_len
- 1] == '\n')
751 /* Stash in m_predecessors, stripping off newline. */
752 m_predecessors
.safe_push (new added_line (replacement_str
,
753 replacement_len
- 1));
757 start_column
= get_effective_column (start_column
);
758 next_column
= get_effective_column (next_column
);
760 int start_offset
= start_column
- 1;
761 int next_offset
= next_column
- 1;
763 gcc_assert (start_offset
>= 0);
764 gcc_assert (next_offset
>= 0);
766 if (start_column
> next_column
)
768 if (start_offset
>= (m_len
+ 1))
770 if (next_offset
>= (m_len
+ 1))
773 size_t victim_len
= next_offset
- start_offset
;
775 /* Ensure buffer is big enough. */
776 size_t new_len
= m_len
+ replacement_len
- victim_len
;
777 ensure_capacity (new_len
);
779 char *suffix
= m_content
+ next_offset
;
780 gcc_assert (suffix
<= m_content
+ m_len
);
781 size_t len_suffix
= (m_content
+ m_len
) - suffix
;
783 /* Move successor content into position. They overlap, so use memmove. */
784 memmove (m_content
+ start_offset
+ replacement_len
,
787 /* Replace target content. They don't overlap, so use memcpy. */
788 memcpy (m_content
+ start_offset
,
794 ensure_terminated ();
796 /* Record the replacement, so that future changes to the line can have
797 their column information adjusted accordingly. */
798 m_line_events
.safe_push (line_event (start_column
, next_column
,
803 /* Determine the number of lines that will be present after
804 editing for this line. Typically this is just 1, but
805 if newlines have been added before this line, they will
809 edited_line::get_effective_line_count () const
811 return m_predecessors
.length () + 1;
814 /* Subroutine of edited_file::print_content.
815 Print this line and any new lines added before it, to PP. */
818 edited_line::print_content (pretty_printer
*pp
) const
822 FOR_EACH_VEC_ELT (m_predecessors
, i
, pred
)
824 pp_string (pp
, pred
->get_content ());
827 pp_string (pp
, m_content
);
830 /* Subroutine of edited_file::print_run_of_changed_lines for
831 printing diff hunks to PP.
832 Print the '+' line for this line, and any newlines added
834 Note that if this edited_line was actually edited, the '-'
835 line has already been printed. If it wasn't, then we merely
836 have a placeholder edited_line for adding newlines to, and
837 we need to print a ' ' line for the edited_line as we haven't
841 edited_line::print_diff_lines (pretty_printer
*pp
) const
845 FOR_EACH_VEC_ELT (m_predecessors
, i
, pred
)
846 print_diff_line (pp
, '+', pred
->get_content (),
848 if (actually_edited_p ())
849 print_diff_line (pp
, '+', m_content
, m_len
);
851 print_diff_line (pp
, ' ', m_content
, m_len
);
854 /* Ensure that the buffer for m_content is at least large enough to hold
855 a string of length LEN and its 0-terminator, doubling on repeated
859 edited_line::ensure_capacity (int len
)
861 /* Allow 1 extra byte for 0-termination. */
862 if (m_alloc_sz
< (len
+ 1))
864 size_t new_alloc_sz
= (len
+ 1) * 2;
865 m_content
= (char *)xrealloc (m_content
, new_alloc_sz
);
866 m_alloc_sz
= new_alloc_sz
;
870 /* Ensure that m_content is 0-terminated. */
873 edited_line::ensure_terminated ()
875 /* 0-terminate the buffer. */
876 gcc_assert (m_len
< m_alloc_sz
);
877 m_content
[m_len
] = '\0';
882 /* Selftests of code-editing. */
886 /* A wrapper class for ensuring that the underlying pointer is freed. */
888 template <typename POINTER_T
>
892 auto_free (POINTER_T p
) : m_ptr (p
) {}
893 ~auto_free () { free (m_ptr
); }
895 operator POINTER_T () { return m_ptr
; }
901 /* Verify that edit_context::get_content works for unedited files. */
906 /* Test of empty file. */
908 const char *content
= ("");
909 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
911 auto_free
<char *> result
= edit
.get_content (tmp
.get_filename ());
912 ASSERT_STREQ ("", result
);
915 /* Test of simple content. */
917 const char *content
= ("/* before */\n"
920 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
922 auto_free
<char *> result
= edit
.get_content (tmp
.get_filename ());
923 ASSERT_STREQ ("/* before */\n"
925 "/* after */\n", result
);
928 /* Test of omitting the trailing newline on the final line. */
930 const char *content
= ("/* before */\n"
933 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
935 auto_free
<char *> result
= edit
.get_content (tmp
.get_filename ());
936 /* We should respect the omitted trailing newline. */
937 ASSERT_STREQ ("/* before */\n"
939 "/* after */", result
);
943 /* Test applying an "insert" fixit, using insert_before. */
946 test_applying_fixits_insert_before (const line_table_case
&case_
)
948 /* Create a tempfile and write some text to it.
949 .........................0000000001111111.
950 .........................1234567890123456. */
951 const char *old_content
= ("/* before */\n"
954 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
955 const char *filename
= tmp
.get_filename ();
956 line_table_test
ltt (case_
);
957 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 2);
959 /* Add a comment in front of "bar.field". */
960 location_t start
= linemap_position_for_column (line_table
, 7);
961 rich_location
richloc (line_table
, start
);
962 richloc
.add_fixit_insert_before ("/* inserted */");
964 if (start
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
968 edit
.add_fixits (&richloc
);
969 auto_free
<char *> new_content
= edit
.get_content (filename
);
970 if (start
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
971 ASSERT_STREQ ("/* before */\n"
972 "foo = /* inserted */bar.field;\n"
973 "/* after */\n", new_content
);
975 /* Verify that locations on other lines aren't affected by the change. */
976 ASSERT_EQ (100, edit
.get_effective_column (filename
, 1, 100));
977 ASSERT_EQ (100, edit
.get_effective_column (filename
, 3, 100));
979 /* Verify locations on the line before the change. */
980 ASSERT_EQ (1, edit
.get_effective_column (filename
, 2, 1));
981 ASSERT_EQ (6, edit
.get_effective_column (filename
, 2, 6));
983 /* Verify locations on the line at and after the change. */
984 ASSERT_EQ (21, edit
.get_effective_column (filename
, 2, 7));
985 ASSERT_EQ (22, edit
.get_effective_column (filename
, 2, 8));
988 auto_free
<char *> diff
= edit
.generate_diff (false);
989 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
991 "-foo = bar.field;\n"
992 "+foo = /* inserted */bar.field;\n"
993 " /* after */\n", diff
);
996 /* Test applying an "insert" fixit, using insert_after, with
997 a range of length > 1 (to ensure that the end-point of
998 the input range is used). */
1001 test_applying_fixits_insert_after (const line_table_case
&case_
)
1003 /* Create a tempfile and write some text to it.
1004 .........................0000000001111111.
1005 .........................1234567890123456. */
1006 const char *old_content
= ("/* before */\n"
1007 "foo = bar.field;\n"
1009 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1010 const char *filename
= tmp
.get_filename ();
1011 line_table_test
ltt (case_
);
1012 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 2);
1014 /* Add a comment after "field". */
1015 location_t start
= linemap_position_for_column (line_table
, 11);
1016 location_t finish
= linemap_position_for_column (line_table
, 15);
1017 location_t field
= make_location (start
, start
, finish
);
1018 rich_location
richloc (line_table
, field
);
1019 richloc
.add_fixit_insert_after ("/* inserted */");
1021 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1024 /* Verify that the text was inserted after the end of "field". */
1026 edit
.add_fixits (&richloc
);
1027 auto_free
<char *> new_content
= edit
.get_content (filename
);
1028 ASSERT_STREQ ("/* before */\n"
1029 "foo = bar.field/* inserted */;\n"
1030 "/* after */\n", new_content
);
1033 auto_free
<char *> diff
= edit
.generate_diff (false);
1034 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1036 "-foo = bar.field;\n"
1037 "+foo = bar.field/* inserted */;\n"
1038 " /* after */\n", diff
);
1041 /* Test applying an "insert" fixit, using insert_after at the end of
1042 a line (contrast with test_applying_fixits_insert_after_failure
1046 test_applying_fixits_insert_after_at_line_end (const line_table_case
&case_
)
1048 /* Create a tempfile and write some text to it.
1049 .........................0000000001111111.
1050 .........................1234567890123456. */
1051 const char *old_content
= ("/* before */\n"
1052 "foo = bar.field;\n"
1054 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1055 const char *filename
= tmp
.get_filename ();
1056 line_table_test
ltt (case_
);
1057 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 2);
1059 /* Add a comment after the semicolon. */
1060 location_t loc
= linemap_position_for_column (line_table
, 16);
1061 rich_location
richloc (line_table
, loc
);
1062 richloc
.add_fixit_insert_after ("/* inserted */");
1064 if (loc
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1068 edit
.add_fixits (&richloc
);
1069 auto_free
<char *> new_content
= edit
.get_content (filename
);
1070 ASSERT_STREQ ("/* before */\n"
1071 "foo = bar.field;/* inserted */\n"
1072 "/* after */\n", new_content
);
1075 auto_free
<char *> diff
= edit
.generate_diff (false);
1076 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1078 "-foo = bar.field;\n"
1079 "+foo = bar.field;/* inserted */\n"
1080 " /* after */\n", diff
);
1083 /* Test of a failed attempt to apply an "insert" fixit, using insert_after,
1084 due to the relevant linemap ending. Contrast with
1085 test_applying_fixits_insert_after_at_line_end above. */
1088 test_applying_fixits_insert_after_failure (const line_table_case
&case_
)
1090 /* Create a tempfile and write some text to it.
1091 .........................0000000001111111.
1092 .........................1234567890123456. */
1093 const char *old_content
= ("/* before */\n"
1094 "foo = bar.field;\n"
1096 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1097 const char *filename
= tmp
.get_filename ();
1098 line_table_test
ltt (case_
);
1099 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 2);
1101 /* Add a comment after the semicolon. */
1102 location_t loc
= linemap_position_for_column (line_table
, 16);
1103 rich_location
richloc (line_table
, loc
);
1105 /* We want a failure of linemap_position_for_loc_and_offset.
1106 We can do this by starting a new linemap at line 3, so that
1107 there is no appropriate location value for the insertion point
1108 within the linemap for line 2. */
1109 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 3);
1111 /* The failure fails to happen at the transition point from
1112 packed ranges to unpacked ranges (where there are some "spare"
1113 location_t values). Skip the test there. */
1114 if (loc
>= LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES
)
1117 /* Offsetting "loc" should now fail (by returning the input loc. */
1118 ASSERT_EQ (loc
, linemap_position_for_loc_and_offset (line_table
, loc
, 1));
1120 /* Hence attempting to use add_fixit_insert_after at the end of the line
1122 richloc
.add_fixit_insert_after ("/* inserted */");
1123 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
1126 edit
.add_fixits (&richloc
);
1127 ASSERT_FALSE (edit
.valid_p ());
1128 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1129 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1132 /* Test applying an "insert" fixit that adds a newline. */
1135 test_applying_fixits_insert_containing_newline (const line_table_case
&case_
)
1137 /* Create a tempfile and write some text to it.
1138 .........................0000000001111111.
1139 .........................1234567890123456. */
1140 const char *old_content
= (" case 'a':\n" /* line 1. */
1141 " x = a;\n" /* line 2. */
1142 " case 'b':\n" /* line 3. */
1143 " x = b;\n");/* line 4. */
1145 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1146 const char *filename
= tmp
.get_filename ();
1147 line_table_test
ltt (case_
);
1148 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 3);
1150 /* Add a "break;" on a line by itself before line 3 i.e. before
1151 column 1 of line 3. */
1152 location_t case_start
= linemap_position_for_column (line_table
, 5);
1153 location_t case_finish
= linemap_position_for_column (line_table
, 13);
1154 location_t case_loc
= make_location (case_start
, case_start
, case_finish
);
1155 rich_location
richloc (line_table
, case_loc
);
1156 location_t line_start
= linemap_position_for_column (line_table
, 1);
1157 richloc
.add_fixit_insert_before (line_start
, " break;\n");
1159 if (case_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1163 edit
.add_fixits (&richloc
);
1164 auto_free
<char *> new_content
= edit
.get_content (filename
);
1165 ASSERT_STREQ ((" case 'a':\n"
1173 auto_free
<char *> diff
= edit
.generate_diff (false);
1174 ASSERT_STREQ (("@@ -1,4 +1,5 @@\n"
1183 /* Test applying a "replace" fixit that grows the affected line. */
1186 test_applying_fixits_growing_replace (const line_table_case
&case_
)
1188 /* Create a tempfile and write some text to it.
1189 .........................0000000001111111.
1190 .........................1234567890123456. */
1191 const char *old_content
= ("/* before */\n"
1192 "foo = bar.field;\n"
1194 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1195 const char *filename
= tmp
.get_filename ();
1196 line_table_test
ltt (case_
);
1197 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1199 /* Replace "field" with "m_field". */
1200 location_t start
= linemap_position_for_column (line_table
, 11);
1201 location_t finish
= linemap_position_for_column (line_table
, 15);
1202 location_t field
= make_location (start
, start
, finish
);
1203 rich_location
richloc (line_table
, field
);
1204 richloc
.add_fixit_replace ("m_field");
1207 edit
.add_fixits (&richloc
);
1208 auto_free
<char *> new_content
= edit
.get_content (filename
);
1209 if (finish
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1211 ASSERT_STREQ ("/* before */\n"
1212 "foo = bar.m_field;\n"
1213 "/* after */\n", new_content
);
1215 /* Verify location of ";" after the change. */
1216 ASSERT_EQ (18, edit
.get_effective_column (filename
, 2, 16));
1219 auto_free
<char *> diff
= edit
.generate_diff (false);
1220 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1222 "-foo = bar.field;\n"
1223 "+foo = bar.m_field;\n"
1224 " /* after */\n", diff
);
1228 /* Test applying a "replace" fixit that shrinks the affected line. */
1231 test_applying_fixits_shrinking_replace (const line_table_case
&case_
)
1233 /* Create a tempfile and write some text to it.
1234 .........................000000000111111111.
1235 .........................123456789012345678. */
1236 const char *old_content
= ("/* before */\n"
1237 "foo = bar.m_field;\n"
1239 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1240 const char *filename
= tmp
.get_filename ();
1241 line_table_test
ltt (case_
);
1242 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1244 /* Replace "field" with "m_field". */
1245 location_t start
= linemap_position_for_column (line_table
, 11);
1246 location_t finish
= linemap_position_for_column (line_table
, 17);
1247 location_t m_field
= make_location (start
, start
, finish
);
1248 rich_location
richloc (line_table
, m_field
);
1249 richloc
.add_fixit_replace ("field");
1252 edit
.add_fixits (&richloc
);
1253 auto_free
<char *> new_content
= edit
.get_content (filename
);
1254 if (finish
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1256 ASSERT_STREQ ("/* before */\n"
1257 "foo = bar.field;\n"
1258 "/* after */\n", new_content
);
1260 /* Verify location of ";" after the change. */
1261 ASSERT_EQ (16, edit
.get_effective_column (filename
, 2, 18));
1264 auto_free
<char *> diff
= edit
.generate_diff (false);
1265 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1267 "-foo = bar.m_field;\n"
1268 "+foo = bar.field;\n"
1269 " /* after */\n", diff
);
1273 /* Replacement fix-it hint containing a newline. */
1276 test_applying_fixits_replace_containing_newline (const line_table_case
&case_
)
1278 /* Create a tempfile and write some text to it.
1279 .........................0000000001111.
1280 .........................1234567890123. */
1281 const char *old_content
= "foo = bar ();\n";
1283 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1284 const char *filename
= tmp
.get_filename ();
1285 line_table_test
ltt (case_
);
1286 linemap_add (line_table
, LC_ENTER
, false, filename
, 1);
1288 /* Replace the " = " with "\n = ", as if we were reformatting an
1289 overly long line. */
1290 location_t start
= linemap_position_for_column (line_table
, 4);
1291 location_t finish
= linemap_position_for_column (line_table
, 6);
1292 location_t loc
= linemap_position_for_column (line_table
, 13);
1293 rich_location
richloc (line_table
, loc
);
1294 source_range range
= source_range::from_locations (start
, finish
);
1295 richloc
.add_fixit_replace (range
, "\n = ");
1297 /* Newlines are only supported within fix-it hints that
1298 are at the start of lines (for entirely new lines), hence
1299 this fix-it should not be displayed. */
1300 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
1302 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1306 edit
.add_fixits (&richloc
);
1307 auto_free
<char *> new_content
= edit
.get_content (filename
);
1308 //ASSERT_STREQ ("foo\n = bar ();\n", new_content);
1311 /* Test applying a "remove" fixit. */
1314 test_applying_fixits_remove (const line_table_case
&case_
)
1316 /* Create a tempfile and write some text to it.
1317 .........................000000000111111111.
1318 .........................123456789012345678. */
1319 const char *old_content
= ("/* before */\n"
1320 "foo = bar.m_field;\n"
1322 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1323 const char *filename
= tmp
.get_filename ();
1324 line_table_test
ltt (case_
);
1325 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1327 /* Remove ".m_field". */
1328 location_t start
= linemap_position_for_column (line_table
, 10);
1329 location_t finish
= linemap_position_for_column (line_table
, 17);
1330 rich_location
richloc (line_table
, start
);
1332 range
.m_start
= start
;
1333 range
.m_finish
= finish
;
1334 richloc
.add_fixit_remove (range
);
1337 edit
.add_fixits (&richloc
);
1338 auto_free
<char *> new_content
= edit
.get_content (filename
);
1339 if (finish
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1341 ASSERT_STREQ ("/* before */\n"
1343 "/* after */\n", new_content
);
1345 /* Verify location of ";" after the change. */
1346 ASSERT_EQ (10, edit
.get_effective_column (filename
, 2, 18));
1349 auto_free
<char *> diff
= edit
.generate_diff (false);
1350 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1352 "-foo = bar.m_field;\n"
1354 " /* after */\n", diff
);
1358 /* Test applying multiple fixits to one line. */
1361 test_applying_fixits_multiple (const line_table_case
&case_
)
1363 /* Create a tempfile and write some text to it.
1364 .........................00000000011111111.
1365 .........................12345678901234567. */
1366 const char *old_content
= ("/* before */\n"
1367 "foo = bar.field;\n"
1369 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1370 const char *filename
= tmp
.get_filename ();
1371 line_table_test
ltt (case_
);
1372 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1374 location_t c7
= linemap_position_for_column (line_table
, 7);
1375 location_t c9
= linemap_position_for_column (line_table
, 9);
1376 location_t c11
= linemap_position_for_column (line_table
, 11);
1377 location_t c15
= linemap_position_for_column (line_table
, 15);
1378 location_t c17
= linemap_position_for_column (line_table
, 17);
1380 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1383 /* Add a comment in front of "bar.field". */
1384 rich_location
insert_a (line_table
, c7
);
1385 insert_a
.add_fixit_insert_before (c7
, "/* alpha */");
1387 /* Add a comment after "bar.field;". */
1388 rich_location
insert_b (line_table
, c17
);
1389 insert_b
.add_fixit_insert_before (c17
, "/* beta */");
1391 /* Replace "bar" with "pub". */
1392 rich_location
replace_a (line_table
, c7
);
1393 replace_a
.add_fixit_replace (source_range::from_locations (c7
, c9
),
1396 /* Replace "field" with "meadow". */
1397 rich_location
replace_b (line_table
, c7
);
1398 replace_b
.add_fixit_replace (source_range::from_locations (c11
, c15
),
1402 edit
.add_fixits (&insert_a
);
1403 ASSERT_EQ (100, edit
.get_effective_column (filename
, 1, 100));
1404 ASSERT_EQ (1, edit
.get_effective_column (filename
, 2, 1));
1405 ASSERT_EQ (6, edit
.get_effective_column (filename
, 2, 6));
1406 ASSERT_EQ (18, edit
.get_effective_column (filename
, 2, 7));
1407 ASSERT_EQ (27, edit
.get_effective_column (filename
, 2, 16));
1408 ASSERT_EQ (100, edit
.get_effective_column (filename
, 3, 100));
1410 edit
.add_fixits (&insert_b
);
1411 edit
.add_fixits (&replace_a
);
1412 edit
.add_fixits (&replace_b
);
1414 if (c17
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1416 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1417 ASSERT_STREQ ("/* before */\n"
1418 "foo = /* alpha */pub.meadow;/* beta */\n"
1423 auto_free
<char *> diff
= edit
.generate_diff (false);
1424 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1426 "-foo = bar.field;\n"
1427 "+foo = /* alpha */pub.meadow;/* beta */\n"
1428 " /* after */\n", diff
);
1432 /* Subroutine of test_applying_fixits_multiple_lines.
1433 Add the text "CHANGED: " to the front of the given line. */
1436 change_line (edit_context
&edit
, int line_num
)
1438 const line_map_ordinary
*ord_map
1439 = LINEMAPS_LAST_ORDINARY_MAP (line_table
);
1440 const int column
= 1;
1442 linemap_position_for_line_and_column (line_table
, ord_map
,
1445 expanded_location exploc
= expand_location (loc
);
1446 if (loc
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1448 ASSERT_EQ (line_num
, exploc
.line
);
1449 ASSERT_EQ (column
, exploc
.column
);
1452 rich_location
insert (line_table
, loc
);
1453 insert
.add_fixit_insert_before ("CHANGED: ");
1454 edit
.add_fixits (&insert
);
1458 /* Subroutine of test_applying_fixits_multiple_lines.
1459 Add the text "INSERTED\n" in front of the given line. */
1462 insert_line (edit_context
&edit
, int line_num
)
1464 const line_map_ordinary
*ord_map
1465 = LINEMAPS_LAST_ORDINARY_MAP (line_table
);
1466 const int column
= 1;
1468 linemap_position_for_line_and_column (line_table
, ord_map
,
1471 expanded_location exploc
= expand_location (loc
);
1472 if (loc
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1474 ASSERT_EQ (line_num
, exploc
.line
);
1475 ASSERT_EQ (column
, exploc
.column
);
1478 rich_location
insert (line_table
, loc
);
1479 insert
.add_fixit_insert_before ("INSERTED\n");
1480 edit
.add_fixits (&insert
);
1484 /* Test of editing multiple lines within a long file,
1485 to ensure that diffs are generated as expected. */
1488 test_applying_fixits_multiple_lines (const line_table_case
&case_
)
1490 /* Create a tempfile and write many lines of text to it. */
1491 named_temp_file
tmp (".txt");
1492 const char *filename
= tmp
.get_filename ();
1493 FILE *f
= fopen (filename
, "w");
1494 ASSERT_NE (f
, NULL
);
1495 for (int i
= 1; i
<= 1000; i
++)
1496 fprintf (f
, "line %i\n", i
);
1499 line_table_test
ltt (case_
);
1500 linemap_add (line_table
, LC_ENTER
, false, filename
, 1);
1501 linemap_position_for_column (line_table
, 127);
1505 /* A run of consecutive lines. */
1506 change_line (edit
, 2);
1507 change_line (edit
, 3);
1508 change_line (edit
, 4);
1509 insert_line (edit
, 5);
1511 /* A run of nearby lines, within the contextual limit. */
1512 change_line (edit
, 150);
1513 change_line (edit
, 151);
1514 location_t last_loc
= change_line (edit
, 153);
1516 if (last_loc
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1520 auto_free
<char *> diff
= edit
.generate_diff (false);
1521 ASSERT_STREQ ("@@ -1,7 +1,8 @@\n"
1526 "+CHANGED: line 2\n"
1527 "+CHANGED: line 3\n"
1528 "+CHANGED: line 4\n"
1533 "@@ -147,10 +148,10 @@\n"
1539 "+CHANGED: line 150\n"
1540 "+CHANGED: line 151\n"
1543 "+CHANGED: line 153\n"
1546 " line 156\n", diff
);
1548 /* Ensure tmp stays alive until this point, so that the tempfile
1549 persists until after the generate_diff call. */
1550 tmp
.get_filename ();
1553 /* Test of converting an initializer for a named field from
1554 the old GCC extension to C99 syntax.
1555 Exercises a shrinking replacement followed by a growing
1556 replacement on the same line. */
1559 test_applying_fixits_modernize_named_init (const line_table_case
&case_
)
1561 /* Create a tempfile and write some text to it.
1562 .........................00000000011111111.
1563 .........................12345678901234567. */
1564 const char *old_content
= ("/* before */\n"
1567 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1568 const char *filename
= tmp
.get_filename ();
1569 line_table_test
ltt (case_
);
1570 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1572 location_t c1
= linemap_position_for_column (line_table
, 1);
1573 location_t c3
= linemap_position_for_column (line_table
, 3);
1574 location_t c8
= linemap_position_for_column (line_table
, 8);
1576 if (c8
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1579 /* Replace "bar" with ".". */
1580 rich_location
r1 (line_table
, c8
);
1581 r1
.add_fixit_replace (source_range::from_locations (c1
, c3
),
1584 /* Replace ":" with "bar =". */
1585 rich_location
r2 (line_table
, c8
);
1586 r2
.add_fixit_replace (source_range::from_locations (c8
, c8
),
1589 /* The order should not matter. Do r1 then r2. */
1592 edit
.add_fixits (&r1
);
1594 /* Verify state after first replacement. */
1596 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1597 /* We should now have:
1598 ............00000000011.
1599 ............12345678901. */
1600 ASSERT_STREQ ("/* before */\n"
1604 /* Location of the "1". */
1605 ASSERT_EQ (6, edit
.get_effective_column (filename
, 2, 8));
1606 /* Location of the ",". */
1607 ASSERT_EQ (9, edit
.get_effective_column (filename
, 2, 11));
1610 edit
.add_fixits (&r2
);
1612 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1613 /* Verify state after second replacement.
1614 ............00000000011111111.
1615 ............12345678901234567. */
1616 ASSERT_STREQ ("/* before */\n"
1622 /* Try again, doing r2 then r1; the new_content should be the same. */
1625 edit
.add_fixits (&r2
);
1626 edit
.add_fixits (&r1
);
1627 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1628 /*.............00000000011111111.
1629 .............12345678901234567. */
1630 ASSERT_STREQ ("/* before */\n"
1637 /* Test of a fixit affecting a file that can't be read. */
1640 test_applying_fixits_unreadable_file ()
1642 const char *filename
= "this-does-not-exist.txt";
1643 line_table_test ltt
;
1644 linemap_add (line_table
, LC_ENTER
, false, filename
, 1);
1646 location_t loc
= linemap_position_for_column (line_table
, 1);
1648 rich_location
insert (line_table
, loc
);
1649 insert
.add_fixit_insert_before ("change 1");
1650 insert
.add_fixit_insert_before ("change 2");
1653 /* Attempting to add the fixits affecting the unreadable file
1654 should transition the edit from valid to invalid. */
1655 ASSERT_TRUE (edit
.valid_p ());
1656 edit
.add_fixits (&insert
);
1657 ASSERT_FALSE (edit
.valid_p ());
1658 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1659 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1662 /* Verify that we gracefully handle an attempt to edit a line
1663 that's beyond the end of the file. */
1666 test_applying_fixits_line_out_of_range ()
1668 /* Create a tempfile and write some text to it.
1669 ........................00000000011111111.
1670 ........................12345678901234567. */
1671 const char *old_content
= "One-liner file\n";
1672 temp_source_file
tmp (SELFTEST_LOCATION
, ".txt", old_content
);
1673 const char *filename
= tmp
.get_filename ();
1674 line_table_test ltt
;
1675 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1677 /* Try to insert a string in line 2. */
1678 location_t loc
= linemap_position_for_column (line_table
, 1);
1680 rich_location
insert (line_table
, loc
);
1681 insert
.add_fixit_insert_before ("change");
1683 /* Verify that attempting the insertion puts an edit_context
1684 into an invalid state. */
1686 ASSERT_TRUE (edit
.valid_p ());
1687 edit
.add_fixits (&insert
);
1688 ASSERT_FALSE (edit
.valid_p ());
1689 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1690 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1693 /* Verify the boundary conditions of column values in fix-it
1694 hints applied to edit_context instances. */
1697 test_applying_fixits_column_validation (const line_table_case
&case_
)
1699 /* Create a tempfile and write some text to it.
1700 ........................00000000011111111.
1701 ........................12345678901234567. */
1702 const char *old_content
= "One-liner file\n";
1703 temp_source_file
tmp (SELFTEST_LOCATION
, ".txt", old_content
);
1704 const char *filename
= tmp
.get_filename ();
1705 line_table_test
ltt (case_
);
1706 linemap_add (line_table
, LC_ENTER
, false, filename
, 1);
1708 location_t c11
= linemap_position_for_column (line_table
, 11);
1709 location_t c14
= linemap_position_for_column (line_table
, 14);
1710 location_t c15
= linemap_position_for_column (line_table
, 15);
1711 location_t c16
= linemap_position_for_column (line_table
, 16);
1713 /* Verify limits of valid columns in insertion fixits. */
1715 /* Verify inserting at the end of the line. */
1717 rich_location
richloc (line_table
, c11
);
1718 richloc
.add_fixit_insert_before (c15
, " change");
1720 /* Col 15 is at the end of the line, so the insertion
1723 edit
.add_fixits (&richloc
);
1724 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1725 if (c15
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1726 ASSERT_STREQ ("One-liner file change\n", new_content
);
1728 ASSERT_EQ (NULL
, new_content
);
1731 /* Verify inserting beyond the end of the line. */
1733 rich_location
richloc (line_table
, c11
);
1734 richloc
.add_fixit_insert_before (c16
, " change");
1736 /* Col 16 is beyond the end of the line, so the insertion
1737 should fail gracefully. */
1739 ASSERT_TRUE (edit
.valid_p ());
1740 edit
.add_fixits (&richloc
);
1741 ASSERT_FALSE (edit
.valid_p ());
1742 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1743 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1746 /* Verify limits of valid columns in replacement fixits. */
1748 /* Verify replacing the end of the line. */
1750 rich_location
richloc (line_table
, c11
);
1751 source_range range
= source_range::from_locations (c11
, c14
);
1752 richloc
.add_fixit_replace (range
, "change");
1754 /* Col 14 is at the end of the line, so the replacement
1757 edit
.add_fixits (&richloc
);
1758 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1759 if (c14
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1760 ASSERT_STREQ ("One-liner change\n", new_content
);
1762 ASSERT_EQ (NULL
, new_content
);
1765 /* Verify going beyond the end of the line. */
1767 rich_location
richloc (line_table
, c11
);
1768 source_range range
= source_range::from_locations (c11
, c15
);
1769 richloc
.add_fixit_replace (range
, "change");
1771 /* Col 15 is after the end of the line, so the replacement
1772 should fail; verify that the attempt fails gracefully. */
1774 ASSERT_TRUE (edit
.valid_p ());
1775 edit
.add_fixits (&richloc
);
1776 ASSERT_FALSE (edit
.valid_p ());
1777 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1778 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1782 /* Run all of the selftests within this file. */
1785 edit_context_c_tests ()
1787 test_get_content ();
1788 for_each_line_table_case (test_applying_fixits_insert_before
);
1789 for_each_line_table_case (test_applying_fixits_insert_after
);
1790 for_each_line_table_case (test_applying_fixits_insert_after_at_line_end
);
1791 for_each_line_table_case (test_applying_fixits_insert_after_failure
);
1792 for_each_line_table_case (test_applying_fixits_insert_containing_newline
);
1793 for_each_line_table_case (test_applying_fixits_growing_replace
);
1794 for_each_line_table_case (test_applying_fixits_shrinking_replace
);
1795 for_each_line_table_case (test_applying_fixits_replace_containing_newline
);
1796 for_each_line_table_case (test_applying_fixits_remove
);
1797 for_each_line_table_case (test_applying_fixits_multiple
);
1798 for_each_line_table_case (test_applying_fixits_multiple_lines
);
1799 for_each_line_table_case (test_applying_fixits_modernize_named_init
);
1800 test_applying_fixits_unreadable_file ();
1801 test_applying_fixits_line_out_of_range ();
1802 for_each_line_table_case (test_applying_fixits_column_validation
);
1805 } // namespace selftest
1807 #endif /* CHECKING_P */