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