+2016-06-22 David Malcolm <dmalcolm@redhat.com>
+
+ * common.opt (fdiagnostics-parseable-fixits): New option.
+ * diagnostic.c: Include "selftest.h".
+ (print_escaped_string): New function.
+ (print_parseable_fixits): New function.
+ (diagnostic_report_diagnostic): Call print_parseable_fixits.
+ (selftest::assert_print_escaped_string): New function.
+ (ASSERT_PRINT_ESCAPED_STRING_STREQ): New macro.
+ (selftest::test_print_escaped_string): New function.
+ (selftest::test_print_parseable_fixits_none): New function.
+ (selftest::test_print_parseable_fixits_insert): New function.
+ (selftest::test_print_parseable_fixits_remove): New function.
+ (selftest::test_print_parseable_fixits_replace): New function.
+ (selftest::diagnostic_c_tests): New function.
+ * diagnostic.h (struct diagnostic_context): Add field
+ "parseable_fixits_p".
+ * doc/invoke.texi (Diagnostic Message Formatting Options): Add
+ -fdiagnostics-parseable-fixits.
+ (-fdiagnostics-parseable-fixits): New option.
+ * opts.c (common_handle_option): Handle
+ -fdiagnostics-parseable-fixits.
+ * selftest-run-tests.c (selftest::run_tests): Call
+ selftest::diagnostic_c_tests.
+ * selftest.h (selftest::diagnostic_c_tests): New prototype.
+
2016-06-22 Ilya Enkovich <ilya.enkovich@intel.com>
PR middle-end/71488
EnumValue
Enum(diagnostic_color_rule) String(auto) Value(DIAGNOSTICS_COLOR_AUTO)
+fdiagnostics-parseable-fixits
+Common Var(flag_diagnostics_parseable_fixits)
+Print fixit hints in machine-readable form.
+
fdiagnostics-show-option
Common Var(flag_diagnostics_show_option) Init(1)
Amend appropriate diagnostic messages with the command line option that controls them.
#include "backtrace.h"
#include "diagnostic.h"
#include "diagnostic-color.h"
+#include "selftest.h"
#ifdef HAVE_TERMIOS_H
# include <termios.h>
context->n_classification_history ++;
}
+/* Helper function for print_parseable_fixits. Print TEXT to PP, obeying the
+ escaping rules for -fdiagnostics-parseable-fixits. */
+
+static void
+print_escaped_string (pretty_printer *pp, const char *text)
+{
+ gcc_assert (pp);
+ gcc_assert (text);
+
+ pp_character (pp, '"');
+ for (const char *ch = text; *ch; ch++)
+ {
+ switch (*ch)
+ {
+ case '\\':
+ /* Escape backslash as two backslashes. */
+ pp_string (pp, "\\\\");
+ break;
+ case '\t':
+ /* Escape tab as "\t". */
+ pp_string (pp, "\\t");
+ break;
+ case '\n':
+ /* Escape newline as "\n". */
+ pp_string (pp, "\\n");
+ break;
+ case '"':
+ /* Escape doublequotes as \". */
+ pp_string (pp, "\\\"");
+ break;
+ default:
+ if (ISPRINT (*ch))
+ pp_character (pp, *ch);
+ else
+ /* Use octal for non-printable chars. */
+ {
+ unsigned char c = (*ch & 0xff);
+ pp_printf (pp, "\\%o%o%o", (c / 64), (c / 8) & 007, c & 007);
+ }
+ break;
+ }
+ }
+ pp_character (pp, '"');
+}
+
+/* Implementation of -fdiagnostics-parseable-fixits. Print a
+ machine-parseable version of all fixits in RICHLOC to PP. */
+
+static void
+print_parseable_fixits (pretty_printer *pp, rich_location *richloc)
+{
+ gcc_assert (pp);
+ gcc_assert (richloc);
+
+ for (unsigned i = 0; i < richloc->get_num_fixit_hints (); i++)
+ {
+ const fixit_hint *hint = richloc->get_fixit_hint (i);
+ source_location start_loc = hint->get_start_loc ();
+ expanded_location start_exploc = expand_location (start_loc);
+ pp_string (pp, "fix-it:");
+ print_escaped_string (pp, start_exploc.file);
+ source_location end_loc;
+
+ /* For compatibility with clang, print as a half-open range. */
+ if (hint->maybe_get_end_loc (&end_loc))
+ {
+ expanded_location end_exploc = expand_location (end_loc);
+ pp_printf (pp, ":{%i:%i-%i:%i}:",
+ start_exploc.line, start_exploc.column,
+ end_exploc.line, end_exploc.column + 1);
+ }
+ else
+ {
+ pp_printf (pp, ":{%i:%i-%i:%i}:",
+ start_exploc.line, start_exploc.column,
+ start_exploc.line, start_exploc.column);
+ }
+ switch (hint->get_kind ())
+ {
+ case fixit_hint::INSERT:
+ {
+ const fixit_insert *insert
+ = static_cast <const fixit_insert *> (hint);
+ print_escaped_string (pp, insert->get_string ());
+ }
+ break;
+
+ case fixit_hint::REMOVE:
+ print_escaped_string (pp, "");
+ break;
+
+ case fixit_hint::REPLACE:
+ {
+ const fixit_replace *replace
+ = static_cast <const fixit_replace *> (hint);
+ print_escaped_string (pp, replace->get_string ());
+ }
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ pp_newline (pp);
+ }
+}
+
/* Report a diagnostic message (an error or a warning) as specified by
DC. This function is *the* subroutine in terms of which front-ends
should implement their specific diagnostic handling modules. The
(*diagnostic_starter (context)) (context, diagnostic);
pp_output_formatted_text (context->printer);
(*diagnostic_finalizer (context)) (context, diagnostic);
+ if (context->parseable_fixits_p)
+ {
+ print_parseable_fixits (context->printer, diagnostic->richloc);
+ pp_flush (context->printer);
+ }
diagnostic_action_after_output (context, diagnostic->kind);
diagnostic->message.format_spec = saved_format_spec;
diagnostic->x_data = NULL;
{
abort ();
}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Helper function for test_print_escaped_string. */
+
+static void
+assert_print_escaped_string (const location &loc, const char *expected_output,
+ const char *input)
+{
+ pretty_printer pp;
+ print_escaped_string (&pp, input);
+ ASSERT_STREQ_AT (loc, expected_output, pp_formatted_text (&pp));
+}
+
+#define ASSERT_PRINT_ESCAPED_STRING_STREQ(EXPECTED_OUTPUT, INPUT) \
+ assert_print_escaped_string (SELFTEST_LOCATION, EXPECTED_OUTPUT, INPUT)
+
+/* Tests of print_escaped_string. */
+
+static void
+test_print_escaped_string ()
+{
+ /* Empty string. */
+ ASSERT_PRINT_ESCAPED_STRING_STREQ ("\"\"", "");
+
+ /* Non-empty string. */
+ ASSERT_PRINT_ESCAPED_STRING_STREQ ("\"hello world\"", "hello world");
+
+ /* Various things that need to be escaped: */
+ /* Backslash. */
+ ASSERT_PRINT_ESCAPED_STRING_STREQ ("\"before\\\\after\"",
+ "before\\after");
+ /* Tab. */
+ ASSERT_PRINT_ESCAPED_STRING_STREQ ("\"before\\tafter\"",
+ "before\tafter");
+ /* Newline. */
+ ASSERT_PRINT_ESCAPED_STRING_STREQ ("\"before\\nafter\"",
+ "before\nafter");
+ /* Double quote. */
+ ASSERT_PRINT_ESCAPED_STRING_STREQ ("\"before\\\"after\"",
+ "before\"after");
+
+ /* Non-printable characters: BEL: '\a': 0x07 */
+ ASSERT_PRINT_ESCAPED_STRING_STREQ ("\"before\\007after\"",
+ "before\aafter");
+ /* Non-printable characters: vertical tab: '\v': 0x0b */
+ ASSERT_PRINT_ESCAPED_STRING_STREQ ("\"before\\013after\"",
+ "before\vafter");
+}
+
+/* Tests of print_parseable_fixits. */
+
+/* Verify that print_parseable_fixits emits the empty string if there
+ are no fixits. */
+
+static void
+test_print_parseable_fixits_none ()
+{
+ pretty_printer pp;
+ rich_location richloc (line_table, UNKNOWN_LOCATION);
+
+ print_parseable_fixits (&pp, &richloc);
+ ASSERT_STREQ ("", pp_formatted_text (&pp));
+}
+
+/* Verify that print_parseable_fixits does the right thing if there
+ is an insertion fixit hint. */
+
+static void
+test_print_parseable_fixits_insert ()
+{
+ pretty_printer pp;
+ rich_location richloc (line_table, UNKNOWN_LOCATION);
+
+ linemap_add (line_table, LC_ENTER, false, "test.c", 0);
+ linemap_line_start (line_table, 5, 100);
+ linemap_add (line_table, LC_LEAVE, false, NULL, 0);
+ location_t where = linemap_position_for_column (line_table, 10);
+ richloc.add_fixit_insert (where, "added content");
+
+ print_parseable_fixits (&pp, &richloc);
+ ASSERT_STREQ ("fix-it:\"test.c\":{5:10-5:10}:\"added content\"\n",
+ pp_formatted_text (&pp));
+}
+
+/* Verify that print_parseable_fixits does the right thing if there
+ is an removal fixit hint. */
+
+static void
+test_print_parseable_fixits_remove ()
+{
+ pretty_printer pp;
+ rich_location richloc (line_table, UNKNOWN_LOCATION);
+
+ linemap_add (line_table, LC_ENTER, false, "test.c", 0);
+ linemap_line_start (line_table, 5, 100);
+ linemap_add (line_table, LC_LEAVE, false, NULL, 0);
+ source_range where;
+ where.m_start = linemap_position_for_column (line_table, 10);
+ where.m_finish = linemap_position_for_column (line_table, 20);
+ richloc.add_fixit_remove (where);
+
+ print_parseable_fixits (&pp, &richloc);
+ ASSERT_STREQ ("fix-it:\"test.c\":{5:10-5:21}:\"\"\n",
+ pp_formatted_text (&pp));
+}
+
+/* Verify that print_parseable_fixits does the right thing if there
+ is an replacement fixit hint. */
+
+static void
+test_print_parseable_fixits_replace ()
+{
+ pretty_printer pp;
+ rich_location richloc (line_table, UNKNOWN_LOCATION);
+
+ linemap_add (line_table, LC_ENTER, false, "test.c", 0);
+ linemap_line_start (line_table, 5, 100);
+ linemap_add (line_table, LC_LEAVE, false, NULL, 0);
+ source_range where;
+ where.m_start = linemap_position_for_column (line_table, 10);
+ where.m_finish = linemap_position_for_column (line_table, 20);
+ richloc.add_fixit_replace (where, "replacement");
+
+ print_parseable_fixits (&pp, &richloc);
+ ASSERT_STREQ ("fix-it:\"test.c\":{5:10-5:21}:\"replacement\"\n",
+ pp_formatted_text (&pp));
+}
+
+/* Run all of the selftests within this file. */
+
+void
+diagnostic_c_tests ()
+{
+ test_print_escaped_string ();
+ test_print_parseable_fixits_none ();
+ test_print_parseable_fixits_insert ();
+ test_print_parseable_fixits_remove ();
+ test_print_parseable_fixits_replace ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
/* Usable by plugins; if true, print a debugging ruler above the
source output. */
bool show_ruler_p;
+
+ /* If true, print fixits in machine-parseable form after the
+ rest of the diagnostic. */
+ bool parseable_fixits_p;
};
static inline void
@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
--fno-diagnostics-show-option -fno-diagnostics-show-caret}
+-fno-diagnostics-show-option -fno-diagnostics-show-caret @gol
+-fdiagnostics-parseable-fixits}
@item Warning Options
@xref{Warning Options,,Options to Request or Suppress Warnings}.
to the terminal, the width is limited to the width given by the
@env{COLUMNS} environment variable or, if not set, to the terminal width.
+@item -fdiagnostics-parseable-fixits
+@opindex fdiagnostics-parseable-fixits
+Emit fix-it hints in a machine-parseable format, suitable for consumption
+by IDEs. For each fix-it, a line will be printed after the relevant
+diagnostic, starting with the string ``fix-it:''. For example:
+
+@smallexample
+fix-it:"test.c":@{45:3-45:21@}:"gtk_widget_show_all"
+@end smallexample
+
+The location is expressed as a half-open range, expressed as a count of
+bytes, starting at byte 1 for the initial column. In the above example,
+bytes 3 through 20 of line 45 of ``test.c'' are to be replaced with the
+given string:
+
+@smallexample
+00000000011111111112222222222
+12345678901234567890123456789
+ gtk_widget_showall (dlg);
+ ^^^^^^^^^^^^^^^^^^
+ gtk_widget_show_all
+@end smallexample
+
+The filename and replacement string escape backslash as ``\\", tab as ``\t'',
+newline as ``\n'', double quotes as ``\"'', non-printable characters as octal
+(e.g. vertical tab as ``\013'').
+
+An empty replacement string indicates that the given range is to be removed.
+An empty range (e.g. ``45:3-45:3'') indicates that the string is to
+be inserted at the given position.
+
@end table
@node Warning Options
diagnostic_color_init (dc, value);
break;
+ case OPT_fdiagnostics_parseable_fixits:
+ dc->parseable_fixits_p = value;
+ break;
+
case OPT_fdiagnostics_show_option:
dc->show_option_requested = value;
break;
/* Higher-level tests, or for components that other selftests don't
rely on. */
diagnostic_show_locus_c_tests ();
+ diagnostic_c_tests ();
fold_const_c_tests ();
spellcheck_c_tests ();
spellcheck_tree_c_tests ();
/* Declarations for specific families of tests (by source file), in
alphabetical order. */
extern void bitmap_c_tests ();
+extern void diagnostic_c_tests ();
extern void diagnostic_show_locus_c_tests ();
extern void et_forest_c_tests ();
extern void fold_const_c_tests ();
+2016-06-22 David Malcolm <dmalcolm@redhat.com>
+
+ * gcc.dg/plugin/diagnostic-test-show-locus-parseable-fixits.c: New
+ file.
+ * gcc.dg/plugin/plugin.exp (plugin_test_list): Add
+ diagnostic-test-show-locus-parseable-fixits.c to sources for
+ diagnostic_plugin_test_show_locus.c.
+ * lib/gcc-defs.exp (freeform_regexps): New global.
+ (dg-regexp): New function.
+ (handle-dg-regexps): New function.
+ * lib/gcc-dg.exp (cleanup-after-saved-dg-test): Reset
+ freeform_regexps to the empty list.
+ * lib/prune.exp (prune_gcc_output): Call handle-dg-regexps.
+
2016-06-22 Ilya Enkovich <ilya.enkovich@intel.com>
PR middle-end/71488
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-parseable-fixits" } */
+
+/* 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
+}
{ 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 } \
+ diagnostic-test-show-locus-color.c \
+ diagnostic-test-show-locus-parseable-fixits.c } \
{ diagnostic_plugin_test_tree_expression_range.c \
diagnostic-test-expressions-1.c } \
{ diagnostic_plugin_show_trees.c \
return $libpath
}
+
+# A list of all uses of dg-regexp, each entry of the form:
+# line-number regexp
+# This is cleared at the end of each test by gcc-dg.exp's wrapper for dg-test.
+set freeform_regexps []
+
+# Directive for looking for a regexp, without any line numbers or other
+# prefixes.
+
+proc dg-regexp { args } {
+ verbose "dg-regexp: args: $args" 2
+
+ global freeform_regexps
+ lappend freeform_regexps $args
+}
+
+# Hook to be called by prune.exp's prune_gcc_output to
+# look for the expected dg-regexp expressions, pruning them,
+# reporting PASS for those that are found, and FAIL for
+# those that weren't found.
+#
+# It returns a pruned version of its output.
+
+proc handle-dg-regexps { text } {
+ global freeform_regexps
+ global testname_with_flags
+
+ foreach entry $freeform_regexps {
+ verbose " entry: $entry" 3
+
+ set linenum [lindex $entry 0]
+ set rexp [lindex $entry 1]
+
+ # Escape newlines in $rexp so that we can print them in
+ # pass/fail results.
+ set escaped_regex [string map {"\n" "\\n"} $rexp]
+ verbose "escaped_regex: ${escaped_regex}" 4
+
+ set title "$testname_with_flags dg-regexp $linenum"
+
+ # Use "regsub" to attempt to prune the pattern from $text
+ if {[regsub -line $rexp $text "" text]} {
+ # Success; the multiline pattern was pruned.
+ pass "$title was found: \"$escaped_regex\""
+ } else {
+ fail "$title not found: \"$escaped_regex\""
+ }
+ }
+
+ return $text
+}
global saved_compiler_env_var
global keep_saved_temps_suffixes
global multiline_expected_outputs
+ global freeform_regexps
set additional_files ""
set additional_sources ""
unset testname_with_flags
}
set multiline_expected_outputs []
+ set freeform_regexps []
}
proc dg-test { args } {
# Call into multiline.exp to handle any multiline output directives.
set text [handle-multiline-outputs $text]
+ # Handle any freeform regexps.
+ set text [handle-dg-regexps $text]
+
#send_user "After:$text\n"
return $text
+2016-06-22 David Malcolm <dmalcolm@redhat.com>
+
+ * include/line-map.h (fixit_hint::get_start_loc): New pure virtual
+ function.
+ (fixit_hint::maybe_get_end_loc): Likewise.
+ (fixit_insert::get_start_loc): New function, implementing
+ fixit_hint::get_start_loc.
+ (fixit_insert::maybe_get_end_loc): New function, implementing
+ fixit_hint::maybe_get_end_loc.
+ (fixit_remove::get_start_loc): New function, implementing
+ fixit_hint::get_start_loc.
+ (fixit_remove::maybe_get_end_loc): New function, implementing
+ fixit_hint::maybe_get_end_loc.
+ (fixit_replace::get_start_loc): New function, implementing
+ fixit_hint::get_start_loc.
+ (fixit_replace::maybe_get_end_loc): New function, implementing
+ fixit_hint::maybe_get_end_loc.
+
2016-06-21 John David Anglin <danglin@gcc.gnu.org>
* line-map.c (location_adhoc_data_update): Use int64_t instead of
virtual enum kind get_kind () const = 0;
virtual bool affects_line_p (const char *file, int line) = 0;
+ virtual source_location get_start_loc () const = 0;
+ virtual bool maybe_get_end_loc (source_location *out) const = 0;
};
class fixit_insert : public fixit_hint
~fixit_insert ();
enum kind get_kind () const { return INSERT; }
bool affects_line_p (const char *file, int line);
+ source_location get_start_loc () const { return m_where; }
+ bool maybe_get_end_loc (source_location *) const { return false; }
source_location get_location () const { return m_where; }
const char *get_string () const { return m_bytes; }
enum kind get_kind () const { return REMOVE; }
bool affects_line_p (const char *file, int line);
+ source_location get_start_loc () const { return m_src_range.m_start; }
+ bool maybe_get_end_loc (source_location *out) const
+ {
+ *out = m_src_range.m_finish;
+ return true;
+ }
source_range get_range () const { return m_src_range; }
enum kind get_kind () const { return REPLACE; }
bool affects_line_p (const char *file, int line);
+ source_location get_start_loc () const { return m_src_range.m_start; }
+ bool maybe_get_end_loc (source_location *out) const
+ {
+ *out = m_src_range.m_finish;
+ return true;
+ }
source_range get_range () const { return m_src_range; }
const char *get_string () const { return m_bytes; }