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