llvm.org GIT mirror llvm / 2957273
Option parsing: support case-insensitive option matching. Re-submitting r189416 with fix for Windows build on where strcasecmp is not defined. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@189501 91177308-0d34-0410-b5e6-96231b3b80d8 Rui Ueyama 5 years ago
4 changed file(s) with 79 addition(s) and 32 deletion(s). Raw diff Collapse all Expand all
5050 /// \brief The static option information table.
5151 const Info *OptionInfos;
5252 unsigned NumOptionInfos;
53 bool IgnoreCase;
5354
5455 unsigned TheInputOptionID;
5556 unsigned TheUnknownOptionID;
7172 }
7273
7374 protected:
74 OptTable(const Info *_OptionInfos, unsigned _NumOptionInfos);
75 OptTable(const Info *_OptionInfos, unsigned _NumOptionInfos,
76 bool _IgnoreCase = false);
7577 public:
7678 ~OptTable();
7779
1313 #include "llvm/Support/ErrorHandling.h"
1414 #include "llvm/Support/raw_ostream.h"
1515 #include
16 #include
1617 #include
1718
1819 using namespace llvm;
1920 using namespace llvm::opt;
2021
21 // Ordering on Info. The ordering is *almost* lexicographic, with two
22 // exceptions. First, '\0' comes at the end of the alphabet instead of
23 // the beginning (thus options precede any other options which prefix
24 // them). Second, for options with the same name, the less permissive
25 // version should come first; a Flag option should precede a Joined
26 // option, for example.
27
28 static int StrCmpOptionName(const char *A, const char *B) {
29 char a = *A, b = *B;
22 namespace llvm {
23 namespace opt {
24
25 // Ordering on Info. The ordering is *almost* case-insensitive lexicographic,
26 // with an exceptions. '\0' comes at the end of the alphabet instead of the
27 // beginning (thus options precede any other options which prefix them).
28 static int StrCmpOptionNameIgnoreCase(const char *A, const char *B) {
29 const char *X = A, *Y = B;
30 char a = tolower(*A), b = tolower(*B);
3031 while (a == b) {
3132 if (a == '\0')
3233 return 0;
3334
34 a = *++A;
35 b = *++B;
35 a = tolower(*++X);
36 b = tolower(*++Y);
3637 }
3738
3839 if (a == '\0') // A is a prefix of B.
4445 return (a < b) ? -1 : 1;
4546 }
4647
47 namespace llvm {
48 namespace opt {
48 static int StrCmpOptionName(const char *A, const char *B) {
49 if (int N = StrCmpOptionNameIgnoreCase(A, B))
50 return N;
51 return strcmp(A, B);
52 }
4953
5054 static inline bool operator<(const OptTable::Info &A, const OptTable::Info &B) {
5155 if (&A == &B)
5256 return false;
5357
5458 if (int N = StrCmpOptionName(A.Name, B.Name))
55 return N == -1;
59 return N < 0;
5660
5761 for (const char * const *APre = A.Prefixes,
5862 * const *BPre = B.Prefixes;
5963 *APre != 0 && *BPre != 0; ++APre, ++BPre) {
6064 if (int N = StrCmpOptionName(*APre, *BPre))
61 return N == -1;
65 return N < 0;
6266 }
6367
6468 // Names are the same, check that classes are in order; exactly one
7074
7175 // Support lower_bound between info and an option name.
7276 static inline bool operator<(const OptTable::Info &I, const char *Name) {
73 return StrCmpOptionName(I.Name, Name) == -1;
77 return StrCmpOptionNameIgnoreCase(I.Name, Name) < 0;
7478 }
7579 static inline bool operator<(const char *Name, const OptTable::Info &I) {
76 return StrCmpOptionName(Name, I.Name) == -1;
80 return StrCmpOptionNameIgnoreCase(Name, I.Name) < 0;
7781 }
7882 }
7983 }
8084
8185 OptSpecifier::OptSpecifier(const Option *Opt) : ID(Opt->getID()) {}
8286
83 OptTable::OptTable(const Info *_OptionInfos, unsigned _NumOptionInfos)
87 OptTable::OptTable(const Info *_OptionInfos, unsigned _NumOptionInfos,
88 bool _IgnoreCase)
8489 : OptionInfos(_OptionInfos),
8590 NumOptionInfos(_NumOptionInfos),
91 IgnoreCase(_IgnoreCase),
8692 TheInputOptionID(0),
8793 TheUnknownOptionID(0),
8894 FirstSearchableIndex(0)
169175 return true;
170176 }
171177
178 // Returns true if X starts with Y, ignoring case.
179 static bool startsWithIgnoreCase(StringRef X, StringRef Y) {
180 if (X.size() < Y.size())
181 return false;
182 return X.substr(0, Y.size()).equals_lower(Y);
183 }
184
172185 /// \returns Matched size. 0 means no match.
173 static unsigned matchOption(const OptTable::Info *I, StringRef Str) {
186 static unsigned matchOption(const OptTable::Info *I, StringRef Str,
187 bool IgnoreCase) {
174188 for (const char * const *Pre = I->Prefixes; *Pre != 0; ++Pre) {
175189 StringRef Prefix(*Pre);
176 if (Str.startswith(Prefix) && Str.substr(Prefix.size()).startswith(I->Name))
177 return Prefix.size() + StringRef(I->Name).size();
190 if (Str.startswith(Prefix)) {
191 StringRef Rest = Str.substr(Prefix.size());
192 bool Matched = IgnoreCase
193 ? startsWithIgnoreCase(Rest, I->Name)
194 : Rest.startswith(I->Name);
195 if (Matched)
196 return Prefix.size() + StringRef(I->Name).size();
197 }
178198 }
179199 return 0;
180200 }
209229 unsigned ArgSize = 0;
210230 // Scan for first option which is a proper prefix.
211231 for (; Start != End; ++Start)
212 if ((ArgSize = matchOption(Start, Str)))
232 if ((ArgSize = matchOption(Start, Str, IgnoreCase)))
213233 break;
214234 if (Start == End)
215235 break;
1919 enum ID {
2020 OPT_INVALID = 0, // This is not an option ID.
2121 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
22 HELPTEXT, METAVAR) OPT_##ID,
22 HELPTEXT, METAVAR) OPT_##ID,
2323 #include "Opts.inc"
2424 LastOption
2525 #undef OPTION
4747 namespace {
4848 class TestOptTable : public OptTable {
4949 public:
50 TestOptTable()
51 : OptTable(InfoTable, array_lengthof(InfoTable)) {}
50 TestOptTable(bool IgnoreCase = false)
51 : OptTable(InfoTable, array_lengthof(InfoTable), IgnoreCase) {}
5252 };
5353 }
5454
156156 EXPECT_EQ(AL->getAllArgValues(OPT_B)[1], "bar");
157157 }
158158
159 TEST(Option, IgnoreCase) {
160 TestOptTable T(true);
161 unsigned MAI, MAC;
162
163 const char *MyArgs[] = { "-a", "-joo" };
164 OwningPtr AL(T.ParseArgs(MyArgs, array_endof(MyArgs), MAI, MAC));
165 EXPECT_TRUE(AL->hasArg(OPT_A));
166 EXPECT_TRUE(AL->hasArg(OPT_B));
167 }
168
169 TEST(Option, DoNotIgnoreCase) {
170 TestOptTable T;
171 unsigned MAI, MAC;
172
173 const char *MyArgs[] = { "-a", "-joo" };
174 OwningPtr AL(T.ParseArgs(MyArgs, array_endof(MyArgs), MAI, MAC));
175 EXPECT_FALSE(AL->hasArg(OPT_A));
176 EXPECT_FALSE(AL->hasArg(OPT_B));
177 }
178
159179 TEST(Option, SlurpEmpty) {
160180 TestOptTable T;
161181 unsigned MAI, MAC;
1212 #include "llvm/ADT/Twine.h"
1313 #include "llvm/TableGen/Record.h"
1414 #include "llvm/TableGen/TableGenBackend.h"
15 #include
16 #include
1517 #include
1618
1719 using namespace llvm;
1820
21 // Ordering on Info. The logic should match with the consumer-side function in
22 // llvm/Option/OptTable.h.
1923 static int StrCmpOptionName(const char *A, const char *B) {
20 char a = *A, b = *B;
24 const char *X = A, *Y = B;
25 char a = tolower(*A), b = tolower(*B);
2126 while (a == b) {
2227 if (a == '\0')
23 return 0;
24
25 a = *++A;
26 b = *++B;
28 return strcmp(A, B);
29
30 a = tolower(*++X);
31 b = tolower(*++Y);
2732 }
2833
2934 if (a == '\0') // A is a prefix of B.
4954 if (!ASent)
5055 if (int Cmp = StrCmpOptionName(A->getValueAsString("Name").c_str(),
5156 B->getValueAsString("Name").c_str()))
52 return Cmp;
57 return Cmp;
5358
5459 if (!ASent) {
5560 std::vector APrefixes = A->getValueAsListOfStrings("Prefixes");