From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 7FE0A3858CD1 for ; Thu, 2 Nov 2023 13:19:40 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7FE0A3858CD1 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 7FE0A3858CD1 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1698931189; cv=none; b=JPN6+PT4HBDLcy5kTToKPHI3UGbcux1T7b8IiIsI2k+AIoTViiPhifH0hkfh+KKVpXXEP3g/6cj8lyq2OaR5QYSn2MCB6SyjjtdrsPkk6w16VVrzaE0rmvAgsdkguIlCHPaiqO22m3QSYOQ2knS6qobYbFNjzDeqLBjHug0ZgTs= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1698931189; c=relaxed/simple; bh=sjrn4sbCiRLeKz8WGDC4ic6OCjErHzsdgyUf0mhxWc8=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=WunnZ/SkPpgdrUQp57chcmeKv0xD067nFLaJR/SUuFmXrQFlSAYLASQfmLrV3RNlTFmrI/iT2KSQteCgLCL12a4CXCTyT1KprhbyOIJKCJK/mmqnIvCeL/TJWNS1ww3/9w9z8eRGNiPWLoZLC0g0ABpQJjrsuJD9zg4B7vrYDOE= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1698931180; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=AUwoH2W/vGHILnb9FqSfcajb79nYUk8U5nWkKdVXrMY=; b=X15wUHWo+oe1X0G/NO+ymF/wFArlnymG1NmCpKmu4CvxLzKyzq0Mzb+jwre8Fr8rNoHR3R gqJ4xslzYc81pQLhmVCfAHX/7oEKysCOd33vJpbNoMw9hrKBrkyQtFChcGeE8S85ndfjgf C8xixRyfOIE7MpVzxUXrC1OFfvaKUAU= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-438-8aYvuoZ0MHeAE0VfvgQrvw-1; Thu, 02 Nov 2023 09:19:37 -0400 X-MC-Unique: 8aYvuoZ0MHeAE0VfvgQrvw-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 50DEE82BA86 for ; Thu, 2 Nov 2023 13:19:37 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.22.8.62]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2146F1C060BA; Thu, 2 Nov 2023 13:19:37 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [PATCH 3/4] diagnostics: add automatic URL-ification within messages Date: Thu, 2 Nov 2023 09:19:32 -0400 Message-Id: <20231102131933.2161191-4-dmalcolm@redhat.com> In-Reply-To: <20231102131933.2161191-1-dmalcolm@redhat.com> References: <20231102131933.2161191-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.7 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: In r10-3781-gd26082357676a3 GCC's pretty-print framework gained the ability to emit embedding URLs via escape sequences for marking up text output.. In r10-3783-gb4c7ca2ef3915a GCC started using this for the [-Wname-of-option] emitted at the end of each diagnostic so that it becomes a hyperlink to the documentation for that option on the GCC website. This makes it much more convenient for the user to locate pertinent documentation when a diagnostic is emitted. The above involved special-casing in one specific place, but there is plenty of quoted text throughout GCC's diagnostic messages that could usefully have a documentation URL: references to options, pragmas, etc This patch adds a new optional "urlifier" parameter to pp_format. The idea is that a urlifier object has responsibility for mapping from quoted strings in diagnostic messages to URLs, and pp_format has the ability to automatically add URL escapes for strings that the urlifier gives it URLs for. For example, given the format string: "%<#pragma pack%> has no effect with %<-fpack-struct%>" with this patch GCC is able to automatically linkify the "#pragma pack" text to https://gcc.gnu.org/onlinedocs/gcc/Structure-Layout-Pragmas.html and the "-fpack-struct" text to: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fpack-struct and we don't have to modify the format string itself. This is only done for the pp_format within diagnostic_report_diagnostic i.e. just for the primary message in each diagnostics, and not for other places within GCC that use pp_format internally. "urlifier" is an abstract base class, with a GCC-specific subclass implementing the logic for generating URLs into GCC's HTML documentation via binary search in a data table. This patch implements the gcc_urlifier with a small table generated by hand; the data table in this patch only covers enough pragmas and options to allow undoing some of the hardcoding from the previous pragma-parsing patch. I have a followup patch that scripts the creation of this data by directly scraping the output of "make html", thus automating all this, and (I hope) minimizing the work of ensuring that documentation URLs emitted by GCC match the generated documentation. gcc/ChangeLog: * Makefile.in (GCC_OBJS): Add gcc-urlifier.o. (OBJS): Likewise. gcc/c-family/ChangeLog: * c-pragma.cc:: Eliminate uses of %{ and %} and get_doc_url in all places where it's just the name of the pragma (or of an option). (handle_pragma_push_options): Fix missing "GCC" in name of pragma in "junk" message. (handle_pragma_pop_options): Likewise. gcc/ChangeLog: * diagnostic.cc: Include "pretty-print-urlifier.h". (diagnostic_initialize): Initialize m_urlifier. (diagnostic_finish): Clean up m_urlifier (diagnostic_report_diagnostic): Pass context->m_urlifier to pp_format. * diagnostic.h (diagnostic_context::m_urlifier): New field. * gcc-urlifier.cc: New file. * gcc-urlifier.def: New file. * gcc-urlifier.h: New file. * gcc.cc: Include "gcc-urlifier.h". (driver::global_initializations): Initialize global_dc->m_urlifier. * pretty-print-urlifier.h: New file. * pretty-print.cc: Include "pretty-print-urlifier.h". (obstack_append_string): New. (urlify_quoted_string): New. (pp_format): Add "urlifier" param and use it to implement optional urlification of quoted text strings. (pp_output_formatted_text): Make buffer a const pointer. (selftest::pp_printf_with_urlifier): New. (selftest::test_urlification): New. (selftest::pretty_print_cc_tests): Call it. * pretty-print.h (class urlifier): New forward declaration. (pp_format): Add optional urlifier param. * selftest-run-tests.cc (selftest::run_tests): Call selftest::gcc_urlifier_cc_tests . * selftest.h (selftest::gcc_urlifier_cc_tests): New decl. * toplev.cc: Include "gcc-urlifier.h". (general_init): Initialize global_dc->m_urlifier. --- gcc/Makefile.in | 3 +- gcc/c-family/c-pragma.cc | 73 ++++------- gcc/diagnostic.cc | 8 +- gcc/diagnostic.h | 4 + gcc/gcc-urlifier.cc | 159 +++++++++++++++++++++++ gcc/gcc-urlifier.def | 20 +++ gcc/gcc-urlifier.h | 26 ++++ gcc/gcc.cc | 2 + gcc/pretty-print-urlifier.h | 33 +++++ gcc/pretty-print.cc | 242 +++++++++++++++++++++++++++++++++++- gcc/pretty-print.h | 5 +- gcc/selftest-run-tests.cc | 1 + gcc/selftest.h | 1 + gcc/toplev.cc | 2 + 14 files changed, 520 insertions(+), 59 deletions(-) create mode 100644 gcc/gcc-urlifier.cc create mode 100644 gcc/gcc-urlifier.def create mode 100644 gcc/gcc-urlifier.h create mode 100644 gcc/pretty-print-urlifier.h diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 41ed8163cd8..ff77d3cdc64 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1279,7 +1279,7 @@ FORTRAN_TARGET_OBJS=@fortran_target_objs@ RUST_TARGET_OBJS=@rust_target_objs@ # Object files for gcc many-languages driver. -GCC_OBJS = gcc.o gcc-main.o ggc-none.o +GCC_OBJS = gcc.o gcc-main.o ggc-none.o gcc-urlifier.o c-family-warn = $(STRICT_WARN) @@ -1452,6 +1452,7 @@ OBJS = \ function-tests.o \ fwprop.o \ gcc-rich-location.o \ + gcc-urlifier.o \ gcse.o \ gcse-common.o \ ggc-common.o \ diff --git a/gcc/c-family/c-pragma.cc b/gcc/c-family/c-pragma.cc index 904a1fcd55d..d762fa68285 100644 --- a/gcc/c-family/c-pragma.cc +++ b/gcc/c-family/c-pragma.cc @@ -291,17 +291,14 @@ public: if (TREE_CODE (x) == INTEGER_CST) return true; - label_text doc_url (get_doc_url ()); warning_at (loc, OPT_Wpragmas, - "ignoring malformed %<%{#pragma pack%}%>:" - " invalid constant", - doc_url.get ()); + "ignoring malformed %<#pragma pack%>:" + " invalid constant"); return false; } void complain_about_malformed_action (pack_action action) const { - label_text doc_url (get_doc_url ()); if (action != pack_action::pop) warning_at (m_last_loc, OPT_Wpragmas, "ignoring malformed %<#pragma pack(push[, id][, ])%>"); @@ -360,11 +357,10 @@ handle_pragma_pack (cpp_reader *) action = pack_action::pop; else { - label_text doc_url (p.get_doc_url ()); warning_at (loc, OPT_Wpragmas, - "ignoring malformed %<%{#pragma pack%}%>:" + "ignoring malformed %<#pragma pack%>:" " unknown action %qE", - doc_url.get (), x); + x); return; } @@ -401,11 +397,9 @@ handle_pragma_pack (cpp_reader *) } else { - label_text doc_url (p.get_doc_url ()); warning_at (loc, OPT_Wpragmas, - "ignoring malformed %<%{#pragma pack%}%>:" - " expected %<)%>, integer, %, or %", - doc_url.get ()); + "ignoring malformed %<#pragma pack%>:" + " expected %<)%>, integer, %, or %"); return; } @@ -414,13 +408,9 @@ handle_pragma_pack (cpp_reader *) if (flag_pack_struct) { - label_text pragma_doc_url (p.get_doc_url ()); - label_text option_doc_url - (get_doc_url ("gcc/Code-Gen-Options.html#index-fpack-struct")); warning (OPT_Wpragmas, - "%<%{#pragma pack%}%> has no effect with %<%{-fpack-struct%}%>" - " - ignored", - pragma_doc_url.get (), option_doc_url.get ()); + "%<#pragma pack%> has no effect with %<-fpack-struct%>" + " - ignored"); return; } @@ -444,11 +434,10 @@ handle_pragma_pack (cpp_reader *) /* FALLTHRU */ default: { - label_text doc_url (p.get_doc_url ()); warning_at (align_loc, OPT_Wpragmas, - "ignoring malformed %<%{#pragma pack%}%>:" + "ignoring malformed %<#pragma pack%>:" " alignment must be a small power of two, not %d", - doc_url.get (), align); + align); return; } } @@ -609,11 +598,10 @@ handle_pragma_weak (cpp_reader *) if (!VAR_OR_FUNCTION_DECL_P (decl)) { auto_diagnostic_group d; - label_text doc_url (p.get_doc_url ()); if (warning_at (name_loc, OPT_Wpragmas, - "%<%{#pragma weak%}%> declaration of %qD not allowed," + "%<#pragma weak%> declaration of %qD not allowed," " ignored", - doc_url.get (), decl)) + decl)) if (DECL_SOURCE_LOCATION (decl) != UNKNOWN_LOCATION) inform (DECL_SOURCE_LOCATION (decl), "%qD is not a variable or function", @@ -668,11 +656,9 @@ public: void complain_about_arg (location_t arg_loc) const { - label_text doc_url (get_doc_url ()); warning_at (arg_loc, OPT_Wpragmas, - "ignoring malformed %<%{#pragma scalar_storage_order%}%>:" - " expected %, %, or %", - doc_url.get ()); + "ignoring malformed %<#pragma scalar_storage_order%>:" + " expected %, %, or %"); } }; @@ -962,12 +948,8 @@ push_visibility (const char *str, int kind, const pragma_parser *p) " %, %, %" " or %")) if (p) - { - label_text doc_url (p->get_doc_url ()); - inform (loc, - "ignoring malformed %<%{#pragma GCC visibility%}%>", - doc_url.get ()); - } + inform (loc, + "ignoring malformed %<#pragma GCC visibility%>"); return; } visibility_options.inpragma = 1; @@ -1014,11 +996,9 @@ handle_pragma_visibility (cpp_reader *) } if (bad == action) { - label_text doc_url (p.get_doc_url ()); warning (OPT_Wpragmas, - "%<%{#pragma GCC visibility%}%> must be followed by % " - "or %", - doc_url.get ()); + "%<#pragma GCC visibility%> must be followed by % " + "or %"); return; } else @@ -1027,11 +1007,8 @@ handle_pragma_visibility (cpp_reader *) { if (! pop_visibility (0)) { - label_text doc_url (p.get_doc_url ()); warning (OPT_Wpragmas, - "no matching push for" - " %<%{#pragma GCC visibility pop%}%>", - doc_url.get ()); + "no matching push for %<#pragma GCC visibility pop%>"); return; } } @@ -1511,7 +1488,7 @@ handle_pragma_push_options (cpp_reader *) token = pragma_lex (&x); if (token != CPP_EOF) { - warning (OPT_Wpragmas, "junk at end of %<#pragma push_options%>"); + warning (OPT_Wpragmas, "junk at end of %<#pragma GCC push_options%>"); return; } @@ -1548,7 +1525,7 @@ handle_pragma_pop_options (cpp_reader *) token = pragma_lex (&x); if (token != CPP_EOF) { - warning (OPT_Wpragmas, "junk at end of %<#pragma pop_options%>"); + warning (OPT_Wpragmas, "junk at end of %<#pragma GCC pop_options%>"); return; } @@ -1653,10 +1630,8 @@ handle_pragma_message (cpp_reader *) message = x; else { - label_text doc_url (p.get_doc_url ()); warning_at (loc, OPT_Wpragmas, - "expected a string after %<%{#pragma message%}%>", - doc_url.get ()); + "expected a string after %<#pragma message%>"); return; } if (!p.require_close_paren ()) @@ -1673,10 +1648,8 @@ handle_pragma_message (cpp_reader *) } else { - label_text doc_url (p.get_doc_url ()); warning_at (loc, OPT_Wpragmas, - "expected a string after %<%{#pragma message%}%>", - doc_url.get ()); + "expected a string after %<#pragma message%>"); return; } diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc index 0f392358aef..e3c440a9e8d 100644 --- a/gcc/diagnostic.cc +++ b/gcc/diagnostic.cc @@ -44,6 +44,7 @@ along with GCC; see the file COPYING3. If not see #include "opts.h" #include "cpplib.h" #include "text-art/theme.h" +#include "pretty-print-urlifier.h" #ifdef HAVE_TERMIOS_H # include @@ -193,6 +194,7 @@ diagnostic_initialize (diagnostic_context *context, int n_opts) context->option_state = NULL; context->option_name = NULL; context->get_option_url = NULL; + context->m_urlifier = nullptr; context->last_location = UNKNOWN_LOCATION; context->last_module = 0; context->x_data = NULL; @@ -347,6 +349,9 @@ diagnostic_finish (diagnostic_context *context) delete context->m_client_data_hooks; context->m_client_data_hooks = NULL; } + + delete context->m_urlifier; + context->m_urlifier = nullptr; } /* Initialize DIAGNOSTIC, where the message MSG has already been @@ -1574,7 +1579,8 @@ diagnostic_report_diagnostic (diagnostic_context *context, context->m_output_format->on_begin_group (); context->diagnostic_group_emission_count++; - pp_format (context->printer, &diagnostic->message); + pp_format (context->printer, &diagnostic->message, + context->m_urlifier); context->m_output_format->on_begin_diagnostic (diagnostic); pp_output_formatted_text (context->printer); if (context->show_cwe) diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index a2c8740cbd0..d79369289bc 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -354,6 +354,10 @@ struct diagnostic_context particular option. */ char *(*get_option_url) (diagnostic_context *, int); + /* An optional hook for adding URLs to quoted text strings in + diagnostics. Only used for the main diagnostic message. */ + urlifier *m_urlifier; + void (*print_path) (diagnostic_context *, const diagnostic_path *); json::value *(*make_json_for_path) (diagnostic_context *, const diagnostic_path *); diff --git a/gcc/gcc-urlifier.cc b/gcc/gcc-urlifier.cc new file mode 100644 index 00000000000..269246bc703 --- /dev/null +++ b/gcc/gcc-urlifier.cc @@ -0,0 +1,159 @@ +/* Automatic generation of links into GCC's documentation. + Copyright (C) 2023 Free Software Foundation, Inc. + Contributed by David Malcolm . + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "pretty-print.h" +#include "pretty-print-urlifier.h" +#include "gcc-urlifier.h" +#include "selftest.h" + +namespace { + +/* Concrete subclass of urlifier for generating links into + GCC's HTML documentation. */ + +class gcc_urlifier : public urlifier +{ +public: + char *get_url_for_quoted_text (const char *p, size_t sz) const final override; + + const char *get_url_suffix_for_quoted_text (const char *p, size_t sz) const; + const char *get_url_suffix_for_quoted_text (const char *p) const; + +private: + static char * + make_doc_url (const char *doc_url_suffix); +}; + +/* class gcc_urlifier : public urlifier. */ + +#define DOC_URL(QUOTED_TEXT, URL_SUFFIX) \ + { (QUOTED_TEXT), (URL_SUFFIX) } + +const struct +{ + const char *quoted_text; + const char *url_suffix; +} doc_urls[] = { + +#include "gcc-urlifier.def" + +}; + +char * +gcc_urlifier::get_url_for_quoted_text (const char *p, size_t sz) const +{ + if (const char *url_suffix = get_url_suffix_for_quoted_text (p, sz)) + return make_doc_url (url_suffix); + return nullptr; +} + +const char * +gcc_urlifier::get_url_suffix_for_quoted_text (const char *p, size_t sz) const +{ + /* Binary search. This assumes that the quoted_text fields of doc_urls + are in sorted order. */ + int min = 0; + int max = ARRAY_SIZE (doc_urls) - 1; + while (true) + { + if (min > max) + return nullptr; + int midpoint = (min + max) / 2; + gcc_assert ((size_t)midpoint < ARRAY_SIZE (doc_urls)); + int cmp = strncmp (p, doc_urls[midpoint].quoted_text, sz); + if (cmp == 0) + { + if (doc_urls[midpoint].quoted_text[sz] == '\0') + return doc_urls[midpoint].url_suffix; + else + max = midpoint - 1; + } + else if (cmp < 0) + max = midpoint - 1; + else + min = midpoint + 1; + } + return nullptr; +} + +const char * +gcc_urlifier::get_url_suffix_for_quoted_text (const char *p) const +{ + return get_url_suffix_for_quoted_text (p, strlen (p)); +} + +char * +gcc_urlifier::make_doc_url (const char *doc_url_suffix) +{ + if (!doc_url_suffix) + return nullptr; + + return concat (DOCUMENTATION_ROOT_URL, doc_url_suffix, nullptr); +} + +} // anonymous namespace + +urlifier * +make_gcc_urlifier () +{ + return new gcc_urlifier (); +} + +#if CHECKING_P + +namespace selftest { + +/* Selftests. */ + +/* Run all of the selftests within this file. */ + +void +gcc_urlifier_cc_tests () +{ + /* Check that doc_urls.quoted_text is sorted. */ + for (size_t idx = 1; idx < ARRAY_SIZE (doc_urls); idx++) + gcc_assert (strcmp (doc_urls[idx - 1].quoted_text, + doc_urls[idx].quoted_text) + < 0); + + gcc_urlifier u; + + ASSERT_EQ (u.get_url_suffix_for_quoted_text (""), nullptr); + ASSERT_EQ (u.get_url_suffix_for_quoted_text (")"), nullptr); + + ASSERT_STREQ (u.get_url_suffix_for_quoted_text ("#pragma message"), + "gcc/Diagnostic-Pragmas.html"); + + // Incomplete prefix of a quoted_text + ASSERT_EQ (u.get_url_suffix_for_quoted_text ("#pragma mess"), nullptr); + + /* Check that every element is findable. */ + for (size_t idx = 0; idx < ARRAY_SIZE (doc_urls); idx++) + ASSERT_STREQ + (u.get_url_suffix_for_quoted_text (doc_urls[idx].quoted_text), + doc_urls[idx].url_suffix); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ diff --git a/gcc/gcc-urlifier.def b/gcc/gcc-urlifier.def new file mode 100644 index 00000000000..360de930e9e --- /dev/null +++ b/gcc/gcc-urlifier.def @@ -0,0 +1,20 @@ +/* Keep this file sorted. */ +DOC_URL ("#pragma GCC diagnostic", "gcc/Diagnostic-Pragmas.html"), +DOC_URL ("#pragma GCC diagnostic ignored_attributes", "gcc/Diagnostic-Pragmas.html"), +DOC_URL ("#pragma GCC ivdep", "gcc/Loop-Specific-Pragmas.html#index-pragma-GCC-ivdep"), +DOC_URL ("#pragma GCC novector", "gcc/Loop-Specific-Pragmas.html#index-pragma-GCC-novector"), +DOC_URL ("#pragma GCC optimize", "gcc/Function-Specific-Option-Pragmas.html#index-pragma-GCC-optimize"), +DOC_URL ("#pragma GCC pop_options", "gcc/Push_002fPop-Macro-Pragmas.html"), +DOC_URL ("#pragma GCC push_options", "gcc/Push_002fPop-Macro-Pragmas.html"), +DOC_URL ("#pragma GCC reset_options", "gcc/Function-Specific-Option-Pragmas.html#index-pragma-GCC-reset_005foptions"), +DOC_URL ("#pragma GCC target", "gcc/Function-Specific-Option-Pragmas.html#index-pragma-GCC-target"), +DOC_URL ("#pragma GCC unroll", "gcc/Loop-Specific-Pragmas.html#index-pragma-GCC-unroll-n"), +DOC_URL ("#pragma GCC visibility", "gcc/Visibility-Pragmas.html"), +DOC_URL ("#pragma GCC visibility pop", "gcc/Visibility-Pragmas.html"), +DOC_URL ("#pragma message", "gcc/Diagnostic-Pragmas.html"), +DOC_URL ("#pragma pack", "gcc/Structure-Layout-Pragmas.html"), +DOC_URL ("#pragma redefine_extname", "gcc/Symbol-Renaming-Pragmas.html"), +DOC_URL ("#pragma scalar_storage_order", "gcc/Structure-Layout-Pragmas.html"), +DOC_URL ("#pragma weak", "gcc/Weak-Pragmas.html"), +DOC_URL ("--version", "gcc/Overall-Options.html#index-version"), +DOC_URL ("-fpack-struct", "gcc/Code-Gen-Options.html#index-fpack-struct"), diff --git a/gcc/gcc-urlifier.h b/gcc/gcc-urlifier.h new file mode 100644 index 00000000000..614e1c64b94 --- /dev/null +++ b/gcc/gcc-urlifier.h @@ -0,0 +1,26 @@ +/* Automatic generation of links into GCC's documentation. + Copyright (C) 2023 Free Software Foundation, Inc. + Contributed by David Malcolm . + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef GCC_GCC_URLIFIER_H +#define GCC_GCC_URLIFIER_H + +extern urlifier *make_gcc_urlifier (); + +#endif /* GCC_GCC_URLIFIER_H */ diff --git a/gcc/gcc.cc b/gcc/gcc.cc index 884284e66b4..2bc2fc0e72d 100644 --- a/gcc/gcc.cc +++ b/gcc/gcc.cc @@ -47,6 +47,7 @@ compilation is specified by a string called a "spec". */ #include "opts-jobserver.h" #include "common/common-target.h" #include "diagnostic-text-art.h" +#include "gcc-urlifier.h" #ifndef MATH_LIBRARY #define MATH_LIBRARY "m" @@ -8293,6 +8294,7 @@ driver::global_initializations () diagnostic_initialize (global_dc, 0); diagnostic_color_init (global_dc); diagnostic_urls_init (global_dc); + global_dc->m_urlifier = make_gcc_urlifier (); #ifdef GCC_DRIVER_HOST_INITIALIZATION /* Perform host dependent initialization when needed. */ diff --git a/gcc/pretty-print-urlifier.h b/gcc/pretty-print-urlifier.h new file mode 100644 index 00000000000..bdb7fca00d4 --- /dev/null +++ b/gcc/pretty-print-urlifier.h @@ -0,0 +1,33 @@ +/* Copyright (C) 2023 Free Software Foundation, Inc. + Contributed by David Malcolm + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef GCC_PRETTY_PRINT_URLIFIER_H +#define GCC_PRETTY_PRINT_URLIFIER_H + +/* Abstract base class for optional use in pp_format for adding URLs + to quoted text strings. */ + +class urlifier +{ +public: + virtual ~urlifier () {} + virtual char *get_url_for_quoted_text (const char *p, size_t sz) const = 0; +}; + +#endif /* GCC_PRETTY_PRINT_URLIFIER_H */ diff --git a/gcc/pretty-print.cc b/gcc/pretty-print.cc index 80780cfd7b8..9a4827622dc 100644 --- a/gcc/pretty-print.cc +++ b/gcc/pretty-print.cc @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "intl.h" #include "pretty-print.h" +#include "pretty-print-urlifier.h" #include "diagnostic-color.h" #include "diagnostic-event-id.h" #include "selftest.h" @@ -1022,6 +1023,95 @@ pp_indent (pretty_printer *pp) static const char *get_end_url_string (pretty_printer *); +/* Append STR to OSTACK, without a null-terminator. */ + +static void +obstack_append_string (obstack *ostack, const char *str) +{ + obstack_grow (ostack, str, strlen (str)); +} + +/* Given quoted text starting at QUOTED_TEXT_START_IDX within PP's buffer, + potentially use URLIFIER (if non-null) to see if there's a URL for the + quoted text. + + If so, replace the quoted part of the text in the buffer with a URLified + version of the text, using PP's settings. + + For example, given this is the buffer: + "this is a test `hello world" + .................^~~~~~~~~~~ + with the quoted text starting at the 'h' of "hello world", the buffer + becomes: + "this is a test `BEGIN_URL(URL)hello worldEND(URL)" + .................^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + .................-----------replacement----------- +*/ + +static void +urlify_quoted_string (pretty_printer *pp, + const urlifier *urlifier, + size_t quoted_text_start_idx) +{ + if (pp->url_format == URL_FORMAT_NONE) + return; + if (!urlifier) + return; + + output_buffer * const buffer = pp_buffer (pp); + + /* Get end of quoted string. */ + const size_t close_quote_idx + = obstack_object_size (&buffer->chunk_obstack); + gcc_assert (close_quote_idx >= quoted_text_start_idx); + if (close_quote_idx == quoted_text_start_idx) + /* Empty quoted string; do nothing. */ + return; + const size_t len = close_quote_idx - quoted_text_start_idx; + const char *start = (buffer->chunk_obstack.object_base + + quoted_text_start_idx); + char *url = urlifier->get_url_for_quoted_text (start, len); + if (!url) + /* No URL for this quoted text; do nothing. */ + return; + + /* Stash a copy of the quoted text. */ + char *text = xstrndup (start, len); + + /* Replace quoted text... */ + buffer->chunk_obstack.next_free -= len; + + /* ...with URLified version of the text. */ + /* Begin URL. */ + switch (pp->url_format) + { + default: + case URL_FORMAT_NONE: + gcc_unreachable (); + case URL_FORMAT_ST: + obstack_append_string (&buffer->chunk_obstack, + "\33]8;;"); + obstack_append_string (&buffer->chunk_obstack, url); + obstack_append_string (&buffer->chunk_obstack, + "\33\\"); + break; + case URL_FORMAT_BEL: + obstack_append_string (&buffer->chunk_obstack, + "\33]8;;"); + obstack_append_string (&buffer->chunk_obstack, url); + obstack_append_string (&buffer->chunk_obstack, + "\a"); + break; + } + /* Add the text back. */ + obstack_append_string (&buffer->chunk_obstack, text); + /* End URL. */ + obstack_append_string (&buffer->chunk_obstack, + get_end_url_string (pp)); + free (text); + free (url); +} + /* The following format specifiers are recognized as being client independent: %d, %i: (signed) integer in base ten. %u: unsigned integer in base ten. @@ -1064,12 +1154,25 @@ static const char *get_end_url_string (pretty_printer *); /* Formatting phases 1 and 2: render TEXT->format_spec plus text->m_args_ptr into a series of chunks in pp_buffer (PP)->args[]. - Phase 3 is in pp_output_formatted_text. */ + Phase 3 is in pp_output_formatted_text. + + If URLIFIER is non-NULL, then use it to add URLs for quoted + strings, so that e.g. + "before % after" + with a URLIFIER that has a URL for "quoted" might be emitted as: + "before `BEGIN_URL(http://example.com)quotedEND_URL' after" + This only works for message fragments that are: + - quoted entirely in phase 1 (e.g. "%"), or + - quoted entirely in phase 2 (e.g. "%qs"), + but *not* in strings that use a mixture of both phases + (e.g. "%"). */ void -pp_format (pretty_printer *pp, text_info *text) +pp_format (pretty_printer *pp, + text_info *text, + const urlifier *urlifier) { - output_buffer *buffer = pp_buffer (pp); + output_buffer * const buffer = pp_buffer (pp); const char *p; const char **args; struct chunk_info *new_chunk_array; @@ -1079,6 +1182,9 @@ pp_format (pretty_printer *pp, text_info *text) bool any_unnumbered = false, any_numbered = false; const char **formatters[PP_NL_ARGMAX]; + /* Keep track of location of last "%", if any. */ + size_t quoted_text_start_idx = 0; + /* Allocate a new chunk structure. */ new_chunk_array = XOBNEW (&buffer->chunk_obstack, struct chunk_info); new_chunk_array->prev = buffer->cur_chunk_array; @@ -1122,11 +1228,21 @@ pp_format (pretty_printer *pp, text_info *text) = colorize_start (pp_show_color (pp), "quote"); obstack_grow (&buffer->chunk_obstack, colorstr, strlen (colorstr)); p++; + + /* Stash offset of start of quoted string. */ + quoted_text_start_idx + = obstack_object_size (&buffer->chunk_obstack); + continue; } case '>': { + if (quoted_text_start_idx) + { + urlify_quoted_string (pp, urlifier, quoted_text_start_idx); + quoted_text_start_idx = 0; + } const char *colorstr = colorize_stop (pp_show_color (pp)); obstack_grow (&buffer->chunk_obstack, colorstr, strlen (colorstr)); } @@ -1168,6 +1284,12 @@ pp_format (pretty_printer *pp, text_info *text) obstack_1grow (&buffer->chunk_obstack, '\0'); gcc_assert (chunk < PP_NL_ARGMAX * 2); args[chunk++] = XOBFINISH (&buffer->chunk_obstack, const char *); + /* We can't yet handle urlifying quoted strings that use + a combination of phase 1 and phase 2 e.g. + "did you mean %<-%s%>". + Stop any phase 1 quoted text if there are going to be any + phase 2 quoted chunks. */ + quoted_text_start_idx = 0; break; } @@ -1270,6 +1392,7 @@ pp_format (pretty_printer *pp, text_info *text) bool plus = false; bool hash = false; bool quote = false; + quoted_text_start_idx = 0; /* We do not attempt to enforce any ordering on the modifier characters. */ @@ -1310,7 +1433,11 @@ pp_format (pretty_printer *pp, text_info *text) gcc_assert (!wide || precision == 0); if (quote) - pp_begin_quote (pp, pp_show_color (pp)); + { + pp_begin_quote (pp, pp_show_color (pp)); + quoted_text_start_idx + = obstack_object_size (&buffer->chunk_obstack); + } switch (*p) { @@ -1480,7 +1607,14 @@ pp_format (pretty_printer *pp, text_info *text) } if (quote) - pp_end_quote (pp, pp_show_color (pp)); + { + if (quoted_text_start_idx) + { + urlify_quoted_string (pp, urlifier, quoted_text_start_idx); + quoted_text_start_idx = 0; + } + pp_end_quote (pp, pp_show_color (pp)); + } obstack_1grow (&buffer->chunk_obstack, '\0'); *formatters[argno] = XOBFINISH (&buffer->chunk_obstack, const char *); @@ -1507,7 +1641,7 @@ void pp_output_formatted_text (pretty_printer *pp) { unsigned int chunk; - output_buffer *buffer = pp_buffer (pp); + output_buffer * const buffer = pp_buffer (pp); struct chunk_info *chunk_array = buffer->cur_chunk_array; const char **args = chunk_array->args; @@ -2640,6 +2774,101 @@ test_null_urls () } } +/* Verify that URLification works as expected. */ + +static void +pp_printf_with_urlifier (pretty_printer *pp, + const urlifier *urlifier, + const char *msg, ...) +{ + va_list ap; + + va_start (ap, msg); + text_info text (msg, &ap, errno); + pp_format (pp, &text, urlifier); + pp_output_formatted_text (pp); + va_end (ap); +} + + +void +test_urlification () +{ + class test_urlifier : public urlifier + { + public: + char * + get_url_for_quoted_text (const char *p, size_t sz) const final override + { + if (!strncmp (p, "-foption", sz)) + return xstrdup ("http://example.com"); + return nullptr; + } + }; + + auto_fix_quotes fix_quotes; + const test_urlifier urlifier; + + /* Uses of "%<" and "%>". */ + { + { + pretty_printer pp; + pp.url_format = URL_FORMAT_NONE; + pp_printf_with_urlifier (&pp, &urlifier, + "foo %<-foption%> % bar"); + ASSERT_STREQ ("foo `-foption' `unrecognized' bar", + pp_formatted_text (&pp)); + } + { + pretty_printer pp; + pp.url_format = URL_FORMAT_ST; + pp_printf_with_urlifier (&pp, &urlifier, + "foo %<-foption%> % bar"); + ASSERT_STREQ + ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'" + " `unrecognized' bar", + pp_formatted_text (&pp)); + } + { + pretty_printer pp; + pp.url_format = URL_FORMAT_BEL; + pp_printf_with_urlifier (&pp, &urlifier, + "foo %<-foption%> % bar"); + ASSERT_STREQ + ("foo `\33]8;;http://example.com\a-foption\33]8;;\a'" + " `unrecognized' bar", + pp_formatted_text (&pp)); + } + } + + /* Use of "%qs". */ + { + pretty_printer pp; + pp.url_format = URL_FORMAT_ST; + pp_printf_with_urlifier (&pp, &urlifier, + "foo %qs %qs bar", + "-foption", "unrecognized"); + ASSERT_STREQ + ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'" + " `unrecognized' bar", + pp_formatted_text (&pp)); + } + + /* Mixed usage of %< and %s, where the quoted string is built between + a mixture of phase 1 and phase 2. */ + { + pretty_printer pp; + pp.url_format = URL_FORMAT_ST; + pp_printf_with_urlifier (&pp, &urlifier, + "foo %<-f%s%> bar", + "option"); + /* We don't support this, but make sure we don't crash. */ + ASSERT_STREQ + ("foo `-foption' bar", + pp_formatted_text (&pp)); + } +} + /* Test multibyte awareness. */ static void test_utf8 () { @@ -2690,6 +2919,7 @@ pretty_print_cc_tests () test_prefixes_and_wrapping (); test_urls (); test_null_urls (); + test_urlification (); test_utf8 (); } diff --git a/gcc/pretty-print.h b/gcc/pretty-print.h index 8759f0def38..9ba2c0a406e 100644 --- a/gcc/pretty-print.h +++ b/gcc/pretty-print.h @@ -228,6 +228,8 @@ class format_postprocessor /* True if colors should be shown. */ #define pp_show_color(PP) (PP)->show_color +class urlifier; + /* The data structure that contains the bare minimum required to do proper pretty-printing. Clients may derived from this structure and add additional fields they need. */ @@ -404,7 +406,8 @@ extern void pp_verbatim (pretty_printer *, const char *, ...) ATTRIBUTE_GCC_PPDIAG(2,3); extern void pp_flush (pretty_printer *); extern void pp_really_flush (pretty_printer *); -extern void pp_format (pretty_printer *, text_info *); +extern void pp_format (pretty_printer *, text_info *, + const urlifier * = nullptr); extern void pp_output_formatted_text (pretty_printer *); extern void pp_format_verbatim (pretty_printer *, text_info *); diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc index e2fc8f84b1b..cb53e90ebaf 100644 --- a/gcc/selftest-run-tests.cc +++ b/gcc/selftest-run-tests.cc @@ -120,6 +120,7 @@ selftest::run_tests () lang_hooks.run_lang_selftests (); text_art_tests (); + gcc_urlifier_cc_tests (); /* Run the analyzer selftests (if enabled). */ ana::selftest::run_analyzer_selftests (); diff --git a/gcc/selftest.h b/gcc/selftest.h index 20d522afda4..b0a4142fe34 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -231,6 +231,7 @@ extern void et_forest_cc_tests (); extern void fibonacci_heap_cc_tests (); extern void fold_const_cc_tests (); extern void function_tests_cc_tests (); +extern void gcc_urlifier_cc_tests (); extern void ggc_tests_cc_tests (); extern void gimple_cc_tests (); extern void hash_map_tests_cc_tests (); diff --git a/gcc/toplev.cc b/gcc/toplev.cc index 9a734890a18..6ca25c6f909 100644 --- a/gcc/toplev.cc +++ b/gcc/toplev.cc @@ -88,6 +88,7 @@ along with GCC; see the file COPYING3. If not see #include "ipa-modref.h" #include "ipa-param-manipulation.h" #include "dbgcnt.h" +#include "gcc-urlifier.h" #include "selftest.h" @@ -1049,6 +1050,7 @@ general_init (const char *argv0, bool init_signals) global_dc->option_state = &global_options; global_dc->option_name = option_name; global_dc->get_option_url = get_option_url; + global_dc->m_urlifier = make_gcc_urlifier (); if (init_signals) { -- 2.26.3