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