From 8a64515099e64564542cbd09be7c9a21c2f580f3 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Fri, 6 Nov 2015 19:50:50 +0000 Subject: Reimplement diagnostic_show_locus, introducing rich_location classes gcc/ChangeLog: * diagnostic-color.c (color_dict): Eliminate "caret"; add "range1" and "range2". (parse_gcc_colors): Update comment to describe default GCC_COLORS. * diagnostic-core.h (warning_at_rich_loc): New declaration. (error_at_rich_loc): New declaration. (permerror_at_rich_loc): New declaration. (inform_at_rich_loc): New declaration. * diagnostic-show-locus.c (adjust_line): Delete. (struct point_state): New struct. (class colorizer): New class. (class layout_point): New class. (class layout_range): New class. (struct line_bounds): New. (class layout): New class. (colorizer::colorizer): New ctor. (colorizer::~colorizer): New dtor. (layout::layout): New ctor. (layout::print_source_line): New method. (layout::print_annotation_line): New method. (layout::get_state_at_point): New method. (layout::get_x_bound_for_row): New method. (diagnostic_show_locus): Reimplement in terms of class layout. (diagnostic_print_caret_line): Delete. * diagnostic.c (diagnostic_initialize): Replace MAX_LOCATIONS_PER_MESSAGE with rich_location::MAX_RANGES. (diagnostic_set_info_translated): Convert param from location_t to rich_location *. Eliminate calls to set_location on the message in favor of storing the rich_location ptr there. (diagnostic_set_info): Convert param from location_t to rich_location *. (diagnostic_build_prefix): Break out array into... (diagnostic_kind_color): New variable. (diagnostic_get_color_for_kind): New function. (diagnostic_report_diagnostic): Colorize the option_text using the color for the severity. (diagnostic_append_note): Update for change in signature of diagnostic_set_info. (diagnostic_append_note_at_rich_loc): New function. (emit_diagnostic): Update for change in signature of diagnostic_set_info. (inform): Likewise. (inform_at_rich_loc): New function. (inform_n): Update for change in signature of diagnostic_set_info. (warning): Likewise. (warning_at): Likewise. (warning_at_rich_loc): New function. (warning_n): Update for change in signature of diagnostic_set_info. (pedwarn): Likewise. (permerror): Likewise. (permerror_at_rich_loc): New function. (error): Update for change in signature of diagnostic_set_info. (error_n): Likewise. (error_at): Likewise. (error_at_rich_loc): New function. (sorry): Update for change in signature of diagnostic_set_info. (fatal_error): Likewise. (internal_error): Likewise. (internal_error_no_backtrace): Likewise. (source_range::debug): New function. * diagnostic.h (struct diagnostic_info): Eliminate field "override_column". Add field "richloc". (struct diagnostic_context): Add field "colorize_source_p". (diagnostic_override_column): Delete. (diagnostic_set_info): Convert param from location_t to rich_location *. (diagnostic_set_info_translated): Likewise. (diagnostic_append_note_at_rich_loc): New function. (diagnostic_num_locations): New function. (diagnostic_expand_location): Get the location from the rich_location. (diagnostic_print_caret_line): Delete. (diagnostic_get_color_for_kind): New declaration. * genmatch.c (linemap_client_expand_location_to_spelling_point): New. (error_cb): Update for change in signature of "error" callback. (fatal_at): Likewise. (warning_at): Likewise. * input.c (linemap_client_expand_location_to_spelling_point): New. * pretty-print.c (text_info::set_range): New method. (text_info::get_location): New method. * pretty-print.h (MAX_LOCATIONS_PER_MESSAGE): Eliminate this macro. (struct text_info): Eliminate "locations" array in favor of "m_richloc", a rich_location *. (textinfo::set_location): Add a "caret_p" param, and reimplement in terms of a call to set_range. (textinfo::get_location): Eliminate inline implementation in favor of an out-of-line reimplementation. (textinfo::set_range): New method. * rtl-error.c (diagnostic_for_asm): Update for change in signature of diagnostic_set_info. * tree-diagnostic.c (default_tree_printer): Update for new "caret_p" param for textinfo::set_location. * tree-pretty-print.c (percent_K_format): Likewise. gcc/c-family/ChangeLog: * c-common.c (c_cpp_error): Convert parameter from location_t to rich_location *. Eliminate the "column_override" parameter and the call to diagnostic_override_column. Update the "done_lexing" clause to set range 0 on the rich_location, rather than overwriting a location_t. * c-common.h (c_cpp_error): Convert parameter from location_t to rich_location *. Eliminate the "column_override" parameter. gcc/c/ChangeLog: * c-decl.c (warn_defaults_to): Update for change in signature of diagnostic_set_info. * c-errors.c (pedwarn_c99): Likewise. (pedwarn_c90): Likewise. * c-objc-common.c (c_tree_printer): Update for new "caret_p" param for textinfo::set_location. gcc/cp/ChangeLog: * error.c (cp_printer): Update for new "caret_p" param for textinfo::set_location. (pedwarn_cxx98): Update for change in signature of diagnostic_set_info. gcc/fortran/ChangeLog: * cpp.c (cb_cpp_error): Convert parameter from location_t to rich_location *. Eliminate the "column_override" parameter. * error.c (gfc_warning): Update for change in signature of diagnostic_set_info. (gfc_format_decoder): Update handling of %C/%L for changes to struct text_info. (gfc_diagnostic_starter): Use richloc when determining whether to print one locus or two. When handling a location that will involve a call to diagnostic_show_locus, only attempt to print the locus for the primary location, and don't call into diagnostic_print_caret_line. (gfc_warning_now_at): Update for change in signature of diagnostic_set_info. (gfc_warning_now): Likewise. (gfc_error_now): Likewise. (gfc_fatal_error): Likewise. (gfc_error): Likewise. (gfc_internal_error): Likewise. gcc/testsuite/ChangeLog: * gcc.dg/plugin/diagnostic-test-show-locus-bw.c: New file. * gcc.dg/plugin/diagnostic-test-show-locus-color.c: New file. * gcc.dg/plugin/diagnostic_plugin_test_show_locus.c: New file. * gcc.dg/plugin/plugin.exp (plugin_test_list): Add the above. * lib/gcc-dg.exp: Load multiline.exp. libcpp/ChangeLog: * errors.c (cpp_diagnostic): Update for change in signature of "error" callback. (cpp_diagnostic_with_line): Likewise, calling override_column on the rich_location. * include/cpplib.h (struct cpp_callbacks): Within "error" callback, convert param from source_location to rich_location *, and drop column_override param. * include/line-map.h (struct source_range): New struct. (struct location_range): New struct. (class rich_location): New class. (linemap_client_expand_location_to_spelling_point): New declaration. * line-map.c (rich_location::rich_location): New ctors. (rich_location::lazily_expand_location): New method. (rich_location::override_column): New method. (rich_location::add_range): New methods. (rich_location::set_range): New method. From-SVN: r229884 --- libcpp/ChangeLog | 19 ++++ libcpp/errors.c | 7 +- libcpp/include/cpplib.h | 4 +- libcpp/include/line-map.h | 218 ++++++++++++++++++++++++++++++++++++++++++++++ libcpp/line-map.c | 130 +++++++++++++++++++++++++++ 5 files changed, 374 insertions(+), 4 deletions(-) (limited to 'libcpp') diff --git a/libcpp/ChangeLog b/libcpp/ChangeLog index 8abf6dd..57ac5bf 100644 --- a/libcpp/ChangeLog +++ b/libcpp/ChangeLog @@ -1,5 +1,24 @@ 2015-11-06 David Malcolm + * errors.c (cpp_diagnostic): Update for change in signature + of "error" callback. + (cpp_diagnostic_with_line): Likewise, calling override_column + on the rich_location. + * include/cpplib.h (struct cpp_callbacks): Within "error" + callback, convert param from source_location to rich_location *, + and drop column_override param. + * include/line-map.h (struct source_range): New struct. + (struct location_range): New struct. + (class rich_location): New class. + (linemap_client_expand_location_to_spelling_point): New declaration. + * line-map.c (rich_location::rich_location): New ctors. + (rich_location::lazily_expand_location): New method. + (rich_location::override_column): New method. + (rich_location::add_range): New methods. + (rich_location::set_range): New method. + +2015-11-06 David Malcolm + * include/line-map.h (struct linemap_stats): Add fields "adhoc_table_size" and "adhoc_table_entries_used". * line-map.c (linemap_get_statistics): Populate above fields. diff --git a/libcpp/errors.c b/libcpp/errors.c index a33196e..c351c11 100644 --- a/libcpp/errors.c +++ b/libcpp/errors.c @@ -57,7 +57,8 @@ cpp_diagnostic (cpp_reader * pfile, int level, int reason, if (!pfile->cb.error) abort (); - ret = pfile->cb.error (pfile, level, reason, src_loc, 0, _(msgid), ap); + rich_location richloc (src_loc); + ret = pfile->cb.error (pfile, level, reason, &richloc, _(msgid), ap); return ret; } @@ -139,7 +140,9 @@ cpp_diagnostic_with_line (cpp_reader * pfile, int level, int reason, if (!pfile->cb.error) abort (); - ret = pfile->cb.error (pfile, level, reason, src_loc, column, _(msgid), ap); + rich_location richloc (src_loc); + richloc.override_column (column); + ret = pfile->cb.error (pfile, level, reason, &richloc, _(msgid), ap); return ret; } diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h index 5eaea6b..a2bdfa0 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -573,9 +573,9 @@ struct cpp_callbacks /* Called to emit a diagnostic. This callback receives the translated message. */ - bool (*error) (cpp_reader *, int, int, source_location, unsigned int, + bool (*error) (cpp_reader *, int, int, rich_location *, const char *, va_list *) - ATTRIBUTE_FPTR_PRINTF(6,0); + ATTRIBUTE_FPTR_PRINTF(5,0); /* Callbacks for when a macro is expanded, or tested (whether defined or not at the time) in #ifdef, #ifndef or "defined". */ diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h index c8618a9..c9340a6 100644 --- a/libcpp/include/line-map.h +++ b/libcpp/include/line-map.h @@ -131,6 +131,47 @@ typedef unsigned int linenum_type; libcpp/location-example.txt. */ typedef unsigned int source_location; +/* A range of source locations. + + Ranges are closed: + m_start is the first location within the range, + m_finish is the last location within the range. + + We may need a more compact way to store these, but for now, + let's do it the simple way, as a pair. */ +struct GTY(()) source_range +{ + source_location m_start; + source_location m_finish; + + /* Display this source_range instance, with MSG as a descriptive + comment. This issues a "note" diagnostic at the range, using + gcc's diagnostic machinery. + + This is declared here, but is implemented within gcc/diagnostic.c, + since it makes use of gcc's diagnostic-printing machinery. This + is a slight layering violation, but this is sufficiently useful + for debugging that it's worth it. + + This declaration would have a DEBUG_FUNCTION annotation, but that + is implemented in gcc/system.h and thus is not available here in + libcpp. */ + void debug (const char *msg) const; + + /* We avoid using constructors, since various structs that + don't yet have constructors will embed instances of + source_range. */ + + /* Make a source_range from a source_location. */ + static source_range from_location (source_location loc) + { + source_range result; + result.m_start = loc; + result.m_finish = loc; + return result; + } +}; + /* Memory allocation function typedef. Works like xrealloc. */ typedef void *(*line_map_realloc) (void *, size_t); @@ -1028,6 +1069,174 @@ typedef struct bool sysp; } expanded_location; +/* Both gcc and emacs number source *lines* starting at 1, but + they have differing conventions for *columns*. + + GCC uses a 1-based convention for source columns, + whereas Emacs's M-x column-number-mode uses a 0-based convention. + + For example, an error in the initial, left-hand + column of source line 3 is reported by GCC as: + + some-file.c:3:1: error: ...etc... + + On navigating to the location of that error in Emacs + (e.g. via "next-error"), + the locus is reported in the Mode Line + (assuming M-x column-number-mode) as: + + some-file.c 10% (3, 0) + + i.e. "3:1:" in GCC corresponds to "(3, 0)" in Emacs. */ + +/* Ranges are closed + m_start is the first location within the range, and + m_finish is the last location within the range. */ +struct location_range +{ + expanded_location m_start; + expanded_location m_finish; + + /* Should a caret be drawn for this range? Typically this is + true for the 0th range, and false for subsequent ranges, + but the Fortran frontend overrides this for rendering 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. */ + bool m_show_caret_p; + expanded_location m_caret; +}; + +/* A "rich" source code location, for use when printing diagnostics. + A rich_location has one or more ranges, each optionally with + a caret. 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. + + 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 + ********* + printf ("arg0: %i arg1: %s arg2: %i", + ^~ + 100, 101, 102); + ~~~ + This rich location has two ranges: + - range 0 is at the "%s" with start = caret = "%" and finish at + the "s". + - range 1 has start/finish covering the "101" and is not flagged for + caret printing; it is perhaps at the start of "101". */ + +class rich_location +{ + public: + /* Constructors. */ + + /* Constructing from a location. */ + rich_location (source_location loc); + + /* Constructing from a source_range. */ + rich_location (source_range src_range); + + /* Accessors. */ + source_location get_loc () const { return m_loc; } + + source_location *get_loc_addr () { return &m_loc; } + + void + add_range (source_location start, source_location finish, + bool show_caret_p); + + void + add_range (source_range src_range, bool show_caret_p); + + void + add_range (location_range *src_range); + + void + set_range (unsigned int idx, source_range src_range, + bool show_caret_p, bool overwrite_loc_p); + + unsigned int get_num_locations () const { return m_num_ranges; } + + location_range *get_range (unsigned int idx) + { + linemap_assert (idx < m_num_ranges); + return &m_ranges[idx]; + } + + expanded_location lazily_expand_location (); + + void + override_column (int column); + +public: + static const int MAX_RANGES = 3; + +protected: + source_location m_loc; + + unsigned int m_num_ranges; + location_range m_ranges[MAX_RANGES]; + + bool m_have_expanded_location; + expanded_location m_expanded_location; +}; + /* 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. */ @@ -1173,4 +1382,13 @@ void linemap_dump (FILE *, struct line_maps *, unsigned, bool); specifies how many macro maps to dump. */ void line_table_dump (FILE *, struct line_maps *, unsigned int, unsigned int); +/* The rich_location class requires a way to expand source_location instances. + We would directly use expand_location_to_spelling_point, which is + implemented in gcc/input.c, but we also need to use it for rich_location + within genmatch.c. + Hence we require client code of libcpp to implement the following + symbol. */ +extern expanded_location +linemap_client_expand_location_to_spelling_point (source_location ); + #endif /* !LIBCPP_LINE_MAP_H */ diff --git a/libcpp/line-map.c b/libcpp/line-map.c index 84403de..3c19f93 100644 --- a/libcpp/line-map.c +++ b/libcpp/line-map.c @@ -1755,3 +1755,133 @@ line_table_dump (FILE *stream, struct line_maps *set, unsigned int num_ordinary, fprintf (stream, "\n"); } } + +/* class rich_location. */ + +/* Construct a rich_location with location LOC as its initial range. */ + +rich_location::rich_location (source_location loc) : + m_loc (loc), + m_num_ranges (0), + m_have_expanded_location (false) +{ + /* Set up the 0th range: */ + add_range (loc, loc, true); + m_ranges[0].m_caret = lazily_expand_location (); +} + +/* Construct a rich_location with source_range SRC_RANGE as its + initial range. */ + +rich_location::rich_location (source_range src_range) +: m_loc (src_range.m_start), + m_num_ranges (0), + m_have_expanded_location (false) +{ + /* Set up the 0th range: */ + add_range (src_range, true); +} + +/* Get an expanded_location for this rich_location's primary + location. */ + +expanded_location +rich_location::lazily_expand_location () +{ + if (!m_have_expanded_location) + { + m_expanded_location + = linemap_client_expand_location_to_spelling_point (m_loc); + m_have_expanded_location = true; + } + + return m_expanded_location; +} + +/* Set the column of the primary location. */ + +void +rich_location::override_column (int column) +{ + lazily_expand_location (); + m_expanded_location.column = column; +} + +/* Add the given range. */ + +void +rich_location::add_range (source_location start, source_location finish, + bool show_caret_p) +{ + linemap_assert (m_num_ranges < MAX_RANGES); + + location_range *range = &m_ranges[m_num_ranges++]; + range->m_start = linemap_client_expand_location_to_spelling_point (start); + range->m_finish = linemap_client_expand_location_to_spelling_point (finish); + range->m_caret = range->m_start; + range->m_show_caret_p = show_caret_p; +} + +/* Add the given range. */ + +void +rich_location::add_range (source_range src_range, bool show_caret_p) +{ + linemap_assert (m_num_ranges < MAX_RANGES); + + add_range (src_range.m_start, src_range.m_finish, show_caret_p); +} + +void +rich_location::add_range (location_range *src_range) +{ + linemap_assert (m_num_ranges < MAX_RANGES); + + m_ranges[m_num_ranges++] = *src_range; +} + +/* Add or overwrite the range given by IDX. It must either + overwrite an existing range, or add one *exactly* on the end of + the array. + + This is primarily for use by gcc when implementing diagnostic + format decoders e.g. the "+" in the C/C++ frontends, for handling + format codes like "%q+D" (which writes the source location of a + tree back into range 0 of the rich_location). + + If SHOW_CARET_P is true, then the range should be rendered with + a caret at its starting location. This + is for use by the Fortran frontend, for implementing the + "%C" and "%L" format codes. */ + +void +rich_location::set_range (unsigned int idx, source_range src_range, + bool show_caret_p, bool overwrite_loc_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_start + = linemap_client_expand_location_to_spelling_point (src_range.m_start); + locrange->m_finish + = linemap_client_expand_location_to_spelling_point (src_range.m_finish); + + locrange->m_show_caret_p = show_caret_p; + if (overwrite_loc_p) + locrange->m_caret = locrange->m_start; + + /* Are we adding a range onto the end? */ + if (idx == m_num_ranges) + m_num_ranges = idx + 1; + + if (idx == 0 && overwrite_loc_p) + { + m_loc = src_range.m_start; + /* Mark any cached value here as dirty. */ + m_have_expanded_location = false; + } +} -- cgit v1.1