1 | //===- OptTable.cpp - Option Table Implementation -------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "llvm/Option/OptTable.h" |
10 | #include "llvm/ADT/STLExtras.h" |
11 | #include "llvm/ADT/StringRef.h" |
12 | #include "llvm/Option/Arg.h" |
13 | #include "llvm/Option/ArgList.h" |
14 | #include "llvm/Option/OptSpecifier.h" |
15 | #include "llvm/Option/Option.h" |
16 | #include "llvm/Support/CommandLine.h" // for expandResponseFiles |
17 | #include "llvm/Support/Compiler.h" |
18 | #include "llvm/Support/ErrorHandling.h" |
19 | #include "llvm/Support/raw_ostream.h" |
20 | #include <algorithm> |
21 | #include <cassert> |
22 | #include <cctype> |
23 | #include <cstring> |
24 | #include <map> |
25 | #include <set> |
26 | #include <string> |
27 | #include <utility> |
28 | #include <vector> |
29 | |
30 | using namespace llvm; |
31 | using namespace llvm::opt; |
32 | |
33 | namespace llvm { |
34 | namespace opt { |
35 | |
36 | // Ordering on Info. The ordering is *almost* case-insensitive lexicographic, |
37 | // with an exception. '\0' comes at the end of the alphabet instead of the |
38 | // beginning (thus options precede any other options which prefix them). |
39 | static int StrCmpOptionNameIgnoreCase(StringRef A, StringRef B) { |
40 | size_t MinSize = std::min(a: A.size(), b: B.size()); |
41 | if (int Res = A.substr(Start: 0, N: MinSize).compare_insensitive(RHS: B.substr(Start: 0, N: MinSize))) |
42 | return Res; |
43 | |
44 | if (A.size() == B.size()) |
45 | return 0; |
46 | |
47 | return (A.size() == MinSize) ? 1 /* A is a prefix of B. */ |
48 | : -1 /* B is a prefix of A */; |
49 | } |
50 | |
51 | #ifndef NDEBUG |
52 | static int StrCmpOptionName(StringRef A, StringRef B) { |
53 | if (int N = StrCmpOptionNameIgnoreCase(A, B)) |
54 | return N; |
55 | return A.compare(B); |
56 | } |
57 | |
58 | static inline bool operator<(const OptTable::Info &A, const OptTable::Info &B) { |
59 | if (&A == &B) |
60 | return false; |
61 | |
62 | if (int N = StrCmpOptionName(A.getName(), B.getName())) |
63 | return N < 0; |
64 | |
65 | for (size_t I = 0, K = std::min(A.Prefixes.size(), B.Prefixes.size()); I != K; |
66 | ++I) |
67 | if (int N = StrCmpOptionName(A.Prefixes[I], B.Prefixes[I])) |
68 | return N < 0; |
69 | |
70 | // Names are the same, check that classes are in order; exactly one |
71 | // should be joined, and it should succeed the other. |
72 | assert(((A.Kind == Option::JoinedClass) ^ (B.Kind == Option::JoinedClass)) && |
73 | "Unexpected classes for options with same name." ); |
74 | return B.Kind == Option::JoinedClass; |
75 | } |
76 | #endif |
77 | |
78 | // Support lower_bound between info and an option name. |
79 | static inline bool operator<(const OptTable::Info &I, StringRef Name) { |
80 | return StrCmpOptionNameIgnoreCase(A: I.getName(), B: Name) < 0; |
81 | } |
82 | |
83 | } // end namespace opt |
84 | } // end namespace llvm |
85 | |
86 | OptSpecifier::OptSpecifier(const Option *Opt) : ID(Opt->getID()) {} |
87 | |
88 | OptTable::OptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase) |
89 | : OptionInfos(OptionInfos), IgnoreCase(IgnoreCase) { |
90 | // Explicitly zero initialize the error to work around a bug in array |
91 | // value-initialization on MinGW with gcc 4.3.5. |
92 | |
93 | // Find start of normal options. |
94 | for (unsigned i = 0, e = getNumOptions(); i != e; ++i) { |
95 | unsigned Kind = getInfo(Opt: i + 1).Kind; |
96 | if (Kind == Option::InputClass) { |
97 | assert(!InputOptionID && "Cannot have multiple input options!" ); |
98 | InputOptionID = getInfo(Opt: i + 1).ID; |
99 | } else if (Kind == Option::UnknownClass) { |
100 | assert(!UnknownOptionID && "Cannot have multiple unknown options!" ); |
101 | UnknownOptionID = getInfo(Opt: i + 1).ID; |
102 | } else if (Kind != Option::GroupClass) { |
103 | FirstSearchableIndex = i; |
104 | break; |
105 | } |
106 | } |
107 | assert(FirstSearchableIndex != 0 && "No searchable options?" ); |
108 | |
109 | #ifndef NDEBUG |
110 | // Check that everything after the first searchable option is a |
111 | // regular option class. |
112 | for (unsigned i = FirstSearchableIndex, e = getNumOptions(); i != e; ++i) { |
113 | Option::OptionClass Kind = (Option::OptionClass) getInfo(i + 1).Kind; |
114 | assert((Kind != Option::InputClass && Kind != Option::UnknownClass && |
115 | Kind != Option::GroupClass) && |
116 | "Special options should be defined first!" ); |
117 | } |
118 | |
119 | // Check that options are in order. |
120 | for (unsigned i = FirstSearchableIndex + 1, e = getNumOptions(); i != e; ++i){ |
121 | if (!(getInfo(i) < getInfo(i + 1))) { |
122 | getOption(i).dump(); |
123 | getOption(i + 1).dump(); |
124 | llvm_unreachable("Options are not in order!" ); |
125 | } |
126 | } |
127 | #endif |
128 | } |
129 | |
130 | void OptTable::buildPrefixChars() { |
131 | assert(PrefixChars.empty() && "rebuilding a non-empty prefix char" ); |
132 | |
133 | // Build prefix chars. |
134 | for (const StringLiteral &Prefix : getPrefixesUnion()) { |
135 | for (char C : Prefix) |
136 | if (!is_contained(Range&: PrefixChars, Element: C)) |
137 | PrefixChars.push_back(Elt: C); |
138 | } |
139 | } |
140 | |
141 | OptTable::~OptTable() = default; |
142 | |
143 | const Option OptTable::getOption(OptSpecifier Opt) const { |
144 | unsigned id = Opt.getID(); |
145 | if (id == 0) |
146 | return Option(nullptr, nullptr); |
147 | assert((unsigned) (id - 1) < getNumOptions() && "Invalid ID." ); |
148 | return Option(&getInfo(Opt: id), this); |
149 | } |
150 | |
151 | static bool isInput(const ArrayRef<StringLiteral> &Prefixes, StringRef Arg) { |
152 | if (Arg == "-" ) |
153 | return true; |
154 | for (const StringRef &Prefix : Prefixes) |
155 | if (Arg.starts_with(Prefix)) |
156 | return false; |
157 | return true; |
158 | } |
159 | |
160 | /// \returns Matched size. 0 means no match. |
161 | static unsigned matchOption(const OptTable::Info *I, StringRef Str, |
162 | bool IgnoreCase) { |
163 | for (auto Prefix : I->Prefixes) { |
164 | if (Str.starts_with(Prefix)) { |
165 | StringRef Rest = Str.substr(Start: Prefix.size()); |
166 | bool Matched = IgnoreCase ? Rest.starts_with_insensitive(Prefix: I->getName()) |
167 | : Rest.starts_with(Prefix: I->getName()); |
168 | if (Matched) |
169 | return Prefix.size() + StringRef(I->getName()).size(); |
170 | } |
171 | } |
172 | return 0; |
173 | } |
174 | |
175 | // Returns true if one of the Prefixes + In.Names matches Option |
176 | static bool optionMatches(const OptTable::Info &In, StringRef Option) { |
177 | for (auto Prefix : In.Prefixes) |
178 | if (Option.ends_with(Suffix: In.getName())) |
179 | if (Option.slice(Start: 0, End: Option.size() - In.getName().size()) == Prefix) |
180 | return true; |
181 | return false; |
182 | } |
183 | |
184 | // This function is for flag value completion. |
185 | // Eg. When "-stdlib=" and "l" was passed to this function, it will return |
186 | // appropiriate values for stdlib, which starts with l. |
187 | std::vector<std::string> |
188 | OptTable::suggestValueCompletions(StringRef Option, StringRef Arg) const { |
189 | // Search all options and return possible values. |
190 | for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) { |
191 | const Info &In = OptionInfos[I]; |
192 | if (!In.Values || !optionMatches(In, Option)) |
193 | continue; |
194 | |
195 | SmallVector<StringRef, 8> Candidates; |
196 | StringRef(In.Values).split(A&: Candidates, Separator: "," , MaxSplit: -1, KeepEmpty: false); |
197 | |
198 | std::vector<std::string> Result; |
199 | for (StringRef Val : Candidates) |
200 | if (Val.starts_with(Prefix: Arg) && Arg != Val) |
201 | Result.push_back(x: std::string(Val)); |
202 | return Result; |
203 | } |
204 | return {}; |
205 | } |
206 | |
207 | std::vector<std::string> |
208 | OptTable::findByPrefix(StringRef Cur, Visibility VisibilityMask, |
209 | unsigned int DisableFlags) const { |
210 | std::vector<std::string> Ret; |
211 | for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) { |
212 | const Info &In = OptionInfos[I]; |
213 | if (In.Prefixes.empty() || (!In.HelpText && !In.GroupID)) |
214 | continue; |
215 | if (!(In.Visibility & VisibilityMask)) |
216 | continue; |
217 | if (In.Flags & DisableFlags) |
218 | continue; |
219 | |
220 | for (auto Prefix : In.Prefixes) { |
221 | std::string S = (Prefix + In.getName() + "\t" ).str(); |
222 | if (In.HelpText) |
223 | S += In.HelpText; |
224 | if (StringRef(S).starts_with(Prefix: Cur) && S != std::string(Cur) + "\t" ) |
225 | Ret.push_back(x: S); |
226 | } |
227 | } |
228 | return Ret; |
229 | } |
230 | |
231 | unsigned OptTable::findNearest(StringRef Option, std::string &NearestString, |
232 | Visibility VisibilityMask, |
233 | unsigned MinimumLength, |
234 | unsigned MaximumDistance) const { |
235 | return internalFindNearest( |
236 | Option, NearestString, MinimumLength, MaximumDistance, |
237 | ExcludeOption: [VisibilityMask](const Info &CandidateInfo) { |
238 | return (CandidateInfo.Visibility & VisibilityMask) == 0; |
239 | }); |
240 | } |
241 | |
242 | unsigned OptTable::findNearest(StringRef Option, std::string &NearestString, |
243 | unsigned FlagsToInclude, unsigned FlagsToExclude, |
244 | unsigned MinimumLength, |
245 | unsigned MaximumDistance) const { |
246 | return internalFindNearest( |
247 | Option, NearestString, MinimumLength, MaximumDistance, |
248 | ExcludeOption: [FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) { |
249 | if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude)) |
250 | return true; |
251 | if (CandidateInfo.Flags & FlagsToExclude) |
252 | return true; |
253 | return false; |
254 | }); |
255 | } |
256 | |
257 | unsigned OptTable::internalFindNearest( |
258 | StringRef Option, std::string &NearestString, unsigned MinimumLength, |
259 | unsigned MaximumDistance, |
260 | std::function<bool(const Info &)> ExcludeOption) const { |
261 | assert(!Option.empty()); |
262 | |
263 | // Consider each [option prefix + option name] pair as a candidate, finding |
264 | // the closest match. |
265 | unsigned BestDistance = |
266 | MaximumDistance == UINT_MAX ? UINT_MAX : MaximumDistance + 1; |
267 | SmallString<16> Candidate; |
268 | SmallString<16> NormalizedName; |
269 | |
270 | for (const Info &CandidateInfo : |
271 | ArrayRef<Info>(OptionInfos).drop_front(N: FirstSearchableIndex)) { |
272 | StringRef CandidateName = CandidateInfo.getName(); |
273 | |
274 | // We can eliminate some option prefix/name pairs as candidates right away: |
275 | // * Ignore option candidates with empty names, such as "--", or names |
276 | // that do not meet the minimum length. |
277 | if (CandidateName.size() < MinimumLength) |
278 | continue; |
279 | |
280 | // Ignore options that are excluded via masks |
281 | if (ExcludeOption(CandidateInfo)) |
282 | continue; |
283 | |
284 | // * Ignore positional argument option candidates (which do not |
285 | // have prefixes). |
286 | if (CandidateInfo.Prefixes.empty()) |
287 | continue; |
288 | |
289 | // Now check if the candidate ends with a character commonly used when |
290 | // delimiting an option from its value, such as '=' or ':'. If it does, |
291 | // attempt to split the given option based on that delimiter. |
292 | char Last = CandidateName.back(); |
293 | bool CandidateHasDelimiter = Last == '=' || Last == ':'; |
294 | StringRef RHS; |
295 | if (CandidateHasDelimiter) { |
296 | std::tie(args&: NormalizedName, args&: RHS) = Option.split(Separator: Last); |
297 | if (Option.find(C: Last) == NormalizedName.size()) |
298 | NormalizedName += Last; |
299 | } else |
300 | NormalizedName = Option; |
301 | |
302 | // Consider each possible prefix for each candidate to find the most |
303 | // appropriate one. For example, if a user asks for "--helm", suggest |
304 | // "--help" over "-help". |
305 | for (auto CandidatePrefix : CandidateInfo.Prefixes) { |
306 | // If Candidate and NormalizedName have more than 'BestDistance' |
307 | // characters of difference, no need to compute the edit distance, it's |
308 | // going to be greater than BestDistance. Don't bother computing Candidate |
309 | // at all. |
310 | size_t CandidateSize = CandidatePrefix.size() + CandidateName.size(), |
311 | NormalizedSize = NormalizedName.size(); |
312 | size_t AbsDiff = CandidateSize > NormalizedSize |
313 | ? CandidateSize - NormalizedSize |
314 | : NormalizedSize - CandidateSize; |
315 | if (AbsDiff > BestDistance) { |
316 | continue; |
317 | } |
318 | Candidate = CandidatePrefix; |
319 | Candidate += CandidateName; |
320 | unsigned Distance = StringRef(Candidate).edit_distance( |
321 | Other: NormalizedName, /*AllowReplacements=*/true, |
322 | /*MaxEditDistance=*/BestDistance); |
323 | if (RHS.empty() && CandidateHasDelimiter) { |
324 | // The Candidate ends with a = or : delimiter, but the option passed in |
325 | // didn't contain the delimiter (or doesn't have anything after it). |
326 | // In that case, penalize the correction: `-nodefaultlibs` is more |
327 | // likely to be a spello for `-nodefaultlib` than `-nodefaultlib:` even |
328 | // though both have an unmodified editing distance of 1, since the |
329 | // latter would need an argument. |
330 | ++Distance; |
331 | } |
332 | if (Distance < BestDistance) { |
333 | BestDistance = Distance; |
334 | NearestString = (Candidate + RHS).str(); |
335 | } |
336 | } |
337 | } |
338 | return BestDistance; |
339 | } |
340 | |
341 | // Parse a single argument, return the new argument, and update Index. If |
342 | // GroupedShortOptions is true, -a matches "-abc" and the argument in Args will |
343 | // be updated to "-bc". This overload does not support VisibilityMask or case |
344 | // insensitive options. |
345 | std::unique_ptr<Arg> OptTable::parseOneArgGrouped(InputArgList &Args, |
346 | unsigned &Index) const { |
347 | // Anything that doesn't start with PrefixesUnion is an input, as is '-' |
348 | // itself. |
349 | const char *CStr = Args.getArgString(Index); |
350 | StringRef Str(CStr); |
351 | if (isInput(Prefixes: getPrefixesUnion(), Arg: Str)) |
352 | return std::make_unique<Arg>(args: getOption(Opt: InputOptionID), args&: Str, args: Index++, args&: CStr); |
353 | |
354 | const Info *End = OptionInfos.data() + OptionInfos.size(); |
355 | StringRef Name = Str.ltrim(Chars: PrefixChars); |
356 | const Info *Start = |
357 | std::lower_bound(OptionInfos.data() + FirstSearchableIndex, End, Name); |
358 | const Info *Fallback = nullptr; |
359 | unsigned Prev = Index; |
360 | |
361 | // Search for the option which matches Str. |
362 | for (; Start != End; ++Start) { |
363 | unsigned ArgSize = matchOption(I: Start, Str, IgnoreCase); |
364 | if (!ArgSize) |
365 | continue; |
366 | |
367 | Option Opt(Start, this); |
368 | if (std::unique_ptr<Arg> A = |
369 | Opt.accept(Args, CurArg: StringRef(Args.getArgString(Index), ArgSize), |
370 | /*GroupedShortOption=*/false, Index)) |
371 | return A; |
372 | |
373 | // If Opt is a Flag of length 2 (e.g. "-a"), we know it is a prefix of |
374 | // the current argument (e.g. "-abc"). Match it as a fallback if no longer |
375 | // option (e.g. "-ab") exists. |
376 | if (ArgSize == 2 && Opt.getKind() == Option::FlagClass) |
377 | Fallback = Start; |
378 | |
379 | // Otherwise, see if the argument is missing. |
380 | if (Prev != Index) |
381 | return nullptr; |
382 | } |
383 | if (Fallback) { |
384 | Option Opt(Fallback, this); |
385 | // Check that the last option isn't a flag wrongly given an argument. |
386 | if (Str[2] == '=') |
387 | return std::make_unique<Arg>(args: getOption(Opt: UnknownOptionID), args&: Str, args: Index++, |
388 | args&: CStr); |
389 | |
390 | if (std::unique_ptr<Arg> A = Opt.accept( |
391 | Args, CurArg: Str.substr(Start: 0, N: 2), /*GroupedShortOption=*/true, Index)) { |
392 | Args.replaceArgString(Index, S: Twine('-') + Str.substr(Start: 2)); |
393 | return A; |
394 | } |
395 | } |
396 | |
397 | // In the case of an incorrect short option extract the character and move to |
398 | // the next one. |
399 | if (Str[1] != '-') { |
400 | CStr = Args.MakeArgString(Str: Str.substr(Start: 0, N: 2)); |
401 | Args.replaceArgString(Index, S: Twine('-') + Str.substr(Start: 2)); |
402 | return std::make_unique<Arg>(args: getOption(Opt: UnknownOptionID), args&: CStr, args&: Index, args&: CStr); |
403 | } |
404 | |
405 | return std::make_unique<Arg>(args: getOption(Opt: UnknownOptionID), args&: Str, args: Index++, args&: CStr); |
406 | } |
407 | |
408 | std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList &Args, unsigned &Index, |
409 | Visibility VisibilityMask) const { |
410 | return internalParseOneArg(Args, Index, ExcludeOption: [VisibilityMask](const Option &Opt) { |
411 | return !Opt.hasVisibilityFlag(Val: VisibilityMask); |
412 | }); |
413 | } |
414 | |
415 | std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList &Args, unsigned &Index, |
416 | unsigned FlagsToInclude, |
417 | unsigned FlagsToExclude) const { |
418 | return internalParseOneArg( |
419 | Args, Index, ExcludeOption: [FlagsToInclude, FlagsToExclude](const Option &Opt) { |
420 | if (FlagsToInclude && !Opt.hasFlag(Val: FlagsToInclude)) |
421 | return true; |
422 | if (Opt.hasFlag(Val: FlagsToExclude)) |
423 | return true; |
424 | return false; |
425 | }); |
426 | } |
427 | |
428 | std::unique_ptr<Arg> OptTable::internalParseOneArg( |
429 | const ArgList &Args, unsigned &Index, |
430 | std::function<bool(const Option &)> ExcludeOption) const { |
431 | unsigned Prev = Index; |
432 | StringRef Str = Args.getArgString(Index); |
433 | |
434 | // Anything that doesn't start with PrefixesUnion is an input, as is '-' |
435 | // itself. |
436 | if (isInput(Prefixes: getPrefixesUnion(), Arg: Str)) |
437 | return std::make_unique<Arg>(args: getOption(Opt: InputOptionID), args&: Str, args: Index++, |
438 | args: Str.data()); |
439 | |
440 | const Info *Start = OptionInfos.data() + FirstSearchableIndex; |
441 | const Info *End = OptionInfos.data() + OptionInfos.size(); |
442 | StringRef Name = Str.ltrim(Chars: PrefixChars); |
443 | |
444 | // Search for the first next option which could be a prefix. |
445 | Start = std::lower_bound(Start, End, Name); |
446 | |
447 | // Options are stored in sorted order, with '\0' at the end of the |
448 | // alphabet. Since the only options which can accept a string must |
449 | // prefix it, we iteratively search for the next option which could |
450 | // be a prefix. |
451 | // |
452 | // FIXME: This is searching much more than necessary, but I am |
453 | // blanking on the simplest way to make it fast. We can solve this |
454 | // problem when we move to TableGen. |
455 | for (; Start != End; ++Start) { |
456 | unsigned ArgSize = 0; |
457 | // Scan for first option which is a proper prefix. |
458 | for (; Start != End; ++Start) |
459 | if ((ArgSize = matchOption(I: Start, Str, IgnoreCase))) |
460 | break; |
461 | if (Start == End) |
462 | break; |
463 | |
464 | Option Opt(Start, this); |
465 | |
466 | if (ExcludeOption(Opt)) |
467 | continue; |
468 | |
469 | // See if this option matches. |
470 | if (std::unique_ptr<Arg> A = |
471 | Opt.accept(Args, CurArg: StringRef(Args.getArgString(Index), ArgSize), |
472 | /*GroupedShortOption=*/false, Index)) |
473 | return A; |
474 | |
475 | // Otherwise, see if this argument was missing values. |
476 | if (Prev != Index) |
477 | return nullptr; |
478 | } |
479 | |
480 | // If we failed to find an option and this arg started with /, then it's |
481 | // probably an input path. |
482 | if (Str[0] == '/') |
483 | return std::make_unique<Arg>(args: getOption(Opt: InputOptionID), args&: Str, args: Index++, |
484 | args: Str.data()); |
485 | |
486 | return std::make_unique<Arg>(args: getOption(Opt: UnknownOptionID), args&: Str, args: Index++, |
487 | args: Str.data()); |
488 | } |
489 | |
490 | InputArgList OptTable::ParseArgs(ArrayRef<const char *> Args, |
491 | unsigned &MissingArgIndex, |
492 | unsigned &MissingArgCount, |
493 | Visibility VisibilityMask) const { |
494 | return internalParseArgs( |
495 | Args, MissingArgIndex, MissingArgCount, |
496 | ExcludeOption: [VisibilityMask](const Option &Opt) { |
497 | return !Opt.hasVisibilityFlag(Val: VisibilityMask); |
498 | }); |
499 | } |
500 | |
501 | InputArgList OptTable::ParseArgs(ArrayRef<const char *> Args, |
502 | unsigned &MissingArgIndex, |
503 | unsigned &MissingArgCount, |
504 | unsigned FlagsToInclude, |
505 | unsigned FlagsToExclude) const { |
506 | return internalParseArgs( |
507 | Args, MissingArgIndex, MissingArgCount, |
508 | ExcludeOption: [FlagsToInclude, FlagsToExclude](const Option &Opt) { |
509 | if (FlagsToInclude && !Opt.hasFlag(Val: FlagsToInclude)) |
510 | return true; |
511 | if (Opt.hasFlag(Val: FlagsToExclude)) |
512 | return true; |
513 | return false; |
514 | }); |
515 | } |
516 | |
517 | InputArgList OptTable::internalParseArgs( |
518 | ArrayRef<const char *> ArgArr, unsigned &MissingArgIndex, |
519 | unsigned &MissingArgCount, |
520 | std::function<bool(const Option &)> ExcludeOption) const { |
521 | InputArgList Args(ArgArr.begin(), ArgArr.end()); |
522 | |
523 | // FIXME: Handle '@' args (or at least error on them). |
524 | |
525 | MissingArgIndex = MissingArgCount = 0; |
526 | unsigned Index = 0, End = ArgArr.size(); |
527 | while (Index < End) { |
528 | // Ingore nullptrs, they are response file's EOL markers |
529 | if (Args.getArgString(Index) == nullptr) { |
530 | ++Index; |
531 | continue; |
532 | } |
533 | // Ignore empty arguments (other things may still take them as arguments). |
534 | StringRef Str = Args.getArgString(Index); |
535 | if (Str == "" ) { |
536 | ++Index; |
537 | continue; |
538 | } |
539 | |
540 | // In DashDashParsing mode, the first "--" stops option scanning and treats |
541 | // all subsequent arguments as positional. |
542 | if (DashDashParsing && Str == "--" ) { |
543 | while (++Index < End) { |
544 | Args.append(A: new Arg(getOption(Opt: InputOptionID), Str, Index, |
545 | Args.getArgString(Index))); |
546 | } |
547 | break; |
548 | } |
549 | |
550 | unsigned Prev = Index; |
551 | std::unique_ptr<Arg> A = GroupedShortOptions |
552 | ? parseOneArgGrouped(Args, Index) |
553 | : internalParseOneArg(Args, Index, ExcludeOption); |
554 | assert((Index > Prev || GroupedShortOptions) && |
555 | "Parser failed to consume argument." ); |
556 | |
557 | // Check for missing argument error. |
558 | if (!A) { |
559 | assert(Index >= End && "Unexpected parser error." ); |
560 | assert(Index - Prev - 1 && "No missing arguments!" ); |
561 | MissingArgIndex = Prev; |
562 | MissingArgCount = Index - Prev - 1; |
563 | break; |
564 | } |
565 | |
566 | Args.append(A: A.release()); |
567 | } |
568 | |
569 | return Args; |
570 | } |
571 | |
572 | InputArgList OptTable::parseArgs(int Argc, char *const *Argv, |
573 | OptSpecifier Unknown, StringSaver &Saver, |
574 | std::function<void(StringRef)> ErrorFn) const { |
575 | SmallVector<const char *, 0> NewArgv; |
576 | // The environment variable specifies initial options which can be overridden |
577 | // by commnad line options. |
578 | cl::expandResponseFiles(Argc, Argv, EnvVar, Saver, NewArgv); |
579 | |
580 | unsigned MAI, MAC; |
581 | opt::InputArgList Args = ParseArgs(Args: ArrayRef(NewArgv), MissingArgIndex&: MAI, MissingArgCount&: MAC); |
582 | if (MAC) |
583 | ErrorFn((Twine(Args.getArgString(Index: MAI)) + ": missing argument" ).str()); |
584 | |
585 | // For each unknwon option, call ErrorFn with a formatted error message. The |
586 | // message includes a suggested alternative option spelling if available. |
587 | std::string Nearest; |
588 | for (const opt::Arg *A : Args.filtered(Ids: Unknown)) { |
589 | std::string Spelling = A->getAsString(Args); |
590 | if (findNearest(Option: Spelling, NearestString&: Nearest) > 1) |
591 | ErrorFn("unknown argument '" + Spelling + "'" ); |
592 | else |
593 | ErrorFn("unknown argument '" + Spelling + "', did you mean '" + Nearest + |
594 | "'?" ); |
595 | } |
596 | return Args; |
597 | } |
598 | |
599 | static std::string getOptionHelpName(const OptTable &Opts, OptSpecifier Id) { |
600 | const Option O = Opts.getOption(Opt: Id); |
601 | std::string Name = O.getPrefixedName().str(); |
602 | |
603 | // Add metavar, if used. |
604 | switch (O.getKind()) { |
605 | case Option::GroupClass: case Option::InputClass: case Option::UnknownClass: |
606 | llvm_unreachable("Invalid option with help text." ); |
607 | |
608 | case Option::MultiArgClass: |
609 | if (const char *MetaVarName = Opts.getOptionMetaVar(id: Id)) { |
610 | // For MultiArgs, metavar is full list of all argument names. |
611 | Name += ' '; |
612 | Name += MetaVarName; |
613 | } |
614 | else { |
615 | // For MultiArgs<N>, if metavar not supplied, print <value> N times. |
616 | for (unsigned i=0, e=O.getNumArgs(); i< e; ++i) { |
617 | Name += " <value>" ; |
618 | } |
619 | } |
620 | break; |
621 | |
622 | case Option::FlagClass: |
623 | break; |
624 | |
625 | case Option::ValuesClass: |
626 | break; |
627 | |
628 | case Option::SeparateClass: case Option::JoinedOrSeparateClass: |
629 | case Option::RemainingArgsClass: case Option::RemainingArgsJoinedClass: |
630 | Name += ' '; |
631 | [[fallthrough]]; |
632 | case Option::JoinedClass: case Option::CommaJoinedClass: |
633 | case Option::JoinedAndSeparateClass: |
634 | if (const char *MetaVarName = Opts.getOptionMetaVar(id: Id)) |
635 | Name += MetaVarName; |
636 | else |
637 | Name += "<value>" ; |
638 | break; |
639 | } |
640 | |
641 | return Name; |
642 | } |
643 | |
644 | namespace { |
645 | struct OptionInfo { |
646 | std::string Name; |
647 | StringRef HelpText; |
648 | }; |
649 | } // namespace |
650 | |
651 | static void PrintHelpOptionList(raw_ostream &OS, StringRef Title, |
652 | std::vector<OptionInfo> &OptionHelp) { |
653 | OS << Title << ":\n" ; |
654 | |
655 | // Find the maximum option length. |
656 | unsigned OptionFieldWidth = 0; |
657 | for (const OptionInfo &Opt : OptionHelp) { |
658 | // Limit the amount of padding we are willing to give up for alignment. |
659 | unsigned Length = Opt.Name.size(); |
660 | if (Length <= 23) |
661 | OptionFieldWidth = std::max(a: OptionFieldWidth, b: Length); |
662 | } |
663 | |
664 | const unsigned InitialPad = 2; |
665 | for (const OptionInfo &Opt : OptionHelp) { |
666 | const std::string &Option = Opt.Name; |
667 | int Pad = OptionFieldWidth + InitialPad; |
668 | int FirstLinePad = OptionFieldWidth - int(Option.size()); |
669 | OS.indent(NumSpaces: InitialPad) << Option; |
670 | |
671 | // Break on long option names. |
672 | if (FirstLinePad < 0) { |
673 | OS << "\n" ; |
674 | FirstLinePad = OptionFieldWidth + InitialPad; |
675 | Pad = FirstLinePad; |
676 | } |
677 | |
678 | SmallVector<StringRef> Lines; |
679 | Opt.HelpText.split(A&: Lines, Separator: '\n'); |
680 | assert(Lines.size() && "Expected at least the first line in the help text" ); |
681 | auto *LinesIt = Lines.begin(); |
682 | OS.indent(NumSpaces: FirstLinePad + 1) << *LinesIt << '\n'; |
683 | while (Lines.end() != ++LinesIt) |
684 | OS.indent(NumSpaces: Pad + 1) << *LinesIt << '\n'; |
685 | } |
686 | } |
687 | |
688 | static const char *getOptionHelpGroup(const OptTable &Opts, OptSpecifier Id) { |
689 | unsigned GroupID = Opts.getOptionGroupID(id: Id); |
690 | |
691 | // If not in a group, return the default help group. |
692 | if (!GroupID) |
693 | return "OPTIONS" ; |
694 | |
695 | // Abuse the help text of the option groups to store the "help group" |
696 | // name. |
697 | // |
698 | // FIXME: Split out option groups. |
699 | if (const char *GroupHelp = Opts.getOptionHelpText(id: GroupID)) |
700 | return GroupHelp; |
701 | |
702 | // Otherwise keep looking. |
703 | return getOptionHelpGroup(Opts, Id: GroupID); |
704 | } |
705 | |
706 | void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title, |
707 | bool ShowHidden, bool ShowAllAliases, |
708 | Visibility VisibilityMask) const { |
709 | return internalPrintHelp( |
710 | OS, Usage, Title, ShowHidden, ShowAllAliases, |
711 | ExcludeOption: [VisibilityMask](const Info &CandidateInfo) -> bool { |
712 | return (CandidateInfo.Visibility & VisibilityMask) == 0; |
713 | }, |
714 | VisibilityMask); |
715 | } |
716 | |
717 | void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title, |
718 | unsigned FlagsToInclude, unsigned FlagsToExclude, |
719 | bool ShowAllAliases) const { |
720 | bool ShowHidden = !(FlagsToExclude & HelpHidden); |
721 | FlagsToExclude &= ~HelpHidden; |
722 | return internalPrintHelp( |
723 | OS, Usage, Title, ShowHidden, ShowAllAliases, |
724 | ExcludeOption: [FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) { |
725 | if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude)) |
726 | return true; |
727 | if (CandidateInfo.Flags & FlagsToExclude) |
728 | return true; |
729 | return false; |
730 | }, |
731 | VisibilityMask: Visibility(0)); |
732 | } |
733 | |
734 | void OptTable::internalPrintHelp( |
735 | raw_ostream &OS, const char *Usage, const char *Title, bool ShowHidden, |
736 | bool ShowAllAliases, std::function<bool(const Info &)> ExcludeOption, |
737 | Visibility VisibilityMask) const { |
738 | OS << "OVERVIEW: " << Title << "\n\n" ; |
739 | OS << "USAGE: " << Usage << "\n\n" ; |
740 | |
741 | // Render help text into a map of group-name to a list of (option, help) |
742 | // pairs. |
743 | std::map<std::string, std::vector<OptionInfo>> GroupedOptionHelp; |
744 | |
745 | for (unsigned Id = 1, e = getNumOptions() + 1; Id != e; ++Id) { |
746 | // FIXME: Split out option groups. |
747 | if (getOptionKind(id: Id) == Option::GroupClass) |
748 | continue; |
749 | |
750 | const Info &CandidateInfo = getInfo(Opt: Id); |
751 | if (!ShowHidden && (CandidateInfo.Flags & opt::HelpHidden)) |
752 | continue; |
753 | |
754 | if (ExcludeOption(CandidateInfo)) |
755 | continue; |
756 | |
757 | // If an alias doesn't have a help text, show a help text for the aliased |
758 | // option instead. |
759 | const char *HelpText = getOptionHelpText(id: Id, VisibilityMask); |
760 | if (!HelpText && ShowAllAliases) { |
761 | const Option Alias = getOption(Opt: Id).getAlias(); |
762 | if (Alias.isValid()) |
763 | HelpText = getOptionHelpText(id: Alias.getID(), VisibilityMask); |
764 | } |
765 | |
766 | if (HelpText && (strlen(s: HelpText) != 0)) { |
767 | const char *HelpGroup = getOptionHelpGroup(Opts: *this, Id); |
768 | const std::string &OptName = getOptionHelpName(Opts: *this, Id); |
769 | GroupedOptionHelp[HelpGroup].push_back(x: {.Name: OptName, .HelpText: HelpText}); |
770 | } |
771 | } |
772 | |
773 | for (auto& OptionGroup : GroupedOptionHelp) { |
774 | if (OptionGroup.first != GroupedOptionHelp.begin()->first) |
775 | OS << "\n" ; |
776 | PrintHelpOptionList(OS, Title: OptionGroup.first, OptionHelp&: OptionGroup.second); |
777 | } |
778 | |
779 | OS.flush(); |
780 | } |
781 | |
782 | GenericOptTable::GenericOptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase) |
783 | : OptTable(OptionInfos, IgnoreCase) { |
784 | |
785 | std::set<StringLiteral> TmpPrefixesUnion; |
786 | for (auto const &Info : OptionInfos.drop_front(N: FirstSearchableIndex)) |
787 | TmpPrefixesUnion.insert(first: Info.Prefixes.begin(), last: Info.Prefixes.end()); |
788 | PrefixesUnionBuffer.append(in_start: TmpPrefixesUnion.begin(), in_end: TmpPrefixesUnion.end()); |
789 | buildPrefixChars(); |
790 | } |
791 | |