From b4c7ca2ef3915a0b2292499dabcaabefdca04d89 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Thu, 10 Oct 2019 17:03:46 +0000 Subject: [PATCH] Documentation hyperlinks for [-Wname-of-option] (PR 87488) This patch uses the support for pretty-printing escaped URLs added in the previous patch (PR 87488) so that in a sufficiently capable terminal the [-Wname-of-option] emitted at the end of each diagnostic becomes a hyperlink to the documentation for that option on the GCC website. I've tested it with Fedora 30's GNOME Terminal (3.32.2 with VTE 0.56.3); the option text is printed with a dotted underline, hovering shows the URL, and on right-clicking it offers menu items to visit/copy the URL. gcc/ChangeLog: PR 87488 * Makefile.in (CFLAGS-opts.o): Pass in DOCUMENTATION_ROOT_URL via -D. * configure.ac (--with-documentation-root-url): New option. * configure: Regenerate. * diagnostic-format-json.cc (json_end_diagnostic): If there is an option URL, add it as a new string field of the diagnostic option. * diagnostic.c (diagnostic_initialize): Initialize get_option_url. (print_option_information): If get_option_url is non-NULL, call it, and if the result is non-NULL, potentially emit an escape sequence to markup the option text with the resulting URL. * diagnostic.h (diagnostic_context::get_option_url): New callback. * doc/invoke.texi (-fdiagnostics-format=): Add "option_url" to example of JSON output. * opts-diagnostic.h (get_option_url): New decl. * opts.c (get_option_url): New function. * toplev.c (general_init): Initialize the get_option_url callback. gcc/testsuite/ChangeLog: PR 87488 * c-c++-common/diagnostic-format-json-2.c: Expect an "option_url" field. * c-c++-common/diagnostic-format-json-3.c: Likewise. * gfortran.dg/diagnostic-format-json-2.F90: Likewise. * gfortran.dg/diagnostic-format-json-3.F90: Likewise. * jit.dg/test-error-array-bounds.c (create_code): Ensure that error messages don't contain escaped URLs. From-SVN: r276843 --- gcc/ChangeLog | 20 +++++++++++++++ gcc/Makefile.in | 2 ++ gcc/configure | 25 +++++++++++++++++-- gcc/configure.ac | 14 +++++++++++ gcc/diagnostic-format-json.cc | 11 ++++++++ gcc/diagnostic.c | 12 +++++++++ gcc/diagnostic.h | 6 +++++ gcc/doc/invoke.texi | 1 + gcc/opts-diagnostic.h | 3 +++ gcc/opts.c | 21 ++++++++++++++++ gcc/testsuite/ChangeLog | 11 ++++++++ .../c-c++-common/diagnostic-format-json-2.c | 1 + .../c-c++-common/diagnostic-format-json-3.c | 1 + .../c-c++-common/diagnostic-format-json-4.c | 10 ++++++-- .../gfortran.dg/diagnostic-format-json-2.F90 | 1 + .../gfortran.dg/diagnostic-format-json-3.F90 | 1 + .../jit.dg/test-error-array-bounds.c | 5 ++-- gcc/toplev.c | 1 + 18 files changed, 140 insertions(+), 6 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 9c34213997f..4409d4595c4 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -5,6 +5,26 @@ predicate. (vcond): Use vcond_comparison_operator. +2019-10-10 David Malcolm + + PR 87488 + * Makefile.in (CFLAGS-opts.o): Pass in DOCUMENTATION_ROOT_URL via + -D. + * configure.ac (--with-documentation-root-url): New option. + * configure: Regenerate. + * diagnostic-format-json.cc (json_end_diagnostic): If there is an + option URL, add it as a new string field of the diagnostic option. + * diagnostic.c (diagnostic_initialize): Initialize get_option_url. + (print_option_information): If get_option_url is non-NULL, call + it, and if the result is non-NULL, potentially emit an escape + sequence to markup the option text with the resulting URL. + * diagnostic.h (diagnostic_context::get_option_url): New callback. + * doc/invoke.texi (-fdiagnostics-format=): Add "option_url" to + example of JSON output. + * opts-diagnostic.h (get_option_url): New decl. + * opts.c (get_option_url): New function. + * toplev.c (general_init): Initialize the get_option_url callback. + 2019-10-10 David Malcolm PR 87488 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 59adfaa3862..c82858fa93e 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -2142,6 +2142,8 @@ lto-wrapper$(exeext): $(LTO_WRAPPER_OBJS) libcommon-target.a $(LIBDEPS) $(LTO_WRAPPER_OBJS) libcommon-target.a $(LIBS) mv -f T$@ $@ +CFLAGS-opts.o += -DDOCUMENTATION_ROOT_URL=\"@DOCUMENTATION_ROOT_URL@\" + # Files used by all variants of C or by the stand-alone pre-processor. CFLAGS-c-family/c-opts.o += @TARGET_SYSTEM_ROOT_DEFINE@ diff --git a/gcc/configure b/gcc/configure index dc9f5fefaa8..9de9ef85f24 100755 --- a/gcc/configure +++ b/gcc/configure @@ -815,6 +815,7 @@ accel_dir_suffix real_target_noncanonical enable_as_accelerator gnat_install_lib +DOCUMENTATION_ROOT_URL REPORT_BUGS_TEXI REPORT_BUGS_TO PKGVERSION @@ -960,6 +961,7 @@ enable_gcov with_specs with_pkgversion with_bugurl +with_documentation_root_url enable_languages with_multilib_list with_zstd @@ -1788,6 +1790,8 @@ Optional Packages: --with-specs=SPECS add SPECS to driver command-line processing --with-pkgversion=PKG Use PKG in the version string in place of "GCC" --with-bugurl=URL Direct users to URL to report a bug + --with-documentation-root-url=URL + Root for documentation URLs --with-multilib-list select multilibs (AArch64, SH and x86-64 only) --with-zstd=PATH specify prefix directory for installed zstd library. Equivalent to --with-zstd-include=PATH/include plus @@ -7802,6 +7806,23 @@ fi +# Allow overriding the default URL for documentation + +# Check whether --with-documentation-root-url was given. +if test "${with_documentation_root_url+set}" = set; then : + withval=$with_documentation_root_url; case "$withval" in + yes) as_fn_error $? "documentation root URL not specified" "$LINENO" 5 ;; + no) as_fn_error $? "documentation root URL not specified" "$LINENO" 5 ;; + *) DOCUMENTATION_ROOT_URL="$withval" + ;; + esac +else + DOCUMENTATION_ROOT_URL="https://gcc.gnu.org/onlinedocs/gcc/" + +fi + + + # Sanity check enable_languages in case someone does not run the toplevel # configure # script. # Check whether --enable-languages was given. @@ -18830,7 +18851,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 18833 "configure" +#line 18854 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -18936,7 +18957,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 18939 "configure" +#line 18960 "configure" #include "confdefs.h" #if HAVE_DLFCN_H diff --git a/gcc/configure.ac b/gcc/configure.ac index 54a67159b98..62f4b2651cc 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -960,6 +960,20 @@ AC_SUBST(CONFIGURE_SPECS) ACX_PKGVERSION([GCC]) ACX_BUGURL([https://gcc.gnu.org/bugs/]) +# Allow overriding the default URL for documentation +AC_ARG_WITH(documentation-root-url, + AS_HELP_STRING([--with-documentation-root-url=URL], + [Root for documentation URLs]), + [case "$withval" in + yes) AC_MSG_ERROR([documentation root URL not specified]) ;; + no) AC_MSG_ERROR([documentation root URL not specified]) ;; + *) DOCUMENTATION_ROOT_URL="$withval" + ;; + esac], + DOCUMENTATION_ROOT_URL="https://gcc.gnu.org/onlinedocs/gcc/" +) +AC_SUBST(DOCUMENTATION_ROOT_URL) + # Sanity check enable_languages in case someone does not run the toplevel # configure # script. AC_ARG_ENABLE(languages, diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.cc index 53c3b630b1c..eb999521a54 100644 --- a/gcc/diagnostic-format-json.cc +++ b/gcc/diagnostic-format-json.cc @@ -154,6 +154,17 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic, free (option_text); } + if (context->get_option_url) + { + char *option_url = context->get_option_url (context, + diagnostic->option_index); + if (option_url) + { + diag_obj->set ("option_url", new json::string (option_url)); + free (option_url); + } + } + /* If we've already emitted a diagnostic within this auto_diagnostic_group, then add diag_obj to its "children" array. */ if (cur_group) diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c index 467cc3963e6..a29bcf155e2 100644 --- a/gcc/diagnostic.c +++ b/gcc/diagnostic.c @@ -200,6 +200,7 @@ diagnostic_initialize (diagnostic_context *context, int n_opts) context->option_enabled = NULL; context->option_state = NULL; context->option_name = NULL; + context->get_option_url = NULL; context->last_location = UNKNOWN_LOCATION; context->last_module = 0; context->x_data = NULL; @@ -912,11 +913,22 @@ print_option_information (diagnostic_context *context, if (option_text) { + char *option_url = NULL; + if (context->get_option_url) + option_url = context->get_option_url (context, + diagnostic->option_index); pretty_printer *pp = context->printer; pp_string (pp, " ["); pp_string (pp, colorize_start (pp_show_color (pp), diagnostic_kind_color[diagnostic->kind])); + if (option_url) + pp_begin_url (pp, option_url); pp_string (pp, option_text); + if (option_url) + { + pp_end_url (pp); + free (option_url); + } pp_string (pp, colorize_stop (pp_show_color (pp))); pp_character (pp, ']'); free (option_text); diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index f0ea8e8bd4c..91e4c509605 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -194,6 +194,12 @@ struct diagnostic_context May be passed 0 as well as the index of a particular option. */ char *(*option_name) (diagnostic_context *, int, diagnostic_t, diagnostic_t); + /* Client hook to return a URL describing the option that controls + a diagnostic. Returns malloced memory. May return NULL if no URL + is available. May be passed 0 as well as the index of a + particular option. */ + char *(*get_option_url) (diagnostic_context *, int); + /* Auxiliary data for client. */ void *x_data; diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index bdbcd95d559..085e8b0b2ca 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -4116,6 +4116,7 @@ might be printed in JSON form (after formatting) like this: ], "message": "this \u2018if\u2019 clause does not guard...", "option": "-Wmisleading-indentation", + "option_url": "https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wmisleading-indentation", "children": [ @{ "kind": "note", diff --git a/gcc/opts-diagnostic.h b/gcc/opts-diagnostic.h index 08110afe3e0..3f58ae91bf6 100644 --- a/gcc/opts-diagnostic.h +++ b/gcc/opts-diagnostic.h @@ -22,4 +22,7 @@ along with GCC; see the file COPYING3. If not see extern char *option_name (diagnostic_context *context, int option_index, diagnostic_t orig_diag_kind, diagnostic_t diag_kind); + +extern char *get_option_url (diagnostic_context *context, int option_index); + #endif diff --git a/gcc/opts.c b/gcc/opts.c index 1070b9de955..32869ca4919 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -3218,3 +3218,24 @@ option_name (diagnostic_context *context, int option_index, else return NULL; } + +/* Return malloced memory for a URL describing the option OPTION_INDEX + which enabled a diagnostic (context CONTEXT). */ + +char * +get_option_url (diagnostic_context *, int option_index) +{ + if (option_index) + /* DOCUMENTATION_ROOT_URL should be supplied via -D by the Makefile + (see --with-documentation-root-url). + + Expect an anchor of the form "index-Wfoo" e.g. + , and thus an id within + the URL of "#index-Wformat". */ + return concat (DOCUMENTATION_ROOT_URL, + "Warning-Options.html", + "#index", cl_options[option_index].opt_text, + NULL); + else + return NULL; +} diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index b6e9d88aa40..c3f588058f3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2019-10-10 David Malcolm + + PR 87488 + * c-c++-common/diagnostic-format-json-2.c: Expect an "option_url" + field. + * c-c++-common/diagnostic-format-json-3.c: Likewise. + * gfortran.dg/diagnostic-format-json-2.F90: Likewise. + * gfortran.dg/diagnostic-format-json-3.F90: Likewise. + * jit.dg/test-error-array-bounds.c (create_code): Ensure that + error messages don't contain escaped URLs. + 2019-10-10 David Malcolm PR 87488 diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-json-2.c b/gcc/testsuite/c-c++-common/diagnostic-format-json-2.c index 239c75ed769..557ccf8378b 100644 --- a/gcc/testsuite/c-c++-common/diagnostic-format-json-2.c +++ b/gcc/testsuite/c-c++-common/diagnostic-format-json-2.c @@ -10,6 +10,7 @@ /* { dg-regexp "\"kind\": \"warning\"" } */ /* { dg-regexp "\"message\": \"#warning message\"" } */ /* { dg-regexp "\"option\": \"-Wcpp\"" } */ +/* { dg-regexp "\"option_url\": \"https:\[^\n\r\"\]*#index-Wcpp\"" } */ /* { dg-regexp "\"caret\": \{" } */ /* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-2.c\"" } */ diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-json-3.c b/gcc/testsuite/c-c++-common/diagnostic-format-json-3.c index 1c54ecacc9f..378205c5bf5 100644 --- a/gcc/testsuite/c-c++-common/diagnostic-format-json-3.c +++ b/gcc/testsuite/c-c++-common/diagnostic-format-json-3.c @@ -10,6 +10,7 @@ /* { dg-regexp "\"kind\": \"error\"" } */ /* { dg-regexp "\"message\": \"#warning message\"" } */ /* { dg-regexp "\"option\": \"-Werror=cpp\"" } */ +/* { dg-regexp "\"option_url\": \"https:\[^\n\r\"\]*#index-Wcpp\"" } */ /* { dg-regexp "\"caret\": \{" } */ /* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-3.c\"" } */ diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-json-4.c b/gcc/testsuite/c-c++-common/diagnostic-format-json-4.c index 1c3b0343b6f..2738be6548f 100644 --- a/gcc/testsuite/c-c++-common/diagnostic-format-json-4.c +++ b/gcc/testsuite/c-c++-common/diagnostic-format-json-4.c @@ -30,13 +30,12 @@ int test (void) /* { dg-regexp "\"line\": 8" } */ /* { dg-regexp "\"column\": 10" } */ -/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */ - /* The outer diagnostic. */ /* { dg-regexp "\"kind\": \"warning\"" } */ /* { dg-regexp "\"message\": \"this 'if' clause does not guard...\"" } */ /* { dg-regexp "\"option\": \"-Wmisleading-indentation\"" } */ +/* { dg-regexp "\"option_url\": \"https:\[^\n\r\"\]*#index-Wmisleading-indentation\"" } */ /* { dg-regexp "\"caret\": \{" } */ /* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-4.c\"" } */ @@ -48,6 +47,13 @@ int test (void) /* { dg-regexp "\"line\": 6" } */ /* { dg-regexp "\"column\": 4" } */ +/* More from the nested diagnostic (we can't guarantee what order the + "file" keys are consumed). */ + +/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */ + +/* More from the outer diagnostic. */ + /* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */ /* { dg-regexp "\"children\": \[\[\{\}, \]*\]" } */ diff --git a/gcc/testsuite/gfortran.dg/diagnostic-format-json-2.F90 b/gcc/testsuite/gfortran.dg/diagnostic-format-json-2.F90 index a6d27d9e16a..bebcf68d431 100644 --- a/gcc/testsuite/gfortran.dg/diagnostic-format-json-2.F90 +++ b/gcc/testsuite/gfortran.dg/diagnostic-format-json-2.F90 @@ -10,6 +10,7 @@ ! { dg-regexp "\"kind\": \"warning\"" } ! { dg-regexp "\"message\": \"#warning message\"" } ! { dg-regexp "\"option\": \"-Wcpp\"" } +! { dg-regexp "\"option_url\": \"\[^\n\r\"\]*#index-Wcpp\"" } ! { dg-regexp "\"caret\": \{" } ! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-2.F90\"" } diff --git a/gcc/testsuite/gfortran.dg/diagnostic-format-json-3.F90 b/gcc/testsuite/gfortran.dg/diagnostic-format-json-3.F90 index 702a937d9c6..7ab78eb570b 100644 --- a/gcc/testsuite/gfortran.dg/diagnostic-format-json-3.F90 +++ b/gcc/testsuite/gfortran.dg/diagnostic-format-json-3.F90 @@ -10,6 +10,7 @@ ! { dg-regexp "\"kind\": \"error\"" } ! { dg-regexp "\"message\": \"#warning message\"" } ! { dg-regexp "\"option\": \"-Werror=cpp\"" } +! { dg-regexp "\"option_url\": \"\[^\n\r\"\]*#index-Wcpp\"" } ! { dg-regexp "\"caret\": \{" } ! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-3.F90\"" } diff --git a/gcc/testsuite/jit.dg/test-error-array-bounds.c b/gcc/testsuite/jit.dg/test-error-array-bounds.c index 889c51737a4..cd9361fba1d 100644 --- a/gcc/testsuite/jit.dg/test-error-array-bounds.c +++ b/gcc/testsuite/jit.dg/test-error-array-bounds.c @@ -20,9 +20,10 @@ create_code (gcc_jit_context *ctxt, void *user_data) gcc_jit_context_add_command_line_option (ctxt, "-Warray-bounds"); gcc_jit_context_add_command_line_option (ctxt, "-ftree-vrp"); - /* Ensure that the error message doesn't contain colorization codes, - even if run at a TTY. */ + /* Ensure that the error message doesn't contain colorization codes + or escaped URLs, even if run at a TTY. */ gcc_jit_context_add_command_line_option (ctxt, "-fdiagnostics-color=never"); + gcc_jit_context_add_command_line_option (ctxt, "-fdiagnostics-urls=never"); gcc_jit_type *char_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CHAR); diff --git a/gcc/toplev.c b/gcc/toplev.c index b85d52ca9b7..1c7002f5c37 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -1110,6 +1110,7 @@ general_init (const char *argv0, bool init_signals) global_dc->option_enabled = option_enabled; global_dc->option_state = &global_options; global_dc->option_name = option_name; + global_dc->get_option_url = get_option_url; if (init_signals) { -- 2.30.2