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