111
|
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 */
|