1/* Provide option suggestion for --complete option and a misspelled
2 used by a user.
3 Copyright (C) 2016-2023 Free Software Foundation, Inc.
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 "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
31option_proposer::~option_proposer ()
32{
33 delete m_option_suggestions;
34}
35
36const char *
37option_proposer::suggest_option (const char *bad_opt)
38{
39 /* Lazily populate m_option_suggestions. */
40 if (!m_option_suggestions)
41 build_option_suggestions (NULL);
42 gcc_assert (m_option_suggestions);
43
44 /* "m_option_suggestions" is now populated. Use it. */
45 return find_closest_string
46 (target: bad_opt,
47 candidates: (auto_vec <const char *> *) m_option_suggestions);
48}
49
50/* Populate RESULTS with valid completions of options that begin
51 with OPTION_PREFIX. */
52
53void
54option_proposer::get_completions (const char *option_prefix,
55 auto_string_vec &results)
56{
57 /* Bail out for an invalid input. */
58 if (option_prefix == NULL || option_prefix[0] == '\0')
59 return;
60
61 /* Option suggestions are built without first leading dash character. */
62 if (option_prefix[0] == '-')
63 option_prefix++;
64
65 size_t length = strlen (s: option_prefix);
66
67 /* Lazily populate m_option_suggestions. */
68 if (!m_option_suggestions)
69 build_option_suggestions (prefix: option_prefix);
70 gcc_assert (m_option_suggestions);
71
72 for (unsigned i = 0; i < m_option_suggestions->length (); i++)
73 {
74 char *candidate = (*m_option_suggestions)[i];
75 if (strlen (s: candidate) >= length
76 && strstr (haystack: candidate, needle: option_prefix) == candidate)
77 results.safe_push (obj: 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
86void
87option_proposer::suggest_completion (const char *option_prefix)
88{
89 auto_string_vec results;
90 get_completions (option_prefix, results);
91 for (unsigned i = 0; i < results.length (); i++)
92 printf (format: "%s\n", results[i]);
93}
94
95void
96option_proposer::build_option_suggestions (const char *prefix)
97{
98 gcc_assert (m_option_suggestions == NULL);
99 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 for (unsigned int i = 0; i < cl_options_count; i++)
105 {
106 const struct cl_option *option = &cl_options[i];
107 const char *opt_text = option->opt_text;
108 switch (i)
109 {
110 default:
111 if (option->var_type == CLVC_ENUM)
112 {
113 const struct cl_enum *e = &cl_enums[option->var_enum];
114 for (unsigned j = 0; e->values[j].arg != NULL; j++)
115 {
116 char *with_arg = concat (opt_text, e->values[j].arg, NULL);
117 add_misspelling_candidates (candidates: m_option_suggestions, option,
118 base_option: with_arg);
119 free (ptr: with_arg);
120 }
121
122 /* Add also variant without an option argument. */
123 add_misspelling_candidates (candidates: m_option_suggestions, option,
124 base_option: opt_text);
125 }
126 else
127 {
128 bool option_added = false;
129 if (option->flags & CL_TARGET)
130 {
131 vec<const char *> option_values
132 = targetm_common.get_valid_option_values (i, prefix);
133 if (!option_values.is_empty ())
134 {
135 option_added = true;
136 for (unsigned j = 0; j < option_values.length (); j++)
137 {
138 char *with_arg = concat (opt_text, option_values[j],
139 NULL);
140 add_misspelling_candidates (candidates: m_option_suggestions, option,
141 base_option: with_arg);
142 free (ptr: with_arg);
143 }
144 }
145 option_values.release ();
146 }
147
148 if (!option_added)
149 add_misspelling_candidates (candidates: m_option_suggestions, option,
150 base_option: opt_text);
151 }
152 break;
153
154 case OPT_fsanitize_:
155 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 {
166 /* Add also variant without an option argument. */
167 add_misspelling_candidates (candidates: m_option_suggestions, option,
168 base_option: opt_text);
169
170 for (int j = 0; sanitizer_opts[j].name != NULL; ++j)
171 {
172 struct cl_option optb;
173 /* -fsanitize=all is not valid, only -fno-sanitize=all.
174 So don't register the positive misspelling candidates
175 for it. */
176 if (sanitizer_opts[j].flag == ~0U && i == OPT_fsanitize_)
177 {
178 optb = *option;
179 optb.opt_text = opt_text = "-fno-sanitize=";
180 optb.cl_reject_negative = true;
181 option = &optb;
182 }
183 /* Get one arg at a time e.g. "-fsanitize=address". */
184 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 add_misspelling_candidates (candidates: m_option_suggestions, option,
191 base_option: with_arg);
192 free (ptr: with_arg);
193 }
194 }
195 break;
196 }
197 }
198}
199
200#if CHECKING_P
201
202namespace selftest {
203
204/* Verify that PROPOSER generates sane auto-completion suggestions
205 for OPTION_PREFIX. */
206
207static void
208verify_autocompletions (option_proposer &proposer, const char *option_prefix)
209{
210 auto_string_vec suggestions;
211 proposer.get_completions (option_prefix, results&: suggestions);
212
213 /* There must be at least one suggestion, and every suggestion must
214 indeed begin with OPTION_PREFIX. */
215
216 ASSERT_GT (suggestions.length (), 0);
217
218 for (unsigned i = 0; i < suggestions.length (); i++)
219 ASSERT_STR_STARTSWITH (suggestions[i], option_prefix);
220}
221
222/* Verify that valid options are auto-completed correctly. */
223
224static void
225test_completion_valid_options (option_proposer &proposer)
226{
227 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 for (const char **ptr = option_prefixes; *ptr != NULL; ptr++)
282 verify_autocompletions (proposer, option_prefix: *ptr);
283}
284
285/* Verify that valid parameters are auto-completed correctly,
286 both with the "--param=PARAM" form and the "--param PARAM" form. */
287
288static void
289test_completion_valid_params (option_proposer &proposer)
290{
291 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 for (const char **ptr = option_prefixes; *ptr != NULL; ptr++)
325 verify_autocompletions (proposer, option_prefix: *ptr);
326}
327
328/* Return true when EXPECTED is one of completions for OPTION_PREFIX string. */
329
330static bool
331in_completion_p (option_proposer &proposer, const char *option_prefix,
332 const char *expected)
333{
334 auto_string_vec suggestions;
335 proposer.get_completions (option_prefix, results&: suggestions);
336
337 for (unsigned i = 0; i < suggestions.length (); i++)
338 {
339 char *r = suggestions[i];
340 if (strcmp (s1: r, s2: expected) == 0)
341 return true;
342 }
343
344 return false;
345}
346
347/* Return true when PROPOSER does not find any partial completion
348 for OPTION_PREFIX. */
349
350static bool
351empty_completion_p (option_proposer &proposer, const char *option_prefix)
352{
353 auto_string_vec suggestions;
354 proposer.get_completions (option_prefix, results&: suggestions);
355 return suggestions.is_empty ();
356}
357
358/* Verify autocompletions of partially-complete options. */
359
360static void
361test_completion_partial_match (option_proposer &proposer)
362{
363 ASSERT_TRUE (in_completion_p (proposer, "-fsani", "-fsanitize=address"));
364 ASSERT_TRUE (in_completion_p (proposer, "-fsani",
365 "-fsanitize-address-use-after-scope"));
366 ASSERT_TRUE (in_completion_p (proposer, "-fipa-icf", "-fipa-icf-functions"));
367 ASSERT_TRUE (in_completion_p (proposer, "-fipa-icf", "-fipa-icf"));
368 ASSERT_TRUE (in_completion_p (proposer, "--param=",
369 "--param=max-vartrack-reverse-op-size="));
370 ASSERT_TRUE (in_completion_p (proposer, "--param ",
371 "--param max-vartrack-reverse-op-size="));
372
373 ASSERT_FALSE (in_completion_p (proposer, "-fipa-icf", "-fipa"));
374 ASSERT_FALSE (in_completion_p (proposer, "-fipa-icf-functions", "-fipa-icf"));
375
376 ASSERT_FALSE (empty_completion_p (proposer, "-"));
377 ASSERT_FALSE (empty_completion_p (proposer, "-fipa"));
378 ASSERT_FALSE (empty_completion_p (proposer, "--par"));
379}
380
381/* Verify that autocompletion does not return any match for garbage inputs. */
382
383static void
384test_completion_garbage (option_proposer &proposer)
385{
386 ASSERT_TRUE (empty_completion_p (proposer, NULL));
387 ASSERT_TRUE (empty_completion_p (proposer, ""));
388 ASSERT_TRUE (empty_completion_p (proposer, "- "));
389 ASSERT_TRUE (empty_completion_p (proposer, "123456789"));
390 ASSERT_TRUE (empty_completion_p (proposer, "---------"));
391 ASSERT_TRUE (empty_completion_p (proposer, "#########"));
392 ASSERT_TRUE (empty_completion_p (proposer, "- - - - - -"));
393 ASSERT_TRUE (empty_completion_p (proposer, "-fsanitize=address2"));
394}
395
396/* Run all of the selftests within this file. */
397
398void
399opt_suggestions_cc_tests ()
400{
401 option_proposer proposer;
402
403 test_completion_valid_options (proposer);
404 test_completion_valid_params (proposer);
405 test_completion_partial_match (proposer);
406 test_completion_garbage (proposer);
407}
408
409} // namespace selftest
410
411#endif /* #if CHECKING_P */
412

source code of gcc/opt-suggestions.cc