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>
This commit is contained in:
parent
af7fa3135b
commit
78d132d73e
16 changed files with 713 additions and 672 deletions
|
@ -1038,6 +1038,7 @@ SYSTEM_H = system.h hwint.h $(srcdir)/../include/libiberty.h \
|
|||
$(HASHTAB_H)
|
||||
PREDICT_H = predict.h predict.def
|
||||
CPPLIB_H = $(srcdir)/../libcpp/include/line-map.h \
|
||||
$(srcdir)/../libcpp/include/rich-location.h \
|
||||
$(srcdir)/../libcpp/include/cpplib.h
|
||||
CODYLIB_H = $(srcdir)/../libcody/cody.hh
|
||||
INPUT_H = $(srcdir)/../libcpp/include/line-map.h input.h
|
||||
|
|
|
@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#ifndef GCC_ANALYZER_ANALYZER_H
|
||||
#define GCC_ANALYZER_ANALYZER_H
|
||||
|
||||
#include "rich-location.h"
|
||||
#include "function.h"
|
||||
#include "json.h"
|
||||
#include "tristate.h"
|
||||
|
|
|
@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "file-prefix-map.h" /* remap_macro_filename() */
|
||||
#include "langhooks.h"
|
||||
#include "attribs.h"
|
||||
#include "rich-location.h"
|
||||
|
||||
/* We may keep statistics about how long which files took to compile. */
|
||||
static int header_time, body_time;
|
||||
|
|
|
@ -156,6 +156,7 @@ struct cl_optimization;
|
|||
struct cl_option;
|
||||
struct cl_decoded_option;
|
||||
struct cl_option_handlers;
|
||||
class rich_location;
|
||||
class diagnostic_context;
|
||||
class pretty_printer;
|
||||
class diagnostic_event_id_t;
|
||||
|
|
|
@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "system.h"
|
||||
|
||||
#include "line-map.h"
|
||||
#include "rich-location.h"
|
||||
#include "diagnostic-core.h"
|
||||
#include "mapper-client.h"
|
||||
#include "intl.h"
|
||||
|
|
|
@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#ifndef GCC_DIAGNOSTIC_H
|
||||
#define GCC_DIAGNOSTIC_H
|
||||
|
||||
#include "rich-location.h"
|
||||
#include "pretty-print.h"
|
||||
#include "diagnostic-core.h"
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
|
||||
#include "typed-splay-tree.h"
|
||||
|
||||
class fixit_hint;
|
||||
class edit_context;
|
||||
class edited_file;
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ along with GCC; see the file COPYING3. If not see
|
|||
#ifndef GCC_RICH_LOCATION_H
|
||||
#define GCC_RICH_LOCATION_H
|
||||
|
||||
#include "rich-location.h"
|
||||
|
||||
/* A gcc_rich_location is libcpp's rich_location with additional
|
||||
helper methods for working with gcc's types. The class is not
|
||||
copyable or assignable because rich_location isn't. */
|
||||
|
|
|
@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include <cpplib.h>
|
||||
#include "rich-location.h"
|
||||
#include "errors.h"
|
||||
#include "hash-table.h"
|
||||
#include "hash-set.h"
|
||||
|
|
|
@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#define GCC_PRETTY_PRINT_H
|
||||
|
||||
#include "obstack.h"
|
||||
#include "rich-location.h"
|
||||
#include "diagnostic-url.h"
|
||||
|
||||
/* Maximum number of format string arguments. */
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#ifndef RUST_LOCATION_H
|
||||
#define RUST_LOCATION_H
|
||||
|
||||
#include "rich-location.h"
|
||||
#include "rust-system.h"
|
||||
|
||||
// A location in an input source file.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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. */
|
||||
|
|
695
libcpp/include/rich-location.h
Normal file
695
libcpp/include/rich-location.h
Normal file
|
@ -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 */
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue