1 /* Diagnostic subroutines for printing source-code 2 Copyright (C) 1999-2018 Free Software Foundation, Inc. 3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com> 4 5 This file is part of GCC. 6 7 GCC is free software; you can redistribute it and/or modify it under 8 the terms of the GNU General Public License as published by the Free 9 Software Foundation; either version 3, or (at your option) any later 10 version. 11 12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13 WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GCC; see the file COPYING3. If not see 19 <http://www.gnu.org/licenses/>. */ 20 21 #include "config.h" 22 #include "system.h" 23 #include "coretypes.h" 24 #include "version.h" 25 #include "demangle.h" 26 #include "intl.h" 27 #include "backtrace.h" 28 #include "diagnostic.h" 29 #include "diagnostic-color.h" 30 #include "gcc-rich-location.h" 31 #include "selftest.h" 32 #include "selftest-diagnostic.h" 33 34 #ifdef HAVE_TERMIOS_H 35 # include <termios.h> 36 #endif 37 38 #ifdef GWINSZ_IN_SYS_IOCTL 39 # include <sys/ioctl.h> 40 #endif 41 42 /* Classes for rendering source code and diagnostics, within an 43 anonymous namespace. 44 The work is done by "class layout", which embeds and uses 45 "class colorizer" and "class layout_range" to get things done. */ 46 47 namespace { 48 49 /* The state at a given point of the source code, assuming that we're 50 in a range: which range are we in, and whether we should draw a caret at 51 this point. */ 52 53 struct point_state 54 { 55 int range_idx; 56 bool draw_caret_p; 57 }; 58 59 /* A class to inject colorization codes when printing the diagnostic locus. 60 61 It has one kind of colorization for each of: 62 - normal text 63 - range 0 (the "primary location") 64 - range 1 65 - range 2 66 67 The class caches the lookup of the color codes for the above. 68 69 The class also has responsibility for tracking which of the above is 70 active, filtering out unnecessary changes. This allows 71 layout::print_source_line and layout::print_annotation_line 72 to simply request a colorization code for *every* character they print, 73 via this class, and have the filtering be done for them here. */ 74 75 class colorizer 76 { 77 public: 78 colorizer (diagnostic_context *context, 79 diagnostic_t diagnostic_kind); 80 ~colorizer (); 81 82 void set_range (int range_idx) { set_state (range_idx); } 83 void set_normal_text () { set_state (STATE_NORMAL_TEXT); } 84 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); } 85 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); } 86 87 private: 88 void set_state (int state); 89 void begin_state (int state); 90 void finish_state (int state); 91 const char *get_color_by_name (const char *); 92 93 private: 94 static const int STATE_NORMAL_TEXT = -1; 95 static const int STATE_FIXIT_INSERT = -2; 96 static const int STATE_FIXIT_DELETE = -3; 97 98 diagnostic_context *m_context; 99 diagnostic_t m_diagnostic_kind; 100 int m_current_state; 101 const char *m_range1; 102 const char *m_range2; 103 const char *m_fixit_insert; 104 const char *m_fixit_delete; 105 const char *m_stop_color; 106 }; 107 108 /* A point within a layout_range; similar to an expanded_location, 109 but after filtering on file. */ 110 111 class layout_point 112 { 113 public: 114 layout_point (const expanded_location &exploc) 115 : m_line (exploc.line), 116 m_column (exploc.column) {} 117 118 linenum_type m_line; 119 int m_column; 120 }; 121 122 /* A class for use by "class layout" below: a filtered location_range. */ 123 124 class layout_range 125 { 126 public: 127 layout_range (const expanded_location *start_exploc, 128 const expanded_location *finish_exploc, 129 bool show_caret_p, 130 const expanded_location *caret_exploc); 131 132 bool contains_point (linenum_type row, int column) const; 133 bool intersects_line_p (linenum_type row) const; 134 135 layout_point m_start; 136 layout_point m_finish; 137 bool m_show_caret_p; 138 layout_point m_caret; 139 }; 140 141 /* A struct for use by layout::print_source_line for telling 142 layout::print_annotation_line the extents of the source line that 143 it printed, so that underlines can be clipped appropriately. */ 144 145 struct line_bounds 146 { 147 int m_first_non_ws; 148 int m_last_non_ws; 149 }; 150 151 /* A range of contiguous source lines within a layout (e.g. "lines 5-10" 152 or "line 23"). During the layout ctor, layout::calculate_line_spans 153 splits the pertinent source lines into a list of disjoint line_span 154 instances (e.g. lines 5-10, lines 15-20, line 23). */ 155 156 struct line_span 157 { 158 line_span (linenum_type first_line, linenum_type last_line) 159 : m_first_line (first_line), m_last_line (last_line) 160 { 161 gcc_assert (first_line <= last_line); 162 } 163 linenum_type get_first_line () const { return m_first_line; } 164 linenum_type get_last_line () const { return m_last_line; } 165 166 bool contains_line_p (linenum_type line) const 167 { 168 return line >= m_first_line && line <= m_last_line; 169 } 170 171 static int comparator (const void *p1, const void *p2) 172 { 173 const line_span *ls1 = (const line_span *)p1; 174 const line_span *ls2 = (const line_span *)p2; 175 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line); 176 if (first_line_cmp) 177 return first_line_cmp; 178 return compare (ls1->m_last_line, ls2->m_last_line); 179 } 180 181 linenum_type m_first_line; 182 linenum_type m_last_line; 183 }; 184 185 #if CHECKING_P 186 187 /* Selftests for line_span. */ 188 189 static void 190 test_line_span () 191 { 192 line_span line_one (1, 1); 193 ASSERT_EQ (1, line_one.get_first_line ()); 194 ASSERT_EQ (1, line_one.get_last_line ()); 195 ASSERT_FALSE (line_one.contains_line_p (0)); 196 ASSERT_TRUE (line_one.contains_line_p (1)); 197 ASSERT_FALSE (line_one.contains_line_p (2)); 198 199 line_span lines_1_to_3 (1, 3); 200 ASSERT_EQ (1, lines_1_to_3.get_first_line ()); 201 ASSERT_EQ (3, lines_1_to_3.get_last_line ()); 202 ASSERT_TRUE (lines_1_to_3.contains_line_p (1)); 203 ASSERT_TRUE (lines_1_to_3.contains_line_p (3)); 204 205 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one)); 206 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0); 207 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0); 208 209 /* A linenum > 2^31. */ 210 const linenum_type LARGEST_LINE = 0xffffffff; 211 line_span largest_line (LARGEST_LINE, LARGEST_LINE); 212 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ()); 213 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ()); 214 215 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0); 216 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0); 217 } 218 219 #endif /* #if CHECKING_P */ 220 221 /* A class to control the overall layout when printing a diagnostic. 222 223 The layout is determined within the constructor. 224 It is then printed by repeatedly calling the "print_source_line", 225 "print_annotation_line" and "print_any_fixits" methods. 226 227 We assume we have disjoint ranges. */ 228 229 class layout 230 { 231 public: 232 layout (diagnostic_context *context, 233 rich_location *richloc, 234 diagnostic_t diagnostic_kind); 235 236 bool maybe_add_location_range (const location_range *loc_range, 237 bool restrict_to_current_line_spans); 238 239 int get_num_line_spans () const { return m_line_spans.length (); } 240 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; } 241 242 bool print_heading_for_line_span_index_p (int line_span_idx) const; 243 244 expanded_location get_expanded_location (const line_span *) const; 245 246 void print_line (linenum_type row); 247 248 private: 249 bool will_show_line_p (linenum_type row) const; 250 void print_leading_fixits (linenum_type row); 251 void print_source_line (linenum_type row, const char *line, int line_width, 252 line_bounds *lbounds_out); 253 bool should_print_annotation_line_p (linenum_type row) const; 254 void print_annotation_line (linenum_type row, const line_bounds lbounds); 255 void print_trailing_fixits (linenum_type row); 256 257 bool annotation_line_showed_range_p (linenum_type line, int start_column, 258 int finish_column) const; 259 void show_ruler (int max_column) const; 260 261 bool validate_fixit_hint_p (const fixit_hint *hint); 262 263 void calculate_line_spans (); 264 265 void print_newline (); 266 267 bool 268 get_state_at_point (/* Inputs. */ 269 linenum_type row, int column, 270 int first_non_ws, int last_non_ws, 271 /* Outputs. */ 272 point_state *out_state); 273 274 int 275 get_x_bound_for_row (linenum_type row, int caret_column, 276 int last_non_ws); 277 278 void 279 move_to_column (int *column, int dest_column); 280 281 private: 282 diagnostic_context *m_context; 283 pretty_printer *m_pp; 284 diagnostic_t m_diagnostic_kind; 285 location_t m_primary_loc; 286 expanded_location m_exploc; 287 colorizer m_colorizer; 288 bool m_colorize_source_p; 289 auto_vec <layout_range> m_layout_ranges; 290 auto_vec <const fixit_hint *> m_fixit_hints; 291 auto_vec <line_span> m_line_spans; 292 int m_x_offset; 293 }; 294 295 /* Implementation of "class colorizer". */ 296 297 /* The constructor for "colorizer". Lookup and store color codes for the 298 different kinds of things we might need to print. */ 299 300 colorizer::colorizer (diagnostic_context *context, 301 diagnostic_t diagnostic_kind) : 302 m_context (context), 303 m_diagnostic_kind (diagnostic_kind), 304 m_current_state (STATE_NORMAL_TEXT) 305 { 306 m_range1 = get_color_by_name ("range1"); 307 m_range2 = get_color_by_name ("range2"); 308 m_fixit_insert = get_color_by_name ("fixit-insert"); 309 m_fixit_delete = get_color_by_name ("fixit-delete"); 310 m_stop_color = colorize_stop (pp_show_color (context->printer)); 311 } 312 313 /* The destructor for "colorize". If colorization is on, print a code to 314 turn it off. */ 315 316 colorizer::~colorizer () 317 { 318 finish_state (m_current_state); 319 } 320 321 /* Update state, printing color codes if necessary if there's a state 322 change. */ 323 324 void 325 colorizer::set_state (int new_state) 326 { 327 if (m_current_state != new_state) 328 { 329 finish_state (m_current_state); 330 m_current_state = new_state; 331 begin_state (new_state); 332 } 333 } 334 335 /* Turn on any colorization for STATE. */ 336 337 void 338 colorizer::begin_state (int state) 339 { 340 switch (state) 341 { 342 case STATE_NORMAL_TEXT: 343 break; 344 345 case STATE_FIXIT_INSERT: 346 pp_string (m_context->printer, m_fixit_insert); 347 break; 348 349 case STATE_FIXIT_DELETE: 350 pp_string (m_context->printer, m_fixit_delete); 351 break; 352 353 case 0: 354 /* Make range 0 be the same color as the "kind" text 355 (error vs warning vs note). */ 356 pp_string 357 (m_context->printer, 358 colorize_start (pp_show_color (m_context->printer), 359 diagnostic_get_color_for_kind (m_diagnostic_kind))); 360 break; 361 362 case 1: 363 pp_string (m_context->printer, m_range1); 364 break; 365 366 case 2: 367 pp_string (m_context->printer, m_range2); 368 break; 369 370 default: 371 /* For ranges beyond 2, alternate between color 1 and color 2. */ 372 { 373 gcc_assert (state > 2); 374 pp_string (m_context->printer, 375 state % 2 ? m_range1 : m_range2); 376 } 377 break; 378 } 379 } 380 381 /* Turn off any colorization for STATE. */ 382 383 void 384 colorizer::finish_state (int state) 385 { 386 if (state != STATE_NORMAL_TEXT) 387 pp_string (m_context->printer, m_stop_color); 388 } 389 390 /* Get the color code for NAME (or the empty string if 391 colorization is disabled). */ 392 393 const char * 394 colorizer::get_color_by_name (const char *name) 395 { 396 return colorize_start (pp_show_color (m_context->printer), name); 397 } 398 399 /* Implementation of class layout_range. */ 400 401 /* The constructor for class layout_range. 402 Initialize various layout_point fields from expanded_location 403 equivalents; we've already filtered on file. */ 404 405 layout_range::layout_range (const expanded_location *start_exploc, 406 const expanded_location *finish_exploc, 407 bool show_caret_p, 408 const expanded_location *caret_exploc) 409 : m_start (*start_exploc), 410 m_finish (*finish_exploc), 411 m_show_caret_p (show_caret_p), 412 m_caret (*caret_exploc) 413 { 414 } 415 416 /* Is (column, row) within the given range? 417 We've already filtered on the file. 418 419 Ranges are closed (both limits are within the range). 420 421 Example A: a single-line range: 422 start: (col=22, line=2) 423 finish: (col=38, line=2) 424 425 |00000011111111112222222222333333333344444444444 426 |34567890123456789012345678901234567890123456789 427 --+----------------------------------------------- 428 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 429 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa 430 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 431 432 Example B: a multiline range with 433 start: (col=14, line=3) 434 finish: (col=08, line=5) 435 436 |00000011111111112222222222333333333344444444444 437 |34567890123456789012345678901234567890123456789 438 --+----------------------------------------------- 439 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 440 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 441 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww 442 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww 443 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 444 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 445 --+----------------------------------------------- 446 447 Legend: 448 - 'b' indicates a point *before* the range 449 - 'S' indicates the start of the range 450 - 'w' indicates a point within the range 451 - 'F' indicates the finish of the range (which is 452 within it). 453 - 'a' indicates a subsequent point *after* the range. */ 454 455 bool 456 layout_range::contains_point (linenum_type row, int column) const 457 { 458 gcc_assert (m_start.m_line <= m_finish.m_line); 459 /* ...but the equivalent isn't true for the columns; 460 consider example B in the comment above. */ 461 462 if (row < m_start.m_line) 463 /* Points before the first line of the range are 464 outside it (corresponding to line 01 in example A 465 and lines 01 and 02 in example B above). */ 466 return false; 467 468 if (row == m_start.m_line) 469 /* On same line as start of range (corresponding 470 to line 02 in example A and line 03 in example B). */ 471 { 472 if (column < m_start.m_column) 473 /* Points on the starting line of the range, but 474 before the column in which it begins. */ 475 return false; 476 477 if (row < m_finish.m_line) 478 /* This is a multiline range; the point 479 is within it (corresponds to line 03 in example B 480 from column 14 onwards) */ 481 return true; 482 else 483 { 484 /* This is a single-line range. */ 485 gcc_assert (row == m_finish.m_line); 486 return column <= m_finish.m_column; 487 } 488 } 489 490 /* The point is in a line beyond that containing the 491 start of the range: lines 03 onwards in example A, 492 and lines 04 onwards in example B. */ 493 gcc_assert (row > m_start.m_line); 494 495 if (row > m_finish.m_line) 496 /* The point is beyond the final line of the range 497 (lines 03 onwards in example A, and lines 06 onwards 498 in example B). */ 499 return false; 500 501 if (row < m_finish.m_line) 502 { 503 /* The point is in a line that's fully within a multiline 504 range (e.g. line 04 in example B). */ 505 gcc_assert (m_start.m_line < m_finish.m_line); 506 return true; 507 } 508 509 gcc_assert (row == m_finish.m_line); 510 511 return column <= m_finish.m_column; 512 } 513 514 /* Does this layout_range contain any part of line ROW? */ 515 516 bool 517 layout_range::intersects_line_p (linenum_type row) const 518 { 519 gcc_assert (m_start.m_line <= m_finish.m_line); 520 if (row < m_start.m_line) 521 return false; 522 if (row > m_finish.m_line) 523 return false; 524 return true; 525 } 526 527 #if CHECKING_P 528 529 /* A helper function for testing layout_range. */ 530 531 static layout_range 532 make_range (int start_line, int start_col, int end_line, int end_col) 533 { 534 const expanded_location start_exploc 535 = {"test.c", start_line, start_col, NULL, false}; 536 const expanded_location finish_exploc 537 = {"test.c", end_line, end_col, NULL, false}; 538 return layout_range (&start_exploc, &finish_exploc, false, 539 &start_exploc); 540 } 541 542 /* Selftests for layout_range::contains_point and 543 layout_range::intersects_line_p. */ 544 545 /* Selftest for layout_range, where the layout_range 546 is a range with start==end i.e. a single point. */ 547 548 static void 549 test_layout_range_for_single_point () 550 { 551 layout_range point = make_range (7, 10, 7, 10); 552 553 /* Tests for layout_range::contains_point. */ 554 555 /* Before the line. */ 556 ASSERT_FALSE (point.contains_point (6, 1)); 557 558 /* On the line, but before start. */ 559 ASSERT_FALSE (point.contains_point (7, 9)); 560 561 /* At the point. */ 562 ASSERT_TRUE (point.contains_point (7, 10)); 563 564 /* On the line, after the point. */ 565 ASSERT_FALSE (point.contains_point (7, 11)); 566 567 /* After the line. */ 568 ASSERT_FALSE (point.contains_point (8, 1)); 569 570 /* Tests for layout_range::intersects_line_p. */ 571 ASSERT_FALSE (point.intersects_line_p (6)); 572 ASSERT_TRUE (point.intersects_line_p (7)); 573 ASSERT_FALSE (point.intersects_line_p (8)); 574 } 575 576 /* Selftest for layout_range, where the layout_range 577 is the single-line range shown as "Example A" above. */ 578 579 static void 580 test_layout_range_for_single_line () 581 { 582 layout_range example_a = make_range (2, 22, 2, 38); 583 584 /* Tests for layout_range::contains_point. */ 585 586 /* Before the line. */ 587 ASSERT_FALSE (example_a.contains_point (1, 1)); 588 589 /* On the line, but before start. */ 590 ASSERT_FALSE (example_a.contains_point (2, 21)); 591 592 /* On the line, at the start. */ 593 ASSERT_TRUE (example_a.contains_point (2, 22)); 594 595 /* On the line, within the range. */ 596 ASSERT_TRUE (example_a.contains_point (2, 23)); 597 598 /* On the line, at the end. */ 599 ASSERT_TRUE (example_a.contains_point (2, 38)); 600 601 /* On the line, after the end. */ 602 ASSERT_FALSE (example_a.contains_point (2, 39)); 603 604 /* After the line. */ 605 ASSERT_FALSE (example_a.contains_point (2, 39)); 606 607 /* Tests for layout_range::intersects_line_p. */ 608 ASSERT_FALSE (example_a.intersects_line_p (1)); 609 ASSERT_TRUE (example_a.intersects_line_p (2)); 610 ASSERT_FALSE (example_a.intersects_line_p (3)); 611 } 612 613 /* Selftest for layout_range, where the layout_range 614 is the multi-line range shown as "Example B" above. */ 615 616 static void 617 test_layout_range_for_multiple_lines () 618 { 619 layout_range example_b = make_range (3, 14, 5, 8); 620 621 /* Tests for layout_range::contains_point. */ 622 623 /* Before first line. */ 624 ASSERT_FALSE (example_b.contains_point (1, 1)); 625 626 /* On the first line, but before start. */ 627 ASSERT_FALSE (example_b.contains_point (3, 13)); 628 629 /* At the start. */ 630 ASSERT_TRUE (example_b.contains_point (3, 14)); 631 632 /* On the first line, within the range. */ 633 ASSERT_TRUE (example_b.contains_point (3, 15)); 634 635 /* On an interior line. 636 The column number should not matter; try various boundary 637 values. */ 638 ASSERT_TRUE (example_b.contains_point (4, 1)); 639 ASSERT_TRUE (example_b.contains_point (4, 7)); 640 ASSERT_TRUE (example_b.contains_point (4, 8)); 641 ASSERT_TRUE (example_b.contains_point (4, 9)); 642 ASSERT_TRUE (example_b.contains_point (4, 13)); 643 ASSERT_TRUE (example_b.contains_point (4, 14)); 644 ASSERT_TRUE (example_b.contains_point (4, 15)); 645 646 /* On the final line, before the end. */ 647 ASSERT_TRUE (example_b.contains_point (5, 7)); 648 649 /* On the final line, at the end. */ 650 ASSERT_TRUE (example_b.contains_point (5, 8)); 651 652 /* On the final line, after the end. */ 653 ASSERT_FALSE (example_b.contains_point (5, 9)); 654 655 /* After the line. */ 656 ASSERT_FALSE (example_b.contains_point (6, 1)); 657 658 /* Tests for layout_range::intersects_line_p. */ 659 ASSERT_FALSE (example_b.intersects_line_p (2)); 660 ASSERT_TRUE (example_b.intersects_line_p (3)); 661 ASSERT_TRUE (example_b.intersects_line_p (4)); 662 ASSERT_TRUE (example_b.intersects_line_p (5)); 663 ASSERT_FALSE (example_b.intersects_line_p (6)); 664 } 665 666 #endif /* #if CHECKING_P */ 667 668 /* Given a source line LINE of length LINE_WIDTH, determine the width 669 without any trailing whitespace. */ 670 671 static int 672 get_line_width_without_trailing_whitespace (const char *line, int line_width) 673 { 674 int result = line_width; 675 while (result > 0) 676 { 677 char ch = line[result - 1]; 678 if (ch == ' ' || ch == '\t' || ch == '\r') 679 result--; 680 else 681 break; 682 } 683 gcc_assert (result >= 0); 684 gcc_assert (result <= line_width); 685 gcc_assert (result == 0 || 686 (line[result - 1] != ' ' 687 && line[result -1] != '\t' 688 && line[result -1] != '\r')); 689 return result; 690 } 691 692 #if CHECKING_P 693 694 /* A helper function for testing get_line_width_without_trailing_whitespace. */ 695 696 static void 697 assert_eq (const char *line, int expected_width) 698 { 699 int actual_value 700 = get_line_width_without_trailing_whitespace (line, strlen (line)); 701 ASSERT_EQ (actual_value, expected_width); 702 } 703 704 /* Verify that get_line_width_without_trailing_whitespace is sane for 705 various inputs. It is not required to handle newlines. */ 706 707 static void 708 test_get_line_width_without_trailing_whitespace () 709 { 710 assert_eq ("", 0); 711 assert_eq (" ", 0); 712 assert_eq ("\t", 0); 713 assert_eq ("\r", 0); 714 assert_eq ("hello world", 11); 715 assert_eq ("hello world ", 11); 716 assert_eq ("hello world \t\t ", 11); 717 assert_eq ("hello world\r", 11); 718 } 719 720 #endif /* #if CHECKING_P */ 721 722 /* Helper function for layout's ctor, for sanitizing locations relative 723 to the primary location within a diagnostic. 724 725 Compare LOC_A and LOC_B to see if it makes sense to print underlines 726 connecting their expanded locations. Doing so is only guaranteed to 727 make sense if the locations share the same macro expansion "history" 728 i.e. they can be traced through the same macro expansions, eventually 729 reaching an ordinary map. 730 731 This may be too strong a condition, but it effectively sanitizes 732 PR c++/70105, which has an example of printing an expression where the 733 final location of the expression is in a different macro, which 734 erroneously was leading to hundreds of lines of irrelevant source 735 being printed. */ 736 737 static bool 738 compatible_locations_p (location_t loc_a, location_t loc_b) 739 { 740 if (IS_ADHOC_LOC (loc_a)) 741 loc_a = get_location_from_adhoc_loc (line_table, loc_a); 742 if (IS_ADHOC_LOC (loc_b)) 743 loc_b = get_location_from_adhoc_loc (line_table, loc_b); 744 745 /* If either location is one of the special locations outside of a 746 linemap, they are only compatible if they are equal. */ 747 if (loc_a < RESERVED_LOCATION_COUNT 748 || loc_b < RESERVED_LOCATION_COUNT) 749 return loc_a == loc_b; 750 751 const line_map *map_a = linemap_lookup (line_table, loc_a); 752 linemap_assert (map_a); 753 754 const line_map *map_b = linemap_lookup (line_table, loc_b); 755 linemap_assert (map_b); 756 757 /* Are they within the same map? */ 758 if (map_a == map_b) 759 { 760 /* Are both within the same macro expansion? */ 761 if (linemap_macro_expansion_map_p (map_a)) 762 { 763 /* Expand each location towards the spelling location, and 764 recurse. */ 765 const line_map_macro *macro_map = linemap_check_macro (map_a); 766 source_location loc_a_toward_spelling 767 = linemap_macro_map_loc_unwind_toward_spelling (line_table, 768 macro_map, 769 loc_a); 770 source_location loc_b_toward_spelling 771 = linemap_macro_map_loc_unwind_toward_spelling (line_table, 772 macro_map, 773 loc_b); 774 return compatible_locations_p (loc_a_toward_spelling, 775 loc_b_toward_spelling); 776 } 777 778 /* Otherwise they are within the same ordinary map. */ 779 return true; 780 } 781 else 782 { 783 /* Within different maps. */ 784 785 /* If either is within a macro expansion, they are incompatible. */ 786 if (linemap_macro_expansion_map_p (map_a) 787 || linemap_macro_expansion_map_p (map_b)) 788 return false; 789 790 /* Within two different ordinary maps; they are compatible iff they 791 are in the same file. */ 792 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a); 793 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b); 794 return ord_map_a->to_file == ord_map_b->to_file; 795 } 796 } 797 798 /* Comparator for sorting fix-it hints. */ 799 800 static int 801 fixit_cmp (const void *p_a, const void *p_b) 802 { 803 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a); 804 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b); 805 return hint_a->get_start_loc () - hint_b->get_start_loc (); 806 } 807 808 /* Implementation of class layout. */ 809 810 /* Constructor for class layout. 811 812 Filter the ranges from the rich_location to those that we can 813 sanely print, populating m_layout_ranges and m_fixit_hints. 814 Determine the range of lines that we will print, splitting them 815 up into an ordered list of disjoint spans of contiguous line numbers. 816 Determine m_x_offset, to ensure that the primary caret 817 will fit within the max_width provided by the diagnostic_context. */ 818 819 layout::layout (diagnostic_context * context, 820 rich_location *richloc, 821 diagnostic_t diagnostic_kind) 822 : m_context (context), 823 m_pp (context->printer), 824 m_diagnostic_kind (diagnostic_kind), 825 m_primary_loc (richloc->get_range (0)->m_loc), 826 m_exploc (richloc->get_expanded_location (0)), 827 m_colorizer (context, diagnostic_kind), 828 m_colorize_source_p (context->colorize_source_p), 829 m_layout_ranges (richloc->get_num_locations ()), 830 m_fixit_hints (richloc->get_num_fixit_hints ()), 831 m_line_spans (1 + richloc->get_num_locations ()), 832 m_x_offset (0) 833 { 834 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++) 835 { 836 /* This diagnostic printer can only cope with "sufficiently sane" ranges. 837 Ignore any ranges that are awkward to handle. */ 838 const location_range *loc_range = richloc->get_range (idx); 839 maybe_add_location_range (loc_range, false); 840 } 841 842 /* Populate m_fixit_hints, filtering to only those that are in the 843 same file. */ 844 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++) 845 { 846 const fixit_hint *hint = richloc->get_fixit_hint (i); 847 if (validate_fixit_hint_p (hint)) 848 m_fixit_hints.safe_push (hint); 849 } 850 851 /* Sort m_fixit_hints. */ 852 m_fixit_hints.qsort (fixit_cmp); 853 854 /* Populate m_line_spans. */ 855 calculate_line_spans (); 856 857 /* Adjust m_x_offset. 858 Center the primary caret to fit in max_width; all columns 859 will be adjusted accordingly. */ 860 int max_width = m_context->caret_max_width; 861 int line_width; 862 const char *line = location_get_source_line (m_exploc.file, m_exploc.line, 863 &line_width); 864 if (line && m_exploc.column <= line_width) 865 { 866 int right_margin = CARET_LINE_MARGIN; 867 int column = m_exploc.column; 868 right_margin = MIN (line_width - column, right_margin); 869 right_margin = max_width - right_margin; 870 if (line_width >= max_width && column > right_margin) 871 m_x_offset = column - right_margin; 872 gcc_assert (m_x_offset >= 0); 873 } 874 875 if (context->show_ruler_p) 876 show_ruler (m_x_offset + max_width); 877 } 878 879 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to 880 those that we can sanely print. 881 882 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also 883 filtered against this layout instance's current line spans: it 884 will only be added if the location is fully within the lines 885 already specified by other locations. 886 887 Return true iff LOC_RANGE was added. */ 888 889 bool 890 layout::maybe_add_location_range (const location_range *loc_range, 891 bool restrict_to_current_line_spans) 892 { 893 gcc_assert (loc_range); 894 895 /* Split the "range" into caret and range information. */ 896 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc); 897 898 /* Expand the various locations. */ 899 expanded_location start 900 = linemap_client_expand_location_to_spelling_point 901 (src_range.m_start, LOCATION_ASPECT_START); 902 expanded_location finish 903 = linemap_client_expand_location_to_spelling_point 904 (src_range.m_finish, LOCATION_ASPECT_FINISH); 905 expanded_location caret 906 = linemap_client_expand_location_to_spelling_point 907 (loc_range->m_loc, LOCATION_ASPECT_CARET); 908 909 /* If any part of the range isn't in the same file as the primary 910 location of this diagnostic, ignore the range. */ 911 if (start.file != m_exploc.file) 912 return false; 913 if (finish.file != m_exploc.file) 914 return false; 915 if (loc_range->m_show_caret_p) 916 if (caret.file != m_exploc.file) 917 return false; 918 919 /* Sanitize the caret location for non-primary ranges. */ 920 if (m_layout_ranges.length () > 0) 921 if (loc_range->m_show_caret_p) 922 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc)) 923 /* Discard any non-primary ranges that can't be printed 924 sanely relative to the primary location. */ 925 return false; 926 927 /* Everything is now known to be in the correct source file, 928 but it may require further sanitization. */ 929 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret); 930 931 /* If we have a range that finishes before it starts (perhaps 932 from something built via macro expansion), printing the 933 range is likely to be nonsensical. Also, attempting to do so 934 breaks assumptions within the printing code (PR c/68473). 935 Similarly, don't attempt to print ranges if one or both ends 936 of the range aren't sane to print relative to the 937 primary location (PR c++/70105). */ 938 if (start.line > finish.line 939 || !compatible_locations_p (src_range.m_start, m_primary_loc) 940 || !compatible_locations_p (src_range.m_finish, m_primary_loc)) 941 { 942 /* Is this the primary location? */ 943 if (m_layout_ranges.length () == 0) 944 { 945 /* We want to print the caret for the primary location, but 946 we must sanitize away m_start and m_finish. */ 947 ri.m_start = ri.m_caret; 948 ri.m_finish = ri.m_caret; 949 } 950 else 951 /* This is a non-primary range; ignore it. */ 952 return false; 953 } 954 955 /* Potentially filter to just the lines already specified by other 956 locations. This is for use by gcc_rich_location::add_location_if_nearby. 957 The layout ctor doesn't use it, and can't because m_line_spans 958 hasn't been set up at that point. */ 959 if (restrict_to_current_line_spans) 960 { 961 if (!will_show_line_p (start.line)) 962 return false; 963 if (!will_show_line_p (finish.line)) 964 return false; 965 if (loc_range->m_show_caret_p) 966 if (!will_show_line_p (caret.line)) 967 return false; 968 } 969 970 /* Passed all the tests; add the range to m_layout_ranges so that 971 it will be printed. */ 972 m_layout_ranges.safe_push (ri); 973 return true; 974 } 975 976 /* Return true iff ROW is within one of the line spans for this layout. */ 977 978 bool 979 layout::will_show_line_p (linenum_type row) const 980 { 981 for (int line_span_idx = 0; line_span_idx < get_num_line_spans (); 982 line_span_idx++) 983 { 984 const line_span *line_span = get_line_span (line_span_idx); 985 if (line_span->contains_line_p (row)) 986 return true; 987 } 988 return false; 989 } 990 991 /* Return true iff we should print a heading when starting the 992 line span with the given index. */ 993 994 bool 995 layout::print_heading_for_line_span_index_p (int line_span_idx) const 996 { 997 /* We print a heading for every change of line span, hence for every 998 line span after the initial one. */ 999 if (line_span_idx > 0) 1000 return true; 1001 1002 /* We also do it for the initial span if the primary location of the 1003 diagnostic is in a different span. */ 1004 if (m_exploc.line > (int)get_line_span (0)->m_last_line) 1005 return true; 1006 1007 return false; 1008 } 1009 1010 /* Get an expanded_location for the first location of interest within 1011 the given line_span. 1012 Used when printing a heading to indicate a new line span. */ 1013 1014 expanded_location 1015 layout::get_expanded_location (const line_span *line_span) const 1016 { 1017 /* Whenever possible, use the caret location. */ 1018 if (line_span->contains_line_p (m_exploc.line)) 1019 return m_exploc; 1020 1021 /* Otherwise, use the start of the first range that's present 1022 within the line_span. */ 1023 for (unsigned int i = 0; i < m_layout_ranges.length (); i++) 1024 { 1025 const layout_range *lr = &m_layout_ranges[i]; 1026 if (line_span->contains_line_p (lr->m_start.m_line)) 1027 { 1028 expanded_location exploc = m_exploc; 1029 exploc.line = lr->m_start.m_line; 1030 exploc.column = lr->m_start.m_column; 1031 return exploc; 1032 } 1033 } 1034 1035 /* Otherwise, use the location of the first fixit-hint present within 1036 the line_span. */ 1037 for (unsigned int i = 0; i < m_fixit_hints.length (); i++) 1038 { 1039 const fixit_hint *hint = m_fixit_hints[i]; 1040 location_t loc = hint->get_start_loc (); 1041 expanded_location exploc = expand_location (loc); 1042 if (line_span->contains_line_p (exploc.line)) 1043 return exploc; 1044 } 1045 1046 /* It should not be possible to have a line span that didn't 1047 contain any of the layout_range or fixit_hint instances. */ 1048 gcc_unreachable (); 1049 return m_exploc; 1050 } 1051 1052 /* Determine if HINT is meaningful to print within this layout. */ 1053 1054 bool 1055 layout::validate_fixit_hint_p (const fixit_hint *hint) 1056 { 1057 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file) 1058 return false; 1059 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file) 1060 return false; 1061 1062 return true; 1063 } 1064 1065 /* Determine the range of lines affected by HINT. 1066 This assumes that HINT has already been filtered by 1067 validate_fixit_hint_p, and so affects the correct source file. */ 1068 1069 static line_span 1070 get_line_span_for_fixit_hint (const fixit_hint *hint) 1071 { 1072 gcc_assert (hint); 1073 return line_span (LOCATION_LINE (hint->get_start_loc ()), 1074 LOCATION_LINE (hint->get_next_loc ())); 1075 } 1076 1077 /* We want to print the pertinent source code at a diagnostic. The 1078 rich_location can contain multiple locations. This will have been 1079 filtered into m_exploc (the caret for the primary location) and 1080 m_layout_ranges, for those ranges within the same source file. 1081 1082 We will print a subset of the lines within the source file in question, 1083 as a collection of "spans" of lines. 1084 1085 This function populates m_line_spans with an ordered, disjoint list of 1086 the line spans of interest. 1087 1088 For example, if the primary caret location is on line 7, with ranges 1089 covering lines 5-6 and lines 9-12: 1090 1091 004 1092 005 |RANGE 0 1093 006 |RANGE 0 1094 007 |PRIMARY CARET 1095 008 1096 009 |RANGE 1 1097 010 |RANGE 1 1098 011 |RANGE 1 1099 012 |RANGE 1 1100 013 1101 1102 then we want two spans: lines 5-7 and lines 9-12. */ 1103 1104 void 1105 layout::calculate_line_spans () 1106 { 1107 /* This should only be called once, by the ctor. */ 1108 gcc_assert (m_line_spans.length () == 0); 1109 1110 /* Populate tmp_spans with individual spans, for each of 1111 m_exploc, and for m_layout_ranges. */ 1112 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ()); 1113 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line)); 1114 for (unsigned int i = 0; i < m_layout_ranges.length (); i++) 1115 { 1116 const layout_range *lr = &m_layout_ranges[i]; 1117 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line); 1118 tmp_spans.safe_push (line_span (lr->m_start.m_line, 1119 lr->m_finish.m_line)); 1120 } 1121 1122 /* Also add spans for any fix-it hints, in case they cover other lines. */ 1123 for (unsigned int i = 0; i < m_fixit_hints.length (); i++) 1124 { 1125 const fixit_hint *hint = m_fixit_hints[i]; 1126 gcc_assert (hint); 1127 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint)); 1128 } 1129 1130 /* Sort them. */ 1131 tmp_spans.qsort(line_span::comparator); 1132 1133 /* Now iterate through tmp_spans, copying into m_line_spans, and 1134 combining where possible. */ 1135 gcc_assert (tmp_spans.length () > 0); 1136 m_line_spans.safe_push (tmp_spans[0]); 1137 for (unsigned int i = 1; i < tmp_spans.length (); i++) 1138 { 1139 line_span *current = &m_line_spans[m_line_spans.length () - 1]; 1140 const line_span *next = &tmp_spans[i]; 1141 gcc_assert (next->m_first_line >= current->m_first_line); 1142 if (next->m_first_line <= current->m_last_line + 1) 1143 { 1144 /* We can merge them. */ 1145 if (next->m_last_line > current->m_last_line) 1146 current->m_last_line = next->m_last_line; 1147 } 1148 else 1149 { 1150 /* No merger possible. */ 1151 m_line_spans.safe_push (*next); 1152 } 1153 } 1154 1155 /* Verify the result, in m_line_spans. */ 1156 gcc_assert (m_line_spans.length () > 0); 1157 for (unsigned int i = 1; i < m_line_spans.length (); i++) 1158 { 1159 const line_span *prev = &m_line_spans[i - 1]; 1160 const line_span *next = &m_line_spans[i]; 1161 /* The individual spans must be sane. */ 1162 gcc_assert (prev->m_first_line <= prev->m_last_line); 1163 gcc_assert (next->m_first_line <= next->m_last_line); 1164 /* The spans must be ordered. */ 1165 gcc_assert (prev->m_first_line < next->m_first_line); 1166 /* There must be a gap of at least one line between separate spans. */ 1167 gcc_assert ((prev->m_last_line + 1) < next->m_first_line); 1168 } 1169 } 1170 1171 /* Print line ROW of source code, potentially colorized at any ranges, and 1172 populate *LBOUNDS_OUT. 1173 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH 1174 is its width. */ 1175 1176 void 1177 layout::print_source_line (linenum_type row, const char *line, int line_width, 1178 line_bounds *lbounds_out) 1179 { 1180 m_colorizer.set_normal_text (); 1181 1182 /* We will stop printing the source line at any trailing 1183 whitespace. */ 1184 line_width = get_line_width_without_trailing_whitespace (line, 1185 line_width); 1186 line += m_x_offset; 1187 1188 pp_space (m_pp); 1189 int first_non_ws = INT_MAX; 1190 int last_non_ws = 0; 1191 int column; 1192 for (column = 1 + m_x_offset; column <= line_width; column++) 1193 { 1194 /* Assuming colorization is enabled for the caret and underline 1195 characters, we may also colorize the associated characters 1196 within the source line. 1197 1198 For frontends that generate range information, we color the 1199 associated characters in the source line the same as the 1200 carets and underlines in the annotation line, to make it easier 1201 for the reader to see the pertinent code. 1202 1203 For frontends that only generate carets, we don't colorize the 1204 characters above them, since this would look strange (e.g. 1205 colorizing just the first character in a token). */ 1206 if (m_colorize_source_p) 1207 { 1208 bool in_range_p; 1209 point_state state; 1210 in_range_p = get_state_at_point (row, column, 1211 0, INT_MAX, 1212 &state); 1213 if (in_range_p) 1214 m_colorizer.set_range (state.range_idx); 1215 else 1216 m_colorizer.set_normal_text (); 1217 } 1218 char c = *line; 1219 if (c == '\0' || c == '\t' || c == '\r') 1220 c = ' '; 1221 if (c != ' ') 1222 { 1223 last_non_ws = column; 1224 if (first_non_ws == INT_MAX) 1225 first_non_ws = column; 1226 } 1227 pp_character (m_pp, c); 1228 line++; 1229 } 1230 print_newline (); 1231 1232 lbounds_out->m_first_non_ws = first_non_ws; 1233 lbounds_out->m_last_non_ws = last_non_ws; 1234 } 1235 1236 /* Determine if we should print an annotation line for ROW. 1237 i.e. if any of m_layout_ranges contains ROW. */ 1238 1239 bool 1240 layout::should_print_annotation_line_p (linenum_type row) const 1241 { 1242 layout_range *range; 1243 int i; 1244 FOR_EACH_VEC_ELT (m_layout_ranges, i, range) 1245 if (range->intersects_line_p (row)) 1246 return true; 1247 return false; 1248 } 1249 1250 /* Print a line consisting of the caret/underlines for the given 1251 source line. */ 1252 1253 void 1254 layout::print_annotation_line (linenum_type row, const line_bounds lbounds) 1255 { 1256 int x_bound = get_x_bound_for_row (row, m_exploc.column, 1257 lbounds.m_last_non_ws); 1258 1259 pp_space (m_pp); 1260 for (int column = 1 + m_x_offset; column < x_bound; column++) 1261 { 1262 bool in_range_p; 1263 point_state state; 1264 in_range_p = get_state_at_point (row, column, 1265 lbounds.m_first_non_ws, 1266 lbounds.m_last_non_ws, 1267 &state); 1268 if (in_range_p) 1269 { 1270 /* Within a range. Draw either the caret or an underline. */ 1271 m_colorizer.set_range (state.range_idx); 1272 if (state.draw_caret_p) 1273 { 1274 /* Draw the caret. */ 1275 char caret_char; 1276 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES) 1277 caret_char = m_context->caret_chars[state.range_idx]; 1278 else 1279 caret_char = '^'; 1280 pp_character (m_pp, caret_char); 1281 } 1282 else 1283 pp_character (m_pp, '~'); 1284 } 1285 else 1286 { 1287 /* Not in a range. */ 1288 m_colorizer.set_normal_text (); 1289 pp_character (m_pp, ' '); 1290 } 1291 } 1292 print_newline (); 1293 } 1294 1295 /* If there are any fixit hints inserting new lines before source line ROW, 1296 print them. 1297 1298 They are printed on lines of their own, before the source line 1299 itself, with a leading '+'. */ 1300 1301 void 1302 layout::print_leading_fixits (linenum_type row) 1303 { 1304 for (unsigned int i = 0; i < m_fixit_hints.length (); i++) 1305 { 1306 const fixit_hint *hint = m_fixit_hints[i]; 1307 1308 if (!hint->ends_with_newline_p ()) 1309 /* Not a newline fixit; print it in print_trailing_fixits. */ 1310 continue; 1311 1312 gcc_assert (hint->insertion_p ()); 1313 1314 if (hint->affects_line_p (m_exploc.file, row)) 1315 { 1316 /* Printing the '+' with normal colorization 1317 and the inserted line with "insert" colorization 1318 helps them stand out from each other, and from 1319 the surrounding text. */ 1320 m_colorizer.set_normal_text (); 1321 pp_character (m_pp, '+'); 1322 m_colorizer.set_fixit_insert (); 1323 /* Print all but the trailing newline of the fix-it hint. 1324 We have to print the newline separately to avoid 1325 getting additional pp prefixes printed. */ 1326 for (size_t i = 0; i < hint->get_length () - 1; i++) 1327 pp_character (m_pp, hint->get_string ()[i]); 1328 m_colorizer.set_normal_text (); 1329 pp_newline (m_pp); 1330 } 1331 } 1332 } 1333 1334 /* Subroutine of layout::print_trailing_fixits. 1335 1336 Determine if the annotation line printed for LINE contained 1337 the exact range from START_COLUMN to FINISH_COLUMN. */ 1338 1339 bool 1340 layout::annotation_line_showed_range_p (linenum_type line, int start_column, 1341 int finish_column) const 1342 { 1343 layout_range *range; 1344 int i; 1345 FOR_EACH_VEC_ELT (m_layout_ranges, i, range) 1346 if (range->m_start.m_line == line 1347 && range->m_start.m_column == start_column 1348 && range->m_finish.m_line == line 1349 && range->m_finish.m_column == finish_column) 1350 return true; 1351 return false; 1352 } 1353 1354 /* Classes for printing trailing fix-it hints i.e. those that 1355 don't add new lines. 1356 1357 For insertion, these can look like: 1358 1359 new_text 1360 1361 For replacement, these can look like: 1362 1363 ------------- : underline showing affected range 1364 new_text 1365 1366 For deletion, these can look like: 1367 1368 ------------- : underline showing affected range 1369 1370 This can become confusing if they overlap, and so we need 1371 to do some preprocessing to decide what to print. 1372 We use the list of fixit_hint instances affecting the line 1373 to build a list of "correction" instances, and print the 1374 latter. 1375 1376 For example, consider a set of fix-its for converting 1377 a C-style cast to a C++ const_cast. 1378 1379 Given: 1380 1381 ..000000000111111111122222222223333333333. 1382 ..123456789012345678901234567890123456789. 1383 foo *f = (foo *)ptr->field; 1384 ^~~~~ 1385 1386 and the fix-it hints: 1387 - replace col 10 (the open paren) with "const_cast<" 1388 - replace col 16 (the close paren) with "> (" 1389 - insert ")" before col 27 1390 1391 then we would get odd-looking output: 1392 1393 foo *f = (foo *)ptr->field; 1394 ^~~~~ 1395 - 1396 const_cast< 1397 - 1398 > ( ) 1399 1400 It would be better to detect when fixit hints are going to 1401 overlap (those that require new lines), and to consolidate 1402 the printing of such fixits, giving something like: 1403 1404 foo *f = (foo *)ptr->field; 1405 ^~~~~ 1406 ----------------- 1407 const_cast<foo *> (ptr->field) 1408 1409 This works by detecting when the printing would overlap, and 1410 effectively injecting no-op replace hints into the gaps between 1411 such fix-its, so that the printing joins up. 1412 1413 In the above example, the overlap of: 1414 - replace col 10 (the open paren) with "const_cast<" 1415 and: 1416 - replace col 16 (the close paren) with "> (" 1417 is fixed by injecting a no-op: 1418 - replace cols 11-15 with themselves ("foo *") 1419 and consolidating these, making: 1420 - replace cols 10-16 with "const_cast<" + "foo *" + "> (" 1421 i.e.: 1422 - replace cols 10-16 with "const_cast<foo *> (" 1423 1424 This overlaps with the final fix-it hint: 1425 - insert ")" before col 27 1426 and so we repeat the consolidation process, by injecting 1427 a no-op: 1428 - replace cols 17-26 with themselves ("ptr->field") 1429 giving: 1430 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")" 1431 i.e.: 1432 - replace cols 10-26 with "const_cast<foo *> (ptr->field)" 1433 1434 and is thus printed as desired. */ 1435 1436 /* A range of columns within a line. */ 1437 1438 struct column_range 1439 { 1440 column_range (int start_, int finish_) : start (start_), finish (finish_) 1441 { 1442 /* We must have either a range, or an insertion. */ 1443 gcc_assert (start <= finish || finish == start - 1); 1444 } 1445 1446 bool operator== (const column_range &other) const 1447 { 1448 return start == other.start && finish == other.finish; 1449 } 1450 1451 int start; 1452 int finish; 1453 }; 1454 1455 /* Get the range of columns that HINT would affect. */ 1456 1457 static column_range 1458 get_affected_columns (const fixit_hint *hint) 1459 { 1460 int start_column = LOCATION_COLUMN (hint->get_start_loc ()); 1461 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1; 1462 1463 return column_range (start_column, finish_column); 1464 } 1465 1466 /* Get the range of columns that would be printed for HINT. */ 1467 1468 static column_range 1469 get_printed_columns (const fixit_hint *hint) 1470 { 1471 int start_column = LOCATION_COLUMN (hint->get_start_loc ()); 1472 int final_hint_column = start_column + hint->get_length () - 1; 1473 if (hint->insertion_p ()) 1474 { 1475 return column_range (start_column, final_hint_column); 1476 } 1477 else 1478 { 1479 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1; 1480 1481 return column_range (start_column, 1482 MAX (finish_column, final_hint_column)); 1483 } 1484 } 1485 1486 /* A struct capturing the bounds of a buffer, to allow for run-time 1487 bounds-checking in a checked build. */ 1488 1489 struct char_span 1490 { 1491 char_span (const char *ptr, size_t n_elts) : m_ptr (ptr), m_n_elts (n_elts) {} 1492 1493 char_span subspan (int offset, int n_elts) 1494 { 1495 gcc_assert (offset >= 0); 1496 gcc_assert (offset < (int)m_n_elts); 1497 gcc_assert (n_elts >= 0); 1498 gcc_assert (offset + n_elts <= (int)m_n_elts); 1499 return char_span (m_ptr + offset, n_elts); 1500 } 1501 1502 const char *m_ptr; 1503 size_t m_n_elts; 1504 }; 1505 1506 /* A correction on a particular line. 1507 This describes a plan for how to print one or more fixit_hint 1508 instances that affected the line, potentially consolidating hints 1509 into corrections to make the result easier for the user to read. */ 1510 1511 struct correction 1512 { 1513 correction (column_range affected_columns, 1514 column_range printed_columns, 1515 const char *new_text, size_t new_text_len) 1516 : m_affected_columns (affected_columns), 1517 m_printed_columns (printed_columns), 1518 m_text (xstrdup (new_text)), 1519 m_len (new_text_len), 1520 m_alloc_sz (new_text_len + 1) 1521 { 1522 } 1523 1524 ~correction () { free (m_text); } 1525 1526 bool insertion_p () const 1527 { 1528 return m_affected_columns.start == m_affected_columns.finish + 1; 1529 } 1530 1531 void ensure_capacity (size_t len); 1532 void ensure_terminated (); 1533 1534 void overwrite (int dst_offset, const char_span &src_span) 1535 { 1536 gcc_assert (dst_offset >= 0); 1537 gcc_assert (dst_offset + src_span.m_n_elts < m_alloc_sz); 1538 memcpy (m_text + dst_offset, src_span.m_ptr, 1539 src_span.m_n_elts); 1540 } 1541 1542 /* If insert, then start: the column before which the text 1543 is to be inserted, and finish is offset by the length of 1544 the replacement. 1545 If replace, then the range of columns affected. */ 1546 column_range m_affected_columns; 1547 1548 /* If insert, then start: the column before which the text 1549 is to be inserted, and finish is offset by the length of 1550 the replacement. 1551 If replace, then the range of columns affected. */ 1552 column_range m_printed_columns; 1553 1554 /* The text to be inserted/used as replacement. */ 1555 char *m_text; 1556 size_t m_len; 1557 size_t m_alloc_sz; 1558 }; 1559 1560 /* Ensure that m_text can hold a string of length LEN 1561 (plus 1 for 0-termination). */ 1562 1563 void 1564 correction::ensure_capacity (size_t len) 1565 { 1566 /* Allow 1 extra byte for 0-termination. */ 1567 if (m_alloc_sz < (len + 1)) 1568 { 1569 size_t new_alloc_sz = (len + 1) * 2; 1570 m_text = (char *)xrealloc (m_text, new_alloc_sz); 1571 m_alloc_sz = new_alloc_sz; 1572 } 1573 } 1574 1575 /* Ensure that m_text is 0-terminated. */ 1576 1577 void 1578 correction::ensure_terminated () 1579 { 1580 /* 0-terminate the buffer. */ 1581 gcc_assert (m_len < m_alloc_sz); 1582 m_text[m_len] = '\0'; 1583 } 1584 1585 /* A list of corrections affecting a particular line. 1586 This is used by layout::print_trailing_fixits for planning 1587 how to print the fix-it hints affecting the line. */ 1588 1589 struct line_corrections 1590 { 1591 line_corrections (const char *filename, linenum_type row) 1592 : m_filename (filename), m_row (row) 1593 {} 1594 ~line_corrections (); 1595 1596 void add_hint (const fixit_hint *hint); 1597 1598 const char *m_filename; 1599 linenum_type m_row; 1600 auto_vec <correction *> m_corrections; 1601 }; 1602 1603 /* struct line_corrections. */ 1604 1605 line_corrections::~line_corrections () 1606 { 1607 unsigned i; 1608 correction *c; 1609 FOR_EACH_VEC_ELT (m_corrections, i, c) 1610 delete c; 1611 } 1612 1613 /* A struct wrapping a particular source line, allowing 1614 run-time bounds-checking of accesses in a checked build. */ 1615 1616 struct source_line 1617 { 1618 source_line (const char *filename, int line); 1619 1620 char_span as_span () { return char_span (chars, width); } 1621 1622 const char *chars; 1623 int width; 1624 }; 1625 1626 /* source_line's ctor. */ 1627 1628 source_line::source_line (const char *filename, int line) 1629 { 1630 chars = location_get_source_line (filename, line, &width); 1631 } 1632 1633 /* Add HINT to the corrections for this line. 1634 Attempt to consolidate nearby hints so that they will not 1635 overlap with printed. */ 1636 1637 void 1638 line_corrections::add_hint (const fixit_hint *hint) 1639 { 1640 column_range affected_columns = get_affected_columns (hint); 1641 column_range printed_columns = get_printed_columns (hint); 1642 1643 /* Potentially consolidate. */ 1644 if (!m_corrections.is_empty ()) 1645 { 1646 correction *last_correction 1647 = m_corrections[m_corrections.length () - 1]; 1648 1649 /* The following consolidation code assumes that the fix-it hints 1650 have been sorted by start (done within layout's ctor). */ 1651 gcc_assert (affected_columns.start 1652 >= last_correction->m_affected_columns.start); 1653 gcc_assert (printed_columns.start 1654 >= last_correction->m_printed_columns.start); 1655 1656 if (printed_columns.start <= last_correction->m_printed_columns.finish) 1657 { 1658 /* We have two hints for which the printed forms of the hints 1659 would touch or overlap, so we need to consolidate them to avoid 1660 confusing the user. 1661 Attempt to inject a "replace" correction from immediately 1662 after the end of the last hint to immediately before the start 1663 of the next hint. */ 1664 column_range between (last_correction->m_affected_columns.finish + 1, 1665 printed_columns.start - 1); 1666 1667 /* Try to read the source. */ 1668 source_line line (m_filename, m_row); 1669 if (line.chars && between.finish < line.width) 1670 { 1671 /* Consolidate into the last correction: 1672 add a no-op "replace" of the "between" text, and 1673 add the text from the new hint. */ 1674 int old_len = last_correction->m_len; 1675 gcc_assert (old_len >= 0); 1676 int between_len = between.finish + 1 - between.start; 1677 gcc_assert (between_len >= 0); 1678 int new_len = old_len + between_len + hint->get_length (); 1679 gcc_assert (new_len >= 0); 1680 last_correction->ensure_capacity (new_len); 1681 last_correction->overwrite 1682 (old_len, 1683 line.as_span ().subspan (between.start - 1, 1684 between.finish + 1 - between.start)); 1685 last_correction->overwrite (old_len + between_len, 1686 char_span (hint->get_string (), 1687 hint->get_length ())); 1688 last_correction->m_len = new_len; 1689 last_correction->ensure_terminated (); 1690 last_correction->m_affected_columns.finish 1691 = affected_columns.finish; 1692 last_correction->m_printed_columns.finish 1693 += between_len + hint->get_length (); 1694 return; 1695 } 1696 } 1697 } 1698 1699 /* If no consolidation happened, add a new correction instance. */ 1700 m_corrections.safe_push (new correction (affected_columns, 1701 printed_columns, 1702 hint->get_string (), 1703 hint->get_length ())); 1704 } 1705 1706 /* If there are any fixit hints on source line ROW, print them. 1707 They are printed in order, attempting to combine them onto lines, but 1708 starting new lines if necessary. 1709 Fix-it hints that insert new lines are handled separately, 1710 in layout::print_leading_fixits. */ 1711 1712 void 1713 layout::print_trailing_fixits (linenum_type row) 1714 { 1715 /* Build a list of correction instances for the line, 1716 potentially consolidating hints (for the sake of readability). */ 1717 line_corrections corrections (m_exploc.file, row); 1718 for (unsigned int i = 0; i < m_fixit_hints.length (); i++) 1719 { 1720 const fixit_hint *hint = m_fixit_hints[i]; 1721 1722 /* Newline fixits are handled by layout::print_leading_fixits. */ 1723 if (hint->ends_with_newline_p ()) 1724 continue; 1725 1726 if (hint->affects_line_p (m_exploc.file, row)) 1727 corrections.add_hint (hint); 1728 } 1729 1730 /* Now print the corrections. */ 1731 unsigned i; 1732 correction *c; 1733 int column = m_x_offset; 1734 1735 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c) 1736 { 1737 /* For now we assume each fixit hint can only touch one line. */ 1738 if (c->insertion_p ()) 1739 { 1740 /* This assumes the insertion just affects one line. */ 1741 int start_column = c->m_printed_columns.start; 1742 move_to_column (&column, start_column); 1743 m_colorizer.set_fixit_insert (); 1744 pp_string (m_pp, c->m_text); 1745 m_colorizer.set_normal_text (); 1746 column += c->m_len; 1747 } 1748 else 1749 { 1750 /* If the range of the replacement wasn't printed in the 1751 annotation line, then print an extra underline to 1752 indicate exactly what is being replaced. 1753 Always show it for removals. */ 1754 int start_column = c->m_affected_columns.start; 1755 int finish_column = c->m_affected_columns.finish; 1756 if (!annotation_line_showed_range_p (row, start_column, 1757 finish_column) 1758 || c->m_len == 0) 1759 { 1760 move_to_column (&column, start_column); 1761 m_colorizer.set_fixit_delete (); 1762 for (; column <= finish_column; column++) 1763 pp_character (m_pp, '-'); 1764 m_colorizer.set_normal_text (); 1765 } 1766 /* Print the replacement text. REPLACE also covers 1767 removals, so only do this extra work (potentially starting 1768 a new line) if we have actual replacement text. */ 1769 if (c->m_len > 0) 1770 { 1771 move_to_column (&column, start_column); 1772 m_colorizer.set_fixit_insert (); 1773 pp_string (m_pp, c->m_text); 1774 m_colorizer.set_normal_text (); 1775 column += c->m_len; 1776 } 1777 } 1778 } 1779 1780 /* Add a trailing newline, if necessary. */ 1781 move_to_column (&column, 0); 1782 } 1783 1784 /* Disable any colorization and emit a newline. */ 1785 1786 void 1787 layout::print_newline () 1788 { 1789 m_colorizer.set_normal_text (); 1790 pp_newline (m_pp); 1791 } 1792 1793 /* Return true if (ROW/COLUMN) is within a range of the layout. 1794 If it returns true, OUT_STATE is written to, with the 1795 range index, and whether we should draw the caret at 1796 (ROW/COLUMN) (as opposed to an underline). */ 1797 1798 bool 1799 layout::get_state_at_point (/* Inputs. */ 1800 linenum_type row, int column, 1801 int first_non_ws, int last_non_ws, 1802 /* Outputs. */ 1803 point_state *out_state) 1804 { 1805 layout_range *range; 1806 int i; 1807 FOR_EACH_VEC_ELT (m_layout_ranges, i, range) 1808 { 1809 if (range->contains_point (row, column)) 1810 { 1811 out_state->range_idx = i; 1812 1813 /* Are we at the range's caret? is it visible? */ 1814 out_state->draw_caret_p = false; 1815 if (range->m_show_caret_p 1816 && row == range->m_caret.m_line 1817 && column == range->m_caret.m_column) 1818 out_state->draw_caret_p = true; 1819 1820 /* Within a multiline range, don't display any underline 1821 in any leading or trailing whitespace on a line. 1822 We do display carets, however. */ 1823 if (!out_state->draw_caret_p) 1824 if (column < first_non_ws || column > last_non_ws) 1825 return false; 1826 1827 /* We are within a range. */ 1828 return true; 1829 } 1830 } 1831 1832 return false; 1833 } 1834 1835 /* Helper function for use by layout::print_line when printing the 1836 annotation line under the source line. 1837 Get the column beyond the rightmost one that could contain a caret or 1838 range marker, given that we stop rendering at trailing whitespace. 1839 ROW is the source line within the given file. 1840 CARET_COLUMN is the column of range 0's caret. 1841 LAST_NON_WS_COLUMN is the last column containing a non-whitespace 1842 character of source (as determined when printing the source line). */ 1843 1844 int 1845 layout::get_x_bound_for_row (linenum_type row, int caret_column, 1846 int last_non_ws_column) 1847 { 1848 int result = caret_column + 1; 1849 1850 layout_range *range; 1851 int i; 1852 FOR_EACH_VEC_ELT (m_layout_ranges, i, range) 1853 { 1854 if (row >= range->m_start.m_line) 1855 { 1856 if (range->m_finish.m_line == row) 1857 { 1858 /* On the final line within a range; ensure that 1859 we render up to the end of the range. */ 1860 if (result <= range->m_finish.m_column) 1861 result = range->m_finish.m_column + 1; 1862 } 1863 else if (row < range->m_finish.m_line) 1864 { 1865 /* Within a multiline range; ensure that we render up to the 1866 last non-whitespace column. */ 1867 if (result <= last_non_ws_column) 1868 result = last_non_ws_column + 1; 1869 } 1870 } 1871 } 1872 1873 return result; 1874 } 1875 1876 /* Given *COLUMN as an x-coordinate, print spaces to position 1877 successive output at DEST_COLUMN, printing a newline if necessary, 1878 and updating *COLUMN. */ 1879 1880 void 1881 layout::move_to_column (int *column, int dest_column) 1882 { 1883 /* Start a new line if we need to. */ 1884 if (*column > dest_column) 1885 { 1886 print_newline (); 1887 *column = m_x_offset; 1888 } 1889 1890 while (*column < dest_column) 1891 { 1892 pp_space (m_pp); 1893 (*column)++; 1894 } 1895 } 1896 1897 /* For debugging layout issues, render a ruler giving column numbers 1898 (after the 1-column indent). */ 1899 1900 void 1901 layout::show_ruler (int max_column) const 1902 { 1903 /* Hundreds. */ 1904 if (max_column > 99) 1905 { 1906 pp_space (m_pp); 1907 for (int column = 1 + m_x_offset; column <= max_column; column++) 1908 if (column % 10 == 0) 1909 pp_character (m_pp, '0' + (column / 100) % 10); 1910 else 1911 pp_space (m_pp); 1912 pp_newline (m_pp); 1913 } 1914 1915 /* Tens. */ 1916 pp_space (m_pp); 1917 for (int column = 1 + m_x_offset; column <= max_column; column++) 1918 if (column % 10 == 0) 1919 pp_character (m_pp, '0' + (column / 10) % 10); 1920 else 1921 pp_space (m_pp); 1922 pp_newline (m_pp); 1923 1924 /* Units. */ 1925 pp_space (m_pp); 1926 for (int column = 1 + m_x_offset; column <= max_column; column++) 1927 pp_character (m_pp, '0' + (column % 10)); 1928 pp_newline (m_pp); 1929 } 1930 1931 /* Print leading fix-its (for new lines inserted before the source line) 1932 then the source line, followed by an annotation line 1933 consisting of any caret/underlines, then any fixits. 1934 If the source line can't be read, print nothing. */ 1935 void 1936 layout::print_line (linenum_type row) 1937 { 1938 int line_width; 1939 const char *line = location_get_source_line (m_exploc.file, row, 1940 &line_width); 1941 if (!line) 1942 return; 1943 1944 line_bounds lbounds; 1945 print_leading_fixits (row); 1946 print_source_line (row, line, line_width, &lbounds); 1947 if (should_print_annotation_line_p (row)) 1948 print_annotation_line (row, lbounds); 1949 print_trailing_fixits (row); 1950 } 1951 1952 } /* End of anonymous namespace. */ 1953 1954 /* If LOC is within the spans of lines that will already be printed for 1955 this gcc_rich_location, then add it as a secondary location and return true. 1956 1957 Otherwise return false. */ 1958 1959 bool 1960 gcc_rich_location::add_location_if_nearby (location_t loc) 1961 { 1962 /* Use the layout location-handling logic to sanitize LOC, 1963 filtering it to the current line spans within a temporary 1964 layout instance. */ 1965 layout layout (global_dc, this, DK_ERROR); 1966 location_range loc_range; 1967 loc_range.m_loc = loc; 1968 loc_range.m_show_caret_p = false; 1969 if (!layout.maybe_add_location_range (&loc_range, true)) 1970 return false; 1971 1972 add_range (loc, false); 1973 return true; 1974 } 1975 1976 /* Print the physical source code corresponding to the location of 1977 this diagnostic, with additional annotations. */ 1978 1979 void 1980 diagnostic_show_locus (diagnostic_context * context, 1981 rich_location *richloc, 1982 diagnostic_t diagnostic_kind) 1983 { 1984 pp_newline (context->printer); 1985 1986 location_t loc = richloc->get_loc (); 1987 /* Do nothing if source-printing has been disabled. */ 1988 if (!context->show_caret) 1989 return; 1990 1991 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */ 1992 if (loc <= BUILTINS_LOCATION) 1993 return; 1994 1995 /* Don't print the same source location twice in a row, unless we have 1996 fix-it hints. */ 1997 if (loc == context->last_location 1998 && richloc->get_num_fixit_hints () == 0) 1999 return; 2000 2001 context->last_location = loc; 2002 2003 char *saved_prefix = pp_take_prefix (context->printer); 2004 pp_set_prefix (context->printer, NULL); 2005 2006 layout layout (context, richloc, diagnostic_kind); 2007 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans (); 2008 line_span_idx++) 2009 { 2010 const line_span *line_span = layout.get_line_span (line_span_idx); 2011 if (layout.print_heading_for_line_span_index_p (line_span_idx)) 2012 { 2013 expanded_location exploc = layout.get_expanded_location (line_span); 2014 context->start_span (context, exploc); 2015 } 2016 linenum_type last_line = line_span->get_last_line (); 2017 for (linenum_type row = line_span->get_first_line (); 2018 row <= last_line; row++) 2019 layout.print_line (row); 2020 } 2021 2022 pp_set_prefix (context->printer, saved_prefix); 2023 } 2024 2025 #if CHECKING_P 2026 2027 namespace selftest { 2028 2029 /* Selftests for diagnostic_show_locus. */ 2030 2031 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */ 2032 2033 static void 2034 test_diagnostic_show_locus_unknown_location () 2035 { 2036 test_diagnostic_context dc; 2037 rich_location richloc (line_table, UNKNOWN_LOCATION); 2038 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2039 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer)); 2040 } 2041 2042 /* Verify that diagnostic_show_locus works sanely for various 2043 single-line cases. 2044 2045 All of these work on the following 1-line source file: 2046 .0000000001111111 2047 .1234567890123456 2048 "foo = bar.field;\n" 2049 which is set up by test_diagnostic_show_locus_one_liner and calls 2050 them. */ 2051 2052 /* Just a caret. */ 2053 2054 static void 2055 test_one_liner_simple_caret () 2056 { 2057 test_diagnostic_context dc; 2058 location_t caret = linemap_position_for_column (line_table, 10); 2059 rich_location richloc (line_table, caret); 2060 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2061 ASSERT_STREQ ("\n" 2062 " foo = bar.field;\n" 2063 " ^\n", 2064 pp_formatted_text (dc.printer)); 2065 } 2066 2067 /* Caret and range. */ 2068 2069 static void 2070 test_one_liner_caret_and_range () 2071 { 2072 test_diagnostic_context dc; 2073 location_t caret = linemap_position_for_column (line_table, 10); 2074 location_t start = linemap_position_for_column (line_table, 7); 2075 location_t finish = linemap_position_for_column (line_table, 15); 2076 location_t loc = make_location (caret, start, finish); 2077 rich_location richloc (line_table, loc); 2078 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2079 ASSERT_STREQ ("\n" 2080 " foo = bar.field;\n" 2081 " ~~~^~~~~~\n", 2082 pp_formatted_text (dc.printer)); 2083 } 2084 2085 /* Multiple ranges and carets. */ 2086 2087 static void 2088 test_one_liner_multiple_carets_and_ranges () 2089 { 2090 test_diagnostic_context dc; 2091 location_t foo 2092 = make_location (linemap_position_for_column (line_table, 2), 2093 linemap_position_for_column (line_table, 1), 2094 linemap_position_for_column (line_table, 3)); 2095 dc.caret_chars[0] = 'A'; 2096 2097 location_t bar 2098 = make_location (linemap_position_for_column (line_table, 8), 2099 linemap_position_for_column (line_table, 7), 2100 linemap_position_for_column (line_table, 9)); 2101 dc.caret_chars[1] = 'B'; 2102 2103 location_t field 2104 = make_location (linemap_position_for_column (line_table, 13), 2105 linemap_position_for_column (line_table, 11), 2106 linemap_position_for_column (line_table, 15)); 2107 dc.caret_chars[2] = 'C'; 2108 2109 rich_location richloc (line_table, foo); 2110 richloc.add_range (bar, true); 2111 richloc.add_range (field, true); 2112 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2113 ASSERT_STREQ ("\n" 2114 " foo = bar.field;\n" 2115 " ~A~ ~B~ ~~C~~\n", 2116 pp_formatted_text (dc.printer)); 2117 } 2118 2119 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */ 2120 2121 static void 2122 test_one_liner_fixit_insert_before () 2123 { 2124 test_diagnostic_context dc; 2125 location_t caret = linemap_position_for_column (line_table, 7); 2126 rich_location richloc (line_table, caret); 2127 richloc.add_fixit_insert_before ("&"); 2128 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2129 ASSERT_STREQ ("\n" 2130 " foo = bar.field;\n" 2131 " ^\n" 2132 " &\n", 2133 pp_formatted_text (dc.printer)); 2134 } 2135 2136 /* Insertion fix-it hint: adding a "[0]" after "foo". */ 2137 2138 static void 2139 test_one_liner_fixit_insert_after () 2140 { 2141 test_diagnostic_context dc; 2142 location_t start = linemap_position_for_column (line_table, 1); 2143 location_t finish = linemap_position_for_column (line_table, 3); 2144 location_t foo = make_location (start, start, finish); 2145 rich_location richloc (line_table, foo); 2146 richloc.add_fixit_insert_after ("[0]"); 2147 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2148 ASSERT_STREQ ("\n" 2149 " foo = bar.field;\n" 2150 " ^~~\n" 2151 " [0]\n", 2152 pp_formatted_text (dc.printer)); 2153 } 2154 2155 /* Removal fix-it hint: removal of the ".field". */ 2156 2157 static void 2158 test_one_liner_fixit_remove () 2159 { 2160 test_diagnostic_context dc; 2161 location_t start = linemap_position_for_column (line_table, 10); 2162 location_t finish = linemap_position_for_column (line_table, 15); 2163 location_t dot = make_location (start, start, finish); 2164 rich_location richloc (line_table, dot); 2165 richloc.add_fixit_remove (); 2166 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2167 ASSERT_STREQ ("\n" 2168 " foo = bar.field;\n" 2169 " ^~~~~~\n" 2170 " ------\n", 2171 pp_formatted_text (dc.printer)); 2172 } 2173 2174 /* Replace fix-it hint: replacing "field" with "m_field". */ 2175 2176 static void 2177 test_one_liner_fixit_replace () 2178 { 2179 test_diagnostic_context dc; 2180 location_t start = linemap_position_for_column (line_table, 11); 2181 location_t finish = linemap_position_for_column (line_table, 15); 2182 location_t field = make_location (start, start, finish); 2183 rich_location richloc (line_table, field); 2184 richloc.add_fixit_replace ("m_field"); 2185 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2186 ASSERT_STREQ ("\n" 2187 " foo = bar.field;\n" 2188 " ^~~~~\n" 2189 " m_field\n", 2190 pp_formatted_text (dc.printer)); 2191 } 2192 2193 /* Replace fix-it hint: replacing "field" with "m_field", 2194 but where the caret was elsewhere. */ 2195 2196 static void 2197 test_one_liner_fixit_replace_non_equal_range () 2198 { 2199 test_diagnostic_context dc; 2200 location_t equals = linemap_position_for_column (line_table, 5); 2201 location_t start = linemap_position_for_column (line_table, 11); 2202 location_t finish = linemap_position_for_column (line_table, 15); 2203 rich_location richloc (line_table, equals); 2204 source_range range; 2205 range.m_start = start; 2206 range.m_finish = finish; 2207 richloc.add_fixit_replace (range, "m_field"); 2208 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2209 /* The replacement range is not indicated in the annotation line, so 2210 it should be indicated via an additional underline. */ 2211 ASSERT_STREQ ("\n" 2212 " foo = bar.field;\n" 2213 " ^\n" 2214 " -----\n" 2215 " m_field\n", 2216 pp_formatted_text (dc.printer)); 2217 } 2218 2219 /* Replace fix-it hint: replacing "field" with "m_field", 2220 where the caret was elsewhere, but where a secondary range 2221 exactly covers "field". */ 2222 2223 static void 2224 test_one_liner_fixit_replace_equal_secondary_range () 2225 { 2226 test_diagnostic_context dc; 2227 location_t equals = linemap_position_for_column (line_table, 5); 2228 location_t start = linemap_position_for_column (line_table, 11); 2229 location_t finish = linemap_position_for_column (line_table, 15); 2230 rich_location richloc (line_table, equals); 2231 location_t field = make_location (start, start, finish); 2232 richloc.add_range (field, false); 2233 richloc.add_fixit_replace (field, "m_field"); 2234 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2235 /* The replacement range is indicated in the annotation line, 2236 so it shouldn't be indicated via an additional underline. */ 2237 ASSERT_STREQ ("\n" 2238 " foo = bar.field;\n" 2239 " ^ ~~~~~\n" 2240 " m_field\n", 2241 pp_formatted_text (dc.printer)); 2242 } 2243 2244 /* Verify that we can use ad-hoc locations when adding fixits to a 2245 rich_location. */ 2246 2247 static void 2248 test_one_liner_fixit_validation_adhoc_locations () 2249 { 2250 /* Generate a range that's too long to be packed, so must 2251 be stored as an ad-hoc location (given the defaults 2252 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */ 2253 const location_t c7 = linemap_position_for_column (line_table, 7); 2254 const location_t c47 = linemap_position_for_column (line_table, 47); 2255 const location_t loc = make_location (c7, c7, c47); 2256 2257 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS) 2258 return; 2259 2260 ASSERT_TRUE (IS_ADHOC_LOC (loc)); 2261 2262 /* Insert. */ 2263 { 2264 rich_location richloc (line_table, loc); 2265 richloc.add_fixit_insert_before (loc, "test"); 2266 /* It should not have been discarded by the validator. */ 2267 ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 2268 2269 test_diagnostic_context dc; 2270 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2271 ASSERT_STREQ ("\n" 2272 " foo = bar.field;\n" 2273 " ^~~~~~~~~~ \n" 2274 " test\n", 2275 pp_formatted_text (dc.printer)); 2276 } 2277 2278 /* Remove. */ 2279 { 2280 rich_location richloc (line_table, loc); 2281 source_range range = source_range::from_locations (loc, c47); 2282 richloc.add_fixit_remove (range); 2283 /* It should not have been discarded by the validator. */ 2284 ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 2285 2286 test_diagnostic_context dc; 2287 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2288 ASSERT_STREQ ("\n" 2289 " foo = bar.field;\n" 2290 " ^~~~~~~~~~ \n" 2291 " -----------------------------------------\n", 2292 pp_formatted_text (dc.printer)); 2293 } 2294 2295 /* Replace. */ 2296 { 2297 rich_location richloc (line_table, loc); 2298 source_range range = source_range::from_locations (loc, c47); 2299 richloc.add_fixit_replace (range, "test"); 2300 /* It should not have been discarded by the validator. */ 2301 ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 2302 2303 test_diagnostic_context dc; 2304 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2305 ASSERT_STREQ ("\n" 2306 " foo = bar.field;\n" 2307 " ^~~~~~~~~~ \n" 2308 " test\n", 2309 pp_formatted_text (dc.printer)); 2310 } 2311 } 2312 2313 /* Test of consolidating insertions at the same location. */ 2314 2315 static void 2316 test_one_liner_many_fixits_1 () 2317 { 2318 test_diagnostic_context dc; 2319 location_t equals = linemap_position_for_column (line_table, 5); 2320 rich_location richloc (line_table, equals); 2321 for (int i = 0; i < 19; i++) 2322 richloc.add_fixit_insert_before ("a"); 2323 ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 2324 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2325 ASSERT_STREQ ("\n" 2326 " foo = bar.field;\n" 2327 " ^\n" 2328 " aaaaaaaaaaaaaaaaaaa\n", 2329 pp_formatted_text (dc.printer)); 2330 } 2331 2332 /* Ensure that we can add an arbitrary number of fix-it hints to a 2333 rich_location, even if they are not consolidated. */ 2334 2335 static void 2336 test_one_liner_many_fixits_2 () 2337 { 2338 test_diagnostic_context dc; 2339 location_t equals = linemap_position_for_column (line_table, 5); 2340 rich_location richloc (line_table, equals); 2341 for (int i = 0; i < 19; i++) 2342 { 2343 location_t loc = linemap_position_for_column (line_table, i * 2); 2344 richloc.add_fixit_insert_before (loc, "a"); 2345 } 2346 ASSERT_EQ (19, richloc.get_num_fixit_hints ()); 2347 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2348 ASSERT_STREQ ("\n" 2349 " foo = bar.field;\n" 2350 " ^\n" 2351 "a a a a a a a a a a a a a a a a a a a\n", 2352 pp_formatted_text (dc.printer)); 2353 } 2354 2355 /* Run the various one-liner tests. */ 2356 2357 static void 2358 test_diagnostic_show_locus_one_liner (const line_table_case &case_) 2359 { 2360 /* Create a tempfile and write some text to it. 2361 ....................0000000001111111. 2362 ....................1234567890123456. */ 2363 const char *content = "foo = bar.field;\n"; 2364 temp_source_file tmp (SELFTEST_LOCATION, ".c", content); 2365 line_table_test ltt (case_); 2366 2367 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1); 2368 2369 location_t line_end = linemap_position_for_column (line_table, 16); 2370 2371 /* Don't attempt to run the tests if column data might be unavailable. */ 2372 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 2373 return; 2374 2375 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end)); 2376 ASSERT_EQ (1, LOCATION_LINE (line_end)); 2377 ASSERT_EQ (16, LOCATION_COLUMN (line_end)); 2378 2379 test_one_liner_simple_caret (); 2380 test_one_liner_caret_and_range (); 2381 test_one_liner_multiple_carets_and_ranges (); 2382 test_one_liner_fixit_insert_before (); 2383 test_one_liner_fixit_insert_after (); 2384 test_one_liner_fixit_remove (); 2385 test_one_liner_fixit_replace (); 2386 test_one_liner_fixit_replace_non_equal_range (); 2387 test_one_liner_fixit_replace_equal_secondary_range (); 2388 test_one_liner_fixit_validation_adhoc_locations (); 2389 test_one_liner_many_fixits_1 (); 2390 test_one_liner_many_fixits_2 (); 2391 } 2392 2393 /* Verify that gcc_rich_location::add_location_if_nearby works. */ 2394 2395 static void 2396 test_add_location_if_nearby (const line_table_case &case_) 2397 { 2398 /* Create a tempfile and write some text to it. 2399 ...000000000111111111122222222223333333333. 2400 ...123456789012345678901234567890123456789. */ 2401 const char *content 2402 = ("struct same_line { double x; double y; ;\n" /* line 1. */ 2403 "struct different_line\n" /* line 2. */ 2404 "{\n" /* line 3. */ 2405 " double x;\n" /* line 4. */ 2406 " double y;\n" /* line 5. */ 2407 ";\n"); /* line 6. */ 2408 temp_source_file tmp (SELFTEST_LOCATION, ".c", content); 2409 line_table_test ltt (case_); 2410 2411 const line_map_ordinary *ord_map 2412 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false, 2413 tmp.get_filename (), 0)); 2414 2415 linemap_line_start (line_table, 1, 100); 2416 2417 const location_t final_line_end 2418 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7); 2419 2420 /* Don't attempt to run the tests if column data might be unavailable. */ 2421 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 2422 return; 2423 2424 /* Test of add_location_if_nearby on the same line as the 2425 primary location. */ 2426 { 2427 const location_t missing_close_brace_1_39 2428 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39); 2429 const location_t matching_open_brace_1_18 2430 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18); 2431 gcc_rich_location richloc (missing_close_brace_1_39); 2432 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18); 2433 ASSERT_TRUE (added); 2434 ASSERT_EQ (2, richloc.get_num_locations ()); 2435 test_diagnostic_context dc; 2436 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2437 ASSERT_STREQ ("\n" 2438 " struct same_line { double x; double y; ;\n" 2439 " ~ ^\n", 2440 pp_formatted_text (dc.printer)); 2441 } 2442 2443 /* Test of add_location_if_nearby on a different line to the 2444 primary location. */ 2445 { 2446 const location_t missing_close_brace_6_1 2447 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1); 2448 const location_t matching_open_brace_3_1 2449 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1); 2450 gcc_rich_location richloc (missing_close_brace_6_1); 2451 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1); 2452 ASSERT_FALSE (added); 2453 ASSERT_EQ (1, richloc.get_num_locations ()); 2454 } 2455 } 2456 2457 /* Verify that we print fixits even if they only affect lines 2458 outside those covered by the ranges in the rich_location. */ 2459 2460 static void 2461 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_) 2462 { 2463 /* Create a tempfile and write some text to it. 2464 ...000000000111111111122222222223333333333. 2465 ...123456789012345678901234567890123456789. */ 2466 const char *content 2467 = ("struct point { double x; double y; };\n" /* line 1. */ 2468 "struct point origin = {x: 0.0,\n" /* line 2. */ 2469 " y\n" /* line 3. */ 2470 "\n" /* line 4. */ 2471 "\n" /* line 5. */ 2472 " : 0.0};\n"); /* line 6. */ 2473 temp_source_file tmp (SELFTEST_LOCATION, ".c", content); 2474 line_table_test ltt (case_); 2475 2476 const line_map_ordinary *ord_map 2477 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false, 2478 tmp.get_filename (), 0)); 2479 2480 linemap_line_start (line_table, 1, 100); 2481 2482 const location_t final_line_end 2483 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36); 2484 2485 /* Don't attempt to run the tests if column data might be unavailable. */ 2486 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 2487 return; 2488 2489 /* A pair of tests for modernizing the initializers to C99-style. */ 2490 2491 /* The one-liner case (line 2). */ 2492 { 2493 test_diagnostic_context dc; 2494 const location_t x 2495 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24); 2496 const location_t colon 2497 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25); 2498 rich_location richloc (line_table, colon); 2499 richloc.add_fixit_insert_before (x, "."); 2500 richloc.add_fixit_replace (colon, "="); 2501 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2502 ASSERT_STREQ ("\n" 2503 " struct point origin = {x: 0.0,\n" 2504 " ^\n" 2505 " .=\n", 2506 pp_formatted_text (dc.printer)); 2507 } 2508 2509 /* The multiline case. The caret for the rich_location is on line 6; 2510 verify that insertion fixit on line 3 is still printed (and that 2511 span starts are printed due to the gap between the span at line 3 2512 and that at line 6). */ 2513 { 2514 test_diagnostic_context dc; 2515 const location_t y 2516 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24); 2517 const location_t colon 2518 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25); 2519 rich_location richloc (line_table, colon); 2520 richloc.add_fixit_insert_before (y, "."); 2521 richloc.add_fixit_replace (colon, "="); 2522 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2523 ASSERT_STREQ ("\n" 2524 "FILENAME:3:24:\n" 2525 " y\n" 2526 " .\n" 2527 "FILENAME:6:25:\n" 2528 " : 0.0};\n" 2529 " ^\n" 2530 " =\n", 2531 pp_formatted_text (dc.printer)); 2532 } 2533 } 2534 2535 2536 /* Verify that fix-it hints are appropriately consolidated. 2537 2538 If any fix-it hints in a rich_location involve locations beyond 2539 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply 2540 the fix-it as a whole, so there should be none. 2541 2542 Otherwise, verify that consecutive "replace" and "remove" fix-its 2543 are merged, and that other fix-its remain separate. */ 2544 2545 static void 2546 test_fixit_consolidation (const line_table_case &case_) 2547 { 2548 line_table_test ltt (case_); 2549 2550 linemap_add (line_table, LC_ENTER, false, "test.c", 1); 2551 2552 const location_t c10 = linemap_position_for_column (line_table, 10); 2553 const location_t c15 = linemap_position_for_column (line_table, 15); 2554 const location_t c16 = linemap_position_for_column (line_table, 16); 2555 const location_t c17 = linemap_position_for_column (line_table, 17); 2556 const location_t c20 = linemap_position_for_column (line_table, 20); 2557 const location_t c21 = linemap_position_for_column (line_table, 21); 2558 const location_t caret = c10; 2559 2560 /* Insert + insert. */ 2561 { 2562 rich_location richloc (line_table, caret); 2563 richloc.add_fixit_insert_before (c10, "foo"); 2564 richloc.add_fixit_insert_before (c15, "bar"); 2565 2566 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS) 2567 /* Bogus column info for 2nd fixit, so no fixits. */ 2568 ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 2569 else 2570 /* They should not have been merged. */ 2571 ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 2572 } 2573 2574 /* Insert + replace. */ 2575 { 2576 rich_location richloc (line_table, caret); 2577 richloc.add_fixit_insert_before (c10, "foo"); 2578 richloc.add_fixit_replace (source_range::from_locations (c15, c17), 2579 "bar"); 2580 2581 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS) 2582 /* Bogus column info for 2nd fixit, so no fixits. */ 2583 ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 2584 else 2585 /* They should not have been merged. */ 2586 ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 2587 } 2588 2589 /* Replace + non-consecutive insert. */ 2590 { 2591 rich_location richloc (line_table, caret); 2592 richloc.add_fixit_replace (source_range::from_locations (c10, c15), 2593 "bar"); 2594 richloc.add_fixit_insert_before (c17, "foo"); 2595 2596 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS) 2597 /* Bogus column info for 2nd fixit, so no fixits. */ 2598 ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 2599 else 2600 /* They should not have been merged. */ 2601 ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 2602 } 2603 2604 /* Replace + non-consecutive replace. */ 2605 { 2606 rich_location richloc (line_table, caret); 2607 richloc.add_fixit_replace (source_range::from_locations (c10, c15), 2608 "foo"); 2609 richloc.add_fixit_replace (source_range::from_locations (c17, c20), 2610 "bar"); 2611 2612 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS) 2613 /* Bogus column info for 2nd fixit, so no fixits. */ 2614 ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 2615 else 2616 /* They should not have been merged. */ 2617 ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 2618 } 2619 2620 /* Replace + consecutive replace. */ 2621 { 2622 rich_location richloc (line_table, caret); 2623 richloc.add_fixit_replace (source_range::from_locations (c10, c15), 2624 "foo"); 2625 richloc.add_fixit_replace (source_range::from_locations (c16, c20), 2626 "bar"); 2627 2628 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS) 2629 /* Bogus column info for 2nd fixit, so no fixits. */ 2630 ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 2631 else 2632 { 2633 /* They should have been merged into a single "replace". */ 2634 ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 2635 const fixit_hint *hint = richloc.get_fixit_hint (0); 2636 ASSERT_STREQ ("foobar", hint->get_string ()); 2637 ASSERT_EQ (c10, hint->get_start_loc ()); 2638 ASSERT_EQ (c21, hint->get_next_loc ()); 2639 } 2640 } 2641 2642 /* Replace + consecutive removal. */ 2643 { 2644 rich_location richloc (line_table, caret); 2645 richloc.add_fixit_replace (source_range::from_locations (c10, c15), 2646 "foo"); 2647 richloc.add_fixit_remove (source_range::from_locations (c16, c20)); 2648 2649 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS) 2650 /* Bogus column info for 2nd fixit, so no fixits. */ 2651 ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 2652 else 2653 { 2654 /* They should have been merged into a single replace, with the 2655 range extended to cover that of the removal. */ 2656 ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 2657 const fixit_hint *hint = richloc.get_fixit_hint (0); 2658 ASSERT_STREQ ("foo", hint->get_string ()); 2659 ASSERT_EQ (c10, hint->get_start_loc ()); 2660 ASSERT_EQ (c21, hint->get_next_loc ()); 2661 } 2662 } 2663 2664 /* Consecutive removals. */ 2665 { 2666 rich_location richloc (line_table, caret); 2667 richloc.add_fixit_remove (source_range::from_locations (c10, c15)); 2668 richloc.add_fixit_remove (source_range::from_locations (c16, c20)); 2669 2670 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS) 2671 /* Bogus column info for 2nd fixit, so no fixits. */ 2672 ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 2673 else 2674 { 2675 /* They should have been merged into a single "replace-with-empty". */ 2676 ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 2677 const fixit_hint *hint = richloc.get_fixit_hint (0); 2678 ASSERT_STREQ ("", hint->get_string ()); 2679 ASSERT_EQ (c10, hint->get_start_loc ()); 2680 ASSERT_EQ (c21, hint->get_next_loc ()); 2681 } 2682 } 2683 } 2684 2685 /* Verify that the line_corrections machinery correctly prints 2686 overlapping fixit-hints. */ 2687 2688 static void 2689 test_overlapped_fixit_printing (const line_table_case &case_) 2690 { 2691 /* Create a tempfile and write some text to it. 2692 ...000000000111111111122222222223333333333. 2693 ...123456789012345678901234567890123456789. */ 2694 const char *content 2695 = (" foo *f = (foo *)ptr->field;\n"); 2696 temp_source_file tmp (SELFTEST_LOCATION, ".C", content); 2697 line_table_test ltt (case_); 2698 2699 const line_map_ordinary *ord_map 2700 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false, 2701 tmp.get_filename (), 0)); 2702 2703 linemap_line_start (line_table, 1, 100); 2704 2705 const location_t final_line_end 2706 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36); 2707 2708 /* Don't attempt to run the tests if column data might be unavailable. */ 2709 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 2710 return; 2711 2712 /* A test for converting a C-style cast to a C++-style cast. */ 2713 const location_t open_paren 2714 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12); 2715 const location_t close_paren 2716 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18); 2717 const location_t expr_start 2718 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19); 2719 const location_t expr_finish 2720 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28); 2721 const location_t expr = make_location (expr_start, expr_start, expr_finish); 2722 2723 /* Various examples of fix-it hints that aren't themselves consolidated, 2724 but for which the *printing* may need consolidation. */ 2725 2726 /* Example where 3 fix-it hints are printed as one. */ 2727 { 2728 test_diagnostic_context dc; 2729 rich_location richloc (line_table, expr); 2730 richloc.add_fixit_replace (open_paren, "const_cast<"); 2731 richloc.add_fixit_replace (close_paren, "> ("); 2732 richloc.add_fixit_insert_after (")"); 2733 2734 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2735 ASSERT_STREQ ("\n" 2736 " foo *f = (foo *)ptr->field;\n" 2737 " ^~~~~~~~~~\n" 2738 " -----------------\n" 2739 " const_cast<foo *> (ptr->field)\n", 2740 pp_formatted_text (dc.printer)); 2741 2742 /* Unit-test the line_corrections machinery. */ 2743 ASSERT_EQ (3, richloc.get_num_fixit_hints ()); 2744 const fixit_hint *hint_0 = richloc.get_fixit_hint (0); 2745 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0)); 2746 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0)); 2747 const fixit_hint *hint_1 = richloc.get_fixit_hint (1); 2748 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1)); 2749 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1)); 2750 const fixit_hint *hint_2 = richloc.get_fixit_hint (2); 2751 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2)); 2752 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2)); 2753 2754 /* Add each hint in turn to a line_corrections instance, 2755 and verify that they are consolidated into one correction instance 2756 as expected. */ 2757 line_corrections lc (tmp.get_filename (), 1); 2758 2759 /* The first replace hint by itself. */ 2760 lc.add_hint (hint_0); 2761 ASSERT_EQ (1, lc.m_corrections.length ()); 2762 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns); 2763 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns); 2764 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text); 2765 2766 /* After the second replacement hint, they are printed together 2767 as a replacement (along with the text between them). */ 2768 lc.add_hint (hint_1); 2769 ASSERT_EQ (1, lc.m_corrections.length ()); 2770 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text); 2771 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns); 2772 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns); 2773 2774 /* After the final insertion hint, they are all printed together 2775 as a replacement (along with the text between them). */ 2776 lc.add_hint (hint_2); 2777 ASSERT_STREQ ("const_cast<foo *> (ptr->field)", 2778 lc.m_corrections[0]->m_text); 2779 ASSERT_EQ (1, lc.m_corrections.length ()); 2780 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns); 2781 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns); 2782 } 2783 2784 /* Example where two are consolidated during printing. */ 2785 { 2786 test_diagnostic_context dc; 2787 rich_location richloc (line_table, expr); 2788 richloc.add_fixit_replace (open_paren, "CAST ("); 2789 richloc.add_fixit_replace (close_paren, ") ("); 2790 richloc.add_fixit_insert_after (")"); 2791 2792 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2793 ASSERT_STREQ ("\n" 2794 " foo *f = (foo *)ptr->field;\n" 2795 " ^~~~~~~~~~\n" 2796 " -\n" 2797 " CAST (-\n" 2798 " ) ( )\n", 2799 pp_formatted_text (dc.printer)); 2800 } 2801 2802 /* Example where none are consolidated during printing. */ 2803 { 2804 test_diagnostic_context dc; 2805 rich_location richloc (line_table, expr); 2806 richloc.add_fixit_replace (open_paren, "CST ("); 2807 richloc.add_fixit_replace (close_paren, ") ("); 2808 richloc.add_fixit_insert_after (")"); 2809 2810 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2811 ASSERT_STREQ ("\n" 2812 " foo *f = (foo *)ptr->field;\n" 2813 " ^~~~~~~~~~\n" 2814 " -\n" 2815 " CST ( -\n" 2816 " ) ( )\n", 2817 pp_formatted_text (dc.printer)); 2818 } 2819 2820 /* Example of deletion fix-it hints. */ 2821 { 2822 test_diagnostic_context dc; 2823 rich_location richloc (line_table, expr); 2824 richloc.add_fixit_insert_before (open_paren, "(bar *)"); 2825 source_range victim = {open_paren, close_paren}; 2826 richloc.add_fixit_remove (victim); 2827 2828 /* This case is actually handled by fixit-consolidation, 2829 rather than by line_corrections. */ 2830 ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 2831 2832 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2833 ASSERT_STREQ ("\n" 2834 " foo *f = (foo *)ptr->field;\n" 2835 " ^~~~~~~~~~\n" 2836 " -------\n" 2837 " (bar *)\n", 2838 pp_formatted_text (dc.printer)); 2839 } 2840 2841 /* Example of deletion fix-it hints that would overlap. */ 2842 { 2843 test_diagnostic_context dc; 2844 rich_location richloc (line_table, expr); 2845 richloc.add_fixit_insert_before (open_paren, "(longer *)"); 2846 source_range victim = {expr_start, expr_finish}; 2847 richloc.add_fixit_remove (victim); 2848 2849 /* These fixits are not consolidated. */ 2850 ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 2851 2852 /* But the corrections are. */ 2853 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2854 ASSERT_STREQ ("\n" 2855 " foo *f = (foo *)ptr->field;\n" 2856 " ^~~~~~~~~~\n" 2857 " -----------------\n" 2858 " (longer *)(foo *)\n", 2859 pp_formatted_text (dc.printer)); 2860 } 2861 2862 /* Example of insertion fix-it hints that would overlap. */ 2863 { 2864 test_diagnostic_context dc; 2865 rich_location richloc (line_table, expr); 2866 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST"); 2867 richloc.add_fixit_insert_after (close_paren, "TEST"); 2868 2869 /* The first insertion is long enough that if printed naively, 2870 it would overlap with the second. 2871 Verify that they are printed as a single replacement. */ 2872 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2873 ASSERT_STREQ ("\n" 2874 " foo *f = (foo *)ptr->field;\n" 2875 " ^~~~~~~~~~\n" 2876 " -------\n" 2877 " LONGER THAN THE CAST(foo *)TEST\n", 2878 pp_formatted_text (dc.printer)); 2879 } 2880 } 2881 2882 /* Verify that the line_corrections machinery correctly prints 2883 overlapping fixit-hints that have been added in the wrong 2884 order. 2885 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/ 2886 2887 static void 2888 test_overlapped_fixit_printing_2 (const line_table_case &case_) 2889 { 2890 /* Create a tempfile and write some text to it. 2891 ...000000000111111111122222222223333333333. 2892 ...123456789012345678901234567890123456789. */ 2893 const char *content 2894 = ("int a5[][0][0] = { 1, 2 };\n"); 2895 temp_source_file tmp (SELFTEST_LOCATION, ".c", content); 2896 line_table_test ltt (case_); 2897 2898 const line_map_ordinary *ord_map 2899 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false, 2900 tmp.get_filename (), 0)); 2901 2902 linemap_line_start (line_table, 1, 100); 2903 2904 const location_t final_line_end 2905 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100); 2906 2907 /* Don't attempt to run the tests if column data might be unavailable. */ 2908 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 2909 return; 2910 2911 const location_t col_1 2912 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1); 2913 const location_t col_20 2914 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20); 2915 const location_t col_21 2916 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21); 2917 const location_t col_23 2918 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23); 2919 const location_t col_25 2920 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25); 2921 2922 /* Two insertions, in the wrong order. */ 2923 { 2924 rich_location richloc (line_table, col_20); 2925 richloc.add_fixit_insert_before (col_23, "{"); 2926 richloc.add_fixit_insert_before (col_21, "}"); 2927 2928 /* These fixits should be accepted; they can't be consolidated. */ 2929 ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 2930 const fixit_hint *hint_0 = richloc.get_fixit_hint (0); 2931 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0)); 2932 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0)); 2933 const fixit_hint *hint_1 = richloc.get_fixit_hint (1); 2934 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1)); 2935 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1)); 2936 2937 /* Verify that they're printed correctly. */ 2938 test_diagnostic_context dc; 2939 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2940 ASSERT_STREQ ("\n" 2941 " int a5[][0][0] = { 1, 2 };\n" 2942 " ^\n" 2943 " } {\n", 2944 pp_formatted_text (dc.printer)); 2945 } 2946 2947 /* Various overlapping insertions, some occurring "out of order" 2948 (reproducing the fix-it hints from PR c/81405). */ 2949 { 2950 test_diagnostic_context dc; 2951 rich_location richloc (line_table, col_20); 2952 2953 richloc.add_fixit_insert_before (col_20, "{{"); 2954 richloc.add_fixit_insert_before (col_21, "}}"); 2955 richloc.add_fixit_insert_before (col_23, "{"); 2956 richloc.add_fixit_insert_before (col_21, "}"); 2957 richloc.add_fixit_insert_before (col_23, "{{"); 2958 richloc.add_fixit_insert_before (col_25, "}"); 2959 richloc.add_fixit_insert_before (col_21, "}"); 2960 richloc.add_fixit_insert_before (col_1, "{"); 2961 richloc.add_fixit_insert_before (col_25, "}"); 2962 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 2963 ASSERT_STREQ ("\n" 2964 " int a5[][0][0] = { 1, 2 };\n" 2965 " ^\n" 2966 " { -----\n" 2967 " {{1}}}}, {{{2 }}\n", 2968 pp_formatted_text (dc.printer)); 2969 } 2970 } 2971 2972 /* Insertion fix-it hint: adding a "break;" on a line by itself. */ 2973 2974 static void 2975 test_fixit_insert_containing_newline (const line_table_case &case_) 2976 { 2977 /* Create a tempfile and write some text to it. 2978 .........................0000000001111111. 2979 .........................1234567890123456. */ 2980 const char *old_content = (" case 'a':\n" /* line 1. */ 2981 " x = a;\n" /* line 2. */ 2982 " case 'b':\n" /* line 3. */ 2983 " x = b;\n");/* line 4. */ 2984 2985 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content); 2986 line_table_test ltt (case_); 2987 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3); 2988 2989 location_t case_start = linemap_position_for_column (line_table, 5); 2990 location_t case_finish = linemap_position_for_column (line_table, 13); 2991 location_t case_loc = make_location (case_start, case_start, case_finish); 2992 location_t line_start = linemap_position_for_column (line_table, 1); 2993 2994 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS) 2995 return; 2996 2997 /* Add a "break;" on a line by itself before line 3 i.e. before 2998 column 1 of line 3. */ 2999 { 3000 rich_location richloc (line_table, case_loc); 3001 richloc.add_fixit_insert_before (line_start, " break;\n"); 3002 test_diagnostic_context dc; 3003 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3004 ASSERT_STREQ ("\n" 3005 "+ break;\n" 3006 " case 'b':\n" 3007 " ^~~~~~~~~\n", 3008 pp_formatted_text (dc.printer)); 3009 } 3010 3011 /* Verify that attempts to add text with a newline fail when the 3012 insertion point is *not* at the start of a line. */ 3013 { 3014 rich_location richloc (line_table, case_loc); 3015 richloc.add_fixit_insert_before (case_start, "break;\n"); 3016 ASSERT_TRUE (richloc.seen_impossible_fixit_p ()); 3017 test_diagnostic_context dc; 3018 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3019 ASSERT_STREQ ("\n" 3020 " case 'b':\n" 3021 " ^~~~~~~~~\n", 3022 pp_formatted_text (dc.printer)); 3023 } 3024 } 3025 3026 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top 3027 of the file, where the fix-it is printed in a different line-span 3028 to the primary range of the diagnostic. */ 3029 3030 static void 3031 test_fixit_insert_containing_newline_2 (const line_table_case &case_) 3032 { 3033 /* Create a tempfile and write some text to it. 3034 .........................0000000001111111. 3035 .........................1234567890123456. */ 3036 const char *old_content = ("test (int ch)\n" /* line 1. */ 3037 "{\n" /* line 2. */ 3038 " putchar (ch);\n" /* line 3. */ 3039 "}\n"); /* line 4. */ 3040 3041 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content); 3042 line_table_test ltt (case_); 3043 3044 const line_map_ordinary *ord_map = linemap_check_ordinary 3045 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0)); 3046 linemap_line_start (line_table, 1, 100); 3047 3048 /* The primary range is the "putchar" token. */ 3049 location_t putchar_start 3050 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2); 3051 location_t putchar_finish 3052 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8); 3053 location_t putchar_loc 3054 = make_location (putchar_start, putchar_start, putchar_finish); 3055 rich_location richloc (line_table, putchar_loc); 3056 3057 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */ 3058 location_t file_start 3059 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1); 3060 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n"); 3061 3062 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS) 3063 return; 3064 3065 test_diagnostic_context dc; 3066 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3067 ASSERT_STREQ ("\n" 3068 "FILENAME:1:1:\n" 3069 "+#include <stdio.h>\n" 3070 " test (int ch)\n" 3071 "FILENAME:3:2:\n" 3072 " putchar (ch);\n" 3073 " ^~~~~~~\n", 3074 pp_formatted_text (dc.printer)); 3075 } 3076 3077 /* Replacement fix-it hint containing a newline. 3078 This will fail, as newlines are only supported when inserting at the 3079 beginning of a line. */ 3080 3081 static void 3082 test_fixit_replace_containing_newline (const line_table_case &case_) 3083 { 3084 /* Create a tempfile and write some text to it. 3085 .........................0000000001111. 3086 .........................1234567890123. */ 3087 const char *old_content = "foo = bar ();\n"; 3088 3089 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content); 3090 line_table_test ltt (case_); 3091 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1); 3092 3093 /* Replace the " = " with "\n = ", as if we were reformatting an 3094 overly long line. */ 3095 location_t start = linemap_position_for_column (line_table, 4); 3096 location_t finish = linemap_position_for_column (line_table, 6); 3097 location_t loc = linemap_position_for_column (line_table, 13); 3098 rich_location richloc (line_table, loc); 3099 source_range range = source_range::from_locations (start, finish); 3100 richloc.add_fixit_replace (range, "\n ="); 3101 3102 /* Arbitrary newlines are not yet supported within fix-it hints, so 3103 the fix-it should not be displayed. */ 3104 ASSERT_TRUE (richloc.seen_impossible_fixit_p ()); 3105 3106 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS) 3107 return; 3108 3109 test_diagnostic_context dc; 3110 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3111 ASSERT_STREQ ("\n" 3112 " foo = bar ();\n" 3113 " ^\n", 3114 pp_formatted_text (dc.printer)); 3115 } 3116 3117 /* Fix-it hint, attempting to delete a newline. 3118 This will fail, as we currently only support fix-it hints that 3119 affect one line at a time. */ 3120 3121 static void 3122 test_fixit_deletion_affecting_newline (const line_table_case &case_) 3123 { 3124 /* Create a tempfile and write some text to it. 3125 ..........................0000000001111. 3126 ..........................1234567890123. */ 3127 const char *old_content = ("foo = bar (\n" 3128 " );\n"); 3129 3130 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content); 3131 line_table_test ltt (case_); 3132 const line_map_ordinary *ord_map = linemap_check_ordinary 3133 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0)); 3134 linemap_line_start (line_table, 1, 100); 3135 3136 /* Attempt to delete the " (\n...)". */ 3137 location_t start 3138 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10); 3139 location_t caret 3140 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11); 3141 location_t finish 3142 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7); 3143 location_t loc = make_location (caret, start, finish); 3144 rich_location richloc (line_table, loc); 3145 richloc. add_fixit_remove (); 3146 3147 /* Fix-it hints that affect more than one line are not yet supported, so 3148 the fix-it should not be displayed. */ 3149 ASSERT_TRUE (richloc.seen_impossible_fixit_p ()); 3150 3151 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS) 3152 return; 3153 3154 test_diagnostic_context dc; 3155 diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3156 ASSERT_STREQ ("\n" 3157 " foo = bar (\n" 3158 " ~^\n" 3159 " );\n" 3160 " ~ \n", 3161 pp_formatted_text (dc.printer)); 3162 } 3163 3164 /* Run all of the selftests within this file. */ 3165 3166 void 3167 diagnostic_show_locus_c_tests () 3168 { 3169 test_line_span (); 3170 3171 test_layout_range_for_single_point (); 3172 test_layout_range_for_single_line (); 3173 test_layout_range_for_multiple_lines (); 3174 3175 test_get_line_width_without_trailing_whitespace (); 3176 3177 test_diagnostic_show_locus_unknown_location (); 3178 3179 for_each_line_table_case (test_diagnostic_show_locus_one_liner); 3180 for_each_line_table_case (test_add_location_if_nearby); 3181 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines); 3182 for_each_line_table_case (test_fixit_consolidation); 3183 for_each_line_table_case (test_overlapped_fixit_printing); 3184 for_each_line_table_case (test_overlapped_fixit_printing_2); 3185 for_each_line_table_case (test_fixit_insert_containing_newline); 3186 for_each_line_table_case (test_fixit_insert_containing_newline_2); 3187 for_each_line_table_case (test_fixit_replace_containing_newline); 3188 for_each_line_table_case (test_fixit_deletion_affecting_newline); 3189 } 3190 3191 } // namespace selftest 3192 3193 #endif /* #if CHECKING_P */ 3194