From patchwork Wed Jun 22 03:25:42 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 638948 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3rZ8T01LFNz9s9r for ; Wed, 22 Jun 2016 12:59:31 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b=sq8fDMD9; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id; q=dns; s=default; b=Qi5FvtQHLN9r SdmGmTiCwm0MHRvE+nZAwZEzBtIZe5tPaS8dSQbIa6pC6V378m/sLMh9dqwSSbIt kiVfrSqc00OC1hHSWrMrXSCS/m2Ds25Nq5O6eQA0h+xhiZlyEz0XRW0jBh47HHfI qT7rTeLSlwwgHeqz00avZrqKCJO5zk0= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id; s=default; bh=2dB+lCJhk0FdwzGw5g wSbfZr868=; b=sq8fDMD9V2O7EVmKtJl6s3QHtMS7Nt22gYLSUoqbQ50d07lvey vuSASuEnY9dLCue00S8wxoejN7Jh8cneV83rBp8VRcCuqPnAX/Ea3j13chLEhQQN dPSe3MQJqpTnxBH9Uk8GRvlPIg9Snkk5+3BHNM9kw7eqlSEognbWj074w= Received: (qmail 9923 invoked by alias); 22 Jun 2016 02:59:22 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 9808 invoked by uid 89); 22 Jun 2016 02:59:10 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.1 required=5.0 tests=BAYES_00, KAM_LOTSOFHASH, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=supplying, *pp, 73, 6, smallexample X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Wed, 22 Jun 2016 02:58:59 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 7DE6D63E14 for ; Wed, 22 Jun 2016 02:58:58 +0000 (UTC) Received: from c64.redhat.com (vpn-239-46.phx2.redhat.com [10.3.239.46]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u5M2wvLd003860; Tue, 21 Jun 2016 22:58:57 -0400 From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [PATCH] Implement -fdiagnostics-parseable-fixits Date: Tue, 21 Jun 2016 23:25:42 -0400 Message-Id: <1466565942-42093-1-git-send-email-dmalcolm@redhat.com> X-IsSubscribed: yes Both GCC and Clang can emit "fix-it" hints for a diagnostic, giving a suggestion about how to fix the issue. Clang has an option -fdiagnostics-parseable-fixits, which emits a machine-readable version of the fixits, for use by IDEs. (The only IDE I know of that supports this format is Xcode [1]; does anyone know of other IDEs supporting this? I'd be very happy if someone implemented Emacs support for it, or could point me at it). This patch implements the option for gcc. It seems prudent to be compatible with Clang here, so I've deliberately used the same option name and attempted to emulate the output format, based on the documentation here: http://llvm.org/releases/3.8.0/tools/clang/docs/UsersManual.html#cmdoption-fdiagnostics-parseable-fixits Experiments with clang (3.4.2) showed that supplying the option does *not* suppress the normal printing of fixits, so I emulated that behavior in my patch. I implemented tests using both -fself-test and DejaGnu. For the DejaGnu test coverage, I attempted to implement detection of the output strings via existing directives, but after several hours of failing, I instead implemented a new "dg-regexp" directive, which doesn't expect anything other than the given regexp. If anyone can see a way to implement the tests using existing directives, I'll port to that. (I believe that the injection of leading line numbers was the issue). I need review of the proposed additions to gcc/testsuite/lib at least (the rest I believe I can self-approve, but another pair of eyes would be appreciated). Successfully bootstrapped®rtested on x86_64-pc-linux-gnu. Successful -fself-test of stage1 on powerpc-ibm-aix7.1.3.0. OK for trunk? [1] http://clang-developers.42468.n3.nabble.com/Parsing-clang-output-td3275815.html#a3276978 gcc/ChangeLog: * 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. gcc/testsuite/ChangeLog: * 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. libcpp/ChangeLog: * 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. --- gcc/common.opt | 4 + gcc/diagnostic.c | 258 +++++++++++++++++++++ gcc/diagnostic.h | 4 + gcc/doc/invoke.texi | 34 ++- gcc/opts.c | 4 + gcc/selftest-run-tests.c | 1 + gcc/selftest.h | 1 + .../diagnostic-test-show-locus-parseable-fixits.c | 41 ++++ gcc/testsuite/gcc.dg/plugin/plugin.exp | 3 +- gcc/testsuite/lib/gcc-defs.exp | 51 ++++ gcc/testsuite/lib/gcc-dg.exp | 2 + gcc/testsuite/lib/prune.exp | 3 + libcpp/include/line-map.h | 16 ++ 13 files changed, 420 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-parseable-fixits.c diff --git a/gcc/common.opt b/gcc/common.opt index f0d7196..5d90385 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1185,6 +1185,10 @@ Enum(diagnostic_color_rule) String(always) Value(DIAGNOSTICS_COLOR_YES) 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. diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c index 9f419fb..bb41011 100644 --- a/gcc/diagnostic.c +++ b/gcc/diagnostic.c @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see #include "backtrace.h" #include "diagnostic.h" #include "diagnostic-color.h" +#include "selftest.h" #ifdef HAVE_TERMIOS_H # include @@ -667,6 +668,112 @@ diagnostic_pop_diagnostics (diagnostic_context *context, location_t where) 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 (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 (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 @@ -828,6 +935,11 @@ diagnostic_report_diagnostic (diagnostic_context *context, (*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; @@ -1302,3 +1414,149 @@ real_abort (void) { 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 */ diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index 58d77df..afce285 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -205,6 +205,10 @@ struct diagnostic_context /* 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 diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index e000218..4b04130 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -246,7 +246,8 @@ 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 --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}. @@ -3389,6 +3390,37 @@ the @option{-fmessage-length=n} option is given. When the output is done 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 diff --git a/gcc/opts.c b/gcc/opts.c index e80331f..7406210 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1871,6 +1871,10 @@ common_handle_option (struct gcc_options *opts, 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; diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c index d4a9c0b..bddf0b2 100644 --- a/gcc/selftest-run-tests.c +++ b/gcc/selftest-run-tests.c @@ -59,6 +59,7 @@ selftest::run_tests () /* 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 (); diff --git a/gcc/selftest.h b/gcc/selftest.h index 2bc7316..c6becdd 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -72,6 +72,7 @@ extern void assert_streq (const location &loc, /* 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 (); diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-parseable-fixits.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-parseable-fixits.c new file mode 100644 index 0000000..1490c98 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-parseable-fixits.c @@ -0,0 +1,41 @@ +/* { 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 +} diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp index be2ac8d..f039c8d 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp @@ -65,7 +65,8 @@ set plugin_test_list [list \ { 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 \ diff --git a/gcc/testsuite/lib/gcc-defs.exp b/gcc/testsuite/lib/gcc-defs.exp index 0210af8..1186e5b 100644 --- a/gcc/testsuite/lib/gcc-defs.exp +++ b/gcc/testsuite/lib/gcc-defs.exp @@ -352,3 +352,54 @@ proc gcc-set-multilib-library-path { compiler } { 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 +} diff --git a/gcc/testsuite/lib/gcc-dg.exp b/gcc/testsuite/lib/gcc-dg.exp index 7039140..b7f0ff7 100644 --- a/gcc/testsuite/lib/gcc-dg.exp +++ b/gcc/testsuite/lib/gcc-dg.exp @@ -899,6 +899,7 @@ if { [info procs saved-dg-test] == [list] } { global saved_compiler_env_var global keep_saved_temps_suffixes global multiline_expected_outputs + global freeform_regexps set additional_files "" set additional_sources "" @@ -924,6 +925,7 @@ if { [info procs saved-dg-test] == [list] } { unset testname_with_flags } set multiline_expected_outputs [] + set freeform_regexps [] } proc dg-test { args } { diff --git a/gcc/testsuite/lib/prune.exp b/gcc/testsuite/lib/prune.exp index ab6f369..d3ff493 100644 --- a/gcc/testsuite/lib/prune.exp +++ b/gcc/testsuite/lib/prune.exp @@ -73,6 +73,9 @@ proc prune_gcc_output { text } { # 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 diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h index 292abce..416419c 100644 --- a/libcpp/include/line-map.h +++ b/libcpp/include/line-map.h @@ -1418,6 +1418,8 @@ public: 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 @@ -1428,6 +1430,8 @@ 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; } @@ -1447,6 +1451,12 @@ class fixit_remove : public fixit_hint 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; } @@ -1463,6 +1473,12 @@ class fixit_replace : public fixit_hint 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; }