1 /* Determining the results of applying fix-it hints.
2 Copyright (C) 2016-2017 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_next (next
), 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
;
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
);
427 = location_get_source_line (m_filename
, line_num
, &len
);
430 for (int i
= 0; i
< len
; i
++)
431 pp_character (pp
, line
[i
]);
433 if (line_num
< line_count
)
434 pp_character (pp
, '\n');
437 if (!missing_trailing_newline
)
438 pp_character (pp
, '\n');
443 /* Print a unified diff to PP, showing any changes that have occurred
447 edited_file::print_diff (pretty_printer
*pp
, bool show_filenames
)
451 pp_string (pp
, colorize_start (pp_show_color (pp
), "diff-filename"));
452 pp_printf (pp
, "--- %s\n", m_filename
);
453 pp_printf (pp
, "+++ %s\n", m_filename
);
454 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
457 edited_line
*el
= m_edited_lines
.min ();
459 bool missing_trailing_newline
;
460 int line_count
= get_num_lines (&missing_trailing_newline
);
462 const int context_lines
= 3;
464 /* Track new line numbers minus old line numbers. */
470 int start_of_hunk
= el
->get_line_num ();
471 start_of_hunk
-= context_lines
;
472 if (start_of_hunk
< 1)
475 /* Locate end of hunk, merging in changed lines
476 that are sufficiently close. */
480 = m_edited_lines
.successor (el
->get_line_num ());
484 int end_of_printed_hunk
= el
->get_line_num () + context_lines
;
485 if (!el
->actually_edited_p ())
486 end_of_printed_hunk
--;
488 if (end_of_printed_hunk
489 >= next_el
->get_line_num () - context_lines
)
495 int end_of_hunk
= el
->get_line_num ();
496 end_of_hunk
+= context_lines
;
497 if (!el
->actually_edited_p ())
499 if (end_of_hunk
> line_count
)
500 end_of_hunk
= line_count
;
502 int new_start_of_hunk
= start_of_hunk
+ line_delta
;
503 line_delta
+= print_diff_hunk (pp
, start_of_hunk
, end_of_hunk
,
505 el
= m_edited_lines
.successor (el
->get_line_num ());
509 /* Print one hunk within a unified diff to PP, covering the
510 given range of lines. OLD_START_OF_HUNK and OLD_END_OF_HUNK are
511 line numbers in the unedited version of the file.
512 NEW_START_OF_HUNK is a line number in the edited version of the file.
513 Return the change in the line count within the hunk. */
516 edited_file::print_diff_hunk (pretty_printer
*pp
, int old_start_of_hunk
,
517 int old_end_of_hunk
, int new_start_of_hunk
)
519 int old_num_lines
= old_end_of_hunk
- old_start_of_hunk
+ 1;
521 = get_effective_line_count (old_start_of_hunk
, old_end_of_hunk
);
523 pp_string (pp
, colorize_start (pp_show_color (pp
), "diff-hunk"));
524 pp_printf (pp
, "@@ -%i,%i +%i,%i @@\n", old_start_of_hunk
, old_num_lines
,
525 new_start_of_hunk
, new_num_lines
);
526 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
528 int line_num
= old_start_of_hunk
;
529 while (line_num
<= old_end_of_hunk
)
531 edited_line
*el
= get_line (line_num
);
534 /* We have an edited line.
535 Consolidate into runs of changed lines. */
536 const int first_changed_line_in_run
= line_num
;
537 while (get_line (line_num
))
539 const int last_changed_line_in_run
= line_num
- 1;
540 print_run_of_changed_lines (pp
, first_changed_line_in_run
,
541 last_changed_line_in_run
);
545 /* Unchanged line. */
548 = location_get_source_line (m_filename
, line_num
, &line_len
);
549 print_diff_line (pp
, ' ', old_line
, line_len
);
554 return new_num_lines
- old_num_lines
;
557 /* Subroutine of edited_file::print_diff_hunk: given a run of lines
558 from START_OF_RUN to END_OF_RUN that all have edited_line instances,
559 print the diff to PP. */
562 edited_file::print_run_of_changed_lines (pretty_printer
*pp
,
566 /* Show old version of lines. */
567 pp_string (pp
, colorize_start (pp_show_color (pp
),
569 for (int line_num
= start_of_run
;
570 line_num
<= end_of_run
;
573 edited_line
*el_in_run
= get_line (line_num
);
574 gcc_assert (el_in_run
);
575 if (el_in_run
->actually_edited_p ())
579 = location_get_source_line (m_filename
, line_num
, &line_len
);
580 print_diff_line (pp
, '-', old_line
, line_len
);
583 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
585 /* Show new version of lines. */
586 pp_string (pp
, colorize_start (pp_show_color (pp
),
588 for (int line_num
= start_of_run
;
589 line_num
<= end_of_run
;
592 edited_line
*el_in_run
= get_line (line_num
);
593 gcc_assert (el_in_run
);
594 el_in_run
->print_diff_lines (pp
);
596 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
599 /* Print one line within a diff, starting with PREFIX_CHAR,
600 followed by the LINE of content, of length LEN. LINE is
601 not necessarily 0-terminated. Print a trailing newline. */
604 print_diff_line (pretty_printer
*pp
, char prefix_char
,
605 const char *line
, int len
)
607 pp_character (pp
, prefix_char
);
608 for (int i
= 0; i
< len
; i
++)
609 pp_character (pp
, line
[i
]);
610 pp_character (pp
, '\n');
613 /* Determine the number of lines that will be present after
614 editing for the range of lines from OLD_START_OF_HUNK to
615 OLD_END_OF_HUNK inclusive. */
618 edited_file::get_effective_line_count (int old_start_of_hunk
,
622 for (int old_line_num
= old_start_of_hunk
; old_line_num
<= old_end_of_hunk
;
625 edited_line
*el
= get_line (old_line_num
);
627 line_count
+= el
->get_effective_line_count ();
634 /* Get the state of LINE within the file, or NULL if it is untouched. */
637 edited_file::get_line (int line
)
639 return m_edited_lines
.lookup (line
);
642 /* Get the state of LINE within the file, creating a state for it
643 if necessary. Return NULL if an error occurs. */
646 edited_file::get_or_insert_line (int line
)
648 edited_line
*el
= get_line (line
);
651 el
= new edited_line (m_filename
, line
);
652 if (el
->get_content () == NULL
)
657 m_edited_lines
.insert (line
, el
);
661 /* Get the total number of lines in m_content, writing
662 true to *MISSING_TRAILING_NEWLINE if the final line
663 if missing a newline, false otherwise. */
666 edited_file::get_num_lines (bool *missing_trailing_newline
)
668 gcc_assert (missing_trailing_newline
);
669 if (m_num_lines
== -1)
676 = location_get_source_line (m_filename
, m_num_lines
+ 1,
684 *missing_trailing_newline
= location_missing_trailing_newline (m_filename
);
688 /* Implementation of class edited_line. */
690 /* edited_line's ctor. */
692 edited_line::edited_line (const char *filename
, int line_num
)
693 : m_line_num (line_num
),
694 m_content (NULL
), m_len (0), m_alloc_sz (0),
698 const char *line
= location_get_source_line (filename
, line_num
,
702 ensure_capacity (m_len
);
703 memcpy (m_content
, line
, m_len
);
704 ensure_terminated ();
707 /* edited_line's dtor. */
709 edited_line::~edited_line ()
715 FOR_EACH_VEC_ELT (m_predecessors
, i
, pred
)
719 /* A callback for deleting edited_line *, for use as a
720 delete_value_fn for edited_file::m_edited_lines. */
723 edited_line::delete_cb (edited_line
*el
)
728 /* Map a location before the edits to a column number after the edits,
729 within a specific line. */
732 edited_line::get_effective_column (int orig_column
) const
736 FOR_EACH_VEC_ELT (m_line_events
, i
, event
)
737 orig_column
= event
->get_effective_column (orig_column
);
741 /* Attempt to replace columns START_COLUMN up to but not including
742 NEXT_COLUMN of the line with the string REPLACEMENT_STR of
743 length REPLACEMENT_LEN, updating the in-memory copy of the line,
744 and the record of edits to the line.
745 Return true if successful; false if an error occurred. */
748 edited_line::apply_fixit (int start_column
,
750 const char *replacement_str
,
753 /* Handle newlines. They will only ever be at the end of the
754 replacement text, thanks to the filtering in rich_location. */
755 if (replacement_len
> 1)
756 if (replacement_str
[replacement_len
- 1] == '\n')
758 /* Stash in m_predecessors, stripping off newline. */
759 m_predecessors
.safe_push (new added_line (replacement_str
,
760 replacement_len
- 1));
764 start_column
= get_effective_column (start_column
);
765 next_column
= get_effective_column (next_column
);
767 int start_offset
= start_column
- 1;
768 int next_offset
= next_column
- 1;
770 gcc_assert (start_offset
>= 0);
771 gcc_assert (next_offset
>= 0);
773 if (start_column
> next_column
)
775 if (start_offset
>= (m_len
+ 1))
777 if (next_offset
>= (m_len
+ 1))
780 size_t victim_len
= next_offset
- start_offset
;
782 /* Ensure buffer is big enough. */
783 size_t new_len
= m_len
+ replacement_len
- victim_len
;
784 ensure_capacity (new_len
);
786 char *suffix
= m_content
+ next_offset
;
787 gcc_assert (suffix
<= m_content
+ m_len
);
788 size_t len_suffix
= (m_content
+ m_len
) - suffix
;
790 /* Move successor content into position. They overlap, so use memmove. */
791 memmove (m_content
+ start_offset
+ replacement_len
,
794 /* Replace target content. They don't overlap, so use memcpy. */
795 memcpy (m_content
+ start_offset
,
801 ensure_terminated ();
803 /* Record the replacement, so that future changes to the line can have
804 their column information adjusted accordingly. */
805 m_line_events
.safe_push (line_event (start_column
, next_column
,
810 /* Determine the number of lines that will be present after
811 editing for this line. Typically this is just 1, but
812 if newlines have been added before this line, they will
816 edited_line::get_effective_line_count () const
818 return m_predecessors
.length () + 1;
821 /* Subroutine of edited_file::print_content.
822 Print this line and any new lines added before it, to PP. */
825 edited_line::print_content (pretty_printer
*pp
) const
829 FOR_EACH_VEC_ELT (m_predecessors
, i
, pred
)
831 pp_string (pp
, pred
->get_content ());
834 pp_string (pp
, m_content
);
837 /* Subroutine of edited_file::print_run_of_changed_lines for
838 printing diff hunks to PP.
839 Print the '+' line for this line, and any newlines added
841 Note that if this edited_line was actually edited, the '-'
842 line has already been printed. If it wasn't, then we merely
843 have a placeholder edited_line for adding newlines to, and
844 we need to print a ' ' line for the edited_line as we haven't
848 edited_line::print_diff_lines (pretty_printer
*pp
) const
852 FOR_EACH_VEC_ELT (m_predecessors
, i
, pred
)
853 print_diff_line (pp
, '+', pred
->get_content (),
855 if (actually_edited_p ())
856 print_diff_line (pp
, '+', m_content
, m_len
);
858 print_diff_line (pp
, ' ', m_content
, m_len
);
861 /* Ensure that the buffer for m_content is at least large enough to hold
862 a string of length LEN and its 0-terminator, doubling on repeated
866 edited_line::ensure_capacity (int len
)
868 /* Allow 1 extra byte for 0-termination. */
869 if (m_alloc_sz
< (len
+ 1))
871 size_t new_alloc_sz
= (len
+ 1) * 2;
872 m_content
= (char *)xrealloc (m_content
, new_alloc_sz
);
873 m_alloc_sz
= new_alloc_sz
;
877 /* Ensure that m_content is 0-terminated. */
880 edited_line::ensure_terminated ()
882 /* 0-terminate the buffer. */
883 gcc_assert (m_len
< m_alloc_sz
);
884 m_content
[m_len
] = '\0';
889 /* Selftests of code-editing. */
893 /* A wrapper class for ensuring that the underlying pointer is freed. */
895 template <typename POINTER_T
>
899 auto_free (POINTER_T p
) : m_ptr (p
) {}
900 ~auto_free () { free (m_ptr
); }
902 operator POINTER_T () { return m_ptr
; }
908 /* Verify that edit_context::get_content works for unedited files. */
913 /* Test of empty file. */
915 const char *content
= ("");
916 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
918 auto_free
<char *> result
= edit
.get_content (tmp
.get_filename ());
919 ASSERT_STREQ ("", result
);
922 /* Test of simple content. */
924 const char *content
= ("/* before */\n"
927 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
929 auto_free
<char *> result
= edit
.get_content (tmp
.get_filename ());
930 ASSERT_STREQ ("/* before */\n"
932 "/* after */\n", result
);
935 /* Test of omitting the trailing newline on the final line. */
937 const char *content
= ("/* before */\n"
940 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
942 auto_free
<char *> result
= edit
.get_content (tmp
.get_filename ());
943 /* We should respect the omitted trailing newline. */
944 ASSERT_STREQ ("/* before */\n"
946 "/* after */", result
);
950 /* Test applying an "insert" fixit, using insert_before. */
953 test_applying_fixits_insert_before (const line_table_case
&case_
)
955 /* Create a tempfile and write some text to it.
956 .........................0000000001111111.
957 .........................1234567890123456. */
958 const char *old_content
= ("/* before */\n"
961 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
962 const char *filename
= tmp
.get_filename ();
963 line_table_test
ltt (case_
);
964 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 2);
966 /* Add a comment in front of "bar.field". */
967 location_t start
= linemap_position_for_column (line_table
, 7);
968 rich_location
richloc (line_table
, start
);
969 richloc
.add_fixit_insert_before ("/* inserted */");
971 if (start
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
975 edit
.add_fixits (&richloc
);
976 auto_free
<char *> new_content
= edit
.get_content (filename
);
977 if (start
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
978 ASSERT_STREQ ("/* before */\n"
979 "foo = /* inserted */bar.field;\n"
980 "/* after */\n", new_content
);
982 /* Verify that locations on other lines aren't affected by the change. */
983 ASSERT_EQ (100, edit
.get_effective_column (filename
, 1, 100));
984 ASSERT_EQ (100, edit
.get_effective_column (filename
, 3, 100));
986 /* Verify locations on the line before the change. */
987 ASSERT_EQ (1, edit
.get_effective_column (filename
, 2, 1));
988 ASSERT_EQ (6, edit
.get_effective_column (filename
, 2, 6));
990 /* Verify locations on the line at and after the change. */
991 ASSERT_EQ (21, edit
.get_effective_column (filename
, 2, 7));
992 ASSERT_EQ (22, edit
.get_effective_column (filename
, 2, 8));
995 auto_free
<char *> diff
= edit
.generate_diff (false);
996 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
998 "-foo = bar.field;\n"
999 "+foo = /* inserted */bar.field;\n"
1000 " /* after */\n", diff
);
1003 /* Test applying an "insert" fixit, using insert_after, with
1004 a range of length > 1 (to ensure that the end-point of
1005 the input range is used). */
1008 test_applying_fixits_insert_after (const line_table_case
&case_
)
1010 /* Create a tempfile and write some text to it.
1011 .........................0000000001111111.
1012 .........................1234567890123456. */
1013 const char *old_content
= ("/* before */\n"
1014 "foo = bar.field;\n"
1016 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1017 const char *filename
= tmp
.get_filename ();
1018 line_table_test
ltt (case_
);
1019 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 2);
1021 /* Add a comment after "field". */
1022 location_t start
= linemap_position_for_column (line_table
, 11);
1023 location_t finish
= linemap_position_for_column (line_table
, 15);
1024 location_t field
= make_location (start
, start
, finish
);
1025 rich_location
richloc (line_table
, field
);
1026 richloc
.add_fixit_insert_after ("/* inserted */");
1028 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1031 /* Verify that the text was inserted after the end of "field". */
1033 edit
.add_fixits (&richloc
);
1034 auto_free
<char *> new_content
= edit
.get_content (filename
);
1035 ASSERT_STREQ ("/* before */\n"
1036 "foo = bar.field/* inserted */;\n"
1037 "/* after */\n", new_content
);
1040 auto_free
<char *> diff
= edit
.generate_diff (false);
1041 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1043 "-foo = bar.field;\n"
1044 "+foo = bar.field/* inserted */;\n"
1045 " /* after */\n", diff
);
1048 /* Test applying an "insert" fixit, using insert_after at the end of
1049 a line (contrast with test_applying_fixits_insert_after_failure
1053 test_applying_fixits_insert_after_at_line_end (const line_table_case
&case_
)
1055 /* Create a tempfile and write some text to it.
1056 .........................0000000001111111.
1057 .........................1234567890123456. */
1058 const char *old_content
= ("/* before */\n"
1059 "foo = bar.field;\n"
1061 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1062 const char *filename
= tmp
.get_filename ();
1063 line_table_test
ltt (case_
);
1064 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 2);
1066 /* Add a comment after the semicolon. */
1067 location_t loc
= linemap_position_for_column (line_table
, 16);
1068 rich_location
richloc (line_table
, loc
);
1069 richloc
.add_fixit_insert_after ("/* inserted */");
1071 if (loc
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1075 edit
.add_fixits (&richloc
);
1076 auto_free
<char *> new_content
= edit
.get_content (filename
);
1077 ASSERT_STREQ ("/* before */\n"
1078 "foo = bar.field;/* inserted */\n"
1079 "/* after */\n", new_content
);
1082 auto_free
<char *> diff
= edit
.generate_diff (false);
1083 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1085 "-foo = bar.field;\n"
1086 "+foo = bar.field;/* inserted */\n"
1087 " /* after */\n", diff
);
1090 /* Test of a failed attempt to apply an "insert" fixit, using insert_after,
1091 due to the relevant linemap ending. Contrast with
1092 test_applying_fixits_insert_after_at_line_end above. */
1095 test_applying_fixits_insert_after_failure (const line_table_case
&case_
)
1097 /* Create a tempfile and write some text to it.
1098 .........................0000000001111111.
1099 .........................1234567890123456. */
1100 const char *old_content
= ("/* before */\n"
1101 "foo = bar.field;\n"
1103 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1104 const char *filename
= tmp
.get_filename ();
1105 line_table_test
ltt (case_
);
1106 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 2);
1108 /* Add a comment after the semicolon. */
1109 location_t loc
= linemap_position_for_column (line_table
, 16);
1110 rich_location
richloc (line_table
, loc
);
1112 /* We want a failure of linemap_position_for_loc_and_offset.
1113 We can do this by starting a new linemap at line 3, so that
1114 there is no appropriate location value for the insertion point
1115 within the linemap for line 2. */
1116 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 3);
1118 /* The failure fails to happen at the transition point from
1119 packed ranges to unpacked ranges (where there are some "spare"
1120 location_t values). Skip the test there. */
1121 if (loc
>= LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES
)
1124 /* Offsetting "loc" should now fail (by returning the input loc. */
1125 ASSERT_EQ (loc
, linemap_position_for_loc_and_offset (line_table
, loc
, 1));
1127 /* Hence attempting to use add_fixit_insert_after at the end of the line
1129 richloc
.add_fixit_insert_after ("/* inserted */");
1130 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
1133 edit
.add_fixits (&richloc
);
1134 ASSERT_FALSE (edit
.valid_p ());
1135 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1136 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1139 /* Test applying an "insert" fixit that adds a newline. */
1142 test_applying_fixits_insert_containing_newline (const line_table_case
&case_
)
1144 /* Create a tempfile and write some text to it.
1145 .........................0000000001111111.
1146 .........................1234567890123456. */
1147 const char *old_content
= (" case 'a':\n" /* line 1. */
1148 " x = a;\n" /* line 2. */
1149 " case 'b':\n" /* line 3. */
1150 " x = b;\n");/* line 4. */
1152 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1153 const char *filename
= tmp
.get_filename ();
1154 line_table_test
ltt (case_
);
1155 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 3);
1157 /* Add a "break;" on a line by itself before line 3 i.e. before
1158 column 1 of line 3. */
1159 location_t case_start
= linemap_position_for_column (line_table
, 5);
1160 location_t case_finish
= linemap_position_for_column (line_table
, 13);
1161 location_t case_loc
= make_location (case_start
, case_start
, case_finish
);
1162 rich_location
richloc (line_table
, case_loc
);
1163 location_t line_start
= linemap_position_for_column (line_table
, 1);
1164 richloc
.add_fixit_insert_before (line_start
, " break;\n");
1166 if (case_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1170 edit
.add_fixits (&richloc
);
1171 auto_free
<char *> new_content
= edit
.get_content (filename
);
1172 ASSERT_STREQ ((" case 'a':\n"
1180 auto_free
<char *> diff
= edit
.generate_diff (false);
1181 ASSERT_STREQ (("@@ -1,4 +1,5 @@\n"
1190 /* Test applying a "replace" fixit that grows the affected line. */
1193 test_applying_fixits_growing_replace (const line_table_case
&case_
)
1195 /* Create a tempfile and write some text to it.
1196 .........................0000000001111111.
1197 .........................1234567890123456. */
1198 const char *old_content
= ("/* before */\n"
1199 "foo = bar.field;\n"
1201 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1202 const char *filename
= tmp
.get_filename ();
1203 line_table_test
ltt (case_
);
1204 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1206 /* Replace "field" with "m_field". */
1207 location_t start
= linemap_position_for_column (line_table
, 11);
1208 location_t finish
= linemap_position_for_column (line_table
, 15);
1209 location_t field
= make_location (start
, start
, finish
);
1210 rich_location
richloc (line_table
, field
);
1211 richloc
.add_fixit_replace ("m_field");
1214 edit
.add_fixits (&richloc
);
1215 auto_free
<char *> new_content
= edit
.get_content (filename
);
1216 if (finish
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1218 ASSERT_STREQ ("/* before */\n"
1219 "foo = bar.m_field;\n"
1220 "/* after */\n", new_content
);
1222 /* Verify location of ";" after the change. */
1223 ASSERT_EQ (18, edit
.get_effective_column (filename
, 2, 16));
1226 auto_free
<char *> diff
= edit
.generate_diff (false);
1227 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1229 "-foo = bar.field;\n"
1230 "+foo = bar.m_field;\n"
1231 " /* after */\n", diff
);
1235 /* Test applying a "replace" fixit that shrinks the affected line. */
1238 test_applying_fixits_shrinking_replace (const line_table_case
&case_
)
1240 /* Create a tempfile and write some text to it.
1241 .........................000000000111111111.
1242 .........................123456789012345678. */
1243 const char *old_content
= ("/* before */\n"
1244 "foo = bar.m_field;\n"
1246 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1247 const char *filename
= tmp
.get_filename ();
1248 line_table_test
ltt (case_
);
1249 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1251 /* Replace "field" with "m_field". */
1252 location_t start
= linemap_position_for_column (line_table
, 11);
1253 location_t finish
= linemap_position_for_column (line_table
, 17);
1254 location_t m_field
= make_location (start
, start
, finish
);
1255 rich_location
richloc (line_table
, m_field
);
1256 richloc
.add_fixit_replace ("field");
1259 edit
.add_fixits (&richloc
);
1260 auto_free
<char *> new_content
= edit
.get_content (filename
);
1261 if (finish
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1263 ASSERT_STREQ ("/* before */\n"
1264 "foo = bar.field;\n"
1265 "/* after */\n", new_content
);
1267 /* Verify location of ";" after the change. */
1268 ASSERT_EQ (16, edit
.get_effective_column (filename
, 2, 18));
1271 auto_free
<char *> diff
= edit
.generate_diff (false);
1272 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1274 "-foo = bar.m_field;\n"
1275 "+foo = bar.field;\n"
1276 " /* after */\n", diff
);
1280 /* Replacement fix-it hint containing a newline. */
1283 test_applying_fixits_replace_containing_newline (const line_table_case
&case_
)
1285 /* Create a tempfile and write some text to it.
1286 .........................0000000001111.
1287 .........................1234567890123. */
1288 const char *old_content
= "foo = bar ();\n";
1290 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1291 const char *filename
= tmp
.get_filename ();
1292 line_table_test
ltt (case_
);
1293 linemap_add (line_table
, LC_ENTER
, false, filename
, 1);
1295 /* Replace the " = " with "\n = ", as if we were reformatting an
1296 overly long line. */
1297 location_t start
= linemap_position_for_column (line_table
, 4);
1298 location_t finish
= linemap_position_for_column (line_table
, 6);
1299 location_t loc
= linemap_position_for_column (line_table
, 13);
1300 rich_location
richloc (line_table
, loc
);
1301 source_range range
= source_range::from_locations (start
, finish
);
1302 richloc
.add_fixit_replace (range
, "\n = ");
1304 /* Newlines are only supported within fix-it hints that
1305 are at the start of lines (for entirely new lines), hence
1306 this fix-it should not be displayed. */
1307 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
1309 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1313 edit
.add_fixits (&richloc
);
1314 auto_free
<char *> new_content
= edit
.get_content (filename
);
1315 //ASSERT_STREQ ("foo\n = bar ();\n", new_content);
1318 /* Test applying a "remove" fixit. */
1321 test_applying_fixits_remove (const line_table_case
&case_
)
1323 /* Create a tempfile and write some text to it.
1324 .........................000000000111111111.
1325 .........................123456789012345678. */
1326 const char *old_content
= ("/* before */\n"
1327 "foo = bar.m_field;\n"
1329 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1330 const char *filename
= tmp
.get_filename ();
1331 line_table_test
ltt (case_
);
1332 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1334 /* Remove ".m_field". */
1335 location_t start
= linemap_position_for_column (line_table
, 10);
1336 location_t finish
= linemap_position_for_column (line_table
, 17);
1337 rich_location
richloc (line_table
, start
);
1339 range
.m_start
= start
;
1340 range
.m_finish
= finish
;
1341 richloc
.add_fixit_remove (range
);
1344 edit
.add_fixits (&richloc
);
1345 auto_free
<char *> new_content
= edit
.get_content (filename
);
1346 if (finish
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1348 ASSERT_STREQ ("/* before */\n"
1350 "/* after */\n", new_content
);
1352 /* Verify location of ";" after the change. */
1353 ASSERT_EQ (10, edit
.get_effective_column (filename
, 2, 18));
1356 auto_free
<char *> diff
= edit
.generate_diff (false);
1357 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1359 "-foo = bar.m_field;\n"
1361 " /* after */\n", diff
);
1365 /* Test applying multiple fixits to one line. */
1368 test_applying_fixits_multiple (const line_table_case
&case_
)
1370 /* Create a tempfile and write some text to it.
1371 .........................00000000011111111.
1372 .........................12345678901234567. */
1373 const char *old_content
= ("/* before */\n"
1374 "foo = bar.field;\n"
1376 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1377 const char *filename
= tmp
.get_filename ();
1378 line_table_test
ltt (case_
);
1379 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1381 location_t c7
= linemap_position_for_column (line_table
, 7);
1382 location_t c9
= linemap_position_for_column (line_table
, 9);
1383 location_t c11
= linemap_position_for_column (line_table
, 11);
1384 location_t c15
= linemap_position_for_column (line_table
, 15);
1385 location_t c17
= linemap_position_for_column (line_table
, 17);
1387 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1390 /* Add a comment in front of "bar.field". */
1391 rich_location
insert_a (line_table
, c7
);
1392 insert_a
.add_fixit_insert_before (c7
, "/* alpha */");
1394 /* Add a comment after "bar.field;". */
1395 rich_location
insert_b (line_table
, c17
);
1396 insert_b
.add_fixit_insert_before (c17
, "/* beta */");
1398 /* Replace "bar" with "pub". */
1399 rich_location
replace_a (line_table
, c7
);
1400 replace_a
.add_fixit_replace (source_range::from_locations (c7
, c9
),
1403 /* Replace "field" with "meadow". */
1404 rich_location
replace_b (line_table
, c7
);
1405 replace_b
.add_fixit_replace (source_range::from_locations (c11
, c15
),
1409 edit
.add_fixits (&insert_a
);
1410 ASSERT_EQ (100, edit
.get_effective_column (filename
, 1, 100));
1411 ASSERT_EQ (1, edit
.get_effective_column (filename
, 2, 1));
1412 ASSERT_EQ (6, edit
.get_effective_column (filename
, 2, 6));
1413 ASSERT_EQ (18, edit
.get_effective_column (filename
, 2, 7));
1414 ASSERT_EQ (27, edit
.get_effective_column (filename
, 2, 16));
1415 ASSERT_EQ (100, edit
.get_effective_column (filename
, 3, 100));
1417 edit
.add_fixits (&insert_b
);
1418 edit
.add_fixits (&replace_a
);
1419 edit
.add_fixits (&replace_b
);
1421 if (c17
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1423 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1424 ASSERT_STREQ ("/* before */\n"
1425 "foo = /* alpha */pub.meadow;/* beta */\n"
1430 auto_free
<char *> diff
= edit
.generate_diff (false);
1431 ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1433 "-foo = bar.field;\n"
1434 "+foo = /* alpha */pub.meadow;/* beta */\n"
1435 " /* after */\n", diff
);
1439 /* Subroutine of test_applying_fixits_multiple_lines.
1440 Add the text "CHANGED: " to the front of the given line. */
1443 change_line (edit_context
&edit
, int line_num
)
1445 const line_map_ordinary
*ord_map
1446 = LINEMAPS_LAST_ORDINARY_MAP (line_table
);
1447 const int column
= 1;
1449 linemap_position_for_line_and_column (line_table
, ord_map
,
1452 expanded_location exploc
= expand_location (loc
);
1453 if (loc
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1455 ASSERT_EQ (line_num
, exploc
.line
);
1456 ASSERT_EQ (column
, exploc
.column
);
1459 rich_location
insert (line_table
, loc
);
1460 insert
.add_fixit_insert_before ("CHANGED: ");
1461 edit
.add_fixits (&insert
);
1465 /* Subroutine of test_applying_fixits_multiple_lines.
1466 Add the text "INSERTED\n" in front of the given line. */
1469 insert_line (edit_context
&edit
, int line_num
)
1471 const line_map_ordinary
*ord_map
1472 = LINEMAPS_LAST_ORDINARY_MAP (line_table
);
1473 const int column
= 1;
1475 linemap_position_for_line_and_column (line_table
, ord_map
,
1478 expanded_location exploc
= expand_location (loc
);
1479 if (loc
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1481 ASSERT_EQ (line_num
, exploc
.line
);
1482 ASSERT_EQ (column
, exploc
.column
);
1485 rich_location
insert (line_table
, loc
);
1486 insert
.add_fixit_insert_before ("INSERTED\n");
1487 edit
.add_fixits (&insert
);
1491 /* Test of editing multiple lines within a long file,
1492 to ensure that diffs are generated as expected. */
1495 test_applying_fixits_multiple_lines (const line_table_case
&case_
)
1497 /* Create a tempfile and write many lines of text to it. */
1498 named_temp_file
tmp (".txt");
1499 const char *filename
= tmp
.get_filename ();
1500 FILE *f
= fopen (filename
, "w");
1501 ASSERT_NE (f
, NULL
);
1502 for (int i
= 1; i
<= 1000; i
++)
1503 fprintf (f
, "line %i\n", i
);
1506 line_table_test
ltt (case_
);
1507 linemap_add (line_table
, LC_ENTER
, false, filename
, 1);
1508 linemap_position_for_column (line_table
, 127);
1512 /* A run of consecutive lines. */
1513 change_line (edit
, 2);
1514 change_line (edit
, 3);
1515 change_line (edit
, 4);
1516 insert_line (edit
, 5);
1518 /* A run of nearby lines, within the contextual limit. */
1519 change_line (edit
, 150);
1520 change_line (edit
, 151);
1521 location_t last_loc
= change_line (edit
, 153);
1523 if (last_loc
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1527 auto_free
<char *> diff
= edit
.generate_diff (false);
1528 ASSERT_STREQ ("@@ -1,7 +1,8 @@\n"
1533 "+CHANGED: line 2\n"
1534 "+CHANGED: line 3\n"
1535 "+CHANGED: line 4\n"
1540 "@@ -147,10 +148,10 @@\n"
1546 "+CHANGED: line 150\n"
1547 "+CHANGED: line 151\n"
1550 "+CHANGED: line 153\n"
1553 " line 156\n", diff
);
1555 /* Ensure tmp stays alive until this point, so that the tempfile
1556 persists until after the generate_diff call. */
1557 tmp
.get_filename ();
1560 /* Test of converting an initializer for a named field from
1561 the old GCC extension to C99 syntax.
1562 Exercises a shrinking replacement followed by a growing
1563 replacement on the same line. */
1566 test_applying_fixits_modernize_named_init (const line_table_case
&case_
)
1568 /* Create a tempfile and write some text to it.
1569 .........................00000000011111111.
1570 .........................12345678901234567. */
1571 const char *old_content
= ("/* before */\n"
1574 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
1575 const char *filename
= tmp
.get_filename ();
1576 line_table_test
ltt (case_
);
1577 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1579 location_t c1
= linemap_position_for_column (line_table
, 1);
1580 location_t c3
= linemap_position_for_column (line_table
, 3);
1581 location_t c8
= linemap_position_for_column (line_table
, 8);
1583 if (c8
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1586 /* Replace "bar" with ".". */
1587 rich_location
r1 (line_table
, c8
);
1588 r1
.add_fixit_replace (source_range::from_locations (c1
, c3
),
1591 /* Replace ":" with "bar =". */
1592 rich_location
r2 (line_table
, c8
);
1593 r2
.add_fixit_replace (source_range::from_locations (c8
, c8
),
1596 /* The order should not matter. Do r1 then r2. */
1599 edit
.add_fixits (&r1
);
1601 /* Verify state after first replacement. */
1603 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1604 /* We should now have:
1605 ............00000000011.
1606 ............12345678901. */
1607 ASSERT_STREQ ("/* before */\n"
1611 /* Location of the "1". */
1612 ASSERT_EQ (6, edit
.get_effective_column (filename
, 2, 8));
1613 /* Location of the ",". */
1614 ASSERT_EQ (9, edit
.get_effective_column (filename
, 2, 11));
1617 edit
.add_fixits (&r2
);
1619 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1620 /* Verify state after second replacement.
1621 ............00000000011111111.
1622 ............12345678901234567. */
1623 ASSERT_STREQ ("/* before */\n"
1629 /* Try again, doing r2 then r1; the new_content should be the same. */
1632 edit
.add_fixits (&r2
);
1633 edit
.add_fixits (&r1
);
1634 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1635 /*.............00000000011111111.
1636 .............12345678901234567. */
1637 ASSERT_STREQ ("/* before */\n"
1644 /* Test of a fixit affecting a file that can't be read. */
1647 test_applying_fixits_unreadable_file ()
1649 const char *filename
= "this-does-not-exist.txt";
1650 line_table_test
ltt ();
1651 linemap_add (line_table
, LC_ENTER
, false, filename
, 1);
1653 location_t loc
= linemap_position_for_column (line_table
, 1);
1655 rich_location
insert (line_table
, loc
);
1656 insert
.add_fixit_insert_before ("change 1");
1657 insert
.add_fixit_insert_before ("change 2");
1660 /* Attempting to add the fixits affecting the unreadable file
1661 should transition the edit from valid to invalid. */
1662 ASSERT_TRUE (edit
.valid_p ());
1663 edit
.add_fixits (&insert
);
1664 ASSERT_FALSE (edit
.valid_p ());
1665 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1666 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1669 /* Verify that we gracefully handle an attempt to edit a line
1670 that's beyond the end of the file. */
1673 test_applying_fixits_line_out_of_range ()
1675 /* Create a tempfile and write some text to it.
1676 ........................00000000011111111.
1677 ........................12345678901234567. */
1678 const char *old_content
= "One-liner file\n";
1679 temp_source_file
tmp (SELFTEST_LOCATION
, ".txt", old_content
);
1680 const char *filename
= tmp
.get_filename ();
1681 line_table_test
ltt ();
1682 linemap_add (line_table
, LC_ENTER
, false, filename
, 2);
1684 /* Try to insert a string in line 2. */
1685 location_t loc
= linemap_position_for_column (line_table
, 1);
1687 rich_location
insert (line_table
, loc
);
1688 insert
.add_fixit_insert_before ("change");
1690 /* Verify that attempting the insertion puts an edit_context
1691 into an invalid state. */
1693 ASSERT_TRUE (edit
.valid_p ());
1694 edit
.add_fixits (&insert
);
1695 ASSERT_FALSE (edit
.valid_p ());
1696 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1697 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1700 /* Verify the boundary conditions of column values in fix-it
1701 hints applied to edit_context instances. */
1704 test_applying_fixits_column_validation (const line_table_case
&case_
)
1706 /* Create a tempfile and write some text to it.
1707 ........................00000000011111111.
1708 ........................12345678901234567. */
1709 const char *old_content
= "One-liner file\n";
1710 temp_source_file
tmp (SELFTEST_LOCATION
, ".txt", old_content
);
1711 const char *filename
= tmp
.get_filename ();
1712 line_table_test
ltt (case_
);
1713 linemap_add (line_table
, LC_ENTER
, false, filename
, 1);
1715 location_t c11
= linemap_position_for_column (line_table
, 11);
1716 location_t c14
= linemap_position_for_column (line_table
, 14);
1717 location_t c15
= linemap_position_for_column (line_table
, 15);
1718 location_t c16
= linemap_position_for_column (line_table
, 16);
1720 /* Verify limits of valid columns in insertion fixits. */
1722 /* Verify inserting at the end of the line. */
1724 rich_location
richloc (line_table
, c11
);
1725 richloc
.add_fixit_insert_before (c15
, " change");
1727 /* Col 15 is at the end of the line, so the insertion
1730 edit
.add_fixits (&richloc
);
1731 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1732 if (c15
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1733 ASSERT_STREQ ("One-liner file change\n", new_content
);
1735 ASSERT_EQ (NULL
, new_content
);
1738 /* Verify inserting beyond the end of the line. */
1740 rich_location
richloc (line_table
, c11
);
1741 richloc
.add_fixit_insert_before (c16
, " change");
1743 /* Col 16 is beyond the end of the line, so the insertion
1744 should fail gracefully. */
1746 ASSERT_TRUE (edit
.valid_p ());
1747 edit
.add_fixits (&richloc
);
1748 ASSERT_FALSE (edit
.valid_p ());
1749 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1750 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1753 /* Verify limits of valid columns in replacement fixits. */
1755 /* Verify replacing the end of the line. */
1757 rich_location
richloc (line_table
, c11
);
1758 source_range range
= source_range::from_locations (c11
, c14
);
1759 richloc
.add_fixit_replace (range
, "change");
1761 /* Col 14 is at the end of the line, so the replacement
1764 edit
.add_fixits (&richloc
);
1765 auto_free
<char *> new_content
= edit
.get_content (tmp
.get_filename ());
1766 if (c14
<= LINE_MAP_MAX_LOCATION_WITH_COLS
)
1767 ASSERT_STREQ ("One-liner change\n", new_content
);
1769 ASSERT_EQ (NULL
, new_content
);
1772 /* Verify going beyond the end of the line. */
1774 rich_location
richloc (line_table
, c11
);
1775 source_range range
= source_range::from_locations (c11
, c15
);
1776 richloc
.add_fixit_replace (range
, "change");
1778 /* Col 15 is after the end of the line, so the replacement
1779 should fail; verify that the attempt fails gracefully. */
1781 ASSERT_TRUE (edit
.valid_p ());
1782 edit
.add_fixits (&richloc
);
1783 ASSERT_FALSE (edit
.valid_p ());
1784 ASSERT_EQ (NULL
, edit
.get_content (filename
));
1785 ASSERT_EQ (NULL
, edit
.generate_diff (false));
1789 /* Run all of the selftests within this file. */
1792 edit_context_c_tests ()
1794 test_get_content ();
1795 for_each_line_table_case (test_applying_fixits_insert_before
);
1796 for_each_line_table_case (test_applying_fixits_insert_after
);
1797 for_each_line_table_case (test_applying_fixits_insert_after_at_line_end
);
1798 for_each_line_table_case (test_applying_fixits_insert_after_failure
);
1799 for_each_line_table_case (test_applying_fixits_insert_containing_newline
);
1800 for_each_line_table_case (test_applying_fixits_growing_replace
);
1801 for_each_line_table_case (test_applying_fixits_shrinking_replace
);
1802 for_each_line_table_case (test_applying_fixits_replace_containing_newline
);
1803 for_each_line_table_case (test_applying_fixits_remove
);
1804 for_each_line_table_case (test_applying_fixits_multiple
);
1805 for_each_line_table_case (test_applying_fixits_multiple_lines
);
1806 for_each_line_table_case (test_applying_fixits_modernize_named_init
);
1807 test_applying_fixits_unreadable_file ();
1808 test_applying_fixits_line_out_of_range ();
1809 for_each_line_table_case (test_applying_fixits_column_validation
);
1812 } // namespace selftest
1814 #endif /* CHECKING_P */