diff options
author | David Malcolm <dmalcolm@redhat.com> | 2023-11-19 06:26:40 -0500 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2023-11-19 06:26:40 -0500 |
commit | 78d132d73ec3784a77b3e2391a540bcdae714bb0 (patch) | |
tree | 2e9ff8838a5c3f6c108b3e4a03788a55e91d181c /libcpp | |
parent | af7fa3135b6b046fe3ba869993221042a65301eb (diff) | |
download | gcc-78d132d73ec3784a77b3e2391a540bcdae714bb0.zip gcc-78d132d73ec3784a77b3e2391a540bcdae714bb0.tar.gz gcc-78d132d73ec3784a77b3e2391a540bcdae714bb0.tar.bz2 |
libcpp: split decls out to rich-location.h
The various decls relating to rich_location are in
libcpp/include/line-map.h, but they don't relate to line maps.
Split them out to their own header: libcpp/include/rich-location.h
No functional change intended.
gcc/ChangeLog:
* Makefile.in (CPPLIB_H): Add libcpp/include/rich-location.h.
* coretypes.h (class rich_location): New forward decl.
gcc/analyzer/ChangeLog:
* analyzer.h: Include "rich-location.h".
gcc/c-family/ChangeLog:
* c-lex.cc: Include "rich-location.h".
gcc/cp/ChangeLog:
* mapper-client.cc: Include "rich-location.h".
gcc/ChangeLog:
* diagnostic.h: Include "rich-location.h".
* edit-context.h (class fixit_hint): New forward decl.
* gcc-rich-location.h: Include "rich-location.h".
* genmatch.cc: Likewise.
* pretty-print.h: Likewise.
gcc/rust/ChangeLog:
* rust-location.h: Include "rich-location.h".
libcpp/ChangeLog:
* Makefile.in (TAGS_SOURCES): Add "include/rich-location.h".
* include/cpplib.h (class rich_location): New forward decl.
* include/line-map.h (class range_label)
(enum range_display_kind, struct location_range)
(class semi_embedded_vec, class rich_location, class label_text)
(class range_label, class fixit_hint): Move to...
* include/rich-location.h: ...this new file.
* internal.h: Include "rich-location.h".
Signed-off-by: David Malcolm <dmalcolm@redhat.com>
Diffstat (limited to 'libcpp')
-rw-r--r-- | libcpp/Makefile.in | 4 | ||||
-rw-r--r-- | libcpp/include/cpplib.h | 2 | ||||
-rw-r--r-- | libcpp/include/line-map.h | 671 | ||||
-rw-r--r-- | libcpp/include/rich-location.h | 695 | ||||
-rw-r--r-- | libcpp/internal.h | 1 |
5 files changed, 701 insertions, 672 deletions
diff --git a/libcpp/Makefile.in b/libcpp/Makefile.in index faf6834..1e063a5 100644 --- a/libcpp/Makefile.in +++ b/libcpp/Makefile.in @@ -270,7 +270,9 @@ po/$(PACKAGE).pot: $(libcpp_a_SOURCES) ETAGS = @ETAGS@ TAGS_SOURCES = $(libcpp_a_SOURCES) internal.h system.h ucnid.h \ - include/cpplib.h include/line-map.h include/mkdeps.h include/symtab.h + include/cpplib.h include/line-map.h include/mkdeps.h include/symtab.h \ + include/rich-location.h + TAGS: $(TAGS_SOURCES) cd $(srcdir) && $(ETAGS) $(TAGS_SOURCES) diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h index 54814f3..f857ffa 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -38,6 +38,8 @@ typedef struct cpp_dir cpp_dir; struct _cpp_file; +class rich_location; + /* The first three groups, apart from '=', can appear in preprocessor expressions (+= and -= are used to indicate unary + and - resp.). This allows a lookup table to be implemented in _cpp_parse_expr. diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h index 615cb42..72db310 100644 --- a/libcpp/include/line-map.h +++ b/libcpp/include/line-map.h @@ -1275,677 +1275,6 @@ typedef struct bool sysp; } expanded_location; -class range_label; - -/* A hint to diagnostic_show_locus on how to print a source range within a - rich_location. - - Typically this is SHOW_RANGE_WITH_CARET for the 0th range, and - SHOW_RANGE_WITHOUT_CARET for subsequent ranges, - but the Fortran frontend uses SHOW_RANGE_WITH_CARET repeatedly for - printing things like: - - x = x + y - 1 2 - Error: Shapes for operands at (1) and (2) are not conformable - - where "1" and "2" are notionally carets. */ - -enum range_display_kind -{ - /* Show the pertinent source line(s), the caret, and underline(s). */ - SHOW_RANGE_WITH_CARET, - - /* Show the pertinent source line(s) and underline(s), but don't - show the caret (just an underline). */ - SHOW_RANGE_WITHOUT_CARET, - - /* Just show the source lines; don't show the range itself. - This is for use when displaying some line-insertion fix-it hints (for - showing the user context on the change, for when it doesn't make sense - to highlight the first column on the next line). */ - SHOW_LINES_WITHOUT_RANGE -}; - -/* A location within a rich_location: a caret&range, with - the caret potentially flagged for display, and an optional - label. */ - -struct location_range -{ - location_t m_loc; - - enum range_display_kind m_range_display_kind; - - /* If non-NULL, the label for this range. */ - const range_label *m_label; -}; - -/* A partially-embedded vec for use within rich_location for storing - ranges and fix-it hints. - - Elements [0..NUM_EMBEDDED) are allocated within m_embed, after - that they are within the dynamically-allocated m_extra. - - This allows for static allocation in the common case, whilst - supporting the rarer case of an arbitrary number of elements. - - Dynamic allocation is not performed unless it's needed. */ - -template <typename T, int NUM_EMBEDDED> -class semi_embedded_vec -{ - public: - semi_embedded_vec (); - ~semi_embedded_vec (); - - unsigned int count () const { return m_num; } - T& operator[] (int idx); - const T& operator[] (int idx) const; - - void push (const T&); - void truncate (int len); - - private: - int m_num; - T m_embedded[NUM_EMBEDDED]; - int m_alloc; - T *m_extra; -}; - -/* Constructor for semi_embedded_vec. In particular, no dynamic allocation - is done. */ - -template <typename T, int NUM_EMBEDDED> -semi_embedded_vec<T, NUM_EMBEDDED>::semi_embedded_vec () -: m_num (0), m_alloc (0), m_extra (NULL) -{ -} - -/* semi_embedded_vec's dtor. Release any dynamically-allocated memory. */ - -template <typename T, int NUM_EMBEDDED> -semi_embedded_vec<T, NUM_EMBEDDED>::~semi_embedded_vec () -{ - XDELETEVEC (m_extra); -} - -/* Look up element IDX, mutably. */ - -template <typename T, int NUM_EMBEDDED> -T& -semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx) -{ - linemap_assert (idx < m_num); - if (idx < NUM_EMBEDDED) - return m_embedded[idx]; - else - { - linemap_assert (m_extra != NULL); - return m_extra[idx - NUM_EMBEDDED]; - } -} - -/* Look up element IDX (const). */ - -template <typename T, int NUM_EMBEDDED> -const T& -semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx) const -{ - linemap_assert (idx < m_num); - if (idx < NUM_EMBEDDED) - return m_embedded[idx]; - else - { - linemap_assert (m_extra != NULL); - return m_extra[idx - NUM_EMBEDDED]; - } -} - -/* Append VALUE to the end of the semi_embedded_vec. */ - -template <typename T, int NUM_EMBEDDED> -void -semi_embedded_vec<T, NUM_EMBEDDED>::push (const T& value) -{ - int idx = m_num++; - if (idx < NUM_EMBEDDED) - m_embedded[idx] = value; - else - { - /* Offset "idx" to be an index within m_extra. */ - idx -= NUM_EMBEDDED; - if (NULL == m_extra) - { - linemap_assert (m_alloc == 0); - m_alloc = 16; - m_extra = XNEWVEC (T, m_alloc); - } - else if (idx >= m_alloc) - { - linemap_assert (m_alloc > 0); - m_alloc *= 2; - m_extra = XRESIZEVEC (T, m_extra, m_alloc); - } - linemap_assert (m_extra); - linemap_assert (idx < m_alloc); - m_extra[idx] = value; - } -} - -/* Truncate to length LEN. No deallocation is performed. */ - -template <typename T, int NUM_EMBEDDED> -void -semi_embedded_vec<T, NUM_EMBEDDED>::truncate (int len) -{ - linemap_assert (len <= m_num); - m_num = len; -} - -class fixit_hint; -class diagnostic_path; - -/* A "rich" source code location, for use when printing diagnostics. - A rich_location has one or more carets&ranges, where the carets - are optional. These are referred to as "ranges" from here. - Typically the zeroth range has a caret; other ranges sometimes - have carets. - - The "primary" location of a rich_location is the caret of range 0, - used for determining the line/column when printing diagnostic - text, such as: - - some-file.c:3:1: error: ...etc... - - Additional ranges may be added to help the user identify other - pertinent clauses in a diagnostic. - - Ranges can (optionally) be given labels via class range_label. - - rich_location instances are intended to be allocated on the stack - when generating diagnostics, and to be short-lived. - - Examples of rich locations - -------------------------- - - Example A - ********* - int i = "foo"; - ^ - This "rich" location is simply a single range (range 0), with - caret = start = finish at the given point. - - Example B - ********* - a = (foo && bar) - ~~~~~^~~~~~~ - This rich location has a single range (range 0), with the caret - at the first "&", and the start/finish at the parentheses. - Compare with example C below. - - Example C - ********* - a = (foo && bar) - ~~~ ^~ ~~~ - This rich location has three ranges: - - Range 0 has its caret and start location at the first "&" and - end at the second "&. - - Range 1 has its start and finish at the "f" and "o" of "foo"; - the caret is not flagged for display, but is perhaps at the "f" - of "foo". - - Similarly, range 2 has its start and finish at the "b" and "r" of - "bar"; the caret is not flagged for display, but is perhaps at the - "b" of "bar". - Compare with example B above. - - Example D (Fortran frontend) - **************************** - x = x + y - 1 2 - This rich location has range 0 at "1", and range 1 at "2". - Both are flagged for caret display. Both ranges have start/finish - equal to their caret point. The frontend overrides the diagnostic - context's default caret character for these ranges. - - Example E (range labels) - ************************ - printf ("arg0: %i arg1: %s arg2: %i", - ^~ - | - const char * - 100, 101, 102); - ~~~ - | - int - This rich location has two ranges: - - range 0 is at the "%s" with start = caret = "%" and finish at - the "s". It has a range_label ("const char *"). - - range 1 has start/finish covering the "101" and is not flagged for - caret printing. The caret is at the start of "101", where its - range_label is printed ("int"). - - Fix-it hints - ------------ - - Rich locations can also contain "fix-it hints", giving suggestions - for the user on how to edit their code to fix a problem. These - can be expressed as insertions, replacements, and removals of text. - The edits by default are relative to the zeroth range within the - rich_location, but optionally they can be expressed relative to - other locations (using various overloaded methods of the form - rich_location::add_fixit_*). - - For example: - - Example F: fix-it hint: insert_before - ************************************* - ptr = arr[0]; - ^~~~~~ - & - This rich location has a single range (range 0) covering "arr[0]", - with the caret at the start. The rich location has a single - insertion fix-it hint, inserted before range 0, added via - richloc.add_fixit_insert_before ("&"); - - Example G: multiple fix-it hints: insert_before and insert_after - **************************************************************** - #define FN(ARG0, ARG1, ARG2) fn(ARG0, ARG1, ARG2) - ^~~~ ^~~~ ^~~~ - ( ) ( ) ( ) - This rich location has three ranges, covering "arg0", "arg1", - and "arg2", all with caret-printing enabled. - The rich location has 6 insertion fix-it hints: each arg - has a pair of insertion fix-it hints, suggesting wrapping - them with parentheses: one a '(' inserted before, - the other a ')' inserted after, added via - richloc.add_fixit_insert_before (LOC, "("); - and - richloc.add_fixit_insert_after (LOC, ")"); - - Example H: fix-it hint: removal - ******************************* - struct s {int i};; - ^ - - - This rich location has a single range at the stray trailing - semicolon, along with a single removal fix-it hint, covering - the same range, added via: - richloc.add_fixit_remove (); - - Example I: fix-it hint: replace - ******************************* - c = s.colour; - ^~~~~~ - color - This rich location has a single range (range 0) covering "colour", - and a single "replace" fix-it hint, covering the same range, - added via - richloc.add_fixit_replace ("color"); - - Example J: fix-it hint: line insertion - ************************************** - - 3 | #include <stddef.h> - + |+#include <stdio.h> - 4 | int the_next_line; - - This rich location has a single range at line 4 column 1, marked - with SHOW_LINES_WITHOUT_RANGE (to avoid printing a meaningless caret - on the "i" of int). It has a insertion fix-it hint of the string - "#include <stdio.h>\n". - - Adding a fix-it hint can fail: for example, attempts to insert content - at the transition between two line maps may fail due to there being no - location_t value to express the new location. - - Attempts to add a fix-it hint within a macro expansion will fail. - - There is only limited support for newline characters in fix-it hints: - only hints with newlines which insert an entire new line are permitted, - inserting at the start of a line, and finishing with a newline - (with no interior newline characters). Other attempts to add - fix-it hints containing newline characters will fail. - Similarly, attempts to delete or replace a range *affecting* multiple - lines will fail. - - The rich_location API handles these failures gracefully, so that - diagnostics can attempt to add fix-it hints without each needing - extensive checking. - - Fix-it hints within a rich_location are "atomic": if any hints can't - be applied, none of them will be (tracked by the m_seen_impossible_fixit - flag), and no fix-its hints will be displayed for that rich_location. - This implies that diagnostic messages need to be worded in such a way - that they make sense whether or not the fix-it hints are displayed, - or that richloc.seen_impossible_fixit_p () should be checked before - issuing the diagnostics. */ - -class rich_location -{ - public: - /* Constructors. */ - - /* Constructing from a location. */ - rich_location (line_maps *set, location_t loc, - const range_label *label = NULL); - - /* Destructor. */ - ~rich_location (); - - /* The class manages the memory pointed to by the elements of - the M_FIXIT_HINTS vector and is not meant to be copied or - assigned. */ - rich_location (const rich_location &) = delete; - void operator= (const rich_location &) = delete; - - /* Accessors. */ - location_t get_loc () const { return get_loc (0); } - location_t get_loc (unsigned int idx) const; - - void - add_range (location_t loc, - enum range_display_kind range_display_kind - = SHOW_RANGE_WITHOUT_CARET, - const range_label *label = NULL); - - void - set_range (unsigned int idx, location_t loc, - enum range_display_kind range_display_kind); - - unsigned int get_num_locations () const { return m_ranges.count (); } - - const location_range *get_range (unsigned int idx) const; - location_range *get_range (unsigned int idx); - - expanded_location get_expanded_location (unsigned int idx) const; - - void - override_column (int column); - - /* Fix-it hints. */ - - /* Methods for adding insertion fix-it hints. */ - - /* Suggest inserting NEW_CONTENT immediately before the primary - range's start. */ - void - add_fixit_insert_before (const char *new_content); - - /* Suggest inserting NEW_CONTENT immediately before the start of WHERE. */ - void - add_fixit_insert_before (location_t where, - const char *new_content); - - /* Suggest inserting NEW_CONTENT immediately after the end of the primary - range. */ - void - add_fixit_insert_after (const char *new_content); - - /* Suggest inserting NEW_CONTENT immediately after the end of WHERE. */ - void - add_fixit_insert_after (location_t where, - const char *new_content); - - /* Methods for adding removal fix-it hints. */ - - /* Suggest removing the content covered by range 0. */ - void - add_fixit_remove (); - - /* Suggest removing the content covered between the start and finish - of WHERE. */ - void - add_fixit_remove (location_t where); - - /* Suggest removing the content covered by SRC_RANGE. */ - void - add_fixit_remove (source_range src_range); - - /* Methods for adding "replace" fix-it hints. */ - - /* Suggest replacing the content covered by range 0 with NEW_CONTENT. */ - void - add_fixit_replace (const char *new_content); - - /* Suggest replacing the content between the start and finish of - WHERE with NEW_CONTENT. */ - void - add_fixit_replace (location_t where, - const char *new_content); - - /* Suggest replacing the content covered by SRC_RANGE with - NEW_CONTENT. */ - void - add_fixit_replace (source_range src_range, - const char *new_content); - - unsigned int get_num_fixit_hints () const { return m_fixit_hints.count (); } - fixit_hint *get_fixit_hint (int idx) const { return m_fixit_hints[idx]; } - fixit_hint *get_last_fixit_hint () const; - bool seen_impossible_fixit_p () const { return m_seen_impossible_fixit; } - - /* Set this if the fix-it hints are not suitable to be - automatically applied. - - For example, if you are suggesting more than one - mutually exclusive solution to a problem, then - it doesn't make sense to apply all of the solutions; - manual intervention is required. - - If set, then the fix-it hints in the rich_location will - be printed, but will not be added to generated patches, - or affect the modified version of the file. */ - void fixits_cannot_be_auto_applied () - { - m_fixits_cannot_be_auto_applied = true; - } - - bool fixits_can_be_auto_applied_p () const - { - return !m_fixits_cannot_be_auto_applied; - } - - /* An optional path through the code. */ - const diagnostic_path *get_path () const { return m_path; } - void set_path (const diagnostic_path *path) { m_path = path; } - - /* A flag for hinting that the diagnostic involves character encoding - issues, and thus that it will be helpful to the user if we show some - representation of how the characters in the pertinent source lines - are encoded. - The default is false (i.e. do not escape). - When set to true, non-ASCII bytes in the pertinent source lines will - be escaped in a manner controlled by the user-supplied option - -fdiagnostics-escape-format=, so that the user can better understand - what's going on with the encoding in their source file. */ - bool escape_on_output_p () const { return m_escape_on_output; } - void set_escape_on_output (bool flag) { m_escape_on_output = flag; } - - const line_maps *get_line_table () const { return m_line_table; } - -private: - bool reject_impossible_fixit (location_t where); - void stop_supporting_fixits (); - void maybe_add_fixit (location_t start, - location_t next_loc, - const char *new_content); - -public: - static const int STATICALLY_ALLOCATED_RANGES = 3; - -protected: - line_maps * const m_line_table; - semi_embedded_vec <location_range, STATICALLY_ALLOCATED_RANGES> m_ranges; - - int m_column_override; - - mutable bool m_have_expanded_location; - bool m_seen_impossible_fixit; - bool m_fixits_cannot_be_auto_applied; - bool m_escape_on_output; - - mutable expanded_location m_expanded_location; - - static const int MAX_STATIC_FIXIT_HINTS = 2; - semi_embedded_vec <fixit_hint *, MAX_STATIC_FIXIT_HINTS> m_fixit_hints; - - const diagnostic_path *m_path; -}; - -/* A struct for the result of range_label::get_text: a NUL-terminated buffer - of localized text, and a flag to determine if the caller should "free" the - buffer. */ - -class label_text -{ -public: - label_text () - : m_buffer (NULL), m_owned (false) - {} - - ~label_text () - { - if (m_owned) - free (m_buffer); - } - - /* Move ctor. */ - label_text (label_text &&other) - : m_buffer (other.m_buffer), m_owned (other.m_owned) - { - other.release (); - } - - /* Move assignment. */ - label_text & operator= (label_text &&other) - { - if (m_owned) - free (m_buffer); - m_buffer = other.m_buffer; - m_owned = other.m_owned; - other.release (); - return *this; - } - - /* Delete the copy ctor and copy-assignment operator. */ - label_text (const label_text &) = delete; - label_text & operator= (const label_text &) = delete; - - /* Create a label_text instance that borrows BUFFER from a - longer-lived owner. */ - static label_text borrow (const char *buffer) - { - return label_text (const_cast <char *> (buffer), false); - } - - /* Create a label_text instance that takes ownership of BUFFER. */ - static label_text take (char *buffer) - { - return label_text (buffer, true); - } - - void release () - { - m_buffer = NULL; - m_owned = false; - } - - const char *get () const - { - return m_buffer; - } - - bool is_owner () const - { - return m_owned; - } - -private: - char *m_buffer; - bool m_owned; - - label_text (char *buffer, bool owned) - : m_buffer (buffer), m_owned (owned) - {} -}; - -/* Abstract base class for labelling a range within a rich_location - (e.g. for labelling expressions with their type). - - Generating the text could require non-trivial work, so this work - is delayed (via the "get_text" virtual function) until the diagnostic - printing code "knows" it needs it, thus avoiding doing it e.g. for - warnings that are filtered by command-line flags. This virtual - function also isolates libcpp and the diagnostics subsystem from - the front-end and middle-end-specific code for generating the text - for the labels. - - Like the rich_location instances they annotate, range_label instances - are intended to be allocated on the stack when generating diagnostics, - and to be short-lived. */ - -class range_label -{ - public: - virtual ~range_label () {} - - /* Get localized text for the label. - The RANGE_IDX is provided, allowing for range_label instances to be - shared by multiple ranges if need be (the "flyweight" design pattern). */ - virtual label_text get_text (unsigned range_idx) const = 0; -}; - -/* A fix-it hint: a suggested insertion, replacement, or deletion of text. - We handle these three types of edit with one class, by representing - them as replacement of a half-open range: - [start, next_loc) - Insertions have start == next_loc: "replace" the empty string at the - start location with the new string. - Deletions are replacement with the empty string. - - There is only limited support for newline characters in fix-it hints - as noted above in the comment for class rich_location. - A fixit_hint instance can have at most one newline character; if - present, the newline character must be the final character of - the content (preventing e.g. fix-its that split a pre-existing line). */ - -class fixit_hint -{ - public: - fixit_hint (location_t start, - location_t next_loc, - const char *new_content); - ~fixit_hint () { free (m_bytes); } - - bool affects_line_p (const line_maps *set, - const char *file, - int line) const; - location_t get_start_loc () const { return m_start; } - location_t get_next_loc () const { return m_next_loc; } - bool maybe_append (location_t start, - location_t next_loc, - const char *new_content); - - const char *get_string () const { return m_bytes; } - size_t get_length () const { return m_len; } - - bool insertion_p () const { return m_start == m_next_loc; } - - bool ends_with_newline_p () const; - - private: - /* We don't use source_range here since, unlike most places, - this is a half-open/half-closed range: - [start, next_loc) - so that we can support insertion via start == next_loc. */ - location_t m_start; - location_t m_next_loc; - char *m_bytes; - size_t m_len; -}; - - /* This is enum is used by the function linemap_resolve_location below. The meaning of the values is explained in the comment of that function. */ diff --git a/libcpp/include/rich-location.h b/libcpp/include/rich-location.h new file mode 100644 index 0000000..878c71c --- /dev/null +++ b/libcpp/include/rich-location.h @@ -0,0 +1,695 @@ +/* Bundles of location information used when printing diagnostics. + Copyright (C) 2015-2023 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! */ + +#ifndef LIBCPP_RICH_LOCATION_H +#define LIBCPP_RICH_LOCATION_H + +class range_label; + +/* A hint to diagnostic_show_locus on how to print a source range within a + rich_location. + + Typically this is SHOW_RANGE_WITH_CARET for the 0th range, and + SHOW_RANGE_WITHOUT_CARET for subsequent ranges, + but the Fortran frontend uses SHOW_RANGE_WITH_CARET repeatedly for + printing things like: + + x = x + y + 1 2 + Error: Shapes for operands at (1) and (2) are not conformable + + where "1" and "2" are notionally carets. */ + +enum range_display_kind +{ + /* Show the pertinent source line(s), the caret, and underline(s). */ + SHOW_RANGE_WITH_CARET, + + /* Show the pertinent source line(s) and underline(s), but don't + show the caret (just an underline). */ + SHOW_RANGE_WITHOUT_CARET, + + /* Just show the source lines; don't show the range itself. + This is for use when displaying some line-insertion fix-it hints (for + showing the user context on the change, for when it doesn't make sense + to highlight the first column on the next line). */ + SHOW_LINES_WITHOUT_RANGE +}; + +/* A location within a rich_location: a caret&range, with + the caret potentially flagged for display, and an optional + label. */ + +struct location_range +{ + location_t m_loc; + + enum range_display_kind m_range_display_kind; + + /* If non-NULL, the label for this range. */ + const range_label *m_label; +}; + +/* A partially-embedded vec for use within rich_location for storing + ranges and fix-it hints. + + Elements [0..NUM_EMBEDDED) are allocated within m_embed, after + that they are within the dynamically-allocated m_extra. + + This allows for static allocation in the common case, whilst + supporting the rarer case of an arbitrary number of elements. + + Dynamic allocation is not performed unless it's needed. */ + +template <typename T, int NUM_EMBEDDED> +class semi_embedded_vec +{ + public: + semi_embedded_vec (); + ~semi_embedded_vec (); + + unsigned int count () const { return m_num; } + T& operator[] (int idx); + const T& operator[] (int idx) const; + + void push (const T&); + void truncate (int len); + + private: + int m_num; + T m_embedded[NUM_EMBEDDED]; + int m_alloc; + T *m_extra; +}; + +/* Constructor for semi_embedded_vec. In particular, no dynamic allocation + is done. */ + +template <typename T, int NUM_EMBEDDED> +semi_embedded_vec<T, NUM_EMBEDDED>::semi_embedded_vec () +: m_num (0), m_alloc (0), m_extra (NULL) +{ +} + +/* semi_embedded_vec's dtor. Release any dynamically-allocated memory. */ + +template <typename T, int NUM_EMBEDDED> +semi_embedded_vec<T, NUM_EMBEDDED>::~semi_embedded_vec () +{ + XDELETEVEC (m_extra); +} + +/* Look up element IDX, mutably. */ + +template <typename T, int NUM_EMBEDDED> +T& +semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx) +{ + linemap_assert (idx < m_num); + if (idx < NUM_EMBEDDED) + return m_embedded[idx]; + else + { + linemap_assert (m_extra != NULL); + return m_extra[idx - NUM_EMBEDDED]; + } +} + +/* Look up element IDX (const). */ + +template <typename T, int NUM_EMBEDDED> +const T& +semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx) const +{ + linemap_assert (idx < m_num); + if (idx < NUM_EMBEDDED) + return m_embedded[idx]; + else + { + linemap_assert (m_extra != NULL); + return m_extra[idx - NUM_EMBEDDED]; + } +} + +/* Append VALUE to the end of the semi_embedded_vec. */ + +template <typename T, int NUM_EMBEDDED> +void +semi_embedded_vec<T, NUM_EMBEDDED>::push (const T& value) +{ + int idx = m_num++; + if (idx < NUM_EMBEDDED) + m_embedded[idx] = value; + else + { + /* Offset "idx" to be an index within m_extra. */ + idx -= NUM_EMBEDDED; + if (NULL == m_extra) + { + linemap_assert (m_alloc == 0); + m_alloc = 16; + m_extra = XNEWVEC (T, m_alloc); + } + else if (idx >= m_alloc) + { + linemap_assert (m_alloc > 0); + m_alloc *= 2; + m_extra = XRESIZEVEC (T, m_extra, m_alloc); + } + linemap_assert (m_extra); + linemap_assert (idx < m_alloc); + m_extra[idx] = value; + } +} + +/* Truncate to length LEN. No deallocation is performed. */ + +template <typename T, int NUM_EMBEDDED> +void +semi_embedded_vec<T, NUM_EMBEDDED>::truncate (int len) +{ + linemap_assert (len <= m_num); + m_num = len; +} + +class fixit_hint; +class diagnostic_path; + +/* A "rich" source code location, for use when printing diagnostics. + A rich_location has one or more carets&ranges, where the carets + are optional. These are referred to as "ranges" from here. + Typically the zeroth range has a caret; other ranges sometimes + have carets. + + The "primary" location of a rich_location is the caret of range 0, + used for determining the line/column when printing diagnostic + text, such as: + + some-file.c:3:1: error: ...etc... + + Additional ranges may be added to help the user identify other + pertinent clauses in a diagnostic. + + Ranges can (optionally) be given labels via class range_label. + + rich_location instances are intended to be allocated on the stack + when generating diagnostics, and to be short-lived. + + Examples of rich locations + -------------------------- + + Example A + ********* + int i = "foo"; + ^ + This "rich" location is simply a single range (range 0), with + caret = start = finish at the given point. + + Example B + ********* + a = (foo && bar) + ~~~~~^~~~~~~ + This rich location has a single range (range 0), with the caret + at the first "&", and the start/finish at the parentheses. + Compare with example C below. + + Example C + ********* + a = (foo && bar) + ~~~ ^~ ~~~ + This rich location has three ranges: + - Range 0 has its caret and start location at the first "&" and + end at the second "&. + - Range 1 has its start and finish at the "f" and "o" of "foo"; + the caret is not flagged for display, but is perhaps at the "f" + of "foo". + - Similarly, range 2 has its start and finish at the "b" and "r" of + "bar"; the caret is not flagged for display, but is perhaps at the + "b" of "bar". + Compare with example B above. + + Example D (Fortran frontend) + **************************** + x = x + y + 1 2 + This rich location has range 0 at "1", and range 1 at "2". + Both are flagged for caret display. Both ranges have start/finish + equal to their caret point. The frontend overrides the diagnostic + context's default caret character for these ranges. + + Example E (range labels) + ************************ + printf ("arg0: %i arg1: %s arg2: %i", + ^~ + | + const char * + 100, 101, 102); + ~~~ + | + int + This rich location has two ranges: + - range 0 is at the "%s" with start = caret = "%" and finish at + the "s". It has a range_label ("const char *"). + - range 1 has start/finish covering the "101" and is not flagged for + caret printing. The caret is at the start of "101", where its + range_label is printed ("int"). + + Fix-it hints + ------------ + + Rich locations can also contain "fix-it hints", giving suggestions + for the user on how to edit their code to fix a problem. These + can be expressed as insertions, replacements, and removals of text. + The edits by default are relative to the zeroth range within the + rich_location, but optionally they can be expressed relative to + other locations (using various overloaded methods of the form + rich_location::add_fixit_*). + + For example: + + Example F: fix-it hint: insert_before + ************************************* + ptr = arr[0]; + ^~~~~~ + & + This rich location has a single range (range 0) covering "arr[0]", + with the caret at the start. The rich location has a single + insertion fix-it hint, inserted before range 0, added via + richloc.add_fixit_insert_before ("&"); + + Example G: multiple fix-it hints: insert_before and insert_after + **************************************************************** + #define FN(ARG0, ARG1, ARG2) fn(ARG0, ARG1, ARG2) + ^~~~ ^~~~ ^~~~ + ( ) ( ) ( ) + This rich location has three ranges, covering "arg0", "arg1", + and "arg2", all with caret-printing enabled. + The rich location has 6 insertion fix-it hints: each arg + has a pair of insertion fix-it hints, suggesting wrapping + them with parentheses: one a '(' inserted before, + the other a ')' inserted after, added via + richloc.add_fixit_insert_before (LOC, "("); + and + richloc.add_fixit_insert_after (LOC, ")"); + + Example H: fix-it hint: removal + ******************************* + struct s {int i};; + ^ + - + This rich location has a single range at the stray trailing + semicolon, along with a single removal fix-it hint, covering + the same range, added via: + richloc.add_fixit_remove (); + + Example I: fix-it hint: replace + ******************************* + c = s.colour; + ^~~~~~ + color + This rich location has a single range (range 0) covering "colour", + and a single "replace" fix-it hint, covering the same range, + added via + richloc.add_fixit_replace ("color"); + + Example J: fix-it hint: line insertion + ************************************** + + 3 | #include <stddef.h> + + |+#include <stdio.h> + 4 | int the_next_line; + + This rich location has a single range at line 4 column 1, marked + with SHOW_LINES_WITHOUT_RANGE (to avoid printing a meaningless caret + on the "i" of int). It has a insertion fix-it hint of the string + "#include <stdio.h>\n". + + Adding a fix-it hint can fail: for example, attempts to insert content + at the transition between two line maps may fail due to there being no + location_t value to express the new location. + + Attempts to add a fix-it hint within a macro expansion will fail. + + There is only limited support for newline characters in fix-it hints: + only hints with newlines which insert an entire new line are permitted, + inserting at the start of a line, and finishing with a newline + (with no interior newline characters). Other attempts to add + fix-it hints containing newline characters will fail. + Similarly, attempts to delete or replace a range *affecting* multiple + lines will fail. + + The rich_location API handles these failures gracefully, so that + diagnostics can attempt to add fix-it hints without each needing + extensive checking. + + Fix-it hints within a rich_location are "atomic": if any hints can't + be applied, none of them will be (tracked by the m_seen_impossible_fixit + flag), and no fix-its hints will be displayed for that rich_location. + This implies that diagnostic messages need to be worded in such a way + that they make sense whether or not the fix-it hints are displayed, + or that richloc.seen_impossible_fixit_p () should be checked before + issuing the diagnostics. */ + +class rich_location +{ + public: + /* Constructors. */ + + /* Constructing from a location. */ + rich_location (line_maps *set, location_t loc, + const range_label *label = NULL); + + /* Destructor. */ + ~rich_location (); + + /* The class manages the memory pointed to by the elements of + the M_FIXIT_HINTS vector and is not meant to be copied or + assigned. */ + rich_location (const rich_location &) = delete; + void operator= (const rich_location &) = delete; + + /* Accessors. */ + location_t get_loc () const { return get_loc (0); } + location_t get_loc (unsigned int idx) const; + + void + add_range (location_t loc, + enum range_display_kind range_display_kind + = SHOW_RANGE_WITHOUT_CARET, + const range_label *label = NULL); + + void + set_range (unsigned int idx, location_t loc, + enum range_display_kind range_display_kind); + + unsigned int get_num_locations () const { return m_ranges.count (); } + + const location_range *get_range (unsigned int idx) const; + location_range *get_range (unsigned int idx); + + expanded_location get_expanded_location (unsigned int idx) const; + + void + override_column (int column); + + /* Fix-it hints. */ + + /* Methods for adding insertion fix-it hints. */ + + /* Suggest inserting NEW_CONTENT immediately before the primary + range's start. */ + void + add_fixit_insert_before (const char *new_content); + + /* Suggest inserting NEW_CONTENT immediately before the start of WHERE. */ + void + add_fixit_insert_before (location_t where, + const char *new_content); + + /* Suggest inserting NEW_CONTENT immediately after the end of the primary + range. */ + void + add_fixit_insert_after (const char *new_content); + + /* Suggest inserting NEW_CONTENT immediately after the end of WHERE. */ + void + add_fixit_insert_after (location_t where, + const char *new_content); + + /* Methods for adding removal fix-it hints. */ + + /* Suggest removing the content covered by range 0. */ + void + add_fixit_remove (); + + /* Suggest removing the content covered between the start and finish + of WHERE. */ + void + add_fixit_remove (location_t where); + + /* Suggest removing the content covered by SRC_RANGE. */ + void + add_fixit_remove (source_range src_range); + + /* Methods for adding "replace" fix-it hints. */ + + /* Suggest replacing the content covered by range 0 with NEW_CONTENT. */ + void + add_fixit_replace (const char *new_content); + + /* Suggest replacing the content between the start and finish of + WHERE with NEW_CONTENT. */ + void + add_fixit_replace (location_t where, + const char *new_content); + + /* Suggest replacing the content covered by SRC_RANGE with + NEW_CONTENT. */ + void + add_fixit_replace (source_range src_range, + const char *new_content); + + unsigned int get_num_fixit_hints () const { return m_fixit_hints.count (); } + fixit_hint *get_fixit_hint (int idx) const { return m_fixit_hints[idx]; } + fixit_hint *get_last_fixit_hint () const; + bool seen_impossible_fixit_p () const { return m_seen_impossible_fixit; } + + /* Set this if the fix-it hints are not suitable to be + automatically applied. + + For example, if you are suggesting more than one + mutually exclusive solution to a problem, then + it doesn't make sense to apply all of the solutions; + manual intervention is required. + + If set, then the fix-it hints in the rich_location will + be printed, but will not be added to generated patches, + or affect the modified version of the file. */ + void fixits_cannot_be_auto_applied () + { + m_fixits_cannot_be_auto_applied = true; + } + + bool fixits_can_be_auto_applied_p () const + { + return !m_fixits_cannot_be_auto_applied; + } + + /* An optional path through the code. */ + const diagnostic_path *get_path () const { return m_path; } + void set_path (const diagnostic_path *path) { m_path = path; } + + /* A flag for hinting that the diagnostic involves character encoding + issues, and thus that it will be helpful to the user if we show some + representation of how the characters in the pertinent source lines + are encoded. + The default is false (i.e. do not escape). + When set to true, non-ASCII bytes in the pertinent source lines will + be escaped in a manner controlled by the user-supplied option + -fdiagnostics-escape-format=, so that the user can better understand + what's going on with the encoding in their source file. */ + bool escape_on_output_p () const { return m_escape_on_output; } + void set_escape_on_output (bool flag) { m_escape_on_output = flag; } + + const line_maps *get_line_table () const { return m_line_table; } + +private: + bool reject_impossible_fixit (location_t where); + void stop_supporting_fixits (); + void maybe_add_fixit (location_t start, + location_t next_loc, + const char *new_content); + +public: + static const int STATICALLY_ALLOCATED_RANGES = 3; + +protected: + line_maps * const m_line_table; + semi_embedded_vec <location_range, STATICALLY_ALLOCATED_RANGES> m_ranges; + + int m_column_override; + + mutable bool m_have_expanded_location; + bool m_seen_impossible_fixit; + bool m_fixits_cannot_be_auto_applied; + bool m_escape_on_output; + + mutable expanded_location m_expanded_location; + + static const int MAX_STATIC_FIXIT_HINTS = 2; + semi_embedded_vec <fixit_hint *, MAX_STATIC_FIXIT_HINTS> m_fixit_hints; + + const diagnostic_path *m_path; +}; + +/* A struct for the result of range_label::get_text: a NUL-terminated buffer + of localized text, and a flag to determine if the caller should "free" the + buffer. */ + +class label_text +{ +public: + label_text () + : m_buffer (NULL), m_owned (false) + {} + + ~label_text () + { + if (m_owned) + free (m_buffer); + } + + /* Move ctor. */ + label_text (label_text &&other) + : m_buffer (other.m_buffer), m_owned (other.m_owned) + { + other.release (); + } + + /* Move assignment. */ + label_text & operator= (label_text &&other) + { + if (m_owned) + free (m_buffer); + m_buffer = other.m_buffer; + m_owned = other.m_owned; + other.release (); + return *this; + } + + /* Delete the copy ctor and copy-assignment operator. */ + label_text (const label_text &) = delete; + label_text & operator= (const label_text &) = delete; + + /* Create a label_text instance that borrows BUFFER from a + longer-lived owner. */ + static label_text borrow (const char *buffer) + { + return label_text (const_cast <char *> (buffer), false); + } + + /* Create a label_text instance that takes ownership of BUFFER. */ + static label_text take (char *buffer) + { + return label_text (buffer, true); + } + + void release () + { + m_buffer = NULL; + m_owned = false; + } + + const char *get () const + { + return m_buffer; + } + + bool is_owner () const + { + return m_owned; + } + +private: + char *m_buffer; + bool m_owned; + + label_text (char *buffer, bool owned) + : m_buffer (buffer), m_owned (owned) + {} +}; + +/* Abstract base class for labelling a range within a rich_location + (e.g. for labelling expressions with their type). + + Generating the text could require non-trivial work, so this work + is delayed (via the "get_text" virtual function) until the diagnostic + printing code "knows" it needs it, thus avoiding doing it e.g. for + warnings that are filtered by command-line flags. This virtual + function also isolates libcpp and the diagnostics subsystem from + the front-end and middle-end-specific code for generating the text + for the labels. + + Like the rich_location instances they annotate, range_label instances + are intended to be allocated on the stack when generating diagnostics, + and to be short-lived. */ + +class range_label +{ + public: + virtual ~range_label () {} + + /* Get localized text for the label. + The RANGE_IDX is provided, allowing for range_label instances to be + shared by multiple ranges if need be (the "flyweight" design pattern). */ + virtual label_text get_text (unsigned range_idx) const = 0; +}; + +/* A fix-it hint: a suggested insertion, replacement, or deletion of text. + We handle these three types of edit with one class, by representing + them as replacement of a half-open range: + [start, next_loc) + Insertions have start == next_loc: "replace" the empty string at the + start location with the new string. + Deletions are replacement with the empty string. + + There is only limited support for newline characters in fix-it hints + as noted above in the comment for class rich_location. + A fixit_hint instance can have at most one newline character; if + present, the newline character must be the final character of + the content (preventing e.g. fix-its that split a pre-existing line). */ + +class fixit_hint +{ + public: + fixit_hint (location_t start, + location_t next_loc, + const char *new_content); + ~fixit_hint () { free (m_bytes); } + + bool affects_line_p (const line_maps *set, + const char *file, + int line) const; + location_t get_start_loc () const { return m_start; } + location_t get_next_loc () const { return m_next_loc; } + bool maybe_append (location_t start, + location_t next_loc, + const char *new_content); + + const char *get_string () const { return m_bytes; } + size_t get_length () const { return m_len; } + + bool insertion_p () const { return m_start == m_next_loc; } + + bool ends_with_newline_p () const; + + private: + /* We don't use source_range here since, unlike most places, + this is a half-open/half-closed range: + [start, next_loc) + so that we can support insertion via start == next_loc. */ + location_t m_start; + location_t m_next_loc; + char *m_bytes; + size_t m_len; +}; + +#endif /* !LIBCPP_RICH_LOCATION_H */ diff --git a/libcpp/internal.h b/libcpp/internal.h index 6a10e9d..134fc33 100644 --- a/libcpp/internal.h +++ b/libcpp/internal.h @@ -24,6 +24,7 @@ along with this program; see the file COPYING3. If not see #include "symtab.h" #include "cpplib.h" +#include "rich-location.h" #if HAVE_ICONV #include <iconv.h> |