131
|
1 /* Provide option suggestion for --complete option and a misspelled
|
|
2 used by a user.
|
145
|
3 Copyright (C) 2016-2020 Free Software Foundation, Inc.
|
131
|
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 option_proposer::~option_proposer ()
|
|
32 {
|
|
33 delete m_option_suggestions;
|
|
34 }
|
|
35
|
|
36 const char *
|
|
37 option_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 (bad_opt,
|
|
47 (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 option_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 (option_prefix);
|
|
66
|
145
|
67 /* Lazily populate m_option_suggestions. */
|
|
68 if (!m_option_suggestions)
|
|
69 build_option_suggestions (option_prefix);
|
|
70 gcc_assert (m_option_suggestions);
|
|
71
|
|
72 for (unsigned i = 0; i < m_option_suggestions->length (); i++)
|
131
|
73 {
|
145
|
74 char *candidate = (*m_option_suggestions)[i];
|
|
75 if (strlen (candidate) >= length
|
|
76 && strstr (candidate, option_prefix) == candidate)
|
|
77 results.safe_push (concat ("-", candidate, NULL));
|
131
|
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 option_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 ("%s\n", results[i]);
|
|
93 }
|
|
94
|
|
95 void
|
|
96 option_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 (m_option_suggestions, option,
|
|
118 with_arg);
|
|
119 free (with_arg);
|
|
120 }
|
145
|
121
|
|
122 /* Add also variant without an option argument. */
|
|
123 add_misspelling_candidates (m_option_suggestions, option,
|
|
124 opt_text);
|
131
|
125 }
|
|
126 else
|
|
127 {
|
145
|
128 bool option_added = false;
|
131
|
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 {
|
145
|
135 option_added = true;
|
131
|
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 (m_option_suggestions, option,
|
|
141 with_arg);
|
|
142 free (with_arg);
|
|
143 }
|
|
144 }
|
|
145 option_values.release ();
|
|
146 }
|
145
|
147
|
|
148 if (!option_added)
|
131
|
149 add_misspelling_candidates (m_option_suggestions, option,
|
|
150 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 {
|
145
|
166 /* Add also variant without an option argument. */
|
|
167 add_misspelling_candidates (m_option_suggestions, option,
|
|
168 opt_text);
|
|
169
|
131
|
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 (m_option_suggestions, option,
|
|
191 with_arg);
|
|
192 free (with_arg);
|
|
193 }
|
|
194 }
|
|
195 break;
|
|
196 }
|
|
197 }
|
|
198 }
|
|
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 verify_autocompletions (option_proposer &proposer, const char *option_prefix)
|
|
209 {
|
|
210 auto_string_vec suggestions;
|
|
211 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 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
|
|
224 static void
|
|
225 test_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, *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
|
|
288 static void
|
|
289 test_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, *ptr);
|
|
326 }
|
|
327
|
|
328 /* Return true when EXPECTED is one of completions for OPTION_PREFIX string. */
|
|
329
|
|
330 static bool
|
|
331 in_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, suggestions);
|
|
336
|
|
337 for (unsigned i = 0; i < suggestions.length (); i++)
|
|
338 {
|
|
339 char *r = suggestions[i];
|
|
340 if (strcmp (r, 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
|
|
350 static bool
|
|
351 empty_completion_p (option_proposer &proposer, const char *option_prefix)
|
|
352 {
|
|
353 auto_string_vec suggestions;
|
|
354 proposer.get_completions (option_prefix, suggestions);
|
|
355 return suggestions.is_empty ();
|
|
356 }
|
|
357
|
|
358 /* Verify autocompletions of partially-complete options. */
|
|
359
|
|
360 static void
|
|
361 test_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=",
|
145
|
369 "--param=max-vartrack-reverse-op-size="));
|
131
|
370 ASSERT_TRUE (in_completion_p (proposer, "--param ",
|
145
|
371 "--param max-vartrack-reverse-op-size="));
|
131
|
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
|
|
383 static void
|
|
384 test_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
|
|
398 void
|
|
399 opt_proposer_c_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 */
|