1/* Bundles of location information used when printing diagnostics.
2 Copyright (C) 2015-2025 Free Software Foundation, Inc.
3
4This program is free software; you can redistribute it and/or modify it
5under the terms of the GNU General Public License as published by the
6Free Software Foundation; either version 3, or (at your option) any
7later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; see the file COPYING3. If not see
16<http://www.gnu.org/licenses/>.
17
18 In other words, you are welcome to use, share and improve this program.
19 You are forbidden to forbid anyone else to use, share and improve
20 what you give them. Help stamp out software-hoarding! */
21
22#ifndef LIBCPP_RICH_LOCATION_H
23#define LIBCPP_RICH_LOCATION_H
24
25#include "label-text.h"
26
27class range_label;
28class label_effects;
29
30/* A hint to diagnostic_show_locus on how to print a source range within a
31 rich_location.
32
33 Typically this is SHOW_RANGE_WITH_CARET for the 0th range, and
34 SHOW_RANGE_WITHOUT_CARET for subsequent ranges,
35 but the Fortran frontend uses SHOW_RANGE_WITH_CARET repeatedly for
36 printing things like:
37
38 x = x + y
39 1 2
40 Error: Shapes for operands at (1) and (2) are not conformable
41
42 where "1" and "2" are notionally carets. */
43
44enum range_display_kind
45{
46 /* Show the pertinent source line(s), the caret, and underline(s). */
47 SHOW_RANGE_WITH_CARET,
48
49 /* Show the pertinent source line(s) and underline(s), but don't
50 show the caret (just an underline). */
51 SHOW_RANGE_WITHOUT_CARET,
52
53 /* Just show the source lines; don't show the range itself.
54 This is for use when displaying some line-insertion fix-it hints (for
55 showing the user context on the change, for when it doesn't make sense
56 to highlight the first column on the next line). */
57 SHOW_LINES_WITHOUT_RANGE
58};
59
60/* A location within a rich_location: a caret&range, with
61 the caret potentially flagged for display, and an optional
62 label. */
63
64struct location_range
65{
66 location_t m_loc;
67
68 enum range_display_kind m_range_display_kind;
69
70 /* If non-NULL, the label for this range. */
71 const range_label *m_label;
72
73 /* If non-null, the name of the color to use for this range. */
74 const char *m_highlight_color;
75};
76
77/* A partially-embedded vec for use within rich_location for storing
78 ranges and fix-it hints.
79
80 Elements [0..NUM_EMBEDDED) are allocated within m_embed, after
81 that they are within the dynamically-allocated m_extra.
82
83 This allows for static allocation in the common case, whilst
84 supporting the rarer case of an arbitrary number of elements.
85
86 Dynamic allocation is not performed unless it's needed. */
87
88template <typename T, int NUM_EMBEDDED>
89class semi_embedded_vec
90{
91 public:
92 semi_embedded_vec ();
93 ~semi_embedded_vec ();
94 semi_embedded_vec (const semi_embedded_vec &other);
95
96 unsigned int count () const { return m_num; }
97 T& operator[] (int idx);
98 const T& operator[] (int idx) const;
99
100 void push (const T&);
101 void truncate (int len);
102
103 private:
104 int m_num;
105 T m_embedded[NUM_EMBEDDED];
106 int m_alloc;
107 T *m_extra;
108};
109
110/* Constructor for semi_embedded_vec. In particular, no dynamic allocation
111 is done. */
112
113template <typename T, int NUM_EMBEDDED>
114semi_embedded_vec<T, NUM_EMBEDDED>::semi_embedded_vec ()
115: m_num (0), m_alloc (0), m_extra (NULL)
116{
117}
118
119/* Copy constructor for semi_embedded_vec. */
120
121template <typename T, int NUM_EMBEDDED>
122semi_embedded_vec<T, NUM_EMBEDDED>::semi_embedded_vec (const semi_embedded_vec &other)
123: m_num (0),
124 m_alloc (other.m_alloc),
125 m_extra (nullptr)
126{
127 if (other.m_extra)
128 m_extra = XNEWVEC (T, m_alloc);
129
130 for (int i = 0; i < other.m_num; i++)
131 push (other[i]);
132}
133
134/* semi_embedded_vec's dtor. Release any dynamically-allocated memory. */
135
136template <typename T, int NUM_EMBEDDED>
137semi_embedded_vec<T, NUM_EMBEDDED>::~semi_embedded_vec ()
138{
139 XDELETEVEC (m_extra);
140}
141
142/* Look up element IDX, mutably. */
143
144template <typename T, int NUM_EMBEDDED>
145T&
146semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx)
147{
148 linemap_assert (idx < m_num);
149 if (idx < NUM_EMBEDDED)
150 return m_embedded[idx];
151 else
152 {
153 linemap_assert (m_extra != NULL);
154 return m_extra[idx - NUM_EMBEDDED];
155 }
156}
157
158/* Look up element IDX (const). */
159
160template <typename T, int NUM_EMBEDDED>
161const T&
162semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx) const
163{
164 linemap_assert (idx < m_num);
165 if (idx < NUM_EMBEDDED)
166 return m_embedded[idx];
167 else
168 {
169 linemap_assert (m_extra != NULL);
170 return m_extra[idx - NUM_EMBEDDED];
171 }
172}
173
174/* Append VALUE to the end of the semi_embedded_vec. */
175
176template <typename T, int NUM_EMBEDDED>
177void
178semi_embedded_vec<T, NUM_EMBEDDED>::push (const T& value)
179{
180 int idx = m_num++;
181 if (idx < NUM_EMBEDDED)
182 m_embedded[idx] = value;
183 else
184 {
185 /* Offset "idx" to be an index within m_extra. */
186 idx -= NUM_EMBEDDED;
187 if (NULL == m_extra)
188 {
189 linemap_assert (m_alloc == 0);
190 m_alloc = 16;
191 m_extra = XNEWVEC (T, m_alloc);
192 }
193 else if (idx >= m_alloc)
194 {
195 linemap_assert (m_alloc > 0);
196 m_alloc *= 2;
197 m_extra = XRESIZEVEC (T, m_extra, m_alloc);
198 }
199 linemap_assert (m_extra);
200 linemap_assert (idx < m_alloc);
201 m_extra[idx] = value;
202 }
203}
204
205/* Truncate to length LEN. No deallocation is performed. */
206
207template <typename T, int NUM_EMBEDDED>
208void
209semi_embedded_vec<T, NUM_EMBEDDED>::truncate (int len)
210{
211 linemap_assert (len <= m_num);
212 m_num = len;
213}
214
215class fixit_hint;
216class diagnostic_path;
217
218/* A "rich" source code location, for use when printing diagnostics.
219 A rich_location has one or more carets&ranges, where the carets
220 are optional. These are referred to as "ranges" from here.
221 Typically the zeroth range has a caret; other ranges sometimes
222 have carets.
223
224 The "primary" location of a rich_location is the caret of range 0,
225 used for determining the line/column when printing diagnostic
226 text, such as:
227
228 some-file.c:3:1: error: ...etc...
229
230 Additional ranges may be added to help the user identify other
231 pertinent clauses in a diagnostic.
232
233 Ranges can (optionally) be given labels via class range_label.
234
235 rich_location instances are intended to be allocated on the stack
236 when generating diagnostics, and to be short-lived.
237
238 Examples of rich locations
239 --------------------------
240
241 Example A
242 *********
243 int i = "foo";
244 ^
245 This "rich" location is simply a single range (range 0), with
246 caret = start = finish at the given point.
247
248 Example B
249 *********
250 a = (foo && bar)
251 ~~~~~^~~~~~~
252 This rich location has a single range (range 0), with the caret
253 at the first "&", and the start/finish at the parentheses.
254 Compare with example C below.
255
256 Example C
257 *********
258 a = (foo && bar)
259 ~~~ ^~ ~~~
260 This rich location has three ranges:
261 - Range 0 has its caret and start location at the first "&" and
262 end at the second "&.
263 - Range 1 has its start and finish at the "f" and "o" of "foo";
264 the caret is not flagged for display, but is perhaps at the "f"
265 of "foo".
266 - Similarly, range 2 has its start and finish at the "b" and "r" of
267 "bar"; the caret is not flagged for display, but is perhaps at the
268 "b" of "bar".
269 Compare with example B above.
270
271 Example D (Fortran frontend)
272 ****************************
273 x = x + y
274 1 2
275 This rich location has range 0 at "1", and range 1 at "2".
276 Both are flagged for caret display. Both ranges have start/finish
277 equal to their caret point. The frontend overrides the diagnostic
278 context's default caret character for these ranges.
279
280 Example E (range labels)
281 ************************
282 printf ("arg0: %i arg1: %s arg2: %i",
283 ^~
284 |
285 const char *
286 100, 101, 102);
287 ~~~
288 |
289 int
290 This rich location has two ranges:
291 - range 0 is at the "%s" with start = caret = "%" and finish at
292 the "s". It has a range_label ("const char *").
293 - range 1 has start/finish covering the "101" and is not flagged for
294 caret printing. The caret is at the start of "101", where its
295 range_label is printed ("int").
296
297 Fix-it hints
298 ------------
299
300 Rich locations can also contain "fix-it hints", giving suggestions
301 for the user on how to edit their code to fix a problem. These
302 can be expressed as insertions, replacements, and removals of text.
303 The edits by default are relative to the zeroth range within the
304 rich_location, but optionally they can be expressed relative to
305 other locations (using various overloaded methods of the form
306 rich_location::add_fixit_*).
307
308 For example:
309
310 Example F: fix-it hint: insert_before
311 *************************************
312 ptr = arr[0];
313 ^~~~~~
314 &
315 This rich location has a single range (range 0) covering "arr[0]",
316 with the caret at the start. The rich location has a single
317 insertion fix-it hint, inserted before range 0, added via
318 richloc.add_fixit_insert_before ("&");
319
320 Example G: multiple fix-it hints: insert_before and insert_after
321 ****************************************************************
322 #define FN(ARG0, ARG1, ARG2) fn(ARG0, ARG1, ARG2)
323 ^~~~ ^~~~ ^~~~
324 ( ) ( ) ( )
325 This rich location has three ranges, covering "arg0", "arg1",
326 and "arg2", all with caret-printing enabled.
327 The rich location has 6 insertion fix-it hints: each arg
328 has a pair of insertion fix-it hints, suggesting wrapping
329 them with parentheses: one a '(' inserted before,
330 the other a ')' inserted after, added via
331 richloc.add_fixit_insert_before (LOC, "(");
332 and
333 richloc.add_fixit_insert_after (LOC, ")");
334
335 Example H: fix-it hint: removal
336 *******************************
337 struct s {int i};;
338 ^
339 -
340 This rich location has a single range at the stray trailing
341 semicolon, along with a single removal fix-it hint, covering
342 the same range, added via:
343 richloc.add_fixit_remove ();
344
345 Example I: fix-it hint: replace
346 *******************************
347 c = s.colour;
348 ^~~~~~
349 color
350 This rich location has a single range (range 0) covering "colour",
351 and a single "replace" fix-it hint, covering the same range,
352 added via
353 richloc.add_fixit_replace ("color");
354
355 Example J: fix-it hint: line insertion
356 **************************************
357
358 3 | #include <stddef.h>
359 + |+#include <stdio.h>
360 4 | int the_next_line;
361
362 This rich location has a single range at line 4 column 1, marked
363 with SHOW_LINES_WITHOUT_RANGE (to avoid printing a meaningless caret
364 on the "i" of int). It has a insertion fix-it hint of the string
365 "#include <stdio.h>\n".
366
367 Adding a fix-it hint can fail: for example, attempts to insert content
368 at the transition between two line maps may fail due to there being no
369 location_t value to express the new location.
370
371 Attempts to add a fix-it hint within a macro expansion will fail.
372
373 There is only limited support for newline characters in fix-it hints:
374 only hints with newlines which insert an entire new line are permitted,
375 inserting at the start of a line, and finishing with a newline
376 (with no interior newline characters). Other attempts to add
377 fix-it hints containing newline characters will fail.
378 Similarly, attempts to delete or replace a range *affecting* multiple
379 lines will fail.
380
381 The rich_location API handles these failures gracefully, so that
382 diagnostics can attempt to add fix-it hints without each needing
383 extensive checking.
384
385 Fix-it hints within a rich_location are "atomic": if any hints can't
386 be applied, none of them will be (tracked by the m_seen_impossible_fixit
387 flag), and no fix-its hints will be displayed for that rich_location.
388 This implies that diagnostic messages need to be worded in such a way
389 that they make sense whether or not the fix-it hints are displayed,
390 or that richloc.seen_impossible_fixit_p () should be checked before
391 issuing the diagnostics. */
392
393class rich_location
394{
395 public:
396 /* Constructors. */
397
398 /* Constructing from a location. */
399 rich_location (line_maps *set, location_t loc,
400 const range_label *label = nullptr,
401 const char *label_highlight_color = nullptr);
402
403 /* Destructor. */
404 ~rich_location ();
405
406 rich_location (const rich_location &);
407 rich_location (rich_location &&) = delete;
408 rich_location &operator= (const rich_location &) = delete;
409 rich_location &operator= (rich_location &&) = delete;
410
411 /* Accessors. */
412 location_t get_loc () const { return get_loc (idx: 0); }
413 location_t get_loc (unsigned int idx) const;
414
415 void set_highlight_color (const char *highlight_color);
416
417 void
418 add_range (location_t loc,
419 enum range_display_kind range_display_kind
420 = SHOW_RANGE_WITHOUT_CARET,
421 const range_label *label = nullptr,
422 const char *highlight_color = nullptr);
423
424 void
425 set_range (unsigned int idx, location_t loc,
426 enum range_display_kind range_display_kind,
427 const char *highlight_color = nullptr);
428
429 unsigned int get_num_locations () const { return m_ranges.count (); }
430
431 const location_range *get_range (unsigned int idx) const;
432 location_range *get_range (unsigned int idx);
433
434 expanded_location get_expanded_location (unsigned int idx) const;
435
436 void
437 override_column (int column);
438
439 /* Fix-it hints. */
440
441 /* Methods for adding insertion fix-it hints. */
442
443 /* Suggest inserting NEW_CONTENT immediately before the primary
444 range's start. */
445 void
446 add_fixit_insert_before (const char *new_content);
447
448 /* Suggest inserting NEW_CONTENT immediately before the start of WHERE. */
449 void
450 add_fixit_insert_before (location_t where,
451 const char *new_content);
452
453 /* Suggest inserting NEW_CONTENT immediately after the end of the primary
454 range. */
455 void
456 add_fixit_insert_after (const char *new_content);
457
458 /* Suggest inserting NEW_CONTENT immediately after the end of WHERE. */
459 void
460 add_fixit_insert_after (location_t where,
461 const char *new_content);
462
463 /* Methods for adding removal fix-it hints. */
464
465 /* Suggest removing the content covered by range 0. */
466 void
467 add_fixit_remove ();
468
469 /* Suggest removing the content covered between the start and finish
470 of WHERE. */
471 void
472 add_fixit_remove (location_t where);
473
474 /* Suggest removing the content covered by SRC_RANGE. */
475 void
476 add_fixit_remove (source_range src_range);
477
478 /* Methods for adding "replace" fix-it hints. */
479
480 /* Suggest replacing the content covered by range 0 with NEW_CONTENT. */
481 void
482 add_fixit_replace (const char *new_content);
483
484 /* Suggest replacing the content between the start and finish of
485 WHERE with NEW_CONTENT. */
486 void
487 add_fixit_replace (location_t where,
488 const char *new_content);
489
490 /* Suggest replacing the content covered by SRC_RANGE with
491 NEW_CONTENT. */
492 void
493 add_fixit_replace (source_range src_range,
494 const char *new_content);
495
496 unsigned int get_num_fixit_hints () const { return m_fixit_hints.count (); }
497 fixit_hint *get_fixit_hint (int idx) const { return m_fixit_hints[idx]; }
498 fixit_hint *get_last_fixit_hint () const;
499 bool seen_impossible_fixit_p () const { return m_seen_impossible_fixit; }
500
501 /* Set this if the fix-it hints are not suitable to be
502 automatically applied.
503
504 For example, if you are suggesting more than one
505 mutually exclusive solution to a problem, then
506 it doesn't make sense to apply all of the solutions;
507 manual intervention is required.
508
509 If set, then the fix-it hints in the rich_location will
510 be printed, but will not be added to generated patches,
511 or affect the modified version of the file. */
512 void fixits_cannot_be_auto_applied ()
513 {
514 m_fixits_cannot_be_auto_applied = true;
515 }
516
517 bool fixits_can_be_auto_applied_p () const
518 {
519 return !m_fixits_cannot_be_auto_applied;
520 }
521
522 /* An optional path through the code. */
523 const diagnostic_path *get_path () const { return m_path; }
524 void set_path (const diagnostic_path *path) { m_path = path; }
525
526 /* A flag for hinting that the diagnostic involves character encoding
527 issues, and thus that it will be helpful to the user if we show some
528 representation of how the characters in the pertinent source lines
529 are encoded.
530 The default is false (i.e. do not escape).
531 When set to true, non-ASCII bytes in the pertinent source lines will
532 be escaped in a manner controlled by the user-supplied option
533 -fdiagnostics-escape-format=, so that the user can better understand
534 what's going on with the encoding in their source file. */
535 bool escape_on_output_p () const { return m_escape_on_output; }
536 void set_escape_on_output (bool flag) { m_escape_on_output = flag; }
537
538 const line_maps *get_line_table () const { return m_line_table; }
539
540 int get_column_override () const { return m_column_override; }
541
542private:
543 bool reject_impossible_fixit (location_t where);
544 void stop_supporting_fixits ();
545 void maybe_add_fixit (location_t start,
546 location_t next_loc,
547 const char *new_content);
548
549public:
550 static const int STATICALLY_ALLOCATED_RANGES = 3;
551
552protected:
553 line_maps * const m_line_table;
554 semi_embedded_vec <location_range, STATICALLY_ALLOCATED_RANGES> m_ranges;
555
556 int m_column_override;
557
558 mutable bool m_have_expanded_location;
559 bool m_seen_impossible_fixit;
560 bool m_fixits_cannot_be_auto_applied;
561 bool m_escape_on_output;
562
563 mutable expanded_location m_expanded_location;
564
565 /* The class manages the memory pointed to by the elements of
566 the m_fixit_hints vector. */
567 static const int MAX_STATIC_FIXIT_HINTS = 2;
568 semi_embedded_vec <fixit_hint *, MAX_STATIC_FIXIT_HINTS> m_fixit_hints;
569
570 const diagnostic_path *m_path;
571};
572
573/* Abstract base class for labelling a range within a rich_location
574 (e.g. for labelling expressions with their type).
575
576 Generating the text could require non-trivial work, so this work
577 is delayed (via the "get_text" virtual function) until the diagnostic
578 printing code "knows" it needs it, thus avoiding doing it e.g. for
579 warnings that are filtered by command-line flags. This virtual
580 function also isolates libcpp and the diagnostics subsystem from
581 the front-end and middle-end-specific code for generating the text
582 for the labels.
583
584 Like the rich_location instances they annotate, range_label instances
585 are intended to be allocated on the stack when generating diagnostics,
586 and to be short-lived. */
587
588class range_label
589{
590 public:
591 virtual ~range_label () {}
592
593 /* Get localized text for the label.
594 The RANGE_IDX is provided, allowing for range_label instances to be
595 shared by multiple ranges if need be (the "flyweight" design pattern). */
596 virtual label_text get_text (unsigned range_idx) const = 0;
597
598 /* Get any special effects for the label (e.g. links to other labels). */
599 virtual const label_effects *get_effects (unsigned /*range_idx*/) const
600 {
601 return nullptr;
602 }
603};
604
605/* A fix-it hint: a suggested insertion, replacement, or deletion of text.
606 We handle these three types of edit with one class, by representing
607 them as replacement of a half-open range:
608 [start, next_loc)
609 Insertions have start == next_loc: "replace" the empty string at the
610 start location with the new string.
611 Deletions are replacement with the empty string.
612
613 There is only limited support for newline characters in fix-it hints
614 as noted above in the comment for class rich_location.
615 A fixit_hint instance can have at most one newline character; if
616 present, the newline character must be the final character of
617 the content (preventing e.g. fix-its that split a pre-existing line). */
618
619class fixit_hint
620{
621 public:
622 fixit_hint (location_t start,
623 location_t next_loc,
624 const char *new_content);
625 fixit_hint (const fixit_hint &other);
626 fixit_hint (fixit_hint &&other) = delete;
627 ~fixit_hint () { free (ptr: m_bytes); }
628 fixit_hint &operator= (const fixit_hint &) = delete;
629 fixit_hint &operator= (fixit_hint &&) = delete;
630
631 bool affects_line_p (const line_maps *set,
632 const char *file,
633 int line) const;
634 location_t get_start_loc () const { return m_start; }
635 location_t get_next_loc () const { return m_next_loc; }
636 bool maybe_append (location_t start,
637 location_t next_loc,
638 const char *new_content);
639
640 const char *get_string () const { return m_bytes; }
641 size_t get_length () const { return m_len; }
642
643 bool insertion_p () const { return m_start == m_next_loc; }
644
645 bool ends_with_newline_p () const;
646
647 private:
648 /* We don't use source_range here since, unlike most places,
649 this is a half-open/half-closed range:
650 [start, next_loc)
651 so that we can support insertion via start == next_loc. */
652 location_t m_start;
653 location_t m_next_loc;
654 char *m_bytes;
655 size_t m_len;
656};
657
658#endif /* !LIBCPP_RICH_LOCATION_H */
659

source code of libcpp/include/rich-location.h