+2016-08-30 David Malcolm <dmalcolm@redhat.com>
+
+ * diagnostic-show-locus.c (colorizer::begin_state): Support more
+ than 3 ranges per diagnostic by alternating between color 1 and
+ color 2.
+ (layout::layout): Replace use of rich_location::MAX_RANGES
+ with richloc->get_num_locations ().
+ (layout::calculate_line_spans): Replace use of
+ rich_location::MAX_RANGES with m_layout_ranges.length ().
+ (layout::print_annotation_line): Handle arbitrary numbers of
+ ranges in caret-printing by defaulting to '^'.
+ (selftest::test_one_liner_many_fixits): New function.
+ (test_diagnostic_show_locus_one_liner): Call it.
+ * diagnostic.c (diagnostic_initialize): Update for renaming
+ of rich_location::MAX_RANGES to
+ rich_location::STATICALLY_ALLOCATED_RANGES.
+ * diagnostic.h (struct diagnostic_context): Likewise.
+
2016-08-30 David Malcolm <dmalcolm@redhat.com>
* selftest.c (selftest::named_temp_file::named_temp_file): New
break;
default:
- /* We don't expect more than 3 ranges per diagnostic. */
- gcc_unreachable ();
+ /* For ranges beyond 2, alternate between color 1 and color 2. */
+ {
+ gcc_assert (state > 2);
+ pp_string (m_context->printer,
+ state % 2 ? m_range1 : m_range2);
+ }
break;
}
}
m_exploc (richloc->get_expanded_location (0)),
m_colorizer (context, diagnostic_kind),
m_colorize_source_p (context->colorize_source_p),
- m_layout_ranges (rich_location::MAX_RANGES),
- m_line_spans (1 + rich_location::MAX_RANGES),
+ m_layout_ranges (richloc->get_num_locations ()),
+ m_line_spans (1 + richloc->get_num_locations ()),
m_x_offset (0)
{
source_location primary_loc = richloc->get_range (0)->m_loc;
/* Populate tmp_spans with individual spans, for each of
m_exploc, and for m_layout_ranges. */
- auto_vec<line_span> tmp_spans (1 + rich_location::MAX_RANGES);
+ auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
{
/* Within a range. Draw either the caret or an underline. */
m_colorizer.set_range (state.range_idx);
if (state.draw_caret_p)
- /* Draw the caret. */
- pp_character (m_pp, m_context->caret_chars[state.range_idx]);
+ {
+ /* Draw the caret. */
+ char caret_char;
+ if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
+ caret_char = m_context->caret_chars[state.range_idx];
+ else
+ caret_char = '^';
+ pp_character (m_pp, caret_char);
+ }
else
pp_character (m_pp, '~');
}
}
}
+/* Ensure that we can add an arbitrary number of fix-it hints to a
+ rich_location. */
+
+static void
+test_one_liner_many_fixits ()
+{
+ test_diagnostic_context dc;
+ location_t equals = linemap_position_for_column (line_table, 5);
+ rich_location richloc (line_table, equals);
+ for (int i = 0; i < 19; i++)
+ richloc.add_fixit_insert ("a");
+ ASSERT_EQ (19, richloc.get_num_fixit_hints ());
+ diagnostic_show_locus (&dc, &richloc, DK_ERROR);
+ ASSERT_STREQ ("\n"
+ " foo = bar.field;\n"
+ " ^\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n"
+ " a\n",
+ pp_formatted_text (dc.printer));
+}
+
/* Run the various one-liner tests. */
static void
test_one_liner_fixit_replace_non_equal_range ();
test_one_liner_fixit_replace_equal_secondary_range ();
test_one_liner_fixit_validation_adhoc_locations ();
+ test_one_liner_many_fixits ();
}
/* Verify that fix-it hints are appropriately consolidated.
context->classify_diagnostic[i] = DK_UNSPECIFIED;
context->show_caret = false;
diagnostic_set_caret_max_width (context, pp_line_cutoff (context->printer));
- for (i = 0; i < rich_location::MAX_RANGES; i++)
+ for (i = 0; i < rich_location::STATICALLY_ALLOCATED_RANGES; i++)
context->caret_chars[i] = '^';
context->show_option_requested = false;
context->abort_on_error = false;
int caret_max_width;
/* Character used for caret diagnostics. */
- char caret_chars[rich_location::MAX_RANGES];
+ char caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES];
/* True if we should print the command line option which controls
each diagnostic, if known. */
+2016-08-30 David Malcolm <dmalcolm@redhat.com>
+
+ * gcc.dg/plugin/diagnostic-test-show-locus-bw.c
+ (test_many_nested_locations): New function.
+ * gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
+ (test_show_locus): Handle "test_many_nested_locations".
+
2016-08-30 David Malcolm <dmalcolm@redhat.com>
* g++.dg/template/double-greater-than-fixit.C: New test case.
{ dg-end-multiline-output "" } */
return local;
}
+
+/* Test of many nested locations and fixits. */
+
+void test_many_nested_locations (void)
+{
+ /* { dg-warning "test of 70 locations" }
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+ sed do eiusmod tempor incididunt ut labore et dolore magna
+ aliqua. Ut enim ad minim veniam, quis nostrud exercitation
+ ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis
+ aute irure dolor in reprehenderit in voluptate velit esse cillum
+ dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+ cupidatat non proident, sunt in culpa qui officia deserunt
+ mollit anim id est laborum.
+ */
+/* { dg-begin-multiline-output "" }
+ /*
+ ^
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+ ^~~~~ ^~~~~ ^~~~~ ^~~ ^~~~ ^~~~~~~~~~~ ^~~~~~~~~~ ^~~~
+ LOREM IPSUM DOLOR SIT AMET CONSECTETUR ADIPISCING ELIT
+ sed do eiusmod tempor incididunt ut labore et dolore magna
+ ^~~ ^~ ^~~~~~~ ^~~~~~ ^~~~~~~~~~ ^~ ^~~~~~ ^~ ^~~~~~ ^~~~~
+ SED DO EIUSMOD TEMPOR INCIDIDUNT UT LABORE ET DOLORE MAGNA
+ aliqua. Ut enim ad minim veniam, quis nostrud exercitation
+ ^~~~~~ ^~ ^~~~ ^~ ^~~~~ ^~~~~~ ^~~~ ^~~~~~~ ^~~~~~~~~~~~
+ ALIQUA UT ENIM AD MINIM VENIAM QUIS NOSTRUD EXERCITATION
+ ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis
+ ^~~~~~~ ^~~~~~~ ^~~~ ^~ ^~~~~~~ ^~ ^~ ^~~~~~~ ^~~~~~~~~ ^~~~
+ ULLAMCO LABORIS NISI UT ALIQUIP EX EA COMMODO CONSEQUAT DUIS
+ aute irure dolor in reprehenderit in voluptate velit esse cillum
+ ^~~~ ^~~~~ ^~~~~ ^~ ^~~~~~~~~~~~~ ^~ ^~~~~~~~~ ^~~~~ ^~~~ ^~~~~~
+ AUTE IRURE DOLOR IN REPREHENDERIT IN VOLUPTATE VELIT ESSE CILLUM
+ dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+ ^~~~~~ ^~ ^~~~~~ ^~~~~ ^~~~~~~~ ^~~~~~~~~ ^~~~ ^~~~~~~~
+ DOLORE EU FUGIAT NULLA PARIATUR EXCEPTEUR SINT OCCAECAT
+ cupidatat non proident, sunt in culpa qui officia deserunt
+ ^~~~~~~~~ ^~~ ^~~~~~~~ ^~~~ ^~ ^~~~~ ^~~ ^~~~~~~ ^~~~~~~~
+ CUPIDATAT NON PROIDENT SUNT IN CULPA QUI OFFICIA DESERUNT
+ mollit anim id est laborum.
+ ^~~~~~ ^~~~ ^~ ^~~ ^~~~~~~
+ MOLLIT ANIM ID EST LABORUM
+ { dg-end-multiline-output "" } */
+}
warning_at (input_location, 0,
"example of plus in format code for %q+D", local);
}
+
+ /* Example of many locations and many fixits.
+ Underline (separately) every word in a comment, and convert them
+ to upper case. */
+ if (0 == strcmp (fnname, "test_many_nested_locations"))
+ {
+ const char *file = LOCATION_FILE (fnstart);
+ const int start_line = fnstart_line + 2;
+ const int finish_line = start_line + 7;
+ location_t loc = get_loc (start_line - 1, 2);
+ rich_location richloc (line_table, loc);
+ for (int line = start_line; line <= finish_line; line++)
+ {
+ int line_size;
+ const char *content = location_get_source_line (file, line,
+ &line_size);
+ gcc_assert (content);
+ /* Split line up into words. */
+ for (int idx = 0; idx < line_size; idx++)
+ {
+ if (ISALPHA (content[idx]))
+ {
+ int start_idx = idx;
+ while (idx < line_size && ISALPHA (content[idx]))
+ idx++;
+ if (idx == line_size || !ISALPHA (content[idx]))
+ {
+ location_t start_of_word = get_loc (line, start_idx);
+ location_t end_of_word = get_loc (line, idx - 1);
+ location_t word
+ = make_location (start_of_word, start_of_word,
+ end_of_word);
+ richloc.add_range (word, true);
+
+ /* Add a fixit, converting to upper case. */
+ char *copy = xstrndup (content + start_idx,
+ idx - start_idx);
+ for (char *ch = copy; *ch; ch++)
+ *ch = TOUPPER (*ch);
+ richloc.add_fixit_replace (word, copy);
+ free (copy);
+ }
+ }
+ }
+ }
+ /* Verify that we added enough locations to fully exercise
+ rich_location. We want to exceed both the
+ statically-allocated buffer in class rich_location,
+ and then trigger a reallocation of the dynamic buffer. */
+ gcc_assert (richloc.get_num_locations () > 3 + (2 * 16));
+ warning_at_rich_loc (&richloc, 0, "test of %i locations",
+ richloc.get_num_locations ());
+ }
}
unsigned int
+2016-08-30 David Malcolm <dmalcolm@redhat.com>
+
+ * include/line-map.h (class semi_embedded_vec): New class.
+ (semi_embedded_vec<T, NUM_EMBEDDED>::semi_embedded_vec): New ctor.
+ (semi_embedded_vec<T, NUM_EMBEDDED>::~semi_embedded_vec): New
+ dtor.
+ (semi_embedded_vec<T, NUM_EMBEDDED>::operator[]): New methods.
+ (semi_embedded_vec<T, NUM_EMBEDDED>::push): New method.
+ (semi_embedded_vec<T, NUM_EMBEDDED>::truncate): New method.
+ (rich_location::get_num_locations): Reimplement in terms of
+ m_ranges.
+ (rich_location::get_range): Make non-inline.
+ (rich_location::get_num_fixit_hints): Reimplement in terms of
+ m_fixit_hints.
+ (rich_location::add_fixit): New function.
+ (rich_location::MAX_RANGES): Rename to...
+ (rich_location::STATICALLY_ALLOCATED_RANGES): ...this.
+ (rich_location::MAX_FIXIT_HINTS): Rename to...
+ (rich_location::STATICALLY_ALLOCATED_RANGES): ...this, and make
+ private.
+ (rich_location::m_num_ranges): Eliminate in favor of...
+ (rich_location::m_ranges): ...this, converting from a fixed-size
+ array to a semi_embedded_vec.
+ (rich_location::m_num_fixit_hints): Eliminate in favor of...
+ (rich_location::m_fixit_hints): ...this, converting from a
+ fixed-size array to a semi_embedded_vec.
+ * line-map.c (rich_location::rich_location): Update for above
+ changes.
+ (rich_location::~rich_location): Likewise.
+ (rich_location::get_loc): Likewise.
+ (rich_location::get_range): New methods.
+ (rich_location::add_range): Update for above changes.
+ (rich_location::set_range): Likewise.
+ (rich_location::add_fixit_insert): Likewise.
+ (rich_location::add_fixit_replace): Likewise.
+ (rich_location::get_last_fixit_hint): Likewise.
+ (rich_location::reject_impossible_fixit): Likewise.
+ (rich_location::add_fixit): New method.
+
2016-08-30 David Malcolm <dmalcolm@redhat.com>
* include/line-map.h (rich_location::add_fixit_insert): Add
bool m_show_caret_p;
};
+/* 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 fixit_insert;
class fixit_remove;
set_range (line_maps *set, unsigned int idx, source_location loc,
bool show_caret_p);
- unsigned int get_num_locations () const { return m_num_ranges; }
+ unsigned int get_num_locations () const { return m_ranges.count (); }
- location_range *get_range (unsigned int idx)
- {
- linemap_assert (idx < m_num_ranges);
- return &m_ranges[idx];
- }
+ const location_range *get_range (unsigned int idx) const;
+ location_range *get_range (unsigned int idx);
expanded_location get_expanded_location (unsigned int idx);
add_fixit_replace (source_range src_range,
const char *new_content);
- unsigned int get_num_fixit_hints () const { return m_num_fixit_hints; }
+ 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;
private:
bool reject_impossible_fixit (source_location where);
+ void add_fixit (fixit_hint *hint);
public:
- static const int MAX_RANGES = 3;
- static const int MAX_FIXIT_HINTS = 2;
+ static const int STATICALLY_ALLOCATED_RANGES = 3;
protected:
line_maps *m_line_table;
- unsigned int m_num_ranges;
- location_range m_ranges[MAX_RANGES];
+ semi_embedded_vec <location_range, STATICALLY_ALLOCATED_RANGES> m_ranges;
int m_column_override;
bool m_have_expanded_location;
expanded_location m_expanded_location;
- unsigned int m_num_fixit_hints;
- fixit_hint *m_fixit_hints[MAX_FIXIT_HINTS];
+ static const int MAX_STATIC_FIXIT_HINTS = 2;
+ semi_embedded_vec <fixit_hint *, MAX_STATIC_FIXIT_HINTS> m_fixit_hints;
+
bool m_seen_impossible_fixit;
};
rich_location::rich_location (line_maps *set, source_location loc) :
m_line_table (set),
- m_num_ranges (0),
+ m_ranges (),
m_column_override (0),
m_have_expanded_location (false),
- m_num_fixit_hints (0),
+ m_fixit_hints (),
m_seen_impossible_fixit (false)
{
add_range (loc, true);
rich_location::~rich_location ()
{
- for (unsigned int i = 0; i < m_num_fixit_hints; i++)
- delete m_fixit_hints[i];
+ for (unsigned int i = 0; i < m_fixit_hints.count (); i++)
+ delete get_fixit_hint (i);
}
/* Get location IDX within this rich_location. */
source_location
rich_location::get_loc (unsigned int idx) const
{
- linemap_assert (idx < m_num_ranges);
- return m_ranges[idx].m_loc;
+ const location_range *locrange = get_range (idx);
+ return locrange->m_loc;
+}
+
+/* Get range IDX within this rich_location. */
+
+const location_range *
+rich_location::get_range (unsigned int idx) const
+{
+ return &m_ranges[idx];
+}
+
+/* Mutable access to range IDX within this rich_location. */
+
+location_range *
+rich_location::get_range (unsigned int idx)
+{
+ return &m_ranges[idx];
}
/* Expand location IDX within this rich_location. */
void
rich_location::add_range (source_location loc, bool show_caret_p)
{
- linemap_assert (m_num_ranges < MAX_RANGES);
-
- location_range *range = &m_ranges[m_num_ranges++];
- range->m_loc = loc;
- range->m_show_caret_p = show_caret_p;
+ location_range range;
+ range.m_loc = loc;
+ range.m_show_caret_p = show_caret_p;
+ m_ranges.push (range);
}
/* Add or overwrite the location given by IDX, setting its location to LOC,
rich_location::set_range (line_maps * /*set*/, unsigned int idx,
source_location loc, bool show_caret_p)
{
- linemap_assert (idx < MAX_RANGES);
-
/* We can either overwrite an existing range, or add one exactly
on the end of the array. */
- linemap_assert (idx <= m_num_ranges);
-
- location_range *locrange = &m_ranges[idx];
- locrange->m_loc = loc;
- locrange->m_show_caret_p = show_caret_p;
+ linemap_assert (idx <= m_ranges.count ());
- /* Are we adding a range onto the end? */
- if (idx == m_num_ranges)
- m_num_ranges = idx + 1;
+ if (idx == m_ranges.count ())
+ add_range (loc, show_caret_p);
+ else
+ {
+ location_range *locrange = get_range (idx);
+ locrange->m_loc = loc;
+ locrange->m_show_caret_p = show_caret_p;
+ }
if (idx == 0)
/* Mark any cached value here as dirty. */
if (reject_impossible_fixit (where))
return;
-
- linemap_assert (m_num_fixit_hints < MAX_FIXIT_HINTS);
- m_fixit_hints[m_num_fixit_hints++]
- = new fixit_insert (where, new_content);
+ add_fixit (new fixit_insert (where, new_content));
}
/* Methods for adding removal fix-it hints. */
rich_location::add_fixit_replace (source_range src_range,
const char *new_content)
{
- linemap_assert (m_num_fixit_hints < MAX_FIXIT_HINTS);
-
src_range.m_start = get_pure_location (m_line_table, src_range.m_start);
src_range.m_finish = get_pure_location (m_line_table, src_range.m_finish);
/* Consolidate neighboring fixits. */
fixit_hint *prev = get_last_fixit_hint ();
- if (m_num_fixit_hints > 0)
- {
- if (prev->maybe_append_replace (m_line_table, src_range, new_content))
- return;
- }
+ if (prev)
+ if (prev->maybe_append_replace (m_line_table, src_range, new_content))
+ return;
- m_fixit_hints[m_num_fixit_hints++]
- = new fixit_replace (src_range, new_content);
+ add_fixit (new fixit_replace (src_range, new_content));
}
/* Get the last fix-it hint within this rich_location, or NULL if none. */
fixit_hint *
rich_location::get_last_fixit_hint () const
{
- if (m_num_fixit_hints > 0)
- return m_fixit_hints[m_num_fixit_hints - 1];
+ if (m_fixit_hints.count () > 0)
+ return get_fixit_hint (m_fixit_hints.count () - 1);
else
return NULL;
}
m_seen_impossible_fixit = true;
/* Purge the rich_location of any fix-its that were already added. */
- for (unsigned int i = 0; i < m_num_fixit_hints; i++)
- delete m_fixit_hints[i];
- m_num_fixit_hints = 0;
+ for (unsigned int i = 0; i < m_fixit_hints.count (); i++)
+ delete get_fixit_hint (i);
+ m_fixit_hints.truncate (0);
return true;
}
+/* Add HINT to the fix-it hints in this rich_location. */
+
+void
+rich_location::add_fixit (fixit_hint *hint)
+{
+ m_fixit_hints.push (hint);
+}
+
/* class fixit_insert. */
fixit_insert::fixit_insert (source_location where,