Hello-

The attached patch is complete including docs, but I tagged as RFC
because I am not sure if anyone will like it, or if the general reaction may
be closer to recoiling in horror :). Would appreciate your thoughts,
please...

Currently, if a UTF-8 locale is detected, GCC changes the quote characters
it outputs in diagnostics to Unicode directional quotes. I feel like this is
a nice touch, so I was wondering whether GCC shouldn't do more along these
lines. This patch adds support for using Unicode line drawing characters and
similar things when outputting diagnostics. There is a new option
-fdiagnostics-unicode-drawing=[auto|never|always] to control it, which
defaults to auto. "auto" will enable the feature under the same
circumstances that Unicode quotes get output, namely when the locale is
determined by gcc_init_libintl() to support UTF-8. (The new option does not
affect Unicode quote characters, which currently are not configurable and
are determined solely by the locale.)

The elements implemented are:

    * Vertical lines, e.g. those indicating labels and those separating the
      source lines from the line numbers, are changed to line drawing
      characters.

    * The diagnostic paths output by the static analyzer make use of line
      drawing characters to output smooth corners etc.

    * The squiggly underline ~~~~~ used to highlight source locations is
      changed to a double underline ═════. The main reason for this is that
      it enables a seamless "tee" character to connect the underline to a
      label line if one exists.

    * Carets (^) are changed to a slightly different character (∧). I think
      the new one is a little nicer looking, although probably not worth the
      trouble on its own. I wanted to implement the support in this patch
      beause carets are harder to change than the rest of the elements
      (front ends have an interface to override them, which currently
      Fortran makes use of), so I thought it worthwhile to get this logic in
      place, so that it can easily be changed to a more superior character
      in the future if one comes up. It would also be easy enough to leave
      the Unicode support in place for carets, but keep the default set to
      the plain one for now.

As an example, this diagnostic from gcc.dg/format/diagnostic-ranges.c:

diagnostic-ranges.c:196:28: warning: field width specifier ‘*’ expects argument 
of type ‘int’, but argument 3 has type ‘long int’ [-Wformat=]
  196 |   __builtin_sprintf (d, " %*ld ", foo + bar, foo);
      |                           ~^~~    ~~~~~~~~~
      |                            |          |
      |                            int        long int

would become instead:

diagnostic-ranges.c:196:28: warning: field width specifier ‘*’ expects argument 
of type ‘int’, but argument 3 has type ‘long int’ [-Wformat=]
  196 │   __builtin_sprintf (d, " %*ld ", foo + bar, foo);
      │                           ═∧══    ════╤════
      │                            │          │
      │                            int        long int

Hopefully you are viewing this in a terminal that displays it properly :), in
which case, hopefully you may find it to be an improvement?

Here is a more involved example from the analyzer:

setjmp-5.c: In function ‘outer’:
setjmp-5.c:21:3: warning: ‘longjmp’ called after enclosing function of ‘setjmp’ 
has returned [-Wanalyzer-stale-setjmp-buffer]
   21 |   longjmp (env, 42); /* { dg-warning "'longjmp' called after enclosing 
function of 'setjmp' has returned" } */
      |   ^~~~~~~~~~~~~~~~~
  ‘outer’: events 1-2
    |
    |   15 | void outer (void)
    |      |      ^~~~~
    |      |      |
    |      |      (1) entry to ‘outer’
    |......
    |   19 |   inner ();
    |      |   ~~~~~~~~
    |      |   |
    |      |   (2) calling ‘inner’ from ‘outer’
    |
    +--> ‘inner’: event 3
           |
           |   10 | static void inner (void)
           |      |             ^~~~~
           |      |             |
           |      |             (3) entry to ‘inner’
           |
         ‘inner’: event 4
           |
           |   12 |   SETJMP (env);
           |      |   ^~~~~~
           |      |   |
           |      |   (4) ‘setjmp’ called here
           |
    <------+
    |
  ‘outer’: events 5-6
    |
    |   19 |   inner ();
    |      |   ^~~~~~~~
    |      |   |
    |      |   (5) returning to ‘outer’ from ‘inner’
    |   20 |
    |   21 |   longjmp (env, 42); /* { dg-warning "'longjmp' called after 
enclosing function of 'setjmp' has returned" } */
    |      |   ~~~~~~~~~~~~~~~~~
    |      |   |
    |      |   (6) here
    |

would become instead:

setjmp-5.c: In function ‘outer’:
setjmp-5.c:21:3: warning: ‘longjmp’ called after enclosing function of ‘setjmp’ 
has returned [-Wanalyzer-stale-setjmp-buffer]
   21 │   longjmp (env, 42); /* { dg-warning "'longjmp' called after enclosing 
function of 'setjmp' has returned" } */
      │   ∧════════════════
  ‘outer’: events 1-2
    │
    │   15 │ void outer (void)
    │      │      ∧════
    │      │      │
    │      │      (1) entry to ‘outer’
    │......
    │   19 │   inner ();
    │      │   ╤═══════
    │      │   │
    │      │   (2) calling ‘inner’ from ‘outer’
    │
    └──> ‘inner’: event 3
           │
           │   10 │ static void inner (void)
           │      │             ∧════
           │      │             │
           │      │             (3) entry to ‘inner’
           │
         ‘inner’: event 4
           │
           │   12 │   SETJMP (env);
           │      │   ∧═════
           │      │   │
           │      │   (4) ‘setjmp’ called here
           │
    ┌<─────┘
    │
  ‘outer’: events 5-6
    │
    │   19 │   inner ();
    │      │   ∧═══════
    │      │   │
    │      │   (5) returning to ‘outer’ from ‘inner’
    │   20 │
    │   21 │   longjmp (env, 42); /* { dg-warning "'longjmp' called after 
enclosing function of 'setjmp' has returned" } */
    │      │   ╤════════════════
    │      │   │
    │      │   (6) here
    │


Although probably premature, bootstrap and regtest were done on x86-64
linux, all tests the same before/after and new tests passing:
FAIL 96 96
PASS 479090 479239
UNSUPPORTED 11946 11946
UNTESTED 194 194
XFAIL 1839 1839
XPASS 36 36

I tried to set this up as a general framework, at least, it is easy in one
place to change the characters that are used for various contexts, so that
if people like the general idea, but not some of the specifics, the patch is
easily modified for that now or in the future. Thanks for any feedback!

-Lewis
From: Lewis Hyatt <lhy...@gmail.com>
Date: Thu, 23 Jul 2020 10:11:04 -0400
Subject: [PATCH] diagnostics: Add support for Unicode drawing characters

Adds the new option -fdiagnostics-unicode-drawing, on by default if a
UTF-8 local is detected, which modifies diagnostics to use extended Unicode
characters, such as line-drawing characters.

gcc/ChangeLog:

        * common.opt: Add new option -fdiagnostics-unicode-drawing.
        * diagnostic-show-locus.c (struct point_state): Add new member.
        (layout::print_source_line): Support Unicode drawing feature.
        (layout::start_annotation_line): Likewise.
        (layout::print_annotation_line): Likewise.
        (layout::print_any_labels): Likewise.
        (layout::print_trailing_fixits): Likewise.
        (layout::get_state_at_point): Likewise.
        (test_one_liner_multiple_carets_and_ranges): Likewise.
        (test_one_liner_multiple_carets_and_ranges_utf8): Likewise.
        * diagnostic.c (diagnostic_drawing_init): New function.
        (diagnostic_initialize): Call the new function.
        * diagnostic.h (struct diagnostic_context): Support Unicode
        drawing feature.
        (enum diagnostics_drawing_rule): New enum for the new option.
        (diagnostic_drawing_init): Declare.
        * doc/invoke.texi: Document the new option.
        * opts.c (common_handle_option): Call diagnostic_drawing_init() to
        support the new option.
        * selftest-diagnostic.c
        (test_diagnostic_context::test_diagnostic_context): Disable
        Unicode drawing in selftests that use test_diagnostic_context.
        * tree-diagnostic-path.cc (path_summary::print): Support Unicode
        drawing feature.

gcc/fortran/ChangeLog:

        * error.c (gfc_diagnostics_init): Adapt custom carets, as they
        need to be strings rather than chars now.
        (gfc_diagnostics_finish): Likewise.

libstdc++-v3/ChangeLog:

        * testsuite/lib/libstdc++.exp: Disable Unicode drawing by default
        in the testsuite.

gcc/testsuite/ChangeLog:

        * gcc.dg/plugin/diagnostic_plugin_test_show_locus.c: Adapt custom
        carets, as they need to be strings rather than chars now.
        * lib/c-compat.exp: Handle removing Unicode drawing option from
        compilers that do not support it.
        * lib/prune.exp: Disable Unicode drawing by default
        in the testsuite.
        * gcc.dg/analyzer/setjmp-5-utf8.c: New test.
        * gcc.dg/format/diagnostic-ranges-utf8.c: New test.

diff --git a/gcc/common.opt b/gcc/common.opt
index a3893a4725e..1839e3471a4 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1386,6 +1386,26 @@ ftabstop=
 Common Joined RejectNegative UInteger
 -ftabstop=<number>      Distance between tab stops for column reporting.
 
+fdiagnostics-unicode-drawing
+Common Alias(fdiagnostics-unicode-drawing=,always,never)
+;
+
+fdiagnostics-unicode-drawing=
+Common Joined RejectNegative Enum(diagnostics_unicode_drawing)
+-fdiagnostics-unicode-drawing=[never|always|auto]      Use Unicode drawing 
characters in diagnostics.
+
+Enum
+Name(diagnostics_unicode_drawing) Type(int)
+
+EnumValue
+Enum(diagnostics_unicode_drawing) String(never) 
Value(DIAGNOSTICS_UNICODE_DRAWING_NO)
+
+EnumValue
+Enum(diagnostics_unicode_drawing) String(always) 
Value(DIAGNOSTICS_UNICODE_DRAWING_YES)
+
+EnumValue
+Enum(diagnostics_unicode_drawing) String(auto) 
Value(DIAGNOSTICS_UNICODE_DRAWING_AUTO)
+
 Enum
 Name(diagnostic_path_format) Type(int)
 
diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
index da3c5b6a92d..a945307a71c 100644
--- a/gcc/diagnostic-show-locus.c
+++ b/gcc/diagnostic-show-locus.c
@@ -62,6 +62,7 @@ struct point_state
 {
   int range_idx;
   bool draw_caret_p;
+  bool has_label_p;
 };
 
 /* A class to inject colorization codes when printing the diagnostic locus.
@@ -1467,7 +1468,7 @@ layout::print_source_line (linenum_type row, const char 
*line, int line_bytes)
       int width = num_digits (row);
       for (int i = 0; i < m_linenum_width - width; i++)
        pp_space (m_pp);
-      pp_printf (m_pp, "%i | ", row);
+      pp_printf (m_pp, "%i %s ", row, m_context->drawing.vertical);
     }
   else
     pp_space (m_pp);
@@ -1596,7 +1597,8 @@ layout::start_annotation_line (char margin_char) const
        pp_space (m_pp);
       for (; i < m_linenum_width; i++)
        pp_character (m_pp, margin_char);
-      pp_string (m_pp, " |");
+      pp_space (m_pp);
+      pp_string (m_pp, m_context->drawing.vertical);
     }
 }
 
@@ -1628,15 +1630,21 @@ layout::print_annotation_line (linenum_type row, const 
line_bounds lbounds)
          if (state.draw_caret_p)
            {
              /* Draw the caret.  */
-             char caret_char;
+             const char *caret_char;
              if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
                caret_char = m_context->caret_chars[state.range_idx];
              else
-               caret_char = '^';
-             pp_character (m_pp, caret_char);
+               caret_char = m_context->drawing.default_caret;
+             pp_string (m_pp, caret_char);
            }
          else
-           pp_character (m_pp, '~');
+           {
+             const char *const underline
+               = state.has_label_p
+               ? m_context->drawing.tee_down2
+               : m_context->drawing.horizontal2;
+             pp_string (m_pp, underline);
+           }
        }
       else
        {
@@ -1821,7 +1829,7 @@ layout::print_any_labels (linenum_type row)
                gcc_assert (column <= label->m_column);
                move_to_column (&column, label->m_column, true);
                m_colorizer.set_range (label->m_state_idx);
-               pp_character (m_pp, '|');
+               pp_string (m_pp, m_context->drawing.vertical);
                m_colorizer.set_normal_text ();
                column++;
              }
@@ -2343,7 +2351,7 @@ layout::print_trailing_fixits (linenum_type row)
              move_to_column (&column, start_column, true);
              m_colorizer.set_fixit_delete ();
              for (; column <= finish_column; column++)
-               pp_character (m_pp, '-');
+               pp_string (m_pp, m_context->drawing.horizontal1);
              m_colorizer.set_normal_text ();
            }
          /* Print the replacement text.  REPLACE also covers
@@ -2400,12 +2408,19 @@ layout::get_state_at_point (/* Inputs.  */
        {
          out_state->range_idx = i;
 
-         /* Are we at the range's caret?  is it visible? */
+         /* If we are at the range's caret, check if it is visible and check
+            if a label line will be output directly below this point, since
+            that enables unicode drawing to use a nicer "tee" character to
+            mesh seamlessly with the label line.  */
          out_state->draw_caret_p = false;
-         if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
-             && row == range->m_caret.m_line
+         out_state->has_label_p = false;
+         if (row == range->m_caret.m_line
              && column == range->m_caret.m_columns[col_unit])
-           out_state->draw_caret_p = true;
+           {
+             out_state->draw_caret_p
+               = (range->m_range_display_kind == SHOW_RANGE_WITH_CARET);
+             out_state->has_label_p = (range->m_label != NULL);
+           }
 
          /* Within a multiline range, don't display any underline
             in any leading or trailing whitespace on a line.
@@ -2976,19 +2991,19 @@ test_one_liner_multiple_carets_and_ranges ()
     = make_location (linemap_position_for_column (line_table, 2),
                     linemap_position_for_column (line_table, 1),
                     linemap_position_for_column (line_table, 3));
-  dc.caret_chars[0] = 'A';
+  dc.caret_chars[0] = "A";
 
   location_t bar
     = make_location (linemap_position_for_column (line_table, 8),
                     linemap_position_for_column (line_table, 7),
                     linemap_position_for_column (line_table, 9));
-  dc.caret_chars[1] = 'B';
+  dc.caret_chars[1] = "B";
 
   location_t field
     = make_location (linemap_position_for_column (line_table, 13),
                     linemap_position_for_column (line_table, 11),
                     linemap_position_for_column (line_table, 15));
-  dc.caret_chars[2] = 'C';
+  dc.caret_chars[2] = "C";
 
   rich_location richloc (line_table, foo);
   richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
@@ -3584,19 +3599,19 @@ test_one_liner_multiple_carets_and_ranges_utf8 ()
     = make_location (linemap_position_for_column (line_table, 7),
                     linemap_position_for_column (line_table, 1),
                     linemap_position_for_column (line_table, 8));
-  dc.caret_chars[0] = 'A';
+  dc.caret_chars[0] = "A";
 
   location_t bar
     = make_location (linemap_position_for_column (line_table, 16),
                     linemap_position_for_column (line_table, 12),
                     linemap_position_for_column (line_table, 17));
-  dc.caret_chars[1] = 'B';
+  dc.caret_chars[1] = "B";
 
   location_t field
     = make_location (linemap_position_for_column (line_table, 26),
                     linemap_position_for_column (line_table, 19),
                     linemap_position_for_column (line_table, 30));
-  dc.caret_chars[2] = 'C';
+  dc.caret_chars[2] = "C";
   rich_location richloc (line_table, foo);
   richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
   richloc.add_range (field, SHOW_RANGE_WITH_CARET);
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index 1b6c9845892..131b7220dcb 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -167,6 +167,14 @@ default_diagnostic_final_cb (diagnostic_context *context)
     }
 }
 
+/* Store the carets in an unnamed namespace so we can verify whether
+   or not a front-end has overriden them.  */
+namespace {
+  const char plain_caret[] = "^";
+  /* U+2227 = Logical And */
+  const char fancy_caret[] = "\xE2\x88\xA7";
+}
+
 /* Initialize the diagnostic message outputting machinery.  */
 void
 diagnostic_initialize (diagnostic_context *context, int n_opts)
@@ -187,7 +195,7 @@ diagnostic_initialize (diagnostic_context *context, int 
n_opts)
   context->show_caret = false;
   diagnostic_set_caret_max_width (context, pp_line_cutoff (context->printer));
   for (i = 0; i < rich_location::STATICALLY_ALLOCATED_RANGES; i++)
-    context->caret_chars[i] = '^';
+    context->caret_chars[i] = plain_caret;
   context->show_cwe = false;
   context->path_format = DPF_NONE;
   context->show_path_depths = false;
@@ -229,6 +237,7 @@ diagnostic_initialize (diagnostic_context *context, int 
n_opts)
   context->begin_group_cb = NULL;
   context->end_group_cb = NULL;
   context->final_cb = default_diagnostic_final_cb;
+  diagnostic_drawing_init (context, DIAGNOSTICS_UNICODE_DRAWING_AUTO);
 }
 
 /* Maybe initialize the color support. We require clients to do this
@@ -286,6 +295,83 @@ diagnostic_urls_init (diagnostic_context *context, int 
value /*= -1 */)
     = determine_url_format ((diagnostic_url_rule_t) value);
 }
 
+/* Initialize unicode drawing support in CONTEXT if requested, or, in auto
+   mode, if the locale supports it.  */
+void
+diagnostic_drawing_init (diagnostic_context *context,
+                        enum diagnostics_drawing_rule rule)
+{
+  switch (rule)
+    {
+    case DIAGNOSTICS_UNICODE_DRAWING_NO:
+      context->drawing.enabled = false;
+      break;
+    case DIAGNOSTICS_UNICODE_DRAWING_YES:
+      context->drawing.enabled = true;
+      break;
+    case DIAGNOSTICS_UNICODE_DRAWING_AUTO:
+      context->drawing.enabled = locale_utf8;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  if (context->drawing.enabled)
+    {
+      /* U+2500 = Box Drawings Light Horizontal */
+      context->drawing.horizontal1 = "\xE2\x94\x80";
+
+      /* U+2550 = Box Drawings Double Horizontal */
+      context->drawing.horizontal2 = "\xE2\x95\x90";
+
+      /* U+252C = Box Drawings Light Down and Horizontal */
+      context->drawing.tee_down1 = "\xE2\x94\xAC";
+
+      /* U+2564 = Box Drawings Down Single and Horizontal Double */
+      context->drawing.tee_down2 = "\xE2\x95\xA4";
+
+      /* U+2502 = Box Drawings Light Vertical */
+      context->drawing.vertical = "\xE2\x94\x82";
+
+      /* U+250C = Box Drawings Light Down and Right */
+      context->drawing.corner_nw = "\xE2\x94\x8C";
+
+      /* U+2510 = Box Drawings Light Down and Left */
+      context->drawing.corner_ne = "\xE2\x94\x90";
+
+      /* U+2518 = Box Drawings Light Up and Left */
+      context->drawing.corner_se = "\xE2\x94\x98";
+
+      /* U+2514 = Box Drawings Light Up and Right */
+      context->drawing.corner_sw = "\xE2\x94\x94";
+
+      context->drawing.default_caret = fancy_caret;
+    }
+  else
+    {
+      context->drawing.horizontal1 = "-";
+      context->drawing.tee_down1 = "-";
+      context->drawing.horizontal2 = "~";
+      context->drawing.tee_down2 = "~";
+      context->drawing.vertical = "|";
+      context->drawing.corner_nw = "+";
+      context->drawing.corner_ne = "+";
+      context->drawing.corner_se = "+";
+      context->drawing.corner_sw = "+";
+      context->drawing.default_caret = plain_caret;
+    }
+
+  /* Configure the customizable carets, unless a front-end has already changed
+     them to something else.  */
+  for (int i = 0; i != rich_location::STATICALLY_ALLOCATED_RANGES; ++i)
+    {
+      if (context->caret_chars[i] == plain_caret
+         || context->caret_chars[i] == fancy_caret)
+       {
+         context->caret_chars[i] = context->drawing.default_caret;
+       }
+    }
+}
+
 /* Do any cleaning up required after the last diagnostic is emitted.  */
 
 void
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index 4051601abfd..329ed555139 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -159,8 +159,9 @@ struct diagnostic_context
   /* Maximum width of the source line printed.  */
   int caret_max_width;
 
-  /* Character used for caret diagnostics.  */
-  char caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES];
+  /* Character used for caret diagnostics.  These are strings to accommodate
+     multibyte characters, but they should have display width 1.  */
+  const char *caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES];
 
   /* True if we should print any CWE identifiers associated with
      diagnostics.  */
@@ -326,6 +327,23 @@ struct diagnostic_context
 
   /* Callback for final cleanup.  */
   void (*final_cb) (diagnostic_context *context);
+
+  /* Some output elements that can be made to look nicer if UTF-8 output
+     is available.  */
+  struct
+  {
+    bool enabled;
+    const char *horizontal1;
+    const char *horizontal2;
+    const char *tee_down1;
+    const char *tee_down2;
+    const char *vertical;
+    const char *corner_nw;
+    const char *corner_ne;
+    const char *corner_se;
+    const char *corner_sw;
+    const char *default_caret;
+  } drawing;
 };
 
 static inline void
@@ -396,6 +414,16 @@ diagnostic_override_option_index (diagnostic_info *info, 
int optidx)
 extern void diagnostic_initialize (diagnostic_context *, int);
 extern void diagnostic_color_init (diagnostic_context *, int value = -1);
 extern void diagnostic_urls_init (diagnostic_context *, int value = -1);
+
+enum diagnostics_drawing_rule
+{
+  DIAGNOSTICS_UNICODE_DRAWING_NO,
+  DIAGNOSTICS_UNICODE_DRAWING_YES,
+  DIAGNOSTICS_UNICODE_DRAWING_AUTO
+};
+extern void diagnostic_drawing_init (diagnostic_context *,
+                                    enum diagnostics_drawing_rule rule);
+
 extern void diagnostic_finish (diagnostic_context *);
 extern void diagnostic_report_current_module (diagnostic_context *, 
location_t);
 extern void diagnostic_show_locus (diagnostic_context *,
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ba18e05fb1a..6ddbe556667 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -282,6 +282,7 @@ Objective-C and Objective-C++ Dialects}.
 @gccoptlist{-fmessage-length=@var{n}  @gol
 -fdiagnostics-show-location=@r{[}once@r{|}every-line@r{]}  @gol
 -fdiagnostics-color=@r{[}auto@r{|}never@r{|}always@r{]}  @gol
+-fdiagnostics-unicode-drawing=@r{[}auto@r{|}never@r{|}always@r{]}  @gol
 -fdiagnostics-urls=@r{[}auto@r{|}never@r{|}always@r{]}  @gol
 -fdiagnostics-format=@r{[}text@r{|}json@r{]}  @gol
 -fno-diagnostics-show-option  -fno-diagnostics-show-caret @gol
@@ -4423,6 +4424,16 @@ SGR substring for highlighting mismatching types within 
template
 arguments in the C++ frontend.
 @end table
 
+@item -fdiagnostics-unicode-drawing[=@var{WHEN}]
+@opindex fdiagnostics-unicode-drawing
+@opindex fno-diagnostics-unicode-drawing
+Use extended characters from the Unicode Standard (such as line drawing
+characters) to improve the appearance of diagnostics.
+
+@var{WHEN} is @samp{never}, @samp{always}, or @samp{auto}.
+@samp{auto}, the default, enables the feature provided the user's locale
+supports UTF-8.
+
 @item -fdiagnostics-urls[=@var{WHEN}]
 @opindex fdiagnostics-urls
 @cindex urls
diff --git a/gcc/fortran/error.c b/gcc/fortran/error.c
index dacc1d7ba51..68be60309ce 100644
--- a/gcc/fortran/error.c
+++ b/gcc/fortran/error.c
@@ -1532,8 +1532,8 @@ gfc_diagnostics_init (void)
   global_dc->start_span = gfc_diagnostic_start_span;
   diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer;
   diagnostic_format_decoder (global_dc) = gfc_format_decoder;
-  global_dc->caret_chars[0] = '1';
-  global_dc->caret_chars[1] = '2';
+  global_dc->caret_chars[0] = "1";
+  global_dc->caret_chars[1] = "2";
   pp_warning_buffer = new (XNEW (output_buffer)) output_buffer ();
   pp_warning_buffer->flush_p = false;
   /* pp_error_buffer is statically allocated.  This simplifies memory
@@ -1550,6 +1550,6 @@ gfc_diagnostics_finish (void)
      defaults.  */
   diagnostic_starter (global_dc) = gfc_diagnostic_starter;
   diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer;
-  global_dc->caret_chars[0] = '^';
-  global_dc->caret_chars[1] = '^';
+  global_dc->caret_chars[0] = global_dc->drawing.default_caret;
+  global_dc->caret_chars[1] = global_dc->drawing.default_caret;
 }
diff --git a/gcc/opts.c b/gcc/opts.c
index 499eb900643..653580e11de 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -2395,6 +2395,10 @@ common_handle_option (struct gcc_options *opts,
       diagnostic_urls_init (dc, value);
       break;
 
+    case OPT_fdiagnostics_unicode_drawing_:
+      diagnostic_drawing_init (dc, (enum diagnostics_drawing_rule)value);
+      break;
+
     case OPT_fdiagnostics_format_:
       diagnostic_output_format_init (dc,
                                     (enum diagnostics_output_format)value);
diff --git a/gcc/selftest-diagnostic.c b/gcc/selftest-diagnostic.c
index 82fddca89ab..3bce8e58048 100644
--- a/gcc/selftest-diagnostic.c
+++ b/gcc/selftest-diagnostic.c
@@ -36,6 +36,10 @@ namespace selftest {
 test_diagnostic_context::test_diagnostic_context ()
 {
   diagnostic_initialize (this, 0);
+
+  /* Disable unicode drawing to make it simpler to write selftest code.  */
+  diagnostic_drawing_init (this, DIAGNOSTICS_UNICODE_DRAWING_NO);
+
   show_caret = true;
   show_labels_p = true;
   show_column = true;
diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-5-utf8.c 
b/gcc/testsuite/gcc.dg/analyzer/setjmp-5-utf8.c
new file mode 100644
index 00000000000..6f65cf9a2da
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-5-utf8.c
@@ -0,0 +1,71 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers 
-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret 
-fdiagnostics-unicode-drawing" } */
+
+/* This tests the same functionality as setjmp-5.c, but enables unicode drawing
+   so as to exercise those features.
+   n.b. NN line numbers do not support unicode drawing mode, so we do not
+   make use of that here.  */
+
+#include "test-setjmp.h"
+#include <stddef.h>
+#include "analyzer-decls.h"
+
+static jmp_buf env;
+
+static void inner (void)
+{
+  SETJMP (env);
+}
+
+void outer (void)
+{
+  int i;
+
+  inner ();
+
+  longjmp (env, 42); /* { dg-warning "'longjmp' called after enclosing 
function of 'setjmp' has returned" } */
+}
+
+/* { dg-begin-multiline-output "" }
+   25 │   longjmp (env, 42);
+      │   ∧════════════════
+  'outer': events 1-2
+    │
+    │   19 │ void outer (void)
+    │      │      ∧════
+    │      │      │
+    │      │      (1) entry to 'outer'
+    │......
+    │   23 │   inner ();
+    │      │   ╤═══════
+    │      │   │
+    │      │   (2) calling 'inner' from 'outer'
+    │
+    └──> 'inner': event 3
+           │
+           │   14 │ static void inner (void)
+           │      │             ∧════
+           │      │             │
+           │      │             (3) entry to 'inner'
+           │
+         'inner': event 4
+           │
+           │   16 │   SETJMP (env);
+           │      │   ∧═════
+           │      │   │
+           │      │   (4) 'setjmp' called here
+           │
+    ┌<─────┘
+    │
+  'outer': events 5-6
+    │
+    │   23 │   inner ();
+    │      │   ∧═══════
+    │      │   │
+    │      │   (5) returning to 'outer' from 'inner'
+    │   24 │ 
+    │   25 │   longjmp (env, 42);
+    │      │   ╤════════════════
+    │      │   │
+    │      │   (6) here
+    │
+    { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/format/diagnostic-ranges-utf8.c 
b/gcc/testsuite/gcc.dg/format/diagnostic-ranges-utf8.c
new file mode 100644
index 00000000000..ac7638e003a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/diagnostic-ranges-utf8.c
@@ -0,0 +1,394 @@
+/* { dg-options "-Wformat -fdiagnostics-show-caret 
-fdiagnostics-unicode-drawing" } */
+
+/* This performs the same tests as diagnostic-ranges.c, but it enables unicode
+   drawing so that this can be tested.  */
+
+
+#include "format.h"
+
+void test_mismatching_types (const char *msg)
+{
+  printf("hello %i", msg);  /* { dg-warning "format '%i' expects argument of 
type 'int', but argument 2 has type 'const char \\*' " } */
+
+/* { dg-begin-multiline-output "" }
+   printf("hello %i", msg);
+                 ═∧   ╤══
+                  │   │
+                  int const char *
+                 %s
+   { dg-end-multiline-output "" } */
+
+
+  printf("hello %s", 42);  /* { dg-warning "format '%s' expects argument of 
type 'char \\*', but argument 2 has type 'int'" } */
+/* { dg-begin-multiline-output "" }
+   printf("hello %s", 42);
+                 ═∧   ╤═
+                  │   │
+                  │   int
+                  char *
+                 %d
+   { dg-end-multiline-output "" } */
+
+  printf("hello %i", (long)0);  /* { dg-warning "format '%i' expects argument 
of type 'int', but argument 2 has type 'long int' " } */
+/* { dg-begin-multiline-output "" }
+   printf("hello %i", (long)0);
+                 ═∧   ╤══════
+                  │   │
+                  int long int
+                 %li
+   { dg-end-multiline-output "" } */
+}
+
+void test_multiple_arguments (void)
+{
+  printf ("arg0: %i  arg1: %s arg 2: %i", /* { dg-warning "29: format '%s'" } 
*/
+          100, 101, 102);
+/* { dg-begin-multiline-output "" }
+   printf ("arg0: %i  arg1: %s arg 2: %i",
+                            ═∧
+                             │
+                             char *
+                            %d
+           100, 101, 102);
+                ╤══           
+                │
+                int
+   { dg-end-multiline-output "" } */
+}
+
+void test_multiple_arguments_2 (int i, int j)
+{
+  printf ("arg0: %i  arg1: %s arg 2: %i", /* { dg-warning "29: format '%s'" } 
*/
+          100, i + j, 102);
+/* { dg-begin-multiline-output "" }
+   printf ("arg0: %i  arg1: %s arg 2: %i",
+                            ═∧
+                             │
+                             char *
+                            %d
+           100, i + j, 102);
+                ══╤══         
+                  │
+                  int
+   { dg-end-multiline-output "" } */
+}
+
+void multiline_format_string (void) {
+  printf ("before the fmt specifier" /* { dg-warning "11: format '%d' expects 
a matching 'int' argument" } */
+/* { dg-begin-multiline-output "" }
+   printf ("before the fmt specifier"
+           ∧═════════════════════════
+   { dg-end-multiline-output "" } */
+
+          "%"
+          "d" /* { dg-message "12: format string is defined here" } */
+          "after the fmt specifier");
+
+/* { dg-begin-multiline-output "" }
+           "%"
+            ══
+           "d"
+           ═∧
+            │
+            int
+   { dg-end-multiline-output "" } */
+}
+
+void test_hex (const char *msg)
+{
+  /* "%" is \x25
+     "i" is \x69 */
+  printf("hello \x25\x69", msg);  /* { dg-warning "format '%i' expects 
argument of type 'int', but argument 2 has type 'const char \\*' " } */
+
+/* { dg-begin-multiline-output "" }
+   printf("hello \x25\x69", msg);
+                 ════∧═══   ╤══
+                     │      │
+                     int    const char *
+                 \x25s
+   { dg-end-multiline-output "" } */
+}
+
+void test_oct (const char *msg)
+{
+  /* "%" is octal 045
+     "i" is octal 151.  */
+  printf("hello \045\151", msg);  /* { dg-warning "format '%i' expects 
argument of type 'int', but argument 2 has type 'const char \\*' " } */
+
+/* { dg-begin-multiline-output "" }
+   printf("hello \045\151", msg);
+                 ════∧═══   ╤══
+                     │      │
+                     int    const char *
+                 \045s
+   { dg-end-multiline-output "" } */
+}
+
+void test_multiple (const char *msg)
+{
+  /* "%" is \x25 in hex
+     "i" is \151 in octal.  */
+  printf("prefix"  "\x25"  "\151"  "suffix",  /* { dg-warning "format '%i'" } 
*/
+         msg);
+/* { dg-begin-multiline-output "" }
+   printf("prefix"  "\x25"  "\151"  "suffix",
+          ∧═══════
+          msg);
+          ╤══
+          │
+          const char *
+  { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+   printf("prefix"  "\x25"  "\151"  "suffix",
+                     ════════∧═══
+                             │
+                             int
+                     \x25"  "s
+  { dg-end-multiline-output "" } */
+}
+
+void test_u8 (const char *msg)
+{
+  printf(u8"hello %i", msg);/* { dg-warning "format '%i' expects argument of 
type 'int', but argument 2 has type 'const char \\*' " } */
+/* { dg-begin-multiline-output "" }
+   printf(u8"hello %i", msg);
+                   ═∧   ╤══
+                    │   │
+                    int const char *
+                   %s
+   { dg-end-multiline-output "" } */
+}
+
+void test_param (long long_i, long long_j)
+{
+  printf ("foo %s bar", long_i + long_j); /* { dg-warning "17: format '%s' 
expects argument of type 'char \\*', but argument 2 has type 'long int'" } */
+/* { dg-begin-multiline-output "" }
+   printf ("foo %s bar", long_i + long_j);
+                ═∧       ═══════╤═══════
+                 │              │
+                 char *         long int
+                %ld
+   { dg-end-multiline-output "" } */
+}
+
+void test_field_width_specifier (long l, int i1, int i2)
+{
+  printf (" %*.*d ", l, i1, i2); /* { dg-warning "14: field width specifier 
'\\*' expects argument of type 'int', but argument 2 has type 'long int'" } */
+/* { dg-begin-multiline-output "" }
+   printf (" %*.*d ", l, i1, i2);
+             ═∧═══    ╤
+              │       │
+              int     long int
+   { dg-end-multiline-output "" } */
+}
+
+/* PR c/72857.  */
+
+void test_field_width_specifier_2 (char *d, long foo, long bar)
+{
+  __builtin_sprintf (d, " %*ld ", foo, foo); /* { dg-warning "28: field width 
specifier '\\*' expects argument of type 'int', but argument 3 has type 'long 
int'" } */
+  /* { dg-begin-multiline-output "" }
+   __builtin_sprintf (d, " %*ld ", foo, foo);
+                           ═∧══    ╤══
+                            │      │
+                            int    long int
+   { dg-end-multiline-output "" } */
+
+  __builtin_sprintf (d, " %*ld ", foo + bar, foo); /* { dg-warning "28: field 
width specifier '\\*' expects argument of type 'int', but argument 3 has type 
'long int'" } */
+  /* { dg-begin-multiline-output "" }
+   __builtin_sprintf (d, " %*ld ", foo + bar, foo);
+                           ═∧══    ════╤════
+                            │          │
+                            int        long int
+   { dg-end-multiline-output "" } */
+}
+
+void test_field_precision_specifier (char *d, long foo, long bar)
+{
+  __builtin_sprintf (d, " %.*ld ", foo, foo); /* { dg-warning "29: field 
precision specifier '\\.\\*' expects argument of type 'int', but argument 3 has 
type 'long int'" } */
+  /* { dg-begin-multiline-output "" }
+   __builtin_sprintf (d, " %.*ld ", foo, foo);
+                           ══∧══    ╤══
+                             │      │
+                             int    long int
+   { dg-end-multiline-output "" } */
+
+  __builtin_sprintf (d, " %.*ld ", foo + bar, foo); /* { dg-warning "29: field 
precision specifier '\\.\\*' expects argument of type 'int', but argument 3 has 
type 'long int'" } */
+  /* { dg-begin-multiline-output "" }
+   __builtin_sprintf (d, " %.*ld ", foo + bar, foo);
+                           ══∧══    ════╤════
+                             │          │
+                             int        long int
+   { dg-end-multiline-output "" } */
+}
+
+void test_spurious_percent (void)
+{
+  printf("hello world %"); /* { dg-warning "23: spurious trailing" } */
+
+/* { dg-begin-multiline-output "" }
+   printf("hello world %");
+                       ∧
+   { dg-end-multiline-output "" } */
+}
+
+void test_empty_precision (char *s, size_t m, double d)
+{
+  strfmon (s, m, "%#.5n", d); /* { dg-warning "20: empty left precision in 
gnu_strfmon format" } */
+/* { dg-begin-multiline-output "" }
+   strfmon (s, m, "%#.5n", d);
+                    ∧
+   { dg-end-multiline-output "" } */
+
+  strfmon (s, m, "%#5.n", d); /* { dg-warning "22: empty precision in 
gnu_strfmon format" } */
+/* { dg-begin-multiline-output "" }
+   strfmon (s, m, "%#5.n", d);
+                      ∧
+   { dg-end-multiline-output "" } */
+}
+
+void test_repeated (int i)
+{
+  printf ("%++d", i); /* { dg-warning "14: repeated '\\+' flag in format" } */
+/* { dg-begin-multiline-output "" }
+   printf ("%++d", i);
+              ∧
+   { dg-end-multiline-output "" } */
+}
+
+void test_conversion_lacks_type (void)
+{
+  printf (" %h"); /* { dg-warning "14:conversion lacks type at end of format" 
} */
+/* { dg-begin-multiline-output "" }
+   printf (" %h");
+              ∧
+   { dg-end-multiline-output "" } */
+}
+
+void test_embedded_nul (void)
+{
+  printf (" \0 "); /* { dg-warning "13:embedded" "warning for embedded NUL" } 
*/
+/* { dg-begin-multiline-output "" }
+   printf (" \0 ");
+             ∧═
+   { dg-end-multiline-output "" } */
+}
+
+void test_macro (const char *msg)
+{
+#define INT_FMT "%i" /* { dg-message "19: format string is defined here" } */
+  printf("hello " INT_FMT " world", msg);  /* { dg-warning "10: format '%i' 
expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
+/* { dg-begin-multiline-output "" }
+   printf("hello " INT_FMT " world", msg);
+          ∧═══════                   ╤══
+                                     │
+                                     const char *
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
+ #define INT_FMT "%i"
+                  ═∧
+                   │
+                   int
+                  %s
+   { dg-end-multiline-output "" } */
+#undef INT_FMT
+}
+
+void test_macro_2 (const char *msg)
+{
+#define PRIu32 "u" /* { dg-message "17: format string is defined here" } */
+  printf("hello %" PRIu32 " world", msg);  /* { dg-warning "10: format '%u' 
expects argument of type 'unsigned int', but argument 2 has type 'const char 
\\*' " } */
+/* { dg-begin-multiline-output "" }
+   printf("hello %" PRIu32 " world", msg);
+          ∧════════                  ╤══
+                                     │
+                                     const char *
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
+ #define PRIu32 "u"
+                 ∧
+                 │
+                 unsigned int
+   { dg-end-multiline-output "" } */
+#undef PRIu32
+}
+
+void test_macro_3 (const char *msg)
+{
+#define FMT_STRING "hello %i world" /* { dg-line test_macro_3_macro_line } */
+  /* { dg-warning "20: format '%i' expects argument of type 'int', but 
argument 2 has type 'const char \\*'" "" { target *-*-*} .-1 } */
+  printf(FMT_STRING, msg);  /* { dg-message "10: in expansion of macro 
'FMT_STRING" } */
+/* { dg-begin-multiline-output "" }
+ #define FMT_STRING "hello %i world"
+                    ∧═══════════════
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
+   printf(FMT_STRING, msg);
+          ∧═════════
+   { dg-end-multiline-output "" } */
+/* { dg-message "28: format string is defined here" "" { target *-*-* } 
test_macro_3_macro_line } */
+/* { dg-begin-multiline-output "" }
+ #define FMT_STRING "hello %i world"
+                           ═∧
+                            │
+                            int
+                           %s
+   { dg-end-multiline-output "" } */
+#undef FMT_STRING
+}
+
+void test_macro_4 (const char *msg)
+{
+#define FMT_STRING "hello %i world" /* { dg-warning "20: format '%i' expects 
argument of type 'int', but argument 2 has type 'const char \\*' " } */
+  printf(FMT_STRING "\n", msg);  /* { dg-message "10: in expansion of macro 
'FMT_STRING" } */
+/* { dg-begin-multiline-output "" }
+ #define FMT_STRING "hello %i world"
+                    ∧═══════════════
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
+   printf(FMT_STRING "\n", msg);
+          ∧═════════
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
+ #define FMT_STRING "hello %i world"
+                           ═∧
+                            │
+                            int
+                           %s
+   { dg-end-multiline-output "" } */
+#undef FMT_STRING
+}
+
+void test_non_contiguous_strings (void)
+{
+  __builtin_printf(" %" "d ", 0.5); /* { dg-warning "20: format .%d. expects 
argument of type .int., but argument 2 has type .double." } */
+                                    /* { dg-message "26: format string is 
defined here" "" { target *-*-* } .-1 } */
+  /* { dg-begin-multiline-output "" }
+   __builtin_printf(" %" "d ", 0.5);
+                    ∧═══       ╤══
+                               │
+                               double
+   { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   __builtin_printf(" %" "d ", 0.5);
+                      ════∧
+                          │
+                          int
+                      %" "f
+   { dg-end-multiline-output "" } */
+}
+
+void test_const_arrays (void)
+{
+  /* TODO: ideally we'd highlight both the format string *and* the use of
+     it here.  For now, just verify that we gracefully handle this case.  */
+  const char a[] = " %d ";
+  __builtin_printf(a, 0.5); /* { dg-warning "20: format .%d. expects argument 
of type .int., but argument 2 has type .double." } */
+  /* { dg-begin-multiline-output "" }
+   __builtin_printf(a, 0.5);
+                    ∧  ╤══
+                       │
+                       double
+   { dg-end-multiline-output "" } */
+}
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
index 482dbda47f7..e662ec43347 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
@@ -289,11 +289,11 @@ test_show_locus (function *fun)
       location_t caret_b = get_loc (line, 11);
       rich_location richloc (line_table, caret_a);
       add_range (&richloc, caret_b, caret_b, SHOW_RANGE_WITH_CARET);
-      global_dc->caret_chars[0] = 'A';
-      global_dc->caret_chars[1] = 'B';
+      global_dc->caret_chars[0] = "A";
+      global_dc->caret_chars[1] = "B";
       warning_at (&richloc, 0, "test");
-      global_dc->caret_chars[0] = '^';
-      global_dc->caret_chars[1] = '^';
+      global_dc->caret_chars[0] = global_dc->drawing.default_caret;
+      global_dc->caret_chars[1] = global_dc->drawing.default_caret;
     }
 
   /* Tests of rendering fixit hints.  */
@@ -407,11 +407,11 @@ test_show_locus (function *fun)
       location_t caret_b = get_loc (line - 1, 19);
       rich_location richloc (line_table, caret_a);
       richloc.add_range (caret_b, SHOW_RANGE_WITH_CARET);
-      global_dc->caret_chars[0] = '1';
-      global_dc->caret_chars[1] = '2';
+      global_dc->caret_chars[0] = "1";
+      global_dc->caret_chars[1] = "2";
       warning_at (&richloc, 0, "test");
-      global_dc->caret_chars[0] = '^';
-      global_dc->caret_chars[1] = '^';
+      global_dc->caret_chars[0] = global_dc->drawing.default_caret;
+      global_dc->caret_chars[1] = global_dc->drawing.default_caret;
     }
 
   /* Example of using the "%q+D" format code, which as well as printing
diff --git a/gcc/testsuite/lib/c-compat.exp b/gcc/testsuite/lib/c-compat.exp
index 9493c214aea..be4f8549d5a 100644
--- a/gcc/testsuite/lib/c-compat.exp
+++ b/gcc/testsuite/lib/c-compat.exp
@@ -36,7 +36,7 @@ load_lib target-libpath.exp
 proc compat-use-alt-compiler { } {
     global GCC_UNDER_TEST ALT_CC_UNDER_TEST
     global compat_same_alt compat_alt_caret compat_alt_color compat_no_line_no
-    global compat_alt_urls
+    global compat_alt_urls compat_alt_drawing
     global TEST_ALWAYS_FLAGS
 
     # We don't need to do this if the alternate compiler is actually
@@ -52,6 +52,9 @@ proc compat-use-alt-compiler { } {
        if { $compat_alt_urls == 0 } then {
            regsub -- "-fdiagnostics-urls=never" $TEST_ALWAYS_FLAGS "" 
TEST_ALWAYS_FLAGS
        }
+       if { $compat_alt_drawing == 0 } then {
+           regsub -- "-fdiagnostics-unicode-drawing=never" $TEST_ALWAYS_FLAGS 
"" TEST_ALWAYS_FLAGS
+       }
        if { $compat_no_line_no == 0 } then {
            regsub -- "-fno-diagnostics-show-line-numbers" $TEST_ALWAYS_FLAGS 
"" TEST_ALWAYS_FLAGS
        }
@@ -85,12 +88,14 @@ proc compat_setup_dfp { } {
     global compat_alt_caret
     global compat_alt_color
     global compat_alt_urls
+    global compat_alt_drawing
     global compat_no_line_no
     global TEST_ALWAYS_FLAGS compat_save_TEST_ALWAYS_FLAGS
 
     set compat_alt_caret 0
     set compat_alt_color 0
     set compat_alt_urls 0
+    set compat_alt_drawing 0
     set compat_no_line_no 0
     set compat_save_TEST_ALWAYS_FLAGS $TEST_ALWAYS_FLAGS
 
@@ -115,6 +120,10 @@ proc compat_setup_dfp { } {
                int dummy; } "-fdiagnostics-urls=never"] != 0 } {
            set compat_alt_urls 1
        }
+       if { [check_no_compiler_messages_nocache compat_alt_has_drawing object {
+               int dummy; } "-fdiagnostics-unicode-drawing=never"] != 0 } {
+           set compat_alt_drawing 1
+       }
        if { [check_no_compiler_messages_nocache compat_alt_has_no_line_no 
object {
                int dummy; } "-fno-diagnostics-show-line-numbers"] != 0 } {
            set compat_no_line_no 1
diff --git a/gcc/testsuite/lib/prune.exp b/gcc/testsuite/lib/prune.exp
index 1c776249f1a..54ab417889f 100644
--- a/gcc/testsuite/lib/prune.exp
+++ b/gcc/testsuite/lib/prune.exp
@@ -21,7 +21,7 @@ load_lib multiline.exp
 if ![info exists TEST_ALWAYS_FLAGS] {
     set TEST_ALWAYS_FLAGS ""
 }
-set TEST_ALWAYS_FLAGS "-fno-diagnostics-show-caret 
-fno-diagnostics-show-line-numbers -fdiagnostics-color=never  
-fdiagnostics-urls=never $TEST_ALWAYS_FLAGS"
+set TEST_ALWAYS_FLAGS "-fno-diagnostics-show-caret 
-fno-diagnostics-show-line-numbers -fdiagnostics-color=never 
-fdiagnostics-urls=never -fdiagnostics-unicode-drawing=never $TEST_ALWAYS_FLAGS"
 
 proc prune_gcc_output { text } {
     global srcdir
diff --git a/gcc/tree-diagnostic-path.cc b/gcc/tree-diagnostic-path.cc
index 82b3c2d6b6a..fe1c7bd1df4 100644
--- a/gcc/tree-diagnostic-path.cc
+++ b/gcc/tree-diagnostic-path.cc
@@ -332,11 +332,13 @@ path_summary::print (diagnostic_context *dc, bool 
show_depths) const
          if (range->m_stack_depth > prev_range->m_stack_depth)
            {
              /* Show pushed stack frame(s).  */
-             const char *push_prefix = "+--> ";
              pp_string (pp, start_line_color);
-             pp_string (pp, push_prefix);
+             pp_string (pp, dc->drawing.corner_sw);
+             pp_string (pp, dc->drawing.horizontal1);
+             pp_string (pp, dc->drawing.horizontal1);
+             pp_string (pp, "> ");
+             cur_indent += 5;
              pp_string (pp, end_line_color);
-             cur_indent += strlen (push_prefix);
            }
        }
       if (range->m_fndecl)
@@ -358,7 +360,7 @@ path_summary::print (diagnostic_context *dc, bool 
show_depths) const
       {
        write_indent (pp, cur_indent + per_frame_indent);
        pp_string (pp, start_line_color);
-       pp_string (pp, "|");
+       pp_string (pp, dc->drawing.vertical);
        pp_string (pp, end_line_color);
        pp_newline (pp);
 
@@ -368,7 +370,7 @@ path_summary::print (diagnostic_context *dc, bool 
show_depths) const
          pretty_printer tmp_pp;
          write_indent (&tmp_pp, cur_indent + per_frame_indent);
          pp_string (&tmp_pp, start_line_color);
-         pp_string (&tmp_pp, "|");
+         pp_string (&tmp_pp, dc->drawing.vertical);
          pp_string (&tmp_pp, end_line_color);
          prefix = xstrdup (pp_formatted_text (&tmp_pp));
        }
@@ -379,7 +381,7 @@ path_summary::print (diagnostic_context *dc, bool 
show_depths) const
 
        write_indent (pp, cur_indent + per_frame_indent);
        pp_string (pp, start_line_color);
-       pp_string (pp, "|");
+       pp_string (pp, dc->drawing.vertical);
        pp_string (pp, end_line_color);
        pp_newline (pp);
       }
@@ -405,18 +407,23 @@ path_summary::print (diagnostic_context *dc, bool 
show_depths) const
                    = vbar_for_next_frame - per_frame_indent;
                  write_indent (pp, vbar_for_next_frame);
                  pp_string (pp, start_line_color);
+                 int col = indent_for_next_frame + per_frame_indent;
+                 if (dc->drawing.enabled)
+                   {
+                     pp_string (pp, dc->drawing.corner_nw);
+                     ++col;
+                   }
                  pp_character (pp, '<');
-                 for (int i = indent_for_next_frame + per_frame_indent;
-                      i < cur_indent + per_frame_indent - 1; i++)
-                   pp_character (pp, '-');
-                 pp_character (pp, '+');
+                 for (; col < cur_indent + per_frame_indent - 1; col++)
+                   pp_string (pp, dc->drawing.horizontal1);
+                 pp_string (pp, dc->drawing.corner_se);
                  pp_string (pp, end_line_color);
                  pp_newline (pp);
                  cur_indent = indent_for_next_frame;
 
                  write_indent (pp, vbar_for_next_frame);
                  pp_string (pp, start_line_color);
-                 pp_printf (pp, "|");
+                 pp_printf (pp, dc->drawing.vertical);
                  pp_string (pp, end_line_color);
                  pp_newline (pp);
                }
diff --git a/libstdc++-v3/testsuite/lib/libstdc++.exp 
b/libstdc++-v3/testsuite/lib/libstdc++.exp
index 16963f2edd5..c672e5bc06b 100644
--- a/libstdc++-v3/testsuite/lib/libstdc++.exp
+++ b/libstdc++-v3/testsuite/lib/libstdc++.exp
@@ -482,7 +482,7 @@ proc v3_target_compile { source dest type options } {
     global STATIC_LIBCXXFLAGS
     global tool
 
-    lappend options "additional_flags=-fno-diagnostics-show-caret 
-fdiagnostics-color=never -fdiagnostics-urls=never"
+    lappend options "additional_flags=-fno-diagnostics-show-caret 
-fdiagnostics-color=never -fdiagnostics-urls=never 
-fdiagnostics-unicode-drawing=never"
 
     if { [target_info needs_status_wrapper] != "" && [info exists gluefile] } {
        lappend options "libs=${gluefile}"

Reply via email to