From patchwork Fri Oct 23 20:41:52 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 535254 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 D444A14137D for ; Sat, 24 Oct 2015 07:27:04 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b=CgC/IJPw; 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:in-reply-to:references; q=dns; s= default; b=eKCjQvhr0HqXcwx5EXgX2bDe7Qwbck1hYvmYLtKt2qlwEcMHn6sDb DP8uOqWegRBQ/hhopbZpMZEmiW4Q4i2T/ur1PWp89jI3ttmDYJrlJpkZH29NjaEV QLOlKhcopepSeTpZYyFIntKRczMVmyqN58eOQDCyfnDwrrUNH12Lx4= 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:in-reply-to:references; s= default; bh=ukJFo7jLmlQzy0muAYhcpAh5K1I=; b=CgC/IJPwJadPPiw0LNiM ArHYkHEu4Q6JA/+E+wIEF1JQiFTuwPwxBm8sZSOgMCXyG335HzeMgH/f6wbB5oWv Q0zSRmzFgRHsAsW7UgoDrMXLlF3lbJ5qP5WNKNv7C10/GwTKsSpBpFB6046cWeCg WLKKYfuqh+eV5LzDIatuvus= Received: (qmail 21688 invoked by alias); 23 Oct 2015 20:25:11 -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 19959 invoked by uid 89); 23 Oct 2015 20:25:06 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=0.1 required=5.0 tests=AWL, BAYES_50, KAM_ASCII_DIVIDERS, KAM_LOTSOFHASH autolearn=no version=3.3.2 X-HELO: eggs.gnu.org Received: from eggs.gnu.org (HELO eggs.gnu.org) (208.118.235.92) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-SHA encrypted) ESMTPS; Fri, 23 Oct 2015 20:24:40 +0000 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Zpisz-0001Ry-Ah for gcc-patches@gcc.gnu.org; Fri, 23 Oct 2015 16:24:35 -0400 Received: from mx1.redhat.com ([209.132.183.28]:35973) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Zpisy-0001RJ-Nr for gcc-patches@gcc.gnu.org; Fri, 23 Oct 2015 16:24:25 -0400 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (Postfix) with ESMTPS id 7FB8AC0BEAA2 for ; Fri, 23 Oct 2015 20:24:22 +0000 (UTC) Received: from c64.redhat.com (vpn-230-149.phx2.redhat.com [10.3.230.149]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t9NKOJgT007382; Fri, 23 Oct 2015 16:24:21 -0400 From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [PATCH 04/10] Reimplement diagnostic_show_locus, introducing rich_location classes (v5) Date: Fri, 23 Oct 2015 16:41:52 -0400 Message-Id: <1445632918-29617-5-git-send-email-dmalcolm@redhat.com> In-Reply-To: <1445632918-29617-1-git-send-email-dmalcolm@redhat.com> References: <1442957171-22904-1-git-send-email-dmalcolm@redhat.com> <1445632918-29617-1-git-send-email-dmalcolm@redhat.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 X-IsSubscribed: yes The change since v4 can be seen at: https://dmalcolm.fedorapeople.org/gcc/2015-10-23/0001-Add-colorize_source_p-to-diagnostic_context.patch which is a tweak to colorization, to handle both frontends that provide ranges and those that only provide carets, and provide a smoother transition path for the latter. gcc/ChangeLog: * diagnostic-color.c (color_dict): Eliminate "caret"; add "range1" and "range2". (parse_gcc_colors): Update comment to describe default GCC_COLORS. * diagnostic-core.h (warning_at_rich_loc): New declaration. (error_at_rich_loc): New declaration. (permerror_at_rich_loc): New declaration. (inform_at_rich_loc): New declaration. * diagnostic-show-locus.c (adjust_line): Delete. (struct point_state): New struct. (class colorizer): New class. (class layout_point): New class. (class layout_range): New class. (class layout): New class. (colorizer::colorizer): New ctor. (colorizer::~colorizer): New dtor. (layout::layout): New ctor. (layout::print_line): New method. (layout::get_state_at_point): New method. (layout::get_x_bound_for_row): New method. (show_ruler): New function. (diagnostic_show_locus): Reimplement in terms of class layout. * diagnostic.c (diagnostic_initialize): Replace MAX_LOCATIONS_PER_MESSAGE with rich_location::MAX_RANGES. (diagnostic_set_info_translated): Convert param from location_t to rich_location *. Eliminate calls to set_location on the message in favor of storing the rich_location ptr there. (diagnostic_set_info): Convert param from location_t to rich_location *. (diagnostic_build_prefix): Break out array into... (diagnostic_kind_color): New variable. (diagnostic_get_color_for_kind): New function. (diagnostic_report_diagnostic): Colorize the option_text using the color for the severity. (diagnostic_append_note): Update for change in signature of diagnostic_set_info. (diagnostic_append_note_at_rich_loc): New function. (emit_diagnostic): Update for change in signature of diagnostic_set_info. (inform): Likewise. (inform_at_rich_loc): New function. (inform_n): Update for change in signature of diagnostic_set_info. (warning): Likewise. (warning_at): Likewise. (warning_at_rich_loc): New function. (warning_n): Update for change in signature of diagnostic_set_info. (pedwarn): Likewise. (permerror): Likewise. (permerror_at_rich_loc): New function. (error): Update for change in signature of diagnostic_set_info. (error_n): Likewise. (error_at): Likewise. (error_at_rich_loc): New function. (sorry): Update for change in signature of diagnostic_set_info. (fatal_error): Likewise. (internal_error): Likewise. (internal_error_no_backtrace): Likewise. (source_range::debug): New function. * diagnostic.h (struct diagnostic_info): Eliminate field "override_column". Add field "richloc". (struct diagnostic_context): Add field "colorize_source_p". (diagnostic_override_column): Delete. (diagnostic_set_info): Convert param from location_t to rich_location *. (diagnostic_set_info_translated): Likewise. (diagnostic_append_note_at_rich_loc): New function. (diagnostic_num_locations): New function. (diagnostic_expand_location): Get the location from the rich_location. (diagnostic_print_caret_line): Delete. (diagnostic_get_color_for_kind): New declaration. * genmatch.c (linemap_client_expand_location_to_spelling_point): New. (error_cb): Update for change in signature of "error" callback. (fatal_at): Likewise. (warning_at): Likewise. * input.c (linemap_client_expand_location_to_spelling_point): New. * pretty-print.c (text_info::set_range): New method. (text_info::get_location): New method. * pretty-print.h (MAX_LOCATIONS_PER_MESSAGE): Eliminate this macro. (struct text_info): Eliminate "locations" array in favor of "m_richloc", a rich_location *. (textinfo::set_location): Add a "caret_p" param, and reimplement in terms of a call to set_range. (textinfo::get_location): Eliminate inline implementation in favor of an out-of-line reimplementation. (textinfo::set_range): New method. * rtl-error.c (diagnostic_for_asm): Update for change in signature of diagnostic_set_info. * tree-diagnostic.c (default_tree_printer): Update for new "caret_p" param for textinfo::set_location. * tree-pretty-print.c (percent_K_format): Likewise. gcc/c-family/ChangeLog: * c-common.c (c_cpp_error): Convert parameter from location_t to rich_location *. Eliminate the "column_override" parameter and the call to diagnostic_override_column. Update the "done_lexing" clause to set range 0 on the rich_location, rather than overwriting a location_t. * c-common.h (c_cpp_error): Convert parameter from location_t to rich_location *. Eliminate the "column_override" parameter. gcc/c/ChangeLog: * c-decl.c (warn_defaults_to): Update for change in signature of diagnostic_set_info. * c-errors.c (pedwarn_c99): Likewise. (pedwarn_c90): Likewise. * c-objc-common.c (c_tree_printer): Update for new "caret_p" param for textinfo::set_location. gcc/cp/ChangeLog: * error.c (cp_printer): Update for new "caret_p" param for textinfo::set_location. (pedwarn_cxx98): Update for change in signature of diagnostic_set_info. gcc/fortran/ChangeLog: * cpp.c (cb_cpp_error): Convert parameter from location_t to rich_location *. Eliminate the "column_override" parameter. * error.c (gfc_warning): Update for change in signature of diagnostic_set_info. (gfc_format_decoder): Update handling of %C/%L for changes to struct text_info. (gfc_diagnostic_starter): Use richloc when determining whether to print one locus or two. When handling a location that will involve a call to diagnostic_show_locus, only attempt to print the locus for the primary location, and don't call into diagnostic_print_caret_line. (gfc_warning_now_at): Update for change in signature of diagnostic_set_info. (gfc_warning_now): Likewise. (gfc_error_now): Likewise. (gfc_fatal_error): Likewise. (gfc_error): Likewise. (gfc_internal_error): Likewise. gcc/testsuite/ChangeLog: * gcc.dg/plugin/diagnostic-test-show-locus-bw.c: New file. * gcc.dg/plugin/diagnostic-test-show-locus-color.c: New file. * gcc.dg/plugin/diagnostic_plugin_test_show_locus.c: New file. * gcc.dg/plugin/plugin.exp (plugin_test_list): Add the above. * lib/gcc-dg.exp: Load multiline.exp. libcpp/ChangeLog: * errors.c (cpp_diagnostic): Update for change in signature of "error" callback. (cpp_diagnostic_with_line): Likewise, calling override_column on the rich_location. * include/cpplib.h (struct cpp_callbacks): Within "error" callback, convert param from source_location to rich_location *, and drop column_override param. * include/line-map.h (struct source_range): New struct. (struct location_range): New struct. (class rich_location): New class. (linemap_client_expand_location_to_spelling_point): New declaration. * line-map.c (rich_location::rich_location): New ctors. (rich_location::lazily_expand_location): New method. (rich_location::override_column): New method. (rich_location::add_range): New methods. (rich_location::set_range): New method. --- gcc/c-family/c-common.c | 15 +- gcc/c-family/c-common.h | 4 +- gcc/c/c-decl.c | 3 +- gcc/c/c-errors.c | 12 +- gcc/c/c-objc-common.c | 2 +- gcc/cp/error.c | 5 +- gcc/diagnostic-color.c | 5 +- gcc/diagnostic-core.h | 8 + gcc/diagnostic-show-locus.c | 778 ++++++++++++++++++--- gcc/diagnostic.c | 196 +++++- gcc/diagnostic.h | 55 +- gcc/fortran/cpp.c | 13 +- gcc/fortran/error.c | 103 +-- gcc/genmatch.c | 27 +- gcc/input.c | 7 + gcc/pretty-print.c | 21 + gcc/pretty-print.h | 25 +- gcc/rtl-error.c | 3 +- .../gcc.dg/plugin/diagnostic-test-show-locus-bw.c | 149 ++++ .../plugin/diagnostic-test-show-locus-color.c | 158 +++++ .../plugin/diagnostic_plugin_test_show_locus.c | 326 +++++++++ gcc/testsuite/gcc.dg/plugin/plugin.exp | 3 + gcc/testsuite/lib/gcc-dg.exp | 1 + gcc/tree-diagnostic.c | 2 +- gcc/tree-pretty-print.c | 2 +- libcpp/errors.c | 7 +- libcpp/include/cpplib.h | 4 +- libcpp/include/line-map.h | 207 ++++++ libcpp/line-map.c | 130 ++++ 29 files changed, 1983 insertions(+), 288 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw.c create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color.c create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 4b64a44..4a5ccb7 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -10477,15 +10477,14 @@ c_option_controlling_cpp_error (int reason) /* Callback from cpp_error for PFILE to print diagnostics from the preprocessor. The diagnostic is of type LEVEL, with REASON set to the reason code if LEVEL is represents a warning, at location - LOCATION unless this is after lexing and the compiler's location - should be used instead, with column number possibly overridden by - COLUMN_OVERRIDE if not zero; MSG is the translated message and AP + RICHLOC unless this is after lexing and the compiler's location + should be used instead; MSG is the translated message and AP the arguments. Returns true if a diagnostic was emitted, false otherwise. */ bool c_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason, - location_t location, unsigned int column_override, + rich_location *richloc, const char *msg, va_list *ap) { diagnostic_info diagnostic; @@ -10526,11 +10525,11 @@ c_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason, gcc_unreachable (); } if (done_lexing) - location = input_location; + richloc->set_range (0, + source_range::from_location (input_location), + true, true); diagnostic_set_info_translated (&diagnostic, msg, ap, - location, dlevel); - if (column_override) - diagnostic_override_column (&diagnostic, column_override); + richloc, dlevel); diagnostic_override_option_index (&diagnostic, c_option_controlling_cpp_error (reason)); ret = report_diagnostic (&diagnostic); diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index d5fb499..b0a7661 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -995,9 +995,9 @@ extern void init_c_lex (void); extern void c_cpp_builtins (cpp_reader *); extern void c_cpp_builtins_optimize_pragma (cpp_reader *, tree, tree); -extern bool c_cpp_error (cpp_reader *, int, int, location_t, unsigned int, +extern bool c_cpp_error (cpp_reader *, int, int, rich_location *, const char *, va_list *) - ATTRIBUTE_GCC_DIAG(6,0); + ATTRIBUTE_GCC_DIAG(5,0); extern int c_common_has_attribute (cpp_reader *); extern bool parse_optimize_options (tree, bool); diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index ce8406a..732080a 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -5297,9 +5297,10 @@ warn_defaults_to (location_t location, int opt, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + rich_location richloc (location); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, flag_isoc99 ? DK_PEDWARN : DK_WARNING); diagnostic.option_index = opt; report_diagnostic (&diagnostic); diff --git a/gcc/c/c-errors.c b/gcc/c/c-errors.c index e5fbf05..0f8b933 100644 --- a/gcc/c/c-errors.c +++ b/gcc/c/c-errors.c @@ -42,13 +42,14 @@ pedwarn_c99 (location_t location, int opt, const char *gmsgid, ...) diagnostic_info diagnostic; va_list ap; bool warned = false; + rich_location richloc (location); va_start (ap, gmsgid); /* If desired, issue the C99/C11 compat warning, which is more specific than -pedantic. */ if (warn_c99_c11_compat > 0) { - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, (pedantic && !flag_isoc11) ? DK_PEDWARN : DK_WARNING); diagnostic.option_index = OPT_Wc99_c11_compat; @@ -60,7 +61,7 @@ pedwarn_c99 (location_t location, int opt, const char *gmsgid, ...) /* For -pedantic outside C11, issue a pedwarn. */ else if (pedantic && !flag_isoc11) { - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN); diagnostic.option_index = opt; warned = report_diagnostic (&diagnostic); } @@ -80,6 +81,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + rich_location richloc (location); va_start (ap, gmsgid); /* Warnings such as -Wvla are the most specific ones. */ @@ -90,7 +92,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...) goto out; else if (opt_var > 0) { - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, (pedantic && !flag_isoc99) ? DK_PEDWARN : DK_WARNING); diagnostic.option_index = opt; @@ -102,7 +104,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...) specific than -pedantic. */ if (warn_c90_c99_compat > 0) { - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, (pedantic && !flag_isoc99) ? DK_PEDWARN : DK_WARNING); diagnostic.option_index = OPT_Wc90_c99_compat; @@ -114,7 +116,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...) /* For -pedantic outside C99, issue a pedwarn. */ else if (pedantic && !flag_isoc99) { - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN); diagnostic.option_index = opt; report_diagnostic (&diagnostic); } diff --git a/gcc/c/c-objc-common.c b/gcc/c/c-objc-common.c index 47fd7de..1e601f9 100644 --- a/gcc/c/c-objc-common.c +++ b/gcc/c/c-objc-common.c @@ -101,7 +101,7 @@ c_tree_printer (pretty_printer *pp, text_info *text, const char *spec, { t = va_arg (*text->args_ptr, tree); if (set_locus) - text->set_location (0, DECL_SOURCE_LOCATION (t)); + text->set_location (0, DECL_SOURCE_LOCATION (t), true); } switch (*spec) diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 17870b5..2e2ff10 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -3562,7 +3562,7 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec, pp_string (pp, result); if (set_locus && t != NULL) - text->set_location (0, location_of (t)); + text->set_location (0, location_of (t), true); return true; #undef next_tree #undef next_tcode @@ -3676,9 +3676,10 @@ pedwarn_cxx98 (location_t location, int opt, const char *gmsgid, ...) diagnostic_info diagnostic; va_list ap; bool ret; + rich_location richloc (location); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, (cxx_dialect == cxx98) ? DK_PEDWARN : DK_WARNING); diagnostic.option_index = opt; ret = report_diagnostic (&diagnostic); diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c index 3fe49b2..d848dfc 100644 --- a/gcc/diagnostic-color.c +++ b/gcc/diagnostic-color.c @@ -164,7 +164,8 @@ static struct color_cap color_dict[] = { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA), 7, false }, { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false }, - { "caret", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN), 5, false }, + { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false }, + { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false }, { "locus", SGR_SEQ (COLOR_BOLD), 5, false }, { "quote", SGR_SEQ (COLOR_BOLD), 5, false }, { NULL, NULL, 0, false } @@ -195,7 +196,7 @@ colorize_stop (bool show_color) } /* Parse GCC_COLORS. The default would look like: - GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' + GCC_COLORS='error=01;31:warning=01;35:note=01;36:range1=32:range2=34;locus=01:quote=01' No character escaping is needed or supported. */ static bool parse_gcc_colors (void) diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h index 66d2e42..a8a7c37 100644 --- a/gcc/diagnostic-core.h +++ b/gcc/diagnostic-core.h @@ -63,18 +63,26 @@ extern bool warning_n (location_t, int, int, const char *, const char *, ...) ATTRIBUTE_GCC_DIAG(4,6) ATTRIBUTE_GCC_DIAG(5,6); extern bool warning_at (location_t, int, const char *, ...) ATTRIBUTE_GCC_DIAG(3,4); +extern bool warning_at_rich_loc (rich_location *, int, const char *, ...) + ATTRIBUTE_GCC_DIAG(3,4); extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2); extern void error_n (location_t, int, const char *, const char *, ...) ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5); extern void error_at (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3); +extern void error_at_rich_loc (rich_location *, const char *, ...) + ATTRIBUTE_GCC_DIAG(2,3); extern void fatal_error (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3) ATTRIBUTE_NORETURN; /* Pass one of the OPT_W* from options.h as the second parameter. */ extern bool pedwarn (location_t, int, const char *, ...) ATTRIBUTE_GCC_DIAG(3,4); extern bool permerror (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3); +extern bool permerror_at_rich_loc (rich_location *, const char *, + ...) ATTRIBUTE_GCC_DIAG(2,3); extern void sorry (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2); extern void inform (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3); +extern void inform_at_rich_loc (rich_location *, const char *, + ...) ATTRIBUTE_GCC_DIAG(2,3); extern void inform_n (location_t, int, const char *, const char *, ...) ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5); extern void verbatim (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2); diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c index 147a2b8..6865209 100644 --- a/gcc/diagnostic-show-locus.c +++ b/gcc/diagnostic-show-locus.c @@ -36,131 +36,711 @@ along with GCC; see the file COPYING3. If not see # include #endif -/* If LINE is longer than MAX_WIDTH, and COLUMN is not smaller than - MAX_WIDTH by some margin, then adjust the start of the line such - that the COLUMN is smaller than MAX_WIDTH minus the margin. The - margin is either CARET_LINE_MARGIN characters or the difference - between the column and the length of the line, whatever is smaller. - The length of LINE is given by LINE_WIDTH. */ -static const char * -adjust_line (const char *line, int line_width, - int max_width, int *column_p) -{ - int right_margin = CARET_LINE_MARGIN; - int column = *column_p; - - gcc_checking_assert (line_width >= column); - right_margin = MIN (line_width - column, right_margin); - right_margin = max_width - right_margin; - if (line_width >= max_width && column > right_margin) +static void +show_ruler (diagnostic_context *context, int max_width, int x_offset); + +/* Classes for rendering source code and diagnostics, within an + anonymous namespace. + The work is done by "class layout", which embeds and uses + "class colorizer" and "class layout_range" to get things done. */ + +namespace { + +/* The state at a given point of the source code, assuming that we're + in a range: which range are we in, and whether we should draw a caret at + this point. */ + +struct point_state +{ + int range_idx; + bool draw_caret_p; +}; + +/* A class to inject colorization codes when printing the diagnostic locus. + + It has one kind of colorization for each of: + - normal text + - range 0 (the "primary location") + - range 1 + - range 2 + + The class caches the lookup of the color codes for the above. + + The class also has responsibility for tracking which of the above is + active, filtering out unnecessary changes. This allows layout::print_line + to simply request a colorization code for *every* character it prints + through this class, and have the filtering be done for it here. */ + +class colorizer +{ + public: + colorizer (diagnostic_context *context, + const diagnostic_info *diagnostic); + ~colorizer (); + + void set_range (int range_idx) { set_state (range_idx); } + void set_normal_text () { set_state (STATE_NORMAL_TEXT); } + + private: + void set_state (int state); + void begin_state (int state); + void finish_state (int state); + + private: + static const int STATE_NORMAL_TEXT = -1; + + diagnostic_context *m_context; + const diagnostic_info *m_diagnostic; + int m_current_state; + const char *m_caret_cs; + const char *m_caret_ce; + const char *m_range1_cs; + const char *m_range2_cs; + const char *m_range_ce; +}; + +/* A point within a layout_range; similar to an expanded_location, + but after filtering on file. */ + +class layout_point +{ + public: + layout_point (const expanded_location &exploc) + : m_line (exploc.line), + m_column (exploc.column) {} + + int m_line; + int m_column; +}; + +/* A class for use by "class layout" below: a filtered location_range. */ + +class layout_range +{ + public: + layout_range (const location_range *loc_range); + + bool contains_point (int row, int column) const; + + layout_point m_start; + layout_point m_finish; + bool m_show_caret_p; + layout_point m_caret; +}; + +/* A class to control the overall layout when printing a diagnostic. + + The layout is determined within the constructor. + It is then printed by repeatedly calling the "print_line" method. + Each such call can print two lines: one for the source line itself, + and potentially an "annotation" line, containing carets/underlines. + + We assume we have disjoint ranges. */ + +class layout +{ + public: + layout (diagnostic_context *context, + const diagnostic_info *diagnostic); + + int get_first_line () const { return m_first_line; } + int get_last_line () const { return m_last_line; } + + void print_line (int row); + + private: + bool + get_state_at_point (/* Inputs. */ + int row, int column, + int first_non_ws, int last_non_ws, + /* Outputs. */ + point_state *out_state); + + int + get_x_bound_for_row (int row, int caret_column, + int last_non_ws); + + private: + diagnostic_context *m_context; + pretty_printer *m_pp; + diagnostic_t m_diagnostic_kind; + expanded_location m_exploc; + colorizer m_colorizer; + bool m_colorize_source_p; + auto_vec m_layout_ranges; + int m_first_line; + int m_last_line; + int m_x_offset; +}; + +/* Implementation of "class colorizer". */ + +/* The constructor for "colorizer". Lookup and store color codes for the + different kinds of things we might need to print. */ + +colorizer::colorizer (diagnostic_context *context, + const diagnostic_info *diagnostic) : + m_context (context), + m_diagnostic (diagnostic), + m_current_state (STATE_NORMAL_TEXT) +{ + m_caret_ce = colorize_stop (pp_show_color (context->printer)); + m_range1_cs = colorize_start (pp_show_color (context->printer), "range1"); + m_range2_cs = colorize_start (pp_show_color (context->printer), "range2"); + m_range_ce = colorize_stop (pp_show_color (context->printer)); +} + +/* The destructor for "colorize". If colorization is on, print a code to + turn it off. */ + +colorizer::~colorizer () +{ + finish_state (m_current_state); +} + +/* Update state, printing color codes if necessary if there's a state + change. */ + +void +colorizer::set_state (int new_state) +{ + if (m_current_state != new_state) { - line += column - right_margin; - *column_p = right_margin; + finish_state (m_current_state); + m_current_state = new_state; + begin_state (new_state); } - return line; } -/* Print the physical source line corresponding to the location of - this diagnostic, and a caret indicating the precise column. This - function only prints two caret characters if the two locations - given by DIAGNOSTIC are on the same line according to - diagnostic_same_line(). */ +/* Turn on any colorization for STATE. */ + void -diagnostic_show_locus (diagnostic_context * context, - const diagnostic_info *diagnostic) +colorizer::begin_state (int state) { - if (!context->show_caret - || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION - || diagnostic_location (diagnostic, 0) == context->last_location) - return; + switch (state) + { + case STATE_NORMAL_TEXT: + break; - context->last_location = diagnostic_location (diagnostic, 0); - expanded_location s0 = diagnostic_expand_location (diagnostic, 0); - expanded_location s1 = { }; - /* Zero-initialized. This is checked later by diagnostic_print_caret_line. */ + case 0: + /* Make range 0 be the same color as the "kind" text + (error vs warning vs note). */ + pp_string + (m_context->printer, + colorize_start (pp_show_color (m_context->printer), + diagnostic_get_color_for_kind (m_diagnostic->kind))); + break; + + case 1: + pp_string (m_context->printer, m_range1_cs); + break; + + case 2: + pp_string (m_context->printer, m_range2_cs); + break; + + default: + /* We don't expect more than 3 ranges per diagnostic. */ + gcc_unreachable (); + break; + } +} + +/* Turn off any colorization for STATE. */ + +void +colorizer::finish_state (int state) +{ + switch (state) + { + case STATE_NORMAL_TEXT: + break; + + case 0: + pp_string (m_context->printer, m_caret_ce); + break; + + default: + /* Within a range. */ + gcc_assert (state > 0); + pp_string (m_context->printer, m_range_ce); + break; + } +} + +/* Implementation of class layout_range. */ + +/* The constructor for class layout_range. + Initialize various layout_point fields from expanded_location + equivalents; we've already filtered on file. */ + +layout_range::layout_range (const location_range *loc_range) +: m_start (loc_range->m_start), + m_finish (loc_range->m_finish), + m_show_caret_p (loc_range->m_show_caret_p), + m_caret (loc_range->m_caret) +{ +} + +/* Is (column, row) within the given range? + We've already filtered on the file. + + Ranges are closed (both limits are within the range). + + Example A: a single-line range: + start: (col=22, line=2) + finish: (col=38, line=2) + + |00000011111111112222222222333333333344444444444 + |34567890123456789012345678901234567890123456789 +--+----------------------------------------------- +01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa +03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + Example B: a multiline range with + start: (col=14, line=3) + finish: (col=08, line=5) + + |00000011111111112222222222333333333344444444444 + |34567890123456789012345678901234567890123456789 +--+----------------------------------------------- +01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww +04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww +05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +--+----------------------------------------------- + + Legend: + - 'b' indicates a point *before* the range + - 'S' indicates the start of the range + - 'w' indicates a point within the range + - 'F' indicates the finish of the range (which is + within it). + - 'a' indicates a subsequent point *after* the range. */ + +bool +layout_range::contains_point (int row, int column) const +{ + gcc_assert (m_start.m_line <= m_finish.m_line); + /* ...but the equivalent isn't true for the columns; + consider example B in the comment above. */ + + if (row < m_start.m_line) + /* Points before the first line of the range are + outside it (corresponding to line 01 in example A + and lines 01 and 02 in example B above). */ + return false; + + if (row == m_start.m_line) + /* On same line as start of range (corresponding + to line 02 in example A and line 03 in example B). */ + { + if (column < m_start.m_column) + /* Points on the starting line of the range, but + before the column in which it begins. */ + return false; + + if (row < m_finish.m_line) + /* This is a multiline range; the point + is within it (corresponds to line 03 in example B + from column 14 onwards) */ + return true; + else + { + /* This is a single-line range. */ + gcc_assert (row == m_finish.m_line); + return column <= m_finish.m_column; + } + } + + /* The point is in a line beyond that containing the + start of the range: lines 03 onwards in example A, + and lines 04 onwards in example B. */ + gcc_assert (row > m_start.m_line); + + if (row > m_finish.m_line) + /* The point is beyond the final line of the range + (lines 03 onwards in example A, and lines 06 onwards + in example B). */ + return false; + + if (row < m_finish.m_line) + { + /* The point is in a line that's fully within a multiline + range (e.g. line 04 in example B). */ + gcc_assert (m_start.m_line < m_finish.m_line); + return true; + } + + gcc_assert (row == m_finish.m_line); + + return column <= m_finish.m_column; +} + +/* Given a source line LINE of length LINE_WIDTH, determine the width + without any trailing whitespace. */ + +static int +get_line_width_without_trailing_whitespace (const char *line, int line_width) +{ + int result = line_width; + while (result > 0) + { + char ch = line[result - 1]; + if (ch == ' ' || ch == '\t') + result--; + else + break; + } + gcc_assert (result >= 0); + gcc_assert (result <= line_width); + gcc_assert (result == 0 || + (line[result - 1] != ' ' + && line[result -1] != '\t')); + return result; +} + +/* Implementation of class layout. */ - if (diagnostic_location (diagnostic, 1) > BUILTINS_LOCATION) - s1 = diagnostic_expand_location (diagnostic, 1); +/* Constructor for class layout. + + Filter the ranges from the rich_location to those that we can + sanely print, populating m_layout_ranges. + Determine the range of lines that we will print. + Determine m_x_offset, to ensure that the primary caret + will fit within the max_width provided by the diagnostic_context. */ + +layout::layout (diagnostic_context * context, + const diagnostic_info *diagnostic) +: m_context (context), + m_pp (context->printer), + m_diagnostic_kind (diagnostic->kind), + m_exploc (diagnostic->richloc->lazily_expand_location ()), + m_colorizer (context, diagnostic), + m_colorize_source_p (context->colorize_source_p), + m_layout_ranges (rich_location::MAX_RANGES), + m_first_line (m_exploc.line), + m_last_line (m_exploc.line), + m_x_offset (0) +{ + rich_location *richloc = diagnostic->richloc; + for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++) + { + /* This diagnostic printer can only cope with "sufficiently sane" ranges. + Ignore any ranges that are awkward to handle. */ + location_range *loc_range = richloc->get_range (idx); + + /* If any part of the range isn't in the same file as the primary + location of this diagnostic, ignore the range. */ + if (loc_range->m_start.file != m_exploc.file) + continue; + if (loc_range->m_finish.file != m_exploc.file) + continue; + if (loc_range->m_show_caret_p) + if (loc_range->m_caret.file != m_exploc.file) + continue; + + /* Passed all the tests; add the range to m_layout_ranges so that + it will be printed. */ + layout_range ri (loc_range); + m_layout_ranges.safe_push (ri); + + /* Update m_first_line/m_last_line if necessary. */ + if (loc_range->m_start.line < m_first_line) + m_first_line = loc_range->m_start.line; + if (loc_range->m_finish.line > m_last_line) + m_last_line = loc_range->m_finish.line; + } + + /* Adjust m_x_offset. + Center the primary caret to fit in max_width; all columns + will be adjusted accordingly. */ + int max_width = m_context->caret_max_width; + int line_width; + const char *line = location_get_source_line (m_exploc.file, m_exploc.line, + &line_width); + if (line && m_exploc.column <= line_width) + { + int right_margin = CARET_LINE_MARGIN; + int column = m_exploc.column; + right_margin = MIN (line_width - column, right_margin); + right_margin = max_width - right_margin; + if (line_width >= max_width && column > right_margin) + m_x_offset = column - right_margin; + gcc_assert (m_x_offset >= 0); + } - diagnostic_print_caret_line (context, s0, s1, - context->caret_chars[0], - context->caret_chars[1]); + if (0) + show_ruler (context, line_width, m_x_offset); } -/* Print (part) of the source line given by xloc1 with caret1 pointing - at the column. If xloc2.column != 0 and it fits within the same - line as xloc1 according to diagnostic_same_line (), then caret2 is - printed at xloc2.colum. Otherwise, the caller has to set up things - to print a second caret line for xloc2. */ +/* Print text describing a line of source code. + This typically prints two lines: + + (1) the source code itself, potentially colorized at any ranges, and + (2) an annotation line containing any carets/underlines + describing the ranges. */ + void -diagnostic_print_caret_line (diagnostic_context * context, - expanded_location xloc1, - expanded_location xloc2, - char caret1, char caret2) -{ - if (!diagnostic_same_line (context, xloc1, xloc2)) - /* This will mean ignore xloc2. */ - xloc2.column = 0; - else if (xloc1.column == xloc2.column) - xloc2.column++; - - int cmax = MAX (xloc1.column, xloc2.column); +layout::print_line (int row) +{ int line_width; - const char *line = location_get_source_line (xloc1.file, xloc1.line, + const char *line = location_get_source_line (m_exploc.file, row, &line_width); - if (line == NULL || cmax > line_width) + if (!line) return; - /* Center the interesting part of the source line to fit in - max_width, and adjust all columns accordingly. */ - int max_width = context->caret_max_width; - int offset = (int) cmax; - line = adjust_line (line, line_width, max_width, &offset); - offset -= cmax; - cmax += offset; - xloc1.column += offset; - if (xloc2.column) - xloc2.column += offset; - - /* Print the source line. */ - pp_newline (context->printer); - const char *saved_prefix = pp_get_prefix (context->printer); - pp_set_prefix (context->printer, NULL); - pp_space (context->printer); - while (max_width > 0 && line_width > 0) + line += m_x_offset; + + m_colorizer.set_normal_text (); + + /* Step 1: print the source code line. */ + + /* We will stop printing at any trailing whitespace. */ + line_width + = get_line_width_without_trailing_whitespace (line, + line_width); + pp_space (m_pp); + int first_non_ws = INT_MAX; + int last_non_ws = 0; + int column; + for (column = 1 + m_x_offset; column <= line_width; column++) { + /* Assuming colorization is enabled for the caret and underline + characters, we may also colorize the associated characters + within the source line. + + For frontends that generate range information, we color the + associated characters in the source line the same as the + carets and underlines in the annotation line, to make it easier + for the reader to see the pertinent code. + + For frontends that only generate carets, we don't colorize the + characters above them, since this would look strange (e.g. + colorizing just the first character in a token). */ + if (m_colorize_source_p) + { + bool in_range_p; + point_state state; + in_range_p = get_state_at_point (row, column, + 0, INT_MAX, + &state); + if (in_range_p) + m_colorizer.set_range (state.range_idx); + else + m_colorizer.set_normal_text (); + } char c = *line == '\t' ? ' ' : *line; if (c == '\0') c = ' '; - pp_character (context->printer, c); - max_width--; - line_width--; + if (c != ' ') + { + last_non_ws = column; + if (first_non_ws == INT_MAX) + first_non_ws = column; + } + pp_character (m_pp, c); line++; } - pp_newline (context->printer); + pp_newline (m_pp); + + /* Step 2: print a line consisting of the caret/underlines for the + given source line. */ + int x_bound = get_x_bound_for_row (row, m_exploc.column, + last_non_ws); + + pp_space (m_pp); + for (int column = 1 + m_x_offset; column < x_bound; column++) + { + bool in_range_p; + point_state state; + in_range_p = get_state_at_point (row, column, + first_non_ws, last_non_ws, + &state); + if (in_range_p) + { + /* Within a range. Draw either the caret or an underline. */ + m_colorizer.set_range (state.range_idx); + if (state.draw_caret_p) + /* Draw the caret. */ + pp_character (m_pp, m_context->caret_chars[state.range_idx]); + else + pp_character (m_pp, '~'); + } + else + { + /* Not in a range. */ + m_colorizer.set_normal_text (); + pp_character (m_pp, ' '); + } + } + pp_newline (m_pp); +} + +/* Return true if (ROW/COLUMN) is within a range of the layout. + If it returns true, OUT_STATE is written to, with the + range index, and whether we should draw the caret at + (ROW/COLUMN) (as opposed to an underline). */ + +bool +layout::get_state_at_point (/* Inputs. */ + int row, int column, + int first_non_ws, int last_non_ws, + /* Outputs. */ + point_state *out_state) +{ + layout_range *range; + int i; + FOR_EACH_VEC_ELT (m_layout_ranges, i, range) + { + if (0) + fprintf (stderr, + "range ( (%i, %i), (%i, %i))->contains_point (%i, %i): %s\n", + range->m_start.m_line, + range->m_start.m_column, + range->m_finish.m_line, + range->m_finish.m_column, + row, + column, + range->contains_point (row, column) ? "true" : "false"); + + if (range->contains_point (row, column)) + { + out_state->range_idx = i; + + /* Are we at the range's caret? is it visible? */ + out_state->draw_caret_p = false; + if (row == range->m_caret.m_line + && column == range->m_caret.m_column) + out_state->draw_caret_p = range->m_show_caret_p; + + /* Within a multiline range, don't display any underline + in any leading or trailing whitespace on a line. + We do display carets, however. */ + if (!out_state->draw_caret_p) + if (column < first_non_ws || column > last_non_ws) + return false; + + /* We are within a range. */ + return true; + } + } + + return false; +} + +/* Helper function for use by layout::print_line when printing the + annotation line under the source line. + Get the column beyond the rightmost one that could contain a caret or + range marker, given that we stop rendering at trailing whitespace. + ROW is the source line within the given file. + CARET_COLUMN is the column of range 0's caret. + LAST_NON_WS_COLUMN is the last column containing a non-whitespace + character of source (as determined when printing the source line). */ + +int +layout::get_x_bound_for_row (int row, int caret_column, + int last_non_ws_column) +{ + int result = caret_column + 1; - /* Print the caret under the line. */ - const char *caret_cs, *caret_ce; - caret_cs = colorize_start (pp_show_color (context->printer), "caret"); - caret_ce = colorize_stop (pp_show_color (context->printer)); - int cmin = xloc2.column - ? MIN (xloc1.column, xloc2.column) : xloc1.column; - int caret_min = cmin == xloc1.column ? caret1 : caret2; - int caret_max = cmin == xloc1.column ? caret2 : caret1; - - /* cmin is >= 1, but we indent with an extra space at the start like - we did above. */ + layout_range *range; int i; - for (i = 0; i < cmin; i++) - pp_space (context->printer); - pp_printf (context->printer, "%s%c%s", caret_cs, caret_min, caret_ce); + FOR_EACH_VEC_ELT (m_layout_ranges, i, range) + { + if (row >= range->m_start.m_line) + { + if (range->m_finish.m_line == row) + { + /* On the final line within a range; ensure that + we render up to the end of the range. */ + if (result <= range->m_finish.m_column) + result = range->m_finish.m_column + 1; + } + else if (row < range->m_finish.m_line) + { + /* Within a multiline range; ensure that we render up to the + last non-whitespace column. */ + if (result <= last_non_ws_column) + result = last_non_ws_column + 1; + } + } + } + + return result; +} + +} /* End of anonymous namespace. */ + +/* For debugging layout issues in diagnostic_show_locus and friends, + render a ruler giving column numbers (after the 1-column indent). */ - if (xloc2.column) +static void +show_ruler (diagnostic_context *context, int max_width, int x_offset) +{ + /* Hundreds. */ + if (max_width > 99) { - for (i++; i < cmax; i++) - pp_space (context->printer); - pp_printf (context->printer, "%s%c%s", caret_cs, caret_max, caret_ce); + pp_space (context->printer); + for (int column = 1 + x_offset; column < max_width; column++) + if (0 == column % 10) + pp_character (context->printer, '0' + (column / 100) % 10); + else + pp_space (context->printer); + pp_newline (context->printer); } + + /* Tens. */ + pp_space (context->printer); + for (int column = 1 + x_offset; column < max_width; column++) + if (0 == column % 10) + pp_character (context->printer, '0' + (column / 10) % 10); + else + pp_space (context->printer); + pp_newline (context->printer); + + /* Units. */ + pp_space (context->printer); + for (int column = 1 + x_offset; column < max_width; column++) + pp_character (context->printer, '0' + (column % 10)); + pp_newline (context->printer); +} + +/* Print the physical source code corresponding to the location of + this diagnostic, with additional annotations. */ + +void +diagnostic_show_locus (diagnostic_context * context, + const diagnostic_info *diagnostic) +{ + if (!context->show_caret + || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION + || diagnostic_location (diagnostic, 0) == context->last_location) + return; + + context->last_location = diagnostic_location (diagnostic, 0); + + pp_newline (context->printer); + + const char *saved_prefix = pp_get_prefix (context->printer); + pp_set_prefix (context->printer, NULL); + + { + layout layout (context, diagnostic); + int last_line = layout.get_last_line (); + for (int row = layout.get_first_line (); + row <= last_line; + row++) + layout.print_line (row); + + /* The closing scope here leads to the dtor for layout and thus + colorizer being called here, which affects the precise + place where colorization is turned off in the unittest + for colorized output. */ + } + pp_set_prefix (context->printer, saved_prefix); - pp_needs_newline (context->printer) = true; } diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c index 831859a..5fe6627 100644 --- a/gcc/diagnostic.c +++ b/gcc/diagnostic.c @@ -144,7 +144,7 @@ diagnostic_initialize (diagnostic_context *context, int n_opts) context->classify_diagnostic[i] = DK_UNSPECIFIED; context->show_caret = false; diagnostic_set_caret_max_width (context, pp_line_cutoff (context->printer)); - for (i = 0; i < MAX_LOCATIONS_PER_MESSAGE; i++) + for (i = 0; i < rich_location::MAX_RANGES; i++) context->caret_chars[i] = '^'; context->show_option_requested = false; context->abort_on_error = false; @@ -234,16 +234,15 @@ diagnostic_finish (diagnostic_context *context) translated. */ void diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg, - va_list *args, location_t location, + va_list *args, rich_location *richloc, diagnostic_t kind) { + gcc_assert (richloc); diagnostic->message.err_no = errno; diagnostic->message.args_ptr = args; diagnostic->message.format_spec = msg; - diagnostic->message.set_location (0, location); - for (int i = 1; i < MAX_LOCATIONS_PER_MESSAGE; i++) - diagnostic->message.set_location (i, UNKNOWN_LOCATION); - diagnostic->override_column = 0; + diagnostic->message.m_richloc = richloc; + diagnostic->richloc = richloc; diagnostic->kind = kind; diagnostic->option_index = 0; } @@ -252,10 +251,27 @@ diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg, translated. */ void diagnostic_set_info (diagnostic_info *diagnostic, const char *gmsgid, - va_list *args, location_t location, + va_list *args, rich_location *richloc, diagnostic_t kind) { - diagnostic_set_info_translated (diagnostic, _(gmsgid), args, location, kind); + gcc_assert (richloc); + diagnostic_set_info_translated (diagnostic, _(gmsgid), args, richloc, kind); +} + +static const char *const diagnostic_kind_color[] = { +#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (C), +#include "diagnostic.def" +#undef DEFINE_DIAGNOSTIC_KIND + NULL +}; + +/* Get a color name for diagnostics of type KIND + Result could be NULL. */ + +const char * +diagnostic_get_color_for_kind (diagnostic_t kind) +{ + return diagnostic_kind_color[kind]; } /* Return a malloc'd string describing a location. The caller is @@ -270,12 +286,6 @@ diagnostic_build_prefix (diagnostic_context *context, #undef DEFINE_DIAGNOSTIC_KIND "must-not-happen" }; - static const char *const diagnostic_kind_color[] = { -#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (C), -#include "diagnostic.def" -#undef DEFINE_DIAGNOSTIC_KIND - NULL - }; gcc_assert (diagnostic->kind < DK_LAST_DIAGNOSTIC_KIND); const char *text = _(diagnostic_kind_text[diagnostic->kind]); @@ -771,10 +781,14 @@ diagnostic_report_diagnostic (diagnostic_context *context, if (option_text) { + const char *cs + = colorize_start (pp_show_color (context->printer), + diagnostic_kind_color[diagnostic->kind]); + const char *ce = colorize_stop (pp_show_color (context->printer)); diagnostic->message.format_spec = ACONCAT ((diagnostic->message.format_spec, " ", - "[", option_text, "]", + "[", cs, option_text, ce, "]", NULL)); free (option_text); } @@ -854,9 +868,40 @@ diagnostic_append_note (diagnostic_context *context, diagnostic_info diagnostic; va_list ap; const char *saved_prefix; + rich_location richloc (location); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_NOTE); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_NOTE); + if (context->inhibit_notes_p) + { + va_end (ap); + return; + } + saved_prefix = pp_get_prefix (context->printer); + pp_set_prefix (context->printer, + diagnostic_build_prefix (context, &diagnostic)); + pp_newline (context->printer); + pp_format (context->printer, &diagnostic.message); + pp_output_formatted_text (context->printer); + pp_destroy_prefix (context->printer); + pp_set_prefix (context->printer, saved_prefix); + diagnostic_show_locus (context, &diagnostic); + va_end (ap); +} + +/* Same as diagnostic_append_note, but at RICHLOC. */ + +void +diagnostic_append_note_at_rich_loc (diagnostic_context *context, + rich_location *richloc, + const char * gmsgid, ...) +{ + diagnostic_info diagnostic; + va_list ap; + const char *saved_prefix; + + va_start (ap, gmsgid); + diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_NOTE); if (context->inhibit_notes_p) { va_end (ap); @@ -881,16 +926,17 @@ emit_diagnostic (diagnostic_t kind, location_t location, int opt, diagnostic_info diagnostic; va_list ap; bool ret; + rich_location richloc (location); va_start (ap, gmsgid); if (kind == DK_PERMERROR) { - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, permissive_error_kind (global_dc)); diagnostic.option_index = permissive_error_option (global_dc); } else { - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, kind); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, kind); if (kind == DK_WARNING || kind == DK_PEDWARN) diagnostic.option_index = opt; } @@ -907,9 +953,23 @@ inform (location_t location, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + rich_location richloc (location); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_NOTE); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_NOTE); + report_diagnostic (&diagnostic); + va_end (ap); +} + +/* Same as "inform", but at RICHLOC. */ +void +inform_at_rich_loc (rich_location *richloc, const char *gmsgid, ...) +{ + diagnostic_info diagnostic; + va_list ap; + + va_start (ap, gmsgid); + diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_NOTE); report_diagnostic (&diagnostic); va_end (ap); } @@ -922,11 +982,12 @@ inform_n (location_t location, int n, const char *singular_gmsgid, { diagnostic_info diagnostic; va_list ap; + rich_location richloc (location); va_start (ap, plural_gmsgid); diagnostic_set_info_translated (&diagnostic, ngettext (singular_gmsgid, plural_gmsgid, n), - &ap, location, DK_NOTE); + &ap, &richloc, DK_NOTE); report_diagnostic (&diagnostic); va_end (ap); } @@ -940,9 +1001,10 @@ warning (int opt, const char *gmsgid, ...) diagnostic_info diagnostic; va_list ap; bool ret; + rich_location richloc (input_location); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_WARNING); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_WARNING); diagnostic.option_index = opt; ret = report_diagnostic (&diagnostic); @@ -960,9 +1022,27 @@ warning_at (location_t location, int opt, const char *gmsgid, ...) diagnostic_info diagnostic; va_list ap; bool ret; + rich_location richloc (location); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_WARNING); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_WARNING); + diagnostic.option_index = opt; + ret = report_diagnostic (&diagnostic); + va_end (ap); + return ret; +} + +/* Same as warning at, but using RICHLOC. */ + +bool +warning_at_rich_loc (rich_location *richloc, int opt, const char *gmsgid, ...) +{ + diagnostic_info diagnostic; + va_list ap; + bool ret; + + va_start (ap, gmsgid); + diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_WARNING); diagnostic.option_index = opt; ret = report_diagnostic (&diagnostic); va_end (ap); @@ -980,11 +1060,13 @@ warning_n (location_t location, int opt, int n, const char *singular_gmsgid, diagnostic_info diagnostic; va_list ap; bool ret; + rich_location richloc (location); va_start (ap, plural_gmsgid); diagnostic_set_info_translated (&diagnostic, ngettext (singular_gmsgid, plural_gmsgid, n), - &ap, location, DK_WARNING); + &ap, &richloc, DK_WARNING +); diagnostic.option_index = opt; ret = report_diagnostic (&diagnostic); va_end (ap); @@ -1010,9 +1092,10 @@ pedwarn (location_t location, int opt, const char *gmsgid, ...) diagnostic_info diagnostic; va_list ap; bool ret; + rich_location richloc (location); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN); diagnostic.option_index = opt; ret = report_diagnostic (&diagnostic); va_end (ap); @@ -1032,9 +1115,28 @@ permerror (location_t location, const char *gmsgid, ...) diagnostic_info diagnostic; va_list ap; bool ret; + rich_location richloc (location); + + va_start (ap, gmsgid); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, + permissive_error_kind (global_dc)); + diagnostic.option_index = permissive_error_option (global_dc); + ret = report_diagnostic (&diagnostic); + va_end (ap); + return ret; +} + +/* Same as "permerror", but at RICHLOC. */ + +bool +permerror_at_rich_loc (rich_location *richloc, const char *gmsgid, ...) +{ + diagnostic_info diagnostic; + va_list ap; + bool ret; va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, location, + diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, permissive_error_kind (global_dc)); diagnostic.option_index = permissive_error_option (global_dc); ret = report_diagnostic (&diagnostic); @@ -1049,9 +1151,10 @@ error (const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + rich_location richloc (input_location); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ERROR); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ERROR); report_diagnostic (&diagnostic); va_end (ap); } @@ -1064,11 +1167,12 @@ error_n (location_t location, int n, const char *singular_gmsgid, { diagnostic_info diagnostic; va_list ap; + rich_location richloc (location); va_start (ap, plural_gmsgid); diagnostic_set_info_translated (&diagnostic, ngettext (singular_gmsgid, plural_gmsgid, n), - &ap, location, DK_ERROR); + &ap, &richloc, DK_ERROR); report_diagnostic (&diagnostic); va_end (ap); } @@ -1079,9 +1183,25 @@ error_at (location_t loc, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + rich_location richloc (loc); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, loc, DK_ERROR); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ERROR); + report_diagnostic (&diagnostic); + va_end (ap); +} + +/* Same as above, but use RICH_LOC. */ + +void +error_at_rich_loc (rich_location *rich_loc, const char *gmsgid, ...) +{ + diagnostic_info diagnostic; + va_list ap; + + va_start (ap, gmsgid); + diagnostic_set_info (&diagnostic, gmsgid, &ap, rich_loc, + DK_ERROR); report_diagnostic (&diagnostic); va_end (ap); } @@ -1094,9 +1214,10 @@ sorry (const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + rich_location richloc (input_location); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_SORRY); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_SORRY); report_diagnostic (&diagnostic); va_end (ap); } @@ -1117,9 +1238,10 @@ fatal_error (location_t loc, const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + rich_location richloc (loc); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, loc, DK_FATAL); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_FATAL); report_diagnostic (&diagnostic); va_end (ap); @@ -1135,9 +1257,10 @@ internal_error (const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + rich_location richloc (input_location); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ICE); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ICE); report_diagnostic (&diagnostic); va_end (ap); @@ -1152,9 +1275,10 @@ internal_error_no_backtrace (const char *gmsgid, ...) { diagnostic_info diagnostic; va_list ap; + rich_location richloc (input_location); va_start (ap, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ICE_NOBT); + diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ICE_NOBT); report_diagnostic (&diagnostic); va_end (ap); @@ -1218,3 +1342,11 @@ real_abort (void) { abort (); } + +void +source_range::debug (const char *msg) const +{ + rich_location richloc (m_start); + richloc.add_range (m_start, m_finish); + inform_at_rich_loc (&richloc, "%s", msg); +} diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index 7fcb6a8..153e84c 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -29,10 +29,12 @@ along with GCC; see the file COPYING3. If not see list in diagnostic.def. */ struct diagnostic_info { - /* Text to be formatted. It also contains the location(s) for this - diagnostic. */ + /* Text to be formatted. */ text_info message; - unsigned int override_column; + + /* The location at which the diagnostic is to be reported. */ + rich_location *richloc; + /* Auxiliary data for client. */ void *x_data; /* The kind of diagnostic it is about. */ @@ -102,8 +104,8 @@ struct diagnostic_context /* Maximum width of the source line printed. */ int caret_max_width; - /* Characters used for caret diagnostics. */ - char caret_chars[MAX_LOCATIONS_PER_MESSAGE]; + /* Character used for caret diagnostics. */ + char caret_chars[rich_location::MAX_RANGES]; /* True if we should print the command line option which controls each diagnostic, if known. */ @@ -181,6 +183,15 @@ struct diagnostic_context int lock; bool inhibit_notes_p; + + /* When printing source code, should the characters at carets and ranges + be colorized? (assuming colorization is on at all). + This should be true for frontends that generate range information + (so that the ranges of code are colorized), + and false for frontends that merely specify points within the + source code (to avoid e.g. colorizing just the first character in + a token, which would look strange). */ + bool colorize_source_p; }; static inline void @@ -252,10 +263,6 @@ extern diagnostic_context *global_dc; #define report_diagnostic(D) diagnostic_report_diagnostic (global_dc, D) -/* Override the column number to be used for reporting a - diagnostic. */ -#define diagnostic_override_column(DI, COL) (DI)->override_column = (COL) - /* Override the option index to be used for reporting a diagnostic. */ #define diagnostic_override_option_index(DI, OPTIDX) \ @@ -279,13 +286,17 @@ extern bool diagnostic_report_diagnostic (diagnostic_context *, diagnostic_info *); #ifdef ATTRIBUTE_GCC_DIAG extern void diagnostic_set_info (diagnostic_info *, const char *, va_list *, - location_t, diagnostic_t) ATTRIBUTE_GCC_DIAG(2,0); + rich_location *, diagnostic_t) ATTRIBUTE_GCC_DIAG(2,0); extern void diagnostic_set_info_translated (diagnostic_info *, const char *, - va_list *, location_t, + va_list *, rich_location *, diagnostic_t) ATTRIBUTE_GCC_DIAG(2,0); extern void diagnostic_append_note (diagnostic_context *, location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(3,4); +extern void diagnostic_append_note_at_rich_loc (diagnostic_context *, + rich_location *, + const char *, ...) + ATTRIBUTE_GCC_DIAG(3,4); #endif extern char *diagnostic_build_prefix (diagnostic_context *, const diagnostic_info *); void default_diagnostic_starter (diagnostic_context *, diagnostic_info *); @@ -306,6 +317,14 @@ diagnostic_location (const diagnostic_info * diagnostic, int which = 0) return diagnostic->message.get_location (which); } +/* Return the number of locations to be printed in DIAGNOSTIC. */ + +static inline unsigned int +diagnostic_num_locations (const diagnostic_info * diagnostic) +{ + return diagnostic->message.m_richloc->get_num_locations (); +} + /* Expand the location of this diagnostic. Use this function for consistency. Parameter WHICH specifies which location. By default, expand the first one. */ @@ -313,12 +332,7 @@ diagnostic_location (const diagnostic_info * diagnostic, int which = 0) static inline expanded_location diagnostic_expand_location (const diagnostic_info * diagnostic, int which = 0) { - expanded_location s - = expand_location_to_spelling_point (diagnostic_location (diagnostic, - which)); - if (which == 0 && diagnostic->override_column) - s.column = diagnostic->override_column; - return s; + return diagnostic->richloc->get_range (which)->m_caret; } /* This is somehow the right-side margin of a caret line, that is, we @@ -338,11 +352,8 @@ diagnostic_same_line (const diagnostic_context *context, && context->caret_max_width - CARET_LINE_MARGIN > abs (s1.column - s2.column); } -void -diagnostic_print_caret_line (diagnostic_context * context, - expanded_location xloc1, - expanded_location xloc2, - char caret1, char caret2); +extern const char * +diagnostic_get_color_for_kind (diagnostic_t kind); /* Pure text formatting support functions. */ extern char *file_name_as_prefix (diagnostic_context *, const char *); diff --git a/gcc/fortran/cpp.c b/gcc/fortran/cpp.c index daffc20..92dc584 100644 --- a/gcc/fortran/cpp.c +++ b/gcc/fortran/cpp.c @@ -149,9 +149,9 @@ static void cb_include (cpp_reader *, source_location, const unsigned char *, static void cb_ident (cpp_reader *, source_location, const cpp_string *); static void cb_used_define (cpp_reader *, source_location, cpp_hashnode *); static void cb_used_undef (cpp_reader *, source_location, cpp_hashnode *); -static bool cb_cpp_error (cpp_reader *, int, int, location_t, unsigned int, +static bool cb_cpp_error (cpp_reader *, int, int, rich_location *, const char *, va_list *) - ATTRIBUTE_GCC_DIAG(6,0); + ATTRIBUTE_GCC_DIAG(5,0); void pp_dir_change (cpp_reader *, const char *); static int dump_macro (cpp_reader *, cpp_hashnode *, void *); @@ -1026,13 +1026,12 @@ cb_used_define (cpp_reader *pfile, source_location line ATTRIBUTE_UNUSED, /* Callback from cpp_error for PFILE to print diagnostics from the preprocessor. The diagnostic is of type LEVEL, with REASON set to the reason code if LEVEL is represents a warning, at location - LOCATION, with column number possibly overridden by COLUMN_OVERRIDE - if not zero; MSG is the translated message and AP the arguments. + RICHLOC; MSG is the translated message and AP the arguments. Returns true if a diagnostic was emitted, false otherwise. */ static bool cb_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason, - location_t location, unsigned int column_override, + rich_location *richloc, const char *msg, va_list *ap) { diagnostic_info diagnostic; @@ -1067,9 +1066,7 @@ cb_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason, gcc_unreachable (); } diagnostic_set_info_translated (&diagnostic, msg, ap, - location, dlevel); - if (column_override) - diagnostic_override_column (&diagnostic, column_override); + richloc, dlevel); if (reason == CPP_W_WARNING_DIRECTIVE) diagnostic_override_option_index (&diagnostic, OPT_Wcpp); ret = report_diagnostic (&diagnostic); diff --git a/gcc/fortran/error.c b/gcc/fortran/error.c index 3825751..4b3d31c 100644 --- a/gcc/fortran/error.c +++ b/gcc/fortran/error.c @@ -773,6 +773,7 @@ gfc_warning (int opt, const char *gmsgid, va_list ap) va_copy (argp, ap); diagnostic_info diagnostic; + rich_location rich_loc (UNKNOWN_LOCATION); bool fatal_errors = global_dc->fatal_errors; pretty_printer *pp = global_dc->printer; output_buffer *tmp_buffer = pp->buffer; @@ -787,7 +788,7 @@ gfc_warning (int opt, const char *gmsgid, va_list ap) --werrorcount; } - diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, + diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_WARNING); diagnostic.option_index = opt; bool ret = report_diagnostic (&diagnostic); @@ -938,10 +939,12 @@ gfc_format_decoder (pretty_printer *pp, /* If location[0] != UNKNOWN_LOCATION means that we already processed one of %C/%L. */ int loc_num = text->get_location (0) == UNKNOWN_LOCATION ? 0 : 1; - text->set_location (loc_num, - linemap_position_for_loc_and_offset (line_table, - loc->lb->location, - offset)); + source_range range + = source_range::from_location ( + linemap_position_for_loc_and_offset (line_table, + loc->lb->location, + offset)); + text->set_range (loc_num, range, true); pp_string (pp, result[loc_num]); return true; } @@ -1024,48 +1027,21 @@ gfc_diagnostic_build_locus_prefix (diagnostic_context *context, } /* This function prints the locus (file:line:column), the diagnostic kind - (Error, Warning) and (optionally) the caret line (a source line - with '1' and/or '2' below it). + (Error, Warning) and (optionally) the relevant lines of code with + annotation lines with '1' and/or '2' below them. - With -fdiagnostic-show-caret (the default) and for valid locations, - it prints for one location: + With -fdiagnostic-show-caret (the default) it prints: - [locus]: + [locus of primary range]: some code 1 Error: Some error at (1) - for two locations that fit in the same locus line: + With -fno-diagnostic-show-caret or if the primary range is not + valid, it prints: - [locus]: - - some code and some more code - 1 2 - Error: Some error at (1) and (2) - - and for two locations that do not fit in the same locus line: - - [locus]: - - some code - 1 - [locus2]: - - some other code - 2 - Error: Some error at (1) and (2) - - With -fno-diagnostic-show-caret or if one of the locations is not - valid, it prints for one location (or for two locations that fit in - the same locus line): - - [locus]: Error: Some error at (1) and (2) - - and for two locations that do not fit in the same locus line: - - [name]:[locus]: Error: (1) - [name]:[locus2]: Error: Some error at (1) and (2) + [locus of primary range]: Error: Some error at (1) and (2) */ static void gfc_diagnostic_starter (diagnostic_context *context, @@ -1075,7 +1051,7 @@ gfc_diagnostic_starter (diagnostic_context *context, expanded_location s1 = diagnostic_expand_location (diagnostic); expanded_location s2; - bool one_locus = diagnostic_location (diagnostic, 1) == UNKNOWN_LOCATION; + bool one_locus = diagnostic->richloc->get_num_locations () < 2; bool same_locus = false; if (!one_locus) @@ -1125,35 +1101,6 @@ gfc_diagnostic_starter (diagnostic_context *context, /* If the caret line was shown, the prefix does not contain the locus. */ pp_set_prefix (context->printer, kind_prefix); - - if (one_locus || same_locus) - return; - - locus_prefix = gfc_diagnostic_build_locus_prefix (context, s2); - if (diagnostic_location (diagnostic, 1) <= BUILTINS_LOCATION) - { - /* No caret line for the second location. Override the previous - prefix with [locus2]:[prefix]. */ - pp_set_prefix (context->printer, - concat (locus_prefix, " ", kind_prefix, NULL)); - free (kind_prefix); - free (locus_prefix); - } - else - { - /* We print the caret for the second location. */ - pp_verbatim (context->printer, locus_prefix); - free (locus_prefix); - /* Fortran uses an empty line between locus and caret line. */ - pp_newline (context->printer); - s1.column = 0; /* Print only a caret line for s2. */ - diagnostic_print_caret_line (context, s2, s1, - context->caret_chars[1], '\0'); - pp_newline (context->printer); - /* If the caret line was shown, the prefix does not contain the - locus. */ - pp_set_prefix (context->printer, kind_prefix); - } } } @@ -1173,10 +1120,11 @@ gfc_warning_now_at (location_t loc, int opt, const char *gmsgid, ...) { va_list argp; diagnostic_info diagnostic; + rich_location rich_loc (loc); bool ret; va_start (argp, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &argp, loc, DK_WARNING); + diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_WARNING); diagnostic.option_index = opt; ret = report_diagnostic (&diagnostic); va_end (argp); @@ -1190,10 +1138,11 @@ gfc_warning_now (int opt, const char *gmsgid, ...) { va_list argp; diagnostic_info diagnostic; + rich_location rich_loc (UNKNOWN_LOCATION); bool ret; va_start (argp, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, + diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_WARNING); diagnostic.option_index = opt; ret = report_diagnostic (&diagnostic); @@ -1209,11 +1158,12 @@ gfc_error_now (const char *gmsgid, ...) { va_list argp; diagnostic_info diagnostic; + rich_location rich_loc (UNKNOWN_LOCATION); error_buffer.flag = true; va_start (argp, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ERROR); + diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_ERROR); report_diagnostic (&diagnostic); va_end (argp); } @@ -1226,9 +1176,10 @@ gfc_fatal_error (const char *gmsgid, ...) { va_list argp; diagnostic_info diagnostic; + rich_location rich_loc (UNKNOWN_LOCATION); va_start (argp, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_FATAL); + diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_FATAL); report_diagnostic (&diagnostic); va_end (argp); @@ -1291,6 +1242,7 @@ gfc_error (const char *gmsgid, va_list ap) } diagnostic_info diagnostic; + rich_location richloc (UNKNOWN_LOCATION); bool fatal_errors = global_dc->fatal_errors; pretty_printer *pp = global_dc->printer; output_buffer *tmp_buffer = pp->buffer; @@ -1306,7 +1258,7 @@ gfc_error (const char *gmsgid, va_list ap) --errorcount; } - diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ERROR); + diagnostic_set_info (&diagnostic, gmsgid, &argp, &richloc, DK_ERROR); report_diagnostic (&diagnostic); if (buffered_p) @@ -1336,9 +1288,10 @@ gfc_internal_error (const char *gmsgid, ...) { va_list argp; diagnostic_info diagnostic; + rich_location rich_loc (UNKNOWN_LOCATION); va_start (argp, gmsgid); - diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ICE); + diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_ICE); report_diagnostic (&diagnostic); va_end (argp); diff --git a/gcc/genmatch.c b/gcc/genmatch.c index 102a635..6bfde06 100644 --- a/gcc/genmatch.c +++ b/gcc/genmatch.c @@ -53,14 +53,23 @@ unsigned verbose; static struct line_maps *line_table; +expanded_location +linemap_client_expand_location_to_spelling_point (source_location loc) +{ + const struct line_map_ordinary *map; + loc = linemap_resolve_location (line_table, loc, LRK_SPELLING_LOCATION, &map); + return linemap_expand_location (line_table, map, loc); +} + static bool #if GCC_VERSION >= 4001 -__attribute__((format (printf, 6, 0))) +__attribute__((format (printf, 5, 0))) #endif -error_cb (cpp_reader *, int errtype, int, source_location location, - unsigned int, const char *msg, va_list *ap) +error_cb (cpp_reader *, int errtype, int, rich_location *richloc, + const char *msg, va_list *ap) { const line_map_ordinary *map; + source_location location = richloc->get_loc (); linemap_resolve_location (line_table, location, LRK_SPELLING_LOCATION, &map); expanded_location loc = linemap_expand_location (line_table, map, location); fprintf (stderr, "%s:%d:%d %s: ", loc.file, loc.line, loc.column, @@ -102,9 +111,10 @@ __attribute__((format (printf, 2, 3))) #endif fatal_at (const cpp_token *tk, const char *msg, ...) { + rich_location richloc (tk->src_loc); va_list ap; va_start (ap, msg); - error_cb (NULL, CPP_DL_FATAL, 0, tk->src_loc, 0, msg, &ap); + error_cb (NULL, CPP_DL_FATAL, 0, &richloc, msg, &ap); va_end (ap); } @@ -114,9 +124,10 @@ __attribute__((format (printf, 2, 3))) #endif fatal_at (source_location loc, const char *msg, ...) { + rich_location richloc (loc); va_list ap; va_start (ap, msg); - error_cb (NULL, CPP_DL_FATAL, 0, loc, 0, msg, &ap); + error_cb (NULL, CPP_DL_FATAL, 0, &richloc, msg, &ap); va_end (ap); } @@ -126,9 +137,10 @@ __attribute__((format (printf, 2, 3))) #endif warning_at (const cpp_token *tk, const char *msg, ...) { + rich_location richloc (tk->src_loc); va_list ap; va_start (ap, msg); - error_cb (NULL, CPP_DL_WARNING, 0, tk->src_loc, 0, msg, &ap); + error_cb (NULL, CPP_DL_WARNING, 0, &richloc, msg, &ap); va_end (ap); } @@ -138,9 +150,10 @@ __attribute__((format (printf, 2, 3))) #endif warning_at (source_location loc, const char *msg, ...) { + rich_location richloc (loc); va_list ap; va_start (ap, msg); - error_cb (NULL, CPP_DL_WARNING, 0, loc, 0, msg, &ap); + error_cb (NULL, CPP_DL_WARNING, 0, &richloc, msg, &ap); va_end (ap); } diff --git a/gcc/input.c b/gcc/input.c index ff80dd9..baf8e7e 100644 --- a/gcc/input.c +++ b/gcc/input.c @@ -751,6 +751,13 @@ expand_location_to_spelling_point (source_location loc) return expand_location_1 (loc, /*expansion_point_p=*/false); } +expanded_location +linemap_client_expand_location_to_spelling_point (source_location loc) +{ + return expand_location_to_spelling_point (loc); +} + + /* If LOCATION is in a system header and if it is a virtual location for a token coming from the expansion of a macro, unwind it to the location of the expansion point of the macro. Otherwise, just return diff --git a/gcc/pretty-print.c b/gcc/pretty-print.c index 5889015..aee4172 100644 --- a/gcc/pretty-print.c +++ b/gcc/pretty-print.c @@ -31,6 +31,27 @@ along with GCC; see the file COPYING3. If not see #include #endif +/* Overwrite the range within this text_info's rich_location. + For use e.g. when implementing "+" in client format decoders. */ + +void +text_info::set_range (unsigned int idx, source_range range, bool caret_p) +{ + gcc_checking_assert (m_richloc); + m_richloc->set_range (idx, range, caret_p, true); +} + +location_t +text_info::get_location (unsigned int index_of_location) const +{ + gcc_checking_assert (m_richloc); + + if (index_of_location == 0) + return m_richloc->get_loc (); + else + return UNKNOWN_LOCATION; +} + // Default construct an output buffer. output_buffer::output_buffer () diff --git a/gcc/pretty-print.h b/gcc/pretty-print.h index 2654b0f..cdee253 100644 --- a/gcc/pretty-print.h +++ b/gcc/pretty-print.h @@ -27,11 +27,6 @@ along with GCC; see the file COPYING3. If not see /* Maximum number of format string arguments. */ #define PP_NL_ARGMAX 30 -/* Maximum number of locations associated to each message. If - location 'i' is UNKNOWN_LOCATION, then location 'i+1' is not - valid. */ -#define MAX_LOCATIONS_PER_MESSAGE 2 - /* The type of a text to be formatted according a format specification along with a list of things. */ struct text_info @@ -40,21 +35,17 @@ struct text_info va_list *args_ptr; int err_no; /* for %m */ void **x_data; + rich_location *m_richloc; - inline void set_location (unsigned int index_of_location, location_t loc) + inline void set_location (unsigned int idx, location_t loc, bool caret_p) { - gcc_checking_assert (index_of_location < MAX_LOCATIONS_PER_MESSAGE); - this->locations[index_of_location] = loc; + source_range src_range; + src_range.m_start = loc; + src_range.m_finish = loc; + set_range (idx, src_range, caret_p); } - - inline location_t get_location (unsigned int index_of_location) const - { - gcc_checking_assert (index_of_location < MAX_LOCATIONS_PER_MESSAGE); - return this->locations[index_of_location]; - } - -private: - location_t locations[MAX_LOCATIONS_PER_MESSAGE]; + void set_range (unsigned int idx, source_range range, bool caret_p); + location_t get_location (unsigned int index_of_location) const; }; /* How often diagnostics are prefixed by their locations: diff --git a/gcc/rtl-error.c b/gcc/rtl-error.c index 8b9b391..d28be1d 100644 --- a/gcc/rtl-error.c +++ b/gcc/rtl-error.c @@ -69,9 +69,10 @@ diagnostic_for_asm (const rtx_insn *insn, const char *msg, va_list *args_ptr, diagnostic_t kind) { diagnostic_info diagnostic; + rich_location richloc (location_for_asm (insn)); diagnostic_set_info (&diagnostic, msg, args_ptr, - location_for_asm (insn), kind); + &richloc, kind); report_diagnostic (&diagnostic); } diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw.c new file mode 100644 index 0000000..a4b16da --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw.c @@ -0,0 +1,149 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdiagnostics-show-caret" } */ + +/* 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. */ + +void test_simple (void) +{ +#if 0 + myvar = myvar.x; /* { dg-warning "test" } */ + +/* { dg-begin-multiline-output "" } + myvar = myvar.x; + ~~~~~^~ + { dg-end-multiline-output "" } */ +#endif +} + +void test_simple_2 (void) +{ +#if 0 + x = first_function () + second_function (); /* { dg-warning "test" } */ + +/* { dg-begin-multiline-output "" } + x = first_function () + second_function (); + ~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ +#endif +} + + +void test_multiline (void) +{ +#if 0 + x = (first_function () + + second_function ()); /* { dg-warning "test" } */ + +/* { dg-begin-multiline-output "" } + x = (first_function () + ~~~~~~~~~~~~~~~~~ + + second_function ()); + ^ ~~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ +#endif +} + +void test_many_lines (void) +{ +#if 0 + x = (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet, + consectetur, adipiscing, elit, + sed, eiusmod, tempor, + incididunt, ut, labore, et, + dolore, magna, aliqua) + + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit, /* { dg-warning "test" } */ + amet, consectetur, + adipiscing, elit, sed, + eiusmod, tempor, incididunt, + ut, labore, et, dolore, + magna, aliqua)); + +/* { dg-begin-multiline-output "" } + x = (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet, + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + consectetur, adipiscing, elit, + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + sed, eiusmod, tempor, + ~~~~~~~~~~~~~~~~~~~~~ + incididunt, ut, labore, et, + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + dolore, magna, aliqua) + ~~~~~~~~~~~~~~~~~~~~~~ + + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit, + ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + amet, consectetur, + ~~~~~~~~~~~~~~~~~~ + adipiscing, elit, sed, + ~~~~~~~~~~~~~~~~~~~~~~ + eiusmod, tempor, incididunt, + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ut, labore, et, dolore, + ~~~~~~~~~~~~~~~~~~~~~~~ + magna, aliqua)); + ~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ +#endif +} + +void test_richloc_from_proper_range (void) +{ +#if 0 + float f = 98.6f; /* { dg-warning "test" } */ +/* { dg-begin-multiline-output "" } + float f = 98.6f; + ^~~~~ + { dg-end-multiline-output "" } */ +#endif +} + +void test_caret_within_proper_range (void) +{ +#if 0 + float f = foo * bar; /* { dg-warning "17: test" } */ +/* { dg-begin-multiline-output "" } + float f = foo * bar; + ~~~~^~~~~ + { dg-end-multiline-output "" } */ +#endif +} + +void test_very_wide_line (void) +{ +#if 0 + float f = foo * bar; /* { dg-warning "95: test" } */ +/* { dg-begin-multiline-output "" } + float f = foo * bar; + ~~~~^~~~~ + { dg-end-multiline-output "" } */ +#endif +} + +void test_multiple_carets (void) +{ +#if 0 + x = x + y /* { dg-warning "8: test" } */ +/* { dg-begin-multiline-output "" } + x = x + y + A B + { dg-end-multiline-output "" } */ +#endif +} + +void test_caret_on_leading_whitespace (void) +{ +#if 0 + ASSOCIATE (y => x) + y = 5 /* { dg-warning "6: test" } */ +/* { dg-begin-multiline-output "" } + ASSOCIATE (y => x) + 2 + y = 5 + 1 + { dg-end-multiline-output "" } */ +#endif +} diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color.c new file mode 100644 index 0000000..47639b2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color.c @@ -0,0 +1,158 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdiagnostics-show-caret -fplugin-arg-diagnostic_plugin_test_show_locus-color" } */ + +/* 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. */ + +void test_simple (void) +{ +#if 0 + myvar = myvar.x; /* { dg-warning "test" } */ + +/* { dg-begin-multiline-output "" } + myvar = myvar.x; + ~~~~~^~ + + { dg-end-multiline-output "" } */ +#endif +} + +void test_simple_2 (void) +{ +#if 0 + x = first_function () + second_function (); /* { dg-warning "test" } */ + +/* { dg-begin-multiline-output "" } + x = first_function () + second_function (); + ~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~ + + { dg-end-multiline-output "" } */ +#endif +} + + +void test_multiline (void) +{ +#if 0 + x = (first_function () + + second_function ()); /* { dg-warning "test" } */ + +/* { dg-begin-multiline-output "" } + x = (first_function () +  ~~~~~~~~~~~~~~~~~ + + second_function ()); + ^ ~~~~~~~~~~~~~~~~~~ + + { dg-end-multiline-output "" } */ +#endif +} + +void test_many_lines (void) +{ +#if 0 + x = (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet, + consectetur, adipiscing, elit, + sed, eiusmod, tempor, + incididunt, ut, labore, et, + dolore, magna, aliqua) + + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit, /* { dg-warning "test" } */ + amet, consectetur, + adipiscing, elit, sed, + eiusmod, tempor, incididunt, + ut, labore, et, dolore, + magna, aliqua)); + +/* { dg-begin-multiline-output "" } + x = (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet, +  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +  consectetur, adipiscing, elit, +  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +  sed, eiusmod, tempor, +  ~~~~~~~~~~~~~~~~~~~~~ +  incididunt, ut, labore, et, +  ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +  dolore, magna, aliqua) +  ~~~~~~~~~~~~~~~~~~~~~~ + + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit, +  ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +  amet, consectetur, +  ~~~~~~~~~~~~~~~~~~ +  adipiscing, elit, sed, +  ~~~~~~~~~~~~~~~~~~~~~~ +  eiusmod, tempor, incididunt, +  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +  ut, labore, et, dolore, +  ~~~~~~~~~~~~~~~~~~~~~~~ +  magna, aliqua)); + ~~~~~~~~~~~~~~ + + { dg-end-multiline-output "" } */ +#endif +} + +void test_richloc_from_proper_range (void) +{ +#if 0 + float f = 98.6f; /* { dg-warning "test" } */ +/* { dg-begin-multiline-output "" } + float f = 98.6f; + ^~~~~ + + { dg-end-multiline-output "" } */ +#endif +} + +void test_caret_within_proper_range (void) +{ +#if 0 + float f = foo * bar; /* { dg-warning "17: test" } */ +/* { dg-begin-multiline-output "" } + float f = foo * bar; + ~~~~^~~~~ + + { dg-end-multiline-output "" } */ +#endif +} + +void test_very_wide_line (void) +{ +#if 0 + float f = foo * bar; /* { dg-warning "95: test" } */ +/* { dg-begin-multiline-output "" } + float f = foo * bar; + ~~~~^~~~~ + + { dg-end-multiline-output "" } */ +#endif +} + +void test_multiple_carets (void) +{ +#if 0 + x = x + y /* { dg-warning "8: test" } */ +/* { dg-begin-multiline-output "" } + x = x + y + A B + + { dg-end-multiline-output "" } */ +#endif +} + +void test_caret_on_leading_whitespace (void) +{ +#if 0 + ASSOCIATE (y => x) + y = 5 /* { dg-warning "6: test" } */ +/* { dg-begin-multiline-output "" } + ASSOCIATE (y => x) + 2 +  y = 5 + 1 + + { dg-end-multiline-output "" } */ +#endif +} 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 new file mode 100644 index 0000000..3471a4e --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c @@ -0,0 +1,326 @@ +/* { dg-options "-O" } */ + +/* This plugin exercises the diagnostics-printing code. + + The goal is to unit-test the range-printing code without needing any + correct range data within the compiler's IR. We can't use any real + diagnostics for this, so we have to fake it, hence this plugin. + + There are two test files used with this code: + + diagnostic-test-show-locus-ascii-bw.c + ..........................-ascii-color.c + + to exercise uncolored vs colored output by supplying plugin arguments + to hack in the desired behavior: + + -fplugin-arg-diagnostic_plugin_test_show_locus-color + + The test files contain functions, but the body of each + function is disabled using the preprocessor. The plugin detects + the functions by name, and inject diagnostics within them, using + hard-coded locations relative to the top of each function. + + The plugin uses a function "get_loc" below to map from line/column + numbers to source_location, and this relies on input_location being in + the same ordinary line_map as the locations in question. The plugin + runs after parsing, so input_location will be at the end of the file. + + This need for all of the test code to be in a single ordinary line map + means that each test file needs to have a very long line near the top + (potentially to cover the extra byte-count of colorized data), + to ensure that further very long lines don't start a new linemap. + This also means that we can't use macros in the test files. */ + +#include "gcc-plugin.h" +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "stringpool.h" +#include "toplev.h" +#include "basic-block.h" +#include "hash-table.h" +#include "vec.h" +#include "ggc.h" +#include "basic-block.h" +#include "tree-ssa-alias.h" +#include "internal-fn.h" +#include "gimple-fold.h" +#include "tree-eh.h" +#include "gimple-expr.h" +#include "is-a.h" +#include "gimple.h" +#include "gimple-iterator.h" +#include "tree.h" +#include "tree-pass.h" +#include "intl.h" +#include "plugin-version.h" +#include "diagnostic.h" +#include "context.h" +#include "print-tree.h" + +int plugin_is_GPL_compatible; + +const pass_data pass_data_test_show_locus = +{ + GIMPLE_PASS, /* type */ + "test_show_locus", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_test_show_locus : public gimple_opt_pass +{ +public: + pass_test_show_locus(gcc::context *ctxt) + : gimple_opt_pass(pass_data_test_show_locus, ctxt) + {} + + /* opt_pass methods: */ + bool gate (function *) { return true; } + virtual unsigned int execute (function *); + +}; // class pass_test_show_locus + +/* Given LINE_NUM and COL_NUM, generate a source_location in the + current file, relative to input_location. This relies on the + location being expressible in the same ordinary line_map as + input_location (which is typically at the end of the source file + when this is called). Hence the test files we compile with this + plugin must have an initial very long line (to avoid long lines + starting a new line map), and must not use macros. + + COL_NUM uses the Emacs convention of 0-based column numbers. */ + +static source_location +get_loc (unsigned int line_num, unsigned int col_num) +{ + /* Use input_location to get the relevant line_map */ + const struct line_map_ordinary *line_map + = (const line_map_ordinary *)(linemap_lookup (line_table, + input_location)); + + /* Convert from 0-based column numbers to 1-based column numbers. */ + source_location loc + = linemap_position_for_line_and_column (line_map, + line_num, col_num + 1); + + return loc; +} + +/* Was "color" passed in as a plugin argument? */ +static bool force_show_locus_color = false; + +/* We want to verify the colorized output of diagnostic_show_locus, + but turning on colorization for everything confuses "dg-warning" etc. + Hence we special-case it within this plugin by using this modified + version of default_diagnostic_finalizer, which, if "color" is + passed in as a plugin argument turns on colorization, but just + for diagnostic_show_locus. */ + +static void +custom_diagnostic_finalizer (diagnostic_context *context, + diagnostic_info *diagnostic) +{ + bool old_show_color = pp_show_color (context->printer); + if (force_show_locus_color) + pp_show_color (context->printer) = true; + diagnostic_show_locus (context, diagnostic); + pp_show_color (context->printer) = old_show_color; + + pp_destroy_prefix (context->printer); + pp_newline_and_flush (context->printer); +} + +/* Exercise the diagnostic machinery to emit various warnings, + for use by diagnostic-test-show-locus-*.c. + + We inject each warning relative to the start of a function, + which avoids lots of hardcoded absolute locations. */ + +static void +test_show_locus (function *fun) +{ + tree fndecl = fun->decl; + tree identifier = DECL_NAME (fndecl); + const char *fnname = IDENTIFIER_POINTER (identifier); + location_t fnstart = fun->function_start_locus; + int fnstart_line = LOCATION_LINE (fnstart); + + diagnostic_finalizer (global_dc) = custom_diagnostic_finalizer; + + /* Hardcode the "terminal width", to verify the behavior of + very wide lines. */ + global_dc->caret_max_width = 70; + + if (0 == strcmp (fnname, "test_simple")) + { + const int line = fnstart_line + 2; + rich_location richloc (get_loc (line, 15)); + richloc.add_range (get_loc (line, 10), get_loc (line, 14)); + richloc.add_range (get_loc (line, 16), get_loc (line, 16)); + warning_at_rich_loc (&richloc, 0, "test"); + } + + if (0 == strcmp (fnname, "test_simple_2")) + { + const int line = fnstart_line + 2; + rich_location richloc (get_loc (line, 24)); + richloc.add_range (get_loc (line, 6), + get_loc (line, 22)); + richloc.add_range (get_loc (line, 26), + get_loc (line, 43)); + warning_at_rich_loc (&richloc, 0, "test"); + } + + if (0 == strcmp (fnname, "test_multiline")) + { + const int line = fnstart_line + 2; + rich_location richloc (get_loc (line + 1, 7)); + richloc.add_range (get_loc (line, 7), + get_loc (line, 23)); + richloc.add_range (get_loc (line + 1, 9), + get_loc (line + 1, 26)); + warning_at_rich_loc (&richloc, 0, "test"); + } + + if (0 == strcmp (fnname, "test_many_lines")) + { + const int line = fnstart_line + 2; + rich_location richloc (get_loc (line + 5, 7)); + richloc.add_range (get_loc (line, 7), + get_loc (line + 4, 65)); + richloc.add_range (get_loc (line + 5, 9), + get_loc (line + 10, 61)); + warning_at_rich_loc (&richloc, 0, "test"); + } + + /* Example of a rich_location constructed directly from a + source_range where the range is larger than one character. */ + if (0 == strcmp (fnname, "test_richloc_from_proper_range")) + { + const int line = fnstart_line + 2; + source_range src_range; + src_range.m_start = get_loc (line, 12); + src_range.m_finish = get_loc (line, 16); + rich_location richloc (src_range); + warning_at_rich_loc (&richloc, 0, "test"); + } + + /* Example of a single-range location where the range starts + before the caret. */ + if (0 == strcmp (fnname, "test_caret_within_proper_range")) + { + const int line = fnstart_line + 2; + location_t caret = get_loc (line, 16); + source_range src_range; + src_range.m_start = get_loc (line, 12); + src_range.m_finish = get_loc (line, 20); + rich_location richloc (caret); + richloc.set_range (0, src_range, true, false); + warning_at_rich_loc (&richloc, 0, "test"); + } + + /* Example of a very wide line, where the information of interest + is beyond the width of the terminal (hardcoded above). */ + if (0 == strcmp (fnname, "test_very_wide_line")) + { + const int line = fnstart_line + 2; + location_t caret = get_loc (line, 94); + source_range src_range; + src_range.m_start = get_loc (line, 90); + src_range.m_finish = get_loc (line, 98); + rich_location richloc (caret); + richloc.set_range (0, src_range, true, false); + warning_at_rich_loc (&richloc, 0, "test"); + } + + /* Example of multiple carets. */ + if (0 == strcmp (fnname, "test_multiple_carets")) + { + const int line = fnstart_line + 2; + location_t caret_a = get_loc (line, 7); + location_t caret_b = get_loc (line, 11); + rich_location richloc (caret_a); + richloc.add_range (caret_b, caret_b, true); + global_dc->caret_chars[0] = 'A'; + global_dc->caret_chars[1] = 'B'; + warning_at_rich_loc (&richloc, 0, "test"); + global_dc->caret_chars[0] = '^'; + global_dc->caret_chars[1] = '^'; + } + + /* Example of two carets where both carets appear to have an off-by-one + error appearing one column early. + Seen with gfortran.dg/associate_5.f03. + In an earlier version of the printer, the printing of caret 0 aka + "1" was suppressed due to it appearing within the leading whitespace + before the text in its line. Ensure that we at least faithfully + print both carets, at the given (erroneous) locations. */ + if (0 == strcmp (fnname, "test_caret_on_leading_whitespace")) + { + const int line = fnstart_line + 3; + location_t caret_a = get_loc (line, 5); + location_t caret_b = get_loc (line - 1, 19); + rich_location richloc (caret_a); + richloc.add_range (caret_b, caret_b, true); + global_dc->caret_chars[0] = '1'; + global_dc->caret_chars[1] = '2'; + warning_at_rich_loc (&richloc, 0, "test"); + global_dc->caret_chars[0] = '^'; + global_dc->caret_chars[1] = '^'; + } +} + +unsigned int +pass_test_show_locus::execute (function *fun) +{ + test_show_locus (fun); + return 0; +} + +static gimple_opt_pass * +make_pass_test_show_locus (gcc::context *ctxt) +{ + return new pass_test_show_locus (ctxt); +} + +int +plugin_init (struct plugin_name_args *plugin_info, + struct plugin_gcc_version *version) +{ + struct register_pass_info pass_info; + const char *plugin_name = plugin_info->base_name; + int argc = plugin_info->argc; + struct plugin_argument *argv = plugin_info->argv; + + if (!plugin_default_version_check (version, &gcc_version)) + return 1; + + /* For now, tell the dc to expect ranges and thus to colorize the source + lines, not just the carets/underlines. This will be redundant + once the C frontend generates ranges. */ + global_dc->colorize_source_p = true; + + for (int i = 0; i < argc; i++) + { + if (0 == strcmp (argv[i].key, "color")) + force_show_locus_color = true; + } + + pass_info.pass = make_pass_test_show_locus (g); + pass_info.reference_pass_name = "ssa"; + pass_info.ref_pass_instance_number = 1; + pass_info.pos_op = PASS_POS_INSERT_AFTER; + register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, + &pass_info); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp index 39fab6e..941bccc 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp @@ -63,6 +63,9 @@ set plugin_test_list [list \ { start_unit_plugin.c start_unit-test-1.c } \ { finish_unit_plugin.c finish_unit-test-1.c } \ { 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 } \ ] foreach plugin_test $plugin_test_list { diff --git a/gcc/testsuite/lib/gcc-dg.exp b/gcc/testsuite/lib/gcc-dg.exp index 7c1ab85..8cc1d87 100644 --- a/gcc/testsuite/lib/gcc-dg.exp +++ b/gcc/testsuite/lib/gcc-dg.exp @@ -29,6 +29,7 @@ load_lib libgloss.exp load_lib target-libpath.exp load_lib torture-options.exp load_lib fortran-modules.exp +load_lib multiline.exp # We set LC_ALL and LANG to C so that we get the same error messages as expected. setenv LC_ALL C diff --git a/gcc/tree-diagnostic.c b/gcc/tree-diagnostic.c index 135f142..02009d8 100644 --- a/gcc/tree-diagnostic.c +++ b/gcc/tree-diagnostic.c @@ -289,7 +289,7 @@ default_tree_printer (pretty_printer *pp, text_info *text, const char *spec, } if (set_locus) - text->set_location (0, DECL_SOURCE_LOCATION (t)); + text->set_location (0, DECL_SOURCE_LOCATION (t), true); if (DECL_P (t)) { diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index ce3f6a8..29bc48a 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -3592,7 +3592,7 @@ void percent_K_format (text_info *text) { tree t = va_arg (*text->args_ptr, tree), block; - text->set_location (0, EXPR_LOCATION (t)); + text->set_location (0, EXPR_LOCATION (t), true); gcc_assert (pp_ti_abstract_origin (text) != NULL); block = TREE_BLOCK (t); *pp_ti_abstract_origin (text) = NULL; diff --git a/libcpp/errors.c b/libcpp/errors.c index a33196e..c351c11 100644 --- a/libcpp/errors.c +++ b/libcpp/errors.c @@ -57,7 +57,8 @@ cpp_diagnostic (cpp_reader * pfile, int level, int reason, if (!pfile->cb.error) abort (); - ret = pfile->cb.error (pfile, level, reason, src_loc, 0, _(msgid), ap); + rich_location richloc (src_loc); + ret = pfile->cb.error (pfile, level, reason, &richloc, _(msgid), ap); return ret; } @@ -139,7 +140,9 @@ cpp_diagnostic_with_line (cpp_reader * pfile, int level, int reason, if (!pfile->cb.error) abort (); - ret = pfile->cb.error (pfile, level, reason, src_loc, column, _(msgid), ap); + rich_location richloc (src_loc); + richloc.override_column (column); + ret = pfile->cb.error (pfile, level, reason, &richloc, _(msgid), ap); return ret; } diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h index 5eaea6b..a2bdfa0 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -573,9 +573,9 @@ struct cpp_callbacks /* Called to emit a diagnostic. This callback receives the translated message. */ - bool (*error) (cpp_reader *, int, int, source_location, unsigned int, + bool (*error) (cpp_reader *, int, int, rich_location *, const char *, va_list *) - ATTRIBUTE_FPTR_PRINTF(6,0); + ATTRIBUTE_FPTR_PRINTF(5,0); /* Callbacks for when a macro is expanded, or tested (whether defined or not at the time) in #ifdef, #ifndef or "defined". */ diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h index 09378f9..84a5ab7 100644 --- a/libcpp/include/line-map.h +++ b/libcpp/include/line-map.h @@ -131,6 +131,35 @@ typedef unsigned int linenum_type; libcpp/location-example.txt. */ typedef unsigned int source_location; +/* A range of source locations. + + Ranges are closed: + m_start is the first location within the range, + m_finish is the last location within the range. + + We may need a more compact way to store these, but for now, + let's do it the simple way, as a pair. */ +struct GTY(()) source_range +{ + source_location m_start; + source_location m_finish; + + void debug (const char *msg) const; + + /* We avoid using constructors, since various structs that + don't yet have constructors will embed instances of + source_range. */ + + /* Make a source_range from a source_location. */ + static source_range from_location (source_location loc) + { + source_range result; + result.m_start = loc; + result.m_finish = loc; + return result; + } +}; + /* Memory allocation function typedef. Works like xrealloc. */ typedef void *(*line_map_realloc) (void *, size_t); @@ -1028,6 +1057,175 @@ typedef struct bool sysp; } expanded_location; +/* Both gcc and emacs number source *lines* starting at 1, but + they have differing conventions for *columns*. + + GCC uses a 1-based convention for source columns, + whereas Emacs's M-x column-number-mode uses a 0-based convention. + + For example, an error in the initial, left-hand + column of source line 3 is reported by GCC as: + + some-file.c:3:1: error: ...etc... + + On navigating to the location of that error in Emacs + (e.g. via "next-error"), + the locus is reported in the Mode Line + (assuming M-x column-number-mode) as: + + some-file.c 10% (3, 0) + + i.e. "3:1:" in GCC corresponds to "(3, 0)" in Emacs. */ + +/* Ranges are closed + m_start is the first location within the range, and + m_finish is the last location within the range. */ +struct location_range +{ + expanded_location m_start; + expanded_location m_finish; + + /* Should a caret be drawn for this range? Typically this is + true for the 0th range, and false for subsequent ranges, + but the Fortran frontend overrides this for rendering things like: + + x = x + y + 1 2 + Error: Shapes for operands at (1) and (2) are not conformable + + where "1" and "2" are notionally carets. */ + bool m_show_caret_p; + expanded_location m_caret; +}; + +/* A "rich" source code location, for use when printing diagnostics. + A rich_location has one or more ranges, each optionally with + a caret. Typically the zeroth range has a caret; other ranges + sometimes have carets. + + The "primary" location of a rich_location is the caret of range 0, + used for determining the line/column when printing diagnostic + text, such as: + + some-file.c:3:1: error: ...etc... + + Additional ranges may be added to help the user identify other + pertinent clauses in a diagnostic. + + rich_location instances are intended to be allocated on the stack + when generating diagnostics, and to be short-lived. + + Examples of rich locations + -------------------------- + + Example A + ********* + int i = "foo"; + ^ + This "rich" location is simply a single range (range 0), with + caret = start = finish at the given point. + + Example B + ********* + a = (foo && bar) + ~~~~~^~~~~~~ + This rich location has a single range (range 0), with the caret + at the first "&", and the start/finish at the parentheses. + Compare with example C below. + + Example C + ********* + a = (foo && bar) + ~~~ ^~ ~~~ + This rich location has three ranges: + - Range 0 has its caret and start location at the first "&" and + end at the second "&. + - Range 1 has its start and finish at the "f" and "o" of "foo"; + the caret is not flagged for display, but is perhaps at the "f" + of "foo". + - Similarly, range 2 has its start and finish at the "b" and "r" of + "bar"; the caret is not flagged for display, but is perhaps at the + "b" of "bar". + Compare with example B above. + + Example D (Fortran frontend) + **************************** + x = x + y + 1 2 + This rich location has range 0 at "1", and range 1 at "2". + Both are flagged for caret display. Both ranges have start/finish + equal to their caret point. The frontend overrides the diagnostic + context's default caret character for these ranges. + + Example E + ********* + printf ("arg0: %i arg1: %s arg2: %i", + ^~ + 100, 101, 102); + ~~~ + This rich location has two ranges: + - range 0 is at the "%s" with start = caret = "%" and finish at + the "s". + - range 1 has start/finish covering the "101" and is not flagged for + caret printing; it is perhaps at the start of "101". */ + +class rich_location +{ + public: + /* Constructors. */ + + /* Constructing from a location. */ + rich_location (source_location loc); + + /* Constructing from a source_range. */ + rich_location (source_range src_range); + + /* Accessors. */ + source_location get_loc () const { return m_loc; } + + source_location *get_loc_addr () { return &m_loc; } + + void + add_range (source_location start, source_location finish, + bool show_caret_p = false); + + void + add_range (source_range src_range, + bool show_caret_p = false); + + void + add_range (location_range *src_range); + + void + set_range (unsigned int idx, source_range src_range, + bool show_caret_p, bool overwrite_loc_p); + + unsigned int get_num_locations () const { return m_num_ranges; } + + location_range *get_range (unsigned int idx) + { + linemap_assert (idx < m_num_ranges); + return &m_ranges[idx]; + } + + expanded_location lazily_expand_location (); + + void + override_column (int column); + +public: + static const int MAX_RANGES = 3; + +protected: + source_location m_loc; + + unsigned int m_num_ranges; + location_range m_ranges[MAX_RANGES]; + + bool m_have_expanded_location; + expanded_location m_expanded_location; +}; + /* This is enum is used by the function linemap_resolve_location below. The meaning of the values is explained in the comment of that function. */ @@ -1173,4 +1371,13 @@ void linemap_dump (FILE *, struct line_maps *, unsigned, bool); specifies how many macro maps to dump. */ void line_table_dump (FILE *, struct line_maps *, unsigned int, unsigned int); +/* The rich_location class requires a way to expand source_location instances. + We would directly use expand_location_to_spelling_point, which is + implemented in gcc/input.c, but we also need to use it for rich_location + within genmatch.c. + Hence we require client code of libcpp to implement the following + symbol. */ +extern expanded_location +linemap_client_expand_location_to_spelling_point (source_location ); + #endif /* !LIBCPP_LINE_MAP_H */ diff --git a/libcpp/line-map.c b/libcpp/line-map.c index 84403de..3c19f93 100644 --- a/libcpp/line-map.c +++ b/libcpp/line-map.c @@ -1755,3 +1755,133 @@ line_table_dump (FILE *stream, struct line_maps *set, unsigned int num_ordinary, fprintf (stream, "\n"); } } + +/* class rich_location. */ + +/* Construct a rich_location with location LOC as its initial range. */ + +rich_location::rich_location (source_location loc) : + m_loc (loc), + m_num_ranges (0), + m_have_expanded_location (false) +{ + /* Set up the 0th range: */ + add_range (loc, loc, true); + m_ranges[0].m_caret = lazily_expand_location (); +} + +/* Construct a rich_location with source_range SRC_RANGE as its + initial range. */ + +rich_location::rich_location (source_range src_range) +: m_loc (src_range.m_start), + m_num_ranges (0), + m_have_expanded_location (false) +{ + /* Set up the 0th range: */ + add_range (src_range, true); +} + +/* Get an expanded_location for this rich_location's primary + location. */ + +expanded_location +rich_location::lazily_expand_location () +{ + if (!m_have_expanded_location) + { + m_expanded_location + = linemap_client_expand_location_to_spelling_point (m_loc); + m_have_expanded_location = true; + } + + return m_expanded_location; +} + +/* Set the column of the primary location. */ + +void +rich_location::override_column (int column) +{ + lazily_expand_location (); + m_expanded_location.column = column; +} + +/* Add the given range. */ + +void +rich_location::add_range (source_location start, source_location finish, + bool show_caret_p) +{ + linemap_assert (m_num_ranges < MAX_RANGES); + + location_range *range = &m_ranges[m_num_ranges++]; + range->m_start = linemap_client_expand_location_to_spelling_point (start); + range->m_finish = linemap_client_expand_location_to_spelling_point (finish); + range->m_caret = range->m_start; + range->m_show_caret_p = show_caret_p; +} + +/* Add the given range. */ + +void +rich_location::add_range (source_range src_range, bool show_caret_p) +{ + linemap_assert (m_num_ranges < MAX_RANGES); + + add_range (src_range.m_start, src_range.m_finish, show_caret_p); +} + +void +rich_location::add_range (location_range *src_range) +{ + linemap_assert (m_num_ranges < MAX_RANGES); + + m_ranges[m_num_ranges++] = *src_range; +} + +/* Add or overwrite the range given by IDX. It must either + overwrite an existing range, or add one *exactly* on the end of + the array. + + This is primarily for use by gcc when implementing diagnostic + format decoders e.g. the "+" in the C/C++ frontends, for handling + format codes like "%q+D" (which writes the source location of a + tree back into range 0 of the rich_location). + + If SHOW_CARET_P is true, then the range should be rendered with + a caret at its starting location. This + is for use by the Fortran frontend, for implementing the + "%C" and "%L" format codes. */ + +void +rich_location::set_range (unsigned int idx, source_range src_range, + bool show_caret_p, bool overwrite_loc_p) +{ + linemap_assert (idx < MAX_RANGES); + + /* We can either overwrite an existing range, or add one exactly + on the end of the array. */ + linemap_assert (idx <= m_num_ranges); + + location_range *locrange = &m_ranges[idx]; + locrange->m_start + = linemap_client_expand_location_to_spelling_point (src_range.m_start); + locrange->m_finish + = linemap_client_expand_location_to_spelling_point (src_range.m_finish); + + locrange->m_show_caret_p = show_caret_p; + if (overwrite_loc_p) + locrange->m_caret = locrange->m_start; + + /* Are we adding a range onto the end? */ + if (idx == m_num_ranges) + m_num_ranges = idx + 1; + + if (idx == 0 && overwrite_loc_p) + { + m_loc = src_range.m_start; + /* Mark any cached value here as dirty. */ + m_have_expanded_location = false; + } +}