Reimplement diagnostic_show_locus, introducing rich_location classes
authorDavid Malcolm <dmalcolm@redhat.com>
Fri, 6 Nov 2015 19:50:50 +0000 (19:50 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Fri, 6 Nov 2015 19:50:50 +0000 (19:50 +0000)
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

36 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c-family/c-common.h
gcc/c/ChangeLog
gcc/c/c-decl.c
gcc/c/c-errors.c
gcc/c/c-objc-common.c
gcc/cp/ChangeLog
gcc/cp/error.c
gcc/diagnostic-color.c
gcc/diagnostic-core.h
gcc/diagnostic-show-locus.c
gcc/diagnostic.c
gcc/diagnostic.h
gcc/fortran/ChangeLog
gcc/fortran/cpp.c
gcc/fortran/error.c
gcc/genmatch.c
gcc/input.c
gcc/pretty-print.c
gcc/pretty-print.h
gcc/rtl-error.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/plugin/plugin.exp
gcc/testsuite/lib/gcc-dg.exp
gcc/tree-diagnostic.c
gcc/tree-pretty-print.c
libcpp/ChangeLog
libcpp/errors.c
libcpp/include/cpplib.h
libcpp/include/line-map.h
libcpp/line-map.c

index eee5b6c92d9c10aa1bcfc7d87f854b5b2b42f68e..7b922364abce1a070035563c1159109c750c52da 100644 (file)
@@ -1,3 +1,98 @@
+2015-11-06  David Malcolm  <dmalcolm@redhat.com>
+
+       * 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.
+
 2015-11-06  Ramana Radhakrishnan  <ramana.radhakrishnan@arm.com>
 
        Properly apply.
index cc9a6420407eb92158de8308512f470f52d3964a..e6d632d8b9763f875969ec12843dc99fc9eb7bc7 100644 (file)
@@ -1,3 +1,13 @@
+2015-11-06  David Malcolm  <dmalcolm@redhat.com>
+
+       * 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.
+
 2015-11-05  Cesar Philippidis  <cesar@codesourcery.com>
            Thomas Schwinge  <thomas@codesourcery.com>
            James Norris  <jnorris@codesourcery.com>
index de5f8b6cbdf6b50a1ca4508c432feec6af7a0262..b7ae7541d43d0945fa976c7b45a935c69a0ebc8a 100644 (file)
@@ -10543,15 +10543,14 @@ c_option_controlling_cpp_error (int reason)
 /* Callback from cpp_error for PFILE to print diagnostics from the
    preprocessor.  The diagnostic is of type LEVEL, with REASON set
    to the reason code if LEVEL is represents a warning, at location
-   LOCATION unless this is after lexing and the compiler's location
-   should be used instead, with column number possibly overridden by
-   COLUMN_OVERRIDE if not zero; MSG is the translated message and AP
+   RICHLOC unless this is after lexing and the compiler's location
+   should be used instead; MSG is the translated message and AP
    the arguments.  Returns true if a diagnostic was emitted, false
    otherwise.  */
 
 bool
 c_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
-            location_t location, unsigned int column_override,
+            rich_location *richloc,
             const char *msg, va_list *ap)
 {
   diagnostic_info diagnostic;
@@ -10592,11 +10591,11 @@ c_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
       gcc_unreachable ();
     }
   if (done_lexing)
-    location = input_location;
+    richloc->set_range (0,
+                       source_range::from_location (input_location),
+                       true, true);
   diagnostic_set_info_translated (&diagnostic, msg, ap,
-                                 location, dlevel);
-  if (column_override)
-    diagnostic_override_column (&diagnostic, column_override);
+                                 richloc, dlevel);
   diagnostic_override_option_index (&diagnostic,
                                     c_option_controlling_cpp_error (reason));
   ret = report_diagnostic (&diagnostic);
index de9768e921419f456bff7dd0f9b4e3a2e4540a7d..c82545405e4b33b5388bf404473c52e462eeb949 100644 (file)
@@ -995,9 +995,9 @@ extern void init_c_lex (void);
 
 extern void c_cpp_builtins (cpp_reader *);
 extern void c_cpp_builtins_optimize_pragma (cpp_reader *, tree, tree);
-extern bool c_cpp_error (cpp_reader *, int, int, location_t, unsigned int,
+extern bool c_cpp_error (cpp_reader *, int, int, rich_location *,
                         const char *, va_list *)
-     ATTRIBUTE_GCC_DIAG(6,0);
+     ATTRIBUTE_GCC_DIAG(5,0);
 extern int c_common_has_attribute (cpp_reader *);
 
 extern bool parse_optimize_options (tree, bool);
index c734a81f45ccc9b34c931312783290b50bd737bf..991d0d34851648ee01a95cc3009343b060184b56 100644 (file)
@@ -1,3 +1,12 @@
+2015-11-06  David Malcolm  <dmalcolm@redhat.com>
+
+       * 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.
+
 2015-11-05  Cesar Philippidis  <cesar@codesourcery.com>
            Thomas Schwinge  <thomas@codesourcery.com>
            James Norris  <jnorris@codesourcery.com>
index 8e355bae637e326b3dfcfea5feeadda508713cbf..e9bdceb8a99e8c72980c423b6463083c07114fe5 100644 (file)
@@ -5285,9 +5285,10 @@ warn_defaults_to (location_t location, int opt, const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
                        flag_isoc99 ? DK_PEDWARN : DK_WARNING);
   diagnostic.option_index = opt;
   report_diagnostic (&diagnostic);
index d9176ec0f119865f412d08fd3be5024729c18a51..b458957474d58b895bfea549aaec6b0071374a05 100644 (file)
@@ -41,13 +41,14 @@ pedwarn_c99 (location_t location, int opt, const char *gmsgid, ...)
   diagnostic_info diagnostic;
   va_list ap;
   bool warned = false;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
   /* If desired, issue the C99/C11 compat warning, which is more specific
      than -pedantic.  */
   if (warn_c99_c11_compat > 0)
     {
-      diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+      diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
                           (pedantic && !flag_isoc11)
                           ? DK_PEDWARN : DK_WARNING);
       diagnostic.option_index = OPT_Wc99_c11_compat;
@@ -59,7 +60,7 @@ pedwarn_c99 (location_t location, int opt, const char *gmsgid, ...)
   /* For -pedantic outside C11, issue a pedwarn.  */
   else if (pedantic && !flag_isoc11)
     {
-      diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN);
+      diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN);
       diagnostic.option_index = opt;
       warned = report_diagnostic (&diagnostic);
     }
@@ -79,6 +80,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
   /* Warnings such as -Wvla are the most specific ones.  */
@@ -89,7 +91,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
         goto out;
       else if (opt_var > 0)
        {
-         diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+         diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
                               (pedantic && !flag_isoc99)
                               ? DK_PEDWARN : DK_WARNING);
          diagnostic.option_index = opt;
@@ -101,7 +103,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
      specific than -pedantic.  */
   if (warn_c90_c99_compat > 0)
     {
-      diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+      diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
                           (pedantic && !flag_isoc99)
                           ? DK_PEDWARN : DK_WARNING);
       diagnostic.option_index = OPT_Wc90_c99_compat;
@@ -113,7 +115,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
   /* For -pedantic outside C99, issue a pedwarn.  */
   else if (pedantic && !flag_isoc99)
     {
-      diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN);
+      diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN);
       diagnostic.option_index = opt;
       report_diagnostic (&diagnostic);
     }
index ac61191c5119c40c86131483b57e1994b6719e13..270b03da5ca8d78e8d6fa71c9d55c4d4902e6b2d 100644 (file)
@@ -100,7 +100,7 @@ c_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
     {
       t = va_arg (*text->args_ptr, tree);
       if (set_locus)
-       text->set_location (0, DECL_SOURCE_LOCATION (t));
+       text->set_location (0, DECL_SOURCE_LOCATION (t), true);
     }
 
   switch (*spec)
index ce8a81f6a1e983db4f0ecbfb3562fb46a111e1eb..fc92a5f0561c05e37bd2f3c538bf338a5263c62b 100644 (file)
@@ -1,3 +1,10 @@
+2015-11-06  David Malcolm  <dmalcolm@redhat.com>
+
+       * 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.
+
 2015-11-06  Jason Merrill  <jason@redhat.com>
 
        Support non-type constrained-type-specifiers.
index 75f6abb415f90e5f11d198c6393b624f801619a5..286389c8aea657074b00e614c020a0d42afc4f3a 100644 (file)
@@ -3563,7 +3563,7 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec,
 
   pp_string (pp, result);
   if (set_locus && t != NULL)
-    text->set_location (0, location_of (t));
+    text->set_location (0, location_of (t), true);
   return true;
 #undef next_tree
 #undef next_tcode
@@ -3677,9 +3677,10 @@ pedwarn_cxx98 (location_t location, int opt, const char *gmsgid, ...)
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
                       (cxx_dialect == cxx98) ? DK_PEDWARN : DK_WARNING);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
index 3fe49b2be47f65b5b8c7b60fda6a4f6ab6a331c5..d848dfca52e62d0744ae2da07e7ffaa12277a282 100644 (file)
@@ -164,7 +164,8 @@ static struct color_cap color_dict[] =
   { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
               7, false },
   { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
-  { "caret", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN), 5, false },
+  { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false },
+  { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
   { "locus", SGR_SEQ (COLOR_BOLD), 5, false },
   { "quote", SGR_SEQ (COLOR_BOLD), 5, false },
   { NULL, NULL, 0, false }
@@ -195,7 +196,7 @@ colorize_stop (bool show_color)
 }
 
 /* Parse GCC_COLORS.  The default would look like:
-   GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
+   GCC_COLORS='error=01;31:warning=01;35:note=01;36:range1=32:range2=34;locus=01:quote=01'
    No character escaping is needed or supported.  */
 static bool
 parse_gcc_colors (void)
index 6cc1e6b3ee52a27112fbc4f1e7aa2ba241bcc1ca..749c847c9ce8072df229f30e4220df2bdd92845b 100644 (file)
@@ -63,18 +63,26 @@ extern bool warning_n (location_t, int, int, const char *, const char *, ...)
     ATTRIBUTE_GCC_DIAG(4,6) ATTRIBUTE_GCC_DIAG(5,6);
 extern bool warning_at (location_t, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
+extern bool warning_at_rich_loc (rich_location *, int, const char *, ...)
+    ATTRIBUTE_GCC_DIAG(3,4);
 extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern void error_n (location_t, int, const char *, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
 extern void error_at (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
+extern void error_at_rich_loc (rich_location *, const char *, ...)
+  ATTRIBUTE_GCC_DIAG(2,3);
 extern void fatal_error (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3)
      ATTRIBUTE_NORETURN;
 /* Pass one of the OPT_W* from options.h as the second parameter.  */
 extern bool pedwarn (location_t, int, const char *, ...)
      ATTRIBUTE_GCC_DIAG(3,4);
 extern bool permerror (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
+extern bool permerror_at_rich_loc (rich_location *, const char *,
+                                  ...) ATTRIBUTE_GCC_DIAG(2,3);
 extern void sorry (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern void inform (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
+extern void inform_at_rich_loc (rich_location *, const char *,
+                               ...) ATTRIBUTE_GCC_DIAG(2,3);
 extern void inform_n (location_t, int, const char *, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
 extern void verbatim (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
index 147a2b8b71d48f6cdbf81ab73954a39014e23c05..22203cdbaa380d88aef3f8f2b38ea265d6f08d13 100644 (file)
@@ -36,131 +36,686 @@ along with GCC; see the file COPYING3.  If not see
 # include <sys/ioctl.h>
 #endif
 
-/* If LINE is longer than MAX_WIDTH, and COLUMN is not smaller than
-   MAX_WIDTH by some margin, then adjust the start of the line such
-   that the COLUMN is smaller than MAX_WIDTH minus the margin.  The
-   margin is either CARET_LINE_MARGIN characters or the difference
-   between the column and the length of the line, whatever is smaller.
-   The length of LINE is given by LINE_WIDTH.  */
-static const char *
-adjust_line (const char *line, int line_width,
-            int max_width, int *column_p)
-{
-  int right_margin = CARET_LINE_MARGIN;
-  int column = *column_p;
-
-  gcc_checking_assert (line_width >= column);
-  right_margin = MIN (line_width - column, right_margin);
-  right_margin = max_width - right_margin;
-  if (line_width >= max_width && column > right_margin)
+/* Classes for rendering source code and diagnostics, within an
+   anonymous namespace.
+   The work is done by "class layout", which embeds and uses
+   "class colorizer" and "class layout_range" to get things done.  */
+
+namespace {
+
+/* The state at a given point of the source code, assuming that we're
+   in a range: which range are we in, and whether we should draw a caret at
+   this point.  */
+
+struct point_state
+{
+  int range_idx;
+  bool draw_caret_p;
+};
+
+/* A class to inject colorization codes when printing the diagnostic locus.
+
+   It has one kind of colorization for each of:
+     - normal text
+     - range 0 (the "primary location")
+     - range 1
+     - range 2
+
+   The class caches the lookup of the color codes for the above.
+
+   The class also has responsibility for tracking which of the above is
+   active, filtering out unnecessary changes.  This allows
+   layout::print_source_line and layout::print_annotation_line
+   to simply request a colorization code for *every* character they print,
+   via this class, and have the filtering be done for them here.  */
+
+class colorizer
+{
+ public:
+  colorizer (diagnostic_context *context,
+            const diagnostic_info *diagnostic);
+  ~colorizer ();
+
+  void set_range (int range_idx) { set_state (range_idx); }
+  void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
+
+ private:
+  void set_state (int state);
+  void begin_state (int state);
+  void finish_state (int state);
+
+ private:
+  static const int STATE_NORMAL_TEXT = -1;
+
+  diagnostic_context *m_context;
+  const diagnostic_info *m_diagnostic;
+  int m_current_state;
+  const char *m_caret_cs;
+  const char *m_caret_ce;
+  const char *m_range1_cs;
+  const char *m_range2_cs;
+  const char *m_range_ce;
+};
+
+/* A point within a layout_range; similar to an expanded_location,
+   but after filtering on file.  */
+
+class layout_point
+{
+ public:
+  layout_point (const expanded_location &exploc)
+  : m_line (exploc.line),
+    m_column (exploc.column) {}
+
+  int m_line;
+  int m_column;
+};
+
+/* A class for use by "class layout" below: a filtered location_range.  */
+
+class layout_range
+{
+ public:
+  layout_range (const location_range *loc_range);
+
+  bool contains_point (int row, int column) const;
+
+  layout_point m_start;
+  layout_point m_finish;
+  bool m_show_caret_p;
+  layout_point m_caret;
+};
+
+/* A struct for use by layout::print_source_line for telling
+   layout::print_annotation_line the extents of the source line that
+   it printed, so that underlines can be clipped appropriately.  */
+
+struct line_bounds
+{
+  int m_first_non_ws;
+  int m_last_non_ws;
+};
+
+/* A class to control the overall layout when printing a diagnostic.
+
+   The layout is determined within the constructor.
+   It is then printed by repeatedly calling the "print_source_line"
+   and "print_annotation_line" methods.
+
+   We assume we have disjoint ranges.  */
+
+class layout
+{
+ public:
+  layout (diagnostic_context *context,
+         const diagnostic_info *diagnostic);
+
+  int get_first_line () const { return m_first_line; }
+  int get_last_line () const { return m_last_line; }
+
+  bool print_source_line (int row, line_bounds *lbounds_out);
+  void print_annotation_line (int row, const line_bounds lbounds);
+
+ private:
+  bool
+  get_state_at_point (/* Inputs.  */
+                     int row, int column,
+                     int first_non_ws, int last_non_ws,
+                     /* Outputs.  */
+                     point_state *out_state);
+
+  int
+  get_x_bound_for_row (int row, int caret_column,
+                      int last_non_ws);
+
+ private:
+  diagnostic_context *m_context;
+  pretty_printer *m_pp;
+  diagnostic_t m_diagnostic_kind;
+  expanded_location m_exploc;
+  colorizer m_colorizer;
+  bool m_colorize_source_p;
+  auto_vec <layout_range> m_layout_ranges;
+  int m_first_line;
+  int m_last_line;
+  int m_x_offset;
+};
+
+/* Implementation of "class colorizer".  */
+
+/* The constructor for "colorizer".  Lookup and store color codes for the
+   different kinds of things we might need to print.  */
+
+colorizer::colorizer (diagnostic_context *context,
+                     const diagnostic_info *diagnostic) :
+  m_context (context),
+  m_diagnostic (diagnostic),
+  m_current_state (STATE_NORMAL_TEXT)
+{
+  m_caret_ce = colorize_stop (pp_show_color (context->printer));
+  m_range1_cs = colorize_start (pp_show_color (context->printer), "range1");
+  m_range2_cs = colorize_start (pp_show_color (context->printer), "range2");
+  m_range_ce = colorize_stop (pp_show_color (context->printer));
+}
+
+/* The destructor for "colorize".  If colorization is on, print a code to
+   turn it off.  */
+
+colorizer::~colorizer ()
+{
+  finish_state (m_current_state);
+}
+
+/* Update state, printing color codes if necessary if there's a state
+   change.  */
+
+void
+colorizer::set_state (int new_state)
+{
+  if (m_current_state != new_state)
     {
-      line += column - right_margin;
-      *column_p = right_margin;
+      finish_state (m_current_state);
+      m_current_state = new_state;
+      begin_state (new_state);
     }
-  return line;
 }
 
-/* Print the physical source line corresponding to the location of
-   this diagnostic, and a caret indicating the precise column.  This
-   function only prints two caret characters if the two locations
-   given by DIAGNOSTIC are on the same line according to
-   diagnostic_same_line().  */
+/* Turn on any colorization for STATE.  */
+
 void
-diagnostic_show_locus (diagnostic_context * context,
-                      const diagnostic_info *diagnostic)
+colorizer::begin_state (int state)
 {
-  if (!context->show_caret
-      || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION
-      || diagnostic_location (diagnostic, 0) == context->last_location)
-    return;
+  switch (state)
+    {
+    case STATE_NORMAL_TEXT:
+      break;
 
-  context->last_location = diagnostic_location (diagnostic, 0);
-  expanded_location s0 = diagnostic_expand_location (diagnostic, 0);
-  expanded_location s1 = { };
-  /* Zero-initialized. This is checked later by diagnostic_print_caret_line.  */
+    case 0:
+      /* Make range 0 be the same color as the "kind" text
+        (error vs warning vs note).  */
+      pp_string
+       (m_context->printer,
+        colorize_start (pp_show_color (m_context->printer),
+                        diagnostic_get_color_for_kind (m_diagnostic->kind)));
+      break;
 
-  if (diagnostic_location (diagnostic, 1) > BUILTINS_LOCATION)
-    s1 = diagnostic_expand_location (diagnostic, 1);
+    case 1:
+      pp_string (m_context->printer, m_range1_cs);
+      break;
 
-  diagnostic_print_caret_line (context, s0, s1,
-                              context->caret_chars[0],
-                              context->caret_chars[1]);
+    case 2:
+      pp_string (m_context->printer, m_range2_cs);
+      break;
+
+    default:
+      /* We don't expect more than 3 ranges per diagnostic.  */
+      gcc_unreachable ();
+      break;
+    }
 }
 
-/* Print (part) of the source line given by xloc1 with caret1 pointing
-   at the column.  If xloc2.column != 0 and it fits within the same
-   line as xloc1 according to diagnostic_same_line (), then caret2 is
-   printed at xloc2.colum.  Otherwise, the caller has to set up things
-   to print a second caret line for xloc2.  */
+/* Turn off any colorization for STATE.  */
+
 void
-diagnostic_print_caret_line (diagnostic_context * context,
-                            expanded_location xloc1,
-                            expanded_location xloc2,
-                            char caret1, char caret2)
-{
-  if (!diagnostic_same_line (context, xloc1, xloc2))
-    /* This will mean ignore xloc2.  */
-    xloc2.column = 0;
-  else if (xloc1.column == xloc2.column)
-    xloc2.column++;
-
-  int cmax = MAX (xloc1.column, xloc2.column);
+colorizer::finish_state (int state)
+{
+  switch (state)
+    {
+    case STATE_NORMAL_TEXT:
+      break;
+
+    case 0:
+      pp_string (m_context->printer, m_caret_ce);
+      break;
+
+    default:
+      /* Within a range.  */
+      gcc_assert (state > 0);
+      pp_string (m_context->printer, m_range_ce);
+      break;
+    }
+}
+
+/* Implementation of class layout_range.  */
+
+/* The constructor for class layout_range.
+   Initialize various layout_point fields from expanded_location
+   equivalents; we've already filtered on file.  */
+
+layout_range::layout_range (const location_range *loc_range)
+: m_start (loc_range->m_start),
+  m_finish (loc_range->m_finish),
+  m_show_caret_p (loc_range->m_show_caret_p),
+  m_caret (loc_range->m_caret)
+{
+}
+
+/* Is (column, row) within the given range?
+   We've already filtered on the file.
+
+   Ranges are closed (both limits are within the range).
+
+   Example A: a single-line range:
+     start:  (col=22, line=2)
+     finish: (col=38, line=2)
+
+  |00000011111111112222222222333333333344444444444
+  |34567890123456789012345678901234567890123456789
+--+-----------------------------------------------
+01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
+03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+   Example B: a multiline range with
+     start:  (col=14, line=3)
+     finish: (col=08, line=5)
+
+  |00000011111111112222222222333333333344444444444
+  |34567890123456789012345678901234567890123456789
+--+-----------------------------------------------
+01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
+04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
+05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+--+-----------------------------------------------
+
+   Legend:
+   - 'b' indicates a point *before* the range
+   - 'S' indicates the start of the range
+   - 'w' indicates a point within the range
+   - 'F' indicates the finish of the range (which is
+        within it).
+   - 'a' indicates a subsequent point *after* the range.  */
+
+bool
+layout_range::contains_point (int row, int column) const
+{
+  gcc_assert (m_start.m_line <= m_finish.m_line);
+  /* ...but the equivalent isn't true for the columns;
+     consider example B in the comment above.  */
+
+  if (row < m_start.m_line)
+    /* Points before the first line of the range are
+       outside it (corresponding to line 01 in example A
+       and lines 01 and 02 in example B above).  */
+    return false;
+
+  if (row == m_start.m_line)
+    /* On same line as start of range (corresponding
+       to line 02 in example A and line 03 in example B).  */
+    {
+      if (column < m_start.m_column)
+       /* Points on the starting line of the range, but
+          before the column in which it begins.  */
+       return false;
+
+      if (row < m_finish.m_line)
+       /* This is a multiline range; the point
+          is within it (corresponds to line 03 in example B
+          from column 14 onwards) */
+       return true;
+      else
+       {
+         /* This is a single-line range.  */
+         gcc_assert (row == m_finish.m_line);
+         return column <= m_finish.m_column;
+       }
+    }
+
+  /* The point is in a line beyond that containing the
+     start of the range: lines 03 onwards in example A,
+     and lines 04 onwards in example B.  */
+  gcc_assert (row > m_start.m_line);
+
+  if (row > m_finish.m_line)
+    /* The point is beyond the final line of the range
+       (lines 03 onwards in example A, and lines 06 onwards
+       in example B).  */
+    return false;
+
+  if (row < m_finish.m_line)
+    {
+      /* The point is in a line that's fully within a multiline
+        range (e.g. line 04 in example B).  */
+      gcc_assert (m_start.m_line < m_finish.m_line);
+      return true;
+    }
+
+  gcc_assert (row ==  m_finish.m_line);
+
+  return column <= m_finish.m_column;
+}
+
+/* Given a source line LINE of length LINE_WIDTH, determine the width
+   without any trailing whitespace.  */
+
+static int
+get_line_width_without_trailing_whitespace (const char *line, int line_width)
+{
+  int result = line_width;
+  while (result > 0)
+    {
+      char ch = line[result - 1];
+      if (ch == ' ' || ch == '\t')
+       result--;
+      else
+       break;
+    }
+  gcc_assert (result >= 0);
+  gcc_assert (result <= line_width);
+  gcc_assert (result == 0 ||
+             (line[result - 1] != ' '
+              && line[result -1] != '\t'));
+  return result;
+}
+
+/* Implementation of class layout.  */
+
+/* Constructor for class layout.
+
+   Filter the ranges from the rich_location to those that we can
+   sanely print, populating m_layout_ranges.
+   Determine the range of lines that we will print.
+   Determine m_x_offset, to ensure that the primary caret
+   will fit within the max_width provided by the diagnostic_context.  */
+
+layout::layout (diagnostic_context * context,
+               const diagnostic_info *diagnostic)
+: m_context (context),
+  m_pp (context->printer),
+  m_diagnostic_kind (diagnostic->kind),
+  m_exploc (diagnostic->richloc->lazily_expand_location ()),
+  m_colorizer (context, diagnostic),
+  m_colorize_source_p (context->colorize_source_p),
+  m_layout_ranges (rich_location::MAX_RANGES),
+  m_first_line (m_exploc.line),
+  m_last_line  (m_exploc.line),
+  m_x_offset (0)
+{
+  rich_location *richloc = diagnostic->richloc;
+  for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
+    {
+      /* This diagnostic printer can only cope with "sufficiently sane" ranges.
+        Ignore any ranges that are awkward to handle.  */
+      location_range *loc_range = richloc->get_range (idx);
+
+      /* If any part of the range isn't in the same file as the primary
+        location of this diagnostic, ignore the range.  */
+      if (loc_range->m_start.file != m_exploc.file)
+       continue;
+      if (loc_range->m_finish.file != m_exploc.file)
+       continue;
+      if (loc_range->m_show_caret_p)
+       if (loc_range->m_caret.file != m_exploc.file)
+         continue;
+
+      /* Passed all the tests; add the range to m_layout_ranges so that
+        it will be printed.  */
+      layout_range ri (loc_range);
+      m_layout_ranges.safe_push (ri);
+
+      /* Update m_first_line/m_last_line if necessary.  */
+      if (loc_range->m_start.line < m_first_line)
+       m_first_line = loc_range->m_start.line;
+      if (loc_range->m_finish.line > m_last_line)
+       m_last_line = loc_range->m_finish.line;
+    }
+
+  /* Adjust m_x_offset.
+     Center the primary caret to fit in max_width; all columns
+     will be adjusted accordingly.  */
+  int max_width = m_context->caret_max_width;
   int line_width;
-  const char *line = location_get_source_line (xloc1.file, xloc1.line,
+  const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
                                               &line_width);
-  if (line == NULL || cmax > line_width)
-    return;
+  if (line && m_exploc.column <= line_width)
+    {
+      int right_margin = CARET_LINE_MARGIN;
+      int column = m_exploc.column;
+      right_margin = MIN (line_width - column, right_margin);
+      right_margin = max_width - right_margin;
+      if (line_width >= max_width && column > right_margin)
+       m_x_offset = column - right_margin;
+      gcc_assert (m_x_offset >= 0);
+    }
+}
 
-  /* Center the interesting part of the source line to fit in
-     max_width, and adjust all columns accordingly.  */
-  int max_width = context->caret_max_width;
-  int offset = (int) cmax;
-  line = adjust_line (line, line_width, max_width, &offset);
-  offset -= cmax;
-  cmax += offset;
-  xloc1.column += offset;
-  if (xloc2.column)
-    xloc2.column += offset;
-
-  /* Print the source line.  */
-  pp_newline (context->printer);
-  const char *saved_prefix = pp_get_prefix (context->printer);
-  pp_set_prefix (context->printer, NULL);
-  pp_space (context->printer);
-  while (max_width > 0 && line_width > 0)
+/* Attempt to print line ROW of source code, potentially colorized at any
+   ranges.
+   Return true if the line was printed, populating *LBOUNDS_OUT.
+   Return false if the source line could not be read, leaving *LBOUNDS_OUT
+   untouched.  */
+
+bool
+layout::print_source_line (int row, line_bounds *lbounds_out)
+{
+  int line_width;
+  const char *line = location_get_source_line (m_exploc.file, row,
+                                              &line_width);
+  if (!line)
+    return false;
+
+  line += m_x_offset;
+
+  m_colorizer.set_normal_text ();
+
+  /* We will stop printing the source line at any trailing
+     whitespace.  */
+  line_width = get_line_width_without_trailing_whitespace (line,
+                                                          line_width);
+
+  pp_space (m_pp);
+  int first_non_ws = INT_MAX;
+  int last_non_ws = 0;
+  int column;
+  for (column = 1 + m_x_offset; column <= line_width; column++)
     {
+      /* Assuming colorization is enabled for the caret and underline
+        characters, we may also colorize the associated characters
+        within the source line.
+
+        For frontends that generate range information, we color the
+        associated characters in the source line the same as the
+        carets and underlines in the annotation line, to make it easier
+        for the reader to see the pertinent code.
+
+        For frontends that only generate carets, we don't colorize the
+        characters above them, since this would look strange (e.g.
+        colorizing just the first character in a token).  */
+      if (m_colorize_source_p)
+       {
+         bool in_range_p;
+         point_state state;
+         in_range_p = get_state_at_point (row, column,
+                                          0, INT_MAX,
+                                          &state);
+         if (in_range_p)
+           m_colorizer.set_range (state.range_idx);
+         else
+           m_colorizer.set_normal_text ();
+       }
       char c = *line == '\t' ? ' ' : *line;
       if (c == '\0')
        c = ' ';
-      pp_character (context->printer, c);
-      max_width--;
-      line_width--;
+      if (c != ' ')
+       {
+         last_non_ws = column;
+         if (first_non_ws == INT_MAX)
+           first_non_ws = column;
+       }
+      pp_character (m_pp, c);
       line++;
     }
-  pp_newline (context->printer);
+  pp_newline (m_pp);
+
+  lbounds_out->m_first_non_ws = first_non_ws;
+  lbounds_out->m_last_non_ws = last_non_ws;
+  return true;
+}
+
+/* Print a line consisting of the caret/underlines for the given
+   source line.  */
 
-  /* Print the caret under the line.  */
-  const char *caret_cs, *caret_ce;
-  caret_cs = colorize_start (pp_show_color (context->printer), "caret");
-  caret_ce = colorize_stop (pp_show_color (context->printer));
-  int cmin = xloc2.column
-    ? MIN (xloc1.column, xloc2.column) : xloc1.column;
-  int caret_min = cmin == xloc1.column ? caret1 : caret2;
-  int caret_max = cmin == xloc1.column ? caret2 : caret1;
-
-  /* cmin is >= 1, but we indent with an extra space at the start like
-     we did above.  */
+void
+layout::print_annotation_line (int row, const line_bounds lbounds)
+{
+  int x_bound = get_x_bound_for_row (row, m_exploc.column,
+                                    lbounds.m_last_non_ws);
+
+  pp_space (m_pp);
+  for (int column = 1 + m_x_offset; column < x_bound; column++)
+    {
+      bool in_range_p;
+      point_state state;
+      in_range_p = get_state_at_point (row, column,
+                                      lbounds.m_first_non_ws,
+                                      lbounds.m_last_non_ws,
+                                      &state);
+      if (in_range_p)
+       {
+         /* 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]);
+         else
+           pp_character (m_pp, '~');
+       }
+      else
+       {
+         /* Not in a range.  */
+         m_colorizer.set_normal_text ();
+         pp_character (m_pp, ' ');
+       }
+    }
+  pp_newline (m_pp);
+}
+
+/* Return true if (ROW/COLUMN) is within a range of the layout.
+   If it returns true, OUT_STATE is written to, with the
+   range index, and whether we should draw the caret at
+   (ROW/COLUMN) (as opposed to an underline).  */
+
+bool
+layout::get_state_at_point (/* Inputs.  */
+                           int row, int column,
+                           int first_non_ws, int last_non_ws,
+                           /* Outputs.  */
+                           point_state *out_state)
+{
+  layout_range *range;
   int i;
-  for (i = 0; i < cmin; i++)
-    pp_space (context->printer);
-  pp_printf (context->printer, "%s%c%s", caret_cs, caret_min, caret_ce);
+  FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
+    {
+      if (range->contains_point (row, column))
+       {
+         out_state->range_idx = i;
+
+         /* Are we at the range's caret?  is it visible? */
+         out_state->draw_caret_p = false;
+         if (row == range->m_caret.m_line
+             && column == range->m_caret.m_column)
+           out_state->draw_caret_p = range->m_show_caret_p;
 
-  if (xloc2.column)
+         /* Within a multiline range, don't display any underline
+            in any leading or trailing whitespace on a line.
+            We do display carets, however.  */
+         if (!out_state->draw_caret_p)
+           if (column < first_non_ws || column > last_non_ws)
+             return false;
+
+         /* We are within a range.  */
+         return true;
+       }
+    }
+
+  return false;
+}
+
+/* Helper function for use by layout::print_line when printing the
+   annotation line under the source line.
+   Get the column beyond the rightmost one that could contain a caret or
+   range marker, given that we stop rendering at trailing whitespace.
+   ROW is the source line within the given file.
+   CARET_COLUMN is the column of range 0's caret.
+   LAST_NON_WS_COLUMN is the last column containing a non-whitespace
+   character of source (as determined when printing the source line).  */
+
+int
+layout::get_x_bound_for_row (int row, int caret_column,
+                            int last_non_ws_column)
+{
+  int result = caret_column + 1;
+
+  layout_range *range;
+  int i;
+  FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
     {
-      for (i++; i < cmax; i++)
-       pp_space (context->printer);
-      pp_printf (context->printer, "%s%c%s", caret_cs, caret_max, caret_ce);
+      if (row >= range->m_start.m_line)
+       {
+         if (range->m_finish.m_line == row)
+           {
+             /* On the final line within a range; ensure that
+                we render up to the end of the range.  */
+             if (result <= range->m_finish.m_column)
+               result = range->m_finish.m_column + 1;
+           }
+         else if (row < range->m_finish.m_line)
+           {
+             /* Within a multiline range; ensure that we render up to the
+                last non-whitespace column.  */
+             if (result <= last_non_ws_column)
+               result = last_non_ws_column + 1;
+           }
+       }
     }
+
+  return result;
+}
+
+} /* End of anonymous namespace.  */
+
+/* Print the physical source code corresponding to the location of
+   this diagnostic, with additional annotations.  */
+
+void
+diagnostic_show_locus (diagnostic_context * context,
+                      const diagnostic_info *diagnostic)
+{
+  if (!context->show_caret
+      || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION
+      || diagnostic_location (diagnostic, 0) == context->last_location)
+    return;
+
+  context->last_location = diagnostic_location (diagnostic, 0);
+
+  pp_newline (context->printer);
+
+  const char *saved_prefix = pp_get_prefix (context->printer);
+  pp_set_prefix (context->printer, NULL);
+
+  {
+    layout layout (context, diagnostic);
+    int last_line = layout.get_last_line ();
+    for (int row = layout.get_first_line ();
+        row <= last_line;
+        row++)
+      {
+       /* Print the source line, followed by an annotation line
+          consisting of any caret/underlines.  If the source line can't
+          be read, print nothing.  */
+       line_bounds lbounds;
+       if (layout.print_source_line (row, &lbounds))
+         layout.print_annotation_line (row, lbounds);
+      }
+
+    /* The closing scope here leads to the dtor for layout and thus
+       colorizer being called here, which affects the precise
+       place where colorization is turned off in the unittest
+       for colorized output.  */
+  }
+
   pp_set_prefix (context->printer, saved_prefix);
-  pp_needs_newline (context->printer) = true;
 }
index 11c369d829277aff97ef028d248d21421e269d40..ee034e7717014f2054915b1f53296f316caa28e2 100644 (file)
@@ -144,7 +144,7 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
     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 < MAX_LOCATIONS_PER_MESSAGE; i++)
+  for (i = 0; i < rich_location::MAX_RANGES; i++)
     context->caret_chars[i] = '^';
   context->show_option_requested = false;
   context->abort_on_error = false;
@@ -234,16 +234,15 @@ diagnostic_finish (diagnostic_context *context)
    translated.  */
 void
 diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg,
-                               va_list *args, location_t location,
+                               va_list *args, rich_location *richloc,
                                diagnostic_t kind)
 {
+  gcc_assert (richloc);
   diagnostic->message.err_no = errno;
   diagnostic->message.args_ptr = args;
   diagnostic->message.format_spec = msg;
-  diagnostic->message.set_location (0, location);
-  for (int i = 1; i < MAX_LOCATIONS_PER_MESSAGE; i++)
-    diagnostic->message.set_location (i, UNKNOWN_LOCATION);
-  diagnostic->override_column = 0;
+  diagnostic->message.m_richloc = richloc;
+  diagnostic->richloc = richloc;
   diagnostic->kind = kind;
   diagnostic->option_index = 0;
 }
@@ -252,10 +251,27 @@ diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg,
    translated.  */
 void
 diagnostic_set_info (diagnostic_info *diagnostic, const char *gmsgid,
-                    va_list *args, location_t location,
+                    va_list *args, rich_location *richloc,
                     diagnostic_t kind)
 {
-  diagnostic_set_info_translated (diagnostic, _(gmsgid), args, location, kind);
+  gcc_assert (richloc);
+  diagnostic_set_info_translated (diagnostic, _(gmsgid), args, richloc, kind);
+}
+
+static const char *const diagnostic_kind_color[] = {
+#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (C),
+#include "diagnostic.def"
+#undef DEFINE_DIAGNOSTIC_KIND
+  NULL
+};
+
+/* Get a color name for diagnostics of type KIND
+   Result could be NULL.  */
+
+const char *
+diagnostic_get_color_for_kind (diagnostic_t kind)
+{
+  return diagnostic_kind_color[kind];
 }
 
 /* Return a malloc'd string describing a location.  The caller is
@@ -270,12 +286,6 @@ diagnostic_build_prefix (diagnostic_context *context,
 #undef DEFINE_DIAGNOSTIC_KIND
     "must-not-happen"
   };
-  static const char *const diagnostic_kind_color[] = {
-#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (C),
-#include "diagnostic.def"
-#undef DEFINE_DIAGNOSTIC_KIND
-    NULL
-  };
   gcc_assert (diagnostic->kind < DK_LAST_DIAGNOSTIC_KIND);
 
   const char *text = _(diagnostic_kind_text[diagnostic->kind]);
@@ -770,10 +780,14 @@ diagnostic_report_diagnostic (diagnostic_context *context,
 
       if (option_text)
        {
+         const char *cs
+           = colorize_start (pp_show_color (context->printer),
+                             diagnostic_kind_color[diagnostic->kind]);
+         const char *ce = colorize_stop (pp_show_color (context->printer));
          diagnostic->message.format_spec
            = ACONCAT ((diagnostic->message.format_spec,
                        " ", 
-                       "[", option_text, "]",
+                       "[", cs, option_text, ce, "]",
                        NULL));
          free (option_text);
        }
@@ -853,9 +867,40 @@ diagnostic_append_note (diagnostic_context *context,
   diagnostic_info diagnostic;
   va_list ap;
   const char *saved_prefix;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_NOTE);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_NOTE);
+  if (context->inhibit_notes_p)
+    {
+      va_end (ap);
+      return;
+    }
+  saved_prefix = pp_get_prefix (context->printer);
+  pp_set_prefix (context->printer,
+                 diagnostic_build_prefix (context, &diagnostic));
+  pp_newline (context->printer);
+  pp_format (context->printer, &diagnostic.message);
+  pp_output_formatted_text (context->printer);
+  pp_destroy_prefix (context->printer);
+  pp_set_prefix (context->printer, saved_prefix);
+  diagnostic_show_locus (context, &diagnostic);
+  va_end (ap);
+}
+
+/* Same as diagnostic_append_note, but at RICHLOC. */
+
+void
+diagnostic_append_note_at_rich_loc (diagnostic_context *context,
+                                   rich_location *richloc,
+                                   const char * gmsgid, ...)
+{
+  diagnostic_info diagnostic;
+  va_list ap;
+  const char *saved_prefix;
+
+  va_start (ap, gmsgid);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_NOTE);
   if (context->inhibit_notes_p)
     {
       va_end (ap);
@@ -880,16 +925,17 @@ emit_diagnostic (diagnostic_t kind, location_t location, int opt,
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
   if (kind == DK_PERMERROR)
     {
-      diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+      diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
                           permissive_error_kind (global_dc));
       diagnostic.option_index = permissive_error_option (global_dc);
     }
   else {
-      diagnostic_set_info (&diagnostic, gmsgid, &ap, location, kind);
+      diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, kind);
       if (kind == DK_WARNING || kind == DK_PEDWARN)
        diagnostic.option_index = opt;
   }
@@ -903,12 +949,26 @@ emit_diagnostic (diagnostic_t kind, location_t location, int opt,
    message.  */
 void
 inform (location_t location, const char *gmsgid, ...)
+{
+  diagnostic_info diagnostic;
+  va_list ap;
+  rich_location richloc (location);
+
+  va_start (ap, gmsgid);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_NOTE);
+  report_diagnostic (&diagnostic);
+  va_end (ap);
+}
+
+/* Same as "inform", but at RICHLOC.  */
+void
+inform_at_rich_loc (rich_location *richloc, const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_NOTE);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_NOTE);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -921,11 +981,12 @@ inform_n (location_t location, int n, const char *singular_gmsgid,
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (location);
 
   va_start (ap, plural_gmsgid);
   diagnostic_set_info_translated (&diagnostic,
                                   ngettext (singular_gmsgid, plural_gmsgid, n),
-                                  &ap, location, DK_NOTE);
+                                  &ap, &richloc, DK_NOTE);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -939,9 +1000,10 @@ warning (int opt, const char *gmsgid, ...)
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (input_location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_WARNING);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_WARNING);
   diagnostic.option_index = opt;
 
   ret = report_diagnostic (&diagnostic);
@@ -955,13 +1017,31 @@ warning (int opt, const char *gmsgid, ...)
 
 bool
 warning_at (location_t location, int opt, const char *gmsgid, ...)
+{
+  diagnostic_info diagnostic;
+  va_list ap;
+  bool ret;
+  rich_location richloc (location);
+
+  va_start (ap, gmsgid);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_WARNING);
+  diagnostic.option_index = opt;
+  ret = report_diagnostic (&diagnostic);
+  va_end (ap);
+  return ret;
+}
+
+/* Same as warning at, but using RICHLOC.  */
+
+bool
+warning_at_rich_loc (rich_location *richloc, int opt, const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_WARNING);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_WARNING);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
   va_end (ap);
@@ -979,11 +1059,13 @@ warning_n (location_t location, int opt, int n, const char *singular_gmsgid,
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (location);
 
   va_start (ap, plural_gmsgid);
   diagnostic_set_info_translated (&diagnostic,
                                   ngettext (singular_gmsgid, plural_gmsgid, n),
-                                  &ap, location, DK_WARNING);
+                                  &ap, &richloc, DK_WARNING
+);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
   va_end (ap);
@@ -1009,9 +1091,10 @@ pedwarn (location_t location, int opt, const char *gmsgid, ...)
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location,  DK_PEDWARN);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,  DK_PEDWARN);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
   va_end (ap);
@@ -1031,9 +1114,28 @@ permerror (location_t location, const char *gmsgid, ...)
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
+                       permissive_error_kind (global_dc));
+  diagnostic.option_index = permissive_error_option (global_dc);
+  ret = report_diagnostic (&diagnostic);
+  va_end (ap);
+  return ret;
+}
+
+/* Same as "permerror", but at RICHLOC.  */
+
+bool
+permerror_at_rich_loc (rich_location *richloc, const char *gmsgid, ...)
+{
+  diagnostic_info diagnostic;
+  va_list ap;
+  bool ret;
+
+  va_start (ap, gmsgid);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc,
                        permissive_error_kind (global_dc));
   diagnostic.option_index = permissive_error_option (global_dc);
   ret = report_diagnostic (&diagnostic);
@@ -1048,9 +1150,10 @@ error (const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (input_location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ERROR);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ERROR);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -1063,11 +1166,12 @@ error_n (location_t location, int n, const char *singular_gmsgid,
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (location);
 
   va_start (ap, plural_gmsgid);
   diagnostic_set_info_translated (&diagnostic,
                                   ngettext (singular_gmsgid, plural_gmsgid, n),
-                                  &ap, location, DK_ERROR);
+                                  &ap, &richloc, DK_ERROR);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -1078,9 +1182,25 @@ error_at (location_t loc, const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (loc);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, loc, DK_ERROR);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ERROR);
+  report_diagnostic (&diagnostic);
+  va_end (ap);
+}
+
+/* Same as above, but use RICH_LOC.  */
+
+void
+error_at_rich_loc (rich_location *rich_loc, const char *gmsgid, ...)
+{
+  diagnostic_info diagnostic;
+  va_list ap;
+
+  va_start (ap, gmsgid);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, rich_loc,
+                      DK_ERROR);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -1093,9 +1213,10 @@ sorry (const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (input_location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_SORRY);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_SORRY);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -1116,9 +1237,10 @@ fatal_error (location_t loc, const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (loc);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, loc, DK_FATAL);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_FATAL);
   report_diagnostic (&diagnostic);
   va_end (ap);
 
@@ -1134,9 +1256,10 @@ internal_error (const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (input_location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ICE);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ICE);
   report_diagnostic (&diagnostic);
   va_end (ap);
 
@@ -1151,9 +1274,10 @@ internal_error_no_backtrace (const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (input_location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ICE_NOBT);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ICE_NOBT);
   report_diagnostic (&diagnostic);
   va_end (ap);
 
@@ -1217,3 +1341,17 @@ real_abort (void)
 {
   abort ();
 }
+
+/* Display the given source_range instance, with MSG as a descriptive
+   comment.  This issues a "note" diagnostic at the range.
+
+   This is declared within libcpp, but implemented here, since it
+   makes use of the diagnostic-printing machinery.  */
+
+DEBUG_FUNCTION void
+source_range::debug (const char *msg) const
+{
+  rich_location richloc (m_start);
+  richloc.add_range (m_start, m_finish, false);
+  inform_at_rich_loc (&richloc, "%s", msg);
+}
index 7fcb6a8cd0eb4b856cea01eb4c434e22d8728f9a..9096e16eb07c953842ea3bc2ceabc1b65693deee 100644 (file)
@@ -29,10 +29,12 @@ along with GCC; see the file COPYING3.  If not see
    list in diagnostic.def.  */
 struct diagnostic_info
 {
-  /* Text to be formatted. It also contains the location(s) for this
-     diagnostic.  */
+  /* Text to be formatted.  */
   text_info message;
-  unsigned int override_column;
+
+  /* The location at which the diagnostic is to be reported.  */
+  rich_location *richloc;
+
   /* Auxiliary data for client.  */
   void *x_data;
   /* The kind of diagnostic it is about.  */
@@ -102,8 +104,8 @@ struct diagnostic_context
   /* Maximum width of the source line printed.  */
   int caret_max_width;
 
-  /* Characters used for caret diagnostics.  */
-  char caret_chars[MAX_LOCATIONS_PER_MESSAGE];
+  /* Character used for caret diagnostics.  */
+  char caret_chars[rich_location::MAX_RANGES];
 
   /* True if we should print the command line option which controls
      each diagnostic, if known.  */
@@ -181,6 +183,15 @@ struct diagnostic_context
   int lock;
 
   bool inhibit_notes_p;
+
+  /* When printing source code, should the characters at carets and ranges
+     be colorized? (assuming colorization is on at all).
+     This should be true for frontends that generate range information
+     (so that the ranges of code are colorized),
+     and false for frontends that merely specify points within the
+     source code (to avoid e.g. colorizing just the first character in
+     a token, which would look strange).  */
+  bool colorize_source_p;
 };
 
 static inline void
@@ -252,10 +263,6 @@ extern diagnostic_context *global_dc;
 
 #define report_diagnostic(D) diagnostic_report_diagnostic (global_dc, D)
 
-/* Override the column number to be used for reporting a
-   diagnostic.  */
-#define diagnostic_override_column(DI, COL) (DI)->override_column = (COL)
-
 /* Override the option index to be used for reporting a
    diagnostic.  */
 #define diagnostic_override_option_index(DI, OPTIDX) \
@@ -279,13 +286,17 @@ extern bool diagnostic_report_diagnostic (diagnostic_context *,
                                          diagnostic_info *);
 #ifdef ATTRIBUTE_GCC_DIAG
 extern void diagnostic_set_info (diagnostic_info *, const char *, va_list *,
-                                location_t, diagnostic_t) ATTRIBUTE_GCC_DIAG(2,0);
+                                rich_location *, diagnostic_t) ATTRIBUTE_GCC_DIAG(2,0);
 extern void diagnostic_set_info_translated (diagnostic_info *, const char *,
-                                           va_list *, location_t,
+                                           va_list *, rich_location *,
                                            diagnostic_t)
      ATTRIBUTE_GCC_DIAG(2,0);
 extern void diagnostic_append_note (diagnostic_context *, location_t,
                                     const char *, ...) ATTRIBUTE_GCC_DIAG(3,4);
+extern void diagnostic_append_note_at_rich_loc (diagnostic_context *,
+                                               rich_location *,
+                                               const char *, ...)
+  ATTRIBUTE_GCC_DIAG(3,4);
 #endif
 extern char *diagnostic_build_prefix (diagnostic_context *, const diagnostic_info *);
 void default_diagnostic_starter (diagnostic_context *, diagnostic_info *);
@@ -306,6 +317,14 @@ diagnostic_location (const diagnostic_info * diagnostic, int which = 0)
   return diagnostic->message.get_location (which);
 }
 
+/* Return the number of locations to be printed in DIAGNOSTIC.  */
+
+static inline unsigned int
+diagnostic_num_locations (const diagnostic_info * diagnostic)
+{
+  return diagnostic->message.m_richloc->get_num_locations ();
+}
+
 /* Expand the location of this diagnostic. Use this function for
    consistency.  Parameter WHICH specifies which location. By default,
    expand the first one.  */
@@ -313,12 +332,7 @@ diagnostic_location (const diagnostic_info * diagnostic, int which = 0)
 static inline expanded_location
 diagnostic_expand_location (const diagnostic_info * diagnostic, int which = 0)
 {
-  expanded_location s
-    = expand_location_to_spelling_point (diagnostic_location (diagnostic,
-                                                             which));
-  if (which == 0 && diagnostic->override_column)
-    s.column = diagnostic->override_column;
-  return s;
+  return diagnostic->richloc->get_range (which)->m_caret;
 }
 
 /* This is somehow the right-side margin of a caret line, that is, we
@@ -338,11 +352,7 @@ diagnostic_same_line (const diagnostic_context *context,
     && context->caret_max_width - CARET_LINE_MARGIN > abs (s1.column - s2.column);
 }
 
-void
-diagnostic_print_caret_line (diagnostic_context * context,
-                            expanded_location xloc1,
-                            expanded_location xloc2,
-                            char caret1, char caret2);
+extern const char *diagnostic_get_color_for_kind (diagnostic_t kind);
 
 /* Pure text formatting support functions.  */
 extern char *file_name_as_prefix (diagnostic_context *, const char *);
index bd3fa7647a50a4d7bef065cf539aa49fc4bcafe7..acbb1b75f060a488b801ecc3ef58795c31a7beed 100644 (file)
@@ -1,3 +1,24 @@
+2015-11-06  David Malcolm  <dmalcolm@redhat.com>
+
+       * 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.
+
 2015-11-05  Cesar Philippidis  <cesar@codesourcery.com>
 
        * openmp.c (gfc_match_omp_clauses): Update support for the tile
index 7d296255b42e9f9d6dba188a2e7324326af1356d..85a2c79cd70965ea04cbb9f5374129347d2d1f81 100644 (file)
@@ -147,9 +147,9 @@ static void cb_include (cpp_reader *, source_location, const unsigned char *,
 static void cb_ident (cpp_reader *, source_location, const cpp_string *);
 static void cb_used_define (cpp_reader *, source_location, cpp_hashnode *);
 static void cb_used_undef (cpp_reader *, source_location, cpp_hashnode *);
-static bool cb_cpp_error (cpp_reader *, int, int, location_t, unsigned int,
+static bool cb_cpp_error (cpp_reader *, int, int, rich_location *,
                          const char *, va_list *)
-     ATTRIBUTE_GCC_DIAG(6,0);
+     ATTRIBUTE_GCC_DIAG(5,0);
 void pp_dir_change (cpp_reader *, const char *);
 
 static int dump_macro (cpp_reader *, cpp_hashnode *, void *);
@@ -1024,13 +1024,12 @@ cb_used_define (cpp_reader *pfile, source_location line ATTRIBUTE_UNUSED,
 /* Callback from cpp_error for PFILE to print diagnostics from the
    preprocessor.  The diagnostic is of type LEVEL, with REASON set
    to the reason code if LEVEL is represents a warning, at location
-   LOCATION, with column number possibly overridden by COLUMN_OVERRIDE
-   if not zero; MSG is the translated message and AP the arguments.
+   RICHLOC; MSG is the translated message and AP the arguments.
    Returns true if a diagnostic was emitted, false otherwise.  */
 
 static bool
 cb_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
-             location_t location, unsigned int column_override,
+             rich_location *richloc,
              const char *msg, va_list *ap)
 {
   diagnostic_info diagnostic;
@@ -1065,9 +1064,7 @@ cb_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
       gcc_unreachable ();
     }
   diagnostic_set_info_translated (&diagnostic, msg, ap,
-                                 location, dlevel);
-  if (column_override)
-    diagnostic_override_column (&diagnostic, column_override);
+                                 richloc, dlevel);
   if (reason == CPP_W_WARNING_DIRECTIVE)
     diagnostic_override_option_index (&diagnostic, OPT_Wcpp);
   ret = report_diagnostic (&diagnostic);
index 3825751ddd01f0e5811e4937f107c877d746ded5..4b3d31ce4ba3c47950d992d66270271a628a8f72 100644 (file)
@@ -773,6 +773,7 @@ gfc_warning (int opt, const char *gmsgid, va_list ap)
   va_copy (argp, ap);
 
   diagnostic_info diagnostic;
+  rich_location rich_loc (UNKNOWN_LOCATION);
   bool fatal_errors = global_dc->fatal_errors;
   pretty_printer *pp = global_dc->printer;
   output_buffer *tmp_buffer = pp->buffer;
@@ -787,7 +788,7 @@ gfc_warning (int opt, const char *gmsgid, va_list ap)
       --werrorcount;
     }
 
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION,
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc,
                       DK_WARNING);
   diagnostic.option_index = opt;
   bool ret = report_diagnostic (&diagnostic);
@@ -938,10 +939,12 @@ gfc_format_decoder (pretty_printer *pp,
        /* If location[0] != UNKNOWN_LOCATION means that we already
           processed one of %C/%L.  */
        int loc_num = text->get_location (0) == UNKNOWN_LOCATION ? 0 : 1;
-       text->set_location (loc_num,
-                           linemap_position_for_loc_and_offset (line_table,
-                                                                loc->lb->location,
-                                                                offset));
+       source_range range
+         = source_range::from_location (
+             linemap_position_for_loc_and_offset (line_table,
+                                                  loc->lb->location,
+                                                  offset));
+       text->set_range (loc_num, range, true);
        pp_string (pp, result[loc_num]);
        return true;
       }
@@ -1024,48 +1027,21 @@ gfc_diagnostic_build_locus_prefix (diagnostic_context *context,
 }
 
 /* This function prints the locus (file:line:column), the diagnostic kind
-   (Error, Warning) and (optionally) the caret line (a source line
-   with '1' and/or '2' below it).
+   (Error, Warning) and (optionally) the relevant lines of code with
+   annotation lines with '1' and/or '2' below them.
 
-   With -fdiagnostic-show-caret (the default) and for valid locations,
-   it prints for one location:
+   With -fdiagnostic-show-caret (the default) it prints:
 
-       [locus]:
+       [locus of primary range]:
        
           some code
                  1
        Error: Some error at (1)
         
-   for two locations that fit in the same locus line:
+  With -fno-diagnostic-show-caret or if the primary range is not
+  valid, it prints:
 
-       [locus]:
-       
-         some code and some more code
-                1       2
-       Error: Some error at (1) and (2)
-
-   and for two locations that do not fit in the same locus line:
-
-       [locus]:
-       
-         some code
-                1
-       [locus2]:
-       
-         some other code
-           2
-       Error: Some error at (1) and (2)
-       
-  With -fno-diagnostic-show-caret or if one of the locations is not
-  valid, it prints for one location (or for two locations that fit in
-  the same locus line):
-
-       [locus]: Error: Some error at (1) and (2)
-
-   and for two locations that do not fit in the same locus line:
-
-       [name]:[locus]: Error: (1)
-       [name]:[locus2]: Error: Some error at (1) and (2)
+       [locus of primary range]: Error: Some error at (1) and (2)
 */
 static void 
 gfc_diagnostic_starter (diagnostic_context *context,
@@ -1075,7 +1051,7 @@ gfc_diagnostic_starter (diagnostic_context *context,
 
   expanded_location s1 = diagnostic_expand_location (diagnostic);
   expanded_location s2;
-  bool one_locus = diagnostic_location (diagnostic, 1) == UNKNOWN_LOCATION;
+  bool one_locus = diagnostic->richloc->get_num_locations () < 2;
   bool same_locus = false;
 
   if (!one_locus) 
@@ -1125,35 +1101,6 @@ gfc_diagnostic_starter (diagnostic_context *context,
       /* If the caret line was shown, the prefix does not contain the
         locus.  */
       pp_set_prefix (context->printer, kind_prefix);
-
-      if (one_locus || same_locus)
-         return;
-
-      locus_prefix = gfc_diagnostic_build_locus_prefix (context, s2);
-      if (diagnostic_location (diagnostic, 1) <= BUILTINS_LOCATION)
-       {
-         /* No caret line for the second location. Override the previous
-            prefix with [locus2]:[prefix].  */
-         pp_set_prefix (context->printer,
-                        concat (locus_prefix, " ", kind_prefix, NULL));
-         free (kind_prefix);
-         free (locus_prefix);
-       }
-      else
-       {
-         /* We print the caret for the second location.  */
-         pp_verbatim (context->printer, locus_prefix);
-         free (locus_prefix);
-         /* Fortran uses an empty line between locus and caret line.  */
-         pp_newline (context->printer);
-         s1.column = 0; /* Print only a caret line for s2.  */
-         diagnostic_print_caret_line (context, s2, s1,
-                                      context->caret_chars[1], '\0');
-         pp_newline (context->printer);
-         /* If the caret line was shown, the prefix does not contain the
-            locus.  */
-         pp_set_prefix (context->printer, kind_prefix);
-       }
     }
 }
 
@@ -1173,10 +1120,11 @@ gfc_warning_now_at (location_t loc, int opt, const char *gmsgid, ...)
 {
   va_list argp;
   diagnostic_info diagnostic;
+  rich_location rich_loc (loc);
   bool ret;
 
   va_start (argp, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, loc, DK_WARNING);
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_WARNING);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
   va_end (argp);
@@ -1190,10 +1138,11 @@ gfc_warning_now (int opt, const char *gmsgid, ...)
 {
   va_list argp;
   diagnostic_info diagnostic;
+  rich_location rich_loc (UNKNOWN_LOCATION);
   bool ret;
 
   va_start (argp, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION,
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc,
                       DK_WARNING);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
@@ -1209,11 +1158,12 @@ gfc_error_now (const char *gmsgid, ...)
 {
   va_list argp;
   diagnostic_info diagnostic;
+  rich_location rich_loc (UNKNOWN_LOCATION);
 
   error_buffer.flag = true;
 
   va_start (argp, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ERROR);
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_ERROR);
   report_diagnostic (&diagnostic);
   va_end (argp);
 }
@@ -1226,9 +1176,10 @@ gfc_fatal_error (const char *gmsgid, ...)
 {
   va_list argp;
   diagnostic_info diagnostic;
+  rich_location rich_loc (UNKNOWN_LOCATION);
 
   va_start (argp, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_FATAL);
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_FATAL);
   report_diagnostic (&diagnostic);
   va_end (argp);
 
@@ -1291,6 +1242,7 @@ gfc_error (const char *gmsgid, va_list ap)
     }
 
   diagnostic_info diagnostic;
+  rich_location richloc (UNKNOWN_LOCATION);
   bool fatal_errors = global_dc->fatal_errors;
   pretty_printer *pp = global_dc->printer;
   output_buffer *tmp_buffer = pp->buffer;
@@ -1306,7 +1258,7 @@ gfc_error (const char *gmsgid, va_list ap)
       --errorcount;
     }
 
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ERROR);
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &richloc, DK_ERROR);
   report_diagnostic (&diagnostic);
 
   if (buffered_p)
@@ -1336,9 +1288,10 @@ gfc_internal_error (const char *gmsgid, ...)
 {
   va_list argp;
   diagnostic_info diagnostic;
+  rich_location rich_loc (UNKNOWN_LOCATION);
 
   va_start (argp, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ICE);
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_ICE);
   report_diagnostic (&diagnostic);
   va_end (argp);
 
index b5a0fffa714e1df19b2e12ebaf0d40360cc7f89a..ad502f972620e373d7d8e25cfd2d1cf8390723e5 100644 (file)
@@ -53,14 +53,31 @@ unsigned verbose;
 
 static struct line_maps *line_table;
 
+/* The rich_location class within libcpp requires a way to expand
+   source_location instances, and relies on the client code
+   providing a symbol named
+     linemap_client_expand_location_to_spelling_point
+   to do this.
+
+   This is the implementation for genmatch.  */
+
+expanded_location
+linemap_client_expand_location_to_spelling_point (source_location loc)
+{
+  const struct line_map_ordinary *map;
+  loc = linemap_resolve_location (line_table, loc, LRK_SPELLING_LOCATION, &map);
+  return linemap_expand_location (line_table, map, loc);
+}
+
 static bool
 #if GCC_VERSION >= 4001
-__attribute__((format (printf, 6, 0)))
+__attribute__((format (printf, 5, 0)))
 #endif
-error_cb (cpp_reader *, int errtype, int, source_location location,
-         unsigned int, const char *msg, va_list *ap)
+error_cb (cpp_reader *, int errtype, int, rich_location *richloc,
+         const char *msg, va_list *ap)
 {
   const line_map_ordinary *map;
+  source_location location = richloc->get_loc ();
   linemap_resolve_location (line_table, location, LRK_SPELLING_LOCATION, &map);
   expanded_location loc = linemap_expand_location (line_table, map, location);
   fprintf (stderr, "%s:%d:%d %s: ", loc.file, loc.line, loc.column,
@@ -102,9 +119,10 @@ __attribute__((format (printf, 2, 3)))
 #endif
 fatal_at (const cpp_token *tk, const char *msg, ...)
 {
+  rich_location richloc (tk->src_loc);
   va_list ap;
   va_start (ap, msg);
-  error_cb (NULL, CPP_DL_FATAL, 0, tk->src_loc, 0, msg, &ap);
+  error_cb (NULL, CPP_DL_FATAL, 0, &richloc, msg, &ap);
   va_end (ap);
 }
 
@@ -114,9 +132,10 @@ __attribute__((format (printf, 2, 3)))
 #endif
 fatal_at (source_location loc, const char *msg, ...)
 {
+  rich_location richloc (loc);
   va_list ap;
   va_start (ap, msg);
-  error_cb (NULL, CPP_DL_FATAL, 0, loc, 0, msg, &ap);
+  error_cb (NULL, CPP_DL_FATAL, 0, &richloc, msg, &ap);
   va_end (ap);
 }
 
@@ -126,9 +145,10 @@ __attribute__((format (printf, 2, 3)))
 #endif
 warning_at (const cpp_token *tk, const char *msg, ...)
 {
+  rich_location richloc (tk->src_loc);
   va_list ap;
   va_start (ap, msg);
-  error_cb (NULL, CPP_DL_WARNING, 0, tk->src_loc, 0, msg, &ap);
+  error_cb (NULL, CPP_DL_WARNING, 0, &richloc, msg, &ap);
   va_end (ap);
 }
 
@@ -138,9 +158,10 @@ __attribute__((format (printf, 2, 3)))
 #endif
 warning_at (source_location loc, const char *msg, ...)
 {
+  rich_location richloc (loc);
   va_list ap;
   va_start (ap, msg);
-  error_cb (NULL, CPP_DL_WARNING, 0, loc, 0, msg, &ap);
+  error_cb (NULL, CPP_DL_WARNING, 0, &richloc, msg, &ap);
   va_end (ap);
 }
 
index ff80dd9708a09cc877f924b237ed67ed31842218..0f6d448b54638b6a81b2639acf1f5ca62e5c49f1 100644 (file)
@@ -751,6 +751,22 @@ expand_location_to_spelling_point (source_location loc)
   return expand_location_1 (loc, /*expansion_point_p=*/false);
 }
 
+/* The rich_location class within libcpp requires a way to expand
+   source_location instances, and relies on the client code
+   providing a symbol named
+     linemap_client_expand_location_to_spelling_point
+   to do this.
+
+   This is the implementation for libcommon.a (all host binaries),
+   which simply calls into expand_location_to_spelling_point.  */
+
+expanded_location
+linemap_client_expand_location_to_spelling_point (source_location loc)
+{
+  return expand_location_to_spelling_point (loc);
+}
+
+
 /* If LOCATION is in a system header and if it is a virtual location for
    a token coming from the expansion of a macro, unwind it to the
    location of the expansion point of the macro.  Otherwise, just return
index b24402de12dfa76a56ee2757262831e6270274fd..4a28d3c5fd9e683aa4faa60b098b2e09db48e27f 100644 (file)
@@ -31,6 +31,27 @@ along with GCC; see the file COPYING3.  If not see
 #include <iconv.h>
 #endif
 
+/* Overwrite the range within this text_info's rich_location.
+   For use e.g. when implementing "+" in client format decoders.  */
+
+void
+text_info::set_range (unsigned int idx, source_range range, bool caret_p)
+{
+  gcc_checking_assert (m_richloc);
+  m_richloc->set_range (idx, range, caret_p, true);
+}
+
+location_t
+text_info::get_location (unsigned int index_of_location) const
+{
+  gcc_checking_assert (m_richloc);
+
+  if (index_of_location == 0)
+    return m_richloc->get_loc ();
+  else
+    return UNKNOWN_LOCATION;
+}
+
 // Default construct an output buffer.
 
 output_buffer::output_buffer ()
index 2654b0fcbecbbd6ea63c91fdef2c1ba9151cf67e..cdee2535fe0f024ae4fd87607bf578907def5570 100644 (file)
@@ -27,11 +27,6 @@ along with GCC; see the file COPYING3.  If not see
 /* Maximum number of format string arguments.  */
 #define PP_NL_ARGMAX   30
 
-/* Maximum number of locations associated to each message.  If
-   location 'i' is UNKNOWN_LOCATION, then location 'i+1' is not
-   valid.  */
-#define MAX_LOCATIONS_PER_MESSAGE 2
-
 /* The type of a text to be formatted according a format specification
    along with a list of things.  */
 struct text_info
@@ -40,21 +35,17 @@ struct text_info
   va_list *args_ptr;
   int err_no;  /* for %m */
   void **x_data;
+  rich_location *m_richloc;
 
-  inline void set_location (unsigned int index_of_location, location_t loc)
+  inline void set_location (unsigned int idx, location_t loc, bool caret_p)
   {
-    gcc_checking_assert (index_of_location < MAX_LOCATIONS_PER_MESSAGE);
-    this->locations[index_of_location] = loc;
+    source_range src_range;
+    src_range.m_start = loc;
+    src_range.m_finish = loc;
+    set_range (idx, src_range, caret_p);
   }
-
-  inline location_t get_location (unsigned int index_of_location) const
-  {
-    gcc_checking_assert (index_of_location < MAX_LOCATIONS_PER_MESSAGE);
-    return this->locations[index_of_location];
-  }
-
-private:
-  location_t locations[MAX_LOCATIONS_PER_MESSAGE];
+  void set_range (unsigned int idx, source_range range, bool caret_p);
+  location_t get_location (unsigned int index_of_location) const;
 };
 
 /* How often diagnostics are prefixed by their locations:
index d461cd4ba33de05b3b316c49ee1bb55d385d8ae7..96da2bd275d3d3bb490000eb73367e7526fe94c6 100644 (file)
@@ -67,9 +67,10 @@ diagnostic_for_asm (const rtx_insn *insn, const char *msg, va_list *args_ptr,
                    diagnostic_t kind)
 {
   diagnostic_info diagnostic;
+  rich_location richloc (location_for_asm (insn));
 
   diagnostic_set_info (&diagnostic, msg, args_ptr,
-                      location_for_asm (insn), kind);
+                      &richloc, kind);
   report_diagnostic (&diagnostic);
 }
 
index efa3146aac80b5b7da295a9ca569f50a269c4452..9a0d1aa22f8e5b9bda20e5a2d6ffee9fe06fc02c 100644 (file)
@@ -1,3 +1,11 @@
+2015-11-06  David Malcolm  <dmalcolm@redhat.com>
+
+       * 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.
+
 2015-11-06  Ramana Radhakrishnan  <ramana.radhakrishnan@arm.com>
 
        * gcc.target/arm/combine-movs.c: Adjust for unified asm.
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw.c
new file mode 100644 (file)
index 0000000..a4b16da
--- /dev/null
@@ -0,0 +1,149 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret" } */
+
+/* This is a collection of unittests for diagnostic_show_locus;
+   see the overview in diagnostic_plugin_test_show_locus.c.
+
+   In particular, note the discussion of why we need a very long line here:
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+   and that we can't use macros in this file.  */
+
+void test_simple (void)
+{
+#if 0
+  myvar = myvar.x; /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+   myvar = myvar.x;
+           ~~~~~^~
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_simple_2 (void)
+{
+#if 0
+  x = first_function () + second_function ();  /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+   x = first_function () + second_function ();
+       ~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+
+void test_multiline (void)
+{
+#if 0
+  x = (first_function ()
+       + second_function ()); /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+   x = (first_function ()
+        ~~~~~~~~~~~~~~~~~
+        + second_function ());
+        ^ ~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_many_lines (void)
+{
+#if 0
+  x = (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet,
+                                            consectetur, adipiscing, elit,
+                                            sed, eiusmod, tempor,
+                                            incididunt, ut, labore, et,
+                                            dolore, magna, aliqua)
+       + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit, /* { dg-warning "test" } */
+                                                amet, consectetur,
+                                                adipiscing, elit, sed,
+                                                eiusmod, tempor, incididunt,
+                                                ut, labore, et, dolore,
+                                                magna, aliqua));
+
+/* { dg-begin-multiline-output "" }
+   x = (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet,
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                                             consectetur, adipiscing, elit,
+                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                                             sed, eiusmod, tempor,
+                                             ~~~~~~~~~~~~~~~~~~~~~
+                                             incididunt, ut, labore, et,
+                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                                             dolore, magna, aliqua)
+                                             ~~~~~~~~~~~~~~~~~~~~~~
+        + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit,
+        ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                                                 amet, consectetur,
+                                                 ~~~~~~~~~~~~~~~~~~
+                                                 adipiscing, elit, sed,
+                                                 ~~~~~~~~~~~~~~~~~~~~~~
+                                                 eiusmod, tempor, incididunt,
+                                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                                                 ut, labore, et, dolore,
+                                                 ~~~~~~~~~~~~~~~~~~~~~~~
+                                                 magna, aliqua));
+                                                 ~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_richloc_from_proper_range (void)
+{
+#if 0
+  float f = 98.6f; /* { dg-warning "test" } */
+/* { dg-begin-multiline-output "" }
+   float f = 98.6f;
+             ^~~~~
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_caret_within_proper_range (void)
+{
+#if 0
+  float f = foo * bar; /* { dg-warning "17: test" } */
+/* { dg-begin-multiline-output "" }
+   float f = foo * bar;
+             ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_very_wide_line (void)
+{
+#if 0
+                                                                                float f = foo * bar; /* { dg-warning "95: test" } */
+/* { dg-begin-multiline-output "" }
+                                              float f = foo * bar;
+                                                        ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_multiple_carets (void)
+{
+#if 0
+   x = x + y /* { dg-warning "8: test" } */
+/* { dg-begin-multiline-output "" }
+    x = x + y
+        A   B
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_caret_on_leading_whitespace (void)
+{
+#if 0
+    ASSOCIATE (y => x)
+      y = 5 /* { dg-warning "6: test" } */
+/* { dg-begin-multiline-output "" }
+     ASSOCIATE (y => x)
+                    2
+       y = 5
+      1
+   { dg-end-multiline-output "" } */
+#endif
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color.c
new file mode 100644 (file)
index 0000000..47639b2
--- /dev/null
@@ -0,0 +1,158 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret -fplugin-arg-diagnostic_plugin_test_show_locus-color" } */
+
+/* This is a collection of unittests for diagnostic_show_locus;
+   see the overview in diagnostic_plugin_test_show_locus.c.
+
+   In particular, note the discussion of why we need a very long line here:
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+   and that we can't use macros in this file.  */
+
+void test_simple (void)
+{
+#if 0
+  myvar = myvar.x; /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+   myvar = \e[32m\e[Kmyvar\e[m\e[K\e[01;35m\e[K.\e[m\e[K\e[34m\e[Kx\e[m\e[K;
+           \e[32m\e[K~~~~~\e[m\e[K\e[01;35m\e[K^\e[m\e[K\e[34m\e[K~
+\e[m\e[K
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_simple_2 (void)
+{
+#if 0
+  x = first_function () + second_function ();  /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+   x = \e[32m\e[Kfirst_function ()\e[m\e[K \e[01;35m\e[K+\e[m\e[K \e[34m\e[Ksecond_function ()\e[m\e[K;
+       \e[32m\e[K~~~~~~~~~~~~~~~~~\e[m\e[K \e[01;35m\e[K^\e[m\e[K \e[34m\e[K~~~~~~~~~~~~~~~~~~
+\e[m\e[K
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+
+void test_multiline (void)
+{
+#if 0
+  x = (first_function ()
+       + second_function ()); /* { dg-warning "test" } */
+
+/* { dg-begin-multiline-output "" }
+   x = (\e[32m\e[Kfirst_function ()
\e[m\e[K       \e[32m\e[K~~~~~~~~~~~~~~~~~
+\e[m\e[K        \e[01;35m\e[K+\e[m\e[K \e[34m\e[Ksecond_function ()\e[m\e[K);
+        \e[01;35m\e[K^\e[m\e[K \e[34m\e[K~~~~~~~~~~~~~~~~~~
+\e[m\e[K
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_many_lines (void)
+{
+#if 0
+  x = (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet,
+                                            consectetur, adipiscing, elit,
+                                            sed, eiusmod, tempor,
+                                            incididunt, ut, labore, et,
+                                            dolore, magna, aliqua)
+       + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit, /* { dg-warning "test" } */
+                                                amet, consectetur,
+                                                adipiscing, elit, sed,
+                                                eiusmod, tempor, incididunt,
+                                                ut, labore, et, dolore,
+                                                magna, aliqua));
+
+/* { dg-begin-multiline-output "" }
+   x = (\e[32m\e[Kfirst_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet,
\e[m\e[K       \e[32m\e[K~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[32m\e[K                                            consectetur, adipiscing, elit,
\e[m\e[K                                            \e[32m\e[K~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[32m\e[K                                            sed, eiusmod, tempor,
\e[m\e[K                                            \e[32m\e[K~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[32m\e[K                                            incididunt, ut, labore, et,
\e[m\e[K                                            \e[32m\e[K~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[32m\e[K                                            dolore, magna, aliqua)
\e[m\e[K                                            \e[32m\e[K~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K        \e[01;35m\e[K+\e[m\e[K \e[34m\e[Ksecond_function_with_a_very_long_name (lorem, ipsum, dolor, sit,
\e[m\e[K       \e[01;35m\e[K^\e[m\e[K \e[34m\e[K~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[34m\e[K                                                amet, consectetur,
\e[m\e[K                                                \e[34m\e[K~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[34m\e[K                                                adipiscing, elit, sed,
\e[m\e[K                                                \e[34m\e[K~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[34m\e[K                                                eiusmod, tempor, incididunt,
\e[m\e[K                                                \e[34m\e[K~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[34m\e[K                                                ut, labore, et, dolore,
\e[m\e[K                                                \e[34m\e[K~~~~~~~~~~~~~~~~~~~~~~~
+\e[m\e[K \e[34m\e[K                                                magna, aliqua)\e[m\e[K);
+                                                 \e[34m\e[K~~~~~~~~~~~~~~
+\e[m\e[K
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_richloc_from_proper_range (void)
+{
+#if 0
+  float f = 98.6f; /* { dg-warning "test" } */
+/* { dg-begin-multiline-output "" }
+   float f = \e[01;35m\e[K98.6f\e[m\e[K;
+             \e[01;35m\e[K^~~~~
+\e[m\e[K
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_caret_within_proper_range (void)
+{
+#if 0
+  float f = foo * bar; /* { dg-warning "17: test" } */
+/* { dg-begin-multiline-output "" }
+   float f = \e[01;35m\e[Kfoo * bar\e[m\e[K;
+             \e[01;35m\e[K~~~~^~~~~
+\e[m\e[K
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_very_wide_line (void)
+{
+#if 0
+                                                                                float f = foo * bar; /* { dg-warning "95: test" } */
+/* { dg-begin-multiline-output "" }
+                                              float f = \e[01;35m\e[Kfoo * bar\e[m\e[K;
+                                                        \e[01;35m\e[K~~~~^~~~~
+\e[m\e[K
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_multiple_carets (void)
+{
+#if 0
+   x = x + y /* { dg-warning "8: test" } */
+/* { dg-begin-multiline-output "" }
+    x = \e[01;35m\e[Kx\e[m\e[K + \e[32m\e[Ky\e[m\e[K
+        \e[01;35m\e[KA\e[m\e[K   \e[32m\e[KB
+\e[m\e[K
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test_caret_on_leading_whitespace (void)
+{
+#if 0
+    ASSOCIATE (y => x)
+      y = 5 /* { dg-warning "6: test" } */
+/* { dg-begin-multiline-output "" }
+     ASSOCIATE (y =>\e[32m\e[K \e[m\e[Kx)
+                    \e[32m\e[K2
+\e[m\e[K      \e[01;35m\e[K \e[m\e[Ky = 5
+      \e[01;35m\e[K1
+\e[m\e[K
+   { dg-end-multiline-output "" } */
+#endif
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
new file mode 100644 (file)
index 0000000..8f5724e
--- /dev/null
@@ -0,0 +1,326 @@
+/* { dg-options "-O" } */
+
+/* This plugin exercises the diagnostics-printing code.
+
+   The goal is to unit-test the range-printing code without needing any
+   correct range data within the compiler's IR.  We can't use any real
+   diagnostics for this, so we have to fake it, hence this plugin.
+
+   There are two test files used with this code:
+
+     diagnostic-test-show-locus-ascii-bw.c
+     ..........................-ascii-color.c
+
+   to exercise uncolored vs colored output by supplying plugin arguments
+   to hack in the desired behavior:
+
+     -fplugin-arg-diagnostic_plugin_test_show_locus-color
+
+   The test files contain functions, but the body of each
+   function is disabled using the preprocessor.  The plugin detects
+   the functions by name, and inject diagnostics within them, using
+   hard-coded locations relative to the top of each function.
+
+   The plugin uses a function "get_loc" below to map from line/column
+   numbers to source_location, and this relies on input_location being in
+   the same ordinary line_map as the locations in question.  The plugin
+   runs after parsing, so input_location will be at the end of the file.
+
+   This need for all of the test code to be in a single ordinary line map
+   means that each test file needs to have a very long line near the top
+   (potentially to cover the extra byte-count of colorized data),
+   to ensure that further very long lines don't start a new linemap.
+   This also means that we can't use macros in the test files.  */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "toplev.h"
+#include "basic-block.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "context.h"
+#include "print-tree.h"
+
+int plugin_is_GPL_compatible;
+
+const pass_data pass_data_test_show_locus =
+{
+  GIMPLE_PASS, /* type */
+  "test_show_locus", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_test_show_locus : public gimple_opt_pass
+{
+public:
+  pass_test_show_locus(gcc::context *ctxt)
+    : gimple_opt_pass(pass_data_test_show_locus, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate (function *) { return true; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_test_show_locus
+
+/* Given LINE_NUM and COL_NUM, generate a source_location in the
+   current file, relative to input_location.  This relies on the
+   location being expressible in the same ordinary line_map as
+   input_location (which is typically at the end of the source file
+   when this is called).  Hence the test files we compile with this
+   plugin must have an initial very long line (to avoid long lines
+   starting a new line map), and must not use macros.
+
+   COL_NUM uses the Emacs convention of 0-based column numbers.  */
+
+static source_location
+get_loc (unsigned int line_num, unsigned int col_num)
+{
+  /* Use input_location to get the relevant line_map */
+  const struct line_map_ordinary *line_map
+    = (const line_map_ordinary *)(linemap_lookup (line_table,
+                                                 input_location));
+
+  /* Convert from 0-based column numbers to 1-based column numbers.  */
+  source_location loc
+    = linemap_position_for_line_and_column (line_map,
+                                           line_num, col_num + 1);
+
+  return loc;
+}
+
+/* Was "color" passed in as a plugin argument?  */
+static bool force_show_locus_color = false;
+
+/* We want to verify the colorized output of diagnostic_show_locus,
+   but turning on colorization for everything confuses "dg-warning" etc.
+   Hence we special-case it within this plugin by using this modified
+   version of default_diagnostic_finalizer, which, if "color" is
+   passed in as a plugin argument turns on colorization, but just
+   for diagnostic_show_locus.  */
+
+static void
+custom_diagnostic_finalizer (diagnostic_context *context,
+                            diagnostic_info *diagnostic)
+{
+  bool old_show_color = pp_show_color (context->printer);
+  if (force_show_locus_color)
+    pp_show_color (context->printer) = true;
+  diagnostic_show_locus (context, diagnostic);
+  pp_show_color (context->printer) = old_show_color;
+
+  pp_destroy_prefix (context->printer);
+  pp_newline_and_flush (context->printer);
+}
+
+/* Exercise the diagnostic machinery to emit various warnings,
+   for use by diagnostic-test-show-locus-*.c.
+
+   We inject each warning relative to the start of a function,
+   which avoids lots of hardcoded absolute locations.  */
+
+static void
+test_show_locus (function *fun)
+{
+  tree fndecl = fun->decl;
+  tree identifier = DECL_NAME (fndecl);
+  const char *fnname = IDENTIFIER_POINTER (identifier);
+  location_t fnstart = fun->function_start_locus;
+  int fnstart_line = LOCATION_LINE (fnstart);
+
+  diagnostic_finalizer (global_dc) = custom_diagnostic_finalizer;
+
+  /* Hardcode the "terminal width", to verify the behavior of
+     very wide lines.  */
+  global_dc->caret_max_width = 70;
+
+  if (0 == strcmp (fnname, "test_simple"))
+    {
+      const int line = fnstart_line + 2;
+      rich_location richloc (get_loc (line, 15));
+      richloc.add_range (get_loc (line, 10), get_loc (line, 14), false);
+      richloc.add_range (get_loc (line, 16), get_loc (line, 16), false);
+      warning_at_rich_loc (&richloc, 0, "test");
+    }
+
+  if (0 == strcmp (fnname, "test_simple_2"))
+    {
+      const int line = fnstart_line + 2;
+      rich_location richloc (get_loc (line, 24));
+      richloc.add_range (get_loc (line, 6),
+                        get_loc (line, 22), false);
+      richloc.add_range (get_loc (line, 26),
+                        get_loc (line, 43), false);
+      warning_at_rich_loc (&richloc, 0, "test");
+    }
+
+  if (0 == strcmp (fnname, "test_multiline"))
+    {
+      const int line = fnstart_line + 2;
+      rich_location richloc (get_loc (line + 1, 7));
+      richloc.add_range (get_loc (line, 7),
+                        get_loc (line, 23), false);
+      richloc.add_range (get_loc (line + 1, 9),
+                        get_loc (line + 1, 26), false);
+      warning_at_rich_loc (&richloc, 0, "test");
+    }
+
+  if (0 == strcmp (fnname, "test_many_lines"))
+    {
+      const int line = fnstart_line + 2;
+      rich_location richloc (get_loc (line + 5, 7));
+      richloc.add_range (get_loc (line, 7),
+                        get_loc (line + 4, 65), false);
+      richloc.add_range (get_loc (line + 5, 9),
+                        get_loc (line + 10, 61), false);
+      warning_at_rich_loc (&richloc, 0, "test");
+    }
+
+  /* Example of a rich_location constructed directly from a
+     source_range where the range is larger than one character.  */
+  if (0 == strcmp (fnname, "test_richloc_from_proper_range"))
+    {
+      const int line = fnstart_line + 2;
+      source_range src_range;
+      src_range.m_start = get_loc (line, 12);
+      src_range.m_finish = get_loc (line, 16);
+      rich_location richloc (src_range);
+      warning_at_rich_loc (&richloc, 0, "test");
+    }
+
+  /* Example of a single-range location where the range starts
+     before the caret.  */
+  if (0 == strcmp (fnname, "test_caret_within_proper_range"))
+    {
+      const int line = fnstart_line + 2;
+      location_t caret = get_loc (line, 16);
+      source_range src_range;
+      src_range.m_start = get_loc (line, 12);
+      src_range.m_finish = get_loc (line, 20);
+      rich_location richloc (caret);
+      richloc.set_range (0, src_range, true, false);
+      warning_at_rich_loc (&richloc, 0, "test");
+    }
+
+  /* Example of a very wide line, where the information of interest
+     is beyond the width of the terminal (hardcoded above).  */
+  if (0 == strcmp (fnname, "test_very_wide_line"))
+    {
+      const int line = fnstart_line + 2;
+      location_t caret = get_loc (line, 94);
+      source_range src_range;
+      src_range.m_start = get_loc (line, 90);
+      src_range.m_finish = get_loc (line, 98);
+      rich_location richloc (caret);
+      richloc.set_range (0, src_range, true, false);
+      warning_at_rich_loc (&richloc, 0, "test");
+    }
+
+  /* Example of multiple carets.  */
+  if (0 == strcmp (fnname, "test_multiple_carets"))
+    {
+      const int line = fnstart_line + 2;
+      location_t caret_a = get_loc (line, 7);
+      location_t caret_b = get_loc (line, 11);
+      rich_location richloc (caret_a);
+      richloc.add_range (caret_b, caret_b, true);
+      global_dc->caret_chars[0] = 'A';
+      global_dc->caret_chars[1] = 'B';
+      warning_at_rich_loc (&richloc, 0, "test");
+      global_dc->caret_chars[0] = '^';
+      global_dc->caret_chars[1] = '^';
+    }
+
+  /* Example of two carets where both carets appear to have an off-by-one
+     error appearing one column early.
+     Seen with gfortran.dg/associate_5.f03.
+     In an earlier version of the printer, the printing of caret 0 aka
+     "1" was suppressed due to it appearing within the leading whitespace
+     before the text in its line.  Ensure that we at least faithfully
+     print both carets, at the given (erroneous) locations.  */
+  if (0 == strcmp (fnname, "test_caret_on_leading_whitespace"))
+    {
+      const int line = fnstart_line + 3;
+      location_t caret_a = get_loc (line, 5);
+      location_t caret_b = get_loc (line - 1, 19);
+      rich_location richloc (caret_a);
+      richloc.add_range (caret_b, caret_b, true);
+      global_dc->caret_chars[0] = '1';
+      global_dc->caret_chars[1] = '2';
+      warning_at_rich_loc (&richloc, 0, "test");
+      global_dc->caret_chars[0] = '^';
+      global_dc->caret_chars[1] = '^';
+    }
+}
+
+unsigned int
+pass_test_show_locus::execute (function *fun)
+{
+  test_show_locus (fun);
+  return 0;
+}
+
+static gimple_opt_pass *
+make_pass_test_show_locus (gcc::context *ctxt)
+{
+  return new pass_test_show_locus (ctxt);
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+            struct plugin_gcc_version *version)
+{
+  struct register_pass_info pass_info;
+  const char *plugin_name = plugin_info->base_name;
+  int argc = plugin_info->argc;
+  struct plugin_argument *argv = plugin_info->argv;
+
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  /* For now, tell the dc to expect ranges and thus to colorize the source
+     lines, not just the carets/underlines.  This will be redundant
+     once the C frontend generates ranges.  */
+  global_dc->colorize_source_p = true;
+
+  for (int i = 0; i < argc; i++)
+    {
+      if (0 == strcmp (argv[i].key, "color"))
+       force_show_locus_color = true;
+    }
+
+  pass_info.pass = make_pass_test_show_locus (g);
+  pass_info.reference_pass_name = "ssa";
+  pass_info.ref_pass_instance_number = 1;
+  pass_info.pos_op = PASS_POS_INSERT_AFTER;
+  register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+                    &pass_info);
+
+  return 0;
+}
index 39fab6ef019a23a4bbfcf6bbbc517cdcb8ae5c4d..941bccc438718daf625e85254ae88c963e623a83 100644 (file)
@@ -63,6 +63,9 @@ set plugin_test_list [list \
     { start_unit_plugin.c start_unit-test-1.c } \
     { finish_unit_plugin.c finish_unit-test-1.c } \
     { wide-int_plugin.c wide-int-test-1.c } \
+    { diagnostic_plugin_test_show_locus.c \
+         diagnostic-test-show-locus-bw.c \
+         diagnostic-test-show-locus-color.c } \
 ]
 
 foreach plugin_test $plugin_test_list {
index 7c1ab85f32e1a26256a43a798323f06cef368da5..8cc1d87d6495a35162dbce5e7e9e7a26f03e5c4a 100644 (file)
@@ -29,6 +29,7 @@ load_lib libgloss.exp
 load_lib target-libpath.exp
 load_lib torture-options.exp
 load_lib fortran-modules.exp
+load_lib multiline.exp
 
 # We set LC_ALL and LANG to C so that we get the same error messages as expected.
 setenv LC_ALL C
index 4ee21b9b277ca0c2df4037ee7b13ee0e9f529f74..bbb830b3e06c4c7546807c8981990b2d22bd7447 100644 (file)
@@ -286,7 +286,7 @@ default_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
     }
 
   if (set_locus)
-    text->set_location (0, DECL_SOURCE_LOCATION (t));
+    text->set_location (0, DECL_SOURCE_LOCATION (t), true);
 
   if (DECL_P (t))
     {
index b1685fd10182f032ac815a005e9c72629b4a9929..3f0a4e6cfe08de2c5fd131315c39de9f337c4e97 100644 (file)
@@ -3770,7 +3770,7 @@ void
 percent_K_format (text_info *text)
 {
   tree t = va_arg (*text->args_ptr, tree), block;
-  text->set_location (0, EXPR_LOCATION (t));
+  text->set_location (0, EXPR_LOCATION (t), true);
   gcc_assert (pp_ti_abstract_origin (text) != NULL);
   block = TREE_BLOCK (t);
   *pp_ti_abstract_origin (text) = NULL;
index 8abf6ddfaa4f6675d0ac5c9f3f2c8252284a5598..57ac5bfbde1f1ffeb0090a710b5b84b76ef8252c 100644 (file)
@@ -1,3 +1,22 @@
+2015-11-06  David Malcolm  <dmalcolm@redhat.com>
+
+       * 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  <dmalcolm@redhat.com>
 
        * include/line-map.h (struct linemap_stats): Add fields
index a33196e91578628493a6163d3f60d4cb5b1d1166..c351c112ad135f4496f4adca8a4bb483f4313004 100644 (file)
@@ -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;
 }
index 5eaea6b60d79c6d6dcbee76d63b00e916cfdf21f..a2bdfa0c808df62b9dfc756243aa398beb6f1f15 100644 (file)
@@ -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".  */
index c8618a970e1e5f507f39a9f88da073b897c5196c..c9340a6eaefb062f3bcd843b37933aa9a3bfa4bc 100644 (file)
@@ -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  */
index 84403de9c77035e95c463b340bea687a4c4f8c05..3c19f93b7a5a671781775cd4493ae687361877d9 100644 (file)
@@ -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;
+    }
+}