1/* Automatic generation of links into GCC's documentation.
2 Copyright (C) 2023-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#include "config.h"
22#include "system.h"
23#include "coretypes.h"
24#include "pretty-print.h"
25#include "pretty-print-urlifier.h"
26#include "gcc-urlifier.h"
27#include "opts.h"
28#include "options.h"
29#include "selftest.h"
30
31namespace {
32
33/* Concrete subclass of urlifier for generating links into
34 GCC's HTML documentation. */
35
36class gcc_urlifier : public urlifier
37{
38public:
39 gcc_urlifier (unsigned int lang_mask)
40 : m_lang_mask (lang_mask)
41 {}
42
43 char *get_url_for_quoted_text (const char *p, size_t sz) const final override;
44
45 label_text get_url_suffix_for_quoted_text (const char *p, size_t sz) const;
46 /* We use ATTRIBUTE_UNUSED as this helper is called only from ASSERTs. */
47 label_text get_url_suffix_for_quoted_text (const char *p) const ATTRIBUTE_UNUSED;
48
49private:
50 label_text get_url_suffix_for_option (const char *p, size_t sz) const;
51
52 static char *
53 make_doc_url (const char *doc_url_suffix);
54
55 unsigned int m_lang_mask;
56};
57
58/* class gcc_urlifier : public urlifier. */
59
60/* Manage a hard-coded mapping from quoted string to URL suffixes
61 in gcc-urlifier.def */
62
63#define DOC_URL(QUOTED_TEXT, URL_SUFFIX) \
64 { (QUOTED_TEXT), (URL_SUFFIX) }
65
66static const struct
67{
68 const char *quoted_text;
69 const char *url_suffix;
70} doc_urls[] = {
71
72#include "gcc-urlifier.def"
73
74};
75
76/* Implementation of urlifier::get_url_for_quoted_text vfunc for GCC
77 diagnostics. */
78
79char *
80gcc_urlifier::get_url_for_quoted_text (const char *p, size_t sz) const
81{
82 label_text url_suffix = get_url_suffix_for_quoted_text (p, sz);
83 if (url_suffix.get ())
84 return make_doc_url (doc_url_suffix: url_suffix.get ());
85 return nullptr;
86}
87
88/* Look for a URL for the quoted string (P, SZ).
89 Return the url suffix if found, or nullptr otherwise. */
90
91label_text
92gcc_urlifier::get_url_suffix_for_quoted_text (const char *p, size_t sz) const
93{
94 if (sz == 0)
95 return label_text ();
96
97 /* If this is an option, look up the option and see if we have
98 a URL for it. */
99 if (p[0] == '-')
100 {
101 label_text suffix = get_url_suffix_for_option (p, sz);
102 if (suffix.get ())
103 return suffix;
104 }
105
106 /* Otherwise, look within the hard-coded data table in gcc-urlifier.def.
107
108 Binary search. This assumes that the quoted_text fields of doc_urls
109 are in sorted order. */
110 int min = 0;
111 int max = ARRAY_SIZE (doc_urls) - 1;
112 while (true)
113 {
114 if (min > max)
115 return label_text ();
116 int midpoint = (min + max) / 2;
117 gcc_assert ((size_t)midpoint < ARRAY_SIZE (doc_urls));
118 int cmp = strncmp (s1: p, s2: doc_urls[midpoint].quoted_text, n: sz);
119 if (cmp == 0)
120 {
121 if (doc_urls[midpoint].quoted_text[sz] == '\0')
122 return label_text::borrow (buffer: doc_urls[midpoint].url_suffix);
123 else
124 max = midpoint - 1;
125 }
126 else if (cmp < 0)
127 max = midpoint - 1;
128 else
129 min = midpoint + 1;
130 }
131
132 /* Not found. */
133 return label_text ();
134}
135
136/* For use in selftests. */
137
138label_text
139gcc_urlifier::get_url_suffix_for_quoted_text (const char *p) const
140{
141 return get_url_suffix_for_quoted_text (p, sz: strlen (s: p));
142}
143
144/* Look for a URL for the quoted string (P, SZ) that appears to be
145 an option.
146 Return the url suffix if found, or nullptr otherwise. */
147
148label_text
149gcc_urlifier::get_url_suffix_for_option (const char *p, size_t sz) const
150{
151 /* Look up this option
152
153 find_opt does a binary search, taking a 0-terminated string,
154 and skipping the leading '-'.
155
156 We have a (pointer,size) pair that doesn't necessarily have a
157 terminator.
158 Additionally, we could have one of the e.g. "-Wno-" variants of
159 the option, which find_opt doesn't handle.
160
161 Hence we need to create input for find_opt in a temporary buffer. */
162 char *option_buffer;
163
164 const char *new_prefix;
165 if (const char *old_prefix = get_option_prefix_remapping (p, sz, out_new_prefix: &new_prefix))
166 {
167 /* We have one of the variants; generate a buffer containing a copy
168 that maps from the old prefix to the new prefix
169 e.g. given "-Wno-suffix", generate "-Wsuffix". */
170 gcc_assert (old_prefix[0] == '-');
171 gcc_assert (new_prefix);
172 gcc_assert (new_prefix[0] == '-');
173
174 const size_t old_prefix_len = strlen (s: old_prefix);
175 gcc_assert (old_prefix_len <= sz);
176 const size_t suffix_len = sz - old_prefix_len;
177 const size_t new_prefix_len = strlen (s: new_prefix);
178 const size_t new_sz = new_prefix_len + suffix_len + 1;
179
180 option_buffer = (char *)xmalloc (new_sz);
181 memcpy (dest: option_buffer, src: new_prefix, n: new_prefix_len);
182 /* Copy suffix. */
183 memcpy (dest: option_buffer + new_prefix_len, src: p + old_prefix_len, n: suffix_len);
184 /* Terminate. */
185 option_buffer[new_prefix_len + suffix_len] = '\0';
186 }
187 else
188 {
189 /* Otherwise we can simply create a 0-terminated clone of the string. */
190 gcc_assert (sz > 0);
191 gcc_assert (p[0] == '-');
192 option_buffer = xstrndup (p, sz);
193 }
194
195 size_t opt = find_opt (input: option_buffer + 1, lang_mask: m_lang_mask);
196 free (ptr: option_buffer);
197
198 if (opt >= N_OPTS)
199 /* Option not recognized. */
200 return label_text ();
201
202 return get_option_url_suffix (option_index: opt, lang_mask: m_lang_mask);
203}
204
205char *
206gcc_urlifier::make_doc_url (const char *doc_url_suffix)
207{
208 if (!doc_url_suffix)
209 return nullptr;
210
211 return concat (DOCUMENTATION_ROOT_URL, doc_url_suffix, nullptr);
212}
213
214} // anonymous namespace
215
216urlifier *
217make_gcc_urlifier (unsigned int lang_mask)
218{
219 return new gcc_urlifier (lang_mask);
220}
221
222#if CHECKING_P
223
224namespace selftest {
225
226/* Selftests. */
227
228/* Run all of the selftests within this file. */
229
230void
231gcc_urlifier_cc_tests ()
232{
233 /* Check that doc_urls.quoted_text is sorted. */
234 for (size_t idx = 1; idx < ARRAY_SIZE (doc_urls); idx++)
235 gcc_assert (strcmp (doc_urls[idx - 1].quoted_text,
236 doc_urls[idx].quoted_text)
237 < 0);
238
239 gcc_urlifier u (0);
240
241 ASSERT_EQ (u.get_url_suffix_for_quoted_text ("").get (), nullptr);
242 ASSERT_EQ (u.get_url_suffix_for_quoted_text (")").get (), nullptr);
243
244 ASSERT_STREQ (u.get_url_suffix_for_quoted_text ("#pragma message").get (),
245 "gcc/Diagnostic-Pragmas.html");
246
247 // Incomplete prefix of a quoted_text
248 ASSERT_EQ (u.get_url_suffix_for_quoted_text ("#pragma mess").get (), nullptr);
249
250 /* Check that every element is findable. */
251 for (size_t idx = 0; idx < ARRAY_SIZE (doc_urls); idx++)
252 ASSERT_STREQ
253 (u.get_url_suffix_for_quoted_text (doc_urls[idx].quoted_text).get (),
254 doc_urls[idx].url_suffix);
255
256 /* Check an option. */
257 ASSERT_STREQ (u.get_url_suffix_for_quoted_text ("-fpack-struct").get (),
258 "gcc/Code-Gen-Options.html#index-fpack-struct");
259
260 /* Check a "-fno-" variant of an option. */
261 ASSERT_STREQ (u.get_url_suffix_for_quoted_text ("-fno-inline").get (),
262 "gcc/Optimize-Options.html#index-finline");
263}
264
265} // namespace selftest
266
267#endif /* #if CHECKING_P */
268

source code of gcc/gcc-urlifier.cc