]>
Commit | Line | Data |
---|---|---|
98086b2b ML |
1 | /* Provide option suggestion for --complete option and a misspelled |
2 | used by a user. | |
aeee4812 | 3 | Copyright (C) 2016-2023 Free Software Foundation, Inc. |
98086b2b ML |
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" | |
98086b2b ML |
26 | #include "spellcheck.h" |
27 | #include "opt-suggestions.h" | |
c98c2430 | 28 | #include "common/common-target.h" |
98086b2b ML |
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) | |
c98c2430 | 41 | build_option_suggestions (NULL); |
98086b2b ML |
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 | ||
d86c7648 ML |
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 | ||
8cc5fcaf ML |
67 | /* Lazily populate m_option_suggestions. */ |
68 | if (!m_option_suggestions) | |
69 | build_option_suggestions (option_prefix); | |
70 | gcc_assert (m_option_suggestions); | |
d86c7648 | 71 | |
8cc5fcaf ML |
72 | for (unsigned i = 0; i < m_option_suggestions->length (); i++) |
73 | { | |
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)); | |
d86c7648 ML |
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 | ||
98086b2b | 95 | void |
c98c2430 | 96 | option_proposer::build_option_suggestions (const char *prefix) |
98086b2b ML |
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 | } | |
90f14305 ML |
121 | |
122 | /* Add also variant without an option argument. */ | |
123 | add_misspelling_candidates (m_option_suggestions, option, | |
124 | opt_text); | |
98086b2b ML |
125 | } |
126 | else | |
c98c2430 | 127 | { |
23ab6350 | 128 | bool option_added = false; |
c98c2430 ML |
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 | { | |
23ab6350 | 135 | option_added = true; |
c98c2430 ML |
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 | } | |
a0bdbb09 | 145 | option_values.release (); |
c98c2430 | 146 | } |
23ab6350 ML |
147 | |
148 | if (!option_added) | |
c98c2430 ML |
149 | add_misspelling_candidates (m_option_suggestions, option, |
150 | opt_text); | |
151 | } | |
98086b2b ML |
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 | { | |
90f14305 ML |
166 | /* Add also variant without an option argument. */ |
167 | add_misspelling_candidates (m_option_suggestions, option, | |
168 | opt_text); | |
169 | ||
98086b2b ML |
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 | } | |
d86c7648 | 199 | |
d86c7648 ML |
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", | |
d86c7648 ML |
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=", | |
8cc5fcaf | 369 | "--param=max-vartrack-reverse-op-size=")); |
d86c7648 | 370 | ASSERT_TRUE (in_completion_p (proposer, "--param ", |
8cc5fcaf | 371 | "--param max-vartrack-reverse-op-size=")); |
d86c7648 ML |
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 | |
d5148d4f | 399 | opt_suggestions_cc_tests () |
d86c7648 ML |
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 */ |