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