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