Branch data Line data Source code
1 : : /* Provide option suggestion for --complete option and a misspelled
2 : : used by a user.
3 : : Copyright (C) 2016-2024 Free Software Foundation, Inc.
4 : :
5 : : This file is part of GCC.
6 : :
7 : : GCC is free software; you can redistribute it and/or modify it under
8 : : the terms of the GNU General Public License as published by the Free
9 : : Software Foundation; either version 3, or (at your option) any later
10 : : version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : : for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along 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 "tm.h"
25 : : #include "opts.h"
26 : : #include "spellcheck.h"
27 : : #include "opt-suggestions.h"
28 : : #include "common/common-target.h"
29 : : #include "selftest.h"
30 : :
31 : 290429 : option_proposer::~option_proposer ()
32 : : {
33 : 290429 : delete m_option_suggestions;
34 : 290429 : }
35 : :
36 : : const char *
37 : 524 : option_proposer::suggest_option (const char *bad_opt)
38 : : {
39 : : /* Lazily populate m_option_suggestions. */
40 : 524 : if (!m_option_suggestions)
41 : 515 : build_option_suggestions (NULL);
42 : 524 : gcc_assert (m_option_suggestions);
43 : :
44 : : /* "m_option_suggestions" is now populated. Use it. */
45 : 524 : return find_closest_string
46 : 524 : (bad_opt,
47 : 524 : (auto_vec <const char *> *) m_option_suggestions);
48 : : }
49 : :
50 : : /* Populate RESULTS with valid completions of options that begin
51 : : with OPTION_PREFIX. */
52 : :
53 : : void
54 : 389 : option_proposer::get_completions (const char *option_prefix,
55 : : auto_string_vec &results)
56 : : {
57 : : /* Bail out for an invalid input. */
58 : 389 : if (option_prefix == NULL || option_prefix[0] == '\0')
59 : : return;
60 : :
61 : : /* Option suggestions are built without first leading dash character. */
62 : 381 : if (option_prefix[0] == '-')
63 : 373 : option_prefix++;
64 : :
65 : 381 : size_t length = strlen (option_prefix);
66 : :
67 : : /* Lazily populate m_option_suggestions. */
68 : 381 : if (!m_option_suggestions)
69 : 9 : build_option_suggestions (option_prefix);
70 : 381 : gcc_assert (m_option_suggestions);
71 : :
72 : 3744849 : for (unsigned i = 0; i < m_option_suggestions->length (); i++)
73 : : {
74 : 3744468 : char *candidate = (*m_option_suggestions)[i];
75 : 3744468 : if (strlen (candidate) >= length
76 : 1875824 : && strstr (candidate, option_prefix) == candidate)
77 : 45438 : results.safe_push (concat ("-", candidate, NULL));
78 : : }
79 : : }
80 : :
81 : : /* Print on stdout a list of valid options that begin with OPTION_PREFIX,
82 : : one per line, suitable for use by Bash completion.
83 : :
84 : : Implementation of the "-completion=" option. */
85 : :
86 : : void
87 : 5 : option_proposer::suggest_completion (const char *option_prefix)
88 : : {
89 : 5 : auto_string_vec results;
90 : 5 : get_completions (option_prefix, results);
91 : 27 : for (unsigned i = 0; i < results.length (); i++)
92 : 22 : printf ("%s\n", results[i]);
93 : 5 : }
94 : :
95 : : void
96 : 524 : option_proposer::build_option_suggestions (const char *prefix)
97 : : {
98 : 524 : gcc_assert (m_option_suggestions == NULL);
99 : 524 : m_option_suggestions = new auto_string_vec ();
100 : :
101 : : /* We build a vec of m_option_suggestions, using add_misspelling_candidates
102 : : to add copies of strings, without a leading dash. */
103 : :
104 : 1261268 : for (unsigned int i = 0; i < cl_options_count; i++)
105 : : {
106 : 1260744 : const struct cl_option *option = &cl_options[i];
107 : 1260744 : const char *opt_text = option->opt_text;
108 : 1260744 : switch (i)
109 : : {
110 : 1259696 : default:
111 : 1259696 : if (option->var_type == CLVC_ENUM)
112 : : {
113 : 42968 : const struct cl_enum *e = &cl_enums[option->var_enum];
114 : 199120 : for (unsigned j = 0; e->values[j].arg != NULL; j++)
115 : : {
116 : 156152 : char *with_arg = concat (opt_text, e->values[j].arg, NULL);
117 : 156152 : add_misspelling_candidates (m_option_suggestions, option,
118 : : with_arg);
119 : 156152 : free (with_arg);
120 : : }
121 : :
122 : : /* Add also variant without an option argument. */
123 : 42968 : add_misspelling_candidates (m_option_suggestions, option,
124 : : opt_text);
125 : : }
126 : : else
127 : : {
128 : 1216728 : bool option_added = false;
129 : 1216728 : if (option->flags & CL_TARGET)
130 : : {
131 : 115804 : vec<const char *> option_values
132 : 115804 : = targetm_common.get_valid_option_values (i, prefix);
133 : 115804 : if (!option_values.is_empty ())
134 : : {
135 : 90652 : option_added = true;
136 : 90652 : for (unsigned j = 0; j < option_values.length (); j++)
137 : : {
138 : 89604 : char *with_arg = concat (opt_text, option_values[j],
139 : : NULL);
140 : 89604 : add_misspelling_candidates (m_option_suggestions, option,
141 : : with_arg);
142 : 89604 : free (with_arg);
143 : : }
144 : : }
145 : 115804 : option_values.release ();
146 : : }
147 : :
148 : 115804 : if (!option_added)
149 : 1215680 : add_misspelling_candidates (m_option_suggestions, option,
150 : : opt_text);
151 : : }
152 : : break;
153 : :
154 : 1048 : case OPT_fsanitize_:
155 : 1048 : case OPT_fsanitize_recover_:
156 : : /* -fsanitize= and -fsanitize-recover= can take
157 : : a comma-separated list of arguments. Given that combinations
158 : : are supported, we can't add all potential candidates to the
159 : : vec, but if we at least add them individually without commas,
160 : : we should do a better job e.g. correcting
161 : : "-sanitize=address"
162 : : to
163 : : "-fsanitize=address"
164 : : rather than to "-Wframe-address" (PR driver/69265). */
165 : 1048 : {
166 : : /* Add also variant without an option argument. */
167 : 1048 : add_misspelling_candidates (m_option_suggestions, option,
168 : : opt_text);
169 : :
170 : 1048 : struct cl_option optb;
171 : 35632 : for (int j = 0; sanitizer_opts[j].name != NULL; ++j)
172 : : {
173 : : /* -fsanitize=all is not valid, only -fno-sanitize=all.
174 : : So don't register the positive misspelling candidates
175 : : for it. */
176 : 34584 : if (sanitizer_opts[j].flag == ~0U && i == OPT_fsanitize_)
177 : : {
178 : 524 : optb = *option;
179 : 524 : optb.opt_text = opt_text = "-fno-sanitize=";
180 : 524 : optb.cl_reject_negative = true;
181 : 524 : option = &optb;
182 : : }
183 : : /* Get one arg at a time e.g. "-fsanitize=address". */
184 : 34584 : char *with_arg = concat (opt_text,
185 : : sanitizer_opts[j].name,
186 : : NULL);
187 : : /* Add with_arg and all of its variant spellings e.g.
188 : : "-fno-sanitize=address" to candidates (albeit without
189 : : leading dashes). */
190 : 34584 : add_misspelling_candidates (m_option_suggestions, option,
191 : : with_arg);
192 : 34584 : free (with_arg);
193 : : }
194 : : }
195 : 1048 : break;
196 : : }
197 : : }
198 : 524 : }
199 : :
200 : : #if CHECKING_P
201 : :
202 : : namespace selftest {
203 : :
204 : : /* Verify that PROPOSER generates sane auto-completion suggestions
205 : : for OPTION_PREFIX. */
206 : :
207 : : static void
208 : 308 : verify_autocompletions (option_proposer &proposer, const char *option_prefix)
209 : : {
210 : 308 : auto_string_vec suggestions;
211 : 308 : proposer.get_completions (option_prefix, suggestions);
212 : :
213 : : /* There must be at least one suggestion, and every suggestion must
214 : : indeed begin with OPTION_PREFIX. */
215 : :
216 : 308 : ASSERT_GT (suggestions.length (), 0);
217 : :
218 : 640 : for (unsigned i = 0; i < suggestions.length (); i++)
219 : 332 : ASSERT_STR_STARTSWITH (suggestions[i], option_prefix);
220 : 308 : }
221 : :
222 : : /* Verify that valid options are auto-completed correctly. */
223 : :
224 : : static void
225 : 4 : test_completion_valid_options (option_proposer &proposer)
226 : : {
227 : 4 : const char *option_prefixes[] =
228 : : {
229 : : "-fno-var-tracking-assignments-toggle",
230 : : "-fpredictive-commoning",
231 : : "--param=stack-clash-protection-guard-size",
232 : : "--param=max-predicted-iterations",
233 : : "-ftree-loop-distribute-patterns",
234 : : "-fno-var-tracking",
235 : : "-Walloc-zero",
236 : : "--param=ipa-cp-value-list-size",
237 : : "-Wsync-nand",
238 : : "-Wno-attributes",
239 : : "--param=tracer-dynamic-coverage-feedback",
240 : : "-Wno-format-contains-nul",
241 : : "-Wnamespaces",
242 : : "-fisolate-erroneous-paths-attribute",
243 : : "-Wno-underflow",
244 : : "-Wtarget-lifetime",
245 : : "--param=asan-globals",
246 : : "-Wno-empty-body",
247 : : "-Wno-odr",
248 : : "-Wformat-zero-length",
249 : : "-Wstringop-truncation",
250 : : "-fno-ipa-vrp",
251 : : "-fmath-errno",
252 : : "-Warray-temporaries",
253 : : "-Wno-unused-label",
254 : : "-Wreturn-local-addr",
255 : : "--param=sms-dfa-history",
256 : : "--param=asan-instrument-reads",
257 : : "-Wreturn-type",
258 : : "-Wc++17-compat",
259 : : "-Wno-effc++",
260 : : "--param=max-fields-for-field-sensitive",
261 : : "-fisolate-erroneous-paths-dereference",
262 : : "-fno-defer-pop",
263 : : "-Wcast-align=strict",
264 : : "-foptimize-strlen",
265 : : "-Wpacked-not-aligned",
266 : : "-funroll-loops",
267 : : "-fif-conversion2",
268 : : "-Wdesignated-init",
269 : : "--param=max-iterations-computation-cost",
270 : : "-Wmultiple-inheritance",
271 : : "-fno-sel-sched-reschedule-pipelined",
272 : : "-Wassign-intercept",
273 : : "-Wno-format-security",
274 : : "-fno-sched-stalled-insns",
275 : : "-fno-tree-tail-merge",
276 : : "-Wlong-long",
277 : : "-Wno-unused-but-set-parameter",
278 : : NULL
279 : : };
280 : :
281 : 200 : for (const char **ptr = option_prefixes; *ptr != NULL; ptr++)
282 : 196 : verify_autocompletions (proposer, *ptr);
283 : 4 : }
284 : :
285 : : /* Verify that valid parameters are auto-completed correctly,
286 : : both with the "--param=PARAM" form and the "--param PARAM" form. */
287 : :
288 : : static void
289 : 4 : test_completion_valid_params (option_proposer &proposer)
290 : : {
291 : 4 : const char *option_prefixes[] =
292 : : {
293 : : "--param=sched-state-edge-prob-cutoff",
294 : : "--param=iv-consider-all-candidates-bound",
295 : : "--param=align-threshold",
296 : : "--param=prefetch-min-insn-to-mem-ratio",
297 : : "--param=max-unrolled-insns",
298 : : "--param=max-early-inliner-iterations",
299 : : "--param=max-vartrack-reverse-op-size",
300 : : "--param=ipa-cp-loop-hint-bonus",
301 : : "--param=tracer-min-branch-ratio",
302 : : "--param=graphite-max-arrays-per-scop",
303 : : "--param=sink-frequency-threshold",
304 : : "--param=max-cse-path-length",
305 : : "--param=sra-max-scalarization-size-Osize",
306 : : "--param=prefetch-latency",
307 : : "--param=dse-max-object-size",
308 : : "--param=asan-globals",
309 : : "--param=max-vartrack-size",
310 : : "--param=case-values-threshold",
311 : : "--param=max-slsr-cand-scan",
312 : : "--param=min-insn-to-prefetch-ratio",
313 : : "--param=tracer-min-branch-probability",
314 : : "--param sink-frequency-threshold",
315 : : "--param max-cse-path-length",
316 : : "--param sra-max-scalarization-size-Osize",
317 : : "--param prefetch-latency",
318 : : "--param dse-max-object-size",
319 : : "--param asan-globals",
320 : : "--param max-vartrack-size",
321 : : NULL
322 : : };
323 : :
324 : 116 : for (const char **ptr = option_prefixes; *ptr != NULL; ptr++)
325 : 112 : verify_autocompletions (proposer, *ptr);
326 : 4 : }
327 : :
328 : : /* Return true when EXPECTED is one of completions for OPTION_PREFIX string. */
329 : :
330 : : static bool
331 : 32 : in_completion_p (option_proposer &proposer, const char *option_prefix,
332 : : const char *expected)
333 : : {
334 : 32 : auto_string_vec suggestions;
335 : 32 : proposer.get_completions (option_prefix, suggestions);
336 : :
337 : 1780 : for (unsigned i = 0; i < suggestions.length (); i++)
338 : : {
339 : 1772 : char *r = suggestions[i];
340 : 1772 : if (strcmp (r, expected) == 0)
341 : : return true;
342 : : }
343 : :
344 : : return false;
345 : 32 : }
346 : :
347 : : /* Return true when PROPOSER does not find any partial completion
348 : : for OPTION_PREFIX. */
349 : :
350 : : static bool
351 : 44 : empty_completion_p (option_proposer &proposer, const char *option_prefix)
352 : : {
353 : 44 : auto_string_vec suggestions;
354 : 44 : proposer.get_completions (option_prefix, suggestions);
355 : 44 : return suggestions.is_empty ();
356 : 44 : }
357 : :
358 : : /* Verify autocompletions of partially-complete options. */
359 : :
360 : : static void
361 : 4 : test_completion_partial_match (option_proposer &proposer)
362 : : {
363 : 4 : ASSERT_TRUE (in_completion_p (proposer, "-fsani", "-fsanitize=address"));
364 : 4 : ASSERT_TRUE (in_completion_p (proposer, "-fsani",
365 : : "-fsanitize-address-use-after-scope"));
366 : 4 : ASSERT_TRUE (in_completion_p (proposer, "-fipa-icf", "-fipa-icf-functions"));
367 : 4 : ASSERT_TRUE (in_completion_p (proposer, "-fipa-icf", "-fipa-icf"));
368 : 4 : ASSERT_TRUE (in_completion_p (proposer, "--param=",
369 : : "--param=max-vartrack-reverse-op-size="));
370 : 4 : ASSERT_TRUE (in_completion_p (proposer, "--param ",
371 : : "--param max-vartrack-reverse-op-size="));
372 : :
373 : 4 : ASSERT_FALSE (in_completion_p (proposer, "-fipa-icf", "-fipa"));
374 : 4 : ASSERT_FALSE (in_completion_p (proposer, "-fipa-icf-functions", "-fipa-icf"));
375 : :
376 : 4 : ASSERT_FALSE (empty_completion_p (proposer, "-"));
377 : 4 : ASSERT_FALSE (empty_completion_p (proposer, "-fipa"));
378 : 4 : ASSERT_FALSE (empty_completion_p (proposer, "--par"));
379 : 4 : }
380 : :
381 : : /* Verify that autocompletion does not return any match for garbage inputs. */
382 : :
383 : : static void
384 : 4 : test_completion_garbage (option_proposer &proposer)
385 : : {
386 : 4 : ASSERT_TRUE (empty_completion_p (proposer, NULL));
387 : 4 : ASSERT_TRUE (empty_completion_p (proposer, ""));
388 : 4 : ASSERT_TRUE (empty_completion_p (proposer, "- "));
389 : 4 : ASSERT_TRUE (empty_completion_p (proposer, "123456789"));
390 : 4 : ASSERT_TRUE (empty_completion_p (proposer, "---------"));
391 : 4 : ASSERT_TRUE (empty_completion_p (proposer, "#########"));
392 : 4 : ASSERT_TRUE (empty_completion_p (proposer, "- - - - - -"));
393 : 4 : ASSERT_TRUE (empty_completion_p (proposer, "-fsanitize=address2"));
394 : 4 : }
395 : :
396 : : /* Run all of the selftests within this file. */
397 : :
398 : : void
399 : 4 : opt_suggestions_cc_tests ()
400 : : {
401 : 4 : option_proposer proposer;
402 : :
403 : 4 : test_completion_valid_options (proposer);
404 : 4 : test_completion_valid_params (proposer);
405 : 4 : test_completion_partial_match (proposer);
406 : 4 : test_completion_garbage (proposer);
407 : 4 : }
408 : :
409 : : } // namespace selftest
410 : :
411 : : #endif /* #if CHECKING_P */
|