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