Line data Source code
1 : /* Determining the results of applying fix-it hints.
2 : Copyright (C) 2016-2026 Free Software Foundation, Inc.
3 :
4 : This file is part of GCC.
5 :
6 : GCC is free software; you can redistribute it and/or modify it under
7 : the terms of the GNU General Public License as published by the Free
8 : Software Foundation; either version 3, or (at your option) any later
9 : version.
10 :
11 : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 : for more details.
15 :
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/>. */
19 :
20 : #include "config.h"
21 : #include "system.h"
22 : #include "coretypes.h"
23 : #include "line-map.h"
24 : #include "diagnostics/changes.h"
25 : #include "pretty-print.h"
26 : #include "diagnostics/color.h"
27 : #include "diagnostics/file-cache.h"
28 : #include "selftest.h"
29 :
30 : namespace diagnostics {
31 : namespace changes {
32 :
33 : /* This file implements a way to track the effect of fix-its,
34 : via a class change_set; the other classes are support classes for
35 : change_set.
36 :
37 : A complication here is that fix-its are expressed relative to coordinates
38 : in the file when it was parsed, before any changes have been made, and
39 : so if there's more that one fix-it to be applied, we have to adjust
40 : later fix-its to allow for the changes made by earlier ones. This
41 : is done by the various "get_effective_column" methods.
42 :
43 : The "filename" params are required to outlive the change_set (no
44 : copy of the underlying str is taken, just the ptr). */
45 :
46 : /* Forward decls. class change_set is declared within changes.h.
47 : The other types are declared here. */
48 : class change_set;
49 : class changed_file;
50 : class changed_line;
51 : class line_event;
52 :
53 : /* A struct to hold the params of a print_diff call. */
54 :
55 : class diff
56 : {
57 : public:
58 2717 : diff (pretty_printer *pp, bool show_filenames)
59 2717 : : m_pp (pp), m_show_filenames (show_filenames) {}
60 :
61 : pretty_printer *m_pp;
62 : bool m_show_filenames;
63 : };
64 :
65 : /* The state of one named file within an change_set: the filename,
66 : and the lines that have been edited so far. */
67 :
68 3611 : class changed_file
69 : {
70 : public:
71 : changed_file (change_set &ec, const char *filename);
72 : static void delete_cb (changed_file *file);
73 :
74 : const char *get_filename () const { return m_filename; }
75 : char *get_content ();
76 :
77 : bool apply_fixit (int line, int start_column,
78 : int next_column,
79 : const char *replacement_str,
80 : int replacement_len);
81 : int get_effective_column (int line, int column);
82 :
83 2645 : static int call_print_diff (const char *, changed_file *file,
84 : void *user_data)
85 : {
86 2645 : diff *d = (diff *)user_data;
87 2645 : file->print_diff (d->m_pp, d->m_show_filenames);
88 2645 : return 0;
89 : }
90 :
91 89180 : file_cache &get_file_cache () const
92 : {
93 89180 : return m_change_set.get_file_cache ();
94 : }
95 :
96 : private:
97 : bool print_content (pretty_printer *pp);
98 : void print_diff (pretty_printer *pp, bool show_filenames);
99 : int print_diff_hunk (pretty_printer *pp, int old_start_of_hunk,
100 : int old_end_of_hunk, int new_start_of_hunk);
101 : changed_line *get_line (int line);
102 : changed_line *get_or_insert_line (int line);
103 : int get_num_lines (bool *missing_trailing_newline);
104 :
105 : int get_effective_line_count (int old_start_of_hunk,
106 : int old_end_of_hunk);
107 :
108 : void print_run_of_changed_lines (pretty_printer *pp,
109 : int start_of_run,
110 : int end_of_run);
111 :
112 : change_set &m_change_set;
113 : const char *m_filename;
114 : typed_splay_tree<int, changed_line *> m_changed_lines;
115 : int m_num_lines;
116 : };
117 :
118 : /* A line added before an changed_line. */
119 :
120 : class added_line
121 : {
122 : public:
123 142 : added_line (const char *content, int len)
124 284 : : m_content (xstrndup (content, len)), m_len (len) {}
125 142 : ~added_line () { free (m_content); }
126 :
127 206 : const char *get_content () const { return m_content; }
128 142 : int get_len () const { return m_len; }
129 :
130 : private:
131 : char *m_content;
132 : int m_len;
133 : };
134 :
135 : /* Class for representing edit events that have occurred on one line of
136 : one file: the replacement of some text between some columns
137 : on the line.
138 :
139 : Subsequent events will need their columns adjusting if they're
140 : are on this line and their column is >= the start point. */
141 :
142 : class line_event
143 : {
144 : public:
145 3433 : line_event (int start, int next, int len) : m_start (start),
146 3433 : m_delta (len - (next - start)) {}
147 :
148 1860 : int get_effective_column (int orig_column) const
149 : {
150 1860 : if (orig_column >= m_start)
151 1220 : return orig_column += m_delta;
152 : else
153 : return orig_column;
154 : }
155 :
156 : private:
157 : int m_start;
158 : int m_delta;
159 : };
160 :
161 : /* The state of one edited line within an changed_file.
162 : As well as the current content of the line, it contains a record of
163 : the changes, so that further changes can be applied in the correct
164 : place.
165 :
166 : When handling fix-it hints containing newlines, new lines are added
167 : as added_line predecessors to an changed_line. Hence it's possible
168 : for an "changed_line" to not actually have been changed, but to merely
169 : be a placeholder for the lines added before it. This can be tested
170 : for with actuall_edited_p, and has a slight effect on how diff hunks
171 : are generated. */
172 :
173 : class changed_line
174 : {
175 : public:
176 : changed_line (file_cache &fc, const char *filename, int line_num);
177 : ~changed_line ();
178 : static void delete_cb (changed_line *el);
179 :
180 11786 : int get_line_num () const { return m_line_num; }
181 3940 : const char *get_content () const { return m_content; }
182 : int get_len () const { return m_len; }
183 :
184 : int get_effective_column (int orig_column) const;
185 : bool apply_fixit (int start_column,
186 : int next_column,
187 : const char *replacement_str,
188 : int replacement_len);
189 :
190 : int get_effective_line_count () const;
191 :
192 : /* Has the content of this line actually changed, or are we merely
193 : recording predecessor added_lines? */
194 20885 : bool actually_edited_p () const { return m_line_events.length () > 0; }
195 :
196 : void print_content (pretty_printer *pp) const;
197 : void print_diff_lines (pretty_printer *pp) const;
198 :
199 : private:
200 : void ensure_capacity (int len);
201 : void ensure_terminated ();
202 :
203 : int m_line_num;
204 : char *m_content;
205 : int m_len;
206 : int m_alloc_sz;
207 : auto_vec <line_event> m_line_events;
208 : auto_vec <added_line *> m_predecessors;
209 : };
210 :
211 : /* Forward decls. */
212 :
213 : static void
214 : print_diff_line (pretty_printer *pp, char prefix_char,
215 : const char *line, int line_size);
216 :
217 : /* Implementation of class change_set. */
218 :
219 : /* change_set's ctor. */
220 :
221 10443 : change_set::change_set (file_cache &fc)
222 10443 : : m_file_cache (fc),
223 10443 : m_valid (true),
224 10443 : m_files (strcmp, NULL, changed_file::delete_cb)
225 10443 : {}
226 :
227 : /* Add any fixits within RICHLOC to this context, recording the
228 : changes that they make. */
229 :
230 : void
231 11368 : change_set::add_fixits (rich_location *richloc)
232 : {
233 11368 : if (!m_valid)
234 : return;
235 11136 : if (richloc->seen_impossible_fixit_p ())
236 : {
237 6768 : m_valid = false;
238 6768 : return;
239 : }
240 8633 : for (unsigned i = 0; i < richloc->get_num_fixit_hints (); i++)
241 : {
242 4265 : const fixit_hint *hint = richloc->get_fixit_hint (i);
243 4265 : if (!apply_fixit (hint))
244 690 : m_valid = false;
245 : }
246 : }
247 :
248 : /* Get the content of the given file, with fix-its applied.
249 : If any errors occurred in this change_set, return NULL.
250 : The ptr should be freed by the caller. */
251 :
252 : char *
253 1288 : change_set::get_content (const char *filename)
254 : {
255 1288 : if (!m_valid)
256 : return NULL;
257 844 : changed_file &file = get_or_insert_file (filename);
258 844 : return file.get_content ();
259 : }
260 :
261 : /* Map a location before the edits to a column number after the edits.
262 : This method is for the selftests. */
263 :
264 : int
265 1088 : change_set::get_effective_column (const char *filename, int line,
266 : int column)
267 : {
268 1088 : changed_file *file = get_file (filename);
269 1088 : if (!file)
270 : return column;
271 1088 : return file->get_effective_column (line, column);
272 : }
273 :
274 : /* Generate a unified diff. The resulting string should be freed by the
275 : caller. Primarily for selftests.
276 : If any errors occurred in this change_set, return NULL. */
277 :
278 : char *
279 9894 : change_set::generate_diff (bool show_filenames)
280 : {
281 9894 : if (!m_valid)
282 : return NULL;
283 :
284 2700 : pretty_printer pp;
285 2700 : print_diff (&pp, show_filenames);
286 2700 : return xstrdup (pp_formatted_text (&pp));
287 2700 : }
288 :
289 : /* Print a unified diff to PP, showing the changes made within the
290 : context. */
291 :
292 : void
293 2717 : change_set::print_diff (pretty_printer *pp, bool show_filenames)
294 : {
295 2717 : if (!m_valid)
296 0 : return;
297 2717 : diff d (pp, show_filenames);
298 2717 : m_files.foreach (changed_file::call_print_diff, &d);
299 : }
300 :
301 : /* Attempt to apply the given fixit. Return true if it can be
302 : applied, or false otherwise. */
303 :
304 : bool
305 4265 : change_set::apply_fixit (const fixit_hint *hint)
306 : {
307 4265 : expanded_location start = expand_location (hint->get_start_loc ());
308 4265 : expanded_location next_loc = expand_location (hint->get_next_loc ());
309 4265 : if (start.file != next_loc.file)
310 : return false;
311 4265 : if (start.line != next_loc.line)
312 : return false;
313 4265 : if (start.column == 0)
314 : return false;
315 4265 : if (next_loc.column == 0)
316 : return false;
317 :
318 4265 : changed_file &file = get_or_insert_file (start.file);
319 4265 : if (!m_valid)
320 : return false;
321 4265 : return file.apply_fixit (start.line, start.column, next_loc.column,
322 : hint->get_string (),
323 4265 : hint->get_length ());
324 : }
325 :
326 : /* Locate the changed_file * for FILENAME, if any
327 : Return NULL if there isn't one. */
328 :
329 : changed_file *
330 6197 : change_set::get_file (const char *filename)
331 : {
332 6197 : gcc_assert (filename);
333 6197 : return m_files.lookup (filename);
334 : }
335 :
336 : /* Locate the changed_file for FILENAME, adding one if there isn't one. */
337 :
338 : changed_file &
339 5109 : change_set::get_or_insert_file (const char *filename)
340 : {
341 5109 : gcc_assert (filename);
342 :
343 5109 : changed_file *file = get_file (filename);
344 5109 : if (file)
345 : return *file;
346 :
347 : /* Not found. */
348 3611 : file = new changed_file (*this, filename);
349 3611 : m_files.insert (filename, file);
350 3611 : return *file;
351 : }
352 :
353 : /* Implementation of class changed_file. */
354 :
355 : /* Callback for m_changed_lines, for comparing line numbers. */
356 :
357 67131 : static int line_comparator (int a, int b)
358 : {
359 67131 : return a - b;
360 : }
361 :
362 : /* changed_file's constructor. */
363 :
364 3611 : changed_file::changed_file (change_set &ec, const char *filename)
365 3611 : : m_change_set (ec),
366 3611 : m_filename (filename),
367 3611 : m_changed_lines (line_comparator, NULL, changed_line::delete_cb),
368 3611 : m_num_lines (-1)
369 : {
370 3611 : }
371 :
372 : /* A callback for deleting changed_file *, for use as a
373 : delete_value_fn for change_set::m_files. */
374 :
375 : void
376 3611 : changed_file::delete_cb (changed_file *file)
377 : {
378 7222 : delete file;
379 3611 : }
380 :
381 : /* Get the content of the file, with fix-its applied.
382 : The ptr should be freed by the caller. */
383 :
384 : char *
385 844 : changed_file::get_content ()
386 : {
387 844 : pretty_printer pp;
388 844 : if (!print_content (&pp))
389 : return NULL;
390 844 : return xstrdup (pp_formatted_text (&pp));
391 844 : }
392 :
393 : /* Attempt to replace columns START_COLUMN up to but not including NEXT_COLUMN
394 : of LINE with the string REPLACEMENT_STR of length REPLACEMENT_LEN,
395 : updating the in-memory copy of the line, and the record of edits to
396 : the line. */
397 :
398 : bool
399 4265 : changed_file::apply_fixit (int line, int start_column, int next_column,
400 : const char *replacement_str,
401 : int replacement_len)
402 : {
403 4265 : changed_line *el = get_or_insert_line (line);
404 4265 : if (!el)
405 : return false;
406 4161 : return el->apply_fixit (start_column, next_column, replacement_str,
407 4161 : replacement_len);
408 : }
409 :
410 : /* Given line LINE, map from COLUMN in the input file to its current
411 : column after edits have been applied. */
412 :
413 : int
414 1088 : changed_file::get_effective_column (int line, int column)
415 : {
416 1088 : const changed_line *el = get_line (line);
417 1088 : if (!el)
418 : return column;
419 832 : return el->get_effective_column (column);
420 : }
421 :
422 : /* Attempt to print the content of the file to PP, with edits applied.
423 : Return true if successful, false otherwise. */
424 :
425 : bool
426 844 : changed_file::print_content (pretty_printer *pp)
427 : {
428 844 : bool missing_trailing_newline;
429 844 : int line_count = get_num_lines (&missing_trailing_newline);
430 3172 : for (int line_num = 1; line_num <= line_count; line_num++)
431 : {
432 2328 : changed_line *el = get_line (line_num);
433 2328 : if (el)
434 832 : el->print_content (pp);
435 : else
436 : {
437 1496 : char_span line
438 1496 : = get_file_cache ().get_source_line (m_filename, line_num);
439 1496 : if (!line)
440 0 : return false;
441 18896 : for (size_t i = 0; i < line.length (); i++)
442 17400 : pp_character (pp, line[i]);
443 : }
444 2328 : if (line_num < line_count)
445 1488 : pp_character (pp, '\n');
446 : }
447 :
448 844 : if (!missing_trailing_newline)
449 836 : pp_character (pp, '\n');
450 :
451 : return true;
452 : }
453 :
454 : /* Print a unified diff to PP, showing any changes that have occurred
455 : to this file. */
456 :
457 : void
458 2645 : changed_file::print_diff (pretty_printer *pp, bool show_filenames)
459 : {
460 2645 : if (show_filenames)
461 : {
462 2077 : pp_string (pp, colorize_start (pp_show_color (pp), "diff-filename"));
463 : /* Avoid -Wformat-diag in non-diagnostic output. */
464 2077 : pp_string (pp, "--- ");
465 2077 : pp_string (pp, m_filename);
466 2077 : pp_newline (pp);
467 2077 : pp_string (pp, "+++ ");
468 2077 : pp_string (pp, m_filename);
469 2077 : pp_newline (pp);
470 2077 : pp_string (pp, colorize_stop (pp_show_color (pp)));
471 : }
472 :
473 2645 : changed_line *el = m_changed_lines.min ();
474 :
475 2645 : bool missing_trailing_newline;
476 2645 : int line_count = get_num_lines (&missing_trailing_newline);
477 :
478 2645 : const int context_lines = 3;
479 :
480 : /* Track new line numbers minus old line numbers. */
481 :
482 2645 : int line_delta = 0;
483 :
484 2645 : while (el)
485 : {
486 2706 : int start_of_hunk = el->get_line_num ();
487 2706 : start_of_hunk -= context_lines;
488 2706 : if (start_of_hunk < 1)
489 : start_of_hunk = 1;
490 :
491 : /* Locate end of hunk, merging in changed lines
492 : that are sufficiently close. */
493 2986 : while (true)
494 : {
495 2986 : changed_line *next_el
496 2986 : = m_changed_lines.successor (el->get_line_num ());
497 341 : if (!next_el)
498 : break;
499 :
500 341 : int end_of_printed_hunk = el->get_line_num () + context_lines;
501 341 : if (!el->actually_edited_p ())
502 57 : end_of_printed_hunk--;
503 :
504 341 : if (end_of_printed_hunk
505 341 : >= next_el->get_line_num () - context_lines)
506 : el = next_el;
507 : else
508 : break;
509 : }
510 :
511 2706 : int end_of_hunk = el->get_line_num ();
512 2706 : end_of_hunk += context_lines;
513 2706 : if (!el->actually_edited_p ())
514 139 : end_of_hunk--;
515 2706 : if (end_of_hunk > line_count)
516 : end_of_hunk = line_count;
517 :
518 2706 : int new_start_of_hunk = start_of_hunk + line_delta;
519 2706 : line_delta += print_diff_hunk (pp, start_of_hunk, end_of_hunk,
520 : new_start_of_hunk);
521 8057 : el = m_changed_lines.successor (el->get_line_num ());
522 : }
523 2645 : }
524 :
525 : /* Print one hunk within a unified diff to PP, covering the
526 : given range of lines. OLD_START_OF_HUNK and OLD_END_OF_HUNK are
527 : line numbers in the unedited version of the file.
528 : NEW_START_OF_HUNK is a line number in the edited version of the file.
529 : Return the change in the line count within the hunk. */
530 :
531 : int
532 2706 : changed_file::print_diff_hunk (pretty_printer *pp, int old_start_of_hunk,
533 : int old_end_of_hunk, int new_start_of_hunk)
534 : {
535 2706 : int old_num_lines = old_end_of_hunk - old_start_of_hunk + 1;
536 2706 : int new_num_lines
537 2706 : = get_effective_line_count (old_start_of_hunk, old_end_of_hunk);
538 :
539 2706 : pp_string (pp, colorize_start (pp_show_color (pp), "diff-hunk"));
540 2706 : pp_printf (pp, "%s -%i,%i +%i,%i %s",
541 : "@@", old_start_of_hunk, old_num_lines,
542 : new_start_of_hunk, new_num_lines, "@@\n");
543 2706 : pp_string (pp, colorize_stop (pp_show_color (pp)));
544 :
545 2706 : int line_num = old_start_of_hunk;
546 10100 : while (line_num <= old_end_of_hunk)
547 : {
548 4688 : changed_line *el = get_line (line_num);
549 4688 : if (el)
550 : {
551 : /* We have an edited line.
552 : Consolidate into runs of changed lines. */
553 : const int first_changed_line_in_run = line_num;
554 5748 : while (get_line (line_num))
555 2986 : line_num++;
556 2762 : const int last_changed_line_in_run = line_num - 1;
557 2762 : print_run_of_changed_lines (pp, first_changed_line_in_run,
558 : last_changed_line_in_run);
559 : }
560 : else
561 : {
562 : /* Unchanged line. */
563 1926 : char_span old_line
564 1926 : = get_file_cache ().get_source_line (m_filename, line_num);
565 1926 : print_diff_line (pp, ' ', old_line.get_buffer (), old_line.length ());
566 1926 : line_num++;
567 : }
568 : }
569 :
570 2706 : return new_num_lines - old_num_lines;
571 : }
572 :
573 : /* Subroutine of changed_file::print_diff_hunk: given a run of lines
574 : from START_OF_RUN to END_OF_RUN that all have changed_line instances,
575 : print the diff to PP. */
576 :
577 : void
578 2762 : changed_file::print_run_of_changed_lines (pretty_printer *pp,
579 : int start_of_run,
580 : int end_of_run)
581 : {
582 : /* Show old version of lines. */
583 2762 : pp_string (pp, colorize_start (pp_show_color (pp),
584 : "diff-delete"));
585 2762 : for (int line_num = start_of_run;
586 5748 : line_num <= end_of_run;
587 : line_num++)
588 : {
589 2986 : changed_line *el_in_run = get_line (line_num);
590 2986 : gcc_assert (el_in_run);
591 5833 : if (el_in_run->actually_edited_p ())
592 : {
593 2847 : char_span old_line
594 2847 : = get_file_cache ().get_source_line (m_filename, line_num);
595 2847 : print_diff_line (pp, '-', old_line.get_buffer (),
596 2847 : old_line.length ());
597 : }
598 : }
599 2762 : pp_string (pp, colorize_stop (pp_show_color (pp)));
600 :
601 : /* Show new version of lines. */
602 2762 : pp_string (pp, colorize_start (pp_show_color (pp),
603 : "diff-insert"));
604 5748 : for (int line_num = start_of_run;
605 5748 : line_num <= end_of_run;
606 : line_num++)
607 : {
608 2986 : changed_line *el_in_run = get_line (line_num);
609 2986 : gcc_assert (el_in_run);
610 2986 : el_in_run->print_diff_lines (pp);
611 : }
612 2762 : pp_string (pp, colorize_stop (pp_show_color (pp)));
613 2762 : }
614 :
615 : /* Print one line within a diff, starting with PREFIX_CHAR,
616 : followed by the LINE of content, of length LEN. LINE is
617 : not necessarily 0-terminated. Print a trailing newline. */
618 :
619 : static void
620 7901 : print_diff_line (pretty_printer *pp, char prefix_char,
621 : const char *line, int len)
622 : {
623 7901 : pp_character (pp, prefix_char);
624 17385105 : for (int i = 0; i < len; i++)
625 17377204 : pp_character (pp, line[i]);
626 7901 : pp_character (pp, '\n');
627 7901 : }
628 :
629 : /* Determine the number of lines that will be present after
630 : editing for the range of lines from OLD_START_OF_HUNK to
631 : OLD_END_OF_HUNK inclusive. */
632 :
633 : int
634 2706 : changed_file::get_effective_line_count (int old_start_of_hunk,
635 : int old_end_of_hunk)
636 : {
637 2706 : int line_count = 0;
638 7618 : for (int old_line_num = old_start_of_hunk; old_line_num <= old_end_of_hunk;
639 : old_line_num++)
640 : {
641 4912 : changed_line *el = get_line (old_line_num);
642 4912 : if (el)
643 2986 : line_count += el->get_effective_line_count ();
644 : else
645 1926 : line_count++;
646 : }
647 2706 : return line_count;
648 : }
649 :
650 : /* Get the state of LINE within the file, or NULL if it is untouched. */
651 :
652 : changed_line *
653 29001 : changed_file::get_line (int line)
654 : {
655 29001 : return m_changed_lines.lookup (line);
656 : }
657 :
658 : /* Get the state of LINE within the file, creating a state for it
659 : if necessary. Return NULL if an error occurs. */
660 :
661 : changed_line *
662 4265 : changed_file::get_or_insert_line (int line)
663 : {
664 4265 : changed_line *el = get_line (line);
665 4265 : if (el)
666 : return el;
667 3940 : el = new changed_line (get_file_cache (), m_filename, line);
668 3940 : if (el->get_content () == NULL)
669 : {
670 104 : delete el;
671 104 : return NULL;
672 : }
673 3836 : m_changed_lines.insert (line, el);
674 3836 : return el;
675 : }
676 :
677 : /* Get the total number of lines in m_content, writing
678 : true to *MISSING_TRAILING_NEWLINE if the final line
679 : if missing a newline, false otherwise. */
680 :
681 : int
682 3489 : changed_file::get_num_lines (bool *missing_trailing_newline)
683 : {
684 3489 : gcc_assert (missing_trailing_newline);
685 3489 : if (m_num_lines == -1)
686 : {
687 2913 : m_num_lines = 0;
688 148051 : while (true)
689 : {
690 75482 : char_span line
691 75482 : = get_file_cache ().get_source_line (m_filename, m_num_lines + 1);
692 75482 : if (line)
693 72569 : m_num_lines++;
694 : else
695 : break;
696 72569 : }
697 : }
698 3489 : *missing_trailing_newline
699 3489 : = get_file_cache ().missing_trailing_newline_p (m_filename);
700 3489 : return m_num_lines;
701 : }
702 :
703 : /* Implementation of class changed_line. */
704 :
705 : /* changed_line's ctor. */
706 :
707 3940 : changed_line::changed_line (file_cache &fc, const char *filename, int line_num)
708 3940 : : m_line_num (line_num),
709 3940 : m_content (NULL), m_len (0), m_alloc_sz (0),
710 3940 : m_line_events (),
711 3940 : m_predecessors ()
712 : {
713 3940 : char_span line = fc.get_source_line (filename, line_num);
714 3940 : if (!line)
715 104 : return;
716 3836 : m_len = line.length ();
717 3836 : ensure_capacity (m_len);
718 3836 : memcpy (m_content, line.get_buffer (), m_len);
719 3836 : ensure_terminated ();
720 : }
721 :
722 : /* changed_line's dtor. */
723 :
724 3940 : changed_line::~changed_line ()
725 : {
726 3940 : unsigned i;
727 3940 : added_line *pred;
728 :
729 3940 : free (m_content);
730 4082 : FOR_EACH_VEC_ELT (m_predecessors, i, pred)
731 142 : delete pred;
732 3940 : }
733 :
734 : /* A callback for deleting changed_line *, for use as a
735 : delete_value_fn for changed_file::m_changed_lines. */
736 :
737 : void
738 3836 : changed_line::delete_cb (changed_line *el)
739 : {
740 3836 : delete el;
741 3836 : }
742 :
743 : /* Map a location before the edits to a column number after the edits,
744 : within a specific line. */
745 :
746 : int
747 8870 : changed_line::get_effective_column (int orig_column) const
748 : {
749 8870 : int i;
750 8870 : line_event *event;
751 10730 : FOR_EACH_VEC_ELT (m_line_events, i, event)
752 3080 : orig_column = event->get_effective_column (orig_column);
753 8870 : return orig_column;
754 : }
755 :
756 : /* Attempt to replace columns START_COLUMN up to but not including
757 : NEXT_COLUMN of the line with the string REPLACEMENT_STR of
758 : length REPLACEMENT_LEN, updating the in-memory copy of the line,
759 : and the record of edits to the line.
760 : Return true if successful; false if an error occurred. */
761 :
762 : bool
763 4161 : changed_line::apply_fixit (int start_column,
764 : int next_column,
765 : const char *replacement_str,
766 : int replacement_len)
767 : {
768 : /* Handle newlines. They will only ever be at the end of the
769 : replacement text, thanks to the filtering in rich_location. */
770 4161 : if (replacement_len > 1)
771 3430 : if (replacement_str[replacement_len - 1] == '\n')
772 : {
773 : /* Stash in m_predecessors, stripping off newline. */
774 426 : m_predecessors.safe_push (new added_line (replacement_str,
775 142 : replacement_len - 1));
776 142 : return true;
777 : }
778 :
779 4019 : start_column = get_effective_column (start_column);
780 4019 : next_column = get_effective_column (next_column);
781 :
782 4019 : int start_offset = start_column - 1;
783 4019 : int next_offset = next_column - 1;
784 :
785 4019 : gcc_assert (start_offset >= 0);
786 4019 : gcc_assert (next_offset >= 0);
787 :
788 4019 : if (start_column > next_column)
789 : return false;
790 4019 : if (start_offset >= (m_len + 1))
791 : return false;
792 3637 : if (next_offset >= (m_len + 1))
793 : return false;
794 :
795 3433 : size_t victim_len = next_offset - start_offset;
796 :
797 : /* Ensure buffer is big enough. */
798 3433 : size_t new_len = m_len + replacement_len - victim_len;
799 3433 : ensure_capacity (new_len);
800 :
801 3433 : char *suffix = m_content + next_offset;
802 3433 : gcc_assert (suffix <= m_content + m_len);
803 3433 : size_t len_suffix = (m_content + m_len) - suffix;
804 :
805 : /* Move successor content into position. They overlap, so use memmove. */
806 3433 : memmove (m_content + start_offset + replacement_len,
807 : suffix, len_suffix);
808 :
809 : /* Replace target content. They don't overlap, so use memcpy. */
810 3433 : memcpy (m_content + start_offset,
811 : replacement_str,
812 : replacement_len);
813 :
814 3433 : m_len = new_len;
815 :
816 3433 : ensure_terminated ();
817 :
818 : /* Record the replacement, so that future changes to the line can have
819 : their column information adjusted accordingly. */
820 6866 : m_line_events.safe_push (line_event (start_column, next_column,
821 3433 : replacement_len));
822 3433 : return true;
823 : }
824 :
825 : /* Determine the number of lines that will be present after
826 : editing for this line. Typically this is just 1, but
827 : if newlines have been added before this line, they will
828 : also be counted. */
829 :
830 : int
831 2986 : changed_line::get_effective_line_count () const
832 : {
833 2986 : return m_predecessors.length () + 1;
834 : }
835 :
836 : /* Subroutine of changed_file::print_content.
837 : Print this line and any new lines added before it, to PP. */
838 :
839 : void
840 832 : changed_line::print_content (pretty_printer *pp) const
841 : {
842 832 : unsigned i;
843 832 : added_line *pred;
844 896 : FOR_EACH_VEC_ELT (m_predecessors, i, pred)
845 : {
846 64 : pp_string (pp, pred->get_content ());
847 64 : pp_newline (pp);
848 : }
849 832 : pp_string (pp, m_content);
850 832 : }
851 :
852 : /* Subroutine of changed_file::print_run_of_changed_lines for
853 : printing diff hunks to PP.
854 : Print the '+' line for this line, and any newlines added
855 : before it.
856 : Note that if this changed_line was actually edited, the '-'
857 : line has already been printed. If it wasn't, then we merely
858 : have a placeholder changed_line for adding newlines to, and
859 : we need to print a ' ' line for the changed_line as we haven't
860 : printed it yet. */
861 :
862 : void
863 2986 : changed_line::print_diff_lines (pretty_printer *pp) const
864 : {
865 2986 : unsigned i;
866 2986 : added_line *pred;
867 3128 : FOR_EACH_VEC_ELT (m_predecessors, i, pred)
868 142 : print_diff_line (pp, '+', pred->get_content (),
869 : pred->get_len ());
870 2986 : if (actually_edited_p ())
871 2847 : print_diff_line (pp, '+', m_content, m_len);
872 : else
873 139 : print_diff_line (pp, ' ', m_content, m_len);
874 2986 : }
875 :
876 : /* Ensure that the buffer for m_content is at least large enough to hold
877 : a string of length LEN and its 0-terminator, doubling on repeated
878 : allocations. */
879 :
880 : void
881 7269 : changed_line::ensure_capacity (int len)
882 : {
883 : /* Allow 1 extra byte for 0-termination. */
884 7269 : if (m_alloc_sz < (len + 1))
885 : {
886 4341 : size_t new_alloc_sz = (len + 1) * 2;
887 4341 : m_content = (char *)xrealloc (m_content, new_alloc_sz);
888 4341 : m_alloc_sz = new_alloc_sz;
889 : }
890 7269 : }
891 :
892 : /* Ensure that m_content is 0-terminated. */
893 :
894 : void
895 7269 : changed_line::ensure_terminated ()
896 : {
897 : /* 0-terminate the buffer. */
898 7269 : gcc_assert (m_len < m_alloc_sz);
899 7269 : m_content[m_len] = '\0';
900 7269 : }
901 :
902 : #if CHECKING_P
903 :
904 : /* Selftests of code-editing. */
905 :
906 : namespace selftest {
907 :
908 : using line_table_case = ::selftest::line_table_case;
909 : using line_table_test = ::selftest::line_table_test;
910 : using temp_source_file = ::selftest::temp_source_file;
911 : using named_temp_file = ::selftest::named_temp_file;
912 :
913 : /* A wrapper class for ensuring that the underlying pointer is freed. */
914 :
915 : template <typename POINTER_T>
916 : class auto_free
917 : {
918 : public:
919 1636 : auto_free (POINTER_T p) : m_ptr (p) {}
920 1316 : ~auto_free () { free (m_ptr); }
921 :
922 : operator POINTER_T () { return m_ptr; }
923 :
924 : private:
925 : POINTER_T m_ptr;
926 : };
927 :
928 : /* Verify that change_set::get_content works for unedited files. */
929 :
930 : static void
931 4 : test_get_content ()
932 : {
933 : /* Test of empty file. */
934 4 : {
935 4 : const char *content = ("");
936 4 : temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
937 4 : file_cache fc;
938 4 : change_set edit (fc);
939 4 : auto_free <char *> result = edit.get_content (tmp.get_filename ());
940 4 : ASSERT_STREQ ("", result);
941 4 : }
942 :
943 : /* Test of simple content. */
944 4 : {
945 4 : const char *content = ("/* before */\n"
946 : "foo = bar.field;\n"
947 : "/* after */\n");
948 4 : temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
949 4 : file_cache fc;
950 4 : change_set edit (fc);
951 4 : auto_free <char *> result = edit.get_content (tmp.get_filename ());
952 4 : ASSERT_STREQ ("/* before */\n"
953 : "foo = bar.field;\n"
954 : "/* after */\n", result);
955 4 : }
956 :
957 : /* Test of omitting the trailing newline on the final line. */
958 4 : {
959 4 : const char *content = ("/* before */\n"
960 : "foo = bar.field;\n"
961 : "/* after */");
962 4 : temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
963 4 : file_cache fc;
964 4 : change_set edit (fc);
965 4 : auto_free <char *> result = edit.get_content (tmp.get_filename ());
966 : /* We should respect the omitted trailing newline. */
967 4 : ASSERT_STREQ ("/* before */\n"
968 : "foo = bar.field;\n"
969 : "/* after */", result);
970 4 : }
971 4 : }
972 :
973 : /* Test applying an "insert" fixit, using insert_before. */
974 :
975 : static void
976 96 : test_applying_fixits_insert_before (const line_table_case &case_)
977 : {
978 : /* Create a tempfile and write some text to it.
979 : .........................0000000001111111.
980 : .........................1234567890123456. */
981 96 : const char *old_content = ("/* before */\n"
982 : "foo = bar.field;\n"
983 : "/* after */\n");
984 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
985 96 : const char *filename = tmp.get_filename ();
986 96 : line_table_test ltt (case_);
987 96 : linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
988 :
989 : /* Add a comment in front of "bar.field". */
990 96 : location_t start = linemap_position_for_column (line_table, 7);
991 96 : rich_location richloc (line_table, start);
992 96 : richloc.add_fixit_insert_before ("/* inserted */");
993 :
994 96 : if (start > LINE_MAP_MAX_LOCATION_WITH_COLS)
995 32 : return;
996 :
997 64 : file_cache fc;
998 64 : change_set edit (fc);
999 64 : edit.add_fixits (&richloc);
1000 64 : auto_free <char *> new_content = edit.get_content (filename);
1001 64 : if (start <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1002 64 : ASSERT_STREQ ("/* before */\n"
1003 : "foo = /* inserted */bar.field;\n"
1004 : "/* after */\n", new_content);
1005 :
1006 : /* Verify that locations on other lines aren't affected by the change. */
1007 64 : ASSERT_EQ (100, edit.get_effective_column (filename, 1, 100));
1008 64 : ASSERT_EQ (100, edit.get_effective_column (filename, 3, 100));
1009 :
1010 : /* Verify locations on the line before the change. */
1011 64 : ASSERT_EQ (1, edit.get_effective_column (filename, 2, 1));
1012 64 : ASSERT_EQ (6, edit.get_effective_column (filename, 2, 6));
1013 :
1014 : /* Verify locations on the line at and after the change. */
1015 64 : ASSERT_EQ (21, edit.get_effective_column (filename, 2, 7));
1016 64 : ASSERT_EQ (22, edit.get_effective_column (filename, 2, 8));
1017 :
1018 : /* Verify diff. */
1019 64 : auto_free <char *> diff = edit.generate_diff (false);
1020 64 : ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1021 : " /* before */\n"
1022 : "-foo = bar.field;\n"
1023 : "+foo = /* inserted */bar.field;\n"
1024 : " /* after */\n", diff);
1025 96 : }
1026 :
1027 : /* Test applying an "insert" fixit, using insert_after, with
1028 : a range of length > 1 (to ensure that the end-point of
1029 : the input range is used). */
1030 :
1031 : static void
1032 96 : test_applying_fixits_insert_after (const line_table_case &case_)
1033 : {
1034 : /* Create a tempfile and write some text to it.
1035 : .........................0000000001111111.
1036 : .........................1234567890123456. */
1037 96 : const char *old_content = ("/* before */\n"
1038 : "foo = bar.field;\n"
1039 : "/* after */\n");
1040 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1041 96 : const char *filename = tmp.get_filename ();
1042 96 : line_table_test ltt (case_);
1043 96 : linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
1044 :
1045 : /* Add a comment after "field". */
1046 96 : location_t start = linemap_position_for_column (line_table, 11);
1047 96 : location_t finish = linemap_position_for_column (line_table, 15);
1048 96 : location_t field = make_location (start, start, finish);
1049 96 : rich_location richloc (line_table, field);
1050 96 : richloc.add_fixit_insert_after ("/* inserted */");
1051 :
1052 96 : if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
1053 32 : return;
1054 :
1055 : /* Verify that the text was inserted after the end of "field". */
1056 64 : file_cache fc;
1057 64 : change_set edit (fc);
1058 64 : edit.add_fixits (&richloc);
1059 64 : auto_free <char *> new_content = edit.get_content (filename);
1060 64 : ASSERT_STREQ ("/* before */\n"
1061 : "foo = bar.field/* inserted */;\n"
1062 : "/* after */\n", new_content);
1063 :
1064 : /* Verify diff. */
1065 64 : auto_free <char *> diff = edit.generate_diff (false);
1066 64 : ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1067 : " /* before */\n"
1068 : "-foo = bar.field;\n"
1069 : "+foo = bar.field/* inserted */;\n"
1070 : " /* after */\n", diff);
1071 96 : }
1072 :
1073 : /* Test applying an "insert" fixit, using insert_after at the end of
1074 : a line (contrast with test_applying_fixits_insert_after_failure
1075 : below). */
1076 :
1077 : static void
1078 96 : test_applying_fixits_insert_after_at_line_end (const line_table_case &case_)
1079 : {
1080 : /* Create a tempfile and write some text to it.
1081 : .........................0000000001111111.
1082 : .........................1234567890123456. */
1083 96 : const char *old_content = ("/* before */\n"
1084 : "foo = bar.field;\n"
1085 : "/* after */\n");
1086 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1087 96 : const char *filename = tmp.get_filename ();
1088 96 : line_table_test ltt (case_);
1089 96 : linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
1090 :
1091 : /* Add a comment after the semicolon. */
1092 96 : location_t loc = linemap_position_for_column (line_table, 16);
1093 96 : rich_location richloc (line_table, loc);
1094 96 : richloc.add_fixit_insert_after ("/* inserted */");
1095 :
1096 96 : if (loc > LINE_MAP_MAX_LOCATION_WITH_COLS)
1097 32 : return;
1098 :
1099 64 : file_cache fc;
1100 64 : change_set edit (fc);
1101 64 : edit.add_fixits (&richloc);
1102 64 : auto_free <char *> new_content = edit.get_content (filename);
1103 64 : ASSERT_STREQ ("/* before */\n"
1104 : "foo = bar.field;/* inserted */\n"
1105 : "/* after */\n", new_content);
1106 :
1107 : /* Verify diff. */
1108 64 : auto_free <char *> diff = edit.generate_diff (false);
1109 64 : ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1110 : " /* before */\n"
1111 : "-foo = bar.field;\n"
1112 : "+foo = bar.field;/* inserted */\n"
1113 : " /* after */\n", diff);
1114 96 : }
1115 :
1116 : /* Test of a failed attempt to apply an "insert" fixit, using insert_after,
1117 : due to the relevant linemap ending. Contrast with
1118 : test_applying_fixits_insert_after_at_line_end above. */
1119 :
1120 : static void
1121 96 : test_applying_fixits_insert_after_failure (const line_table_case &case_)
1122 : {
1123 : /* Create a tempfile and write some text to it.
1124 : .........................0000000001111111.
1125 : .........................1234567890123456. */
1126 96 : const char *old_content = ("/* before */\n"
1127 : "foo = bar.field;\n"
1128 : "/* after */\n");
1129 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1130 96 : const char *filename = tmp.get_filename ();
1131 96 : line_table_test ltt (case_);
1132 96 : linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 2);
1133 :
1134 : /* Add a comment after the semicolon. */
1135 96 : location_t loc = linemap_position_for_column (line_table, 16);
1136 96 : rich_location richloc (line_table, loc);
1137 :
1138 : /* We want a failure of linemap_position_for_loc_and_offset.
1139 : We can do this by starting a new linemap at line 3, so that
1140 : there is no appropriate location value for the insertion point
1141 : within the linemap for line 2. */
1142 96 : linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
1143 :
1144 : /* The failure fails to happen at the transition point from
1145 : packed ranges to unpacked ranges (where there are some "spare"
1146 : location_t values). Skip the test there. */
1147 96 : if (loc >= LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES)
1148 76 : return;
1149 :
1150 : /* Offsetting "loc" should now fail (by returning the input loc. */
1151 20 : ASSERT_EQ (loc, linemap_position_for_loc_and_offset (line_table, loc, 1));
1152 :
1153 : /* Hence attempting to use add_fixit_insert_after at the end of the line
1154 : should now fail. */
1155 20 : richloc.add_fixit_insert_after ("/* inserted */");
1156 20 : ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
1157 :
1158 20 : file_cache fc;
1159 20 : change_set edit (fc);
1160 20 : edit.add_fixits (&richloc);
1161 20 : ASSERT_FALSE (edit.valid_p ());
1162 20 : ASSERT_EQ (NULL, edit.get_content (filename));
1163 20 : ASSERT_EQ (NULL, edit.generate_diff (false));
1164 96 : }
1165 :
1166 : /* Test applying an "insert" fixit that adds a newline. */
1167 :
1168 : static void
1169 96 : test_applying_fixits_insert_containing_newline (const line_table_case &case_)
1170 : {
1171 : /* Create a tempfile and write some text to it.
1172 : .........................0000000001111111.
1173 : .........................1234567890123456. */
1174 96 : const char *old_content = (" case 'a':\n" /* line 1. */
1175 : " x = a;\n" /* line 2. */
1176 : " case 'b':\n" /* line 3. */
1177 : " x = b;\n");/* line 4. */
1178 :
1179 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1180 96 : const char *filename = tmp.get_filename ();
1181 96 : line_table_test ltt (case_);
1182 96 : linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
1183 :
1184 : /* Add a "break;" on a line by itself before line 3 i.e. before
1185 : column 1 of line 3. */
1186 96 : location_t case_start = linemap_position_for_column (line_table, 5);
1187 96 : location_t case_finish = linemap_position_for_column (line_table, 13);
1188 96 : location_t case_loc = make_location (case_start, case_start, case_finish);
1189 96 : rich_location richloc (line_table, case_loc);
1190 96 : location_t line_start = linemap_position_for_column (line_table, 1);
1191 96 : richloc.add_fixit_insert_before (line_start, " break;\n");
1192 :
1193 96 : if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
1194 32 : return;
1195 :
1196 64 : file_cache fc;
1197 64 : change_set edit (fc);
1198 64 : edit.add_fixits (&richloc);
1199 64 : auto_free <char *> new_content = edit.get_content (filename);
1200 64 : ASSERT_STREQ ((" case 'a':\n"
1201 : " x = a;\n"
1202 : " break;\n"
1203 : " case 'b':\n"
1204 : " x = b;\n"),
1205 : new_content);
1206 :
1207 : /* Verify diff. */
1208 64 : auto_free <char *> diff = edit.generate_diff (false);
1209 64 : ASSERT_STREQ (("@@ -1,4 +1,5 @@\n"
1210 : " case 'a':\n"
1211 : " x = a;\n"
1212 : "+ break;\n"
1213 : " case 'b':\n"
1214 : " x = b;\n"),
1215 : diff);
1216 96 : }
1217 :
1218 : /* Test applying a "replace" fixit that grows the affected line. */
1219 :
1220 : static void
1221 96 : test_applying_fixits_growing_replace (const line_table_case &case_)
1222 : {
1223 : /* Create a tempfile and write some text to it.
1224 : .........................0000000001111111.
1225 : .........................1234567890123456. */
1226 96 : const char *old_content = ("/* before */\n"
1227 : "foo = bar.field;\n"
1228 : "/* after */\n");
1229 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1230 96 : const char *filename = tmp.get_filename ();
1231 96 : line_table_test ltt (case_);
1232 96 : linemap_add (line_table, LC_ENTER, false, filename, 2);
1233 :
1234 : /* Replace "field" with "m_field". */
1235 96 : location_t start = linemap_position_for_column (line_table, 11);
1236 96 : location_t finish = linemap_position_for_column (line_table, 15);
1237 96 : location_t field = make_location (start, start, finish);
1238 96 : rich_location richloc (line_table, field);
1239 96 : richloc.add_fixit_replace ("m_field");
1240 :
1241 96 : file_cache fc;
1242 96 : change_set edit (fc);
1243 96 : edit.add_fixits (&richloc);
1244 96 : auto_free <char *> new_content = edit.get_content (filename);
1245 96 : if (finish <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1246 : {
1247 64 : ASSERT_STREQ ("/* before */\n"
1248 : "foo = bar.m_field;\n"
1249 : "/* after */\n", new_content);
1250 :
1251 : /* Verify location of ";" after the change. */
1252 64 : ASSERT_EQ (18, edit.get_effective_column (filename, 2, 16));
1253 :
1254 : /* Verify diff. */
1255 64 : auto_free <char *> diff = edit.generate_diff (false);
1256 64 : ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1257 : " /* before */\n"
1258 : "-foo = bar.field;\n"
1259 : "+foo = bar.m_field;\n"
1260 : " /* after */\n", diff);
1261 64 : }
1262 96 : }
1263 :
1264 : /* Test applying a "replace" fixit that shrinks the affected line. */
1265 :
1266 : static void
1267 96 : test_applying_fixits_shrinking_replace (const line_table_case &case_)
1268 : {
1269 : /* Create a tempfile and write some text to it.
1270 : .........................000000000111111111.
1271 : .........................123456789012345678. */
1272 96 : const char *old_content = ("/* before */\n"
1273 : "foo = bar.m_field;\n"
1274 : "/* after */\n");
1275 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1276 96 : const char *filename = tmp.get_filename ();
1277 96 : line_table_test ltt (case_);
1278 96 : linemap_add (line_table, LC_ENTER, false, filename, 2);
1279 :
1280 : /* Replace "field" with "m_field". */
1281 96 : location_t start = linemap_position_for_column (line_table, 11);
1282 96 : location_t finish = linemap_position_for_column (line_table, 17);
1283 96 : location_t m_field = make_location (start, start, finish);
1284 96 : rich_location richloc (line_table, m_field);
1285 96 : richloc.add_fixit_replace ("field");
1286 :
1287 96 : file_cache fc;
1288 96 : change_set edit (fc);
1289 96 : edit.add_fixits (&richloc);
1290 96 : auto_free <char *> new_content = edit.get_content (filename);
1291 96 : if (finish <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1292 : {
1293 64 : ASSERT_STREQ ("/* before */\n"
1294 : "foo = bar.field;\n"
1295 : "/* after */\n", new_content);
1296 :
1297 : /* Verify location of ";" after the change. */
1298 64 : ASSERT_EQ (16, edit.get_effective_column (filename, 2, 18));
1299 :
1300 : /* Verify diff. */
1301 64 : auto_free <char *> diff = edit.generate_diff (false);
1302 64 : ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1303 : " /* before */\n"
1304 : "-foo = bar.m_field;\n"
1305 : "+foo = bar.field;\n"
1306 : " /* after */\n", diff);
1307 64 : }
1308 96 : }
1309 :
1310 : /* Replacement fix-it hint containing a newline. */
1311 :
1312 : static void
1313 96 : test_applying_fixits_replace_containing_newline (const line_table_case &case_)
1314 : {
1315 : /* Create a tempfile and write some text to it.
1316 : .........................0000000001111.
1317 : .........................1234567890123. */
1318 96 : const char *old_content = "foo = bar ();\n";
1319 :
1320 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1321 96 : const char *filename = tmp.get_filename ();
1322 96 : line_table_test ltt (case_);
1323 96 : linemap_add (line_table, LC_ENTER, false, filename, 1);
1324 :
1325 : /* Replace the " = " with "\n = ", as if we were reformatting an
1326 : overly long line. */
1327 96 : location_t start = linemap_position_for_column (line_table, 4);
1328 96 : location_t finish = linemap_position_for_column (line_table, 6);
1329 96 : location_t loc = linemap_position_for_column (line_table, 13);
1330 96 : rich_location richloc (line_table, loc);
1331 96 : source_range range = source_range::from_locations (start, finish);
1332 96 : richloc.add_fixit_replace (range, "\n = ");
1333 :
1334 : /* Newlines are only supported within fix-it hints that
1335 : are at the start of lines (for entirely new lines), hence
1336 : this fix-it should not be displayed. */
1337 96 : ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
1338 :
1339 96 : if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
1340 32 : return;
1341 :
1342 64 : file_cache fc;
1343 64 : change_set edit (fc);
1344 64 : edit.add_fixits (&richloc);
1345 64 : auto_free <char *> new_content = edit.get_content (filename);
1346 : //ASSERT_STREQ ("foo\n = bar ();\n", new_content);
1347 96 : }
1348 :
1349 : /* Test applying a "remove" fixit. */
1350 :
1351 : static void
1352 96 : test_applying_fixits_remove (const line_table_case &case_)
1353 : {
1354 : /* Create a tempfile and write some text to it.
1355 : .........................000000000111111111.
1356 : .........................123456789012345678. */
1357 96 : const char *old_content = ("/* before */\n"
1358 : "foo = bar.m_field;\n"
1359 : "/* after */\n");
1360 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1361 96 : const char *filename = tmp.get_filename ();
1362 96 : line_table_test ltt (case_);
1363 96 : linemap_add (line_table, LC_ENTER, false, filename, 2);
1364 :
1365 : /* Remove ".m_field". */
1366 96 : location_t start = linemap_position_for_column (line_table, 10);
1367 96 : location_t finish = linemap_position_for_column (line_table, 17);
1368 96 : rich_location richloc (line_table, start);
1369 96 : source_range range;
1370 96 : range.m_start = start;
1371 96 : range.m_finish = finish;
1372 96 : richloc.add_fixit_remove (range);
1373 :
1374 96 : file_cache fc;
1375 96 : change_set edit (fc);
1376 96 : edit.add_fixits (&richloc);
1377 96 : auto_free <char *> new_content = edit.get_content (filename);
1378 96 : if (finish <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1379 : {
1380 64 : ASSERT_STREQ ("/* before */\n"
1381 : "foo = bar;\n"
1382 : "/* after */\n", new_content);
1383 :
1384 : /* Verify location of ";" after the change. */
1385 64 : ASSERT_EQ (10, edit.get_effective_column (filename, 2, 18));
1386 :
1387 : /* Verify diff. */
1388 64 : auto_free <char *> diff = edit.generate_diff (false);
1389 64 : ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1390 : " /* before */\n"
1391 : "-foo = bar.m_field;\n"
1392 : "+foo = bar;\n"
1393 : " /* after */\n", diff);
1394 64 : }
1395 96 : }
1396 :
1397 : /* Test applying multiple fixits to one line. */
1398 :
1399 : static void
1400 96 : test_applying_fixits_multiple (const line_table_case &case_)
1401 : {
1402 : /* Create a tempfile and write some text to it.
1403 : .........................00000000011111111.
1404 : .........................12345678901234567. */
1405 96 : const char *old_content = ("/* before */\n"
1406 : "foo = bar.field;\n"
1407 : "/* after */\n");
1408 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1409 96 : const char *filename = tmp.get_filename ();
1410 96 : line_table_test ltt (case_);
1411 96 : linemap_add (line_table, LC_ENTER, false, filename, 2);
1412 :
1413 96 : location_t c7 = linemap_position_for_column (line_table, 7);
1414 96 : location_t c9 = linemap_position_for_column (line_table, 9);
1415 96 : location_t c11 = linemap_position_for_column (line_table, 11);
1416 96 : location_t c15 = linemap_position_for_column (line_table, 15);
1417 96 : location_t c17 = linemap_position_for_column (line_table, 17);
1418 :
1419 96 : if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1420 32 : return;
1421 :
1422 : /* Add a comment in front of "bar.field". */
1423 64 : rich_location insert_a (line_table, c7);
1424 64 : insert_a.add_fixit_insert_before (c7, "/* alpha */");
1425 :
1426 : /* Add a comment after "bar.field;". */
1427 64 : rich_location insert_b (line_table, c17);
1428 64 : insert_b.add_fixit_insert_before (c17, "/* beta */");
1429 :
1430 : /* Replace "bar" with "pub". */
1431 64 : rich_location replace_a (line_table, c7);
1432 64 : replace_a.add_fixit_replace (source_range::from_locations (c7, c9),
1433 : "pub");
1434 :
1435 : /* Replace "field" with "meadow". */
1436 64 : rich_location replace_b (line_table, c7);
1437 64 : replace_b.add_fixit_replace (source_range::from_locations (c11, c15),
1438 : "meadow");
1439 :
1440 64 : file_cache fc;
1441 64 : change_set edit (fc);
1442 64 : edit.add_fixits (&insert_a);
1443 64 : ASSERT_EQ (100, edit.get_effective_column (filename, 1, 100));
1444 64 : ASSERT_EQ (1, edit.get_effective_column (filename, 2, 1));
1445 64 : ASSERT_EQ (6, edit.get_effective_column (filename, 2, 6));
1446 64 : ASSERT_EQ (18, edit.get_effective_column (filename, 2, 7));
1447 64 : ASSERT_EQ (27, edit.get_effective_column (filename, 2, 16));
1448 64 : ASSERT_EQ (100, edit.get_effective_column (filename, 3, 100));
1449 :
1450 64 : edit.add_fixits (&insert_b);
1451 64 : edit.add_fixits (&replace_a);
1452 64 : edit.add_fixits (&replace_b);
1453 :
1454 64 : if (c17 <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1455 : {
1456 64 : auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
1457 64 : ASSERT_STREQ ("/* before */\n"
1458 : "foo = /* alpha */pub.meadow;/* beta */\n"
1459 : "/* after */\n",
1460 : new_content);
1461 :
1462 : /* Verify diff. */
1463 64 : auto_free <char *> diff = edit.generate_diff (false);
1464 64 : ASSERT_STREQ ("@@ -1,3 +1,3 @@\n"
1465 : " /* before */\n"
1466 : "-foo = bar.field;\n"
1467 : "+foo = /* alpha */pub.meadow;/* beta */\n"
1468 : " /* after */\n", diff);
1469 64 : }
1470 96 : }
1471 :
1472 : /* Subroutine of test_applying_fixits_multiple_lines.
1473 : Add the text "CHANGED: " to the front of the given line. */
1474 :
1475 : static location_t
1476 576 : change_line (change_set &edit, int line_num)
1477 : {
1478 576 : const line_map_ordinary *ord_map
1479 576 : = LINEMAPS_LAST_ORDINARY_MAP (line_table);
1480 576 : const int column = 1;
1481 576 : location_t loc =
1482 576 : linemap_position_for_line_and_column (line_table, ord_map,
1483 : line_num, column);
1484 :
1485 576 : expanded_location exploc = expand_location (loc);
1486 576 : if (loc <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1487 : {
1488 344 : ASSERT_EQ (line_num, exploc.line);
1489 344 : ASSERT_EQ (column, exploc.column);
1490 : }
1491 :
1492 576 : rich_location insert (line_table, loc);
1493 576 : insert.add_fixit_insert_before ("CHANGED: ");
1494 576 : edit.add_fixits (&insert);
1495 1152 : return loc;
1496 576 : }
1497 :
1498 : /* Subroutine of test_applying_fixits_multiple_lines.
1499 : Add the text "INSERTED\n" in front of the given line. */
1500 :
1501 : static location_t
1502 96 : insert_line (change_set &edit, int line_num)
1503 : {
1504 96 : const line_map_ordinary *ord_map
1505 96 : = LINEMAPS_LAST_ORDINARY_MAP (line_table);
1506 96 : const int column = 1;
1507 96 : location_t loc =
1508 96 : linemap_position_for_line_and_column (line_table, ord_map,
1509 : line_num, column);
1510 :
1511 96 : expanded_location exploc = expand_location (loc);
1512 96 : if (loc <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1513 : {
1514 56 : ASSERT_EQ (line_num, exploc.line);
1515 56 : ASSERT_EQ (column, exploc.column);
1516 : }
1517 :
1518 96 : rich_location insert (line_table, loc);
1519 96 : insert.add_fixit_insert_before ("INSERTED\n");
1520 96 : edit.add_fixits (&insert);
1521 192 : return loc;
1522 96 : }
1523 :
1524 : /* Test of editing multiple lines within a long file,
1525 : to ensure that diffs are generated as expected. */
1526 :
1527 : static void
1528 96 : test_applying_fixits_multiple_lines (const line_table_case &case_)
1529 : {
1530 : /* Create a tempfile and write many lines of text to it. */
1531 96 : named_temp_file tmp (".txt");
1532 96 : const char *filename = tmp.get_filename ();
1533 96 : FILE *f = fopen (filename, "w");
1534 96 : ASSERT_NE (f, NULL);
1535 96096 : for (int i = 1; i <= 1000; i++)
1536 96000 : fprintf (f, "line %i\n", i);
1537 96 : fclose (f);
1538 :
1539 96 : line_table_test ltt (case_);
1540 96 : linemap_add (line_table, LC_ENTER, false, filename, 1);
1541 96 : linemap_position_for_column (line_table, 127);
1542 :
1543 96 : file_cache fc;
1544 96 : change_set edit (fc);
1545 :
1546 : /* A run of consecutive lines. */
1547 96 : change_line (edit, 2);
1548 96 : change_line (edit, 3);
1549 96 : change_line (edit, 4);
1550 96 : insert_line (edit, 5);
1551 :
1552 : /* A run of nearby lines, within the contextual limit. */
1553 96 : change_line (edit, 150);
1554 96 : change_line (edit, 151);
1555 96 : location_t last_loc = change_line (edit, 153);
1556 :
1557 96 : if (last_loc > LINE_MAP_MAX_LOCATION_WITH_COLS)
1558 40 : return;
1559 :
1560 : /* Verify diff. */
1561 56 : auto_free <char *> diff = edit.generate_diff (false);
1562 56 : ASSERT_STREQ ("@@ -1,7 +1,8 @@\n"
1563 : " line 1\n"
1564 : "-line 2\n"
1565 : "-line 3\n"
1566 : "-line 4\n"
1567 : "+CHANGED: line 2\n"
1568 : "+CHANGED: line 3\n"
1569 : "+CHANGED: line 4\n"
1570 : "+INSERTED\n"
1571 : " line 5\n"
1572 : " line 6\n"
1573 : " line 7\n"
1574 : "@@ -147,10 +148,10 @@\n"
1575 : " line 147\n"
1576 : " line 148\n"
1577 : " line 149\n"
1578 : "-line 150\n"
1579 : "-line 151\n"
1580 : "+CHANGED: line 150\n"
1581 : "+CHANGED: line 151\n"
1582 : " line 152\n"
1583 : "-line 153\n"
1584 : "+CHANGED: line 153\n"
1585 : " line 154\n"
1586 : " line 155\n"
1587 : " line 156\n", diff);
1588 :
1589 : /* Ensure tmp stays alive until this point, so that the tempfile
1590 : persists until after the generate_diff call. */
1591 56 : tmp.get_filename ();
1592 96 : }
1593 :
1594 : /* Test of converting an initializer for a named field from
1595 : the old GCC extension to C99 syntax.
1596 : Exercises a shrinking replacement followed by a growing
1597 : replacement on the same line. */
1598 :
1599 : static void
1600 96 : test_applying_fixits_modernize_named_init (const line_table_case &case_)
1601 : {
1602 : /* Create a tempfile and write some text to it.
1603 : .........................00000000011111111.
1604 : .........................12345678901234567. */
1605 96 : const char *old_content = ("/* before */\n"
1606 : "bar : 1,\n"
1607 : "/* after */\n");
1608 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
1609 96 : const char *filename = tmp.get_filename ();
1610 96 : line_table_test ltt (case_);
1611 96 : linemap_add (line_table, LC_ENTER, false, filename, 2);
1612 :
1613 96 : location_t c1 = linemap_position_for_column (line_table, 1);
1614 96 : location_t c3 = linemap_position_for_column (line_table, 3);
1615 96 : location_t c8 = linemap_position_for_column (line_table, 8);
1616 :
1617 96 : if (c8 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1618 32 : return;
1619 :
1620 : /* Replace "bar" with ".". */
1621 64 : rich_location r1 (line_table, c8);
1622 64 : r1.add_fixit_replace (source_range::from_locations (c1, c3),
1623 : ".");
1624 :
1625 : /* Replace ":" with "bar =". */
1626 64 : rich_location r2 (line_table, c8);
1627 64 : r2.add_fixit_replace (source_range::from_locations (c8, c8),
1628 : "bar =");
1629 :
1630 : /* The order should not matter. Do r1 then r2. */
1631 64 : {
1632 64 : file_cache fc;
1633 64 : change_set edit (fc);
1634 64 : edit.add_fixits (&r1);
1635 :
1636 : /* Verify state after first replacement. */
1637 64 : {
1638 64 : auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
1639 : /* We should now have:
1640 : ............00000000011.
1641 : ............12345678901. */
1642 64 : ASSERT_STREQ ("/* before */\n"
1643 : ". : 1,\n"
1644 : "/* after */\n",
1645 : new_content);
1646 : /* Location of the "1". */
1647 64 : ASSERT_EQ (6, edit.get_effective_column (filename, 2, 8));
1648 : /* Location of the ",". */
1649 64 : ASSERT_EQ (9, edit.get_effective_column (filename, 2, 11));
1650 64 : }
1651 :
1652 64 : edit.add_fixits (&r2);
1653 :
1654 64 : auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
1655 : /* Verify state after second replacement.
1656 : ............00000000011111111.
1657 : ............12345678901234567. */
1658 64 : ASSERT_STREQ ("/* before */\n"
1659 : ". bar = 1,\n"
1660 : "/* after */\n",
1661 : new_content);
1662 64 : }
1663 :
1664 : /* Try again, doing r2 then r1; the new_content should be the same. */
1665 64 : {
1666 64 : file_cache fc;
1667 64 : change_set edit (fc);
1668 64 : edit.add_fixits (&r2);
1669 64 : edit.add_fixits (&r1);
1670 64 : auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
1671 : /*.............00000000011111111.
1672 : .............12345678901234567. */
1673 64 : ASSERT_STREQ ("/* before */\n"
1674 : ". bar = 1,\n"
1675 : "/* after */\n",
1676 : new_content);
1677 64 : }
1678 96 : }
1679 :
1680 : /* Test of a fixit affecting a file that can't be read. */
1681 :
1682 : static void
1683 4 : test_applying_fixits_unreadable_file ()
1684 : {
1685 4 : const char *filename = "this-does-not-exist.txt";
1686 4 : line_table_test ltt;
1687 4 : linemap_add (line_table, LC_ENTER, false, filename, 1);
1688 :
1689 4 : location_t loc = linemap_position_for_column (line_table, 1);
1690 :
1691 4 : rich_location insert (line_table, loc);
1692 4 : insert.add_fixit_insert_before ("change 1");
1693 4 : insert.add_fixit_insert_before ("change 2");
1694 :
1695 4 : file_cache fc;
1696 4 : change_set edit (fc);
1697 : /* Attempting to add the fixits affecting the unreadable file
1698 : should transition the edit from valid to invalid. */
1699 4 : ASSERT_TRUE (edit.valid_p ());
1700 4 : edit.add_fixits (&insert);
1701 4 : ASSERT_FALSE (edit.valid_p ());
1702 4 : ASSERT_EQ (NULL, edit.get_content (filename));
1703 4 : ASSERT_EQ (NULL, edit.generate_diff (false));
1704 4 : }
1705 :
1706 : /* Verify that we gracefully handle an attempt to edit a line
1707 : that's beyond the end of the file. */
1708 :
1709 : static void
1710 4 : test_applying_fixits_line_out_of_range ()
1711 : {
1712 : /* Create a tempfile and write some text to it.
1713 : ........................00000000011111111.
1714 : ........................12345678901234567. */
1715 4 : const char *old_content = "One-liner file\n";
1716 4 : temp_source_file tmp (SELFTEST_LOCATION, ".txt", old_content);
1717 4 : const char *filename = tmp.get_filename ();
1718 4 : line_table_test ltt;
1719 4 : linemap_add (line_table, LC_ENTER, false, filename, 2);
1720 :
1721 : /* Try to insert a string in line 2. */
1722 4 : location_t loc = linemap_position_for_column (line_table, 1);
1723 :
1724 4 : rich_location insert (line_table, loc);
1725 4 : insert.add_fixit_insert_before ("change");
1726 :
1727 : /* Verify that attempting the insertion puts an change_set
1728 : into an invalid state. */
1729 4 : file_cache fc;
1730 4 : change_set edit (fc);
1731 4 : ASSERT_TRUE (edit.valid_p ());
1732 4 : edit.add_fixits (&insert);
1733 4 : ASSERT_FALSE (edit.valid_p ());
1734 4 : ASSERT_EQ (NULL, edit.get_content (filename));
1735 4 : ASSERT_EQ (NULL, edit.generate_diff (false));
1736 4 : }
1737 :
1738 : /* Verify the boundary conditions of column values in fix-it
1739 : hints applied to change_set instances. */
1740 :
1741 : static void
1742 96 : test_applying_fixits_column_validation (const line_table_case &case_)
1743 : {
1744 : /* Create a tempfile and write some text to it.
1745 : ........................00000000011111111.
1746 : ........................12345678901234567. */
1747 96 : const char *old_content = "One-liner file\n";
1748 96 : temp_source_file tmp (SELFTEST_LOCATION, ".txt", old_content);
1749 96 : const char *filename = tmp.get_filename ();
1750 96 : line_table_test ltt (case_);
1751 96 : linemap_add (line_table, LC_ENTER, false, filename, 1);
1752 :
1753 96 : location_t c11 = linemap_position_for_column (line_table, 11);
1754 96 : location_t c14 = linemap_position_for_column (line_table, 14);
1755 96 : location_t c15 = linemap_position_for_column (line_table, 15);
1756 96 : location_t c16 = linemap_position_for_column (line_table, 16);
1757 :
1758 : /* Verify limits of valid columns in insertion fixits. */
1759 :
1760 : /* Verify inserting at the end of the line. */
1761 96 : {
1762 96 : rich_location richloc (line_table, c11);
1763 96 : richloc.add_fixit_insert_before (c15, " change");
1764 :
1765 : /* Col 15 is at the end of the line, so the insertion
1766 : should succeed. */
1767 96 : file_cache fc;
1768 96 : change_set edit (fc);
1769 96 : edit.add_fixits (&richloc);
1770 96 : auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
1771 96 : if (c15 <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1772 64 : ASSERT_STREQ ("One-liner file change\n", new_content);
1773 : else
1774 32 : ASSERT_EQ (NULL, new_content);
1775 96 : }
1776 :
1777 : /* Verify inserting beyond the end of the line. */
1778 96 : {
1779 96 : rich_location richloc (line_table, c11);
1780 96 : richloc.add_fixit_insert_before (c16, " change");
1781 :
1782 : /* Col 16 is beyond the end of the line, so the insertion
1783 : should fail gracefully. */
1784 96 : file_cache fc;
1785 96 : change_set edit (fc);
1786 96 : ASSERT_TRUE (edit.valid_p ());
1787 96 : edit.add_fixits (&richloc);
1788 96 : ASSERT_FALSE (edit.valid_p ());
1789 96 : ASSERT_EQ (NULL, edit.get_content (filename));
1790 96 : ASSERT_EQ (NULL, edit.generate_diff (false));
1791 96 : }
1792 :
1793 : /* Verify limits of valid columns in replacement fixits. */
1794 :
1795 : /* Verify replacing the end of the line. */
1796 96 : {
1797 96 : rich_location richloc (line_table, c11);
1798 96 : source_range range = source_range::from_locations (c11, c14);
1799 96 : richloc.add_fixit_replace (range, "change");
1800 :
1801 : /* Col 14 is at the end of the line, so the replacement
1802 : should succeed. */
1803 96 : file_cache fc;
1804 96 : change_set edit (fc);
1805 96 : edit.add_fixits (&richloc);
1806 96 : auto_free <char *> new_content = edit.get_content (tmp.get_filename ());
1807 96 : if (c14 <= LINE_MAP_MAX_LOCATION_WITH_COLS)
1808 64 : ASSERT_STREQ ("One-liner change\n", new_content);
1809 : else
1810 32 : ASSERT_EQ (NULL, new_content);
1811 96 : }
1812 :
1813 : /* Verify going beyond the end of the line. */
1814 96 : {
1815 96 : rich_location richloc (line_table, c11);
1816 96 : source_range range = source_range::from_locations (c11, c15);
1817 96 : richloc.add_fixit_replace (range, "change");
1818 :
1819 : /* Col 15 is after the end of the line, so the replacement
1820 : should fail; verify that the attempt fails gracefully. */
1821 96 : file_cache fc;
1822 96 : change_set edit (fc);
1823 96 : ASSERT_TRUE (edit.valid_p ());
1824 96 : edit.add_fixits (&richloc);
1825 96 : ASSERT_FALSE (edit.valid_p ());
1826 96 : ASSERT_EQ (NULL, edit.get_content (filename));
1827 96 : ASSERT_EQ (NULL, edit.generate_diff (false));
1828 96 : }
1829 96 : }
1830 :
1831 : static void
1832 4 : run_all_tests ()
1833 : {
1834 4 : test_get_content ();
1835 4 : for_each_line_table_case (test_applying_fixits_insert_before);
1836 4 : for_each_line_table_case (test_applying_fixits_insert_after);
1837 4 : for_each_line_table_case (test_applying_fixits_insert_after_at_line_end);
1838 4 : for_each_line_table_case (test_applying_fixits_insert_after_failure);
1839 4 : for_each_line_table_case (test_applying_fixits_insert_containing_newline);
1840 4 : for_each_line_table_case (test_applying_fixits_growing_replace);
1841 4 : for_each_line_table_case (test_applying_fixits_shrinking_replace);
1842 4 : for_each_line_table_case (test_applying_fixits_replace_containing_newline);
1843 4 : for_each_line_table_case (test_applying_fixits_remove);
1844 4 : for_each_line_table_case (test_applying_fixits_multiple);
1845 4 : for_each_line_table_case (test_applying_fixits_multiple_lines);
1846 4 : for_each_line_table_case (test_applying_fixits_modernize_named_init);
1847 4 : test_applying_fixits_unreadable_file ();
1848 4 : test_applying_fixits_line_out_of_range ();
1849 4 : for_each_line_table_case (test_applying_fixits_column_validation);
1850 4 : }
1851 :
1852 : } // namespace diagnostics::changes::selftest
1853 :
1854 : #endif /* CHECKING_P */
1855 :
1856 : } // namespace diagnostics::changes
1857 :
1858 : #if CHECKING_P
1859 :
1860 : namespace selftest { // diagnostics::selftest
1861 :
1862 : /* Run all of the selftests within this file. */
1863 :
1864 : void
1865 4 : changes_cc_tests ()
1866 : {
1867 4 : diagnostics::changes::selftest::run_all_tests ();
1868 4 : }
1869 :
1870 : } // namespace selftest
1871 :
1872 : #endif /* CHECKING_P */
1873 :
1874 : } // namespace diagnostics
|