In some situations, we would like to point to a location which was not
encoded when tokenizing. This happens, for example, in two prominent
cases:

1) To get precise locations within strings
(https://gcc.gnu.org/PR52952) for example, for Wformat warnings.

2) In the Fortran FE, which gives quite precise location information
by tracking the characters that it wants to warn about instead of
relying on the line-map machinery.

The most straightforward way to implement this is by adding variants
of diagnostic functions that take an explicit "offset" argument and
pass this offset through the whole diagnostics machinery. This is what
I implemented in the patch format_offset.diff attached. The downside
is that we would need to add even more variants (with/without offset)
of various diagnostic functions and track the offset/no-offset cases
explicitly.

The nicer/cleaner alternative is to somehow (re)compute a single
location value from a given location plus the new offset. This is what
I implemented in patch fortran-diagnostics-part3.diff in
linemap_redo_position_for_column(). As far as I understand, this
method only works reliably if the location+offset does not jump to a
different line map, that is, if to_column < (1u <<
map->d.ordinary.column_bits). Otherwise, we may need to recompute all
successive line-maps to accommodate the new location. The best way to
do the latter (or to work-around that issue) is not clear to me at the
moment.

Thus, I am putting forward these two alternative implementations and
seeking comments/advice/help in deciding what would be the best way to
fix this key missing piece of GCC diagnostics.

Related to this, perhaps I should make a more general call for help.
Despite the heroic, constant torrent of diagnostic fixes by Paolo,
Marek and others, I have not seen much progress on the key
infrastructure issues in the roadmap
(https://gcc.gnu.org/wiki/Better_Diagnostics). We have had at least
one major item per release since GCC 4.5, but I don't see any
particular item being tackled for GCC 5.0. Are you planning to tackle
any of them?

I have a simple patch to implement Fix-it hints but it needs more
work. Unfortunately, I have very little free time to dedicate to GCC
nowadays, so I'm afraid I might not even be able to finish this in
time. Any item in that list would be a nice major feature for GCC 5.0.
Perhaps we need to ask for help in gcc/gcc-help or some other forum.

Cheers,

Manuel.
Index: gcc/c-family/c-format.c
===================================================================
--- gcc/c-family/c-format.c     (revision 197155)
+++ gcc/c-family/c-format.c     (working copy)
@@ -377,10 +377,11 @@ typedef struct format_wanted_type
   int format_length;
   /* The actual parameter to check against the wanted type.  */
   tree param;
   /* The argument number of that parameter.  */
   int arg_num;
+  int offset_loc;
   /* The next type to check for this format conversion, or NULL if none.  */
   struct format_wanted_type *next;
 } format_wanted_type;
 
 /* Convenience macro for format_length_info meaning unused.  */
@@ -903,10 +904,11 @@ typedef struct
   /* Number of leaves of the format argument that were unterminated
      strings.  */
   int number_unterminated;
   /* Number of leaves of the format argument that were not counted above.  */
   int number_other;
+  location_t loc;
 } format_check_results;
 
 typedef struct
 {
   format_check_results *res;
@@ -996,11 +998,11 @@ check_function_format (tree attrs, int n
     {
       if (is_attribute_p ("format", TREE_PURPOSE (a)))
        {
          /* Yup; check it.  */
          function_format_info info;
-         decode_format_attr (TREE_VALUE (a), &info, 1);
+         decode_format_attr (TREE_VALUE (a), &info, /*validated=*/true);
          if (warn_format)
            {
              /* FIXME: Rewrite all the internal functions in this file
                 to use the ARGARRAY directly instead of constructing this
                 temporary list.  */
@@ -1465,10 +1467,11 @@ check_format_arg (void *ctx, tree format
   if (TREE_CODE (format_tree) != ADDR_EXPR)
     {
       res->number_non_literal++;
       return;
     }
+  res->loc = EXPR_LOCATION(format_tree);
   format_tree = TREE_OPERAND (format_tree, 0);
   if (format_types[info->format_type].flags 
       & (int) FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL)
     {
       bool objc_str = (info->format_type == gcc_objc_string_format_type);
@@ -1637,11 +1640,13 @@ check_format_info_main (format_check_res
 
       if (*format_chars++ != '%')
        continue;
       if (*format_chars == 0)
        {
-         warning (OPT_Wformat_, "spurious trailing %<%%%> in format");
+         warning_at (res->loc ? res->loc : input_location, 
+                      format_chars - orig_format_chars,
+                      OPT_Wformat_, "spurious trailing %<%%%> in format");
          continue;
        }
       if (*format_chars == '%')
        {
          ++format_chars;
@@ -2449,10 +2454,11 @@ format_type_warning (format_wanted_type
   const char *wanted_type_name = type->wanted_type_name;
   const char *format_start = type->format_start;
   int format_length = type->format_length;
   int pointer_count = type->pointer_count;
   int arg_num = type->arg_num;
+  int offset_loc = type->offset_loc;
 
   char *p;
   /* If ARG_TYPE is a typedef with a misleading name (for example,
      size_t but not the standard size_t expected by printf %zu), avoid
      printing the typedef name.  */
Index: gcc/diagnostic.c
===================================================================
--- gcc/diagnostic.c    (revision 197155)
+++ gcc/diagnostic.c    (working copy)
@@ -189,10 +189,11 @@ diagnostic_set_info_translated (diagnost
   diagnostic->message.format_spec = msg;
   diagnostic->location = location;
   diagnostic->override_column = 0;
   diagnostic->kind = kind;
   diagnostic->option_index = 0;
+  diagnostic->offset = 0;
 }
 
 /* Initialize DIAGNOSTIC, where the message GMSGID has not yet been
    translated.  */
 void
@@ -215,10 +216,11 @@ diagnostic_build_prefix (diagnostic_cont
 #undef DEFINE_DIAGNOSTIC_KIND
     "must-not-happen"
   };
   const char *text = _(diagnostic_kind_text[diagnostic->kind]);
   expanded_location s = expand_location_to_spelling_point 
(diagnostic->location);
+  s.column += diagnostic->offset;
   if (diagnostic->override_column)
     s.column = diagnostic->override_column;
   gcc_assert (diagnostic->kind < DK_LAST_DIAGNOSTIC_KIND);
 
   return
@@ -269,10 +271,11 @@ diagnostic_show_locus (diagnostic_contex
       || diagnostic->location == context->last_location)
     return;
 
   context->last_location = diagnostic->location;
   s = expand_location_to_spelling_point (diagnostic->location);
+  s.column += diagnostic->offset;
   line = location_get_source_line (s);
   if (line == NULL)
     return;
 
   max_width = context->caret_max_width;
@@ -945,10 +948,27 @@ warning_at (location_t location, int opt
   ret = report_diagnostic (&diagnostic);
   va_end (ap);
   return ret;
 }
 
+
+bool
+warning_at (location_t location, unsigned int offset, 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.option_index = opt;
+  diagnostic.offset = offset;
+  ret = report_diagnostic (&diagnostic);
+  va_end (ap);
+  return ret;
+}
+
 /* A "pedantic" warning at LOCATION: issues a warning unless
    -pedantic-errors was given on the command line, in which case it
    issues an error.  Use this for diagnostics required by the relevant
    language standard, if you have chosen not to make them errors.
 
Index: gcc/diagnostic.h
===================================================================
--- gcc/diagnostic.h    (revision 197155)
+++ gcc/diagnostic.h    (working copy)
@@ -36,10 +36,11 @@ typedef struct diagnostic_info
   void *x_data;
   /* The kind of diagnostic it is about.  */
   diagnostic_t kind;
   /* Which OPT_* directly controls this diagnostic.  */
   int option_index;
+  int offset;
 } diagnostic_info;
 
 /* Each time a diagnostic's classification is changed with a pragma,
    we record the change and the location of the change in an array of
    these structs.  */
Index: gcc/diagnostic-core.h
===================================================================
--- gcc/diagnostic-core.h       (revision 197155)
+++ gcc/diagnostic-core.h       (working copy)
@@ -58,10 +58,13 @@ extern void internal_error (const char *
      ATTRIBUTE_NORETURN;
 /* Pass one of the OPT_W* from options.h as the first parameter.  */
 extern bool warning (int, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
 extern bool warning_at (location_t, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
+extern bool warning_at (location_t, unsigned int, int, const char *, ...)
+    ATTRIBUTE_GCC_DIAG(4,5);
+
 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 fatal_error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2)
Index: gcc/tree-diagnostic.c
===================================================================
--- gcc/tree-diagnostic.c       (revision 214251)
+++ gcc/tree-diagnostic.c       (working copy)
@@ -244,11 +244,11 @@ virt_loc_aware_diagnostic_finalizer (dia
   maybe_unwind_expanded_macro_loc (context, diagnostic,
                                   diagnostic->location);
 }
 
 /* Default tree printer.   Handles declarations only.  */
-static bool
+bool
 default_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
                      int precision, bool wide, bool set_locus, bool hash)
 {
   tree t;
 
Index: gcc/fortran/gfortran.h
===================================================================
--- gcc/fortran/gfortran.h      (revision 214251)
+++ gcc/fortran/gfortran.h      (working copy)
@@ -2696,11 +2696,13 @@ void gfc_warning_cmdline (const char *gm
 
 void gfc_clear_warning (void);
 void gfc_warning_check (void);
 
 void gfc_error (const char *, ...) ATTRIBUTE_GCC_GFC(1,2);
+void gfc_error_cmdline (const char *gmsgid, ...) ATTRIBUTE_GCC_GFC(1,2);
 void gfc_error_now (const char *, ...) ATTRIBUTE_GCC_GFC(1,2);
+void gfc_error_now_2 (const char *, ...) ATTRIBUTE_GCC_GFC(1,2);
 void gfc_fatal_error (const char *, ...) ATTRIBUTE_NORETURN 
ATTRIBUTE_GCC_GFC(1,2);
 void gfc_internal_error (const char *, ...) ATTRIBUTE_NORETURN 
ATTRIBUTE_GCC_GFC(1,2);
 void gfc_clear_error (void);
 int gfc_error_check (void);
 int gfc_error_flag_test (void);
Index: gcc/fortran/error.c
===================================================================
--- gcc/fortran/error.c (revision 214251)
+++ gcc/fortran/error.c (working copy)
@@ -956,10 +956,67 @@ gfc_warning_now (const char *gmsgid, ...
     gfc_increment_error_count();
 
   buffer_flag = i;
 }
 
+/* Encode and return a source_location from a column number. The
+   source line considered is the last source line used to call
+   linemap_line_start, i.e, the last source line which a location was
+   encoded from.  */
+
+source_location
+linemap_redo_position_for_column (struct line_maps *set, source_location loc,
+                                  unsigned int to_column)
+{
+  const struct line_map * map = linemap_lookup (set, loc);
+  gcc_assert(to_column < (1u << map->d.ordinary.column_bits));
+  source_location r = loc + to_column;
+  gcc_assert (map == linemap_lookup (set, r));
+  return r;
+}
+
+bool
+default_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
+                     int precision, bool wide, bool set_locus, bool hash);
+
+/* Called from output_format -- during diagnostic message processing --
+   to handle C++ specific format specifier with the following meanings:
+
+   %C  Current locus (no argument) 
+*/
+static bool
+gfc_format_decoder (pretty_printer *pp,
+                   text_info *text, const char *spec,
+                   int precision, bool wide, bool set_locus, bool verbose)
+{
+  const char *result;
+  switch (*spec)
+    {
+    case 'C':
+      {
+       result = "(1)"; 
+        gcc_assert (text->locus);
+        *text->locus = gfc_current_locus.lb->location;
+        gcc_assert (gfc_current_locus.nextc - gfc_current_locus.lb->line);
+        unsigned int c1 = gfc_current_locus.nextc - gfc_current_locus.lb->line;
+        *text->locus 
+         = linemap_redo_position_for_column (line_table, *text->locus,
+                                             c1 + 1);
+        global_dc->caret_char = '1';
+       break;
+      }
+    default:
+      /* We can get called from the gimplifier.  */
+      /* ??? Is there a way to set the formart_decoder back to this default? */
+      return default_tree_printer (pp, text, spec, precision, wide, set_locus,
+                                  verbose);
+    }
+
+  pp_string (pp, result);
+  return true;
+}
+
 /* Return a malloc'd string describing a location.  The caller is
    responsible for freeing the memory.  */
 static char *
 gfc_diagnostic_build_prefix (diagnostic_context *context,
                             const diagnostic_info *diagnostic)
@@ -1065,10 +1122,24 @@ gfc_warning_cmdline (const char *gmsgid,
                       DK_WARNING);
   report_diagnostic (&diagnostic);
   va_end (argp);
 }
 
+/* Give an error about the command-line.  */
+
+void
+gfc_error_cmdline (const char *gmsgid, ...)
+{
+  va_list argp;
+  diagnostic_info diagnostic;
+
+  va_start (argp, gmsgid);
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ERROR);
+  report_diagnostic (&diagnostic);
+  va_end (argp);
+}
+
 /* Clear the warning flag.  */
 
 void
 gfc_clear_warning (void)
 {
@@ -1145,10 +1216,29 @@ warning:
 
 
 /* Immediate error.  */
 
 void
+gfc_error_now_2 (const char *gmsgid, ...)
+{
+  va_list argp;
+  diagnostic_info diagnostic;
+
+  va_start (argp, gmsgid);
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ERROR);
+  report_diagnostic (&diagnostic);
+  va_end (argp);
+
+  gfc_increment_error_count();
+
+  if (flag_fatal_errors)
+    exit (FATAL_EXIT_CODE);
+}
+
+/* Immediate error.  */
+
+void
 gfc_error_now (const char *gmsgid, ...)
 {
   va_list argp;
   int i;
 
@@ -1319,7 +1409,8 @@ gfc_errors_to_warnings (int f)
 void
 gfc_diagnostics_init (void)
 {
   diagnostic_starter (global_dc) = gfc_diagnostic_starter;
   diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer;
+  diagnostic_format_decoder (global_dc) = gfc_format_decoder;
   global_dc->caret_char = '^';
 }
Index: gcc/fortran/parse.c
===================================================================
--- gcc/fortran/parse.c (revision 214251)
+++ gcc/fortran/parse.c (working copy)
@@ -844,11 +844,11 @@ next_free (void)
          do
            c = gfc_next_ascii_char ();
          while (ISDIGIT(c));
 
          if (!gfc_is_whitespace (c))
-           gfc_error_now ("Non-numeric character in statement label at %C");
+           gfc_error_now_2 ("Non-numeric character in statement label at %C");
 
          return ST_NONE;
        }
       else
        {

Reply via email to