1 1.1 mrg /* Diagnostic subroutines for printing source-code 2 1.1 mrg Copyright (C) 1999-2022 Free Software Foundation, Inc. 3 1.1 mrg Contributed by Gabriel Dos Reis <gdr (at) codesourcery.com> 4 1.1 mrg 5 1.1 mrg This file is part of GCC. 6 1.1 mrg 7 1.1 mrg GCC is free software; you can redistribute it and/or modify it under 8 1.1 mrg the terms of the GNU General Public License as published by the Free 9 1.1 mrg Software Foundation; either version 3, or (at your option) any later 10 1.1 mrg version. 11 1.1 mrg 12 1.1 mrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13 1.1 mrg WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 1.1 mrg FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 1.1 mrg for more details. 16 1.1 mrg 17 1.1 mrg You should have received a copy of the GNU General Public License 18 1.1 mrg along with GCC; see the file COPYING3. If not see 19 1.1 mrg <http://www.gnu.org/licenses/>. */ 20 1.1 mrg 21 1.1 mrg #include "config.h" 22 1.1 mrg #include "system.h" 23 1.1 mrg #include "coretypes.h" 24 1.1 mrg #include "version.h" 25 1.1 mrg #include "demangle.h" 26 1.1 mrg #include "intl.h" 27 1.1 mrg #include "backtrace.h" 28 1.1 mrg #include "diagnostic.h" 29 1.1 mrg #include "diagnostic-color.h" 30 1.1 mrg #include "gcc-rich-location.h" 31 1.1 mrg #include "selftest.h" 32 1.1 mrg #include "selftest-diagnostic.h" 33 1.1 mrg #include "cpplib.h" 34 1.1 mrg 35 1.1 mrg #ifdef HAVE_TERMIOS_H 36 1.1 mrg # include <termios.h> 37 1.1 mrg #endif 38 1.1 mrg 39 1.1 mrg #ifdef GWINSZ_IN_SYS_IOCTL 40 1.1 mrg # include <sys/ioctl.h> 41 1.1 mrg #endif 42 1.1 mrg 43 1.1 mrg /* Disable warnings about quoting issues in the pp_xxx calls below 44 1.1 mrg that (intentionally) don't follow GCC diagnostic conventions. */ 45 1.1 mrg #if __GNUC__ >= 10 46 1.1 mrg # pragma GCC diagnostic push 47 1.1 mrg # pragma GCC diagnostic ignored "-Wformat-diag" 48 1.1 mrg #endif 49 1.1 mrg 50 1.1 mrg /* Classes for rendering source code and diagnostics, within an 51 1.1 mrg anonymous namespace. 52 1.1 mrg The work is done by "class layout", which embeds and uses 53 1.1 mrg "class colorizer" and "class layout_range" to get things done. */ 54 1.1 mrg 55 1.1 mrg namespace { 56 1.1 mrg 57 1.1 mrg /* The state at a given point of the source code, assuming that we're 58 1.1 mrg in a range: which range are we in, and whether we should draw a caret at 59 1.1 mrg this point. */ 60 1.1 mrg 61 1.1 mrg struct point_state 62 1.1 mrg { 63 1.1 mrg int range_idx; 64 1.1 mrg bool draw_caret_p; 65 1.1 mrg }; 66 1.1 mrg 67 1.1 mrg /* A class to inject colorization codes when printing the diagnostic locus. 68 1.1 mrg 69 1.1 mrg It has one kind of colorization for each of: 70 1.1 mrg - normal text 71 1.1 mrg - range 0 (the "primary location") 72 1.1 mrg - range 1 73 1.1 mrg - range 2 74 1.1 mrg 75 1.1 mrg The class caches the lookup of the color codes for the above. 76 1.1 mrg 77 1.1 mrg The class also has responsibility for tracking which of the above is 78 1.1 mrg active, filtering out unnecessary changes. This allows 79 1.1 mrg layout::print_source_line and layout::print_annotation_line 80 1.1 mrg to simply request a colorization code for *every* character they print, 81 1.1 mrg via this class, and have the filtering be done for them here. */ 82 1.1 mrg 83 1.1 mrg class colorizer 84 1.1 mrg { 85 1.1 mrg public: 86 1.1 mrg colorizer (diagnostic_context *context, 87 1.1 mrg diagnostic_t diagnostic_kind); 88 1.1 mrg ~colorizer (); 89 1.1 mrg 90 1.1 mrg void set_range (int range_idx) 91 1.1 mrg { 92 1.1 mrg /* Normally we emphasize the primary location, then alternate between 93 1.1 mrg two colors for the secondary locations. 94 1.1 mrg But if we're printing a run of events in a diagnostic path, that 95 1.1 mrg makes no sense, so print all of them with the same colorization. */ 96 1.1 mrg if (m_diagnostic_kind == DK_DIAGNOSTIC_PATH) 97 1.1 mrg set_state (0); 98 1.1 mrg else 99 1.1 mrg set_state (range_idx); 100 1.1 mrg } 101 1.1 mrg void set_normal_text () { set_state (STATE_NORMAL_TEXT); } 102 1.1 mrg void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); } 103 1.1 mrg void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); } 104 1.1 mrg 105 1.1 mrg private: 106 1.1 mrg void set_state (int state); 107 1.1 mrg void begin_state (int state); 108 1.1 mrg void finish_state (int state); 109 1.1 mrg const char *get_color_by_name (const char *); 110 1.1 mrg 111 1.1 mrg private: 112 1.1 mrg static const int STATE_NORMAL_TEXT = -1; 113 1.1 mrg static const int STATE_FIXIT_INSERT = -2; 114 1.1 mrg static const int STATE_FIXIT_DELETE = -3; 115 1.1 mrg 116 1.1 mrg diagnostic_context *m_context; 117 1.1 mrg diagnostic_t m_diagnostic_kind; 118 1.1 mrg int m_current_state; 119 1.1 mrg const char *m_range1; 120 1.1 mrg const char *m_range2; 121 1.1 mrg const char *m_fixit_insert; 122 1.1 mrg const char *m_fixit_delete; 123 1.1 mrg const char *m_stop_color; 124 1.1 mrg }; 125 1.1 mrg 126 1.1 mrg /* In order to handle multibyte sources properly, all of this logic needs to be 127 1.1 mrg aware of the distinction between the number of bytes and the number of 128 1.1 mrg display columns occupied by a character, which are not the same for non-ASCII 129 1.1 mrg characters. For example, the Unicode pi symbol, U+03C0, is encoded in UTF-8 130 1.1 mrg as "\xcf\x80", and thus occupies 2 bytes of space while only occupying 1 131 1.1 mrg display column when it is output. A typical emoji, such as U+1F602 (in 132 1.1 mrg UTF-8, "\xf0\x9f\x98\x82"), requires 4 bytes and has a display width of 2. 133 1.1 mrg 134 1.1 mrg The below example line, which is also used for selftests below, shows how the 135 1.1 mrg display column and byte column are related: 136 1.1 mrg 137 1.1 mrg 0000000001111111111222222 display 138 1.1 mrg 1234567890123456789012345 columns 139 1.1 mrg SS_foo = P_bar.SS_fieldP; 140 1.1 mrg 0000000111111111222222223 byte 141 1.1 mrg 1356789012456789134567891 columns 142 1.1 mrg 143 1.1 mrg Here SS represents the two display columns for the U+1F602 emoji, and P 144 1.1 mrg represents the one display column for the U+03C0 pi symbol. As an example, a 145 1.1 mrg diagnostic pointing to the final P on this line is at byte column 29 and 146 1.1 mrg display column 24. This reflects the fact that the three extended characters 147 1.1 mrg before the final P occupy cumulatively 5 more bytes than they do display 148 1.1 mrg columns (a difference of 2 for each of the two SSs, and one for the other P). 149 1.1 mrg 150 1.1 mrg One or the other of the two column units is more useful depending on the 151 1.1 mrg context. For instance, in order to output the caret at the correct location, 152 1.1 mrg we need to count display columns; in order to colorize a source line, we need 153 1.1 mrg to count the bytes. All locations are provided to us as byte counts, which 154 1.1 mrg we augment with the display column on demand so that it can be used when 155 1.1 mrg needed. This is not the most efficient way to do things since it requires 156 1.1 mrg looping over the whole line each time, but it should be fine for the purpose 157 1.1 mrg of outputting diagnostics. 158 1.1 mrg 159 1.1 mrg In order to keep straight which units (byte or display) are in use at a 160 1.1 mrg given time, the following enum lets us specify that explicitly. */ 161 1.1 mrg 162 1.1 mrg enum column_unit { 163 1.1 mrg /* Measured in raw bytes. */ 164 1.1 mrg CU_BYTES = 0, 165 1.1 mrg 166 1.1 mrg /* Measured in display units. */ 167 1.1 mrg CU_DISPLAY_COLS, 168 1.1 mrg 169 1.1 mrg /* For arrays indexed by column_unit. */ 170 1.1 mrg CU_NUM_UNITS 171 1.1 mrg }; 172 1.1 mrg 173 1.1 mrg /* Utility class to augment an exploc with the corresponding display column. */ 174 1.1 mrg 175 1.1 mrg class exploc_with_display_col : public expanded_location 176 1.1 mrg { 177 1.1 mrg public: 178 1.1 mrg exploc_with_display_col (const expanded_location &exploc, 179 1.1 mrg const cpp_char_column_policy &policy, 180 1.1 mrg enum location_aspect aspect) 181 1.1 mrg : expanded_location (exploc), 182 1.1 mrg m_display_col (location_compute_display_column (exploc, policy)) 183 1.1 mrg { 184 1.1 mrg if (exploc.column > 0) 185 1.1 mrg { 186 1.1 mrg /* m_display_col is now the final column of the byte. 187 1.1 mrg If escaping has happened, we may want the first column instead. */ 188 1.1 mrg if (aspect != LOCATION_ASPECT_FINISH) 189 1.1 mrg { 190 1.1 mrg expanded_location prev_exploc (exploc); 191 1.1 mrg prev_exploc.column--; 192 1.1 mrg int prev_display_col 193 1.1 mrg = (location_compute_display_column (prev_exploc, policy)); 194 1.1 mrg m_display_col = prev_display_col + 1; 195 1.1 mrg } 196 1.1 mrg } 197 1.1 mrg } 198 1.1 mrg 199 1.1 mrg int m_display_col; 200 1.1 mrg }; 201 1.1 mrg 202 1.1 mrg 203 1.1 mrg /* A point within a layout_range; similar to an exploc_with_display_col, 204 1.1 mrg but after filtering on file. */ 205 1.1 mrg 206 1.1 mrg class layout_point 207 1.1 mrg { 208 1.1 mrg public: 209 1.1 mrg layout_point (const exploc_with_display_col &exploc) 210 1.1 mrg : m_line (exploc.line) 211 1.1 mrg { 212 1.1 mrg m_columns[CU_BYTES] = exploc.column; 213 1.1 mrg m_columns[CU_DISPLAY_COLS] = exploc.m_display_col; 214 1.1 mrg } 215 1.1 mrg 216 1.1 mrg linenum_type m_line; 217 1.1 mrg int m_columns[CU_NUM_UNITS]; 218 1.1 mrg }; 219 1.1 mrg 220 1.1 mrg /* A class for use by "class layout" below: a filtered location_range. */ 221 1.1 mrg 222 1.1 mrg class layout_range 223 1.1 mrg { 224 1.1 mrg public: 225 1.1 mrg layout_range (const exploc_with_display_col &start_exploc, 226 1.1 mrg const exploc_with_display_col &finish_exploc, 227 1.1 mrg enum range_display_kind range_display_kind, 228 1.1 mrg const exploc_with_display_col &caret_exploc, 229 1.1 mrg unsigned original_idx, 230 1.1 mrg const range_label *label); 231 1.1 mrg 232 1.1 mrg bool contains_point (linenum_type row, int column, 233 1.1 mrg enum column_unit col_unit) const; 234 1.1 mrg bool intersects_line_p (linenum_type row) const; 235 1.1 mrg 236 1.1 mrg layout_point m_start; 237 1.1 mrg layout_point m_finish; 238 1.1 mrg enum range_display_kind m_range_display_kind; 239 1.1 mrg layout_point m_caret; 240 1.1 mrg unsigned m_original_idx; 241 1.1 mrg const range_label *m_label; 242 1.1 mrg }; 243 1.1 mrg 244 1.1 mrg /* A struct for use by layout::print_source_line for telling 245 1.1 mrg layout::print_annotation_line the extents of the source line that 246 1.1 mrg it printed, so that underlines can be clipped appropriately. Units 247 1.1 mrg are 1-based display columns. */ 248 1.1 mrg 249 1.1 mrg struct line_bounds 250 1.1 mrg { 251 1.1 mrg int m_first_non_ws_disp_col; 252 1.1 mrg int m_last_non_ws_disp_col; 253 1.1 mrg 254 1.1 mrg line_bounds () 255 1.1 mrg { 256 1.1 mrg m_first_non_ws_disp_col = INT_MAX; 257 1.1 mrg m_last_non_ws_disp_col = 0; 258 1.1 mrg } 259 1.1 mrg }; 260 1.1 mrg 261 1.1 mrg /* A range of contiguous source lines within a layout (e.g. "lines 5-10" 262 1.1 mrg or "line 23"). During the layout ctor, layout::calculate_line_spans 263 1.1 mrg splits the pertinent source lines into a list of disjoint line_span 264 1.1 mrg instances (e.g. lines 5-10, lines 15-20, line 23). */ 265 1.1 mrg 266 1.1 mrg class line_span 267 1.1 mrg { 268 1.1 mrg public: 269 1.1 mrg line_span (linenum_type first_line, linenum_type last_line) 270 1.1 mrg : m_first_line (first_line), m_last_line (last_line) 271 1.1 mrg { 272 1.1 mrg gcc_assert (first_line <= last_line); 273 1.1 mrg } 274 1.1 mrg linenum_type get_first_line () const { return m_first_line; } 275 1.1 mrg linenum_type get_last_line () const { return m_last_line; } 276 1.1 mrg 277 1.1 mrg bool contains_line_p (linenum_type line) const 278 1.1 mrg { 279 1.1 mrg return line >= m_first_line && line <= m_last_line; 280 1.1 mrg } 281 1.1 mrg 282 1.1 mrg static int comparator (const void *p1, const void *p2) 283 1.1 mrg { 284 1.1 mrg const line_span *ls1 = (const line_span *)p1; 285 1.1 mrg const line_span *ls2 = (const line_span *)p2; 286 1.1 mrg int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line); 287 1.1 mrg if (first_line_cmp) 288 1.1 mrg return first_line_cmp; 289 1.1 mrg return compare (ls1->m_last_line, ls2->m_last_line); 290 1.1 mrg } 291 1.1 mrg 292 1.1 mrg linenum_type m_first_line; 293 1.1 mrg linenum_type m_last_line; 294 1.1 mrg }; 295 1.1 mrg 296 1.1 mrg #if CHECKING_P 297 1.1 mrg 298 1.1 mrg /* Selftests for line_span. */ 299 1.1 mrg 300 1.1 mrg static void 301 1.1 mrg test_line_span () 302 1.1 mrg { 303 1.1 mrg line_span line_one (1, 1); 304 1.1 mrg ASSERT_EQ (1, line_one.get_first_line ()); 305 1.1 mrg ASSERT_EQ (1, line_one.get_last_line ()); 306 1.1 mrg ASSERT_FALSE (line_one.contains_line_p (0)); 307 1.1 mrg ASSERT_TRUE (line_one.contains_line_p (1)); 308 1.1 mrg ASSERT_FALSE (line_one.contains_line_p (2)); 309 1.1 mrg 310 1.1 mrg line_span lines_1_to_3 (1, 3); 311 1.1 mrg ASSERT_EQ (1, lines_1_to_3.get_first_line ()); 312 1.1 mrg ASSERT_EQ (3, lines_1_to_3.get_last_line ()); 313 1.1 mrg ASSERT_TRUE (lines_1_to_3.contains_line_p (1)); 314 1.1 mrg ASSERT_TRUE (lines_1_to_3.contains_line_p (3)); 315 1.1 mrg 316 1.1 mrg ASSERT_EQ (0, line_span::comparator (&line_one, &line_one)); 317 1.1 mrg ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0); 318 1.1 mrg ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0); 319 1.1 mrg 320 1.1 mrg /* A linenum > 2^31. */ 321 1.1 mrg const linenum_type LARGEST_LINE = 0xffffffff; 322 1.1 mrg line_span largest_line (LARGEST_LINE, LARGEST_LINE); 323 1.1 mrg ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ()); 324 1.1 mrg ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ()); 325 1.1 mrg 326 1.1 mrg ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0); 327 1.1 mrg ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0); 328 1.1 mrg } 329 1.1 mrg 330 1.1 mrg #endif /* #if CHECKING_P */ 331 1.1 mrg 332 1.1 mrg /* A bundle of information containing how to print unicode 333 1.1 mrg characters and bytes when quoting source code. 334 1.1 mrg 335 1.1 mrg Provides a unified place to support escaping some subset 336 1.1 mrg of characters to some format. 337 1.1 mrg 338 1.1 mrg Extends char_column_policy; printing is split out to avoid 339 1.1 mrg libcpp having to know about pretty_printer. */ 340 1.1 mrg 341 1.1 mrg struct char_display_policy : public cpp_char_column_policy 342 1.1 mrg { 343 1.1 mrg public: 344 1.1 mrg char_display_policy (int tabstop, 345 1.1 mrg int (*width_cb) (cppchar_t c), 346 1.1 mrg void (*print_cb) (pretty_printer *pp, 347 1.1 mrg const cpp_decoded_char &cp)) 348 1.1 mrg : cpp_char_column_policy (tabstop, width_cb), 349 1.1 mrg m_print_cb (print_cb) 350 1.1 mrg { 351 1.1 mrg } 352 1.1 mrg 353 1.1 mrg void (*m_print_cb) (pretty_printer *pp, 354 1.1 mrg const cpp_decoded_char &cp); 355 1.1 mrg }; 356 1.1 mrg 357 1.1 mrg /* A class to control the overall layout when printing a diagnostic. 358 1.1 mrg 359 1.1 mrg The layout is determined within the constructor. 360 1.1 mrg It is then printed by repeatedly calling the "print_source_line", 361 1.1 mrg "print_annotation_line" and "print_any_fixits" methods. 362 1.1 mrg 363 1.1 mrg We assume we have disjoint ranges. */ 364 1.1 mrg 365 1.1 mrg class layout 366 1.1 mrg { 367 1.1 mrg public: 368 1.1 mrg layout (diagnostic_context *context, 369 1.1 mrg rich_location *richloc, 370 1.1 mrg diagnostic_t diagnostic_kind); 371 1.1 mrg 372 1.1 mrg bool maybe_add_location_range (const location_range *loc_range, 373 1.1 mrg unsigned original_idx, 374 1.1 mrg bool restrict_to_current_line_spans); 375 1.1 mrg 376 1.1 mrg int get_num_line_spans () const { return m_line_spans.length (); } 377 1.1 mrg const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; } 378 1.1 mrg 379 1.1 mrg int get_linenum_width () const { return m_linenum_width; } 380 1.1 mrg int get_x_offset_display () const { return m_x_offset_display; } 381 1.1 mrg 382 1.1 mrg void print_gap_in_line_numbering (); 383 1.1 mrg bool print_heading_for_line_span_index_p (int line_span_idx) const; 384 1.1 mrg 385 1.1 mrg expanded_location get_expanded_location (const line_span *) const; 386 1.1 mrg 387 1.1 mrg void print_line (linenum_type row); 388 1.1 mrg 389 1.1 mrg void on_bad_codepoint (const char *ptr, cppchar_t ch, size_t ch_sz); 390 1.1 mrg 391 1.1 mrg private: 392 1.1 mrg bool will_show_line_p (linenum_type row) const; 393 1.1 mrg void print_leading_fixits (linenum_type row); 394 1.1 mrg line_bounds print_source_line (linenum_type row, const char *line, 395 1.1 mrg int line_bytes); 396 1.1 mrg bool should_print_annotation_line_p (linenum_type row) const; 397 1.1 mrg void start_annotation_line (char margin_char = ' ') const; 398 1.1 mrg void print_annotation_line (linenum_type row, const line_bounds lbounds); 399 1.1 mrg void print_any_labels (linenum_type row); 400 1.1 mrg void print_trailing_fixits (linenum_type row); 401 1.1 mrg 402 1.1 mrg bool annotation_line_showed_range_p (linenum_type line, int start_column, 403 1.1 mrg int finish_column) const; 404 1.1 mrg void show_ruler (int max_column) const; 405 1.1 mrg 406 1.1 mrg bool validate_fixit_hint_p (const fixit_hint *hint); 407 1.1 mrg 408 1.1 mrg void calculate_line_spans (); 409 1.1 mrg void calculate_linenum_width (); 410 1.1 mrg void calculate_x_offset_display (); 411 1.1 mrg 412 1.1 mrg void print_newline (); 413 1.1 mrg 414 1.1 mrg bool 415 1.1 mrg get_state_at_point (/* Inputs. */ 416 1.1 mrg linenum_type row, int column, 417 1.1 mrg int first_non_ws, int last_non_ws, 418 1.1 mrg enum column_unit col_unit, 419 1.1 mrg /* Outputs. */ 420 1.1 mrg point_state *out_state); 421 1.1 mrg 422 1.1 mrg int 423 1.1 mrg get_x_bound_for_row (linenum_type row, int caret_column, 424 1.1 mrg int last_non_ws); 425 1.1 mrg 426 1.1 mrg void 427 1.1 mrg move_to_column (int *column, int dest_column, bool add_left_margin); 428 1.1 mrg 429 1.1 mrg private: 430 1.1 mrg diagnostic_context *m_context; 431 1.1 mrg pretty_printer *m_pp; 432 1.1 mrg char_display_policy m_policy; 433 1.1 mrg location_t m_primary_loc; 434 1.1 mrg exploc_with_display_col m_exploc; 435 1.1 mrg colorizer m_colorizer; 436 1.1 mrg bool m_colorize_source_p; 437 1.1 mrg bool m_show_labels_p; 438 1.1 mrg bool m_show_line_numbers_p; 439 1.1 mrg bool m_diagnostic_path_p; 440 1.1 mrg auto_vec <layout_range> m_layout_ranges; 441 1.1 mrg auto_vec <const fixit_hint *> m_fixit_hints; 442 1.1 mrg auto_vec <line_span> m_line_spans; 443 1.1 mrg int m_linenum_width; 444 1.1 mrg int m_x_offset_display; 445 1.1 mrg bool m_escape_on_output; 446 1.1 mrg }; 447 1.1 mrg 448 1.1 mrg /* Implementation of "class colorizer". */ 449 1.1 mrg 450 1.1 mrg /* The constructor for "colorizer". Lookup and store color codes for the 451 1.1 mrg different kinds of things we might need to print. */ 452 1.1 mrg 453 1.1 mrg colorizer::colorizer (diagnostic_context *context, 454 1.1 mrg diagnostic_t diagnostic_kind) : 455 1.1 mrg m_context (context), 456 1.1 mrg m_diagnostic_kind (diagnostic_kind), 457 1.1 mrg m_current_state (STATE_NORMAL_TEXT) 458 1.1 mrg { 459 1.1 mrg m_range1 = get_color_by_name ("range1"); 460 1.1 mrg m_range2 = get_color_by_name ("range2"); 461 1.1 mrg m_fixit_insert = get_color_by_name ("fixit-insert"); 462 1.1 mrg m_fixit_delete = get_color_by_name ("fixit-delete"); 463 1.1 mrg m_stop_color = colorize_stop (pp_show_color (context->printer)); 464 1.1 mrg } 465 1.1 mrg 466 1.1 mrg /* The destructor for "colorize". If colorization is on, print a code to 467 1.1 mrg turn it off. */ 468 1.1 mrg 469 1.1 mrg colorizer::~colorizer () 470 1.1 mrg { 471 1.1 mrg finish_state (m_current_state); 472 1.1 mrg } 473 1.1 mrg 474 1.1 mrg /* Update state, printing color codes if necessary if there's a state 475 1.1 mrg change. */ 476 1.1 mrg 477 1.1 mrg void 478 1.1 mrg colorizer::set_state (int new_state) 479 1.1 mrg { 480 1.1 mrg if (m_current_state != new_state) 481 1.1 mrg { 482 1.1 mrg finish_state (m_current_state); 483 1.1 mrg m_current_state = new_state; 484 1.1 mrg begin_state (new_state); 485 1.1 mrg } 486 1.1 mrg } 487 1.1 mrg 488 1.1 mrg /* Turn on any colorization for STATE. */ 489 1.1 mrg 490 1.1 mrg void 491 1.1 mrg colorizer::begin_state (int state) 492 1.1 mrg { 493 1.1 mrg switch (state) 494 1.1 mrg { 495 1.1 mrg case STATE_NORMAL_TEXT: 496 1.1 mrg break; 497 1.1 mrg 498 1.1 mrg case STATE_FIXIT_INSERT: 499 1.1 mrg pp_string (m_context->printer, m_fixit_insert); 500 1.1 mrg break; 501 1.1 mrg 502 1.1 mrg case STATE_FIXIT_DELETE: 503 1.1 mrg pp_string (m_context->printer, m_fixit_delete); 504 1.1 mrg break; 505 1.1 mrg 506 1.1 mrg case 0: 507 1.1 mrg /* Make range 0 be the same color as the "kind" text 508 1.1 mrg (error vs warning vs note). */ 509 1.1 mrg pp_string 510 1.1 mrg (m_context->printer, 511 1.1 mrg colorize_start (pp_show_color (m_context->printer), 512 1.1 mrg diagnostic_get_color_for_kind (m_diagnostic_kind))); 513 1.1 mrg break; 514 1.1 mrg 515 1.1 mrg case 1: 516 1.1 mrg pp_string (m_context->printer, m_range1); 517 1.1 mrg break; 518 1.1 mrg 519 1.1 mrg case 2: 520 1.1 mrg pp_string (m_context->printer, m_range2); 521 1.1 mrg break; 522 1.1 mrg 523 1.1 mrg default: 524 1.1 mrg /* For ranges beyond 2, alternate between color 1 and color 2. */ 525 1.1 mrg { 526 1.1 mrg gcc_assert (state > 2); 527 1.1 mrg pp_string (m_context->printer, 528 1.1 mrg state % 2 ? m_range1 : m_range2); 529 1.1 mrg } 530 1.1 mrg break; 531 1.1 mrg } 532 1.1 mrg } 533 1.1 mrg 534 1.1 mrg /* Turn off any colorization for STATE. */ 535 1.1 mrg 536 1.1 mrg void 537 1.1 mrg colorizer::finish_state (int state) 538 1.1 mrg { 539 1.1 mrg if (state != STATE_NORMAL_TEXT) 540 1.1 mrg pp_string (m_context->printer, m_stop_color); 541 1.1 mrg } 542 1.1 mrg 543 1.1 mrg /* Get the color code for NAME (or the empty string if 544 1.1 mrg colorization is disabled). */ 545 1.1 mrg 546 1.1 mrg const char * 547 1.1 mrg colorizer::get_color_by_name (const char *name) 548 1.1 mrg { 549 1.1 mrg return colorize_start (pp_show_color (m_context->printer), name); 550 1.1 mrg } 551 1.1 mrg 552 1.1 mrg /* Implementation of class layout_range. */ 553 1.1 mrg 554 1.1 mrg /* The constructor for class layout_range. 555 1.1 mrg Initialize various layout_point fields from expanded_location 556 1.1 mrg equivalents; we've already filtered on file. */ 557 1.1 mrg 558 1.1 mrg layout_range::layout_range (const exploc_with_display_col &start_exploc, 559 1.1 mrg const exploc_with_display_col &finish_exploc, 560 1.1 mrg enum range_display_kind range_display_kind, 561 1.1 mrg const exploc_with_display_col &caret_exploc, 562 1.1 mrg unsigned original_idx, 563 1.1 mrg const range_label *label) 564 1.1 mrg : m_start (start_exploc), 565 1.1 mrg m_finish (finish_exploc), 566 1.1 mrg m_range_display_kind (range_display_kind), 567 1.1 mrg m_caret (caret_exploc), 568 1.1 mrg m_original_idx (original_idx), 569 1.1 mrg m_label (label) 570 1.1 mrg { 571 1.1 mrg } 572 1.1 mrg 573 1.1 mrg /* Is (column, row) within the given range? 574 1.1 mrg We've already filtered on the file. 575 1.1 mrg 576 1.1 mrg Ranges are closed (both limits are within the range). 577 1.1 mrg 578 1.1 mrg Example A: a single-line range: 579 1.1 mrg start: (col=22, line=2) 580 1.1 mrg finish: (col=38, line=2) 581 1.1 mrg 582 1.1 mrg |00000011111111112222222222333333333344444444444 583 1.1 mrg |34567890123456789012345678901234567890123456789 584 1.1 mrg --+----------------------------------------------- 585 1.1 mrg 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 586 1.1 mrg 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa 587 1.1 mrg 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 588 1.1 mrg 589 1.1 mrg Example B: a multiline range with 590 1.1 mrg start: (col=14, line=3) 591 1.1 mrg finish: (col=08, line=5) 592 1.1 mrg 593 1.1 mrg |00000011111111112222222222333333333344444444444 594 1.1 mrg |34567890123456789012345678901234567890123456789 595 1.1 mrg --+----------------------------------------------- 596 1.1 mrg 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 597 1.1 mrg 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 598 1.1 mrg 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww 599 1.1 mrg 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww 600 1.1 mrg 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 601 1.1 mrg 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 602 1.1 mrg --+----------------------------------------------- 603 1.1 mrg 604 1.1 mrg Legend: 605 1.1 mrg - 'b' indicates a point *before* the range 606 1.1 mrg - 'S' indicates the start of the range 607 1.1 mrg - 'w' indicates a point within the range 608 1.1 mrg - 'F' indicates the finish of the range (which is 609 1.1 mrg within it). 610 1.1 mrg - 'a' indicates a subsequent point *after* the range. 611 1.1 mrg 612 1.1 mrg COL_UNIT controls whether we check the byte column or 613 1.1 mrg the display column; one or the other is more convenient 614 1.1 mrg depending on the context. */ 615 1.1 mrg 616 1.1 mrg bool 617 1.1 mrg layout_range::contains_point (linenum_type row, int column, 618 1.1 mrg enum column_unit col_unit) const 619 1.1 mrg { 620 1.1 mrg gcc_assert (m_start.m_line <= m_finish.m_line); 621 1.1 mrg /* ...but the equivalent isn't true for the columns; 622 1.1 mrg consider example B in the comment above. */ 623 1.1 mrg 624 1.1 mrg if (row < m_start.m_line) 625 1.1 mrg /* Points before the first line of the range are 626 1.1 mrg outside it (corresponding to line 01 in example A 627 1.1 mrg and lines 01 and 02 in example B above). */ 628 1.1 mrg return false; 629 1.1 mrg 630 1.1 mrg if (row == m_start.m_line) 631 1.1 mrg /* On same line as start of range (corresponding 632 1.1 mrg to line 02 in example A and line 03 in example B). */ 633 1.1 mrg { 634 1.1 mrg if (column < m_start.m_columns[col_unit]) 635 1.1 mrg /* Points on the starting line of the range, but 636 1.1 mrg before the column in which it begins. */ 637 1.1 mrg return false; 638 1.1 mrg 639 1.1 mrg if (row < m_finish.m_line) 640 1.1 mrg /* This is a multiline range; the point 641 1.1 mrg is within it (corresponds to line 03 in example B 642 1.1 mrg from column 14 onwards) */ 643 1.1 mrg return true; 644 1.1 mrg else 645 1.1 mrg { 646 1.1 mrg /* This is a single-line range. */ 647 1.1 mrg gcc_assert (row == m_finish.m_line); 648 1.1 mrg return column <= m_finish.m_columns[col_unit]; 649 1.1 mrg } 650 1.1 mrg } 651 1.1 mrg 652 1.1 mrg /* The point is in a line beyond that containing the 653 1.1 mrg start of the range: lines 03 onwards in example A, 654 1.1 mrg and lines 04 onwards in example B. */ 655 1.1 mrg gcc_assert (row > m_start.m_line); 656 1.1 mrg 657 1.1 mrg if (row > m_finish.m_line) 658 1.1 mrg /* The point is beyond the final line of the range 659 1.1 mrg (lines 03 onwards in example A, and lines 06 onwards 660 1.1 mrg in example B). */ 661 1.1 mrg return false; 662 1.1 mrg 663 1.1 mrg if (row < m_finish.m_line) 664 1.1 mrg { 665 1.1 mrg /* The point is in a line that's fully within a multiline 666 1.1 mrg range (e.g. line 04 in example B). */ 667 1.1 mrg gcc_assert (m_start.m_line < m_finish.m_line); 668 1.1 mrg return true; 669 1.1 mrg } 670 1.1 mrg 671 1.1 mrg gcc_assert (row == m_finish.m_line); 672 1.1 mrg 673 1.1 mrg return column <= m_finish.m_columns[col_unit]; 674 1.1 mrg } 675 1.1 mrg 676 1.1 mrg /* Does this layout_range contain any part of line ROW? */ 677 1.1 mrg 678 1.1 mrg bool 679 1.1 mrg layout_range::intersects_line_p (linenum_type row) const 680 1.1 mrg { 681 1.1 mrg gcc_assert (m_start.m_line <= m_finish.m_line); 682 1.1 mrg if (row < m_start.m_line) 683 1.1 mrg return false; 684 1.1 mrg if (row > m_finish.m_line) 685 1.1 mrg return false; 686 1.1 mrg return true; 687 1.1 mrg } 688 1.1 mrg 689 1.1 mrg #if CHECKING_P 690 1.1 mrg 691 1.1 mrg /* Default for when we don't care what the tab expansion is set to. */ 692 1.1 mrg static const int def_tabstop = 8; 693 1.1 mrg 694 1.1 mrg static cpp_char_column_policy def_policy () 695 1.1 mrg { 696 1.1 mrg return cpp_char_column_policy (def_tabstop, cpp_wcwidth); 697 1.1 mrg } 698 1.1 mrg 699 1.1 mrg /* Create some expanded locations for testing layout_range. The filename 700 1.1 mrg member of the explocs is set to the empty string. This member will only be 701 1.1 mrg inspected by the calls to location_compute_display_column() made from the 702 1.1 mrg layout_point constructors. That function will check for an empty filename 703 1.1 mrg argument and not attempt to open it, rather treating the non-existent data 704 1.1 mrg as if the display width were the same as the byte count. Tests exercising a 705 1.1 mrg real difference between byte count and display width are performed later, 706 1.1 mrg e.g. in test_diagnostic_show_locus_one_liner_utf8(). */ 707 1.1 mrg 708 1.1 mrg static layout_range 709 1.1 mrg make_range (int start_line, int start_col, int end_line, int end_col) 710 1.1 mrg { 711 1.1 mrg const expanded_location start_exploc 712 1.1 mrg = {"", start_line, start_col, NULL, false}; 713 1.1 mrg const expanded_location finish_exploc 714 1.1 mrg = {"", end_line, end_col, NULL, false}; 715 1.1 mrg return layout_range (exploc_with_display_col (start_exploc, def_policy (), 716 1.1 mrg LOCATION_ASPECT_START), 717 1.1 mrg exploc_with_display_col (finish_exploc, def_policy (), 718 1.1 mrg LOCATION_ASPECT_FINISH), 719 1.1 mrg SHOW_RANGE_WITHOUT_CARET, 720 1.1 mrg exploc_with_display_col (start_exploc, def_policy (), 721 1.1 mrg LOCATION_ASPECT_CARET), 722 1.1 mrg 0, NULL); 723 1.1 mrg } 724 1.1 mrg 725 1.1 mrg /* Selftests for layout_range::contains_point and 726 1.1 mrg layout_range::intersects_line_p. */ 727 1.1 mrg 728 1.1 mrg /* Selftest for layout_range, where the layout_range 729 1.1 mrg is a range with start==end i.e. a single point. */ 730 1.1 mrg 731 1.1 mrg static void 732 1.1 mrg test_layout_range_for_single_point () 733 1.1 mrg { 734 1.1 mrg layout_range point = make_range (7, 10, 7, 10); 735 1.1 mrg 736 1.1 mrg /* Tests for layout_range::contains_point. */ 737 1.1 mrg 738 1.1 mrg for (int i = 0; i != CU_NUM_UNITS; ++i) 739 1.1 mrg { 740 1.1 mrg const enum column_unit col_unit = (enum column_unit) i; 741 1.1 mrg 742 1.1 mrg /* Before the line. */ 743 1.1 mrg ASSERT_FALSE (point.contains_point (6, 1, col_unit)); 744 1.1 mrg 745 1.1 mrg /* On the line, but before start. */ 746 1.1 mrg ASSERT_FALSE (point.contains_point (7, 9, col_unit)); 747 1.1 mrg 748 1.1 mrg /* At the point. */ 749 1.1 mrg ASSERT_TRUE (point.contains_point (7, 10, col_unit)); 750 1.1 mrg 751 1.1 mrg /* On the line, after the point. */ 752 1.1 mrg ASSERT_FALSE (point.contains_point (7, 11, col_unit)); 753 1.1 mrg 754 1.1 mrg /* After the line. */ 755 1.1 mrg ASSERT_FALSE (point.contains_point (8, 1, col_unit)); 756 1.1 mrg } 757 1.1 mrg 758 1.1 mrg /* Tests for layout_range::intersects_line_p. */ 759 1.1 mrg ASSERT_FALSE (point.intersects_line_p (6)); 760 1.1 mrg ASSERT_TRUE (point.intersects_line_p (7)); 761 1.1 mrg ASSERT_FALSE (point.intersects_line_p (8)); 762 1.1 mrg } 763 1.1 mrg 764 1.1 mrg /* Selftest for layout_range, where the layout_range 765 1.1 mrg is the single-line range shown as "Example A" above. */ 766 1.1 mrg 767 1.1 mrg static void 768 1.1 mrg test_layout_range_for_single_line () 769 1.1 mrg { 770 1.1 mrg layout_range example_a = make_range (2, 22, 2, 38); 771 1.1 mrg 772 1.1 mrg /* Tests for layout_range::contains_point. */ 773 1.1 mrg 774 1.1 mrg for (int i = 0; i != CU_NUM_UNITS; ++i) 775 1.1 mrg { 776 1.1 mrg const enum column_unit col_unit = (enum column_unit) i; 777 1.1 mrg 778 1.1 mrg /* Before the line. */ 779 1.1 mrg ASSERT_FALSE (example_a.contains_point (1, 1, col_unit)); 780 1.1 mrg 781 1.1 mrg /* On the line, but before start. */ 782 1.1 mrg ASSERT_FALSE (example_a.contains_point (2, 21, col_unit)); 783 1.1 mrg 784 1.1 mrg /* On the line, at the start. */ 785 1.1 mrg ASSERT_TRUE (example_a.contains_point (2, 22, col_unit)); 786 1.1 mrg 787 1.1 mrg /* On the line, within the range. */ 788 1.1 mrg ASSERT_TRUE (example_a.contains_point (2, 23, col_unit)); 789 1.1 mrg 790 1.1 mrg /* On the line, at the end. */ 791 1.1 mrg ASSERT_TRUE (example_a.contains_point (2, 38, col_unit)); 792 1.1 mrg 793 1.1 mrg /* On the line, after the end. */ 794 1.1 mrg ASSERT_FALSE (example_a.contains_point (2, 39, col_unit)); 795 1.1 mrg 796 1.1 mrg /* After the line. */ 797 1.1 mrg ASSERT_FALSE (example_a.contains_point (2, 39, col_unit)); 798 1.1 mrg } 799 1.1 mrg 800 1.1 mrg /* Tests for layout_range::intersects_line_p. */ 801 1.1 mrg ASSERT_FALSE (example_a.intersects_line_p (1)); 802 1.1 mrg ASSERT_TRUE (example_a.intersects_line_p (2)); 803 1.1 mrg ASSERT_FALSE (example_a.intersects_line_p (3)); 804 1.1 mrg } 805 1.1 mrg 806 1.1 mrg /* Selftest for layout_range, where the layout_range 807 1.1 mrg is the multi-line range shown as "Example B" above. */ 808 1.1 mrg 809 1.1 mrg static void 810 1.1 mrg test_layout_range_for_multiple_lines () 811 1.1 mrg { 812 1.1 mrg layout_range example_b = make_range (3, 14, 5, 8); 813 1.1 mrg 814 1.1 mrg /* Tests for layout_range::contains_point. */ 815 1.1 mrg 816 1.1 mrg for (int i = 0; i != CU_NUM_UNITS; ++i) 817 1.1 mrg { 818 1.1 mrg const enum column_unit col_unit = (enum column_unit) i; 819 1.1 mrg 820 1.1 mrg /* Before first line. */ 821 1.1 mrg ASSERT_FALSE (example_b.contains_point (1, 1, col_unit)); 822 1.1 mrg 823 1.1 mrg /* On the first line, but before start. */ 824 1.1 mrg ASSERT_FALSE (example_b.contains_point (3, 13, col_unit)); 825 1.1 mrg 826 1.1 mrg /* At the start. */ 827 1.1 mrg ASSERT_TRUE (example_b.contains_point (3, 14, col_unit)); 828 1.1 mrg 829 1.1 mrg /* On the first line, within the range. */ 830 1.1 mrg ASSERT_TRUE (example_b.contains_point (3, 15, col_unit)); 831 1.1 mrg 832 1.1 mrg /* On an interior line. 833 1.1 mrg The column number should not matter; try various boundary 834 1.1 mrg values. */ 835 1.1 mrg ASSERT_TRUE (example_b.contains_point (4, 1, col_unit)); 836 1.1 mrg ASSERT_TRUE (example_b.contains_point (4, 7, col_unit)); 837 1.1 mrg ASSERT_TRUE (example_b.contains_point (4, 8, col_unit)); 838 1.1 mrg ASSERT_TRUE (example_b.contains_point (4, 9, col_unit)); 839 1.1 mrg ASSERT_TRUE (example_b.contains_point (4, 13, col_unit)); 840 1.1 mrg ASSERT_TRUE (example_b.contains_point (4, 14, col_unit)); 841 1.1 mrg ASSERT_TRUE (example_b.contains_point (4, 15, col_unit)); 842 1.1 mrg 843 1.1 mrg /* On the final line, before the end. */ 844 1.1 mrg ASSERT_TRUE (example_b.contains_point (5, 7, col_unit)); 845 1.1 mrg 846 1.1 mrg /* On the final line, at the end. */ 847 1.1 mrg ASSERT_TRUE (example_b.contains_point (5, 8, col_unit)); 848 1.1 mrg 849 1.1 mrg /* On the final line, after the end. */ 850 1.1 mrg ASSERT_FALSE (example_b.contains_point (5, 9, col_unit)); 851 1.1 mrg 852 1.1 mrg /* After the line. */ 853 1.1 mrg ASSERT_FALSE (example_b.contains_point (6, 1, col_unit)); 854 1.1 mrg } 855 1.1 mrg 856 1.1 mrg /* Tests for layout_range::intersects_line_p. */ 857 1.1 mrg ASSERT_FALSE (example_b.intersects_line_p (2)); 858 1.1 mrg ASSERT_TRUE (example_b.intersects_line_p (3)); 859 1.1 mrg ASSERT_TRUE (example_b.intersects_line_p (4)); 860 1.1 mrg ASSERT_TRUE (example_b.intersects_line_p (5)); 861 1.1 mrg ASSERT_FALSE (example_b.intersects_line_p (6)); 862 1.1 mrg } 863 1.1 mrg 864 1.1 mrg #endif /* #if CHECKING_P */ 865 1.1 mrg 866 1.1 mrg /* Given a source line LINE of length LINE_BYTES bytes, determine the length 867 1.1 mrg (still in bytes, not display cols) without any trailing whitespace. */ 868 1.1 mrg 869 1.1 mrg static int 870 1.1 mrg get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes) 871 1.1 mrg { 872 1.1 mrg int result = line_bytes; 873 1.1 mrg while (result > 0) 874 1.1 mrg { 875 1.1 mrg char ch = line[result - 1]; 876 1.1 mrg if (ch == ' ' || ch == '\t' || ch == '\r') 877 1.1 mrg result--; 878 1.1 mrg else 879 1.1 mrg break; 880 1.1 mrg } 881 1.1 mrg gcc_assert (result >= 0); 882 1.1 mrg gcc_assert (result <= line_bytes); 883 1.1 mrg gcc_assert (result == 0 || 884 1.1 mrg (line[result - 1] != ' ' 885 1.1 mrg && line[result -1] != '\t' 886 1.1 mrg && line[result -1] != '\r')); 887 1.1 mrg return result; 888 1.1 mrg } 889 1.1 mrg 890 1.1 mrg #if CHECKING_P 891 1.1 mrg 892 1.1 mrg /* A helper function for testing get_line_bytes_without_trailing_whitespace. */ 893 1.1 mrg 894 1.1 mrg static void 895 1.1 mrg assert_eq (const char *line, int expected_bytes) 896 1.1 mrg { 897 1.1 mrg int actual_value 898 1.1 mrg = get_line_bytes_without_trailing_whitespace (line, strlen (line)); 899 1.1 mrg ASSERT_EQ (actual_value, expected_bytes); 900 1.1 mrg } 901 1.1 mrg 902 1.1 mrg /* Verify that get_line_bytes_without_trailing_whitespace is sane for 903 1.1 mrg various inputs. It is not required to handle newlines. */ 904 1.1 mrg 905 1.1 mrg static void 906 1.1 mrg test_get_line_bytes_without_trailing_whitespace () 907 1.1 mrg { 908 1.1 mrg assert_eq ("", 0); 909 1.1 mrg assert_eq (" ", 0); 910 1.1 mrg assert_eq ("\t", 0); 911 1.1 mrg assert_eq ("\r", 0); 912 1.1 mrg assert_eq ("hello world", 11); 913 1.1 mrg assert_eq ("hello world ", 11); 914 1.1 mrg assert_eq ("hello world \t\t ", 11); 915 1.1 mrg assert_eq ("hello world\r", 11); 916 1.1 mrg } 917 1.1 mrg 918 1.1 mrg #endif /* #if CHECKING_P */ 919 1.1 mrg 920 1.1 mrg /* Helper function for layout's ctor, for sanitizing locations relative 921 1.1 mrg to the primary location within a diagnostic. 922 1.1 mrg 923 1.1 mrg Compare LOC_A and LOC_B to see if it makes sense to print underlines 924 1.1 mrg connecting their expanded locations. Doing so is only guaranteed to 925 1.1 mrg make sense if the locations share the same macro expansion "history" 926 1.1 mrg i.e. they can be traced through the same macro expansions, eventually 927 1.1 mrg reaching an ordinary map. 928 1.1 mrg 929 1.1 mrg This may be too strong a condition, but it effectively sanitizes 930 1.1 mrg PR c++/70105, which has an example of printing an expression where the 931 1.1 mrg final location of the expression is in a different macro, which 932 1.1 mrg erroneously was leading to hundreds of lines of irrelevant source 933 1.1 mrg being printed. */ 934 1.1 mrg 935 1.1 mrg static bool 936 1.1 mrg compatible_locations_p (location_t loc_a, location_t loc_b) 937 1.1 mrg { 938 1.1 mrg if (IS_ADHOC_LOC (loc_a)) 939 1.1 mrg loc_a = get_location_from_adhoc_loc (line_table, loc_a); 940 1.1 mrg if (IS_ADHOC_LOC (loc_b)) 941 1.1 mrg loc_b = get_location_from_adhoc_loc (line_table, loc_b); 942 1.1 mrg 943 1.1 mrg /* If either location is one of the special locations outside of a 944 1.1 mrg linemap, they are only compatible if they are equal. */ 945 1.1 mrg if (loc_a < RESERVED_LOCATION_COUNT 946 1.1 mrg || loc_b < RESERVED_LOCATION_COUNT) 947 1.1 mrg return loc_a == loc_b; 948 1.1 mrg 949 1.1 mrg const line_map *map_a = linemap_lookup (line_table, loc_a); 950 1.1 mrg linemap_assert (map_a); 951 1.1 mrg 952 1.1 mrg const line_map *map_b = linemap_lookup (line_table, loc_b); 953 1.1 mrg linemap_assert (map_b); 954 1.1 mrg 955 1.1 mrg /* Are they within the same map? */ 956 1.1 mrg if (map_a == map_b) 957 1.1 mrg { 958 1.1 mrg /* Are both within the same macro expansion? */ 959 1.1 mrg if (linemap_macro_expansion_map_p (map_a)) 960 1.1 mrg { 961 1.1 mrg /* If so, then they're only compatible if either both are 962 1.1 mrg from the macro definition, or both from the macro arguments. */ 963 1.1 mrg bool loc_a_from_defn 964 1.1 mrg = linemap_location_from_macro_definition_p (line_table, loc_a); 965 1.1 mrg bool loc_b_from_defn 966 1.1 mrg = linemap_location_from_macro_definition_p (line_table, loc_b); 967 1.1 mrg if (loc_a_from_defn != loc_b_from_defn) 968 1.1 mrg return false; 969 1.1 mrg 970 1.1 mrg /* Expand each location towards the spelling location, and 971 1.1 mrg recurse. */ 972 1.1 mrg const line_map_macro *macro_map = linemap_check_macro (map_a); 973 1.1 mrg location_t loc_a_toward_spelling 974 1.1 mrg = linemap_macro_map_loc_unwind_toward_spelling (line_table, 975 1.1 mrg macro_map, 976 1.1 mrg loc_a); 977 1.1 mrg location_t loc_b_toward_spelling 978 1.1 mrg = linemap_macro_map_loc_unwind_toward_spelling (line_table, 979 1.1 mrg macro_map, 980 1.1 mrg loc_b); 981 1.1 mrg return compatible_locations_p (loc_a_toward_spelling, 982 1.1 mrg loc_b_toward_spelling); 983 1.1 mrg } 984 1.1 mrg 985 1.1 mrg /* Otherwise they are within the same ordinary map. */ 986 1.1 mrg return true; 987 1.1 mrg } 988 1.1 mrg else 989 1.1 mrg { 990 1.1 mrg /* Within different maps. */ 991 1.1 mrg 992 1.1 mrg /* If either is within a macro expansion, they are incompatible. */ 993 1.1 mrg if (linemap_macro_expansion_map_p (map_a) 994 1.1 mrg || linemap_macro_expansion_map_p (map_b)) 995 1.1 mrg return false; 996 1.1 mrg 997 1.1 mrg /* Within two different ordinary maps; they are compatible iff they 998 1.1 mrg are in the same file. */ 999 1.1 mrg const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a); 1000 1.1 mrg const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b); 1001 1.1 mrg return ord_map_a->to_file == ord_map_b->to_file; 1002 1.1 mrg } 1003 1.1 mrg } 1004 1.1 mrg 1005 1.1 mrg /* Comparator for sorting fix-it hints. */ 1006 1.1 mrg 1007 1.1 mrg static int 1008 1.1 mrg fixit_cmp (const void *p_a, const void *p_b) 1009 1.1 mrg { 1010 1.1 mrg const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a); 1011 1.1 mrg const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b); 1012 1.1 mrg return hint_a->get_start_loc () - hint_b->get_start_loc (); 1013 1.1 mrg } 1014 1.1 mrg 1015 1.1 mrg /* Callbacks for use when not escaping the source. */ 1016 1.1 mrg 1017 1.1 mrg /* The default callback for char_column_policy::m_width_cb is cpp_wcwidth. */ 1018 1.1 mrg 1019 1.1 mrg /* Callback for char_display_policy::m_print_cb for printing source chars 1020 1.1 mrg when not escaping the source. */ 1021 1.1 mrg 1022 1.1 mrg static void 1023 1.1 mrg default_print_decoded_ch (pretty_printer *pp, 1024 1.1 mrg const cpp_decoded_char &decoded_ch) 1025 1.1 mrg { 1026 1.1 mrg for (const char *ptr = decoded_ch.m_start_byte; 1027 1.1 mrg ptr != decoded_ch.m_next_byte; ptr++) 1028 1.1 mrg { 1029 1.1 mrg if (*ptr == '\0' || *ptr == '\r') 1030 1.1 mrg { 1031 1.1 mrg pp_space (pp); 1032 1.1 mrg continue; 1033 1.1 mrg } 1034 1.1 mrg 1035 1.1 mrg pp_character (pp, *ptr); 1036 1.1 mrg } 1037 1.1 mrg } 1038 1.1 mrg 1039 1.1 mrg /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */ 1040 1.1 mrg 1041 1.1 mrg static const int width_per_escaped_byte = 4; 1042 1.1 mrg 1043 1.1 mrg /* Callback for char_column_policy::m_width_cb for determining the 1044 1.1 mrg display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */ 1045 1.1 mrg 1046 1.1 mrg static int 1047 1.1 mrg escape_as_bytes_width (cppchar_t ch) 1048 1.1 mrg { 1049 1.1 mrg if (ch < 0x80 && ISPRINT (ch)) 1050 1.1 mrg return cpp_wcwidth (ch); 1051 1.1 mrg else 1052 1.1 mrg { 1053 1.1 mrg if (ch <= 0x7F) return 1 * width_per_escaped_byte; 1054 1.1 mrg if (ch <= 0x7FF) return 2 * width_per_escaped_byte; 1055 1.1 mrg if (ch <= 0xFFFF) return 3 * width_per_escaped_byte; 1056 1.1 mrg return 4 * width_per_escaped_byte; 1057 1.1 mrg } 1058 1.1 mrg } 1059 1.1 mrg 1060 1.1 mrg /* Callback for char_display_policy::m_print_cb for printing source chars 1061 1.1 mrg when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */ 1062 1.1 mrg 1063 1.1 mrg static void 1064 1.1 mrg escape_as_bytes_print (pretty_printer *pp, 1065 1.1 mrg const cpp_decoded_char &decoded_ch) 1066 1.1 mrg { 1067 1.1 mrg if (!decoded_ch.m_valid_ch) 1068 1.1 mrg { 1069 1.1 mrg for (const char *iter = decoded_ch.m_start_byte; 1070 1.1 mrg iter != decoded_ch.m_next_byte; ++iter) 1071 1.1 mrg { 1072 1.1 mrg char buf[16]; 1073 1.1 mrg sprintf (buf, "<%02x>", (unsigned char)*iter); 1074 1.1 mrg pp_string (pp, buf); 1075 1.1 mrg } 1076 1.1 mrg return; 1077 1.1 mrg } 1078 1.1 mrg 1079 1.1 mrg cppchar_t ch = decoded_ch.m_ch; 1080 1.1 mrg if (ch < 0x80 && ISPRINT (ch)) 1081 1.1 mrg pp_character (pp, ch); 1082 1.1 mrg else 1083 1.1 mrg { 1084 1.1 mrg for (const char *iter = decoded_ch.m_start_byte; 1085 1.1 mrg iter < decoded_ch.m_next_byte; ++iter) 1086 1.1 mrg { 1087 1.1 mrg char buf[16]; 1088 1.1 mrg sprintf (buf, "<%02x>", (unsigned char)*iter); 1089 1.1 mrg pp_string (pp, buf); 1090 1.1 mrg } 1091 1.1 mrg } 1092 1.1 mrg } 1093 1.1 mrg 1094 1.1 mrg /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */ 1095 1.1 mrg 1096 1.1 mrg /* Callback for char_column_policy::m_width_cb for determining the 1097 1.1 mrg display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */ 1098 1.1 mrg 1099 1.1 mrg static int 1100 1.1 mrg escape_as_unicode_width (cppchar_t ch) 1101 1.1 mrg { 1102 1.1 mrg if (ch < 0x80 && ISPRINT (ch)) 1103 1.1 mrg return cpp_wcwidth (ch); 1104 1.1 mrg else 1105 1.1 mrg { 1106 1.1 mrg // Width of "<U+%04x>" 1107 1.1 mrg if (ch > 0xfffff) 1108 1.1 mrg return 10; 1109 1.1 mrg else if (ch > 0xffff) 1110 1.1 mrg return 9; 1111 1.1 mrg else 1112 1.1 mrg return 8; 1113 1.1 mrg } 1114 1.1 mrg } 1115 1.1 mrg 1116 1.1 mrg /* Callback for char_display_policy::m_print_cb for printing source chars 1117 1.1 mrg when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */ 1118 1.1 mrg 1119 1.1 mrg static void 1120 1.1 mrg escape_as_unicode_print (pretty_printer *pp, 1121 1.1 mrg const cpp_decoded_char &decoded_ch) 1122 1.1 mrg { 1123 1.1 mrg if (!decoded_ch.m_valid_ch) 1124 1.1 mrg { 1125 1.1 mrg escape_as_bytes_print (pp, decoded_ch); 1126 1.1 mrg return; 1127 1.1 mrg } 1128 1.1 mrg 1129 1.1 mrg cppchar_t ch = decoded_ch.m_ch; 1130 1.1 mrg if (ch < 0x80 && ISPRINT (ch)) 1131 1.1 mrg pp_character (pp, ch); 1132 1.1 mrg else 1133 1.1 mrg { 1134 1.1 mrg char buf[16]; 1135 1.1 mrg sprintf (buf, "<U+%04X>", ch); 1136 1.1 mrg pp_string (pp, buf); 1137 1.1 mrg } 1138 1.1 mrg } 1139 1.1 mrg 1140 1.1 mrg /* Populate a char_display_policy based on DC and RICHLOC. */ 1141 1.1 mrg 1142 1.1 mrg static char_display_policy 1143 1.1 mrg make_policy (const diagnostic_context &dc, 1144 1.1 mrg const rich_location &richloc) 1145 1.1 mrg { 1146 1.1 mrg /* The default is to not escape non-ASCII bytes. */ 1147 1.1 mrg char_display_policy result 1148 1.1 mrg (dc.tabstop, cpp_wcwidth, default_print_decoded_ch); 1149 1.1 mrg 1150 1.1 mrg /* If the diagnostic suggests escaping non-ASCII bytes, then 1151 1.1 mrg use policy from user-supplied options. */ 1152 1.1 mrg if (richloc.escape_on_output_p ()) 1153 1.1 mrg { 1154 1.1 mrg result.m_undecoded_byte_width = width_per_escaped_byte; 1155 1.1 mrg switch (dc.escape_format) 1156 1.1 mrg { 1157 1.1 mrg default: 1158 1.1 mrg gcc_unreachable (); 1159 1.1 mrg case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE: 1160 1.1 mrg result.m_width_cb = escape_as_unicode_width; 1161 1.1 mrg result.m_print_cb = escape_as_unicode_print; 1162 1.1 mrg break; 1163 1.1 mrg case DIAGNOSTICS_ESCAPE_FORMAT_BYTES: 1164 1.1 mrg result.m_width_cb = escape_as_bytes_width; 1165 1.1 mrg result.m_print_cb = escape_as_bytes_print; 1166 1.1 mrg break; 1167 1.1 mrg } 1168 1.1 mrg } 1169 1.1 mrg 1170 1.1 mrg return result; 1171 1.1 mrg } 1172 1.1 mrg 1173 1.1 mrg /* Implementation of class layout. */ 1174 1.1 mrg 1175 1.1 mrg /* Constructor for class layout. 1176 1.1 mrg 1177 1.1 mrg Filter the ranges from the rich_location to those that we can 1178 1.1 mrg sanely print, populating m_layout_ranges and m_fixit_hints. 1179 1.1 mrg Determine the range of lines that we will print, splitting them 1180 1.1 mrg up into an ordered list of disjoint spans of contiguous line numbers. 1181 1.1 mrg Determine m_x_offset_display, to ensure that the primary caret 1182 1.1 mrg will fit within the max_width provided by the diagnostic_context. */ 1183 1.1 mrg 1184 1.1 mrg layout::layout (diagnostic_context * context, 1185 1.1 mrg rich_location *richloc, 1186 1.1 mrg diagnostic_t diagnostic_kind) 1187 1.1 mrg : m_context (context), 1188 1.1 mrg m_pp (context->printer), 1189 1.1 mrg m_policy (make_policy (*context, *richloc)), 1190 1.1 mrg m_primary_loc (richloc->get_range (0)->m_loc), 1191 1.1 mrg m_exploc (richloc->get_expanded_location (0), m_policy, 1192 1.1 mrg LOCATION_ASPECT_CARET), 1193 1.1 mrg m_colorizer (context, diagnostic_kind), 1194 1.1 mrg m_colorize_source_p (context->colorize_source_p), 1195 1.1 mrg m_show_labels_p (context->show_labels_p), 1196 1.1 mrg m_show_line_numbers_p (context->show_line_numbers_p), 1197 1.1 mrg m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH), 1198 1.1 mrg m_layout_ranges (richloc->get_num_locations ()), 1199 1.1 mrg m_fixit_hints (richloc->get_num_fixit_hints ()), 1200 1.1 mrg m_line_spans (1 + richloc->get_num_locations ()), 1201 1.1 mrg m_linenum_width (0), 1202 1.1 mrg m_x_offset_display (0), 1203 1.1 mrg m_escape_on_output (richloc->escape_on_output_p ()) 1204 1.1 mrg { 1205 1.1 mrg for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++) 1206 1.1 mrg { 1207 1.1 mrg /* This diagnostic printer can only cope with "sufficiently sane" ranges. 1208 1.1 mrg Ignore any ranges that are awkward to handle. */ 1209 1.1 mrg const location_range *loc_range = richloc->get_range (idx); 1210 1.1 mrg maybe_add_location_range (loc_range, idx, false); 1211 1.1 mrg } 1212 1.1 mrg 1213 1.1 mrg /* Populate m_fixit_hints, filtering to only those that are in the 1214 1.1 mrg same file. */ 1215 1.1 mrg for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++) 1216 1.1 mrg { 1217 1.1 mrg const fixit_hint *hint = richloc->get_fixit_hint (i); 1218 1.1 mrg if (validate_fixit_hint_p (hint)) 1219 1.1 mrg m_fixit_hints.safe_push (hint); 1220 1.1 mrg } 1221 1.1 mrg 1222 1.1 mrg /* Sort m_fixit_hints. */ 1223 1.1 mrg m_fixit_hints.qsort (fixit_cmp); 1224 1.1 mrg 1225 1.1 mrg /* Populate the indicated members. */ 1226 1.1 mrg calculate_line_spans (); 1227 1.1 mrg calculate_linenum_width (); 1228 1.1 mrg calculate_x_offset_display (); 1229 1.1 mrg 1230 1.1 mrg if (context->show_ruler_p) 1231 1.1 mrg show_ruler (m_x_offset_display + m_context->caret_max_width); 1232 1.1 mrg } 1233 1.1 mrg 1234 1.1 mrg 1235 1.1 mrg /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to 1236 1.1 mrg those that we can sanely print. 1237 1.1 mrg 1238 1.1 mrg ORIGINAL_IDX is the index of LOC_RANGE within its rich_location, 1239 1.1 mrg (for use as extrinsic state by label ranges FIXME). 1240 1.1 mrg 1241 1.1 mrg If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also 1242 1.1 mrg filtered against this layout instance's current line spans: it 1243 1.1 mrg will only be added if the location is fully within the lines 1244 1.1 mrg already specified by other locations. 1245 1.1 mrg 1246 1.1 mrg Return true iff LOC_RANGE was added. */ 1247 1.1 mrg 1248 1.1 mrg bool 1249 1.1 mrg layout::maybe_add_location_range (const location_range *loc_range, 1250 1.1 mrg unsigned original_idx, 1251 1.1 mrg bool restrict_to_current_line_spans) 1252 1.1 mrg { 1253 1.1 mrg gcc_assert (loc_range); 1254 1.1 mrg 1255 1.1 mrg /* Split the "range" into caret and range information. */ 1256 1.1 mrg source_range src_range = get_range_from_loc (line_table, loc_range->m_loc); 1257 1.1 mrg 1258 1.1 mrg /* Expand the various locations. */ 1259 1.1 mrg expanded_location start 1260 1.1 mrg = linemap_client_expand_location_to_spelling_point 1261 1.1 mrg (src_range.m_start, LOCATION_ASPECT_START); 1262 1.1 mrg expanded_location finish 1263 1.1 mrg = linemap_client_expand_location_to_spelling_point 1264 1.1 mrg (src_range.m_finish, LOCATION_ASPECT_FINISH); 1265 1.1 mrg expanded_location caret 1266 1.1 mrg = linemap_client_expand_location_to_spelling_point 1267 1.1 mrg (loc_range->m_loc, LOCATION_ASPECT_CARET); 1268 1.1 mrg 1269 1.1 mrg /* If any part of the range isn't in the same file as the primary 1270 1.1 mrg location of this diagnostic, ignore the range. */ 1271 1.1 mrg if (start.file != m_exploc.file) 1272 1.1 mrg return false; 1273 1.1 mrg if (finish.file != m_exploc.file) 1274 1.1 mrg return false; 1275 1.1 mrg if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET) 1276 1.1 mrg if (caret.file != m_exploc.file) 1277 1.1 mrg return false; 1278 1.1 mrg 1279 1.1 mrg /* Sanitize the caret location for non-primary ranges. */ 1280 1.1 mrg if (m_layout_ranges.length () > 0) 1281 1.1 mrg if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET) 1282 1.1 mrg if (!compatible_locations_p (loc_range->m_loc, m_primary_loc)) 1283 1.1 mrg /* Discard any non-primary ranges that can't be printed 1284 1.1 mrg sanely relative to the primary location. */ 1285 1.1 mrg return false; 1286 1.1 mrg 1287 1.1 mrg /* Everything is now known to be in the correct source file, 1288 1.1 mrg but it may require further sanitization. */ 1289 1.1 mrg layout_range ri (exploc_with_display_col (start, m_policy, 1290 1.1 mrg LOCATION_ASPECT_START), 1291 1.1 mrg exploc_with_display_col (finish, m_policy, 1292 1.1 mrg LOCATION_ASPECT_FINISH), 1293 1.1 mrg loc_range->m_range_display_kind, 1294 1.1 mrg exploc_with_display_col (caret, m_policy, 1295 1.1 mrg LOCATION_ASPECT_CARET), 1296 1.1 mrg original_idx, loc_range->m_label); 1297 1.1 mrg 1298 1.1 mrg /* If we have a range that finishes before it starts (perhaps 1299 1.1 mrg from something built via macro expansion), printing the 1300 1.1 mrg range is likely to be nonsensical. Also, attempting to do so 1301 1.1 mrg breaks assumptions within the printing code (PR c/68473). 1302 1.1 mrg Similarly, don't attempt to print ranges if one or both ends 1303 1.1 mrg of the range aren't sane to print relative to the 1304 1.1 mrg primary location (PR c++/70105). */ 1305 1.1 mrg if (start.line > finish.line 1306 1.1 mrg || !compatible_locations_p (src_range.m_start, m_primary_loc) 1307 1.1 mrg || !compatible_locations_p (src_range.m_finish, m_primary_loc)) 1308 1.1 mrg { 1309 1.1 mrg /* Is this the primary location? */ 1310 1.1 mrg if (m_layout_ranges.length () == 0) 1311 1.1 mrg { 1312 1.1 mrg /* We want to print the caret for the primary location, but 1313 1.1 mrg we must sanitize away m_start and m_finish. */ 1314 1.1 mrg ri.m_start = ri.m_caret; 1315 1.1 mrg ri.m_finish = ri.m_caret; 1316 1.1 mrg } 1317 1.1 mrg else 1318 1.1 mrg /* This is a non-primary range; ignore it. */ 1319 1.1 mrg return false; 1320 1.1 mrg } 1321 1.1 mrg 1322 1.1 mrg /* Potentially filter to just the lines already specified by other 1323 1.1 mrg locations. This is for use by gcc_rich_location::add_location_if_nearby. 1324 1.1 mrg The layout ctor doesn't use it, and can't because m_line_spans 1325 1.1 mrg hasn't been set up at that point. */ 1326 1.1 mrg if (restrict_to_current_line_spans) 1327 1.1 mrg { 1328 1.1 mrg if (!will_show_line_p (start.line)) 1329 1.1 mrg return false; 1330 1.1 mrg if (!will_show_line_p (finish.line)) 1331 1.1 mrg return false; 1332 1.1 mrg if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET) 1333 1.1 mrg if (!will_show_line_p (caret.line)) 1334 1.1 mrg return false; 1335 1.1 mrg } 1336 1.1 mrg 1337 1.1 mrg /* Passed all the tests; add the range to m_layout_ranges so that 1338 1.1 mrg it will be printed. */ 1339 1.1 mrg m_layout_ranges.safe_push (ri); 1340 1.1 mrg return true; 1341 1.1 mrg } 1342 1.1 mrg 1343 1.1 mrg /* Return true iff ROW is within one of the line spans for this layout. */ 1344 1.1 mrg 1345 1.1 mrg bool 1346 1.1 mrg layout::will_show_line_p (linenum_type row) const 1347 1.1 mrg { 1348 1.1 mrg for (int line_span_idx = 0; line_span_idx < get_num_line_spans (); 1349 1.1 mrg line_span_idx++) 1350 1.1 mrg { 1351 1.1 mrg const line_span *line_span = get_line_span (line_span_idx); 1352 1.1 mrg if (line_span->contains_line_p (row)) 1353 1.1 mrg return true; 1354 1.1 mrg } 1355 1.1 mrg return false; 1356 1.1 mrg } 1357 1.1 mrg 1358 1.1 mrg /* Print a line showing a gap in the line numbers, for showing the boundary 1359 1.1 mrg between two line spans. */ 1360 1.1 mrg 1361 1.1 mrg void 1362 1.1 mrg layout::print_gap_in_line_numbering () 1363 1.1 mrg { 1364 1.1 mrg gcc_assert (m_show_line_numbers_p); 1365 1.1 mrg 1366 1.1 mrg pp_emit_prefix (m_pp); 1367 1.1 mrg 1368 1.1 mrg for (int i = 0; i < m_linenum_width + 1; i++) 1369 1.1 mrg pp_character (m_pp, '.'); 1370 1.1 mrg 1371 1.1 mrg pp_newline (m_pp); 1372 1.1 mrg } 1373 1.1 mrg 1374 1.1 mrg /* Return true iff we should print a heading when starting the 1375 1.1 mrg line span with the given index. */ 1376 1.1 mrg 1377 1.1 mrg bool 1378 1.1 mrg layout::print_heading_for_line_span_index_p (int line_span_idx) const 1379 1.1 mrg { 1380 1.1 mrg /* We print a heading for every change of line span, hence for every 1381 1.1 mrg line span after the initial one. */ 1382 1.1 mrg if (line_span_idx > 0) 1383 1.1 mrg return true; 1384 1.1 mrg 1385 1.1 mrg /* We also do it for the initial span if the primary location of the 1386 1.1 mrg diagnostic is in a different span. */ 1387 1.1 mrg if (m_exploc.line > (int)get_line_span (0)->m_last_line) 1388 1.1 mrg return true; 1389 1.1 mrg 1390 1.1 mrg return false; 1391 1.1 mrg } 1392 1.1 mrg 1393 1.1 mrg /* Get an expanded_location for the first location of interest within 1394 1.1 mrg the given line_span. 1395 1.1 mrg Used when printing a heading to indicate a new line span. */ 1396 1.1 mrg 1397 1.1 mrg expanded_location 1398 1.1 mrg layout::get_expanded_location (const line_span *line_span) const 1399 1.1 mrg { 1400 1.1 mrg /* Whenever possible, use the caret location. */ 1401 1.1 mrg if (line_span->contains_line_p (m_exploc.line)) 1402 1.1 mrg return m_exploc; 1403 1.1 mrg 1404 1.1 mrg /* Otherwise, use the start of the first range that's present 1405 1.1 mrg within the line_span. */ 1406 1.1 mrg for (unsigned int i = 0; i < m_layout_ranges.length (); i++) 1407 1.1 mrg { 1408 1.1 mrg const layout_range *lr = &m_layout_ranges[i]; 1409 1.1 mrg if (line_span->contains_line_p (lr->m_start.m_line)) 1410 1.1 mrg { 1411 1.1 mrg expanded_location exploc = m_exploc; 1412 1.1 mrg exploc.line = lr->m_start.m_line; 1413 1.1 mrg exploc.column = lr->m_start.m_columns[CU_BYTES]; 1414 1.1 mrg return exploc; 1415 1.1 mrg } 1416 1.1 mrg } 1417 1.1 mrg 1418 1.1 mrg /* Otherwise, use the location of the first fixit-hint present within 1419 1.1 mrg the line_span. */ 1420 1.1 mrg for (unsigned int i = 0; i < m_fixit_hints.length (); i++) 1421 1.1 mrg { 1422 1.1 mrg const fixit_hint *hint = m_fixit_hints[i]; 1423 1.1 mrg location_t loc = hint->get_start_loc (); 1424 1.1 mrg expanded_location exploc = expand_location (loc); 1425 1.1 mrg if (line_span->contains_line_p (exploc.line)) 1426 1.1 mrg return exploc; 1427 1.1 mrg } 1428 1.1 mrg 1429 1.1 mrg /* It should not be possible to have a line span that didn't 1430 1.1 mrg contain any of the layout_range or fixit_hint instances. */ 1431 1.1 mrg gcc_unreachable (); 1432 1.1 mrg return m_exploc; 1433 1.1 mrg } 1434 1.1 mrg 1435 1.1 mrg /* Determine if HINT is meaningful to print within this layout. */ 1436 1.1 mrg 1437 1.1 mrg bool 1438 1.1 mrg layout::validate_fixit_hint_p (const fixit_hint *hint) 1439 1.1 mrg { 1440 1.1 mrg if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file) 1441 1.1 mrg return false; 1442 1.1 mrg if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file) 1443 1.1 mrg return false; 1444 1.1 mrg 1445 1.1 mrg return true; 1446 1.1 mrg } 1447 1.1 mrg 1448 1.1 mrg /* Determine the range of lines affected by HINT. 1449 1.1 mrg This assumes that HINT has already been filtered by 1450 1.1 mrg validate_fixit_hint_p, and so affects the correct source file. */ 1451 1.1 mrg 1452 1.1 mrg static line_span 1453 1.1 mrg get_line_span_for_fixit_hint (const fixit_hint *hint) 1454 1.1 mrg { 1455 1.1 mrg gcc_assert (hint); 1456 1.1 mrg 1457 1.1 mrg int start_line = LOCATION_LINE (hint->get_start_loc ()); 1458 1.1 mrg 1459 1.1 mrg /* For line-insertion fix-it hints, add the previous line to the 1460 1.1 mrg span, to give the user more context on the proposed change. */ 1461 1.1 mrg if (hint->ends_with_newline_p ()) 1462 1.1 mrg if (start_line > 1) 1463 1.1 mrg start_line--; 1464 1.1 mrg 1465 1.1 mrg return line_span (start_line, 1466 1.1 mrg LOCATION_LINE (hint->get_next_loc ())); 1467 1.1 mrg } 1468 1.1 mrg 1469 1.1 mrg /* We want to print the pertinent source code at a diagnostic. The 1470 1.1 mrg rich_location can contain multiple locations. This will have been 1471 1.1 mrg filtered into m_exploc (the caret for the primary location) and 1472 1.1 mrg m_layout_ranges, for those ranges within the same source file. 1473 1.1 mrg 1474 1.1 mrg We will print a subset of the lines within the source file in question, 1475 1.1 mrg as a collection of "spans" of lines. 1476 1.1 mrg 1477 1.1 mrg This function populates m_line_spans with an ordered, disjoint list of 1478 1.1 mrg the line spans of interest. 1479 1.1 mrg 1480 1.1 mrg Printing a gap between line spans takes one line, so, when printing 1481 1.1 mrg line numbers, we allow a gap of up to one line between spans when 1482 1.1 mrg merging, since it makes more sense to print the source line rather than a 1483 1.1 mrg "gap-in-line-numbering" line. When not printing line numbers, it's 1484 1.1 mrg better to be more explicit about what's going on, so keeping them as 1485 1.1 mrg separate spans is preferred. 1486 1.1 mrg 1487 1.1 mrg For example, if the primary range is on lines 8-10, with secondary ranges 1488 1.1 mrg covering lines 5-6 and lines 13-15: 1489 1.1 mrg 1490 1.1 mrg 004 1491 1.1 mrg 005 |RANGE 1 1492 1.1 mrg 006 |RANGE 1 1493 1.1 mrg 007 1494 1.1 mrg 008 |PRIMARY RANGE 1495 1.1 mrg 009 |PRIMARY CARET 1496 1.1 mrg 010 |PRIMARY RANGE 1497 1.1 mrg 011 1498 1.1 mrg 012 1499 1.1 mrg 013 |RANGE 2 1500 1.1 mrg 014 |RANGE 2 1501 1.1 mrg 015 |RANGE 2 1502 1.1 mrg 016 1503 1.1 mrg 1504 1.1 mrg With line numbering on, we want two spans: lines 5-10 and lines 13-15. 1505 1.1 mrg 1506 1.1 mrg With line numbering off (with span headers), we want three spans: lines 5-6, 1507 1.1 mrg lines 8-10, and lines 13-15. */ 1508 1.1 mrg 1509 1.1 mrg void 1510 1.1 mrg layout::calculate_line_spans () 1511 1.1 mrg { 1512 1.1 mrg /* This should only be called once, by the ctor. */ 1513 1.1 mrg gcc_assert (m_line_spans.length () == 0); 1514 1.1 mrg 1515 1.1 mrg /* Populate tmp_spans with individual spans, for each of 1516 1.1 mrg m_exploc, and for m_layout_ranges. */ 1517 1.1 mrg auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ()); 1518 1.1 mrg tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line)); 1519 1.1 mrg for (unsigned int i = 0; i < m_layout_ranges.length (); i++) 1520 1.1 mrg { 1521 1.1 mrg const layout_range *lr = &m_layout_ranges[i]; 1522 1.1 mrg gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line); 1523 1.1 mrg tmp_spans.safe_push (line_span (lr->m_start.m_line, 1524 1.1 mrg lr->m_finish.m_line)); 1525 1.1 mrg } 1526 1.1 mrg 1527 1.1 mrg /* Also add spans for any fix-it hints, in case they cover other lines. */ 1528 1.1 mrg for (unsigned int i = 0; i < m_fixit_hints.length (); i++) 1529 1.1 mrg { 1530 1.1 mrg const fixit_hint *hint = m_fixit_hints[i]; 1531 1.1 mrg gcc_assert (hint); 1532 1.1 mrg tmp_spans.safe_push (get_line_span_for_fixit_hint (hint)); 1533 1.1 mrg } 1534 1.1 mrg 1535 1.1 mrg /* Sort them. */ 1536 1.1 mrg tmp_spans.qsort(line_span::comparator); 1537 1.1 mrg 1538 1.1 mrg /* Now iterate through tmp_spans, copying into m_line_spans, and 1539 1.1 mrg combining where possible. */ 1540 1.1 mrg gcc_assert (tmp_spans.length () > 0); 1541 1.1 mrg m_line_spans.safe_push (tmp_spans[0]); 1542 1.1 mrg for (unsigned int i = 1; i < tmp_spans.length (); i++) 1543 1.1 mrg { 1544 1.1 mrg line_span *current = &m_line_spans[m_line_spans.length () - 1]; 1545 1.1 mrg const line_span *next = &tmp_spans[i]; 1546 1.1 mrg gcc_assert (next->m_first_line >= current->m_first_line); 1547 1.1 mrg const int merger_distance = m_show_line_numbers_p ? 1 : 0; 1548 1.1 mrg if ((linenum_arith_t)next->m_first_line 1549 1.1 mrg <= (linenum_arith_t)current->m_last_line + 1 + merger_distance) 1550 1.1 mrg { 1551 1.1 mrg /* We can merge them. */ 1552 1.1 mrg if (next->m_last_line > current->m_last_line) 1553 1.1 mrg current->m_last_line = next->m_last_line; 1554 1.1 mrg } 1555 1.1 mrg else 1556 1.1 mrg { 1557 1.1 mrg /* No merger possible. */ 1558 1.1 mrg m_line_spans.safe_push (*next); 1559 1.1 mrg } 1560 1.1 mrg } 1561 1.1 mrg 1562 1.1 mrg /* Verify the result, in m_line_spans. */ 1563 1.1 mrg gcc_assert (m_line_spans.length () > 0); 1564 1.1 mrg for (unsigned int i = 1; i < m_line_spans.length (); i++) 1565 1.1 mrg { 1566 1.1 mrg const line_span *prev = &m_line_spans[i - 1]; 1567 1.1 mrg const line_span *next = &m_line_spans[i]; 1568 1.1 mrg /* The individual spans must be sane. */ 1569 1.1 mrg gcc_assert (prev->m_first_line <= prev->m_last_line); 1570 1.1 mrg gcc_assert (next->m_first_line <= next->m_last_line); 1571 1.1 mrg /* The spans must be ordered. */ 1572 1.1 mrg gcc_assert (prev->m_first_line < next->m_first_line); 1573 1.1 mrg /* There must be a gap of at least one line between separate spans. */ 1574 1.1 mrg gcc_assert ((prev->m_last_line + 1) < next->m_first_line); 1575 1.1 mrg } 1576 1.1 mrg } 1577 1.1 mrg 1578 1.1 mrg /* Determine how many display columns need to be reserved for line numbers, 1579 1.1 mrg based on the largest line number that will be needed, and populate 1580 1.1 mrg m_linenum_width. */ 1581 1.1 mrg 1582 1.1 mrg void 1583 1.1 mrg layout::calculate_linenum_width () 1584 1.1 mrg { 1585 1.1 mrg gcc_assert (m_line_spans.length () > 0); 1586 1.1 mrg const line_span *last_span = &m_line_spans[m_line_spans.length () - 1]; 1587 1.1 mrg int highest_line = last_span->m_last_line; 1588 1.1 mrg if (highest_line < 0) 1589 1.1 mrg highest_line = 0; 1590 1.1 mrg m_linenum_width = num_digits (highest_line); 1591 1.1 mrg /* If we're showing jumps in the line-numbering, allow at least 3 chars. */ 1592 1.1 mrg if (m_line_spans.length () > 1) 1593 1.1 mrg m_linenum_width = MAX (m_linenum_width, 3); 1594 1.1 mrg /* If there's a minimum margin width, apply it (subtracting 1 for the space 1595 1.1 mrg after the line number. */ 1596 1.1 mrg m_linenum_width = MAX (m_linenum_width, m_context->min_margin_width - 1); 1597 1.1 mrg } 1598 1.1 mrg 1599 1.1 mrg /* Calculate m_x_offset_display, which improves readability in case the source 1600 1.1 mrg line of interest is longer than the user's display. All lines output will be 1601 1.1 mrg shifted to the left (so that their beginning is no longer displayed) by 1602 1.1 mrg m_x_offset_display display columns, so that the caret is in a reasonable 1603 1.1 mrg location. */ 1604 1.1 mrg 1605 1.1 mrg void 1606 1.1 mrg layout::calculate_x_offset_display () 1607 1.1 mrg { 1608 1.1 mrg m_x_offset_display = 0; 1609 1.1 mrg 1610 1.1 mrg const int max_width = m_context->caret_max_width; 1611 1.1 mrg if (!max_width) 1612 1.1 mrg { 1613 1.1 mrg /* Nothing to do, the width is not capped. */ 1614 1.1 mrg return; 1615 1.1 mrg } 1616 1.1 mrg 1617 1.1 mrg const char_span line = location_get_source_line (m_exploc.file, 1618 1.1 mrg m_exploc.line); 1619 1.1 mrg if (!line) 1620 1.1 mrg { 1621 1.1 mrg /* Nothing to do, we couldn't find the source line. */ 1622 1.1 mrg return; 1623 1.1 mrg } 1624 1.1 mrg int caret_display_column = m_exploc.m_display_col; 1625 1.1 mrg const int line_bytes 1626 1.1 mrg = get_line_bytes_without_trailing_whitespace (line.get_buffer (), 1627 1.1 mrg line.length ()); 1628 1.1 mrg int eol_display_column 1629 1.1 mrg = cpp_display_width (line.get_buffer (), line_bytes, m_policy); 1630 1.1 mrg if (caret_display_column > eol_display_column 1631 1.1 mrg || !caret_display_column) 1632 1.1 mrg { 1633 1.1 mrg /* This does not make sense, so don't try to do anything in this case. */ 1634 1.1 mrg return; 1635 1.1 mrg } 1636 1.1 mrg 1637 1.1 mrg /* Adjust caret and eol positions to include the left margin. If we are 1638 1.1 mrg outputting line numbers, then the left margin is equal to m_linenum_width 1639 1.1 mrg plus three for the " | " which follows it. Otherwise the left margin width 1640 1.1 mrg is equal to 1, because layout::print_source_line() will prefix each line 1641 1.1 mrg with a space. */ 1642 1.1 mrg const int source_display_cols = eol_display_column; 1643 1.1 mrg int left_margin_size = 1; 1644 1.1 mrg if (m_show_line_numbers_p) 1645 1.1 mrg left_margin_size = m_linenum_width + 3; 1646 1.1 mrg caret_display_column += left_margin_size; 1647 1.1 mrg eol_display_column += left_margin_size; 1648 1.1 mrg 1649 1.1 mrg if (eol_display_column <= max_width) 1650 1.1 mrg { 1651 1.1 mrg /* Nothing to do, everything fits in the display. */ 1652 1.1 mrg return; 1653 1.1 mrg } 1654 1.1 mrg 1655 1.1 mrg /* The line is too long for the display. Calculate an offset such that the 1656 1.1 mrg caret is not too close to the right edge of the screen. It will be 1657 1.1 mrg CARET_LINE_MARGIN display columns from the right edge, unless it is closer 1658 1.1 mrg than that to the end of the source line anyway. */ 1659 1.1 mrg int right_margin_size = CARET_LINE_MARGIN; 1660 1.1 mrg right_margin_size = MIN (eol_display_column - caret_display_column, 1661 1.1 mrg right_margin_size); 1662 1.1 mrg if (right_margin_size + left_margin_size >= max_width) 1663 1.1 mrg { 1664 1.1 mrg /* The max_width is very small, so anything we try to do will not be very 1665 1.1 mrg effective; just punt in this case and output with no offset. */ 1666 1.1 mrg return; 1667 1.1 mrg } 1668 1.1 mrg const int max_caret_display_column = max_width - right_margin_size; 1669 1.1 mrg if (caret_display_column > max_caret_display_column) 1670 1.1 mrg { 1671 1.1 mrg m_x_offset_display = caret_display_column - max_caret_display_column; 1672 1.1 mrg /* Make sure we don't offset the line into oblivion. */ 1673 1.1 mrg static const int min_cols_visible = 2; 1674 1.1 mrg if (source_display_cols - m_x_offset_display < min_cols_visible) 1675 1.1 mrg m_x_offset_display = 0; 1676 1.1 mrg } 1677 1.1 mrg } 1678 1.1 mrg 1679 1.1 mrg /* Print line ROW of source code, potentially colorized at any ranges, and 1680 1.1 mrg return the line bounds. LINE is the source line (not necessarily 1681 1.1 mrg 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both 1682 1.1 mrg colorization and tab expansion, this function tracks the line position in 1683 1.1 mrg both byte and display column units. */ 1684 1.1 mrg 1685 1.1 mrg line_bounds 1686 1.1 mrg layout::print_source_line (linenum_type row, const char *line, int line_bytes) 1687 1.1 mrg { 1688 1.1 mrg m_colorizer.set_normal_text (); 1689 1.1 mrg 1690 1.1 mrg pp_emit_prefix (m_pp); 1691 1.1 mrg if (m_show_line_numbers_p) 1692 1.1 mrg { 1693 1.1 mrg int width = num_digits (row); 1694 1.1 mrg for (int i = 0; i < m_linenum_width - width; i++) 1695 1.1 mrg pp_space (m_pp); 1696 1.1 mrg pp_printf (m_pp, "%i | ", row); 1697 1.1 mrg } 1698 1.1 mrg else 1699 1.1 mrg pp_space (m_pp); 1700 1.1 mrg 1701 1.1 mrg /* We will stop printing the source line at any trailing whitespace. */ 1702 1.1 mrg line_bytes = get_line_bytes_without_trailing_whitespace (line, 1703 1.1 mrg line_bytes); 1704 1.1 mrg 1705 1.1 mrg /* This object helps to keep track of which display column we are at, which is 1706 1.1 mrg necessary for computing the line bounds in display units, for doing 1707 1.1 mrg tab expansion, and for implementing m_x_offset_display. */ 1708 1.1 mrg cpp_display_width_computation dw (line, line_bytes, m_policy); 1709 1.1 mrg 1710 1.1 mrg /* Skip the first m_x_offset_display display columns. In case the leading 1711 1.1 mrg portion that will be skipped ends with a character with wcwidth > 1, then 1712 1.1 mrg it is possible we skipped too much, so account for that by padding with 1713 1.1 mrg spaces. Note that this does the right thing too in case a tab was the last 1714 1.1 mrg character to be skipped over; the tab is effectively replaced by the 1715 1.1 mrg correct number of trailing spaces needed to offset by the desired number of 1716 1.1 mrg display columns. */ 1717 1.1 mrg for (int skipped_display_cols = dw.advance_display_cols (m_x_offset_display); 1718 1.1 mrg skipped_display_cols > m_x_offset_display; --skipped_display_cols) 1719 1.1 mrg pp_space (m_pp); 1720 1.1 mrg 1721 1.1 mrg /* Print the line and compute the line_bounds. */ 1722 1.1 mrg line_bounds lbounds; 1723 1.1 mrg while (!dw.done ()) 1724 1.1 mrg { 1725 1.1 mrg /* Assuming colorization is enabled for the caret and underline 1726 1.1 mrg characters, we may also colorize the associated characters 1727 1.1 mrg within the source line. 1728 1.1 mrg 1729 1.1 mrg For frontends that generate range information, we color the 1730 1.1 mrg associated characters in the source line the same as the 1731 1.1 mrg carets and underlines in the annotation line, to make it easier 1732 1.1 mrg for the reader to see the pertinent code. 1733 1.1 mrg 1734 1.1 mrg For frontends that only generate carets, we don't colorize the 1735 1.1 mrg characters above them, since this would look strange (e.g. 1736 1.1 mrg colorizing just the first character in a token). */ 1737 1.1 mrg if (m_colorize_source_p) 1738 1.1 mrg { 1739 1.1 mrg bool in_range_p; 1740 1.1 mrg point_state state; 1741 1.1 mrg const int start_byte_col = dw.bytes_processed () + 1; 1742 1.1 mrg in_range_p = get_state_at_point (row, start_byte_col, 1743 1.1 mrg 0, INT_MAX, 1744 1.1 mrg CU_BYTES, 1745 1.1 mrg &state); 1746 1.1 mrg if (in_range_p) 1747 1.1 mrg m_colorizer.set_range (state.range_idx); 1748 1.1 mrg else 1749 1.1 mrg m_colorizer.set_normal_text (); 1750 1.1 mrg } 1751 1.1 mrg 1752 1.1 mrg /* Get the display width of the next character to be output, expanding 1753 1.1 mrg tabs and replacing some control bytes with spaces as necessary. */ 1754 1.1 mrg const char *c = dw.next_byte (); 1755 1.1 mrg const int start_disp_col = dw.display_cols_processed () + 1; 1756 1.1 mrg cpp_decoded_char cp; 1757 1.1 mrg const int this_display_width = dw.process_next_codepoint (&cp); 1758 1.1 mrg if (*c == '\t') 1759 1.1 mrg { 1760 1.1 mrg /* The returned display width is the number of spaces into which the 1761 1.1 mrg tab should be expanded. */ 1762 1.1 mrg for (int i = 0; i != this_display_width; ++i) 1763 1.1 mrg pp_space (m_pp); 1764 1.1 mrg continue; 1765 1.1 mrg } 1766 1.1 mrg 1767 1.1 mrg /* We have a (possibly multibyte) character to output; update the line 1768 1.1 mrg bounds if it is not whitespace. */ 1769 1.1 mrg if (*c != ' ') 1770 1.1 mrg { 1771 1.1 mrg lbounds.m_last_non_ws_disp_col = dw.display_cols_processed (); 1772 1.1 mrg if (lbounds.m_first_non_ws_disp_col == INT_MAX) 1773 1.1 mrg lbounds.m_first_non_ws_disp_col = start_disp_col; 1774 1.1 mrg } 1775 1.1 mrg 1776 1.1 mrg /* Output the character. */ 1777 1.1 mrg m_policy.m_print_cb (m_pp, cp); 1778 1.1 mrg c = dw.next_byte (); 1779 1.1 mrg } 1780 1.1 mrg print_newline (); 1781 1.1 mrg return lbounds; 1782 1.1 mrg } 1783 1.1 mrg 1784 1.1 mrg /* Determine if we should print an annotation line for ROW. 1785 1.1 mrg i.e. if any of m_layout_ranges contains ROW. */ 1786 1.1 mrg 1787 1.1 mrg bool 1788 1.1 mrg layout::should_print_annotation_line_p (linenum_type row) const 1789 1.1 mrg { 1790 1.1 mrg layout_range *range; 1791 1.1 mrg int i; 1792 1.1 mrg FOR_EACH_VEC_ELT (m_layout_ranges, i, range) 1793 1.1 mrg { 1794 1.1 mrg if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE) 1795 1.1 mrg return false; 1796 1.1 mrg if (range->intersects_line_p (row)) 1797 1.1 mrg return true; 1798 1.1 mrg } 1799 1.1 mrg return false; 1800 1.1 mrg } 1801 1.1 mrg 1802 1.1 mrg /* Begin an annotation line. If m_show_line_numbers_p, print the left 1803 1.1 mrg margin, which is empty for annotation lines. Otherwise, do nothing. */ 1804 1.1 mrg 1805 1.1 mrg void 1806 1.1 mrg layout::start_annotation_line (char margin_char) const 1807 1.1 mrg { 1808 1.1 mrg pp_emit_prefix (m_pp); 1809 1.1 mrg if (m_show_line_numbers_p) 1810 1.1 mrg { 1811 1.1 mrg /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3 1812 1.1 mrg of it, right-aligned, padded with spaces. */ 1813 1.1 mrg int i; 1814 1.1 mrg for (i = 0; i < m_linenum_width - 3; i++) 1815 1.1 mrg pp_space (m_pp); 1816 1.1 mrg for (; i < m_linenum_width; i++) 1817 1.1 mrg pp_character (m_pp, margin_char); 1818 1.1 mrg pp_string (m_pp, " |"); 1819 1.1 mrg } 1820 1.1 mrg } 1821 1.1 mrg 1822 1.1 mrg /* Print a line consisting of the caret/underlines for the given 1823 1.1 mrg source line. */ 1824 1.1 mrg 1825 1.1 mrg void 1826 1.1 mrg layout::print_annotation_line (linenum_type row, const line_bounds lbounds) 1827 1.1 mrg { 1828 1.1 mrg int x_bound = get_x_bound_for_row (row, m_exploc.m_display_col, 1829 1.1 mrg lbounds.m_last_non_ws_disp_col); 1830 1.1 mrg 1831 1.1 mrg start_annotation_line (); 1832 1.1 mrg pp_space (m_pp); 1833 1.1 mrg 1834 1.1 mrg for (int column = 1 + m_x_offset_display; column < x_bound; column++) 1835 1.1 mrg { 1836 1.1 mrg bool in_range_p; 1837 1.1 mrg point_state state; 1838 1.1 mrg in_range_p = get_state_at_point (row, column, 1839 1.1 mrg lbounds.m_first_non_ws_disp_col, 1840 1.1 mrg lbounds.m_last_non_ws_disp_col, 1841 1.1 mrg CU_DISPLAY_COLS, 1842 1.1 mrg &state); 1843 1.1 mrg if (in_range_p) 1844 1.1 mrg { 1845 1.1 mrg /* Within a range. Draw either the caret or an underline. */ 1846 1.1 mrg m_colorizer.set_range (state.range_idx); 1847 1.1 mrg if (state.draw_caret_p) 1848 1.1 mrg { 1849 1.1 mrg /* Draw the caret. */ 1850 1.1 mrg char caret_char; 1851 1.1 mrg if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES) 1852 1.1 mrg caret_char = m_context->caret_chars[state.range_idx]; 1853 1.1 mrg else 1854 1.1 mrg caret_char = '^'; 1855 1.1 mrg pp_character (m_pp, caret_char); 1856 1.1 mrg } 1857 1.1 mrg else 1858 1.1 mrg pp_character (m_pp, '~'); 1859 1.1 mrg } 1860 1.1 mrg else 1861 1.1 mrg { 1862 1.1 mrg /* Not in a range. */ 1863 1.1 mrg m_colorizer.set_normal_text (); 1864 1.1 mrg pp_character (m_pp, ' '); 1865 1.1 mrg } 1866 1.1 mrg } 1867 1.1 mrg print_newline (); 1868 1.1 mrg } 1869 1.1 mrg 1870 1.1 mrg /* Implementation detail of layout::print_any_labels. 1871 1.1 mrg 1872 1.1 mrg A label within the given row of source. */ 1873 1.1 mrg 1874 1.1 mrg class line_label 1875 1.1 mrg { 1876 1.1 mrg public: 1877 1.1 mrg line_label (const cpp_char_column_policy &policy, 1878 1.1 mrg int state_idx, int column, 1879 1.1 mrg label_text text) 1880 1.1 mrg : m_state_idx (state_idx), m_column (column), 1881 1.1 mrg m_text (text), m_label_line (0), m_has_vbar (true) 1882 1.1 mrg { 1883 1.1 mrg const int bytes = strlen (text.m_buffer); 1884 1.1 mrg m_display_width = cpp_display_width (text.m_buffer, bytes, policy); 1885 1.1 mrg } 1886 1.1 mrg 1887 1.1 mrg /* Sorting is primarily by column, then by state index. */ 1888 1.1 mrg static int comparator (const void *p1, const void *p2) 1889 1.1 mrg { 1890 1.1 mrg const line_label *ll1 = (const line_label *)p1; 1891 1.1 mrg const line_label *ll2 = (const line_label *)p2; 1892 1.1 mrg int column_cmp = compare (ll1->m_column, ll2->m_column); 1893 1.1 mrg if (column_cmp) 1894 1.1 mrg return column_cmp; 1895 1.1 mrg /* Order by reverse state index, so that labels are printed 1896 1.1 mrg in order of insertion into the rich_location when the 1897 1.1 mrg sorted list is walked backwards. */ 1898 1.1 mrg return -compare (ll1->m_state_idx, ll2->m_state_idx); 1899 1.1 mrg } 1900 1.1 mrg 1901 1.1 mrg int m_state_idx; 1902 1.1 mrg int m_column; 1903 1.1 mrg label_text m_text; 1904 1.1 mrg size_t m_display_width; 1905 1.1 mrg int m_label_line; 1906 1.1 mrg bool m_has_vbar; 1907 1.1 mrg }; 1908 1.1 mrg 1909 1.1 mrg /* Print any labels in this row. */ 1910 1.1 mrg void 1911 1.1 mrg layout::print_any_labels (linenum_type row) 1912 1.1 mrg { 1913 1.1 mrg int i; 1914 1.1 mrg auto_vec<line_label> labels; 1915 1.1 mrg 1916 1.1 mrg /* Gather the labels that are to be printed into "labels". */ 1917 1.1 mrg { 1918 1.1 mrg layout_range *range; 1919 1.1 mrg FOR_EACH_VEC_ELT (m_layout_ranges, i, range) 1920 1.1 mrg { 1921 1.1 mrg /* Most ranges don't have labels, so reject this first. */ 1922 1.1 mrg if (range->m_label == NULL) 1923 1.1 mrg continue; 1924 1.1 mrg 1925 1.1 mrg /* The range's caret must be on this line. */ 1926 1.1 mrg if (range->m_caret.m_line != row) 1927 1.1 mrg continue; 1928 1.1 mrg 1929 1.1 mrg /* Reject labels that aren't fully visible due to clipping 1930 1.1 mrg by m_x_offset_display. */ 1931 1.1 mrg const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS]; 1932 1.1 mrg if (disp_col <= m_x_offset_display) 1933 1.1 mrg continue; 1934 1.1 mrg 1935 1.1 mrg label_text text; 1936 1.1 mrg text = range->m_label->get_text (range->m_original_idx); 1937 1.1 mrg 1938 1.1 mrg /* Allow for labels that return NULL from their get_text 1939 1.1 mrg implementation (so e.g. such labels can control their own 1940 1.1 mrg visibility). */ 1941 1.1 mrg if (text.m_buffer == NULL) 1942 1.1 mrg continue; 1943 1.1 mrg 1944 1.1 mrg labels.safe_push (line_label (m_policy, i, disp_col, text)); 1945 1.1 mrg } 1946 1.1 mrg } 1947 1.1 mrg 1948 1.1 mrg /* Bail out if there are no labels on this row. */ 1949 1.1 mrg if (labels.length () == 0) 1950 1.1 mrg return; 1951 1.1 mrg 1952 1.1 mrg /* Sort them. */ 1953 1.1 mrg labels.qsort(line_label::comparator); 1954 1.1 mrg 1955 1.1 mrg /* Figure out how many "label lines" we need, and which 1956 1.1 mrg one each label is printed in. 1957 1.1 mrg 1958 1.1 mrg For example, if the labels aren't too densely packed, 1959 1.1 mrg we can fit them on the same line, giving two "label lines": 1960 1.1 mrg 1961 1.1 mrg foo + bar 1962 1.1 mrg ~~~ ~~~ 1963 1.1 mrg | | : label line 0 1964 1.1 mrg l0 l1 : label line 1 1965 1.1 mrg 1966 1.1 mrg If they would touch each other or overlap, then we need 1967 1.1 mrg additional "label lines": 1968 1.1 mrg 1969 1.1 mrg foo + bar 1970 1.1 mrg ~~~ ~~~ 1971 1.1 mrg | | : label line 0 1972 1.1 mrg | label 1 : label line 1 1973 1.1 mrg label 0 : label line 2 1974 1.1 mrg 1975 1.1 mrg Place the final label on label line 1, and work backwards, adding 1976 1.1 mrg label lines as needed. 1977 1.1 mrg 1978 1.1 mrg If multiple labels are at the same place, put them on separate 1979 1.1 mrg label lines: 1980 1.1 mrg 1981 1.1 mrg foo + bar 1982 1.1 mrg ^ : label line 0 1983 1.1 mrg | : label line 1 1984 1.1 mrg label 0 : label line 2 1985 1.1 mrg label 1 : label line 3. */ 1986 1.1 mrg 1987 1.1 mrg int max_label_line = 1; 1988 1.1 mrg { 1989 1.1 mrg int next_column = INT_MAX; 1990 1.1 mrg line_label *label; 1991 1.1 mrg FOR_EACH_VEC_ELT_REVERSE (labels, i, label) 1992 1.1 mrg { 1993 1.1 mrg /* Would this label "touch" or overlap the next label? */ 1994 1.1 mrg if (label->m_column + label->m_display_width >= (size_t)next_column) 1995 1.1 mrg { 1996 1.1 mrg max_label_line++; 1997 1.1 mrg 1998 1.1 mrg /* If we've already seen labels with the same column, suppress the 1999 1.1 mrg vertical bar for subsequent ones in this backwards iteration; 2000 1.1 mrg hence only the one with the highest label_line has m_has_vbar set. */ 2001 1.1 mrg if (label->m_column == next_column) 2002 1.1 mrg label->m_has_vbar = false; 2003 1.1 mrg } 2004 1.1 mrg 2005 1.1 mrg label->m_label_line = max_label_line; 2006 1.1 mrg next_column = label->m_column; 2007 1.1 mrg } 2008 1.1 mrg } 2009 1.1 mrg 2010 1.1 mrg /* Print the "label lines". For each label within the line, print 2011 1.1 mrg either a vertical bar ('|') for the labels that are lower down, or the 2012 1.1 mrg labels themselves once we've reached their line. */ 2013 1.1 mrg { 2014 1.1 mrg for (int label_line = 0; label_line <= max_label_line; label_line++) 2015 1.1 mrg { 2016 1.1 mrg start_annotation_line (); 2017 1.1 mrg pp_space (m_pp); 2018 1.1 mrg int column = 1 + m_x_offset_display; 2019 1.1 mrg line_label *label; 2020 1.1 mrg FOR_EACH_VEC_ELT (labels, i, label) 2021 1.1 mrg { 2022 1.1 mrg if (label_line > label->m_label_line) 2023 1.1 mrg /* We've printed all the labels for this label line. */ 2024 1.1 mrg break; 2025 1.1 mrg 2026 1.1 mrg if (label_line == label->m_label_line) 2027 1.1 mrg { 2028 1.1 mrg gcc_assert (column <= label->m_column); 2029 1.1 mrg move_to_column (&column, label->m_column, true); 2030 1.1 mrg /* Colorize the text, unless it's for events in a 2031 1.1 mrg diagnostic_path. */ 2032 1.1 mrg if (!m_diagnostic_path_p) 2033 1.1 mrg m_colorizer.set_range (label->m_state_idx); 2034 1.1 mrg pp_string (m_pp, label->m_text.m_buffer); 2035 1.1 mrg m_colorizer.set_normal_text (); 2036 1.1 mrg column += label->m_display_width; 2037 1.1 mrg } 2038 1.1 mrg else if (label->m_has_vbar) 2039 1.1 mrg { 2040 1.1 mrg gcc_assert (column <= label->m_column); 2041 1.1 mrg move_to_column (&column, label->m_column, true); 2042 1.1 mrg m_colorizer.set_range (label->m_state_idx); 2043 1.1 mrg pp_character (m_pp, '|'); 2044 1.1 mrg m_colorizer.set_normal_text (); 2045 1.1 mrg column++; 2046 1.1 mrg } 2047 1.1 mrg } 2048 1.1 mrg print_newline (); 2049 1.1 mrg } 2050 1.1 mrg } 2051 1.1 mrg 2052 1.1 mrg /* Clean up. */ 2053 1.1 mrg { 2054 1.1 mrg line_label *label; 2055 1.1 mrg FOR_EACH_VEC_ELT (labels, i, label) 2056 1.1 mrg label->m_text.maybe_free (); 2057 1.1 mrg } 2058 1.1 mrg } 2059 1.1 mrg 2060 1.1 mrg /* If there are any fixit hints inserting new lines before source line ROW, 2061 1.1 mrg print them. 2062 1.1 mrg 2063 1.1 mrg They are printed on lines of their own, before the source line 2064 1.1 mrg itself, with a leading '+'. */ 2065 1.1 mrg 2066 1.1 mrg void 2067 1.1 mrg layout::print_leading_fixits (linenum_type row) 2068 1.1 mrg { 2069 1.1 mrg for (unsigned int i = 0; i < m_fixit_hints.length (); i++) 2070 1.1 mrg { 2071 1.1 mrg const fixit_hint *hint = m_fixit_hints[i]; 2072 1.1 mrg 2073 1.1 mrg if (!hint->ends_with_newline_p ()) 2074 1.1 mrg /* Not a newline fixit; print it in print_trailing_fixits. */ 2075 1.1 mrg continue; 2076 1.1 mrg 2077 1.1 mrg gcc_assert (hint->insertion_p ()); 2078 1.1 mrg 2079 1.1 mrg if (hint->affects_line_p (m_exploc.file, row)) 2080 1.1 mrg { 2081 1.1 mrg /* Printing the '+' with normal colorization 2082 1.1 mrg and the inserted line with "insert" colorization 2083 1.1 mrg helps them stand out from each other, and from 2084 1.1 mrg the surrounding text. */ 2085 1.1 mrg m_colorizer.set_normal_text (); 2086 1.1 mrg start_annotation_line ('+'); 2087 1.1 mrg pp_character (m_pp, '+'); 2088 1.1 mrg m_colorizer.set_fixit_insert (); 2089 1.1 mrg /* Print all but the trailing newline of the fix-it hint. 2090 1.1 mrg We have to print the newline separately to avoid 2091 1.1 mrg getting additional pp prefixes printed. */ 2092 1.1 mrg for (size_t i = 0; i < hint->get_length () - 1; i++) 2093 1.1 mrg pp_character (m_pp, hint->get_string ()[i]); 2094 1.1 mrg m_colorizer.set_normal_text (); 2095 1.1 mrg pp_newline (m_pp); 2096 1.1 mrg } 2097 1.1 mrg } 2098 1.1 mrg } 2099 1.1 mrg 2100 1.1 mrg /* Subroutine of layout::print_trailing_fixits. 2101 1.1 mrg 2102 1.1 mrg Determine if the annotation line printed for LINE contained 2103 1.1 mrg the exact range from START_COLUMN to FINISH_COLUMN (in display units). */ 2104 1.1 mrg 2105 1.1 mrg bool 2106 1.1 mrg layout::annotation_line_showed_range_p (linenum_type line, int start_column, 2107 1.1 mrg int finish_column) const 2108 1.1 mrg { 2109 1.1 mrg layout_range *range; 2110 1.1 mrg int i; 2111 1.1 mrg FOR_EACH_VEC_ELT (m_layout_ranges, i, range) 2112 1.1 mrg if (range->m_start.m_line == line 2113 1.1 mrg && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column 2114 1.1 mrg && range->m_finish.m_line == line 2115 1.1 mrg && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column) 2116 1.1 mrg return true; 2117 1.1 mrg return false; 2118 1.1 mrg } 2119 1.1 mrg 2120 1.1 mrg /* Classes for printing trailing fix-it hints i.e. those that 2121 1.1 mrg don't add new lines. 2122 1.1 mrg 2123 1.1 mrg For insertion, these can look like: 2124 1.1 mrg 2125 1.1 mrg new_text 2126 1.1 mrg 2127 1.1 mrg For replacement, these can look like: 2128 1.1 mrg 2129 1.1 mrg ------------- : underline showing affected range 2130 1.1 mrg new_text 2131 1.1 mrg 2132 1.1 mrg For deletion, these can look like: 2133 1.1 mrg 2134 1.1 mrg ------------- : underline showing affected range 2135 1.1 mrg 2136 1.1 mrg This can become confusing if they overlap, and so we need 2137 1.1 mrg to do some preprocessing to decide what to print. 2138 1.1 mrg We use the list of fixit_hint instances affecting the line 2139 1.1 mrg to build a list of "correction" instances, and print the 2140 1.1 mrg latter. 2141 1.1 mrg 2142 1.1 mrg For example, consider a set of fix-its for converting 2143 1.1 mrg a C-style cast to a C++ const_cast. 2144 1.1 mrg 2145 1.1 mrg Given: 2146 1.1 mrg 2147 1.1 mrg ..000000000111111111122222222223333333333. 2148 1.1 mrg ..123456789012345678901234567890123456789. 2149 1.1 mrg foo *f = (foo *)ptr->field; 2150 1.1 mrg ^~~~~ 2151 1.1 mrg 2152 1.1 mrg and the fix-it hints: 2153 1.1 mrg - replace col 10 (the open paren) with "const_cast<" 2154 1.1 mrg - replace col 16 (the close paren) with "> (" 2155 1.1 mrg - insert ")" before col 27 2156 1.1 mrg 2157 1.1 mrg then we would get odd-looking output: 2158 1.1 mrg 2159 1.1 mrg foo *f = (foo *)ptr->field; 2160 1.1 mrg ^~~~~ 2161 1.1 mrg - 2162 1.1 mrg const_cast< 2163 1.1 mrg - 2164 1.1 mrg > ( ) 2165 1.1 mrg 2166 1.1 mrg It would be better to detect when fixit hints are going to 2167 1.1 mrg overlap (those that require new lines), and to consolidate 2168 1.1 mrg the printing of such fixits, giving something like: 2169 1.1 mrg 2170 1.1 mrg foo *f = (foo *)ptr->field; 2171 1.1 mrg ^~~~~ 2172 1.1 mrg ----------------- 2173 1.1 mrg const_cast<foo *> (ptr->field) 2174 1.1 mrg 2175 1.1 mrg This works by detecting when the printing would overlap, and 2176 1.1 mrg effectively injecting no-op replace hints into the gaps between 2177 1.1 mrg such fix-its, so that the printing joins up. 2178 1.1 mrg 2179 1.1 mrg In the above example, the overlap of: 2180 1.1 mrg - replace col 10 (the open paren) with "const_cast<" 2181 1.1 mrg and: 2182 1.1 mrg - replace col 16 (the close paren) with "> (" 2183 1.1 mrg is fixed by injecting a no-op: 2184 1.1 mrg - replace cols 11-15 with themselves ("foo *") 2185 1.1 mrg and consolidating these, making: 2186 1.1 mrg - replace cols 10-16 with "const_cast<" + "foo *" + "> (" 2187 1.1 mrg i.e.: 2188 1.1 mrg - replace cols 10-16 with "const_cast<foo *> (" 2189 1.1 mrg 2190 1.1 mrg This overlaps with the final fix-it hint: 2191 1.1 mrg - insert ")" before col 27 2192 1.1 mrg and so we repeat the consolidation process, by injecting 2193 1.1 mrg a no-op: 2194 1.1 mrg - replace cols 17-26 with themselves ("ptr->field") 2195 1.1 mrg giving: 2196 1.1 mrg - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")" 2197 1.1 mrg i.e.: 2198 1.1 mrg - replace cols 10-26 with "const_cast<foo *> (ptr->field)" 2199 1.1 mrg 2200 1.1 mrg and is thus printed as desired. */ 2201 1.1 mrg 2202 1.1 mrg /* A range of (byte or display) columns within a line. */ 2203 1.1 mrg 2204 1.1 mrg class column_range 2205 1.1 mrg { 2206 1.1 mrg public: 2207 1.1 mrg column_range (int start_, int finish_) : start (start_), finish (finish_) 2208 1.1 mrg { 2209 1.1 mrg /* We must have either a range, or an insertion. */ 2210 1.1 mrg gcc_assert (start <= finish || finish == start - 1); 2211 1.1 mrg } 2212 1.1 mrg 2213 1.1 mrg bool operator== (const column_range &other) const 2214 1.1 mrg { 2215 1.1 mrg return start == other.start && finish == other.finish; 2216 1.1 mrg } 2217 1.1 mrg 2218 1.1 mrg int start; 2219 1.1 mrg int finish; 2220 1.1 mrg }; 2221 1.1 mrg 2222 1.1 mrg /* Get the range of bytes or display columns that HINT would affect. */ 2223 1.1 mrg static column_range 2224 1.1 mrg get_affected_range (const cpp_char_column_policy &policy, 2225 1.1 mrg const fixit_hint *hint, enum column_unit col_unit) 2226 1.1 mrg { 2227 1.1 mrg expanded_location exploc_start = expand_location (hint->get_start_loc ()); 2228 1.1 mrg expanded_location exploc_finish = expand_location (hint->get_next_loc ()); 2229 1.1 mrg --exploc_finish.column; 2230 1.1 mrg 2231 1.1 mrg int start_column; 2232 1.1 mrg int finish_column; 2233 1.1 mrg if (col_unit == CU_DISPLAY_COLS) 2234 1.1 mrg { 2235 1.1 mrg start_column = location_compute_display_column (exploc_start, policy); 2236 1.1 mrg if (hint->insertion_p ()) 2237 1.1 mrg finish_column = start_column - 1; 2238 1.1 mrg else 2239 1.1 mrg finish_column = location_compute_display_column (exploc_finish, policy); 2240 1.1 mrg } 2241 1.1 mrg else 2242 1.1 mrg { 2243 1.1 mrg start_column = exploc_start.column; 2244 1.1 mrg finish_column = exploc_finish.column; 2245 1.1 mrg } 2246 1.1 mrg return column_range (start_column, finish_column); 2247 1.1 mrg } 2248 1.1 mrg 2249 1.1 mrg /* Get the range of display columns that would be printed for HINT. */ 2250 1.1 mrg 2251 1.1 mrg static column_range 2252 1.1 mrg get_printed_columns (const cpp_char_column_policy &policy, 2253 1.1 mrg const fixit_hint *hint) 2254 1.1 mrg { 2255 1.1 mrg expanded_location exploc = expand_location (hint->get_start_loc ()); 2256 1.1 mrg int start_column = location_compute_display_column (exploc, policy); 2257 1.1 mrg int hint_width = cpp_display_width (hint->get_string (), hint->get_length (), 2258 1.1 mrg policy); 2259 1.1 mrg int final_hint_column = start_column + hint_width - 1; 2260 1.1 mrg if (hint->insertion_p ()) 2261 1.1 mrg { 2262 1.1 mrg return column_range (start_column, final_hint_column); 2263 1.1 mrg } 2264 1.1 mrg else 2265 1.1 mrg { 2266 1.1 mrg exploc = expand_location (hint->get_next_loc ()); 2267 1.1 mrg --exploc.column; 2268 1.1 mrg int finish_column = location_compute_display_column (exploc, policy); 2269 1.1 mrg return column_range (start_column, 2270 1.1 mrg MAX (finish_column, final_hint_column)); 2271 1.1 mrg } 2272 1.1 mrg } 2273 1.1 mrg 2274 1.1 mrg /* A correction on a particular line. 2275 1.1 mrg This describes a plan for how to print one or more fixit_hint 2276 1.1 mrg instances that affected the line, potentially consolidating hints 2277 1.1 mrg into corrections to make the result easier for the user to read. */ 2278 1.1 mrg 2279 1.1 mrg class correction 2280 1.1 mrg { 2281 1.1 mrg public: 2282 1.1 mrg correction (column_range affected_bytes, 2283 1.1 mrg column_range affected_columns, 2284 1.1 mrg column_range printed_columns, 2285 1.1 mrg const char *new_text, size_t new_text_len, 2286 1.1 mrg const cpp_char_column_policy &policy) 2287 1.1 mrg : m_affected_bytes (affected_bytes), 2288 1.1 mrg m_affected_columns (affected_columns), 2289 1.1 mrg m_printed_columns (printed_columns), 2290 1.1 mrg m_text (xstrdup (new_text)), 2291 1.1 mrg m_byte_length (new_text_len), 2292 1.1 mrg m_policy (policy), 2293 1.1 mrg m_alloc_sz (new_text_len + 1) 2294 1.1 mrg { 2295 1.1 mrg compute_display_cols (); 2296 1.1 mrg } 2297 1.1 mrg 2298 1.1 mrg ~correction () { free (m_text); } 2299 1.1 mrg 2300 1.1 mrg bool insertion_p () const 2301 1.1 mrg { 2302 1.1 mrg return m_affected_bytes.start == m_affected_bytes.finish + 1; 2303 1.1 mrg } 2304 1.1 mrg 2305 1.1 mrg void ensure_capacity (size_t len); 2306 1.1 mrg void ensure_terminated (); 2307 1.1 mrg 2308 1.1 mrg void compute_display_cols () 2309 1.1 mrg { 2310 1.1 mrg m_display_cols = cpp_display_width (m_text, m_byte_length, m_policy); 2311 1.1 mrg } 2312 1.1 mrg 2313 1.1 mrg void overwrite (int dst_offset, const char_span &src_span) 2314 1.1 mrg { 2315 1.1 mrg gcc_assert (dst_offset >= 0); 2316 1.1 mrg gcc_assert (dst_offset + src_span.length () < m_alloc_sz); 2317 1.1 mrg memcpy (m_text + dst_offset, src_span.get_buffer (), 2318 1.1 mrg src_span.length ()); 2319 1.1 mrg } 2320 1.1 mrg 2321 1.1 mrg /* If insert, then start: the column before which the text 2322 1.1 mrg is to be inserted, and finish is offset by the length of 2323 1.1 mrg the replacement. 2324 1.1 mrg If replace, then the range of columns affected. */ 2325 1.1 mrg column_range m_affected_bytes; 2326 1.1 mrg column_range m_affected_columns; 2327 1.1 mrg 2328 1.1 mrg /* If insert, then start: the column before which the text 2329 1.1 mrg is to be inserted, and finish is offset by the length of 2330 1.1 mrg the replacement. 2331 1.1 mrg If replace, then the range of columns affected. */ 2332 1.1 mrg column_range m_printed_columns; 2333 1.1 mrg 2334 1.1 mrg /* The text to be inserted/used as replacement. */ 2335 1.1 mrg char *m_text; 2336 1.1 mrg size_t m_byte_length; /* Not including null-terminator. */ 2337 1.1 mrg int m_display_cols; 2338 1.1 mrg const cpp_char_column_policy &m_policy; 2339 1.1 mrg size_t m_alloc_sz; 2340 1.1 mrg }; 2341 1.1 mrg 2342 1.1 mrg /* Ensure that m_text can hold a string of length LEN 2343 1.1 mrg (plus 1 for 0-termination). */ 2344 1.1 mrg 2345 1.1 mrg void 2346 1.1 mrg correction::ensure_capacity (size_t len) 2347 1.1 mrg { 2348 1.1 mrg /* Allow 1 extra byte for 0-termination. */ 2349 1.1 mrg if (m_alloc_sz < (len + 1)) 2350 1.1 mrg { 2351 1.1 mrg size_t new_alloc_sz = (len + 1) * 2; 2352 1.1 mrg m_text = (char *)xrealloc (m_text, new_alloc_sz); 2353 1.1 mrg m_alloc_sz = new_alloc_sz; 2354 1.1 mrg } 2355 1.1 mrg } 2356 1.1 mrg 2357 1.1 mrg /* Ensure that m_text is 0-terminated. */ 2358 1.1 mrg 2359 1.1 mrg void 2360 1.1 mrg correction::ensure_terminated () 2361 1.1 mrg { 2362 1.1 mrg /* 0-terminate the buffer. */ 2363 1.1 mrg gcc_assert (m_byte_length < m_alloc_sz); 2364 1.1 mrg m_text[m_byte_length] = '\0'; 2365 1.1 mrg } 2366 1.1 mrg 2367 1.1 mrg /* A list of corrections affecting a particular line. 2368 1.1 mrg This is used by layout::print_trailing_fixits for planning 2369 1.1 mrg how to print the fix-it hints affecting the line. */ 2370 1.1 mrg 2371 1.1 mrg class line_corrections 2372 1.1 mrg { 2373 1.1 mrg public: 2374 1.1 mrg line_corrections (const char_display_policy &policy, 2375 1.1 mrg const char *filename, 2376 1.1 mrg linenum_type row) 2377 1.1 mrg : m_policy (policy), m_filename (filename), m_row (row) 2378 1.1 mrg {} 2379 1.1 mrg ~line_corrections (); 2380 1.1 mrg 2381 1.1 mrg void add_hint (const fixit_hint *hint); 2382 1.1 mrg 2383 1.1 mrg const char_display_policy &m_policy; 2384 1.1 mrg const char *m_filename; 2385 1.1 mrg linenum_type m_row; 2386 1.1 mrg auto_vec <correction *> m_corrections; 2387 1.1 mrg }; 2388 1.1 mrg 2389 1.1 mrg /* struct line_corrections. */ 2390 1.1 mrg 2391 1.1 mrg line_corrections::~line_corrections () 2392 1.1 mrg { 2393 1.1 mrg unsigned i; 2394 1.1 mrg correction *c; 2395 1.1 mrg FOR_EACH_VEC_ELT (m_corrections, i, c) 2396 1.1 mrg delete c; 2397 1.1 mrg } 2398 1.1 mrg 2399 1.1 mrg /* A struct wrapping a particular source line, allowing 2400 1.1 mrg run-time bounds-checking of accesses in a checked build. */ 2401 1.1 mrg 2402 1.1 mrg class source_line 2403 1.1 mrg { 2404 1.1 mrg public: 2405 1.1 mrg source_line (const char *filename, int line); 2406 1.1 mrg 2407 1.1 mrg char_span as_span () { return char_span (chars, width); } 2408 1.1 mrg 2409 1.1 mrg const char *chars; 2410 1.1 mrg int width; 2411 1.1 mrg }; 2412 1.1 mrg 2413 1.1 mrg /* source_line's ctor. */ 2414 1.1 mrg 2415 1.1 mrg source_line::source_line (const char *filename, int line) 2416 1.1 mrg { 2417 1.1 mrg char_span span = location_get_source_line (filename, line); 2418 1.1 mrg chars = span.get_buffer (); 2419 1.1 mrg width = span.length (); 2420 1.1 mrg } 2421 1.1 mrg 2422 1.1 mrg /* Add HINT to the corrections for this line. 2423 1.1 mrg Attempt to consolidate nearby hints so that they will not 2424 1.1 mrg overlap with printed. */ 2425 1.1 mrg 2426 1.1 mrg void 2427 1.1 mrg line_corrections::add_hint (const fixit_hint *hint) 2428 1.1 mrg { 2429 1.1 mrg column_range affected_bytes = get_affected_range (m_policy, hint, CU_BYTES); 2430 1.1 mrg column_range affected_columns = get_affected_range (m_policy, hint, 2431 1.1 mrg CU_DISPLAY_COLS); 2432 1.1 mrg column_range printed_columns = get_printed_columns (m_policy, hint); 2433 1.1 mrg 2434 1.1 mrg /* Potentially consolidate. */ 2435 1.1 mrg if (!m_corrections.is_empty ()) 2436 1.1 mrg { 2437 1.1 mrg correction *last_correction 2438 1.1 mrg = m_corrections[m_corrections.length () - 1]; 2439 1.1 mrg 2440 1.1 mrg /* The following consolidation code assumes that the fix-it hints 2441 1.1 mrg have been sorted by start (done within layout's ctor). */ 2442 1.1 mrg gcc_assert (affected_bytes.start 2443 1.1 mrg >= last_correction->m_affected_bytes.start); 2444 1.1 mrg gcc_assert (printed_columns.start 2445 1.1 mrg >= last_correction->m_printed_columns.start); 2446 1.1 mrg 2447 1.1 mrg if (printed_columns.start <= last_correction->m_printed_columns.finish) 2448 1.1 mrg { 2449 1.1 mrg /* We have two hints for which the printed forms of the hints 2450 1.1 mrg would touch or overlap, so we need to consolidate them to avoid 2451 1.1 mrg confusing the user. 2452 1.1 mrg Attempt to inject a "replace" correction from immediately 2453 1.1 mrg after the end of the last hint to immediately before the start 2454 1.1 mrg of the next hint. */ 2455 1.1 mrg column_range between (last_correction->m_affected_bytes.finish + 1, 2456 1.1 mrg affected_bytes.start - 1); 2457 1.1 mrg 2458 1.1 mrg /* Try to read the source. */ 2459 1.1 mrg source_line line (m_filename, m_row); 2460 1.1 mrg if (line.chars && between.finish < line.width) 2461 1.1 mrg { 2462 1.1 mrg /* Consolidate into the last correction: 2463 1.1 mrg add a no-op "replace" of the "between" text, and 2464 1.1 mrg add the text from the new hint. */ 2465 1.1 mrg int old_byte_len = last_correction->m_byte_length; 2466 1.1 mrg gcc_assert (old_byte_len >= 0); 2467 1.1 mrg int between_byte_len = between.finish + 1 - between.start; 2468 1.1 mrg gcc_assert (between_byte_len >= 0); 2469 1.1 mrg int new_byte_len 2470 1.1 mrg = old_byte_len + between_byte_len + hint->get_length (); 2471 1.1 mrg gcc_assert (new_byte_len >= 0); 2472 1.1 mrg last_correction->ensure_capacity (new_byte_len); 2473 1.1 mrg last_correction->overwrite 2474 1.1 mrg (old_byte_len, 2475 1.1 mrg line.as_span ().subspan (between.start - 1, 2476 1.1 mrg between.finish + 1 - between.start)); 2477 1.1 mrg last_correction->overwrite (old_byte_len + between_byte_len, 2478 1.1 mrg char_span (hint->get_string (), 2479 1.1 mrg hint->get_length ())); 2480 1.1 mrg last_correction->m_byte_length = new_byte_len; 2481 1.1 mrg last_correction->ensure_terminated (); 2482 1.1 mrg last_correction->m_affected_bytes.finish 2483 1.1 mrg = affected_bytes.finish; 2484 1.1 mrg last_correction->m_affected_columns.finish 2485 1.1 mrg = affected_columns.finish; 2486 1.1 mrg int prev_display_cols = last_correction->m_display_cols; 2487 1.1 mrg last_correction->compute_display_cols (); 2488 1.1 mrg last_correction->m_printed_columns.finish 2489 1.1 mrg += last_correction->m_display_cols - prev_display_cols; 2490 1.1 mrg return; 2491 1.1 mrg } 2492 1.1 mrg } 2493 1.1 mrg } 2494 1.1 mrg 2495 1.1 mrg /* If no consolidation happened, add a new correction instance. */ 2496 1.1 mrg m_corrections.safe_push (new correction (affected_bytes, 2497 1.1 mrg affected_columns, 2498 1.1 mrg printed_columns, 2499 1.1 mrg hint->get_string (), 2500 1.1 mrg hint->get_length (), 2501 1.1 mrg m_policy)); 2502 1.1 mrg } 2503 1.1 mrg 2504 1.1 mrg /* If there are any fixit hints on source line ROW, print them. 2505 1.1 mrg They are printed in order, attempting to combine them onto lines, but 2506 1.1 mrg starting new lines if necessary. 2507 1.1 mrg Fix-it hints that insert new lines are handled separately, 2508 1.1 mrg in layout::print_leading_fixits. */ 2509 1.1 mrg 2510 1.1 mrg void 2511 1.1 mrg layout::print_trailing_fixits (linenum_type row) 2512 1.1 mrg { 2513 1.1 mrg /* Build a list of correction instances for the line, 2514 1.1 mrg potentially consolidating hints (for the sake of readability). */ 2515 1.1 mrg line_corrections corrections (m_policy, m_exploc.file, row); 2516 1.1 mrg for (unsigned int i = 0; i < m_fixit_hints.length (); i++) 2517 1.1 mrg { 2518 1.1 mrg const fixit_hint *hint = m_fixit_hints[i]; 2519 1.1 mrg 2520 1.1 mrg /* Newline fixits are handled by layout::print_leading_fixits. */ 2521 1.1 mrg if (hint->ends_with_newline_p ()) 2522 1.1 mrg continue; 2523 1.1 mrg 2524 1.1 mrg if (hint->affects_line_p (m_exploc.file, row)) 2525 1.1 mrg corrections.add_hint (hint); 2526 1.1 mrg } 2527 1.1 mrg 2528 1.1 mrg /* Now print the corrections. */ 2529 1.1 mrg unsigned i; 2530 1.1 mrg correction *c; 2531 1.1 mrg int column = m_x_offset_display; 2532 1.1 mrg 2533 1.1 mrg if (!corrections.m_corrections.is_empty ()) 2534 1.1 mrg start_annotation_line (); 2535 1.1 mrg 2536 1.1 mrg FOR_EACH_VEC_ELT (corrections.m_corrections, i, c) 2537 1.1 mrg { 2538 1.1 mrg /* For now we assume each fixit hint can only touch one line. */ 2539 1.1 mrg if (c->insertion_p ()) 2540 1.1 mrg { 2541 1.1 mrg /* This assumes the insertion just affects one line. */ 2542 1.1 mrg int start_column = c->m_printed_columns.start; 2543 1.1 mrg move_to_column (&column, start_column, true); 2544 1.1 mrg m_colorizer.set_fixit_insert (); 2545 1.1 mrg pp_string (m_pp, c->m_text); 2546 1.1 mrg m_colorizer.set_normal_text (); 2547 1.1 mrg column += c->m_display_cols; 2548 1.1 mrg } 2549 1.1 mrg else 2550 1.1 mrg { 2551 1.1 mrg /* If the range of the replacement wasn't printed in the 2552 1.1 mrg annotation line, then print an extra underline to 2553 1.1 mrg indicate exactly what is being replaced. 2554 1.1 mrg Always show it for removals. */ 2555 1.1 mrg int start_column = c->m_affected_columns.start; 2556 1.1 mrg int finish_column = c->m_affected_columns.finish; 2557 1.1 mrg if (!annotation_line_showed_range_p (row, start_column, 2558 1.1 mrg finish_column) 2559 1.1 mrg || c->m_byte_length == 0) 2560 1.1 mrg { 2561 1.1 mrg move_to_column (&column, start_column, true); 2562 1.1 mrg m_colorizer.set_fixit_delete (); 2563 1.1 mrg for (; column <= finish_column; column++) 2564 1.1 mrg pp_character (m_pp, '-'); 2565 1.1 mrg m_colorizer.set_normal_text (); 2566 1.1 mrg } 2567 1.1 mrg /* Print the replacement text. REPLACE also covers 2568 1.1 mrg removals, so only do this extra work (potentially starting 2569 1.1 mrg a new line) if we have actual replacement text. */ 2570 1.1 mrg if (c->m_byte_length > 0) 2571 1.1 mrg { 2572 1.1 mrg move_to_column (&column, start_column, true); 2573 1.1 mrg m_colorizer.set_fixit_insert (); 2574 1.1 mrg pp_string (m_pp, c->m_text); 2575 1.1 mrg m_colorizer.set_normal_text (); 2576 1.1 mrg column += c->m_display_cols; 2577 1.1 mrg } 2578 1.1 mrg } 2579 1.1 mrg } 2580 1.1 mrg 2581 1.1 mrg /* Add a trailing newline, if necessary. */ 2582 1.1 mrg move_to_column (&column, 0, false); 2583 1.1 mrg } 2584 1.1 mrg 2585 1.1 mrg /* Disable any colorization and emit a newline. */ 2586 1.1 mrg 2587 1.1 mrg void 2588 1.1 mrg layout::print_newline () 2589 1.1 mrg { 2590 1.1 mrg m_colorizer.set_normal_text (); 2591 1.1 mrg pp_newline (m_pp); 2592 1.1 mrg } 2593 1.1 mrg 2594 1.1 mrg /* Return true if (ROW/COLUMN) is within a range of the layout. 2595 1.1 mrg If it returns true, OUT_STATE is written to, with the 2596 1.1 mrg range index, and whether we should draw the caret at 2597 1.1 mrg (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls 2598 1.1 mrg whether all inputs and outputs are in bytes or display column units. */ 2599 1.1 mrg 2600 1.1 mrg bool 2601 1.1 mrg layout::get_state_at_point (/* Inputs. */ 2602 1.1 mrg linenum_type row, int column, 2603 1.1 mrg int first_non_ws, int last_non_ws, 2604 1.1 mrg enum column_unit col_unit, 2605 1.1 mrg /* Outputs. */ 2606 1.1 mrg point_state *out_state) 2607 1.1 mrg { 2608 1.1 mrg layout_range *range; 2609 1.1 mrg int i; 2610 1.1 mrg FOR_EACH_VEC_ELT (m_layout_ranges, i, range) 2611 1.1 mrg { 2612 1.1 mrg if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE) 2613 1.1 mrg /* Bail out early, so that such ranges don't affect underlining or 2614 1.1 mrg source colorization. */ 2615 1.1 mrg continue; 2616 1.1 mrg 2617 1.1 mrg if (range->contains_point (row, column, col_unit)) 2618 1.1 mrg { 2619 1.1 mrg out_state->range_idx = i; 2620 1.1 mrg 2621 1.1 mrg /* Are we at the range's caret? is it visible? */ 2622 1.1 mrg out_state->draw_caret_p = false; 2623 1.1 mrg if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET 2624 1.1 mrg && row == range->m_caret.m_line 2625 1.1 mrg && column == range->m_caret.m_columns[col_unit]) 2626 1.1 mrg out_state->draw_caret_p = true; 2627 1.1 mrg 2628 1.1 mrg /* Within a multiline range, don't display any underline 2629 1.1 mrg in any leading or trailing whitespace on a line. 2630 1.1 mrg We do display carets, however. */ 2631 1.1 mrg if (!out_state->draw_caret_p) 2632 1.1 mrg if (column < first_non_ws || column > last_non_ws) 2633 1.1 mrg return false; 2634 1.1 mrg 2635 1.1 mrg /* We are within a range. */ 2636 1.1 mrg return true; 2637 1.1 mrg } 2638 1.1 mrg } 2639 1.1 mrg 2640 1.1 mrg return false; 2641 1.1 mrg } 2642 1.1 mrg 2643 1.1 mrg /* Helper function for use by layout::print_line when printing the 2644 1.1 mrg annotation line under the source line. 2645 1.1 mrg Get the display column beyond the rightmost one that could contain a caret 2646 1.1 mrg or range marker, given that we stop rendering at trailing whitespace. 2647 1.1 mrg ROW is the source line within the given file. 2648 1.1 mrg CARET_COLUMN is the display column of range 0's caret. 2649 1.1 mrg LAST_NON_WS_COLUMN is the last display column containing a non-whitespace 2650 1.1 mrg character of source (as determined when printing the source line). */ 2651 1.1 mrg 2652 1.1 mrg int 2653 1.1 mrg layout::get_x_bound_for_row (linenum_type row, int caret_column, 2654 1.1 mrg int last_non_ws_column) 2655 1.1 mrg { 2656 1.1 mrg int result = caret_column + 1; 2657 1.1 mrg 2658 1.1 mrg layout_range *range; 2659 1.1 mrg int i; 2660 1.1 mrg FOR_EACH_VEC_ELT (m_layout_ranges, i, range) 2661 1.1 mrg { 2662 1.1 mrg if (row >= range->m_start.m_line) 2663 1.1 mrg { 2664 1.1 mrg if (range->m_finish.m_line == row) 2665 1.1 mrg { 2666 1.1 mrg /* On the final line within a range; ensure that 2667 1.1 mrg we render up to the end of the range. */ 2668 1.1 mrg const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS]; 2669 1.1 mrg if (result <= disp_col) 2670 1.1 mrg result = disp_col + 1; 2671 1.1 mrg } 2672 1.1 mrg else if (row < range->m_finish.m_line) 2673 1.1 mrg { 2674 1.1 mrg /* Within a multiline range; ensure that we render up to the 2675 1.1 mrg last non-whitespace column. */ 2676 1.1 mrg if (result <= last_non_ws_column) 2677 1.1 mrg result = last_non_ws_column + 1; 2678 1.1 mrg } 2679 1.1 mrg } 2680 1.1 mrg } 2681 1.1 mrg 2682 1.1 mrg return result; 2683 1.1 mrg } 2684 1.1 mrg 2685 1.1 mrg /* Given *COLUMN as an x-coordinate, print spaces to position 2686 1.1 mrg successive output at DEST_COLUMN, printing a newline if necessary, 2687 1.1 mrg and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty) 2688 1.1 mrg left margin after any newline. */ 2689 1.1 mrg 2690 1.1 mrg void 2691 1.1 mrg layout::move_to_column (int *column, int dest_column, bool add_left_margin) 2692 1.1 mrg { 2693 1.1 mrg /* Start a new line if we need to. */ 2694 1.1 mrg if (*column > dest_column) 2695 1.1 mrg { 2696 1.1 mrg print_newline (); 2697 1.1 mrg if (add_left_margin) 2698 1.1 mrg start_annotation_line (); 2699 1.1 mrg *column = m_x_offset_display; 2700 1.1 mrg } 2701 1.1 mrg 2702 1.1 mrg while (*column < dest_column) 2703 1.1 mrg { 2704 1.1 mrg pp_space (m_pp); 2705 1.1 mrg (*column)++; 2706 1.1 mrg } 2707 1.1 mrg } 2708 1.1 mrg 2709 1.1 mrg /* For debugging layout issues, render a ruler giving column numbers 2710 1.1 mrg (after the 1-column indent). */ 2711 1.1 mrg 2712 1.1 mrg void 2713 1.1 mrg layout::show_ruler (int max_column) const 2714 1.1 mrg { 2715 1.1 mrg /* Hundreds. */ 2716 1.1 mrg if (max_column > 99) 2717 1.1 mrg { 2718 1.1 mrg start_annotation_line (); 2719 1.1 mrg pp_space (m_pp); 2720 1.1 mrg for (int column = 1 + m_x_offset_display; column <= max_column; column++) 2721 1.1 mrg if (column % 10 == 0) 2722 1.1 mrg pp_character (m_pp, '0' + (column / 100) % 10); 2723 1.1 mrg else 2724 1.1 mrg pp_space (m_pp); 2725 1.1 mrg pp_newline (m_pp); 2726 1.1 mrg } 2727 1.1 mrg 2728 1.1 mrg /* Tens. */ 2729 1.1 mrg start_annotation_line (); 2730 1.1 mrg pp_space (m_pp); 2731 1.1 mrg for (int column = 1 + m_x_offset_display; column <= max_column; column++) 2732 1.1 mrg if (column % 10 == 0) 2733 1.1 mrg pp_character (m_pp, '0' + (column / 10) % 10); 2734 1.1 mrg else 2735 1.1 mrg pp_space (m_pp); 2736 1.1 mrg pp_newline (m_pp); 2737 1.1 mrg 2738 1.1 mrg /* Units. */ 2739 1.1 mrg start_annotation_line (); 2740 1.1 mrg pp_space (m_pp); 2741 1.1 mrg for (int column = 1 + m_x_offset_display; column <= max_column; column++) 2742 1.1 mrg pp_character (m_pp, '0' + (column % 10)); 2743 1.1 mrg pp_newline (m_pp); 2744 1.1 mrg } 2745 1.1 mrg 2746 1.1 mrg /* Print leading fix-its (for new lines inserted before the source line) 2747 1.1 mrg then the source line, followed by an annotation line 2748 1.1 mrg consisting of any caret/underlines, then any fixits. 2749 1.1 mrg If the source line can't be read, print nothing. */ 2750 1.1 mrg void 2751 1.1 mrg layout::print_line (linenum_type row) 2752 1.1 mrg { 2753 1.1 mrg char_span line = location_get_source_line (m_exploc.file, row); 2754 1.1 mrg if (!line) 2755 1.1 mrg return; 2756 1.1 mrg 2757 1.1 mrg print_leading_fixits (row); 2758 1.1 mrg const line_bounds lbounds 2759 1.1 mrg = print_source_line (row, line.get_buffer (), line.length ()); 2760 1.1 mrg if (should_print_annotation_line_p (row)) 2761 1.1 mrg print_annotation_line (row, lbounds); 2762 1.1 mrg if (m_show_labels_p) 2763 1.1 mrg print_any_labels (row); 2764 1.1 mrg print_trailing_fixits (row); 2765 1.1 mrg } 2766 1.1 mrg 2767 1.1 mrg } /* End of anonymous namespace. */ 2768 1.1 mrg 2769 1.1 mrg /* If LOC is within the spans of lines that will already be printed for 2770 1.1 mrg this gcc_rich_location, then add it as a secondary location and return true. 2771 1.1 mrg 2772 1.1 mrg Otherwise return false. */ 2773 1.1 mrg 2774 1.1 mrg bool 2775 1.1 mrg gcc_rich_location::add_location_if_nearby (location_t loc, 2776 1.1 mrg bool restrict_to_current_line_spans, 2777 1.1 mrg const range_label *label) 2778 1.1 mrg { 2779 1.1 mrg /* Use the layout location-handling logic to sanitize LOC, 2780 1.1 mrg filtering it to the current line spans within a temporary 2781 1.1 mrg layout instance. */ 2782 1.1 mrg layout layout (global_dc, this, DK_ERROR); 2783 1.1 mrg location_range loc_range; 2784 1.1 mrg loc_range.m_loc = loc; 2785 1.1 mrg loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET; 2786 1.1 mrg if (!layout.maybe_add_location_range (&loc_range, 0, 2787 1.1 mrg restrict_to_current_line_spans)) 2788 1.1 mrg return false; 2789 1.1 mrg 2790 1.1 mrg add_range (loc, SHOW_RANGE_WITHOUT_CARET, label); 2791 1.1 mrg return true; 2792 1.1 mrg } 2793 1.1 mrg 2794 1.1 mrg /* Print the physical source code corresponding to the location of 2795 1.1 mrg this diagnostic, with additional annotations. */ 2796 1.1 mrg 2797 1.1 mrg void 2798 1.1 mrg diagnostic_show_locus (diagnostic_context * context, 2799 1.1 mrg rich_location *richloc, 2800 1.1 mrg diagnostic_t diagnostic_kind) 2801 1.1 mrg { 2802 1.1 mrg location_t loc = richloc->get_loc (); 2803 1.1 mrg /* Do nothing if source-printing has been disabled. */ 2804 1.1 mrg if (!context->show_caret) 2805 1.1 mrg return; 2806 1.1 mrg 2807 1.1 mrg /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */ 2808 1.1 mrg if (loc <= BUILTINS_LOCATION) 2809 1.1 mrg return; 2810 1.1 mrg 2811 1.1 mrg /* Don't print the same source location twice in a row, unless we have 2812 1.1 mrg fix-it hints, or multiple locations, or a label. */ 2813 1.1 mrg if (loc == context->last_location 2814 1.1 mrg && richloc->get_num_fixit_hints () == 0 2815 1.1 mrg && richloc->get_num_locations () == 1 2816 1.1 mrg && richloc->get_range (0)->m_label == NULL) 2817 1.1 mrg return; 2818 1.1 mrg 2819 1.1 mrg context->last_location = loc; 2820 1.1 mrg 2821 1.1 mrg layout layout (context, richloc, diagnostic_kind); 2822 1.1 mrg for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans (); 2823 1.1 mrg line_span_idx++) 2824 1.1 mrg { 2825 1.1 mrg const line_span *line_span = layout.get_line_span (line_span_idx); 2826 1.1 mrg if (context->show_line_numbers_p) 2827 1.1 mrg { 2828 1.1 mrg /* With line numbers, we should show whenever the line-numbering 2829 1.1 mrg "jumps". */ 2830 1.1 mrg if (line_span_idx > 0) 2831 1.1 mrg layout.print_gap_in_line_numbering (); 2832 1.1 mrg } 2833 1.1 mrg else 2834 1.1 mrg { 2835 1.1 mrg /* Without line numbers, we print headings for some line spans. */ 2836 1.1 mrg if (layout.print_heading_for_line_span_index_p (line_span_idx)) 2837 1.1 mrg { 2838 1.1 mrg expanded_location exploc 2839 1.1 mrg = layout.get_expanded_location (line_span); 2840 1.1 mrg context->start_span (context, exploc); 2841 1.1 mrg } 2842 1.1 mrg } 2843 1.1 mrg /* Iterate over the lines within this span (using linenum_arith_t to 2844 1.1 mrg avoid overflow with 0xffffffff causing an infinite loop). */ 2845 1.1 mrg linenum_arith_t last_line = line_span->get_last_line (); 2846 1.1 mrg for (linenum_arith_t row = line_span->get_first_line (); 2847 1.1 mrg row <= last_line; row++) 2848 1.1 mrg layout.print_line (row); 2849 1.1 mrg } 2850 1.1 mrg } 2851 1.1 mrg 2852 1.1 mrg #if CHECKING_P 2853 1.1 mrg 2854 1.1 mrg namespace selftest { 2855 1.1 mrg 2856 1.1 mrg /* Selftests for diagnostic_show_locus. */ 2857 1.1 mrg 2858 1.1 mrg /* Verify that cpp_display_width correctly handles escaping. */ 2859 1.1 mrg 2860 1.1 mrg static void 2861 1.1 mrg test_display_widths () 2862 1.1 mrg { 2863 1.1 mrg gcc_rich_location richloc (UNKNOWN_LOCATION); 2864 1.1 mrg 2865 1.1 mrg /* U+03C0 "GREEK SMALL LETTER PI". */ 2866 1.1 mrg const char *pi = "\xCF\x80"; 2867 1.1 mrg /* U+1F642 "SLIGHTLY SMILING FACE". */ 2868 1.1 mrg const char *emoji = "\xF0\x9F\x99\x82"; 2869 1.1 mrg /* Stray trailing byte of a UTF-8 character. */ 2870 1.1 mrg const char *stray = "\xBF"; 2871 1.1 mrg /* U+10FFFF. */ 2872 1.1 mrg const char *max_codepoint = "\xF4\x8F\xBF\xBF"; 2873 1.1 mrg 2874 1.1 mrg /* No escaping. */ 2875 1.1 mrg { 2876 1.1 mrg test_diagnostic_context dc; 2877 1.1 mrg char_display_policy policy (make_policy (dc, richloc)); 2878 1.1 mrg ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 1); 2879 1.1 mrg ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 2); 2880 1.1 mrg ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 1); 2881 1.1 mrg /* Don't check width of U+10FFFF; it's in a private use plane. */ 2882 1.1 mrg } 2883 1.1 mrg 2884 1.1 mrg richloc.set_escape_on_output (true); 2885 1.1 mrg 2886 1.1 mrg { 2887 1.1 mrg test_diagnostic_context dc; 2888 1.1 mrg dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE; 2889 1.1 mrg char_display_policy policy (make_policy (dc, richloc)); 2890 1.1 mrg ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8); 2891 1.1 mrg ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 9); 2892 1.1 mrg ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4); 2893 1.1 mrg ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint), 2894 1.1 mrg policy), 2895 1.1 mrg strlen ("<U+10FFFF>")); 2896 1.1 mrg } 2897 1.1 mrg 2898 1.1 mrg { 2899 1.1 mrg test_diagnostic_context dc; 2900 1.1 mrg dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES; 2901 1.1 mrg char_display_policy policy (make_policy (dc, richloc)); 2902 1.1 mrg ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8); 2903 1.1 mrg ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 16); 2904 1.1 mrg ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4); 2905 1.1 mrg ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint), 2906 1.1 mrg policy), 2907 1.1 mrg 16); 2908 1.1 mrg } 2909 1.1 mrg } 2910 1.1 mrg 2911 1.1 mrg /* For precise tests of the layout, make clear where the source line will 2912 1.1 mrg start. test_left_margin sets the total byte count from the left side of the 2913 1.1 mrg screen to the start of source lines, after the line number and the separator, 2914 1.1 mrg which consists of the three characters " | ". */ 2915 1.1 mrg static const int test_linenum_sep = 3; 2916 1.1 mrg static const int test_left_margin = 7; 2917 1.1 mrg 2918 1.1 mrg /* Helper function for test_layout_x_offset_display_utf8(). */ 2919 1.1 mrg static void 2920 1.1 mrg test_offset_impl (int caret_byte_col, int max_width, 2921 1.1 mrg int expected_x_offset_display, 2922 1.1 mrg int left_margin = test_left_margin) 2923 1.1 mrg { 2924 1.1 mrg test_diagnostic_context dc; 2925 1.1 mrg dc.caret_max_width = max_width; 2926 1.1 mrg /* diagnostic_context::min_margin_width sets the minimum space reserved for 2927 1.1 mrg the line number plus one space after. */ 2928 1.1 mrg dc.min_margin_width = left_margin - test_linenum_sep + 1; 2929 1.1 mrg dc.show_line_numbers_p = true; 2930 1.1 mrg rich_location richloc (line_table, 2931 1.1 mrg linemap_position_for_column (line_table, 2932 1.1 mrg caret_byte_col)); 2933 1.1 mrg layout test_layout (&dc, &richloc, DK_ERROR); 2934 1.1 mrg ASSERT_EQ (left_margin - test_linenum_sep, 2935 1.1 mrg test_layout.get_linenum_width ()); 2936 1.1 mrg ASSERT_EQ (expected_x_offset_display, 2937 1.1 mrg test_layout.get_x_offset_display ()); 2938 1.1 mrg } 2939 1.1 mrg 2940 1.1 mrg /* Test that layout::calculate_x_offset_display() works. */ 2941 1.1 mrg static void 2942 1.1 mrg test_layout_x_offset_display_utf8 (const line_table_case &case_) 2943 1.1 mrg { 2944 1.1 mrg 2945 1.1 mrg const char *content 2946 1.1 mrg = "This line is very long, so that we can use it to test the logic for " 2947 1.1 mrg "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a " 2948 1.1 mrg "pair of emojis that occupies 8 bytes and 4 display columns, starting at " 2949 1.1 mrg "column #102.\n"; 2950 1.1 mrg 2951 1.1 mrg /* Number of bytes in the line, subtracting one to remove the newline. */ 2952 1.1 mrg const int line_bytes = strlen (content) - 1; 2953 1.1 mrg 2954 1.1 mrg /* Number of display columns occupied by the line; each of the 2 emojis 2955 1.1 mrg takes up 2 fewer display columns than it does bytes. */ 2956 1.1 mrg const int line_display_cols = line_bytes - 2*2; 2957 1.1 mrg 2958 1.1 mrg /* The column of the first emoji. Byte or display is the same as there are 2959 1.1 mrg no multibyte characters earlier on the line. */ 2960 1.1 mrg const int emoji_col = 102; 2961 1.1 mrg 2962 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", content); 2963 1.1 mrg line_table_test ltt (case_); 2964 1.1 mrg 2965 1.1 mrg linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1); 2966 1.1 mrg 2967 1.1 mrg location_t line_end = linemap_position_for_column (line_table, line_bytes); 2968 1.1 mrg 2969 1.1 mrg /* Don't attempt to run the tests if column data might be unavailable. */ 2970 1.1 mrg if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 2971 1.1 mrg return; 2972 1.1 mrg 2973 1.1 mrg ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end)); 2974 1.1 mrg ASSERT_EQ (1, LOCATION_LINE (line_end)); 2975 1.1 mrg ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end)); 2976 1.1 mrg 2977 1.1 mrg char_span lspan = location_get_source_line (tmp.get_filename (), 1); 2978 1.1 mrg ASSERT_EQ (line_display_cols, 2979 1.1 mrg cpp_display_width (lspan.get_buffer (), lspan.length (), 2980 1.1 mrg def_policy ())); 2981 1.1 mrg ASSERT_EQ (line_display_cols, 2982 1.1 mrg location_compute_display_column (expand_location (line_end), 2983 1.1 mrg def_policy ())); 2984 1.1 mrg ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1), 2985 1.1 mrg "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8)); 2986 1.1 mrg 2987 1.1 mrg /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */ 2988 1.1 mrg 2989 1.1 mrg /* No constraint on the width -> no offset. */ 2990 1.1 mrg test_offset_impl (emoji_col, 0, 0); 2991 1.1 mrg 2992 1.1 mrg /* Caret is before the beginning -> no offset. */ 2993 1.1 mrg test_offset_impl (0, 100, 0); 2994 1.1 mrg 2995 1.1 mrg /* Caret is past the end of the line -> no offset. */ 2996 1.1 mrg test_offset_impl (line_bytes+1, 100, 0); 2997 1.1 mrg 2998 1.1 mrg /* Line fits in the display -> no offset. */ 2999 1.1 mrg test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0); 3000 1.1 mrg test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0); 3001 1.1 mrg 3002 1.1 mrg /* Line is too long for the display but caret location is OK 3003 1.1 mrg anyway -> no offset. */ 3004 1.1 mrg static const int small_width = 24; 3005 1.1 mrg test_offset_impl (1, small_width, 0); 3006 1.1 mrg 3007 1.1 mrg /* Width constraint is very small -> no offset. */ 3008 1.1 mrg test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0); 3009 1.1 mrg 3010 1.1 mrg /* Line would be offset, but due to large line numbers, offsetting 3011 1.1 mrg would remove the whole line -> no offset. */ 3012 1.1 mrg static const int huge_left_margin = 100; 3013 1.1 mrg test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin); 3014 1.1 mrg 3015 1.1 mrg /* Line is the same length as the display, but the line number makes it too 3016 1.1 mrg long, so offset is required. Caret is at the end so padding on the right 3017 1.1 mrg is not in effect. */ 3018 1.1 mrg for (int excess = 1; excess <= 3; ++excess) 3019 1.1 mrg test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess, 3020 1.1 mrg excess); 3021 1.1 mrg 3022 1.1 mrg /* Line is much too long for the display, caret is near the end -> 3023 1.1 mrg offset should be such that the line fits in the display and caret 3024 1.1 mrg remains the same distance from the end that it was. */ 3025 1.1 mrg for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10); 3026 1.1 mrg caret_offset <= max_offset; ++caret_offset) 3027 1.1 mrg test_offset_impl (line_bytes - caret_offset, small_width, 3028 1.1 mrg line_display_cols + test_left_margin - small_width); 3029 1.1 mrg 3030 1.1 mrg /* As previous case but caret is closer to the middle; now we want it to end 3031 1.1 mrg up CARET_LINE_MARGIN bytes from the end. */ 3032 1.1 mrg ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN); 3033 1.1 mrg test_offset_impl (emoji_col, small_width, 3034 1.1 mrg emoji_col + test_left_margin 3035 1.1 mrg - (small_width - CARET_LINE_MARGIN)); 3036 1.1 mrg 3037 1.1 mrg /* Test that the source line is offset as expected when printed. */ 3038 1.1 mrg { 3039 1.1 mrg test_diagnostic_context dc; 3040 1.1 mrg dc.caret_max_width = small_width - 6; 3041 1.1 mrg dc.min_margin_width = test_left_margin - test_linenum_sep + 1; 3042 1.1 mrg dc.show_line_numbers_p = true; 3043 1.1 mrg dc.show_ruler_p = true; 3044 1.1 mrg rich_location richloc (line_table, 3045 1.1 mrg linemap_position_for_column (line_table, 3046 1.1 mrg emoji_col)); 3047 1.1 mrg layout test_layout (&dc, &richloc, DK_ERROR); 3048 1.1 mrg test_layout.print_line (1); 3049 1.1 mrg ASSERT_STREQ (" | 1 \n" 3050 1.1 mrg " | 1 \n" 3051 1.1 mrg " | 234567890123456789\n" 3052 1.1 mrg " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis " 3053 1.1 mrg "that occupies 8 bytes and 4 display columns, starting at " 3054 1.1 mrg "column #102.\n" 3055 1.1 mrg " | ^\n\n", 3056 1.1 mrg pp_formatted_text (dc.printer)); 3057 1.1 mrg } 3058 1.1 mrg 3059 1.1 mrg /* Similar to the previous example, but now the offset called for would split 3060 1.1 mrg the first emoji in the middle of the UTF-8 sequence. Check that we replace 3061 1.1 mrg it with a padding space in this case. */ 3062 1.1 mrg { 3063 1.1 mrg test_diagnostic_context dc; 3064 1.1 mrg dc.caret_max_width = small_width - 5; 3065 1.1 mrg dc.min_margin_width = test_left_margin - test_linenum_sep + 1; 3066 1.1 mrg dc.show_line_numbers_p = true; 3067 1.1 mrg dc.show_ruler_p = true; 3068 1.1 mrg rich_location richloc (line_table, 3069 1.1 mrg linemap_position_for_column (line_table, 3070 1.1 mrg emoji_col + 2)); 3071 1.1 mrg layout test_layout (&dc, &richloc, DK_ERROR); 3072 1.1 mrg test_layout.print_line (1); 3073 1.1 mrg ASSERT_STREQ (" | 1 1 \n" 3074 1.1 mrg " | 1 2 \n" 3075 1.1 mrg " | 3456789012345678901\n" 3076 1.1 mrg " 1 | \xf0\x9f\x98\x82 is a pair of emojis " 3077 1.1 mrg "that occupies 8 bytes and 4 display columns, starting at " 3078 1.1 mrg "column #102.\n" 3079 1.1 mrg " | ^\n\n", 3080 1.1 mrg pp_formatted_text (dc.printer)); 3081 1.1 mrg } 3082 1.1 mrg 3083 1.1 mrg } 3084 1.1 mrg 3085 1.1 mrg static void 3086 1.1 mrg test_layout_x_offset_display_tab (const line_table_case &case_) 3087 1.1 mrg { 3088 1.1 mrg const char *content 3089 1.1 mrg = "This line is very long, so that we can use it to test the logic for " 3090 1.1 mrg "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and " 3091 1.1 mrg "a variable number of display columns, starting at column #103.\n"; 3092 1.1 mrg 3093 1.1 mrg /* Number of bytes in the line, subtracting one to remove the newline. */ 3094 1.1 mrg const int line_bytes = strlen (content) - 1; 3095 1.1 mrg 3096 1.1 mrg /* The column where the tab begins. Byte or display is the same as there are 3097 1.1 mrg no multibyte characters earlier on the line. */ 3098 1.1 mrg const int tab_col = 103; 3099 1.1 mrg 3100 1.1 mrg /* Effective extra size of the tab beyond what a single space would have taken 3101 1.1 mrg up, indexed by tabstop. */ 3102 1.1 mrg static const int num_tabstops = 11; 3103 1.1 mrg int extra_width[num_tabstops]; 3104 1.1 mrg for (int tabstop = 1; tabstop != num_tabstops; ++tabstop) 3105 1.1 mrg { 3106 1.1 mrg const int this_tab_size = tabstop - (tab_col - 1) % tabstop; 3107 1.1 mrg extra_width[tabstop] = this_tab_size - 1; 3108 1.1 mrg } 3109 1.1 mrg /* Example of this calculation: if tabstop is 10, the tab starting at column 3110 1.1 mrg #103 has to expand into 8 spaces, covering columns 103-110, so that the 3111 1.1 mrg next character is at column #111. So it takes up 7 more columns than 3112 1.1 mrg a space would have taken up. */ 3113 1.1 mrg ASSERT_EQ (7, extra_width[10]); 3114 1.1 mrg 3115 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", content); 3116 1.1 mrg line_table_test ltt (case_); 3117 1.1 mrg 3118 1.1 mrg linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1); 3119 1.1 mrg 3120 1.1 mrg location_t line_end = linemap_position_for_column (line_table, line_bytes); 3121 1.1 mrg 3122 1.1 mrg /* Don't attempt to run the tests if column data might be unavailable. */ 3123 1.1 mrg if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 3124 1.1 mrg return; 3125 1.1 mrg 3126 1.1 mrg /* Check that cpp_display_width handles the tabs as expected. */ 3127 1.1 mrg char_span lspan = location_get_source_line (tmp.get_filename (), 1); 3128 1.1 mrg ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1))); 3129 1.1 mrg for (int tabstop = 1; tabstop != num_tabstops; ++tabstop) 3130 1.1 mrg { 3131 1.1 mrg cpp_char_column_policy policy (tabstop, cpp_wcwidth); 3132 1.1 mrg ASSERT_EQ (line_bytes + extra_width[tabstop], 3133 1.1 mrg cpp_display_width (lspan.get_buffer (), lspan.length (), 3134 1.1 mrg policy)); 3135 1.1 mrg ASSERT_EQ (line_bytes + extra_width[tabstop], 3136 1.1 mrg location_compute_display_column (expand_location (line_end), 3137 1.1 mrg policy)); 3138 1.1 mrg } 3139 1.1 mrg 3140 1.1 mrg /* Check that the tab is expanded to the expected number of spaces. */ 3141 1.1 mrg rich_location richloc (line_table, 3142 1.1 mrg linemap_position_for_column (line_table, 3143 1.1 mrg tab_col + 1)); 3144 1.1 mrg for (int tabstop = 1; tabstop != num_tabstops; ++tabstop) 3145 1.1 mrg { 3146 1.1 mrg test_diagnostic_context dc; 3147 1.1 mrg dc.tabstop = tabstop; 3148 1.1 mrg layout test_layout (&dc, &richloc, DK_ERROR); 3149 1.1 mrg test_layout.print_line (1); 3150 1.1 mrg const char *out = pp_formatted_text (dc.printer); 3151 1.1 mrg ASSERT_EQ (NULL, strchr (out, '\t')); 3152 1.1 mrg const char *left_quote = strchr (out, '`'); 3153 1.1 mrg const char *right_quote = strchr (out, '\''); 3154 1.1 mrg ASSERT_NE (NULL, left_quote); 3155 1.1 mrg ASSERT_NE (NULL, right_quote); 3156 1.1 mrg ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2); 3157 1.1 mrg } 3158 1.1 mrg 3159 1.1 mrg /* Check that the line is offset properly and that the tab is broken up 3160 1.1 mrg into the expected number of spaces when it is the last character skipped 3161 1.1 mrg over. */ 3162 1.1 mrg for (int tabstop = 1; tabstop != num_tabstops; ++tabstop) 3163 1.1 mrg { 3164 1.1 mrg test_diagnostic_context dc; 3165 1.1 mrg dc.tabstop = tabstop; 3166 1.1 mrg static const int small_width = 24; 3167 1.1 mrg dc.caret_max_width = small_width - 4; 3168 1.1 mrg dc.min_margin_width = test_left_margin - test_linenum_sep + 1; 3169 1.1 mrg dc.show_line_numbers_p = true; 3170 1.1 mrg layout test_layout (&dc, &richloc, DK_ERROR); 3171 1.1 mrg test_layout.print_line (1); 3172 1.1 mrg 3173 1.1 mrg /* We have arranged things so that two columns will be printed before 3174 1.1 mrg the caret. If the tab results in more than one space, this should 3175 1.1 mrg produce two spaces in the output; otherwise, it will be a single space 3176 1.1 mrg preceded by the opening quote before the tab character. */ 3177 1.1 mrg const char *output1 3178 1.1 mrg = " 1 | ' is a tab that occupies 1 byte and a variable number of " 3179 1.1 mrg "display columns, starting at column #103.\n" 3180 1.1 mrg " | ^\n\n"; 3181 1.1 mrg const char *output2 3182 1.1 mrg = " 1 | ` ' is a tab that occupies 1 byte and a variable number of " 3183 1.1 mrg "display columns, starting at column #103.\n" 3184 1.1 mrg " | ^\n\n"; 3185 1.1 mrg const char *expected_output = (extra_width[tabstop] ? output1 : output2); 3186 1.1 mrg ASSERT_STREQ (expected_output, pp_formatted_text (dc.printer)); 3187 1.1 mrg } 3188 1.1 mrg } 3189 1.1 mrg 3190 1.1 mrg 3191 1.1 mrg /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */ 3192 1.1 mrg 3193 1.1 mrg static void 3194 1.1 mrg test_diagnostic_show_locus_unknown_location () 3195 1.1 mrg { 3196 1.1 mrg test_diagnostic_context dc; 3197 1.1 mrg rich_location richloc (line_table, UNKNOWN_LOCATION); 3198 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3199 1.1 mrg ASSERT_STREQ ("", pp_formatted_text (dc.printer)); 3200 1.1 mrg } 3201 1.1 mrg 3202 1.1 mrg /* Verify that diagnostic_show_locus works sanely for various 3203 1.1 mrg single-line cases. 3204 1.1 mrg 3205 1.1 mrg All of these work on the following 1-line source file: 3206 1.1 mrg .0000000001111111 3207 1.1 mrg .1234567890123456 3208 1.1 mrg "foo = bar.field;\n" 3209 1.1 mrg which is set up by test_diagnostic_show_locus_one_liner and calls 3210 1.1 mrg them. */ 3211 1.1 mrg 3212 1.1 mrg /* Just a caret. */ 3213 1.1 mrg 3214 1.1 mrg static void 3215 1.1 mrg test_one_liner_simple_caret () 3216 1.1 mrg { 3217 1.1 mrg test_diagnostic_context dc; 3218 1.1 mrg location_t caret = linemap_position_for_column (line_table, 10); 3219 1.1 mrg rich_location richloc (line_table, caret); 3220 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3221 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3222 1.1 mrg " ^\n", 3223 1.1 mrg pp_formatted_text (dc.printer)); 3224 1.1 mrg } 3225 1.1 mrg 3226 1.1 mrg /* Caret and range. */ 3227 1.1 mrg 3228 1.1 mrg static void 3229 1.1 mrg test_one_liner_caret_and_range () 3230 1.1 mrg { 3231 1.1 mrg test_diagnostic_context dc; 3232 1.1 mrg location_t caret = linemap_position_for_column (line_table, 10); 3233 1.1 mrg location_t start = linemap_position_for_column (line_table, 7); 3234 1.1 mrg location_t finish = linemap_position_for_column (line_table, 15); 3235 1.1 mrg location_t loc = make_location (caret, start, finish); 3236 1.1 mrg rich_location richloc (line_table, loc); 3237 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3238 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3239 1.1 mrg " ~~~^~~~~~\n", 3240 1.1 mrg pp_formatted_text (dc.printer)); 3241 1.1 mrg } 3242 1.1 mrg 3243 1.1 mrg /* Multiple ranges and carets. */ 3244 1.1 mrg 3245 1.1 mrg static void 3246 1.1 mrg test_one_liner_multiple_carets_and_ranges () 3247 1.1 mrg { 3248 1.1 mrg test_diagnostic_context dc; 3249 1.1 mrg location_t foo 3250 1.1 mrg = make_location (linemap_position_for_column (line_table, 2), 3251 1.1 mrg linemap_position_for_column (line_table, 1), 3252 1.1 mrg linemap_position_for_column (line_table, 3)); 3253 1.1 mrg dc.caret_chars[0] = 'A'; 3254 1.1 mrg 3255 1.1 mrg location_t bar 3256 1.1 mrg = make_location (linemap_position_for_column (line_table, 8), 3257 1.1 mrg linemap_position_for_column (line_table, 7), 3258 1.1 mrg linemap_position_for_column (line_table, 9)); 3259 1.1 mrg dc.caret_chars[1] = 'B'; 3260 1.1 mrg 3261 1.1 mrg location_t field 3262 1.1 mrg = make_location (linemap_position_for_column (line_table, 13), 3263 1.1 mrg linemap_position_for_column (line_table, 11), 3264 1.1 mrg linemap_position_for_column (line_table, 15)); 3265 1.1 mrg dc.caret_chars[2] = 'C'; 3266 1.1 mrg 3267 1.1 mrg rich_location richloc (line_table, foo); 3268 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITH_CARET); 3269 1.1 mrg richloc.add_range (field, SHOW_RANGE_WITH_CARET); 3270 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3271 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3272 1.1 mrg " ~A~ ~B~ ~~C~~\n", 3273 1.1 mrg pp_formatted_text (dc.printer)); 3274 1.1 mrg } 3275 1.1 mrg 3276 1.1 mrg /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */ 3277 1.1 mrg 3278 1.1 mrg static void 3279 1.1 mrg test_one_liner_fixit_insert_before () 3280 1.1 mrg { 3281 1.1 mrg test_diagnostic_context dc; 3282 1.1 mrg location_t caret = linemap_position_for_column (line_table, 7); 3283 1.1 mrg rich_location richloc (line_table, caret); 3284 1.1 mrg richloc.add_fixit_insert_before ("&"); 3285 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3286 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3287 1.1 mrg " ^\n" 3288 1.1 mrg " &\n", 3289 1.1 mrg pp_formatted_text (dc.printer)); 3290 1.1 mrg } 3291 1.1 mrg 3292 1.1 mrg /* Insertion fix-it hint: adding a "[0]" after "foo". */ 3293 1.1 mrg 3294 1.1 mrg static void 3295 1.1 mrg test_one_liner_fixit_insert_after () 3296 1.1 mrg { 3297 1.1 mrg test_diagnostic_context dc; 3298 1.1 mrg location_t start = linemap_position_for_column (line_table, 1); 3299 1.1 mrg location_t finish = linemap_position_for_column (line_table, 3); 3300 1.1 mrg location_t foo = make_location (start, start, finish); 3301 1.1 mrg rich_location richloc (line_table, foo); 3302 1.1 mrg richloc.add_fixit_insert_after ("[0]"); 3303 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3304 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3305 1.1 mrg " ^~~\n" 3306 1.1 mrg " [0]\n", 3307 1.1 mrg pp_formatted_text (dc.printer)); 3308 1.1 mrg } 3309 1.1 mrg 3310 1.1 mrg /* Removal fix-it hint: removal of the ".field". 3311 1.1 mrg Also verify the interaction of pp_set_prefix with rulers and 3312 1.1 mrg fix-it hints. */ 3313 1.1 mrg 3314 1.1 mrg static void 3315 1.1 mrg test_one_liner_fixit_remove () 3316 1.1 mrg { 3317 1.1 mrg location_t start = linemap_position_for_column (line_table, 10); 3318 1.1 mrg location_t finish = linemap_position_for_column (line_table, 15); 3319 1.1 mrg location_t dot = make_location (start, start, finish); 3320 1.1 mrg rich_location richloc (line_table, dot); 3321 1.1 mrg richloc.add_fixit_remove (); 3322 1.1 mrg 3323 1.1 mrg /* Normal. */ 3324 1.1 mrg { 3325 1.1 mrg test_diagnostic_context dc; 3326 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3327 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3328 1.1 mrg " ^~~~~~\n" 3329 1.1 mrg " ------\n", 3330 1.1 mrg pp_formatted_text (dc.printer)); 3331 1.1 mrg } 3332 1.1 mrg 3333 1.1 mrg /* Test of adding a prefix. */ 3334 1.1 mrg { 3335 1.1 mrg test_diagnostic_context dc; 3336 1.1 mrg pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE; 3337 1.1 mrg pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:")); 3338 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3339 1.1 mrg ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n" 3340 1.1 mrg "TEST PREFIX: ^~~~~~\n" 3341 1.1 mrg "TEST PREFIX: ------\n", 3342 1.1 mrg pp_formatted_text (dc.printer)); 3343 1.1 mrg } 3344 1.1 mrg 3345 1.1 mrg /* Normal, with ruler. */ 3346 1.1 mrg { 3347 1.1 mrg test_diagnostic_context dc; 3348 1.1 mrg dc.show_ruler_p = true; 3349 1.1 mrg dc.caret_max_width = 104; 3350 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3351 1.1 mrg ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n" 3352 1.1 mrg " 1 2 3 4 5 6 7 8 9 0 \n" 3353 1.1 mrg " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n" 3354 1.1 mrg " foo = bar.field;\n" 3355 1.1 mrg " ^~~~~~\n" 3356 1.1 mrg " ------\n", 3357 1.1 mrg pp_formatted_text (dc.printer)); 3358 1.1 mrg } 3359 1.1 mrg 3360 1.1 mrg /* Test of adding a prefix, with ruler. */ 3361 1.1 mrg { 3362 1.1 mrg test_diagnostic_context dc; 3363 1.1 mrg dc.show_ruler_p = true; 3364 1.1 mrg dc.caret_max_width = 50; 3365 1.1 mrg pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE; 3366 1.1 mrg pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:")); 3367 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3368 1.1 mrg ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n" 3369 1.1 mrg "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n" 3370 1.1 mrg "TEST PREFIX: foo = bar.field;\n" 3371 1.1 mrg "TEST PREFIX: ^~~~~~\n" 3372 1.1 mrg "TEST PREFIX: ------\n", 3373 1.1 mrg pp_formatted_text (dc.printer)); 3374 1.1 mrg } 3375 1.1 mrg 3376 1.1 mrg /* Test of adding a prefix, with ruler and line numbers. */ 3377 1.1 mrg { 3378 1.1 mrg test_diagnostic_context dc; 3379 1.1 mrg dc.show_ruler_p = true; 3380 1.1 mrg dc.caret_max_width = 50; 3381 1.1 mrg dc.show_line_numbers_p = true; 3382 1.1 mrg pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE; 3383 1.1 mrg pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:")); 3384 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3385 1.1 mrg ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n" 3386 1.1 mrg "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n" 3387 1.1 mrg "TEST PREFIX: 1 | foo = bar.field;\n" 3388 1.1 mrg "TEST PREFIX: | ^~~~~~\n" 3389 1.1 mrg "TEST PREFIX: | ------\n", 3390 1.1 mrg pp_formatted_text (dc.printer)); 3391 1.1 mrg } 3392 1.1 mrg } 3393 1.1 mrg 3394 1.1 mrg /* Replace fix-it hint: replacing "field" with "m_field". */ 3395 1.1 mrg 3396 1.1 mrg static void 3397 1.1 mrg test_one_liner_fixit_replace () 3398 1.1 mrg { 3399 1.1 mrg test_diagnostic_context dc; 3400 1.1 mrg location_t start = linemap_position_for_column (line_table, 11); 3401 1.1 mrg location_t finish = linemap_position_for_column (line_table, 15); 3402 1.1 mrg location_t field = make_location (start, start, finish); 3403 1.1 mrg rich_location richloc (line_table, field); 3404 1.1 mrg richloc.add_fixit_replace ("m_field"); 3405 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3406 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3407 1.1 mrg " ^~~~~\n" 3408 1.1 mrg " m_field\n", 3409 1.1 mrg pp_formatted_text (dc.printer)); 3410 1.1 mrg } 3411 1.1 mrg 3412 1.1 mrg /* Replace fix-it hint: replacing "field" with "m_field", 3413 1.1 mrg but where the caret was elsewhere. */ 3414 1.1 mrg 3415 1.1 mrg static void 3416 1.1 mrg test_one_liner_fixit_replace_non_equal_range () 3417 1.1 mrg { 3418 1.1 mrg test_diagnostic_context dc; 3419 1.1 mrg location_t equals = linemap_position_for_column (line_table, 5); 3420 1.1 mrg location_t start = linemap_position_for_column (line_table, 11); 3421 1.1 mrg location_t finish = linemap_position_for_column (line_table, 15); 3422 1.1 mrg rich_location richloc (line_table, equals); 3423 1.1 mrg source_range range; 3424 1.1 mrg range.m_start = start; 3425 1.1 mrg range.m_finish = finish; 3426 1.1 mrg richloc.add_fixit_replace (range, "m_field"); 3427 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3428 1.1 mrg /* The replacement range is not indicated in the annotation line, so 3429 1.1 mrg it should be indicated via an additional underline. */ 3430 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3431 1.1 mrg " ^\n" 3432 1.1 mrg " -----\n" 3433 1.1 mrg " m_field\n", 3434 1.1 mrg pp_formatted_text (dc.printer)); 3435 1.1 mrg } 3436 1.1 mrg 3437 1.1 mrg /* Replace fix-it hint: replacing "field" with "m_field", 3438 1.1 mrg where the caret was elsewhere, but where a secondary range 3439 1.1 mrg exactly covers "field". */ 3440 1.1 mrg 3441 1.1 mrg static void 3442 1.1 mrg test_one_liner_fixit_replace_equal_secondary_range () 3443 1.1 mrg { 3444 1.1 mrg test_diagnostic_context dc; 3445 1.1 mrg location_t equals = linemap_position_for_column (line_table, 5); 3446 1.1 mrg location_t start = linemap_position_for_column (line_table, 11); 3447 1.1 mrg location_t finish = linemap_position_for_column (line_table, 15); 3448 1.1 mrg rich_location richloc (line_table, equals); 3449 1.1 mrg location_t field = make_location (start, start, finish); 3450 1.1 mrg richloc.add_range (field); 3451 1.1 mrg richloc.add_fixit_replace (field, "m_field"); 3452 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3453 1.1 mrg /* The replacement range is indicated in the annotation line, 3454 1.1 mrg so it shouldn't be indicated via an additional underline. */ 3455 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3456 1.1 mrg " ^ ~~~~~\n" 3457 1.1 mrg " m_field\n", 3458 1.1 mrg pp_formatted_text (dc.printer)); 3459 1.1 mrg } 3460 1.1 mrg 3461 1.1 mrg /* Verify that we can use ad-hoc locations when adding fixits to a 3462 1.1 mrg rich_location. */ 3463 1.1 mrg 3464 1.1 mrg static void 3465 1.1 mrg test_one_liner_fixit_validation_adhoc_locations () 3466 1.1 mrg { 3467 1.1 mrg /* Generate a range that's too long to be packed, so must 3468 1.1 mrg be stored as an ad-hoc location (given the defaults 3469 1.1 mrg of 5 bits or 0 bits of packed range); 41 columns > 2**5. */ 3470 1.1 mrg const location_t c7 = linemap_position_for_column (line_table, 7); 3471 1.1 mrg const location_t c47 = linemap_position_for_column (line_table, 47); 3472 1.1 mrg const location_t loc = make_location (c7, c7, c47); 3473 1.1 mrg 3474 1.1 mrg if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS) 3475 1.1 mrg return; 3476 1.1 mrg 3477 1.1 mrg ASSERT_TRUE (IS_ADHOC_LOC (loc)); 3478 1.1 mrg 3479 1.1 mrg /* Insert. */ 3480 1.1 mrg { 3481 1.1 mrg rich_location richloc (line_table, loc); 3482 1.1 mrg richloc.add_fixit_insert_before (loc, "test"); 3483 1.1 mrg /* It should not have been discarded by the validator. */ 3484 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 3485 1.1 mrg 3486 1.1 mrg test_diagnostic_context dc; 3487 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3488 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3489 1.1 mrg " ^~~~~~~~~~ \n" 3490 1.1 mrg " test\n", 3491 1.1 mrg pp_formatted_text (dc.printer)); 3492 1.1 mrg } 3493 1.1 mrg 3494 1.1 mrg /* Remove. */ 3495 1.1 mrg { 3496 1.1 mrg rich_location richloc (line_table, loc); 3497 1.1 mrg source_range range = source_range::from_locations (loc, c47); 3498 1.1 mrg richloc.add_fixit_remove (range); 3499 1.1 mrg /* It should not have been discarded by the validator. */ 3500 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 3501 1.1 mrg 3502 1.1 mrg test_diagnostic_context dc; 3503 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3504 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3505 1.1 mrg " ^~~~~~~~~~ \n" 3506 1.1 mrg " -----------------------------------------\n", 3507 1.1 mrg pp_formatted_text (dc.printer)); 3508 1.1 mrg } 3509 1.1 mrg 3510 1.1 mrg /* Replace. */ 3511 1.1 mrg { 3512 1.1 mrg rich_location richloc (line_table, loc); 3513 1.1 mrg source_range range = source_range::from_locations (loc, c47); 3514 1.1 mrg richloc.add_fixit_replace (range, "test"); 3515 1.1 mrg /* It should not have been discarded by the validator. */ 3516 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 3517 1.1 mrg 3518 1.1 mrg test_diagnostic_context dc; 3519 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3520 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3521 1.1 mrg " ^~~~~~~~~~ \n" 3522 1.1 mrg " test\n", 3523 1.1 mrg pp_formatted_text (dc.printer)); 3524 1.1 mrg } 3525 1.1 mrg } 3526 1.1 mrg 3527 1.1 mrg /* Test of consolidating insertions at the same location. */ 3528 1.1 mrg 3529 1.1 mrg static void 3530 1.1 mrg test_one_liner_many_fixits_1 () 3531 1.1 mrg { 3532 1.1 mrg test_diagnostic_context dc; 3533 1.1 mrg location_t equals = linemap_position_for_column (line_table, 5); 3534 1.1 mrg rich_location richloc (line_table, equals); 3535 1.1 mrg for (int i = 0; i < 19; i++) 3536 1.1 mrg richloc.add_fixit_insert_before ("a"); 3537 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 3538 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3539 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3540 1.1 mrg " ^\n" 3541 1.1 mrg " aaaaaaaaaaaaaaaaaaa\n", 3542 1.1 mrg pp_formatted_text (dc.printer)); 3543 1.1 mrg } 3544 1.1 mrg 3545 1.1 mrg /* Ensure that we can add an arbitrary number of fix-it hints to a 3546 1.1 mrg rich_location, even if they are not consolidated. */ 3547 1.1 mrg 3548 1.1 mrg static void 3549 1.1 mrg test_one_liner_many_fixits_2 () 3550 1.1 mrg { 3551 1.1 mrg test_diagnostic_context dc; 3552 1.1 mrg location_t equals = linemap_position_for_column (line_table, 5); 3553 1.1 mrg rich_location richloc (line_table, equals); 3554 1.1 mrg for (int i = 0; i < 19; i++) 3555 1.1 mrg { 3556 1.1 mrg location_t loc = linemap_position_for_column (line_table, (i * 2) + 1); 3557 1.1 mrg richloc.add_fixit_insert_before (loc, "a"); 3558 1.1 mrg } 3559 1.1 mrg ASSERT_EQ (19, richloc.get_num_fixit_hints ()); 3560 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3561 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3562 1.1 mrg " ^\n" 3563 1.1 mrg " a a a a a a a a a a a a a a a a a a a\n", 3564 1.1 mrg pp_formatted_text (dc.printer)); 3565 1.1 mrg } 3566 1.1 mrg 3567 1.1 mrg /* Test of labeling the ranges within a rich_location. */ 3568 1.1 mrg 3569 1.1 mrg static void 3570 1.1 mrg test_one_liner_labels () 3571 1.1 mrg { 3572 1.1 mrg location_t foo 3573 1.1 mrg = make_location (linemap_position_for_column (line_table, 1), 3574 1.1 mrg linemap_position_for_column (line_table, 1), 3575 1.1 mrg linemap_position_for_column (line_table, 3)); 3576 1.1 mrg location_t bar 3577 1.1 mrg = make_location (linemap_position_for_column (line_table, 7), 3578 1.1 mrg linemap_position_for_column (line_table, 7), 3579 1.1 mrg linemap_position_for_column (line_table, 9)); 3580 1.1 mrg location_t field 3581 1.1 mrg = make_location (linemap_position_for_column (line_table, 11), 3582 1.1 mrg linemap_position_for_column (line_table, 11), 3583 1.1 mrg linemap_position_for_column (line_table, 15)); 3584 1.1 mrg 3585 1.1 mrg /* Example where all the labels fit on one line. */ 3586 1.1 mrg { 3587 1.1 mrg text_range_label label0 ("0"); 3588 1.1 mrg text_range_label label1 ("1"); 3589 1.1 mrg text_range_label label2 ("2"); 3590 1.1 mrg gcc_rich_location richloc (foo, &label0); 3591 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); 3592 1.1 mrg richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2); 3593 1.1 mrg 3594 1.1 mrg { 3595 1.1 mrg test_diagnostic_context dc; 3596 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3597 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3598 1.1 mrg " ^~~ ~~~ ~~~~~\n" 3599 1.1 mrg " | | |\n" 3600 1.1 mrg " 0 1 2\n", 3601 1.1 mrg pp_formatted_text (dc.printer)); 3602 1.1 mrg } 3603 1.1 mrg 3604 1.1 mrg /* Verify that we can disable label-printing. */ 3605 1.1 mrg { 3606 1.1 mrg test_diagnostic_context dc; 3607 1.1 mrg dc.show_labels_p = false; 3608 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3609 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3610 1.1 mrg " ^~~ ~~~ ~~~~~\n", 3611 1.1 mrg pp_formatted_text (dc.printer)); 3612 1.1 mrg } 3613 1.1 mrg } 3614 1.1 mrg 3615 1.1 mrg /* Example where the labels need extra lines. */ 3616 1.1 mrg { 3617 1.1 mrg text_range_label label0 ("label 0"); 3618 1.1 mrg text_range_label label1 ("label 1"); 3619 1.1 mrg text_range_label label2 ("label 2"); 3620 1.1 mrg gcc_rich_location richloc (foo, &label0); 3621 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); 3622 1.1 mrg richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2); 3623 1.1 mrg 3624 1.1 mrg test_diagnostic_context dc; 3625 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3626 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3627 1.1 mrg " ^~~ ~~~ ~~~~~\n" 3628 1.1 mrg " | | |\n" 3629 1.1 mrg " | | label 2\n" 3630 1.1 mrg " | label 1\n" 3631 1.1 mrg " label 0\n", 3632 1.1 mrg pp_formatted_text (dc.printer)); 3633 1.1 mrg } 3634 1.1 mrg 3635 1.1 mrg /* Example of boundary conditions: label 0 and 1 have just enough clearance, 3636 1.1 mrg but label 1 just touches label 2. */ 3637 1.1 mrg { 3638 1.1 mrg text_range_label label0 ("aaaaa"); 3639 1.1 mrg text_range_label label1 ("bbbb"); 3640 1.1 mrg text_range_label label2 ("c"); 3641 1.1 mrg gcc_rich_location richloc (foo, &label0); 3642 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); 3643 1.1 mrg richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2); 3644 1.1 mrg 3645 1.1 mrg test_diagnostic_context dc; 3646 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3647 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3648 1.1 mrg " ^~~ ~~~ ~~~~~\n" 3649 1.1 mrg " | | |\n" 3650 1.1 mrg " | | c\n" 3651 1.1 mrg " aaaaa bbbb\n", 3652 1.1 mrg pp_formatted_text (dc.printer)); 3653 1.1 mrg } 3654 1.1 mrg 3655 1.1 mrg /* Example of out-of-order ranges (thus requiring a sort). */ 3656 1.1 mrg { 3657 1.1 mrg text_range_label label0 ("0"); 3658 1.1 mrg text_range_label label1 ("1"); 3659 1.1 mrg text_range_label label2 ("2"); 3660 1.1 mrg gcc_rich_location richloc (field, &label0); 3661 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); 3662 1.1 mrg richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2); 3663 1.1 mrg 3664 1.1 mrg test_diagnostic_context dc; 3665 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3666 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3667 1.1 mrg " ~~~ ~~~ ^~~~~\n" 3668 1.1 mrg " | | |\n" 3669 1.1 mrg " 2 1 0\n", 3670 1.1 mrg pp_formatted_text (dc.printer)); 3671 1.1 mrg } 3672 1.1 mrg 3673 1.1 mrg /* Ensure we don't ICE if multiple ranges with labels are on 3674 1.1 mrg the same point. */ 3675 1.1 mrg { 3676 1.1 mrg text_range_label label0 ("label 0"); 3677 1.1 mrg text_range_label label1 ("label 1"); 3678 1.1 mrg text_range_label label2 ("label 2"); 3679 1.1 mrg gcc_rich_location richloc (bar, &label0); 3680 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); 3681 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2); 3682 1.1 mrg 3683 1.1 mrg test_diagnostic_context dc; 3684 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3685 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3686 1.1 mrg " ^~~\n" 3687 1.1 mrg " |\n" 3688 1.1 mrg " label 0\n" 3689 1.1 mrg " label 1\n" 3690 1.1 mrg " label 2\n", 3691 1.1 mrg pp_formatted_text (dc.printer)); 3692 1.1 mrg } 3693 1.1 mrg 3694 1.1 mrg /* Example of out-of-order ranges (thus requiring a sort), where 3695 1.1 mrg they overlap, and there are multiple ranges on the same point. */ 3696 1.1 mrg { 3697 1.1 mrg text_range_label label_0a ("label 0a"); 3698 1.1 mrg text_range_label label_1a ("label 1a"); 3699 1.1 mrg text_range_label label_2a ("label 2a"); 3700 1.1 mrg text_range_label label_0b ("label 0b"); 3701 1.1 mrg text_range_label label_1b ("label 1b"); 3702 1.1 mrg text_range_label label_2b ("label 2b"); 3703 1.1 mrg text_range_label label_0c ("label 0c"); 3704 1.1 mrg text_range_label label_1c ("label 1c"); 3705 1.1 mrg text_range_label label_2c ("label 2c"); 3706 1.1 mrg gcc_rich_location richloc (field, &label_0a); 3707 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a); 3708 1.1 mrg richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a); 3709 1.1 mrg 3710 1.1 mrg richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b); 3711 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b); 3712 1.1 mrg richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b); 3713 1.1 mrg 3714 1.1 mrg richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c); 3715 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c); 3716 1.1 mrg richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c); 3717 1.1 mrg 3718 1.1 mrg test_diagnostic_context dc; 3719 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3720 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3721 1.1 mrg " ~~~ ~~~ ^~~~~\n" 3722 1.1 mrg " | | |\n" 3723 1.1 mrg " | | label 0a\n" 3724 1.1 mrg " | | label 0b\n" 3725 1.1 mrg " | | label 0c\n" 3726 1.1 mrg " | label 1a\n" 3727 1.1 mrg " | label 1b\n" 3728 1.1 mrg " | label 1c\n" 3729 1.1 mrg " label 2a\n" 3730 1.1 mrg " label 2b\n" 3731 1.1 mrg " label 2c\n", 3732 1.1 mrg pp_formatted_text (dc.printer)); 3733 1.1 mrg } 3734 1.1 mrg 3735 1.1 mrg /* Verify that a NULL result from range_label::get_text is 3736 1.1 mrg handled gracefully. */ 3737 1.1 mrg { 3738 1.1 mrg text_range_label label (NULL); 3739 1.1 mrg gcc_rich_location richloc (bar, &label); 3740 1.1 mrg 3741 1.1 mrg test_diagnostic_context dc; 3742 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3743 1.1 mrg ASSERT_STREQ (" foo = bar.field;\n" 3744 1.1 mrg " ^~~\n", 3745 1.1 mrg pp_formatted_text (dc.printer)); 3746 1.1 mrg } 3747 1.1 mrg 3748 1.1 mrg /* TODO: example of formatted printing (needs to be in 3749 1.1 mrg gcc-rich-location.cc due to Makefile.in issues). */ 3750 1.1 mrg } 3751 1.1 mrg 3752 1.1 mrg /* Run the various one-liner tests. */ 3753 1.1 mrg 3754 1.1 mrg static void 3755 1.1 mrg test_diagnostic_show_locus_one_liner (const line_table_case &case_) 3756 1.1 mrg { 3757 1.1 mrg /* Create a tempfile and write some text to it. 3758 1.1 mrg ....................0000000001111111. 3759 1.1 mrg ....................1234567890123456. */ 3760 1.1 mrg const char *content = "foo = bar.field;\n"; 3761 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", content); 3762 1.1 mrg line_table_test ltt (case_); 3763 1.1 mrg 3764 1.1 mrg linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1); 3765 1.1 mrg 3766 1.1 mrg location_t line_end = linemap_position_for_column (line_table, 16); 3767 1.1 mrg 3768 1.1 mrg /* Don't attempt to run the tests if column data might be unavailable. */ 3769 1.1 mrg if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 3770 1.1 mrg return; 3771 1.1 mrg 3772 1.1 mrg ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end)); 3773 1.1 mrg ASSERT_EQ (1, LOCATION_LINE (line_end)); 3774 1.1 mrg ASSERT_EQ (16, LOCATION_COLUMN (line_end)); 3775 1.1 mrg 3776 1.1 mrg test_one_liner_simple_caret (); 3777 1.1 mrg test_one_liner_caret_and_range (); 3778 1.1 mrg test_one_liner_multiple_carets_and_ranges (); 3779 1.1 mrg test_one_liner_fixit_insert_before (); 3780 1.1 mrg test_one_liner_fixit_insert_after (); 3781 1.1 mrg test_one_liner_fixit_remove (); 3782 1.1 mrg test_one_liner_fixit_replace (); 3783 1.1 mrg test_one_liner_fixit_replace_non_equal_range (); 3784 1.1 mrg test_one_liner_fixit_replace_equal_secondary_range (); 3785 1.1 mrg test_one_liner_fixit_validation_adhoc_locations (); 3786 1.1 mrg test_one_liner_many_fixits_1 (); 3787 1.1 mrg test_one_liner_many_fixits_2 (); 3788 1.1 mrg test_one_liner_labels (); 3789 1.1 mrg } 3790 1.1 mrg 3791 1.1 mrg /* Version of all one-liner tests exercising multibyte awareness. For 3792 1.1 mrg simplicity we stick to using two multibyte characters in the test, U+1F602 3793 1.1 mrg == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0 3794 1.1 mrg == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the 3795 1.1 mrg below asserts would be easier to read if we used UTF-8 directly in the 3796 1.1 mrg string constants, but it seems better not to demand the host compiler 3797 1.1 mrg support this, when it isn't otherwise necessary. Instead, whenever an 3798 1.1 mrg extended character appears in a string, we put a line break after it so that 3799 1.1 mrg all succeeding characters can appear visually at the correct display column. 3800 1.1 mrg 3801 1.1 mrg All of these work on the following 1-line source file: 3802 1.1 mrg 3803 1.1 mrg .0000000001111111111222222 display 3804 1.1 mrg .1234567890123456789012345 columns 3805 1.1 mrg "SS_foo = P_bar.SS_fieldP;\n" 3806 1.1 mrg .0000000111111111222222223 byte 3807 1.1 mrg .1356789012456789134567891 columns 3808 1.1 mrg 3809 1.1 mrg which is set up by test_diagnostic_show_locus_one_liner and calls 3810 1.1 mrg them. Here SS represents the two display columns for the U+1F602 emoji and 3811 1.1 mrg P represents the one display column for the U+03C0 pi symbol. */ 3812 1.1 mrg 3813 1.1 mrg /* Just a caret. */ 3814 1.1 mrg 3815 1.1 mrg static void 3816 1.1 mrg test_one_liner_simple_caret_utf8 () 3817 1.1 mrg { 3818 1.1 mrg test_diagnostic_context dc; 3819 1.1 mrg location_t caret = linemap_position_for_column (line_table, 18); 3820 1.1 mrg rich_location richloc (line_table, caret); 3821 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3822 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 3823 1.1 mrg "_foo = \xcf\x80" 3824 1.1 mrg "_bar.\xf0\x9f\x98\x82" 3825 1.1 mrg "_field\xcf\x80" 3826 1.1 mrg ";\n" 3827 1.1 mrg " ^\n", 3828 1.1 mrg pp_formatted_text (dc.printer)); 3829 1.1 mrg } 3830 1.1 mrg 3831 1.1 mrg /* Caret and range. */ 3832 1.1 mrg static void 3833 1.1 mrg test_one_liner_caret_and_range_utf8 () 3834 1.1 mrg { 3835 1.1 mrg test_diagnostic_context dc; 3836 1.1 mrg location_t caret = linemap_position_for_column (line_table, 18); 3837 1.1 mrg location_t start = linemap_position_for_column (line_table, 12); 3838 1.1 mrg location_t finish = linemap_position_for_column (line_table, 30); 3839 1.1 mrg location_t loc = make_location (caret, start, finish); 3840 1.1 mrg rich_location richloc (line_table, loc); 3841 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3842 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 3843 1.1 mrg "_foo = \xcf\x80" 3844 1.1 mrg "_bar.\xf0\x9f\x98\x82" 3845 1.1 mrg "_field\xcf\x80" 3846 1.1 mrg ";\n" 3847 1.1 mrg " ~~~~~^~~~~~~~~~\n", 3848 1.1 mrg pp_formatted_text (dc.printer)); 3849 1.1 mrg } 3850 1.1 mrg 3851 1.1 mrg /* Multiple ranges and carets. */ 3852 1.1 mrg 3853 1.1 mrg static void 3854 1.1 mrg test_one_liner_multiple_carets_and_ranges_utf8 () 3855 1.1 mrg { 3856 1.1 mrg test_diagnostic_context dc; 3857 1.1 mrg location_t foo 3858 1.1 mrg = make_location (linemap_position_for_column (line_table, 7), 3859 1.1 mrg linemap_position_for_column (line_table, 1), 3860 1.1 mrg linemap_position_for_column (line_table, 8)); 3861 1.1 mrg dc.caret_chars[0] = 'A'; 3862 1.1 mrg 3863 1.1 mrg location_t bar 3864 1.1 mrg = make_location (linemap_position_for_column (line_table, 16), 3865 1.1 mrg linemap_position_for_column (line_table, 12), 3866 1.1 mrg linemap_position_for_column (line_table, 17)); 3867 1.1 mrg dc.caret_chars[1] = 'B'; 3868 1.1 mrg 3869 1.1 mrg location_t field 3870 1.1 mrg = make_location (linemap_position_for_column (line_table, 26), 3871 1.1 mrg linemap_position_for_column (line_table, 19), 3872 1.1 mrg linemap_position_for_column (line_table, 30)); 3873 1.1 mrg dc.caret_chars[2] = 'C'; 3874 1.1 mrg rich_location richloc (line_table, foo); 3875 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITH_CARET); 3876 1.1 mrg richloc.add_range (field, SHOW_RANGE_WITH_CARET); 3877 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3878 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 3879 1.1 mrg "_foo = \xcf\x80" 3880 1.1 mrg "_bar.\xf0\x9f\x98\x82" 3881 1.1 mrg "_field\xcf\x80" 3882 1.1 mrg ";\n" 3883 1.1 mrg " ~~~~A~ ~~~B~ ~~~~~C~~~\n", 3884 1.1 mrg pp_formatted_text (dc.printer)); 3885 1.1 mrg } 3886 1.1 mrg 3887 1.1 mrg /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */ 3888 1.1 mrg 3889 1.1 mrg static void 3890 1.1 mrg test_one_liner_fixit_insert_before_utf8 () 3891 1.1 mrg { 3892 1.1 mrg test_diagnostic_context dc; 3893 1.1 mrg location_t caret = linemap_position_for_column (line_table, 12); 3894 1.1 mrg rich_location richloc (line_table, caret); 3895 1.1 mrg richloc.add_fixit_insert_before ("&"); 3896 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3897 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 3898 1.1 mrg "_foo = \xcf\x80" 3899 1.1 mrg "_bar.\xf0\x9f\x98\x82" 3900 1.1 mrg "_field\xcf\x80" 3901 1.1 mrg ";\n" 3902 1.1 mrg " ^\n" 3903 1.1 mrg " &\n", 3904 1.1 mrg pp_formatted_text (dc.printer)); 3905 1.1 mrg } 3906 1.1 mrg 3907 1.1 mrg /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */ 3908 1.1 mrg 3909 1.1 mrg static void 3910 1.1 mrg test_one_liner_fixit_insert_after_utf8 () 3911 1.1 mrg { 3912 1.1 mrg test_diagnostic_context dc; 3913 1.1 mrg location_t start = linemap_position_for_column (line_table, 1); 3914 1.1 mrg location_t finish = linemap_position_for_column (line_table, 8); 3915 1.1 mrg location_t foo = make_location (start, start, finish); 3916 1.1 mrg rich_location richloc (line_table, foo); 3917 1.1 mrg richloc.add_fixit_insert_after ("[0]"); 3918 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3919 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 3920 1.1 mrg "_foo = \xcf\x80" 3921 1.1 mrg "_bar.\xf0\x9f\x98\x82" 3922 1.1 mrg "_field\xcf\x80" 3923 1.1 mrg ";\n" 3924 1.1 mrg " ^~~~~~\n" 3925 1.1 mrg " [0]\n", 3926 1.1 mrg pp_formatted_text (dc.printer)); 3927 1.1 mrg } 3928 1.1 mrg 3929 1.1 mrg /* Removal fix-it hint: removal of the ".SS_fieldP". */ 3930 1.1 mrg 3931 1.1 mrg static void 3932 1.1 mrg test_one_liner_fixit_remove_utf8 () 3933 1.1 mrg { 3934 1.1 mrg test_diagnostic_context dc; 3935 1.1 mrg location_t start = linemap_position_for_column (line_table, 18); 3936 1.1 mrg location_t finish = linemap_position_for_column (line_table, 30); 3937 1.1 mrg location_t dot = make_location (start, start, finish); 3938 1.1 mrg rich_location richloc (line_table, dot); 3939 1.1 mrg richloc.add_fixit_remove (); 3940 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3941 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 3942 1.1 mrg "_foo = \xcf\x80" 3943 1.1 mrg "_bar.\xf0\x9f\x98\x82" 3944 1.1 mrg "_field\xcf\x80" 3945 1.1 mrg ";\n" 3946 1.1 mrg " ^~~~~~~~~~\n" 3947 1.1 mrg " ----------\n", 3948 1.1 mrg pp_formatted_text (dc.printer)); 3949 1.1 mrg } 3950 1.1 mrg 3951 1.1 mrg /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */ 3952 1.1 mrg 3953 1.1 mrg static void 3954 1.1 mrg test_one_liner_fixit_replace_utf8 () 3955 1.1 mrg { 3956 1.1 mrg test_diagnostic_context dc; 3957 1.1 mrg location_t start = linemap_position_for_column (line_table, 19); 3958 1.1 mrg location_t finish = linemap_position_for_column (line_table, 30); 3959 1.1 mrg location_t field = make_location (start, start, finish); 3960 1.1 mrg rich_location richloc (line_table, field); 3961 1.1 mrg richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80"); 3962 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3963 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 3964 1.1 mrg "_foo = \xcf\x80" 3965 1.1 mrg "_bar.\xf0\x9f\x98\x82" 3966 1.1 mrg "_field\xcf\x80" 3967 1.1 mrg ";\n" 3968 1.1 mrg " ^~~~~~~~~\n" 3969 1.1 mrg " m_\xf0\x9f\x98\x82" 3970 1.1 mrg "_field\xcf\x80\n", 3971 1.1 mrg pp_formatted_text (dc.printer)); 3972 1.1 mrg } 3973 1.1 mrg 3974 1.1 mrg /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP", 3975 1.1 mrg but where the caret was elsewhere. */ 3976 1.1 mrg 3977 1.1 mrg static void 3978 1.1 mrg test_one_liner_fixit_replace_non_equal_range_utf8 () 3979 1.1 mrg { 3980 1.1 mrg test_diagnostic_context dc; 3981 1.1 mrg location_t equals = linemap_position_for_column (line_table, 10); 3982 1.1 mrg location_t start = linemap_position_for_column (line_table, 19); 3983 1.1 mrg location_t finish = linemap_position_for_column (line_table, 30); 3984 1.1 mrg rich_location richloc (line_table, equals); 3985 1.1 mrg source_range range; 3986 1.1 mrg range.m_start = start; 3987 1.1 mrg range.m_finish = finish; 3988 1.1 mrg richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80"); 3989 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 3990 1.1 mrg /* The replacement range is not indicated in the annotation line, so 3991 1.1 mrg it should be indicated via an additional underline. */ 3992 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 3993 1.1 mrg "_foo = \xcf\x80" 3994 1.1 mrg "_bar.\xf0\x9f\x98\x82" 3995 1.1 mrg "_field\xcf\x80" 3996 1.1 mrg ";\n" 3997 1.1 mrg " ^\n" 3998 1.1 mrg " ---------\n" 3999 1.1 mrg " m_\xf0\x9f\x98\x82" 4000 1.1 mrg "_field\xcf\x80\n", 4001 1.1 mrg pp_formatted_text (dc.printer)); 4002 1.1 mrg } 4003 1.1 mrg 4004 1.1 mrg /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP", 4005 1.1 mrg where the caret was elsewhere, but where a secondary range 4006 1.1 mrg exactly covers "field". */ 4007 1.1 mrg 4008 1.1 mrg static void 4009 1.1 mrg test_one_liner_fixit_replace_equal_secondary_range_utf8 () 4010 1.1 mrg { 4011 1.1 mrg test_diagnostic_context dc; 4012 1.1 mrg location_t equals = linemap_position_for_column (line_table, 10); 4013 1.1 mrg location_t start = linemap_position_for_column (line_table, 19); 4014 1.1 mrg location_t finish = linemap_position_for_column (line_table, 30); 4015 1.1 mrg rich_location richloc (line_table, equals); 4016 1.1 mrg location_t field = make_location (start, start, finish); 4017 1.1 mrg richloc.add_range (field); 4018 1.1 mrg richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80"); 4019 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4020 1.1 mrg /* The replacement range is indicated in the annotation line, 4021 1.1 mrg so it shouldn't be indicated via an additional underline. */ 4022 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 4023 1.1 mrg "_foo = \xcf\x80" 4024 1.1 mrg "_bar.\xf0\x9f\x98\x82" 4025 1.1 mrg "_field\xcf\x80" 4026 1.1 mrg ";\n" 4027 1.1 mrg " ^ ~~~~~~~~~\n" 4028 1.1 mrg " m_\xf0\x9f\x98\x82" 4029 1.1 mrg "_field\xcf\x80\n", 4030 1.1 mrg pp_formatted_text (dc.printer)); 4031 1.1 mrg } 4032 1.1 mrg 4033 1.1 mrg /* Verify that we can use ad-hoc locations when adding fixits to a 4034 1.1 mrg rich_location. */ 4035 1.1 mrg 4036 1.1 mrg static void 4037 1.1 mrg test_one_liner_fixit_validation_adhoc_locations_utf8 () 4038 1.1 mrg { 4039 1.1 mrg /* Generate a range that's too long to be packed, so must 4040 1.1 mrg be stored as an ad-hoc location (given the defaults 4041 1.1 mrg of 5 bits or 0 bits of packed range); 41 columns > 2**5. */ 4042 1.1 mrg const location_t c12 = linemap_position_for_column (line_table, 12); 4043 1.1 mrg const location_t c52 = linemap_position_for_column (line_table, 52); 4044 1.1 mrg const location_t loc = make_location (c12, c12, c52); 4045 1.1 mrg 4046 1.1 mrg if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS) 4047 1.1 mrg return; 4048 1.1 mrg 4049 1.1 mrg ASSERT_TRUE (IS_ADHOC_LOC (loc)); 4050 1.1 mrg 4051 1.1 mrg /* Insert. */ 4052 1.1 mrg { 4053 1.1 mrg rich_location richloc (line_table, loc); 4054 1.1 mrg richloc.add_fixit_insert_before (loc, "test"); 4055 1.1 mrg /* It should not have been discarded by the validator. */ 4056 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 4057 1.1 mrg 4058 1.1 mrg test_diagnostic_context dc; 4059 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4060 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 4061 1.1 mrg "_foo = \xcf\x80" 4062 1.1 mrg "_bar.\xf0\x9f\x98\x82" 4063 1.1 mrg "_field\xcf\x80" 4064 1.1 mrg ";\n" 4065 1.1 mrg " ^~~~~~~~~~~~~~~~ \n" 4066 1.1 mrg " test\n", 4067 1.1 mrg pp_formatted_text (dc.printer)); 4068 1.1 mrg } 4069 1.1 mrg 4070 1.1 mrg /* Remove. */ 4071 1.1 mrg { 4072 1.1 mrg rich_location richloc (line_table, loc); 4073 1.1 mrg source_range range = source_range::from_locations (loc, c52); 4074 1.1 mrg richloc.add_fixit_remove (range); 4075 1.1 mrg /* It should not have been discarded by the validator. */ 4076 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 4077 1.1 mrg 4078 1.1 mrg test_diagnostic_context dc; 4079 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4080 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 4081 1.1 mrg "_foo = \xcf\x80" 4082 1.1 mrg "_bar.\xf0\x9f\x98\x82" 4083 1.1 mrg "_field\xcf\x80" 4084 1.1 mrg ";\n" 4085 1.1 mrg " ^~~~~~~~~~~~~~~~ \n" 4086 1.1 mrg " -------------------------------------\n", 4087 1.1 mrg pp_formatted_text (dc.printer)); 4088 1.1 mrg } 4089 1.1 mrg 4090 1.1 mrg /* Replace. */ 4091 1.1 mrg { 4092 1.1 mrg rich_location richloc (line_table, loc); 4093 1.1 mrg source_range range = source_range::from_locations (loc, c52); 4094 1.1 mrg richloc.add_fixit_replace (range, "test"); 4095 1.1 mrg /* It should not have been discarded by the validator. */ 4096 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 4097 1.1 mrg 4098 1.1 mrg test_diagnostic_context dc; 4099 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4100 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 4101 1.1 mrg "_foo = \xcf\x80" 4102 1.1 mrg "_bar.\xf0\x9f\x98\x82" 4103 1.1 mrg "_field\xcf\x80" 4104 1.1 mrg ";\n" 4105 1.1 mrg " ^~~~~~~~~~~~~~~~ \n" 4106 1.1 mrg " test\n", 4107 1.1 mrg pp_formatted_text (dc.printer)); 4108 1.1 mrg } 4109 1.1 mrg } 4110 1.1 mrg 4111 1.1 mrg /* Test of consolidating insertions at the same location. */ 4112 1.1 mrg 4113 1.1 mrg static void 4114 1.1 mrg test_one_liner_many_fixits_1_utf8 () 4115 1.1 mrg { 4116 1.1 mrg test_diagnostic_context dc; 4117 1.1 mrg location_t equals = linemap_position_for_column (line_table, 10); 4118 1.1 mrg rich_location richloc (line_table, equals); 4119 1.1 mrg for (int i = 0; i < 19; i++) 4120 1.1 mrg richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80"); 4121 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 4122 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4123 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 4124 1.1 mrg "_foo = \xcf\x80" 4125 1.1 mrg "_bar.\xf0\x9f\x98\x82" 4126 1.1 mrg "_field\xcf\x80" 4127 1.1 mrg ";\n" 4128 1.1 mrg " ^\n" 4129 1.1 mrg " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@" 4130 1.1 mrg "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n", 4131 1.1 mrg pp_formatted_text (dc.printer)); 4132 1.1 mrg } 4133 1.1 mrg 4134 1.1 mrg /* Ensure that we can add an arbitrary number of fix-it hints to a 4135 1.1 mrg rich_location, even if they are not consolidated. */ 4136 1.1 mrg 4137 1.1 mrg static void 4138 1.1 mrg test_one_liner_many_fixits_2_utf8 () 4139 1.1 mrg { 4140 1.1 mrg test_diagnostic_context dc; 4141 1.1 mrg location_t equals = linemap_position_for_column (line_table, 10); 4142 1.1 mrg rich_location richloc (line_table, equals); 4143 1.1 mrg const int nlocs = 19; 4144 1.1 mrg int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32, 4145 1.1 mrg 34, 36, 38, 40, 42, 44}; 4146 1.1 mrg for (int i = 0; i != nlocs; ++i) 4147 1.1 mrg { 4148 1.1 mrg location_t loc = linemap_position_for_column (line_table, locs[i]); 4149 1.1 mrg richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80"); 4150 1.1 mrg } 4151 1.1 mrg 4152 1.1 mrg ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ()); 4153 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4154 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 4155 1.1 mrg "_foo = \xcf\x80" 4156 1.1 mrg "_bar.\xf0\x9f\x98\x82" 4157 1.1 mrg "_field\xcf\x80" 4158 1.1 mrg ";\n" 4159 1.1 mrg " ^\n" 4160 1.1 mrg " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @" 4161 1.1 mrg " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n", 4162 1.1 mrg pp_formatted_text (dc.printer)); 4163 1.1 mrg } 4164 1.1 mrg 4165 1.1 mrg /* Test of labeling the ranges within a rich_location. */ 4166 1.1 mrg 4167 1.1 mrg static void 4168 1.1 mrg test_one_liner_labels_utf8 () 4169 1.1 mrg { 4170 1.1 mrg location_t foo 4171 1.1 mrg = make_location (linemap_position_for_column (line_table, 1), 4172 1.1 mrg linemap_position_for_column (line_table, 1), 4173 1.1 mrg linemap_position_for_column (line_table, 8)); 4174 1.1 mrg location_t bar 4175 1.1 mrg = make_location (linemap_position_for_column (line_table, 12), 4176 1.1 mrg linemap_position_for_column (line_table, 12), 4177 1.1 mrg linemap_position_for_column (line_table, 17)); 4178 1.1 mrg location_t field 4179 1.1 mrg = make_location (linemap_position_for_column (line_table, 19), 4180 1.1 mrg linemap_position_for_column (line_table, 19), 4181 1.1 mrg linemap_position_for_column (line_table, 30)); 4182 1.1 mrg 4183 1.1 mrg /* Example where all the labels fit on one line. */ 4184 1.1 mrg { 4185 1.1 mrg /* These three labels contain multibyte characters such that their byte 4186 1.1 mrg lengths are respectively (12, 10, 18), but their display widths are only 4187 1.1 mrg (6, 5, 9). All three fit on the line when considering the display 4188 1.1 mrg widths, but not when considering the byte widths, so verify that we do 4189 1.1 mrg indeed put them all on one line. */ 4190 1.1 mrg text_range_label label0 4191 1.1 mrg ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"); 4192 1.1 mrg text_range_label label1 4193 1.1 mrg ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"); 4194 1.1 mrg text_range_label label2 4195 1.1 mrg ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80" 4196 1.1 mrg "\xcf\x80"); 4197 1.1 mrg gcc_rich_location richloc (foo, &label0); 4198 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); 4199 1.1 mrg richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2); 4200 1.1 mrg 4201 1.1 mrg { 4202 1.1 mrg test_diagnostic_context dc; 4203 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4204 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 4205 1.1 mrg "_foo = \xcf\x80" 4206 1.1 mrg "_bar.\xf0\x9f\x98\x82" 4207 1.1 mrg "_field\xcf\x80" 4208 1.1 mrg ";\n" 4209 1.1 mrg " ^~~~~~ ~~~~~ ~~~~~~~~~\n" 4210 1.1 mrg " | | |\n" 4211 1.1 mrg " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80" 4212 1.1 mrg " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80" 4213 1.1 mrg " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82" 4214 1.1 mrg "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n", 4215 1.1 mrg pp_formatted_text (dc.printer)); 4216 1.1 mrg } 4217 1.1 mrg 4218 1.1 mrg } 4219 1.1 mrg 4220 1.1 mrg /* Example where the labels need extra lines. */ 4221 1.1 mrg { 4222 1.1 mrg text_range_label label0 ("label 0\xf0\x9f\x98\x82"); 4223 1.1 mrg text_range_label label1 ("label 1\xcf\x80"); 4224 1.1 mrg text_range_label label2 ("label 2\xcf\x80"); 4225 1.1 mrg gcc_rich_location richloc (foo, &label0); 4226 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); 4227 1.1 mrg richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2); 4228 1.1 mrg 4229 1.1 mrg test_diagnostic_context dc; 4230 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4231 1.1 mrg 4232 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 4233 1.1 mrg "_foo = \xcf\x80" 4234 1.1 mrg "_bar.\xf0\x9f\x98\x82" 4235 1.1 mrg "_field\xcf\x80" 4236 1.1 mrg ";\n" 4237 1.1 mrg " ^~~~~~ ~~~~~ ~~~~~~~~~\n" 4238 1.1 mrg " | | |\n" 4239 1.1 mrg " | | label 2\xcf\x80\n" 4240 1.1 mrg " | label 1\xcf\x80\n" 4241 1.1 mrg " label 0\xf0\x9f\x98\x82\n", 4242 1.1 mrg pp_formatted_text (dc.printer)); 4243 1.1 mrg } 4244 1.1 mrg 4245 1.1 mrg /* Example of boundary conditions: label 0 and 1 have just enough clearance, 4246 1.1 mrg but label 1 just touches label 2. */ 4247 1.1 mrg { 4248 1.1 mrg text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80"); 4249 1.1 mrg text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82"); 4250 1.1 mrg text_range_label label2 ("c"); 4251 1.1 mrg gcc_rich_location richloc (foo, &label0); 4252 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); 4253 1.1 mrg richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2); 4254 1.1 mrg 4255 1.1 mrg test_diagnostic_context dc; 4256 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4257 1.1 mrg ASSERT_STREQ (" \xf0\x9f\x98\x82" 4258 1.1 mrg "_foo = \xcf\x80" 4259 1.1 mrg "_bar.\xf0\x9f\x98\x82" 4260 1.1 mrg "_field\xcf\x80" 4261 1.1 mrg ";\n" 4262 1.1 mrg " ^~~~~~ ~~~~~ ~~~~~~~~~\n" 4263 1.1 mrg " | | |\n" 4264 1.1 mrg " | | c\n" 4265 1.1 mrg " aaaaa\xf0\x9f\x98\x82\xcf\x80" 4266 1.1 mrg " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n", 4267 1.1 mrg pp_formatted_text (dc.printer)); 4268 1.1 mrg } 4269 1.1 mrg 4270 1.1 mrg /* Example of escaping the source lines. */ 4271 1.1 mrg { 4272 1.1 mrg text_range_label label0 ("label 0\xf0\x9f\x98\x82"); 4273 1.1 mrg text_range_label label1 ("label 1\xcf\x80"); 4274 1.1 mrg text_range_label label2 ("label 2\xcf\x80"); 4275 1.1 mrg gcc_rich_location richloc (foo, &label0); 4276 1.1 mrg richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); 4277 1.1 mrg richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2); 4278 1.1 mrg richloc.set_escape_on_output (true); 4279 1.1 mrg 4280 1.1 mrg { 4281 1.1 mrg test_diagnostic_context dc; 4282 1.1 mrg dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE; 4283 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4284 1.1 mrg ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n" 4285 1.1 mrg " ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n" 4286 1.1 mrg " | | |\n" 4287 1.1 mrg " | | label 2\xcf\x80\n" 4288 1.1 mrg " | label 1\xcf\x80\n" 4289 1.1 mrg " label 0\xf0\x9f\x98\x82\n", 4290 1.1 mrg pp_formatted_text (dc.printer)); 4291 1.1 mrg } 4292 1.1 mrg { 4293 1.1 mrg test_diagnostic_context dc; 4294 1.1 mrg dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES; 4295 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4296 1.1 mrg ASSERT_STREQ 4297 1.1 mrg (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n" 4298 1.1 mrg " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" 4299 1.1 mrg " | | |\n" 4300 1.1 mrg " | | label 2\xcf\x80\n" 4301 1.1 mrg " | label 1\xcf\x80\n" 4302 1.1 mrg " label 0\xf0\x9f\x98\x82\n", 4303 1.1 mrg pp_formatted_text (dc.printer)); 4304 1.1 mrg } 4305 1.1 mrg } 4306 1.1 mrg } 4307 1.1 mrg 4308 1.1 mrg /* Make sure that colorization codes don't interrupt a multibyte 4309 1.1 mrg sequence, which would corrupt it. */ 4310 1.1 mrg static void 4311 1.1 mrg test_one_liner_colorized_utf8 () 4312 1.1 mrg { 4313 1.1 mrg test_diagnostic_context dc; 4314 1.1 mrg dc.colorize_source_p = true; 4315 1.1 mrg diagnostic_color_init (&dc, DIAGNOSTICS_COLOR_YES); 4316 1.1 mrg const location_t pi = linemap_position_for_column (line_table, 12); 4317 1.1 mrg rich_location richloc (line_table, pi); 4318 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4319 1.1 mrg 4320 1.1 mrg /* In order to avoid having the test depend on exactly how the colorization 4321 1.1 mrg was effected, just confirm there are two pi characters in the output. */ 4322 1.1 mrg const char *result = pp_formatted_text (dc.printer); 4323 1.1 mrg const char *null_term = result + strlen (result); 4324 1.1 mrg const char *first_pi = strstr (result, "\xcf\x80"); 4325 1.1 mrg ASSERT_TRUE (first_pi && first_pi <= null_term - 2); 4326 1.1 mrg ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80"); 4327 1.1 mrg } 4328 1.1 mrg 4329 1.1 mrg /* Run the various one-liner tests. */ 4330 1.1 mrg 4331 1.1 mrg static void 4332 1.1 mrg test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_) 4333 1.1 mrg { 4334 1.1 mrg /* Create a tempfile and write some text to it. */ 4335 1.1 mrg const char *content 4336 1.1 mrg /* Display columns. 4337 1.1 mrg 0000000000000000000000011111111111111111111111111111112222222222222 4338 1.1 mrg 1111111122222222345678900000000123456666666677777777890123444444445 */ 4339 1.1 mrg = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n"; 4340 1.1 mrg /* 0000000000000000000001111111111111111111222222222222222222222233333 4341 1.1 mrg 1111222233334444567890122223333456789999000011112222345678999900001 4342 1.1 mrg Byte columns. */ 4343 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", content); 4344 1.1 mrg line_table_test ltt (case_); 4345 1.1 mrg 4346 1.1 mrg linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1); 4347 1.1 mrg 4348 1.1 mrg location_t line_end = linemap_position_for_column (line_table, 31); 4349 1.1 mrg 4350 1.1 mrg /* Don't attempt to run the tests if column data might be unavailable. */ 4351 1.1 mrg if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 4352 1.1 mrg return; 4353 1.1 mrg 4354 1.1 mrg ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end)); 4355 1.1 mrg ASSERT_EQ (1, LOCATION_LINE (line_end)); 4356 1.1 mrg ASSERT_EQ (31, LOCATION_COLUMN (line_end)); 4357 1.1 mrg 4358 1.1 mrg char_span lspan = location_get_source_line (tmp.get_filename (), 1); 4359 1.1 mrg ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (), 4360 1.1 mrg def_policy ())); 4361 1.1 mrg ASSERT_EQ (25, location_compute_display_column (expand_location (line_end), 4362 1.1 mrg def_policy ())); 4363 1.1 mrg 4364 1.1 mrg test_one_liner_simple_caret_utf8 (); 4365 1.1 mrg test_one_liner_caret_and_range_utf8 (); 4366 1.1 mrg test_one_liner_multiple_carets_and_ranges_utf8 (); 4367 1.1 mrg test_one_liner_fixit_insert_before_utf8 (); 4368 1.1 mrg test_one_liner_fixit_insert_after_utf8 (); 4369 1.1 mrg test_one_liner_fixit_remove_utf8 (); 4370 1.1 mrg test_one_liner_fixit_replace_utf8 (); 4371 1.1 mrg test_one_liner_fixit_replace_non_equal_range_utf8 (); 4372 1.1 mrg test_one_liner_fixit_replace_equal_secondary_range_utf8 (); 4373 1.1 mrg test_one_liner_fixit_validation_adhoc_locations_utf8 (); 4374 1.1 mrg test_one_liner_many_fixits_1_utf8 (); 4375 1.1 mrg test_one_liner_many_fixits_2_utf8 (); 4376 1.1 mrg test_one_liner_labels_utf8 (); 4377 1.1 mrg test_one_liner_colorized_utf8 (); 4378 1.1 mrg } 4379 1.1 mrg 4380 1.1 mrg /* Verify that gcc_rich_location::add_location_if_nearby works. */ 4381 1.1 mrg 4382 1.1 mrg static void 4383 1.1 mrg test_add_location_if_nearby (const line_table_case &case_) 4384 1.1 mrg { 4385 1.1 mrg /* Create a tempfile and write some text to it. 4386 1.1 mrg ...000000000111111111122222222223333333333. 4387 1.1 mrg ...123456789012345678901234567890123456789. */ 4388 1.1 mrg const char *content 4389 1.1 mrg = ("struct same_line { double x; double y; ;\n" /* line 1. */ 4390 1.1 mrg "struct different_line\n" /* line 2. */ 4391 1.1 mrg "{\n" /* line 3. */ 4392 1.1 mrg " double x;\n" /* line 4. */ 4393 1.1 mrg " double y;\n" /* line 5. */ 4394 1.1 mrg ";\n"); /* line 6. */ 4395 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", content); 4396 1.1 mrg line_table_test ltt (case_); 4397 1.1 mrg 4398 1.1 mrg const line_map_ordinary *ord_map 4399 1.1 mrg = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false, 4400 1.1 mrg tmp.get_filename (), 0)); 4401 1.1 mrg 4402 1.1 mrg linemap_line_start (line_table, 1, 100); 4403 1.1 mrg 4404 1.1 mrg const location_t final_line_end 4405 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 6, 7); 4406 1.1 mrg 4407 1.1 mrg /* Don't attempt to run the tests if column data might be unavailable. */ 4408 1.1 mrg if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 4409 1.1 mrg return; 4410 1.1 mrg 4411 1.1 mrg /* Test of add_location_if_nearby on the same line as the 4412 1.1 mrg primary location. */ 4413 1.1 mrg { 4414 1.1 mrg const location_t missing_close_brace_1_39 4415 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 39); 4416 1.1 mrg const location_t matching_open_brace_1_18 4417 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 18); 4418 1.1 mrg gcc_rich_location richloc (missing_close_brace_1_39); 4419 1.1 mrg bool added = richloc.add_location_if_nearby (matching_open_brace_1_18); 4420 1.1 mrg ASSERT_TRUE (added); 4421 1.1 mrg ASSERT_EQ (2, richloc.get_num_locations ()); 4422 1.1 mrg test_diagnostic_context dc; 4423 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4424 1.1 mrg ASSERT_STREQ (" struct same_line { double x; double y; ;\n" 4425 1.1 mrg " ~ ^\n", 4426 1.1 mrg pp_formatted_text (dc.printer)); 4427 1.1 mrg } 4428 1.1 mrg 4429 1.1 mrg /* Test of add_location_if_nearby on a different line to the 4430 1.1 mrg primary location. */ 4431 1.1 mrg { 4432 1.1 mrg const location_t missing_close_brace_6_1 4433 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 6, 1); 4434 1.1 mrg const location_t matching_open_brace_3_1 4435 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 3, 1); 4436 1.1 mrg gcc_rich_location richloc (missing_close_brace_6_1); 4437 1.1 mrg bool added = richloc.add_location_if_nearby (matching_open_brace_3_1); 4438 1.1 mrg ASSERT_FALSE (added); 4439 1.1 mrg ASSERT_EQ (1, richloc.get_num_locations ()); 4440 1.1 mrg } 4441 1.1 mrg } 4442 1.1 mrg 4443 1.1 mrg /* Verify that we print fixits even if they only affect lines 4444 1.1 mrg outside those covered by the ranges in the rich_location. */ 4445 1.1 mrg 4446 1.1 mrg static void 4447 1.1 mrg test_diagnostic_show_locus_fixit_lines (const line_table_case &case_) 4448 1.1 mrg { 4449 1.1 mrg /* Create a tempfile and write some text to it. 4450 1.1 mrg ...000000000111111111122222222223333333333. 4451 1.1 mrg ...123456789012345678901234567890123456789. */ 4452 1.1 mrg const char *content 4453 1.1 mrg = ("struct point { double x; double y; };\n" /* line 1. */ 4454 1.1 mrg "struct point origin = {x: 0.0,\n" /* line 2. */ 4455 1.1 mrg " y\n" /* line 3. */ 4456 1.1 mrg "\n" /* line 4. */ 4457 1.1 mrg "\n" /* line 5. */ 4458 1.1 mrg " : 0.0};\n"); /* line 6. */ 4459 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", content); 4460 1.1 mrg line_table_test ltt (case_); 4461 1.1 mrg 4462 1.1 mrg const line_map_ordinary *ord_map 4463 1.1 mrg = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false, 4464 1.1 mrg tmp.get_filename (), 0)); 4465 1.1 mrg 4466 1.1 mrg linemap_line_start (line_table, 1, 100); 4467 1.1 mrg 4468 1.1 mrg const location_t final_line_end 4469 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 6, 36); 4470 1.1 mrg 4471 1.1 mrg /* Don't attempt to run the tests if column data might be unavailable. */ 4472 1.1 mrg if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 4473 1.1 mrg return; 4474 1.1 mrg 4475 1.1 mrg /* A pair of tests for modernizing the initializers to C99-style. */ 4476 1.1 mrg 4477 1.1 mrg /* The one-liner case (line 2). */ 4478 1.1 mrg { 4479 1.1 mrg test_diagnostic_context dc; 4480 1.1 mrg const location_t x 4481 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 2, 24); 4482 1.1 mrg const location_t colon 4483 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 2, 25); 4484 1.1 mrg rich_location richloc (line_table, colon); 4485 1.1 mrg richloc.add_fixit_insert_before (x, "."); 4486 1.1 mrg richloc.add_fixit_replace (colon, "="); 4487 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4488 1.1 mrg ASSERT_STREQ (" struct point origin = {x: 0.0,\n" 4489 1.1 mrg " ^\n" 4490 1.1 mrg " .=\n", 4491 1.1 mrg pp_formatted_text (dc.printer)); 4492 1.1 mrg } 4493 1.1 mrg 4494 1.1 mrg /* The multiline case. The caret for the rich_location is on line 6; 4495 1.1 mrg verify that insertion fixit on line 3 is still printed (and that 4496 1.1 mrg span starts are printed due to the gap between the span at line 3 4497 1.1 mrg and that at line 6). */ 4498 1.1 mrg { 4499 1.1 mrg test_diagnostic_context dc; 4500 1.1 mrg const location_t y 4501 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 3, 24); 4502 1.1 mrg const location_t colon 4503 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 6, 25); 4504 1.1 mrg rich_location richloc (line_table, colon); 4505 1.1 mrg richloc.add_fixit_insert_before (y, "."); 4506 1.1 mrg richloc.add_fixit_replace (colon, "="); 4507 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4508 1.1 mrg ASSERT_STREQ ("FILENAME:3:24:\n" 4509 1.1 mrg " y\n" 4510 1.1 mrg " .\n" 4511 1.1 mrg "FILENAME:6:25:\n" 4512 1.1 mrg " : 0.0};\n" 4513 1.1 mrg " ^\n" 4514 1.1 mrg " =\n", 4515 1.1 mrg pp_formatted_text (dc.printer)); 4516 1.1 mrg } 4517 1.1 mrg 4518 1.1 mrg /* As above, but verify the behavior of multiple line spans 4519 1.1 mrg with line-numbering enabled. */ 4520 1.1 mrg { 4521 1.1 mrg const location_t y 4522 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 3, 24); 4523 1.1 mrg const location_t colon 4524 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 6, 25); 4525 1.1 mrg rich_location richloc (line_table, colon); 4526 1.1 mrg richloc.add_fixit_insert_before (y, "."); 4527 1.1 mrg richloc.add_fixit_replace (colon, "="); 4528 1.1 mrg test_diagnostic_context dc; 4529 1.1 mrg dc.show_line_numbers_p = true; 4530 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4531 1.1 mrg ASSERT_STREQ (" 3 | y\n" 4532 1.1 mrg " | .\n" 4533 1.1 mrg "......\n" 4534 1.1 mrg " 6 | : 0.0};\n" 4535 1.1 mrg " | ^\n" 4536 1.1 mrg " | =\n", 4537 1.1 mrg pp_formatted_text (dc.printer)); 4538 1.1 mrg } 4539 1.1 mrg } 4540 1.1 mrg 4541 1.1 mrg 4542 1.1 mrg /* Verify that fix-it hints are appropriately consolidated. 4543 1.1 mrg 4544 1.1 mrg If any fix-it hints in a rich_location involve locations beyond 4545 1.1 mrg LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply 4546 1.1 mrg the fix-it as a whole, so there should be none. 4547 1.1 mrg 4548 1.1 mrg Otherwise, verify that consecutive "replace" and "remove" fix-its 4549 1.1 mrg are merged, and that other fix-its remain separate. */ 4550 1.1 mrg 4551 1.1 mrg static void 4552 1.1 mrg test_fixit_consolidation (const line_table_case &case_) 4553 1.1 mrg { 4554 1.1 mrg line_table_test ltt (case_); 4555 1.1 mrg 4556 1.1 mrg linemap_add (line_table, LC_ENTER, false, "test.c", 1); 4557 1.1 mrg 4558 1.1 mrg const location_t c10 = linemap_position_for_column (line_table, 10); 4559 1.1 mrg const location_t c15 = linemap_position_for_column (line_table, 15); 4560 1.1 mrg const location_t c16 = linemap_position_for_column (line_table, 16); 4561 1.1 mrg const location_t c17 = linemap_position_for_column (line_table, 17); 4562 1.1 mrg const location_t c20 = linemap_position_for_column (line_table, 20); 4563 1.1 mrg const location_t c21 = linemap_position_for_column (line_table, 21); 4564 1.1 mrg const location_t caret = c10; 4565 1.1 mrg 4566 1.1 mrg /* Insert + insert. */ 4567 1.1 mrg { 4568 1.1 mrg rich_location richloc (line_table, caret); 4569 1.1 mrg richloc.add_fixit_insert_before (c10, "foo"); 4570 1.1 mrg richloc.add_fixit_insert_before (c15, "bar"); 4571 1.1 mrg 4572 1.1 mrg if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS) 4573 1.1 mrg /* Bogus column info for 2nd fixit, so no fixits. */ 4574 1.1 mrg ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 4575 1.1 mrg else 4576 1.1 mrg /* They should not have been merged. */ 4577 1.1 mrg ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 4578 1.1 mrg } 4579 1.1 mrg 4580 1.1 mrg /* Insert + replace. */ 4581 1.1 mrg { 4582 1.1 mrg rich_location richloc (line_table, caret); 4583 1.1 mrg richloc.add_fixit_insert_before (c10, "foo"); 4584 1.1 mrg richloc.add_fixit_replace (source_range::from_locations (c15, c17), 4585 1.1 mrg "bar"); 4586 1.1 mrg 4587 1.1 mrg if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS) 4588 1.1 mrg /* Bogus column info for 2nd fixit, so no fixits. */ 4589 1.1 mrg ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 4590 1.1 mrg else 4591 1.1 mrg /* They should not have been merged. */ 4592 1.1 mrg ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 4593 1.1 mrg } 4594 1.1 mrg 4595 1.1 mrg /* Replace + non-consecutive insert. */ 4596 1.1 mrg { 4597 1.1 mrg rich_location richloc (line_table, caret); 4598 1.1 mrg richloc.add_fixit_replace (source_range::from_locations (c10, c15), 4599 1.1 mrg "bar"); 4600 1.1 mrg richloc.add_fixit_insert_before (c17, "foo"); 4601 1.1 mrg 4602 1.1 mrg if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS) 4603 1.1 mrg /* Bogus column info for 2nd fixit, so no fixits. */ 4604 1.1 mrg ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 4605 1.1 mrg else 4606 1.1 mrg /* They should not have been merged. */ 4607 1.1 mrg ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 4608 1.1 mrg } 4609 1.1 mrg 4610 1.1 mrg /* Replace + non-consecutive replace. */ 4611 1.1 mrg { 4612 1.1 mrg rich_location richloc (line_table, caret); 4613 1.1 mrg richloc.add_fixit_replace (source_range::from_locations (c10, c15), 4614 1.1 mrg "foo"); 4615 1.1 mrg richloc.add_fixit_replace (source_range::from_locations (c17, c20), 4616 1.1 mrg "bar"); 4617 1.1 mrg 4618 1.1 mrg if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS) 4619 1.1 mrg /* Bogus column info for 2nd fixit, so no fixits. */ 4620 1.1 mrg ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 4621 1.1 mrg else 4622 1.1 mrg /* They should not have been merged. */ 4623 1.1 mrg ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 4624 1.1 mrg } 4625 1.1 mrg 4626 1.1 mrg /* Replace + consecutive replace. */ 4627 1.1 mrg { 4628 1.1 mrg rich_location richloc (line_table, caret); 4629 1.1 mrg richloc.add_fixit_replace (source_range::from_locations (c10, c15), 4630 1.1 mrg "foo"); 4631 1.1 mrg richloc.add_fixit_replace (source_range::from_locations (c16, c20), 4632 1.1 mrg "bar"); 4633 1.1 mrg 4634 1.1 mrg if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS) 4635 1.1 mrg /* Bogus column info for 2nd fixit, so no fixits. */ 4636 1.1 mrg ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 4637 1.1 mrg else 4638 1.1 mrg { 4639 1.1 mrg /* They should have been merged into a single "replace". */ 4640 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 4641 1.1 mrg const fixit_hint *hint = richloc.get_fixit_hint (0); 4642 1.1 mrg ASSERT_STREQ ("foobar", hint->get_string ()); 4643 1.1 mrg ASSERT_EQ (c10, hint->get_start_loc ()); 4644 1.1 mrg ASSERT_EQ (c21, hint->get_next_loc ()); 4645 1.1 mrg } 4646 1.1 mrg } 4647 1.1 mrg 4648 1.1 mrg /* Replace + consecutive removal. */ 4649 1.1 mrg { 4650 1.1 mrg rich_location richloc (line_table, caret); 4651 1.1 mrg richloc.add_fixit_replace (source_range::from_locations (c10, c15), 4652 1.1 mrg "foo"); 4653 1.1 mrg richloc.add_fixit_remove (source_range::from_locations (c16, c20)); 4654 1.1 mrg 4655 1.1 mrg if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS) 4656 1.1 mrg /* Bogus column info for 2nd fixit, so no fixits. */ 4657 1.1 mrg ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 4658 1.1 mrg else 4659 1.1 mrg { 4660 1.1 mrg /* They should have been merged into a single replace, with the 4661 1.1 mrg range extended to cover that of the removal. */ 4662 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 4663 1.1 mrg const fixit_hint *hint = richloc.get_fixit_hint (0); 4664 1.1 mrg ASSERT_STREQ ("foo", hint->get_string ()); 4665 1.1 mrg ASSERT_EQ (c10, hint->get_start_loc ()); 4666 1.1 mrg ASSERT_EQ (c21, hint->get_next_loc ()); 4667 1.1 mrg } 4668 1.1 mrg } 4669 1.1 mrg 4670 1.1 mrg /* Consecutive removals. */ 4671 1.1 mrg { 4672 1.1 mrg rich_location richloc (line_table, caret); 4673 1.1 mrg richloc.add_fixit_remove (source_range::from_locations (c10, c15)); 4674 1.1 mrg richloc.add_fixit_remove (source_range::from_locations (c16, c20)); 4675 1.1 mrg 4676 1.1 mrg if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS) 4677 1.1 mrg /* Bogus column info for 2nd fixit, so no fixits. */ 4678 1.1 mrg ASSERT_EQ (0, richloc.get_num_fixit_hints ()); 4679 1.1 mrg else 4680 1.1 mrg { 4681 1.1 mrg /* They should have been merged into a single "replace-with-empty". */ 4682 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 4683 1.1 mrg const fixit_hint *hint = richloc.get_fixit_hint (0); 4684 1.1 mrg ASSERT_STREQ ("", hint->get_string ()); 4685 1.1 mrg ASSERT_EQ (c10, hint->get_start_loc ()); 4686 1.1 mrg ASSERT_EQ (c21, hint->get_next_loc ()); 4687 1.1 mrg } 4688 1.1 mrg } 4689 1.1 mrg } 4690 1.1 mrg 4691 1.1 mrg /* Verify that the line_corrections machinery correctly prints 4692 1.1 mrg overlapping fixit-hints. */ 4693 1.1 mrg 4694 1.1 mrg static void 4695 1.1 mrg test_overlapped_fixit_printing (const line_table_case &case_) 4696 1.1 mrg { 4697 1.1 mrg /* Create a tempfile and write some text to it. 4698 1.1 mrg ...000000000111111111122222222223333333333. 4699 1.1 mrg ...123456789012345678901234567890123456789. */ 4700 1.1 mrg const char *content 4701 1.1 mrg = (" foo *f = (foo *)ptr->field;\n"); 4702 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".C", content); 4703 1.1 mrg line_table_test ltt (case_); 4704 1.1 mrg 4705 1.1 mrg const line_map_ordinary *ord_map 4706 1.1 mrg = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false, 4707 1.1 mrg tmp.get_filename (), 0)); 4708 1.1 mrg 4709 1.1 mrg linemap_line_start (line_table, 1, 100); 4710 1.1 mrg 4711 1.1 mrg const location_t final_line_end 4712 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 6, 36); 4713 1.1 mrg 4714 1.1 mrg /* Don't attempt to run the tests if column data might be unavailable. */ 4715 1.1 mrg if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 4716 1.1 mrg return; 4717 1.1 mrg 4718 1.1 mrg /* A test for converting a C-style cast to a C++-style cast. */ 4719 1.1 mrg const location_t open_paren 4720 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 12); 4721 1.1 mrg const location_t close_paren 4722 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 18); 4723 1.1 mrg const location_t expr_start 4724 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 19); 4725 1.1 mrg const location_t expr_finish 4726 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 28); 4727 1.1 mrg const location_t expr = make_location (expr_start, expr_start, expr_finish); 4728 1.1 mrg 4729 1.1 mrg /* Various examples of fix-it hints that aren't themselves consolidated, 4730 1.1 mrg but for which the *printing* may need consolidation. */ 4731 1.1 mrg 4732 1.1 mrg /* Example where 3 fix-it hints are printed as one. */ 4733 1.1 mrg { 4734 1.1 mrg test_diagnostic_context dc; 4735 1.1 mrg rich_location richloc (line_table, expr); 4736 1.1 mrg richloc.add_fixit_replace (open_paren, "const_cast<"); 4737 1.1 mrg richloc.add_fixit_replace (close_paren, "> ("); 4738 1.1 mrg richloc.add_fixit_insert_after (")"); 4739 1.1 mrg 4740 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4741 1.1 mrg ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n" 4742 1.1 mrg " ^~~~~~~~~~\n" 4743 1.1 mrg " -----------------\n" 4744 1.1 mrg " const_cast<foo *> (ptr->field)\n", 4745 1.1 mrg pp_formatted_text (dc.printer)); 4746 1.1 mrg 4747 1.1 mrg /* Unit-test the line_corrections machinery. */ 4748 1.1 mrg char_display_policy policy (make_policy (dc, richloc)); 4749 1.1 mrg ASSERT_EQ (3, richloc.get_num_fixit_hints ()); 4750 1.1 mrg const fixit_hint *hint_0 = richloc.get_fixit_hint (0); 4751 1.1 mrg ASSERT_EQ (column_range (12, 12), 4752 1.1 mrg get_affected_range (policy, hint_0, CU_BYTES)); 4753 1.1 mrg ASSERT_EQ (column_range (12, 12), 4754 1.1 mrg get_affected_range (policy, hint_0, CU_DISPLAY_COLS)); 4755 1.1 mrg ASSERT_EQ (column_range (12, 22), get_printed_columns (policy, hint_0)); 4756 1.1 mrg const fixit_hint *hint_1 = richloc.get_fixit_hint (1); 4757 1.1 mrg ASSERT_EQ (column_range (18, 18), 4758 1.1 mrg get_affected_range (policy, hint_1, CU_BYTES)); 4759 1.1 mrg ASSERT_EQ (column_range (18, 18), 4760 1.1 mrg get_affected_range (policy, hint_1, CU_DISPLAY_COLS)); 4761 1.1 mrg ASSERT_EQ (column_range (18, 20), get_printed_columns (policy, hint_1)); 4762 1.1 mrg const fixit_hint *hint_2 = richloc.get_fixit_hint (2); 4763 1.1 mrg ASSERT_EQ (column_range (29, 28), 4764 1.1 mrg get_affected_range (policy, hint_2, CU_BYTES)); 4765 1.1 mrg ASSERT_EQ (column_range (29, 28), 4766 1.1 mrg get_affected_range (policy, hint_2, CU_DISPLAY_COLS)); 4767 1.1 mrg ASSERT_EQ (column_range (29, 29), get_printed_columns (policy, hint_2)); 4768 1.1 mrg 4769 1.1 mrg /* Add each hint in turn to a line_corrections instance, 4770 1.1 mrg and verify that they are consolidated into one correction instance 4771 1.1 mrg as expected. */ 4772 1.1 mrg line_corrections lc (policy, tmp.get_filename (), 1); 4773 1.1 mrg 4774 1.1 mrg /* The first replace hint by itself. */ 4775 1.1 mrg lc.add_hint (hint_0); 4776 1.1 mrg ASSERT_EQ (1, lc.m_corrections.length ()); 4777 1.1 mrg ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes); 4778 1.1 mrg ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns); 4779 1.1 mrg ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns); 4780 1.1 mrg ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text); 4781 1.1 mrg 4782 1.1 mrg /* After the second replacement hint, they are printed together 4783 1.1 mrg as a replacement (along with the text between them). */ 4784 1.1 mrg lc.add_hint (hint_1); 4785 1.1 mrg ASSERT_EQ (1, lc.m_corrections.length ()); 4786 1.1 mrg ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text); 4787 1.1 mrg ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes); 4788 1.1 mrg ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns); 4789 1.1 mrg ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns); 4790 1.1 mrg 4791 1.1 mrg /* After the final insertion hint, they are all printed together 4792 1.1 mrg as a replacement (along with the text between them). */ 4793 1.1 mrg lc.add_hint (hint_2); 4794 1.1 mrg ASSERT_STREQ ("const_cast<foo *> (ptr->field)", 4795 1.1 mrg lc.m_corrections[0]->m_text); 4796 1.1 mrg ASSERT_EQ (1, lc.m_corrections.length ()); 4797 1.1 mrg ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes); 4798 1.1 mrg ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns); 4799 1.1 mrg ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns); 4800 1.1 mrg } 4801 1.1 mrg 4802 1.1 mrg /* Example where two are consolidated during printing. */ 4803 1.1 mrg { 4804 1.1 mrg test_diagnostic_context dc; 4805 1.1 mrg rich_location richloc (line_table, expr); 4806 1.1 mrg richloc.add_fixit_replace (open_paren, "CAST ("); 4807 1.1 mrg richloc.add_fixit_replace (close_paren, ") ("); 4808 1.1 mrg richloc.add_fixit_insert_after (")"); 4809 1.1 mrg 4810 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4811 1.1 mrg ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n" 4812 1.1 mrg " ^~~~~~~~~~\n" 4813 1.1 mrg " -\n" 4814 1.1 mrg " CAST (-\n" 4815 1.1 mrg " ) ( )\n", 4816 1.1 mrg pp_formatted_text (dc.printer)); 4817 1.1 mrg } 4818 1.1 mrg 4819 1.1 mrg /* Example where none are consolidated during printing. */ 4820 1.1 mrg { 4821 1.1 mrg test_diagnostic_context dc; 4822 1.1 mrg rich_location richloc (line_table, expr); 4823 1.1 mrg richloc.add_fixit_replace (open_paren, "CST ("); 4824 1.1 mrg richloc.add_fixit_replace (close_paren, ") ("); 4825 1.1 mrg richloc.add_fixit_insert_after (")"); 4826 1.1 mrg 4827 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4828 1.1 mrg ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n" 4829 1.1 mrg " ^~~~~~~~~~\n" 4830 1.1 mrg " -\n" 4831 1.1 mrg " CST ( -\n" 4832 1.1 mrg " ) ( )\n", 4833 1.1 mrg pp_formatted_text (dc.printer)); 4834 1.1 mrg } 4835 1.1 mrg 4836 1.1 mrg /* Example of deletion fix-it hints. */ 4837 1.1 mrg { 4838 1.1 mrg test_diagnostic_context dc; 4839 1.1 mrg rich_location richloc (line_table, expr); 4840 1.1 mrg richloc.add_fixit_insert_before (open_paren, "(bar *)"); 4841 1.1 mrg source_range victim = {open_paren, close_paren}; 4842 1.1 mrg richloc.add_fixit_remove (victim); 4843 1.1 mrg 4844 1.1 mrg /* This case is actually handled by fixit-consolidation, 4845 1.1 mrg rather than by line_corrections. */ 4846 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 4847 1.1 mrg 4848 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4849 1.1 mrg ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n" 4850 1.1 mrg " ^~~~~~~~~~\n" 4851 1.1 mrg " -------\n" 4852 1.1 mrg " (bar *)\n", 4853 1.1 mrg pp_formatted_text (dc.printer)); 4854 1.1 mrg } 4855 1.1 mrg 4856 1.1 mrg /* Example of deletion fix-it hints that would overlap. */ 4857 1.1 mrg { 4858 1.1 mrg test_diagnostic_context dc; 4859 1.1 mrg rich_location richloc (line_table, expr); 4860 1.1 mrg richloc.add_fixit_insert_before (open_paren, "(longer *)"); 4861 1.1 mrg source_range victim = {expr_start, expr_finish}; 4862 1.1 mrg richloc.add_fixit_remove (victim); 4863 1.1 mrg 4864 1.1 mrg /* These fixits are not consolidated. */ 4865 1.1 mrg ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 4866 1.1 mrg 4867 1.1 mrg /* But the corrections are. */ 4868 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4869 1.1 mrg ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n" 4870 1.1 mrg " ^~~~~~~~~~\n" 4871 1.1 mrg " -----------------\n" 4872 1.1 mrg " (longer *)(foo *)\n", 4873 1.1 mrg pp_formatted_text (dc.printer)); 4874 1.1 mrg } 4875 1.1 mrg 4876 1.1 mrg /* Example of insertion fix-it hints that would overlap. */ 4877 1.1 mrg { 4878 1.1 mrg test_diagnostic_context dc; 4879 1.1 mrg rich_location richloc (line_table, expr); 4880 1.1 mrg richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST"); 4881 1.1 mrg richloc.add_fixit_insert_after (close_paren, "TEST"); 4882 1.1 mrg 4883 1.1 mrg /* The first insertion is long enough that if printed naively, 4884 1.1 mrg it would overlap with the second. 4885 1.1 mrg Verify that they are printed as a single replacement. */ 4886 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4887 1.1 mrg ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n" 4888 1.1 mrg " ^~~~~~~~~~\n" 4889 1.1 mrg " -------\n" 4890 1.1 mrg " LONGER THAN THE CAST(foo *)TEST\n", 4891 1.1 mrg pp_formatted_text (dc.printer)); 4892 1.1 mrg } 4893 1.1 mrg } 4894 1.1 mrg 4895 1.1 mrg /* Multibyte-aware version of preceding tests. See comments above 4896 1.1 mrg test_one_liner_simple_caret_utf8() too, we use the same two multibyte 4897 1.1 mrg characters here. */ 4898 1.1 mrg 4899 1.1 mrg static void 4900 1.1 mrg test_overlapped_fixit_printing_utf8 (const line_table_case &case_) 4901 1.1 mrg { 4902 1.1 mrg /* Create a tempfile and write some text to it. */ 4903 1.1 mrg 4904 1.1 mrg const char *content 4905 1.1 mrg /* Display columns. 4906 1.1 mrg 00000000000000000000000111111111111111111111111222222222222222223 4907 1.1 mrg 12344444444555555556789012344444444555555556789012345678999999990 */ 4908 1.1 mrg = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n"; 4909 1.1 mrg /* 00000000000000000000011111111111111111111112222222222333333333333 4910 1.1 mrg 12344445555666677778901234566667777888899990123456789012333344445 4911 1.1 mrg Byte columns. */ 4912 1.1 mrg 4913 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".C", content); 4914 1.1 mrg line_table_test ltt (case_); 4915 1.1 mrg 4916 1.1 mrg const line_map_ordinary *ord_map 4917 1.1 mrg = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false, 4918 1.1 mrg tmp.get_filename (), 0)); 4919 1.1 mrg 4920 1.1 mrg linemap_line_start (line_table, 1, 100); 4921 1.1 mrg 4922 1.1 mrg const location_t final_line_end 4923 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 6, 50); 4924 1.1 mrg 4925 1.1 mrg /* Don't attempt to run the tests if column data might be unavailable. */ 4926 1.1 mrg if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 4927 1.1 mrg return; 4928 1.1 mrg 4929 1.1 mrg /* A test for converting a C-style cast to a C++-style cast. */ 4930 1.1 mrg const location_t open_paren 4931 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 14); 4932 1.1 mrg const location_t close_paren 4933 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 22); 4934 1.1 mrg const location_t expr_start 4935 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 23); 4936 1.1 mrg const location_t expr_finish 4937 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 34); 4938 1.1 mrg const location_t expr = make_location (expr_start, expr_start, expr_finish); 4939 1.1 mrg 4940 1.1 mrg /* Various examples of fix-it hints that aren't themselves consolidated, 4941 1.1 mrg but for which the *printing* may need consolidation. */ 4942 1.1 mrg 4943 1.1 mrg /* Example where 3 fix-it hints are printed as one. */ 4944 1.1 mrg { 4945 1.1 mrg test_diagnostic_context dc; 4946 1.1 mrg rich_location richloc (line_table, expr); 4947 1.1 mrg richloc.add_fixit_replace (open_paren, "const_cast<"); 4948 1.1 mrg richloc.add_fixit_replace (close_paren, "> ("); 4949 1.1 mrg richloc.add_fixit_insert_after (")"); 4950 1.1 mrg 4951 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 4952 1.1 mrg ASSERT_STREQ (" f\xf0\x9f\x98\x82" 4953 1.1 mrg " *f = (f\xf0\x9f\x98\x82" 4954 1.1 mrg " *)ptr->field\xcf\x80" 4955 1.1 mrg ";\n" 4956 1.1 mrg " ^~~~~~~~~~~\n" 4957 1.1 mrg " ------------------\n" 4958 1.1 mrg " const_cast<f\xf0\x9f\x98\x82" 4959 1.1 mrg " *> (ptr->field\xcf\x80" 4960 1.1 mrg ")\n", 4961 1.1 mrg pp_formatted_text (dc.printer)); 4962 1.1 mrg 4963 1.1 mrg /* Unit-test the line_corrections machinery. */ 4964 1.1 mrg char_display_policy policy (make_policy (dc, richloc)); 4965 1.1 mrg ASSERT_EQ (3, richloc.get_num_fixit_hints ()); 4966 1.1 mrg const fixit_hint *hint_0 = richloc.get_fixit_hint (0); 4967 1.1 mrg ASSERT_EQ (column_range (14, 14), 4968 1.1 mrg get_affected_range (policy, hint_0, CU_BYTES)); 4969 1.1 mrg ASSERT_EQ (column_range (12, 12), 4970 1.1 mrg get_affected_range (policy, hint_0, CU_DISPLAY_COLS)); 4971 1.1 mrg ASSERT_EQ (column_range (12, 22), get_printed_columns (policy, hint_0)); 4972 1.1 mrg const fixit_hint *hint_1 = richloc.get_fixit_hint (1); 4973 1.1 mrg ASSERT_EQ (column_range (22, 22), 4974 1.1 mrg get_affected_range (policy, hint_1, CU_BYTES)); 4975 1.1 mrg ASSERT_EQ (column_range (18, 18), 4976 1.1 mrg get_affected_range (policy, hint_1, CU_DISPLAY_COLS)); 4977 1.1 mrg ASSERT_EQ (column_range (18, 20), get_printed_columns (policy, hint_1)); 4978 1.1 mrg const fixit_hint *hint_2 = richloc.get_fixit_hint (2); 4979 1.1 mrg ASSERT_EQ (column_range (35, 34), 4980 1.1 mrg get_affected_range (policy, hint_2, CU_BYTES)); 4981 1.1 mrg ASSERT_EQ (column_range (30, 29), 4982 1.1 mrg get_affected_range (policy, hint_2, CU_DISPLAY_COLS)); 4983 1.1 mrg ASSERT_EQ (column_range (30, 30), get_printed_columns (policy, hint_2)); 4984 1.1 mrg 4985 1.1 mrg /* Add each hint in turn to a line_corrections instance, 4986 1.1 mrg and verify that they are consolidated into one correction instance 4987 1.1 mrg as expected. */ 4988 1.1 mrg line_corrections lc (policy, tmp.get_filename (), 1); 4989 1.1 mrg 4990 1.1 mrg /* The first replace hint by itself. */ 4991 1.1 mrg lc.add_hint (hint_0); 4992 1.1 mrg ASSERT_EQ (1, lc.m_corrections.length ()); 4993 1.1 mrg ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes); 4994 1.1 mrg ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns); 4995 1.1 mrg ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns); 4996 1.1 mrg ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text); 4997 1.1 mrg 4998 1.1 mrg /* After the second replacement hint, they are printed together 4999 1.1 mrg as a replacement (along with the text between them). */ 5000 1.1 mrg lc.add_hint (hint_1); 5001 1.1 mrg ASSERT_EQ (1, lc.m_corrections.length ()); 5002 1.1 mrg ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (", 5003 1.1 mrg lc.m_corrections[0]->m_text); 5004 1.1 mrg ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes); 5005 1.1 mrg ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns); 5006 1.1 mrg ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns); 5007 1.1 mrg 5008 1.1 mrg /* After the final insertion hint, they are all printed together 5009 1.1 mrg as a replacement (along with the text between them). */ 5010 1.1 mrg lc.add_hint (hint_2); 5011 1.1 mrg ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)", 5012 1.1 mrg lc.m_corrections[0]->m_text); 5013 1.1 mrg ASSERT_EQ (1, lc.m_corrections.length ()); 5014 1.1 mrg ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes); 5015 1.1 mrg ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns); 5016 1.1 mrg ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns); 5017 1.1 mrg } 5018 1.1 mrg 5019 1.1 mrg /* Example where two are consolidated during printing. */ 5020 1.1 mrg { 5021 1.1 mrg test_diagnostic_context dc; 5022 1.1 mrg rich_location richloc (line_table, expr); 5023 1.1 mrg richloc.add_fixit_replace (open_paren, "CAST ("); 5024 1.1 mrg richloc.add_fixit_replace (close_paren, ") ("); 5025 1.1 mrg richloc.add_fixit_insert_after (")"); 5026 1.1 mrg 5027 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5028 1.1 mrg ASSERT_STREQ (" f\xf0\x9f\x98\x82" 5029 1.1 mrg " *f = (f\xf0\x9f\x98\x82" 5030 1.1 mrg " *)ptr->field\xcf\x80" 5031 1.1 mrg ";\n" 5032 1.1 mrg " ^~~~~~~~~~~\n" 5033 1.1 mrg " -\n" 5034 1.1 mrg " CAST (-\n" 5035 1.1 mrg " ) ( )\n", 5036 1.1 mrg pp_formatted_text (dc.printer)); 5037 1.1 mrg } 5038 1.1 mrg 5039 1.1 mrg /* Example where none are consolidated during printing. */ 5040 1.1 mrg { 5041 1.1 mrg test_diagnostic_context dc; 5042 1.1 mrg rich_location richloc (line_table, expr); 5043 1.1 mrg richloc.add_fixit_replace (open_paren, "CST ("); 5044 1.1 mrg richloc.add_fixit_replace (close_paren, ") ("); 5045 1.1 mrg richloc.add_fixit_insert_after (")"); 5046 1.1 mrg 5047 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5048 1.1 mrg ASSERT_STREQ (" f\xf0\x9f\x98\x82" 5049 1.1 mrg " *f = (f\xf0\x9f\x98\x82" 5050 1.1 mrg " *)ptr->field\xcf\x80" 5051 1.1 mrg ";\n" 5052 1.1 mrg " ^~~~~~~~~~~\n" 5053 1.1 mrg " -\n" 5054 1.1 mrg " CST ( -\n" 5055 1.1 mrg " ) ( )\n", 5056 1.1 mrg pp_formatted_text (dc.printer)); 5057 1.1 mrg } 5058 1.1 mrg 5059 1.1 mrg /* Example of deletion fix-it hints. */ 5060 1.1 mrg { 5061 1.1 mrg test_diagnostic_context dc; 5062 1.1 mrg rich_location richloc (line_table, expr); 5063 1.1 mrg richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)"); 5064 1.1 mrg source_range victim = {open_paren, close_paren}; 5065 1.1 mrg richloc.add_fixit_remove (victim); 5066 1.1 mrg 5067 1.1 mrg /* This case is actually handled by fixit-consolidation, 5068 1.1 mrg rather than by line_corrections. */ 5069 1.1 mrg ASSERT_EQ (1, richloc.get_num_fixit_hints ()); 5070 1.1 mrg 5071 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5072 1.1 mrg ASSERT_STREQ (" f\xf0\x9f\x98\x82" 5073 1.1 mrg " *f = (f\xf0\x9f\x98\x82" 5074 1.1 mrg " *)ptr->field\xcf\x80" 5075 1.1 mrg ";\n" 5076 1.1 mrg " ^~~~~~~~~~~\n" 5077 1.1 mrg " -------\n" 5078 1.1 mrg " (bar\xf0\x9f\x98\x82" 5079 1.1 mrg " *)\n", 5080 1.1 mrg pp_formatted_text (dc.printer)); 5081 1.1 mrg } 5082 1.1 mrg 5083 1.1 mrg /* Example of deletion fix-it hints that would overlap. */ 5084 1.1 mrg { 5085 1.1 mrg test_diagnostic_context dc; 5086 1.1 mrg rich_location richloc (line_table, expr); 5087 1.1 mrg richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)"); 5088 1.1 mrg source_range victim = {expr_start, expr_finish}; 5089 1.1 mrg richloc.add_fixit_remove (victim); 5090 1.1 mrg 5091 1.1 mrg /* These fixits are not consolidated. */ 5092 1.1 mrg ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 5093 1.1 mrg 5094 1.1 mrg /* But the corrections are. */ 5095 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5096 1.1 mrg ASSERT_STREQ (" f\xf0\x9f\x98\x82" 5097 1.1 mrg " *f = (f\xf0\x9f\x98\x82" 5098 1.1 mrg " *)ptr->field\xcf\x80" 5099 1.1 mrg ";\n" 5100 1.1 mrg " ^~~~~~~~~~~\n" 5101 1.1 mrg " ------------------\n" 5102 1.1 mrg " (long\xf0\x9f\x98\x82" 5103 1.1 mrg " *)(f\xf0\x9f\x98\x82" 5104 1.1 mrg " *)\n", 5105 1.1 mrg pp_formatted_text (dc.printer)); 5106 1.1 mrg } 5107 1.1 mrg 5108 1.1 mrg /* Example of insertion fix-it hints that would overlap. */ 5109 1.1 mrg { 5110 1.1 mrg test_diagnostic_context dc; 5111 1.1 mrg rich_location richloc (line_table, expr); 5112 1.1 mrg richloc.add_fixit_insert_before 5113 1.1 mrg (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST"); 5114 1.1 mrg richloc.add_fixit_insert_after (close_paren, "TEST"); 5115 1.1 mrg 5116 1.1 mrg /* The first insertion is long enough that if printed naively, 5117 1.1 mrg it would overlap with the second. 5118 1.1 mrg Verify that they are printed as a single replacement. */ 5119 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5120 1.1 mrg ASSERT_STREQ (" f\xf0\x9f\x98\x82" 5121 1.1 mrg " *f = (f\xf0\x9f\x98\x82" 5122 1.1 mrg " *)ptr->field\xcf\x80" 5123 1.1 mrg ";\n" 5124 1.1 mrg " ^~~~~~~~~~~\n" 5125 1.1 mrg " -------\n" 5126 1.1 mrg " L\xf0\x9f\x98\x82" 5127 1.1 mrg "NGER THAN THE CAST(f\xf0\x9f\x98\x82" 5128 1.1 mrg " *)TEST\n", 5129 1.1 mrg pp_formatted_text (dc.printer)); 5130 1.1 mrg } 5131 1.1 mrg } 5132 1.1 mrg 5133 1.1 mrg /* Verify that the line_corrections machinery correctly prints 5134 1.1 mrg overlapping fixit-hints that have been added in the wrong 5135 1.1 mrg order. 5136 1.1 mrg Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/ 5137 1.1 mrg 5138 1.1 mrg static void 5139 1.1 mrg test_overlapped_fixit_printing_2 (const line_table_case &case_) 5140 1.1 mrg { 5141 1.1 mrg /* Create a tempfile and write some text to it. 5142 1.1 mrg ...000000000111111111122222222223333333333. 5143 1.1 mrg ...123456789012345678901234567890123456789. */ 5144 1.1 mrg const char *content 5145 1.1 mrg = ("int a5[][0][0] = { 1, 2 };\n"); 5146 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", content); 5147 1.1 mrg line_table_test ltt (case_); 5148 1.1 mrg 5149 1.1 mrg const line_map_ordinary *ord_map 5150 1.1 mrg = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false, 5151 1.1 mrg tmp.get_filename (), 0)); 5152 1.1 mrg 5153 1.1 mrg linemap_line_start (line_table, 1, 100); 5154 1.1 mrg 5155 1.1 mrg const location_t final_line_end 5156 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 100); 5157 1.1 mrg 5158 1.1 mrg /* Don't attempt to run the tests if column data might be unavailable. */ 5159 1.1 mrg if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 5160 1.1 mrg return; 5161 1.1 mrg 5162 1.1 mrg const location_t col_1 5163 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 1); 5164 1.1 mrg const location_t col_20 5165 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 20); 5166 1.1 mrg const location_t col_21 5167 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 21); 5168 1.1 mrg const location_t col_23 5169 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 23); 5170 1.1 mrg const location_t col_25 5171 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 25); 5172 1.1 mrg 5173 1.1 mrg /* Two insertions, in the wrong order. */ 5174 1.1 mrg { 5175 1.1 mrg test_diagnostic_context dc; 5176 1.1 mrg 5177 1.1 mrg rich_location richloc (line_table, col_20); 5178 1.1 mrg richloc.add_fixit_insert_before (col_23, "{"); 5179 1.1 mrg richloc.add_fixit_insert_before (col_21, "}"); 5180 1.1 mrg 5181 1.1 mrg /* These fixits should be accepted; they can't be consolidated. */ 5182 1.1 mrg char_display_policy policy (make_policy (dc, richloc)); 5183 1.1 mrg ASSERT_EQ (2, richloc.get_num_fixit_hints ()); 5184 1.1 mrg const fixit_hint *hint_0 = richloc.get_fixit_hint (0); 5185 1.1 mrg ASSERT_EQ (column_range (23, 22), 5186 1.1 mrg get_affected_range (policy, hint_0, CU_BYTES)); 5187 1.1 mrg ASSERT_EQ (column_range (23, 23), get_printed_columns (policy, hint_0)); 5188 1.1 mrg const fixit_hint *hint_1 = richloc.get_fixit_hint (1); 5189 1.1 mrg ASSERT_EQ (column_range (21, 20), 5190 1.1 mrg get_affected_range (policy, hint_1, CU_BYTES)); 5191 1.1 mrg ASSERT_EQ (column_range (21, 21), get_printed_columns (policy, hint_1)); 5192 1.1 mrg 5193 1.1 mrg /* Verify that they're printed correctly. */ 5194 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5195 1.1 mrg ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n" 5196 1.1 mrg " ^\n" 5197 1.1 mrg " } {\n", 5198 1.1 mrg pp_formatted_text (dc.printer)); 5199 1.1 mrg } 5200 1.1 mrg 5201 1.1 mrg /* Various overlapping insertions, some occurring "out of order" 5202 1.1 mrg (reproducing the fix-it hints from PR c/81405). */ 5203 1.1 mrg { 5204 1.1 mrg test_diagnostic_context dc; 5205 1.1 mrg rich_location richloc (line_table, col_20); 5206 1.1 mrg 5207 1.1 mrg richloc.add_fixit_insert_before (col_20, "{{"); 5208 1.1 mrg richloc.add_fixit_insert_before (col_21, "}}"); 5209 1.1 mrg richloc.add_fixit_insert_before (col_23, "{"); 5210 1.1 mrg richloc.add_fixit_insert_before (col_21, "}"); 5211 1.1 mrg richloc.add_fixit_insert_before (col_23, "{{"); 5212 1.1 mrg richloc.add_fixit_insert_before (col_25, "}"); 5213 1.1 mrg richloc.add_fixit_insert_before (col_21, "}"); 5214 1.1 mrg richloc.add_fixit_insert_before (col_1, "{"); 5215 1.1 mrg richloc.add_fixit_insert_before (col_25, "}"); 5216 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5217 1.1 mrg ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n" 5218 1.1 mrg " ^\n" 5219 1.1 mrg " { -----\n" 5220 1.1 mrg " {{1}}}}, {{{2 }}\n", 5221 1.1 mrg pp_formatted_text (dc.printer)); 5222 1.1 mrg } 5223 1.1 mrg } 5224 1.1 mrg 5225 1.1 mrg /* Insertion fix-it hint: adding a "break;" on a line by itself. */ 5226 1.1 mrg 5227 1.1 mrg static void 5228 1.1 mrg test_fixit_insert_containing_newline (const line_table_case &case_) 5229 1.1 mrg { 5230 1.1 mrg /* Create a tempfile and write some text to it. 5231 1.1 mrg .........................0000000001111111. 5232 1.1 mrg .........................1234567890123456. */ 5233 1.1 mrg const char *old_content = (" case 'a':\n" /* line 1. */ 5234 1.1 mrg " x = a;\n" /* line 2. */ 5235 1.1 mrg " case 'b':\n" /* line 3. */ 5236 1.1 mrg " x = b;\n");/* line 4. */ 5237 1.1 mrg 5238 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content); 5239 1.1 mrg line_table_test ltt (case_); 5240 1.1 mrg linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3); 5241 1.1 mrg 5242 1.1 mrg location_t case_start = linemap_position_for_column (line_table, 5); 5243 1.1 mrg location_t case_finish = linemap_position_for_column (line_table, 13); 5244 1.1 mrg location_t case_loc = make_location (case_start, case_start, case_finish); 5245 1.1 mrg location_t line_start = linemap_position_for_column (line_table, 1); 5246 1.1 mrg 5247 1.1 mrg if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS) 5248 1.1 mrg return; 5249 1.1 mrg 5250 1.1 mrg /* Add a "break;" on a line by itself before line 3 i.e. before 5251 1.1 mrg column 1 of line 3. */ 5252 1.1 mrg { 5253 1.1 mrg rich_location richloc (line_table, case_loc); 5254 1.1 mrg richloc.add_fixit_insert_before (line_start, " break;\n"); 5255 1.1 mrg 5256 1.1 mrg /* Without line numbers. */ 5257 1.1 mrg { 5258 1.1 mrg test_diagnostic_context dc; 5259 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5260 1.1 mrg ASSERT_STREQ (" x = a;\n" 5261 1.1 mrg "+ break;\n" 5262 1.1 mrg " case 'b':\n" 5263 1.1 mrg " ^~~~~~~~~\n", 5264 1.1 mrg pp_formatted_text (dc.printer)); 5265 1.1 mrg } 5266 1.1 mrg 5267 1.1 mrg /* With line numbers. */ 5268 1.1 mrg { 5269 1.1 mrg test_diagnostic_context dc; 5270 1.1 mrg dc.show_line_numbers_p = true; 5271 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5272 1.1 mrg ASSERT_STREQ (" 2 | x = a;\n" 5273 1.1 mrg " +++ |+ break;\n" 5274 1.1 mrg " 3 | case 'b':\n" 5275 1.1 mrg " | ^~~~~~~~~\n", 5276 1.1 mrg pp_formatted_text (dc.printer)); 5277 1.1 mrg } 5278 1.1 mrg } 5279 1.1 mrg 5280 1.1 mrg /* Verify that attempts to add text with a newline fail when the 5281 1.1 mrg insertion point is *not* at the start of a line. */ 5282 1.1 mrg { 5283 1.1 mrg rich_location richloc (line_table, case_loc); 5284 1.1 mrg richloc.add_fixit_insert_before (case_start, "break;\n"); 5285 1.1 mrg ASSERT_TRUE (richloc.seen_impossible_fixit_p ()); 5286 1.1 mrg test_diagnostic_context dc; 5287 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5288 1.1 mrg ASSERT_STREQ (" case 'b':\n" 5289 1.1 mrg " ^~~~~~~~~\n", 5290 1.1 mrg pp_formatted_text (dc.printer)); 5291 1.1 mrg } 5292 1.1 mrg } 5293 1.1 mrg 5294 1.1 mrg /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top 5295 1.1 mrg of the file, where the fix-it is printed in a different line-span 5296 1.1 mrg to the primary range of the diagnostic. */ 5297 1.1 mrg 5298 1.1 mrg static void 5299 1.1 mrg test_fixit_insert_containing_newline_2 (const line_table_case &case_) 5300 1.1 mrg { 5301 1.1 mrg /* Create a tempfile and write some text to it. 5302 1.1 mrg .........................0000000001111111. 5303 1.1 mrg .........................1234567890123456. */ 5304 1.1 mrg const char *old_content = ("test (int ch)\n" /* line 1. */ 5305 1.1 mrg "{\n" /* line 2. */ 5306 1.1 mrg " putchar (ch);\n" /* line 3. */ 5307 1.1 mrg "}\n"); /* line 4. */ 5308 1.1 mrg 5309 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content); 5310 1.1 mrg line_table_test ltt (case_); 5311 1.1 mrg 5312 1.1 mrg const line_map_ordinary *ord_map = linemap_check_ordinary 5313 1.1 mrg (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0)); 5314 1.1 mrg linemap_line_start (line_table, 1, 100); 5315 1.1 mrg 5316 1.1 mrg /* The primary range is the "putchar" token. */ 5317 1.1 mrg location_t putchar_start 5318 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 3, 2); 5319 1.1 mrg location_t putchar_finish 5320 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 3, 8); 5321 1.1 mrg location_t putchar_loc 5322 1.1 mrg = make_location (putchar_start, putchar_start, putchar_finish); 5323 1.1 mrg rich_location richloc (line_table, putchar_loc); 5324 1.1 mrg 5325 1.1 mrg /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */ 5326 1.1 mrg location_t file_start 5327 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 1); 5328 1.1 mrg richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n"); 5329 1.1 mrg 5330 1.1 mrg if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS) 5331 1.1 mrg return; 5332 1.1 mrg 5333 1.1 mrg { 5334 1.1 mrg test_diagnostic_context dc; 5335 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5336 1.1 mrg ASSERT_STREQ ("FILENAME:1:1:\n" 5337 1.1 mrg "+#include <stdio.h>\n" 5338 1.1 mrg " test (int ch)\n" 5339 1.1 mrg "FILENAME:3:2:\n" 5340 1.1 mrg " putchar (ch);\n" 5341 1.1 mrg " ^~~~~~~\n", 5342 1.1 mrg pp_formatted_text (dc.printer)); 5343 1.1 mrg } 5344 1.1 mrg 5345 1.1 mrg /* With line-numbering, the line spans are close enough to be 5346 1.1 mrg consolidated, since it makes little sense to skip line 2. */ 5347 1.1 mrg { 5348 1.1 mrg test_diagnostic_context dc; 5349 1.1 mrg dc.show_line_numbers_p = true; 5350 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5351 1.1 mrg ASSERT_STREQ (" +++ |+#include <stdio.h>\n" 5352 1.1 mrg " 1 | test (int ch)\n" 5353 1.1 mrg " 2 | {\n" 5354 1.1 mrg " 3 | putchar (ch);\n" 5355 1.1 mrg " | ^~~~~~~\n", 5356 1.1 mrg pp_formatted_text (dc.printer)); 5357 1.1 mrg } 5358 1.1 mrg } 5359 1.1 mrg 5360 1.1 mrg /* Replacement fix-it hint containing a newline. 5361 1.1 mrg This will fail, as newlines are only supported when inserting at the 5362 1.1 mrg beginning of a line. */ 5363 1.1 mrg 5364 1.1 mrg static void 5365 1.1 mrg test_fixit_replace_containing_newline (const line_table_case &case_) 5366 1.1 mrg { 5367 1.1 mrg /* Create a tempfile and write some text to it. 5368 1.1 mrg .........................0000000001111. 5369 1.1 mrg .........................1234567890123. */ 5370 1.1 mrg const char *old_content = "foo = bar ();\n"; 5371 1.1 mrg 5372 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content); 5373 1.1 mrg line_table_test ltt (case_); 5374 1.1 mrg linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1); 5375 1.1 mrg 5376 1.1 mrg /* Replace the " = " with "\n = ", as if we were reformatting an 5377 1.1 mrg overly long line. */ 5378 1.1 mrg location_t start = linemap_position_for_column (line_table, 4); 5379 1.1 mrg location_t finish = linemap_position_for_column (line_table, 6); 5380 1.1 mrg location_t loc = linemap_position_for_column (line_table, 13); 5381 1.1 mrg rich_location richloc (line_table, loc); 5382 1.1 mrg source_range range = source_range::from_locations (start, finish); 5383 1.1 mrg richloc.add_fixit_replace (range, "\n ="); 5384 1.1 mrg 5385 1.1 mrg /* Arbitrary newlines are not yet supported within fix-it hints, so 5386 1.1 mrg the fix-it should not be displayed. */ 5387 1.1 mrg ASSERT_TRUE (richloc.seen_impossible_fixit_p ()); 5388 1.1 mrg 5389 1.1 mrg if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS) 5390 1.1 mrg return; 5391 1.1 mrg 5392 1.1 mrg test_diagnostic_context dc; 5393 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5394 1.1 mrg ASSERT_STREQ (" foo = bar ();\n" 5395 1.1 mrg " ^\n", 5396 1.1 mrg pp_formatted_text (dc.printer)); 5397 1.1 mrg } 5398 1.1 mrg 5399 1.1 mrg /* Fix-it hint, attempting to delete a newline. 5400 1.1 mrg This will fail, as we currently only support fix-it hints that 5401 1.1 mrg affect one line at a time. */ 5402 1.1 mrg 5403 1.1 mrg static void 5404 1.1 mrg test_fixit_deletion_affecting_newline (const line_table_case &case_) 5405 1.1 mrg { 5406 1.1 mrg /* Create a tempfile and write some text to it. 5407 1.1 mrg ..........................0000000001111. 5408 1.1 mrg ..........................1234567890123. */ 5409 1.1 mrg const char *old_content = ("foo = bar (\n" 5410 1.1 mrg " );\n"); 5411 1.1 mrg 5412 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content); 5413 1.1 mrg line_table_test ltt (case_); 5414 1.1 mrg const line_map_ordinary *ord_map = linemap_check_ordinary 5415 1.1 mrg (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0)); 5416 1.1 mrg linemap_line_start (line_table, 1, 100); 5417 1.1 mrg 5418 1.1 mrg /* Attempt to delete the " (\n...)". */ 5419 1.1 mrg location_t start 5420 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 10); 5421 1.1 mrg location_t caret 5422 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 11); 5423 1.1 mrg location_t finish 5424 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 2, 7); 5425 1.1 mrg location_t loc = make_location (caret, start, finish); 5426 1.1 mrg rich_location richloc (line_table, loc); 5427 1.1 mrg richloc. add_fixit_remove (); 5428 1.1 mrg 5429 1.1 mrg /* Fix-it hints that affect more than one line are not yet supported, so 5430 1.1 mrg the fix-it should not be displayed. */ 5431 1.1 mrg ASSERT_TRUE (richloc.seen_impossible_fixit_p ()); 5432 1.1 mrg 5433 1.1 mrg if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS) 5434 1.1 mrg return; 5435 1.1 mrg 5436 1.1 mrg test_diagnostic_context dc; 5437 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5438 1.1 mrg ASSERT_STREQ (" foo = bar (\n" 5439 1.1 mrg " ~^\n" 5440 1.1 mrg " );\n" 5441 1.1 mrg " ~ \n", 5442 1.1 mrg pp_formatted_text (dc.printer)); 5443 1.1 mrg } 5444 1.1 mrg 5445 1.1 mrg static void 5446 1.1 mrg test_tab_expansion (const line_table_case &case_) 5447 1.1 mrg { 5448 1.1 mrg /* Create a tempfile and write some text to it. This example uses a tabstop 5449 1.1 mrg of 8, as the column numbers attempt to indicate: 5450 1.1 mrg 5451 1.1 mrg .....................000.01111111111.22222333333 display 5452 1.1 mrg .....................123.90123456789.56789012345 columns */ 5453 1.1 mrg const char *content = " \t This: `\t' is a tab.\n"; 5454 1.1 mrg /* ....................000 00000011111 11111222222 byte 5455 1.1 mrg ....................123 45678901234 56789012345 columns */ 5456 1.1 mrg 5457 1.1 mrg const int tabstop = 8; 5458 1.1 mrg cpp_char_column_policy policy (tabstop, cpp_wcwidth); 5459 1.1 mrg const int first_non_ws_byte_col = 7; 5460 1.1 mrg const int right_quote_byte_col = 15; 5461 1.1 mrg const int last_byte_col = 25; 5462 1.1 mrg ASSERT_EQ (35, cpp_display_width (content, last_byte_col, policy)); 5463 1.1 mrg 5464 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", content); 5465 1.1 mrg line_table_test ltt (case_); 5466 1.1 mrg linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1); 5467 1.1 mrg 5468 1.1 mrg /* Don't attempt to run the tests if column data might be unavailable. */ 5469 1.1 mrg location_t line_end = linemap_position_for_column (line_table, last_byte_col); 5470 1.1 mrg if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) 5471 1.1 mrg return; 5472 1.1 mrg 5473 1.1 mrg /* Check that the leading whitespace with mixed tabs and spaces is expanded 5474 1.1 mrg into 11 spaces. Recall that print_line() also puts one space before 5475 1.1 mrg everything too. */ 5476 1.1 mrg { 5477 1.1 mrg test_diagnostic_context dc; 5478 1.1 mrg dc.tabstop = tabstop; 5479 1.1 mrg rich_location richloc (line_table, 5480 1.1 mrg linemap_position_for_column (line_table, 5481 1.1 mrg first_non_ws_byte_col)); 5482 1.1 mrg layout test_layout (&dc, &richloc, DK_ERROR); 5483 1.1 mrg test_layout.print_line (1); 5484 1.1 mrg ASSERT_STREQ (" This: ` ' is a tab.\n" 5485 1.1 mrg " ^\n", 5486 1.1 mrg pp_formatted_text (dc.printer)); 5487 1.1 mrg } 5488 1.1 mrg 5489 1.1 mrg /* Confirm the display width was tracked correctly across the internal tab 5490 1.1 mrg as well. */ 5491 1.1 mrg { 5492 1.1 mrg test_diagnostic_context dc; 5493 1.1 mrg dc.tabstop = tabstop; 5494 1.1 mrg rich_location richloc (line_table, 5495 1.1 mrg linemap_position_for_column (line_table, 5496 1.1 mrg right_quote_byte_col)); 5497 1.1 mrg layout test_layout (&dc, &richloc, DK_ERROR); 5498 1.1 mrg test_layout.print_line (1); 5499 1.1 mrg ASSERT_STREQ (" This: ` ' is a tab.\n" 5500 1.1 mrg " ^\n", 5501 1.1 mrg pp_formatted_text (dc.printer)); 5502 1.1 mrg } 5503 1.1 mrg } 5504 1.1 mrg 5505 1.1 mrg /* Verify that the escaping machinery can cope with a variety of different 5506 1.1 mrg invalid bytes. */ 5507 1.1 mrg 5508 1.1 mrg static void 5509 1.1 mrg test_escaping_bytes_1 (const line_table_case &case_) 5510 1.1 mrg { 5511 1.1 mrg const char content[] = "before\0\1\2\3\r\x80\xff""after\n"; 5512 1.1 mrg const size_t sz = sizeof (content); 5513 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz); 5514 1.1 mrg line_table_test ltt (case_); 5515 1.1 mrg const line_map_ordinary *ord_map = linemap_check_ordinary 5516 1.1 mrg (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0)); 5517 1.1 mrg linemap_line_start (line_table, 1, 100); 5518 1.1 mrg 5519 1.1 mrg location_t finish 5520 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 5521 1.1 mrg strlen (content)); 5522 1.1 mrg 5523 1.1 mrg if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS) 5524 1.1 mrg return; 5525 1.1 mrg 5526 1.1 mrg /* Locations of the NUL and \r bytes. */ 5527 1.1 mrg location_t nul_loc 5528 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 7); 5529 1.1 mrg location_t r_loc 5530 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 11); 5531 1.1 mrg gcc_rich_location richloc (nul_loc); 5532 1.1 mrg richloc.add_range (r_loc); 5533 1.1 mrg 5534 1.1 mrg { 5535 1.1 mrg test_diagnostic_context dc; 5536 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5537 1.1 mrg ASSERT_STREQ (" before \1\2\3 \x80\xff""after\n" 5538 1.1 mrg " ^ ~\n", 5539 1.1 mrg pp_formatted_text (dc.printer)); 5540 1.1 mrg } 5541 1.1 mrg richloc.set_escape_on_output (true); 5542 1.1 mrg { 5543 1.1 mrg test_diagnostic_context dc; 5544 1.1 mrg dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE; 5545 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5546 1.1 mrg ASSERT_STREQ 5547 1.1 mrg (" before<U+0000><U+0001><U+0002><U+0003><U+000D><80><ff>after\n" 5548 1.1 mrg " ^~~~~~~~ ~~~~~~~~\n", 5549 1.1 mrg pp_formatted_text (dc.printer)); 5550 1.1 mrg } 5551 1.1 mrg { 5552 1.1 mrg test_diagnostic_context dc; 5553 1.1 mrg dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES; 5554 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5555 1.1 mrg ASSERT_STREQ (" before<00><01><02><03><0d><80><ff>after\n" 5556 1.1 mrg " ^~~~ ~~~~\n", 5557 1.1 mrg pp_formatted_text (dc.printer)); 5558 1.1 mrg } 5559 1.1 mrg } 5560 1.1 mrg 5561 1.1 mrg /* As above, but verify that we handle the initial byte of a line 5562 1.1 mrg correctly. */ 5563 1.1 mrg 5564 1.1 mrg static void 5565 1.1 mrg test_escaping_bytes_2 (const line_table_case &case_) 5566 1.1 mrg { 5567 1.1 mrg const char content[] = "\0after\n"; 5568 1.1 mrg const size_t sz = sizeof (content); 5569 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz); 5570 1.1 mrg line_table_test ltt (case_); 5571 1.1 mrg const line_map_ordinary *ord_map = linemap_check_ordinary 5572 1.1 mrg (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0)); 5573 1.1 mrg linemap_line_start (line_table, 1, 100); 5574 1.1 mrg 5575 1.1 mrg location_t finish 5576 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 5577 1.1 mrg strlen (content)); 5578 1.1 mrg 5579 1.1 mrg if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS) 5580 1.1 mrg return; 5581 1.1 mrg 5582 1.1 mrg /* Location of the NUL byte. */ 5583 1.1 mrg location_t nul_loc 5584 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 1, 1); 5585 1.1 mrg gcc_rich_location richloc (nul_loc); 5586 1.1 mrg 5587 1.1 mrg { 5588 1.1 mrg test_diagnostic_context dc; 5589 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5590 1.1 mrg ASSERT_STREQ (" after\n" 5591 1.1 mrg " ^\n", 5592 1.1 mrg pp_formatted_text (dc.printer)); 5593 1.1 mrg } 5594 1.1 mrg richloc.set_escape_on_output (true); 5595 1.1 mrg { 5596 1.1 mrg test_diagnostic_context dc; 5597 1.1 mrg dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE; 5598 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5599 1.1 mrg ASSERT_STREQ (" <U+0000>after\n" 5600 1.1 mrg " ^~~~~~~~\n", 5601 1.1 mrg pp_formatted_text (dc.printer)); 5602 1.1 mrg } 5603 1.1 mrg { 5604 1.1 mrg test_diagnostic_context dc; 5605 1.1 mrg dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES; 5606 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5607 1.1 mrg ASSERT_STREQ (" <00>after\n" 5608 1.1 mrg " ^~~~\n", 5609 1.1 mrg pp_formatted_text (dc.printer)); 5610 1.1 mrg } 5611 1.1 mrg } 5612 1.1 mrg 5613 1.1 mrg /* Verify that line numbers are correctly printed for the case of 5614 1.1 mrg a multiline range in which the width of the line numbers changes 5615 1.1 mrg (e.g. from "9" to "10"). */ 5616 1.1 mrg 5617 1.1 mrg static void 5618 1.1 mrg test_line_numbers_multiline_range () 5619 1.1 mrg { 5620 1.1 mrg /* Create a tempfile and write some text to it. */ 5621 1.1 mrg pretty_printer pp; 5622 1.1 mrg for (int i = 0; i < 20; i++) 5623 1.1 mrg /* .........0000000001111111. 5624 1.1 mrg .............1234567890123456. */ 5625 1.1 mrg pp_printf (&pp, "this is line %i\n", i + 1); 5626 1.1 mrg temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp)); 5627 1.1 mrg line_table_test ltt; 5628 1.1 mrg 5629 1.1 mrg const line_map_ordinary *ord_map = linemap_check_ordinary 5630 1.1 mrg (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0)); 5631 1.1 mrg linemap_line_start (line_table, 1, 100); 5632 1.1 mrg 5633 1.1 mrg /* Create a multi-line location, starting at the "line" of line 9, with 5634 1.1 mrg a caret on the "is" of line 10, finishing on the "this" line 11. */ 5635 1.1 mrg 5636 1.1 mrg location_t start 5637 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 9, 9); 5638 1.1 mrg location_t caret 5639 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 10, 6); 5640 1.1 mrg location_t finish 5641 1.1 mrg = linemap_position_for_line_and_column (line_table, ord_map, 11, 4); 5642 1.1 mrg location_t loc = make_location (caret, start, finish); 5643 1.1 mrg 5644 1.1 mrg test_diagnostic_context dc; 5645 1.1 mrg dc.show_line_numbers_p = true; 5646 1.1 mrg dc.min_margin_width = 0; 5647 1.1 mrg gcc_rich_location richloc (loc); 5648 1.1 mrg diagnostic_show_locus (&dc, &richloc, DK_ERROR); 5649 1.1 mrg ASSERT_STREQ (" 9 | this is line 9\n" 5650 1.1 mrg " | ~~~~~~\n" 5651 1.1 mrg "10 | this is line 10\n" 5652 1.1 mrg " | ~~~~~^~~~~~~~~~\n" 5653 1.1 mrg "11 | this is line 11\n" 5654 1.1 mrg " | ~~~~ \n", 5655 1.1 mrg pp_formatted_text (dc.printer)); 5656 1.1 mrg } 5657 1.1 mrg 5658 1.1 mrg /* Run all of the selftests within this file. */ 5659 1.1 mrg 5660 1.1 mrg void 5661 1.1 mrg diagnostic_show_locus_cc_tests () 5662 1.1 mrg { 5663 1.1 mrg test_line_span (); 5664 1.1 mrg 5665 1.1 mrg test_layout_range_for_single_point (); 5666 1.1 mrg test_layout_range_for_single_line (); 5667 1.1 mrg test_layout_range_for_multiple_lines (); 5668 1.1 mrg 5669 1.1 mrg test_display_widths (); 5670 1.1 mrg 5671 1.1 mrg for_each_line_table_case (test_layout_x_offset_display_utf8); 5672 1.1 mrg for_each_line_table_case (test_layout_x_offset_display_tab); 5673 1.1 mrg 5674 1.1 mrg test_get_line_bytes_without_trailing_whitespace (); 5675 1.1 mrg 5676 1.1 mrg test_diagnostic_show_locus_unknown_location (); 5677 1.1 mrg 5678 1.1 mrg for_each_line_table_case (test_diagnostic_show_locus_one_liner); 5679 1.1 mrg for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8); 5680 1.1 mrg for_each_line_table_case (test_add_location_if_nearby); 5681 1.1 mrg for_each_line_table_case (test_diagnostic_show_locus_fixit_lines); 5682 1.1 mrg for_each_line_table_case (test_fixit_consolidation); 5683 1.1 mrg for_each_line_table_case (test_overlapped_fixit_printing); 5684 1.1 mrg for_each_line_table_case (test_overlapped_fixit_printing_utf8); 5685 1.1 mrg for_each_line_table_case (test_overlapped_fixit_printing_2); 5686 1.1 mrg for_each_line_table_case (test_fixit_insert_containing_newline); 5687 1.1 mrg for_each_line_table_case (test_fixit_insert_containing_newline_2); 5688 1.1 mrg for_each_line_table_case (test_fixit_replace_containing_newline); 5689 1.1 mrg for_each_line_table_case (test_fixit_deletion_affecting_newline); 5690 1.1 mrg for_each_line_table_case (test_tab_expansion); 5691 1.1 mrg for_each_line_table_case (test_escaping_bytes_1); 5692 1.1 mrg for_each_line_table_case (test_escaping_bytes_2); 5693 1.1 mrg 5694 1.1 mrg test_line_numbers_multiline_range (); 5695 1.1 mrg } 5696 1.1 mrg 5697 1.1 mrg } // namespace selftest 5698 1.1 mrg 5699 1.1 mrg #endif /* #if CHECKING_P */ 5700 1.1 mrg 5701 1.1 mrg #if __GNUC__ >= 10 5702 1.1 mrg # pragma GCC diagnostic pop 5703 1.1 mrg #endif 5704