diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c index 4250bf96c8b..7d65ac7379f 100644 --- a/gcc/diagnostic.c +++ b/gcc/diagnostic.c @@ -219,7 +219,14 @@ diagnostic_initialize (diagnostic_context *context, int n_opts) context->show_line_numbers_p = false; context->min_margin_width = 0; context->show_ruler_p = false; - context->parseable_fixits_p = false; + if (const char *var = getenv ("GCC_EXTRA_DIAGNOSTIC_OUTPUT")) + { + if (!strcmp (var, "fixits-v1")) + context->extra_output_kind = EXTRA_DIAGNOSTIC_OUTPUT_fixits_v1; + else if (!strcmp (var, "fixits-v2")) + context->extra_output_kind = EXTRA_DIAGNOSTIC_OUTPUT_fixits_v2; + /* Silently ignore unrecognized values. */ + } context->column_unit = DIAGNOSTICS_COLUMN_UNIT_DISPLAY; context->column_origin = 1; context->tabstop = 8; @@ -357,30 +364,41 @@ diagnostic_get_color_for_kind (diagnostic_t kind) return diagnostic_kind_color[kind]; } +/* Given an expanded_location, convert the column (which is in 1-based bytes) + to the requested units, without converting the origin. + Return -1 if the column is invalid (<= 0). */ + +static int +convert_column_unit (enum diagnostics_column_unit column_unit, + int tabstop, + expanded_location s) +{ + if (s.column <= 0) + return -1; + + switch (column_unit) + { + default: + gcc_unreachable (); + + case DIAGNOSTICS_COLUMN_UNIT_DISPLAY: + return location_compute_display_column (s, tabstop); + + case DIAGNOSTICS_COLUMN_UNIT_BYTE: + return s.column; + } +} + /* Given an expanded_location, convert the column (which is in 1-based bytes) to the requested units and origin. Return -1 if the column is invalid (<= 0). */ int diagnostic_converted_column (diagnostic_context *context, expanded_location s) { - if (s.column <= 0) + int one_based_col + = convert_column_unit (context->column_unit, context->tabstop, s); + if (one_based_col <= 0) return -1; - - int one_based_col; - switch (context->column_unit) - { - case DIAGNOSTICS_COLUMN_UNIT_DISPLAY: - one_based_col = location_compute_display_column (s, context->tabstop); - break; - - case DIAGNOSTICS_COLUMN_UNIT_BYTE: - one_based_col = s.column; - break; - - default: - gcc_unreachable (); - } - return one_based_col + (context->column_origin - 1); } @@ -931,11 +949,16 @@ print_escaped_string (pretty_printer *pp, const char *text) pp_character (pp, '"'); } -/* Implementation of -fdiagnostics-parseable-fixits. Print a - machine-parseable version of all fixits in RICHLOC to PP. */ +/* Implementation of -fdiagnostics-parseable-fixits and + GCC_EXTRA_DIAGNOSTIC_OUTPUT. + Print a machine-parseable version of all fixits in RICHLOC to PP, + using COLUMN_UNIT to express columns. + Use TABSTOP when handling DIAGNOSTICS_COLUMN_UNIT_DISPLAY. */ static void -print_parseable_fixits (pretty_printer *pp, rich_location *richloc) +print_parseable_fixits (pretty_printer *pp, rich_location *richloc, + enum diagnostics_column_unit column_unit, + int tabstop) { gcc_assert (pp); gcc_assert (richloc); @@ -953,9 +976,13 @@ print_parseable_fixits (pretty_printer *pp, rich_location *richloc) /* For compatibility with clang, print as a half-open range. */ location_t next_loc = hint->get_next_loc (); expanded_location next_exploc = expand_location (next_loc); + int start_col + = convert_column_unit (column_unit, tabstop, start_exploc); + int next_col + = convert_column_unit (column_unit, tabstop, next_exploc); pp_printf (pp, ":{%i:%i-%i:%i}:", - start_exploc.line, start_exploc.column, - next_exploc.line, next_exploc.column); + start_exploc.line, start_col, + next_exploc.line, next_col); print_escaped_string (pp, hint->get_string ()); pp_newline (pp); } @@ -1221,10 +1248,22 @@ diagnostic_report_diagnostic (diagnostic_context *context, if (context->show_option_requested) print_option_information (context, diagnostic, orig_diag_kind); (*diagnostic_finalizer (context)) (context, diagnostic, orig_diag_kind); - if (context->parseable_fixits_p) + switch (context->extra_output_kind) { - print_parseable_fixits (context->printer, diagnostic->richloc); + default: + break; + case EXTRA_DIAGNOSTIC_OUTPUT_fixits_v1: + print_parseable_fixits (context->printer, diagnostic->richloc, + DIAGNOSTICS_COLUMN_UNIT_BYTE, + context->tabstop); pp_flush (context->printer); + break; + case EXTRA_DIAGNOSTIC_OUTPUT_fixits_v2: + print_parseable_fixits (context->printer, diagnostic->richloc, + DIAGNOSTICS_COLUMN_UNIT_DISPLAY, + context->tabstop); + pp_flush (context->printer); + break; } diagnostic_action_after_output (context, diagnostic->kind); diagnostic->x_data = NULL; @@ -2023,7 +2062,7 @@ test_print_parseable_fixits_none () pretty_printer pp; rich_location richloc (line_table, UNKNOWN_LOCATION); - print_parseable_fixits (&pp, &richloc); + print_parseable_fixits (&pp, &richloc, DIAGNOSTICS_COLUMN_UNIT_BYTE, 8); ASSERT_STREQ ("", pp_formatted_text (&pp)); } @@ -2042,7 +2081,7 @@ test_print_parseable_fixits_insert () location_t where = linemap_position_for_column (line_table, 10); richloc.add_fixit_insert_before (where, "added content"); - print_parseable_fixits (&pp, &richloc); + print_parseable_fixits (&pp, &richloc, DIAGNOSTICS_COLUMN_UNIT_BYTE, 8); ASSERT_STREQ ("fix-it:\"test.c\":{5:10-5:10}:\"added content\"\n", pp_formatted_text (&pp)); } @@ -2064,7 +2103,7 @@ test_print_parseable_fixits_remove () where.m_finish = linemap_position_for_column (line_table, 20); richloc.add_fixit_remove (where); - print_parseable_fixits (&pp, &richloc); + print_parseable_fixits (&pp, &richloc, DIAGNOSTICS_COLUMN_UNIT_BYTE, 8); ASSERT_STREQ ("fix-it:\"test.c\":{5:10-5:21}:\"\"\n", pp_formatted_text (&pp)); } @@ -2086,11 +2125,59 @@ test_print_parseable_fixits_replace () where.m_finish = linemap_position_for_column (line_table, 20); richloc.add_fixit_replace (where, "replacement"); - print_parseable_fixits (&pp, &richloc); + print_parseable_fixits (&pp, &richloc, DIAGNOSTICS_COLUMN_UNIT_BYTE, 8); ASSERT_STREQ ("fix-it:\"test.c\":{5:10-5:21}:\"replacement\"\n", pp_formatted_text (&pp)); } +/* Verify that print_parseable_fixits correctly handles + DIAGNOSTICS_COLUMN_UNIT_BYTE vs DIAGNOSTICS_COLUMN_UNIT_COLUMN. */ + +static void +test_print_parseable_fixits_bytes_vs_display_columns () +{ + line_table_test ltt; + rich_location richloc (line_table, UNKNOWN_LOCATION); + + /* 1-based byte offsets: 12345677778888999900001234567. */ + const char *const content = "smile \xf0\x9f\x98\x82 colour\n"; + /* 1-based display cols: 123456[......7-8.....]9012345. */ + const int tabstop = 8; + + temp_source_file tmp (SELFTEST_LOCATION, ".c", content); + const char *const fname = tmp.get_filename (); + + linemap_add (line_table, LC_ENTER, false, fname, 0); + linemap_line_start (line_table, 1, 100); + linemap_add (line_table, LC_LEAVE, false, NULL, 0); + source_range where; + where.m_start = linemap_position_for_column (line_table, 12); + where.m_finish = linemap_position_for_column (line_table, 17); + richloc.add_fixit_replace (where, "color"); + + const int buf_len = strlen (fname) + 100; + char *const expected = XNEWVEC (char, buf_len); + + { + pretty_printer pp; + print_parseable_fixits (&pp, &richloc, DIAGNOSTICS_COLUMN_UNIT_BYTE, + tabstop); + snprintf (expected, buf_len, + "fix-it:\"%s\":{1:12-1:18}:\"color\"\n", fname); + ASSERT_STREQ (expected, pp_formatted_text (&pp)); + } + { + pretty_printer pp; + print_parseable_fixits (&pp, &richloc, DIAGNOSTICS_COLUMN_UNIT_DISPLAY, + tabstop); + snprintf (expected, buf_len, + "fix-it:\"%s\":{1:10-1:16}:\"color\"\n", fname); + ASSERT_STREQ (expected, pp_formatted_text (&pp)); + } + + XDELETEVEC (expected); +} + /* Verify that diagnostic_get_location_text (..., SHOW_COLUMN) generates EXPECTED_LOC_TEXT, given FILENAME, LINE, COLUMN, with @@ -2213,6 +2300,7 @@ diagnostic_c_tests () test_print_parseable_fixits_insert (); test_print_parseable_fixits_remove (); test_print_parseable_fixits_replace (); + test_print_parseable_fixits_bytes_vs_display_columns (); test_diagnostic_get_location_text (); test_num_digits (); diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index d13a6ce741f..9a6eefcf918 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -66,6 +66,22 @@ enum diagnostic_path_format DPF_INLINE_EVENTS }; +/* An enum for capturing values of GCC_EXTRA_DIAGNOSTIC_OUTPUT, + and for -fdiagnostics-parseable-fixits. */ + +enum diagnostics_extra_output_kind +{ + /* No extra output, or an unrecognized value. */ + EXTRA_DIAGNOSTIC_OUTPUT_none, + + /* Emit fix-it hints using the "fixits-v1" format, equivalent to + -fdiagnostics-parseable-fixits. */ + EXTRA_DIAGNOSTIC_OUTPUT_fixits_v1, + + /* Emit fix-it hints using the "fixits-v2" format. */ + EXTRA_DIAGNOSTIC_OUTPUT_fixits_v2 +}; + /* A diagnostic is described by the MESSAGE to send, the FILE and LINE of its context and its KIND (ice, error, warning, note, ...) See complete list in diagnostic.def. */ @@ -290,9 +306,10 @@ struct diagnostic_context source output. */ bool show_ruler_p; - /* If true, print fixits in machine-parseable form after the - rest of the diagnostic. */ - bool parseable_fixits_p; + /* Used to specify additional diagnostic output to be emitted after the + rest of the diagnostic. This is for implementing + -fdiagnostics-parseable-fixits and GCC_EXTRA_DIAGNOSTIC_OUTPUT. */ + enum diagnostics_extra_output_kind extra_output_kind; /* What units to use when outputting the column number. */ enum diagnostics_column_unit column_unit; diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 298f1f873e3..8f100911048 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -32711,6 +32711,25 @@ Recognize EUCJP characters. If @env{LANG} is not defined, or if it has some other value, then the compiler uses @code{mblen} and @code{mbtowc} as defined by the default locale to recognize and translate multibyte characters. + +@item GCC_EXTRA_DIAGNOSTIC_OUTPUT +If @env{GCC_EXTRA_DIAGNOSTIC_OUTPUT} is set to one of the following values, +then additional text will be emitted to stderr when fix-it hints are +emitted. @option{-fdiagnostics-parseable-fixits} and +@option{-fno-diagnostics-parseable-fixits} take precedence over this +environment variable. + +@table @samp +@item fixits-v1 +Emit parseable fix-it hints, equivalent to +@option{-fdiagnostics-parseable-fixits}. In particular, columns are +expressed as a count of bytes, starting at byte 1 for the initial column. + +@item fixits-v2 +As @code{fixits-v1}, but columns are expressed as display columns, +as per @option{-fdiagnostics-column-unit=display}. +@end table + @end table @noindent diff --git a/gcc/opts.c b/gcc/opts.c index 527f0dde706..389797d29bd 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -2522,7 +2522,9 @@ common_handle_option (struct gcc_options *opts, break; case OPT_fdiagnostics_parseable_fixits: - dc->parseable_fixits_p = value; + dc->extra_output_kind = (value + ? EXTRA_DIAGNOSTIC_OUTPUT_fixits_v1 + : EXTRA_DIAGNOSTIC_OUTPUT_none); break; case OPT_fdiagnostics_column_unit_: diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-GCC_EXTRA_DIAGNOSTIC_OUTPUT-fixits-v1.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-GCC_EXTRA_DIAGNOSTIC_OUTPUT-fixits-v1.c new file mode 100644 index 00000000000..8f6f7162f76 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-GCC_EXTRA_DIAGNOSTIC_OUTPUT-fixits-v1.c @@ -0,0 +1,71 @@ +/* { dg-do compile } */ +/* { dg-options "-O" } */ +/* { dg-set-compiler-env-var GCC_EXTRA_DIAGNOSTIC_OUTPUT "fixits-v1" } +/* 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. */ + +/* Unit test for rendering of insertion fixit hints + (example taken from PR 62316). */ + +void test_fixit_insert (void) +{ +#if 0 + int a[2][2] = { 0, 1 , 2, 3 }; /* { dg-warning "insertion hints" } */ +/* { dg-regexp "fix-it:.*\\{17:20-17:20\\}:.*" } */ +/* { dg-regexp "fix-it:.*\\{17:24-17:24\\}:.*" } */ +#endif +} + +/* Unit test for rendering of "remove" fixit hints. */ + +void test_fixit_remove (void) +{ +#if 0 + int a;; /* { dg-warning "example of a removal hint" } */ +/* { dg-regexp "fix-it:.*\\{28:9-28:10\\}:.*" } */ +#endif +} + +/* Unit test for rendering of "replace" fixit hints. */ + +void test_fixit_replace (void) +{ +#if 0 + gtk_widget_showall (dlg); /* { dg-warning "example of a replacement hint" } */ +/* { dg-regexp "fix-it:.*\\{38:3-38:21\\}:.*" } */ +#endif +} + +/* Unit test for rendering of fix-it hints that add new lines. */ + +void test_fixit_insert_newline (void) +{ +#if 0 + switch (op) + { + case 'a': + x = a; + case 'b': /* { dg-warning "newline insertion" } */ + x = b; + } +/* { dg-regexp "fix-it:.*\\{52:1-52:1\\}:.*\\n" } */ +#endif +} + +/* Unit test for mutually-exclusive suggestions. */ + +void test_mutually_exclusive_suggestions (void) +{ +#if 0 + original; /* { dg-warning "warning 1" } */ +/* { dg-warning "warning 2" "" { target *-*-* } .-1 } */ +/* We should print the mutually-incompatible fix-it hints within + -fdiagnostics-parseable-fixits; verify that they are printed. */ +/* { dg-regexp "fix-it:.*\\{64:3-64:11}:.*\\n" } */ +/* { dg-regexp "fix-it:.*\\{64:3-64:11}:.*\\n" } */ +#endif +} diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-GCC_EXTRA_DIAGNOSTIC_OUTPUT-fixits-v2.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-GCC_EXTRA_DIAGNOSTIC_OUTPUT-fixits-v2.c new file mode 100644 index 00000000000..d5ebd93869c --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-GCC_EXTRA_DIAGNOSTIC_OUTPUT-fixits-v2.c @@ -0,0 +1,71 @@ +/* { dg-do compile } */ +/* { dg-options "-O" } */ +/* { dg-set-compiler-env-var GCC_EXTRA_DIAGNOSTIC_OUTPUT "fixits-v2" } +/* 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. */ + +/* Unit test for rendering of insertion fixit hints + (example taken from PR 62316). */ + +void test_fixit_insert (void) +{ +#if 0 + int a[2][2] = { 0, 1 , 2, 3 }; /* { dg-warning "insertion hints" } */ +/* { dg-regexp "fix-it:.*\\{17:20-17:20\\}:.*" } */ +/* { dg-regexp "fix-it:.*\\{17:24-17:24\\}:.*" } */ +#endif +} + +/* Unit test for rendering of "remove" fixit hints. */ + +void test_fixit_remove (void) +{ +#if 0 + int a;; /* { dg-warning "example of a removal hint" } */ +/* { dg-regexp "fix-it:.*\\{28:9-28:10\\}:.*" } */ +#endif +} + +/* Unit test for rendering of "replace" fixit hints. */ + +void test_fixit_replace (void) +{ +#if 0 + gtk_widget_showall (dlg); /* { dg-warning "example of a replacement hint" } */ +/* { dg-regexp "fix-it:.*\\{38:3-38:21\\}:.*" } */ +#endif +} + +/* Unit test for rendering of fix-it hints that add new lines. */ + +void test_fixit_insert_newline (void) +{ +#if 0 + switch (op) + { + case 'a': + x = a; + case 'b': /* { dg-warning "newline insertion" } */ + x = b; + } +/* { dg-regexp "fix-it:.*\\{52:1-52:1\\}:.*\\n" } */ +#endif +} + +/* Unit test for mutually-exclusive suggestions. */ + +void test_mutually_exclusive_suggestions (void) +{ +#if 0 + original; /* { dg-warning "warning 1" } */ +/* { dg-warning "warning 2" "" { target *-*-* } .-1 } */ +/* We should print the mutually-incompatible fix-it hints within + -fdiagnostics-parseable-fixits; verify that they are printed. */ +/* { dg-regexp "fix-it:.*\\{64:3-64:11}:.*\\n" } */ +/* { dg-regexp "fix-it:.*\\{64:3-64:11}:.*\\n" } */ +#endif +} diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp index f686dd07e84..66a437d18d3 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp @@ -79,6 +79,8 @@ set plugin_test_list [list \ diagnostic-test-show-locus-bw-line-numbers-2.c \ diagnostic-test-show-locus-color-line-numbers.c \ diagnostic-test-show-locus-parseable-fixits.c \ + diagnostic-test-show-locus-GCC_EXTRA_DIAGNOSTIC_OUTPUT-fixits-v1.c \ + diagnostic-test-show-locus-GCC_EXTRA_DIAGNOSTIC_OUTPUT-fixits-v2.c \ diagnostic-test-show-locus-generate-patch.c }\ { diagnostic_plugin_test_tree_expression_range.c \ diagnostic-test-expressions-1.c } \