| 1 | /* Bundles of location information used when printing diagnostics. |
| 2 | Copyright (C) 2015-2025 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 | 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 | 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 *; |
| 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 | semi_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 | |
| 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 | class 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 | |
| 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 | 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 | |
| 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 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 | |
| 588 | class 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 | |
| 619 | class 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 | |