Option parsing: properly handle flag aliases for joined options (PR23394)
[oota-llvm.git] / lib / Option / Option.cpp
1 //===--- Option.cpp - Abstract Driver Options -----------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "llvm/Option/Option.h"
11 #include "llvm/ADT/Twine.h"
12 #include "llvm/Option/Arg.h"
13 #include "llvm/Option/ArgList.h"
14 #include "llvm/Support/ErrorHandling.h"
15 #include "llvm/Support/raw_ostream.h"
16 #include <algorithm>
17 #include <cassert>
18
19 using namespace llvm;
20 using namespace llvm::opt;
21
22 Option::Option(const OptTable::Info *info, const OptTable *owner)
23   : Info(info), Owner(owner) {
24
25   // Multi-level aliases are not supported. This just simplifies option
26   // tracking, it is not an inherent limitation.
27   assert((!Info || !getAlias().isValid() || !getAlias().getAlias().isValid()) &&
28          "Multi-level aliases are not supported.");
29
30   if (Info && getAliasArgs()) {
31     assert(getAlias().isValid() && "Only alias options can have alias args.");
32     assert(getKind() == FlagClass && "Only Flag aliases can have alias args.");
33     assert(getAlias().getKind() != FlagClass &&
34            "Cannot provide alias args to a flag option.");
35   }
36 }
37
38 void Option::dump() const {
39   llvm::errs() << "<";
40   switch (getKind()) {
41 #define P(N) case N: llvm::errs() << #N; break
42     P(GroupClass);
43     P(InputClass);
44     P(UnknownClass);
45     P(FlagClass);
46     P(JoinedClass);
47     P(SeparateClass);
48     P(CommaJoinedClass);
49     P(MultiArgClass);
50     P(JoinedOrSeparateClass);
51     P(JoinedAndSeparateClass);
52     P(RemainingArgsClass);
53 #undef P
54   }
55
56   if (Info->Prefixes) {
57     llvm::errs() << " Prefixes:[";
58     for (const char * const *Pre = Info->Prefixes; *Pre != nullptr; ++Pre) {
59       llvm::errs() << '"' << *Pre << (*(Pre + 1) == nullptr ? "\"" : "\", ");
60     }
61     llvm::errs() << ']';
62   }
63
64   llvm::errs() << " Name:\"" << getName() << '"';
65
66   const Option Group = getGroup();
67   if (Group.isValid()) {
68     llvm::errs() << " Group:";
69     Group.dump();
70   }
71
72   const Option Alias = getAlias();
73   if (Alias.isValid()) {
74     llvm::errs() << " Alias:";
75     Alias.dump();
76   }
77
78   if (getKind() == MultiArgClass)
79     llvm::errs() << " NumArgs:" << getNumArgs();
80
81   llvm::errs() << ">\n";
82 }
83
84 bool Option::matches(OptSpecifier Opt) const {
85   // Aliases are never considered in matching, look through them.
86   const Option Alias = getAlias();
87   if (Alias.isValid())
88     return Alias.matches(Opt);
89
90   // Check exact match.
91   if (getID() == Opt.getID())
92     return true;
93
94   const Option Group = getGroup();
95   if (Group.isValid())
96     return Group.matches(Opt);
97   return false;
98 }
99
100 Arg *Option::accept(const ArgList &Args,
101                     unsigned &Index,
102                     unsigned ArgSize) const {
103   const Option &UnaliasedOption = getUnaliasedOption();
104   StringRef Spelling;
105   // If the option was an alias, get the spelling from the unaliased one.
106   if (getID() == UnaliasedOption.getID()) {
107     Spelling = StringRef(Args.getArgString(Index), ArgSize);
108   } else {
109     Spelling = Args.MakeArgString(Twine(UnaliasedOption.getPrefix()) +
110                                   Twine(UnaliasedOption.getName()));
111   }
112
113   switch (getKind()) {
114   case FlagClass: {
115     if (ArgSize != strlen(Args.getArgString(Index)))
116       return nullptr;
117
118     Arg *A = new Arg(UnaliasedOption, Spelling, Index++);
119     if (getAliasArgs()) {
120       const char *Val = getAliasArgs();
121       while (*Val != '\0') {
122         A->getValues().push_back(Val);
123
124         // Move past the '\0' to the next argument.
125         Val += strlen(Val) + 1;
126       }
127     }
128
129     if (UnaliasedOption.getKind() == JoinedClass && !getAliasArgs())
130       // A Flag alias for a Joined option must provide an argument.
131       A->getValues().push_back("");
132
133     return A;
134   }
135   case JoinedClass: {
136     const char *Value = Args.getArgString(Index) + ArgSize;
137     return new Arg(UnaliasedOption, Spelling, Index++, Value);
138   }
139   case CommaJoinedClass: {
140     // Always matches.
141     const char *Str = Args.getArgString(Index) + ArgSize;
142     Arg *A = new Arg(UnaliasedOption, Spelling, Index++);
143
144     // Parse out the comma separated values.
145     const char *Prev = Str;
146     for (;; ++Str) {
147       char c = *Str;
148
149       if (!c || c == ',') {
150         if (Prev != Str) {
151           char *Value = new char[Str - Prev + 1];
152           memcpy(Value, Prev, Str - Prev);
153           Value[Str - Prev] = '\0';
154           A->getValues().push_back(Value);
155         }
156
157         if (!c)
158           break;
159
160         Prev = Str + 1;
161       }
162     }
163     A->setOwnsValues(true);
164
165     return A;
166   }
167   case SeparateClass:
168     // Matches iff this is an exact match.
169     // FIXME: Avoid strlen.
170     if (ArgSize != strlen(Args.getArgString(Index)))
171       return nullptr;
172
173     Index += 2;
174     if (Index > Args.getNumInputArgStrings() ||
175         Args.getArgString(Index - 1) == nullptr)
176       return nullptr;
177
178     return new Arg(UnaliasedOption, Spelling,
179                    Index - 2, Args.getArgString(Index - 1));
180   case MultiArgClass: {
181     // Matches iff this is an exact match.
182     // FIXME: Avoid strlen.
183     if (ArgSize != strlen(Args.getArgString(Index)))
184       return nullptr;
185
186     Index += 1 + getNumArgs();
187     if (Index > Args.getNumInputArgStrings())
188       return nullptr;
189
190     Arg *A = new Arg(UnaliasedOption, Spelling, Index - 1 - getNumArgs(),
191                       Args.getArgString(Index - getNumArgs()));
192     for (unsigned i = 1; i != getNumArgs(); ++i)
193       A->getValues().push_back(Args.getArgString(Index - getNumArgs() + i));
194     return A;
195   }
196   case JoinedOrSeparateClass: {
197     // If this is not an exact match, it is a joined arg.
198     // FIXME: Avoid strlen.
199     if (ArgSize != strlen(Args.getArgString(Index))) {
200       const char *Value = Args.getArgString(Index) + ArgSize;
201       return new Arg(*this, Spelling, Index++, Value);
202     }
203
204     // Otherwise it must be separate.
205     Index += 2;
206     if (Index > Args.getNumInputArgStrings() ||
207         Args.getArgString(Index - 1) == nullptr)
208       return nullptr;
209
210     return new Arg(UnaliasedOption, Spelling,
211                    Index - 2, Args.getArgString(Index - 1));
212   }
213   case JoinedAndSeparateClass:
214     // Always matches.
215     Index += 2;
216     if (Index > Args.getNumInputArgStrings() ||
217         Args.getArgString(Index - 1) == nullptr)
218       return nullptr;
219
220     return new Arg(UnaliasedOption, Spelling, Index - 2,
221                    Args.getArgString(Index - 2) + ArgSize,
222                    Args.getArgString(Index - 1));
223   case RemainingArgsClass: {
224     // Matches iff this is an exact match.
225     // FIXME: Avoid strlen.
226     if (ArgSize != strlen(Args.getArgString(Index)))
227       return nullptr;
228     Arg *A = new Arg(UnaliasedOption, Spelling, Index++);
229     while (Index < Args.getNumInputArgStrings() &&
230            Args.getArgString(Index) != nullptr)
231       A->getValues().push_back(Args.getArgString(Index++));
232     return A;
233   }
234   default:
235     llvm_unreachable("Invalid option kind!");
236   }
237 }