Line data Source code
1 : /* Bundles of location information used when printing diagnostics.
2 : Copyright (C) 2015-2026 Free Software Foundation, Inc.
3 :
4 : This program is free software; you can redistribute it and/or modify it
5 : under the terms of the GNU General Public License as published by the
6 : Free Software Foundation; either version 3, or (at your option) any
7 : later version.
8 :
9 : This program is distributed in the hope that it will be useful,
10 : but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : GNU General Public License for more details.
13 :
14 : You should have received a copy of the GNU General Public License
15 : along 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 :
27 : class range_label;
28 : namespace diagnostics { class 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 :
44 : enum 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 :
64 : struct 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 :
88 : template <typename T, int NUM_EMBEDDED>
89 : class 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 1567051 : 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 :
113 : template <typename T, int NUM_EMBEDDED>
114 : semi_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 :
121 : template <typename T, int NUM_EMBEDDED>
122 : semi_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 :
136 : template <typename T, int NUM_EMBEDDED>
137 : semi_embedded_vec<T, NUM_EMBEDDED>::~semi_embedded_vec ()
138 : {
139 : XDELETEVEC (m_extra);
140 : }
141 :
142 : /* Look up element IDX, mutably. */
143 :
144 : template <typename T, int NUM_EMBEDDED>
145 : T&
146 : semi_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 :
160 : template <typename T, int NUM_EMBEDDED>
161 : const T&
162 19376 : semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx) const
163 : {
164 19376 : linemap_assert (idx < m_num);
165 19376 : if (idx < NUM_EMBEDDED)
166 13695 : return m_embedded[idx];
167 : else
168 : {
169 5681 : linemap_assert (m_extra != NULL);
170 5681 : return m_extra[idx - NUM_EMBEDDED];
171 : }
172 : }
173 :
174 : /* Append VALUE to the end of the semi_embedded_vec. */
175 :
176 : template <typename T, int NUM_EMBEDDED>
177 : void
178 : semi_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 :
207 : template <typename T, int NUM_EMBEDDED>
208 : void
209 : semi_embedded_vec<T, NUM_EMBEDDED>::truncate (int len)
210 : {
211 : linemap_assert (len <= m_num);
212 : m_num = len;
213 : }
214 :
215 : class fixit_hint;
216 : namespace diagnostics { namespace paths { class 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 :
393 : class 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 106099960 : location_t get_loc () const { return get_loc (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 1440545 : 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 126506 : unsigned int get_num_fixit_hints () const { return m_fixit_hints.count (); }
497 19376 : fixit_hint *get_fixit_hint (int idx) const { return m_fixit_hints[idx]; }
498 : fixit_hint *get_last_fixit_hint () const;
499 13796 : 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 140 : void fixits_cannot_be_auto_applied ()
513 : {
514 140 : m_fixits_cannot_be_auto_applied = true;
515 : }
516 :
517 60 : bool fixits_can_be_auto_applied_p () const
518 : {
519 60 : return !m_fixits_cannot_be_auto_applied;
520 : }
521 :
522 : /* An optional path through the code. */
523 345058 : const diagnostics::paths::path *get_path () const { return m_path; }
524 893512 : void set_path (const diagnostics::paths::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 101458 : bool escape_on_output_p () const { return m_escape_on_output; }
536 468 : void set_escape_on_output (bool flag) { m_escape_on_output = flag; }
537 :
538 49799 : const line_maps *get_line_table () const { return m_line_table; }
539 :
540 1178 : int get_column_override () const { return m_column_override; }
541 :
542 : private:
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 :
549 : public:
550 : static const int STATICALLY_ALLOCATED_RANGES = 3;
551 :
552 : protected:
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 diagnostics::paths::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 :
588 10496 : class range_label
589 : {
590 : public:
591 9194 : 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 diagnostics::label_effects *
600 8266 : get_effects (unsigned /*range_idx*/) const
601 : {
602 8266 : return nullptr;
603 : }
604 : };
605 :
606 : /* A fix-it hint: a suggested insertion, replacement, or deletion of text.
607 : We handle these three types of edit with one class, by representing
608 : them as replacement of a half-open range:
609 : [start, next_loc)
610 : Insertions have start == next_loc: "replace" the empty string at the
611 : start location with the new string.
612 : Deletions are replacement with the empty string.
613 :
614 : There is only limited support for newline characters in fix-it hints
615 : as noted above in the comment for class rich_location.
616 : A fixit_hint instance can have at most one newline character; if
617 : present, the newline character must be the final character of
618 : the content (preventing e.g. fix-its that split a pre-existing line). */
619 :
620 : class fixit_hint
621 : {
622 : public:
623 : fixit_hint (location_t start,
624 : location_t next_loc,
625 : const char *new_content);
626 : fixit_hint (const fixit_hint &other);
627 : fixit_hint (fixit_hint &&other) = delete;
628 : ~fixit_hint () { free (m_bytes); }
629 : fixit_hint &operator= (const fixit_hint &) = delete;
630 : fixit_hint &operator= (fixit_hint &&) = delete;
631 :
632 : bool affects_line_p (const line_maps *set,
633 : const char *file,
634 : int line) const;
635 76028 : location_t get_start_loc () const { return m_start; }
636 60148 : location_t get_next_loc () const { return m_next_loc; }
637 : bool maybe_append (location_t start,
638 : location_t next_loc,
639 : const char *new_content);
640 :
641 12641 : const char *get_string () const { return m_bytes; }
642 39337 : size_t get_length () const { return m_len; }
643 :
644 27762 : bool insertion_p () const { return m_start == m_next_loc; }
645 5 : bool deletion_p () const { return m_len == 0; }
646 4 : bool replacement_p () const { return m_len > 0 && m_start != m_next_loc; }
647 :
648 : bool ends_with_newline_p () const;
649 :
650 : private:
651 : /* We don't use source_range here since, unlike most places,
652 : this is a half-open/half-closed range:
653 : [start, next_loc)
654 : so that we can support insertion via start == next_loc. */
655 : location_t m_start;
656 : location_t m_next_loc;
657 : char *m_bytes;
658 : size_t m_len;
659 : };
660 :
661 : #endif /* !LIBCPP_RICH_LOCATION_H */
|