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