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