1 //===- unittest/Support/OptionParsingTest.cpp - OptTable tests ------------===// 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/ADT/STLExtras.h" 10 #include "llvm/Option/Arg.h" 11 #include "llvm/Option/ArgList.h" 12 #include "llvm/Option/Option.h" 13 #include "gtest/gtest.h" 14 15 using namespace llvm; 16 using namespace llvm::opt; 17 18 enum ID { 19 OPT_INVALID = 0, // This is not an option ID. 20 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 21 HELPTEXT, METAVAR, VALUES) \ 22 OPT_##ID, 23 #include "Opts.inc" 24 LastOption 25 #undef OPTION 26 }; 27 28 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; 29 #include "Opts.inc" 30 #undef PREFIX 31 32 enum OptionFlags { 33 OptFlag1 = (1 << 4), 34 OptFlag2 = (1 << 5), 35 OptFlag3 = (1 << 6) 36 }; 37 38 static const OptTable::Info InfoTable[] = { 39 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 40 HELPTEXT, METAVAR, VALUES) \ 41 {PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, Option::KIND##Class, \ 42 PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS, VALUES}, 43 #include "Opts.inc" 44 #undef OPTION 45 }; 46 47 namespace { 48 class TestOptTable : public OptTable { 49 public: 50 TestOptTable(bool IgnoreCase = false) 51 : OptTable(InfoTable, IgnoreCase) {} 52 }; 53 } 54 55 const char *Args[] = { 56 "-A", 57 "-Bhi", 58 "--C=desu", 59 "-C", "bye", 60 "-D,adena", 61 "-E", "apple", "bloom", 62 "-Fblarg", 63 "-F", "42", 64 "-Gchuu", "2" 65 }; 66 67 TEST(Option, OptionParsing) { 68 TestOptTable T; 69 unsigned MAI, MAC; 70 InputArgList AL = T.ParseArgs(Args, MAI, MAC); 71 72 // Check they all exist. 73 EXPECT_TRUE(AL.hasArg(OPT_A)); 74 EXPECT_TRUE(AL.hasArg(OPT_B)); 75 EXPECT_TRUE(AL.hasArg(OPT_C)); 76 EXPECT_TRUE(AL.hasArg(OPT_D)); 77 EXPECT_TRUE(AL.hasArg(OPT_E)); 78 EXPECT_TRUE(AL.hasArg(OPT_F)); 79 EXPECT_TRUE(AL.hasArg(OPT_G)); 80 81 // Check the values. 82 EXPECT_EQ("hi", AL.getLastArgValue(OPT_B)); 83 EXPECT_EQ("bye", AL.getLastArgValue(OPT_C)); 84 EXPECT_EQ("adena", AL.getLastArgValue(OPT_D)); 85 std::vector<std::string> Es = AL.getAllArgValues(OPT_E); 86 EXPECT_EQ("apple", Es[0]); 87 EXPECT_EQ("bloom", Es[1]); 88 EXPECT_EQ("42", AL.getLastArgValue(OPT_F)); 89 std::vector<std::string> Gs = AL.getAllArgValues(OPT_G); 90 EXPECT_EQ("chuu", Gs[0]); 91 EXPECT_EQ("2", Gs[1]); 92 93 // Check the help text. 94 std::string Help; 95 raw_string_ostream RSO(Help); 96 T.printHelp(RSO, "test", "title!"); 97 EXPECT_NE(std::string::npos, Help.find("-A")); 98 99 // Check usage line. 100 T.printHelp(RSO, "name [options] file...", "title!"); 101 EXPECT_NE(std::string::npos, Help.find("USAGE: name [options] file...\n")); 102 103 // Test aliases. 104 auto Cs = AL.filtered(OPT_C); 105 ASSERT_NE(Cs.begin(), Cs.end()); 106 EXPECT_EQ("desu", StringRef((*Cs.begin())->getValue())); 107 ArgStringList ASL; 108 (*Cs.begin())->render(AL, ASL); 109 ASSERT_EQ(2u, ASL.size()); 110 EXPECT_EQ("-C", StringRef(ASL[0])); 111 EXPECT_EQ("desu", StringRef(ASL[1])); 112 } 113 114 TEST(Option, ParseWithFlagExclusions) { 115 TestOptTable T; 116 unsigned MAI, MAC; 117 118 // Exclude flag3 to avoid parsing as OPT_SLASH_C. 119 InputArgList AL = T.ParseArgs(Args, MAI, MAC, 120 /*FlagsToInclude=*/0, 121 /*FlagsToExclude=*/OptFlag3); 122 EXPECT_TRUE(AL.hasArg(OPT_A)); 123 EXPECT_TRUE(AL.hasArg(OPT_C)); 124 EXPECT_FALSE(AL.hasArg(OPT_SLASH_C)); 125 126 // Exclude flag1 to avoid parsing as OPT_C. 127 AL = T.ParseArgs(Args, MAI, MAC, 128 /*FlagsToInclude=*/0, 129 /*FlagsToExclude=*/OptFlag1); 130 EXPECT_TRUE(AL.hasArg(OPT_B)); 131 EXPECT_FALSE(AL.hasArg(OPT_C)); 132 EXPECT_TRUE(AL.hasArg(OPT_SLASH_C)); 133 134 const char *NewArgs[] = { "/C", "foo", "--C=bar" }; 135 AL = T.ParseArgs(NewArgs, MAI, MAC); 136 EXPECT_TRUE(AL.hasArg(OPT_SLASH_C)); 137 EXPECT_TRUE(AL.hasArg(OPT_C)); 138 EXPECT_EQ("foo", AL.getLastArgValue(OPT_SLASH_C)); 139 EXPECT_EQ("bar", AL.getLastArgValue(OPT_C)); 140 } 141 142 TEST(Option, ParseAliasInGroup) { 143 TestOptTable T; 144 unsigned MAI, MAC; 145 146 const char *MyArgs[] = { "-I" }; 147 InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); 148 EXPECT_TRUE(AL.hasArg(OPT_H)); 149 } 150 151 TEST(Option, AliasArgs) { 152 TestOptTable T; 153 unsigned MAI, MAC; 154 155 const char *MyArgs[] = { "-J", "-Joo" }; 156 InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); 157 EXPECT_TRUE(AL.hasArg(OPT_B)); 158 EXPECT_EQ("foo", AL.getAllArgValues(OPT_B)[0]); 159 EXPECT_EQ("bar", AL.getAllArgValues(OPT_B)[1]); 160 } 161 162 TEST(Option, IgnoreCase) { 163 TestOptTable T(true); 164 unsigned MAI, MAC; 165 166 const char *MyArgs[] = { "-a", "-joo" }; 167 InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); 168 EXPECT_TRUE(AL.hasArg(OPT_A)); 169 EXPECT_TRUE(AL.hasArg(OPT_B)); 170 } 171 172 TEST(Option, DoNotIgnoreCase) { 173 TestOptTable T; 174 unsigned MAI, MAC; 175 176 const char *MyArgs[] = { "-a", "-joo" }; 177 InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); 178 EXPECT_FALSE(AL.hasArg(OPT_A)); 179 EXPECT_FALSE(AL.hasArg(OPT_B)); 180 } 181 182 TEST(Option, SlurpEmpty) { 183 TestOptTable T; 184 unsigned MAI, MAC; 185 186 const char *MyArgs[] = { "-A", "-slurp" }; 187 InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); 188 EXPECT_TRUE(AL.hasArg(OPT_A)); 189 EXPECT_TRUE(AL.hasArg(OPT_Slurp)); 190 EXPECT_EQ(0U, AL.getAllArgValues(OPT_Slurp).size()); 191 } 192 193 TEST(Option, Slurp) { 194 TestOptTable T; 195 unsigned MAI, MAC; 196 197 const char *MyArgs[] = { "-A", "-slurp", "-B", "--", "foo" }; 198 InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); 199 EXPECT_EQ(AL.size(), 2U); 200 EXPECT_TRUE(AL.hasArg(OPT_A)); 201 EXPECT_FALSE(AL.hasArg(OPT_B)); 202 EXPECT_TRUE(AL.hasArg(OPT_Slurp)); 203 EXPECT_EQ(3U, AL.getAllArgValues(OPT_Slurp).size()); 204 EXPECT_EQ("-B", AL.getAllArgValues(OPT_Slurp)[0]); 205 EXPECT_EQ("--", AL.getAllArgValues(OPT_Slurp)[1]); 206 EXPECT_EQ("foo", AL.getAllArgValues(OPT_Slurp)[2]); 207 } 208 209 TEST(Option, SlurpJoinedEmpty) { 210 TestOptTable T; 211 unsigned MAI, MAC; 212 213 const char *MyArgs[] = { "-A", "-slurpjoined" }; 214 InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); 215 EXPECT_TRUE(AL.hasArg(OPT_A)); 216 EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined)); 217 EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined).size(), 0U); 218 } 219 220 TEST(Option, SlurpJoinedOneJoined) { 221 TestOptTable T; 222 unsigned MAI, MAC; 223 224 const char *MyArgs[] = { "-A", "-slurpjoinedfoo" }; 225 InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); 226 EXPECT_TRUE(AL.hasArg(OPT_A)); 227 EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined)); 228 EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined).size(), 1U); 229 EXPECT_EQ(AL.getAllArgValues(OPT_SlurpJoined)[0], "foo"); 230 } 231 232 TEST(Option, SlurpJoinedAndSeparate) { 233 TestOptTable T; 234 unsigned MAI, MAC; 235 236 const char *MyArgs[] = { "-A", "-slurpjoinedfoo", "bar", "baz" }; 237 InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); 238 EXPECT_TRUE(AL.hasArg(OPT_A)); 239 EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined)); 240 EXPECT_EQ(3U, AL.getAllArgValues(OPT_SlurpJoined).size()); 241 EXPECT_EQ("foo", AL.getAllArgValues(OPT_SlurpJoined)[0]); 242 EXPECT_EQ("bar", AL.getAllArgValues(OPT_SlurpJoined)[1]); 243 EXPECT_EQ("baz", AL.getAllArgValues(OPT_SlurpJoined)[2]); 244 } 245 246 TEST(Option, SlurpJoinedButSeparate) { 247 TestOptTable T; 248 unsigned MAI, MAC; 249 250 const char *MyArgs[] = { "-A", "-slurpjoined", "foo", "bar", "baz" }; 251 InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); 252 EXPECT_TRUE(AL.hasArg(OPT_A)); 253 EXPECT_TRUE(AL.hasArg(OPT_SlurpJoined)); 254 EXPECT_EQ(3U, AL.getAllArgValues(OPT_SlurpJoined).size()); 255 EXPECT_EQ("foo", AL.getAllArgValues(OPT_SlurpJoined)[0]); 256 EXPECT_EQ("bar", AL.getAllArgValues(OPT_SlurpJoined)[1]); 257 EXPECT_EQ("baz", AL.getAllArgValues(OPT_SlurpJoined)[2]); 258 } 259 260 TEST(Option, FlagAliasToJoined) { 261 TestOptTable T; 262 unsigned MAI, MAC; 263 264 // Check that a flag alias provides an empty argument to a joined option. 265 const char *MyArgs[] = { "-K" }; 266 InputArgList AL = T.ParseArgs(MyArgs, MAI, MAC); 267 EXPECT_EQ(AL.size(), 1U); 268 EXPECT_TRUE(AL.hasArg(OPT_B)); 269 EXPECT_EQ(1U, AL.getAllArgValues(OPT_B).size()); 270 EXPECT_EQ("", AL.getAllArgValues(OPT_B)[0]); 271 } 272 273 TEST(Option, FindNearest) { 274 TestOptTable T; 275 std::string Nearest; 276 277 // Options that are too short should not be considered 278 // "near" other short options. 279 EXPECT_GT(T.findNearest("-A", Nearest), 4U); 280 EXPECT_GT(T.findNearest("/C", Nearest), 4U); 281 EXPECT_GT(T.findNearest("--C=foo", Nearest), 4U); 282 283 // The nearest candidate should mirror the amount of prefix 284 // characters used in the original string. 285 EXPECT_EQ(1U, T.findNearest("-blorb", Nearest)); 286 EXPECT_EQ(Nearest, "-blorp"); 287 EXPECT_EQ(1U, T.findNearest("--blorm", Nearest)); 288 EXPECT_EQ(Nearest, "--blorp"); 289 EXPECT_EQ(1U, T.findNearest("-blarg", Nearest)); 290 EXPECT_EQ(Nearest, "-blarn"); 291 EXPECT_EQ(1U, T.findNearest("--blarm", Nearest)); 292 EXPECT_EQ(Nearest, "--blarn"); 293 EXPECT_EQ(1U, T.findNearest("-fjormp", Nearest)); 294 EXPECT_EQ(Nearest, "--fjormp"); 295 296 // The nearest candidate respects the prefix and value delimiter 297 // of the original string. 298 EXPECT_EQ(1U, T.findNearest("/framb:foo", Nearest)); 299 EXPECT_EQ(Nearest, "/cramb:foo"); 300 301 // `--glormp` should have an editing distance > 0 from `--glormp=`. 302 EXPECT_GT(T.findNearest("--glorrmp", Nearest), 0U); 303 EXPECT_EQ(Nearest, "--glorrmp="); 304 EXPECT_EQ(0U, T.findNearest("--glorrmp=foo", Nearest)); 305 306 // `--blurmps` should correct to `--blurmp`, not `--blurmp=`, even though 307 // both naively have an editing distance of 1. 308 EXPECT_EQ(1U, T.findNearest("--blurmps", Nearest)); 309 EXPECT_EQ(Nearest, "--blurmp"); 310 311 // ...but `--blurmps=foo` should correct to `--blurmp=foo`. 312 EXPECT_EQ(1U, T.findNearest("--blurmps=foo", Nearest)); 313 EXPECT_EQ(Nearest, "--blurmp=foo"); 314 315 // Flags should be included and excluded as specified. 316 EXPECT_EQ(1U, T.findNearest("-doopf", Nearest, /*FlagsToInclude=*/OptFlag2)); 317 EXPECT_EQ(Nearest, "-doopf2"); 318 EXPECT_EQ(1U, T.findNearest("-doopf", Nearest, 319 /*FlagsToInclude=*/0, 320 /*FlagsToExclude=*/OptFlag2)); 321 EXPECT_EQ(Nearest, "-doopf1"); 322 } 323 324 TEST(DISABLED_Option, FindNearestFIXME) { 325 TestOptTable T; 326 std::string Nearest; 327 328 // FIXME: Options with joined values should not have those values considered 329 // when calculating distance. The test below would fail if run, but it should 330 // succeed. 331 EXPECT_EQ(1U, T.findNearest("--erbghFoo", Nearest)); 332 EXPECT_EQ(Nearest, "--ermghFoo"); 333 334 } 335 336 TEST(Option, ParseGroupedShortOptions) { 337 TestOptTable T; 338 T.setGroupedShortOptions(true); 339 unsigned MAI, MAC; 340 341 // Grouped short options can be followed by a long Flag (-Joo), or a non-Flag 342 // option (-C=1). 343 const char *Args1[] = {"-AIJ", "-AIJoo", "-AC=1"}; 344 InputArgList AL = T.ParseArgs(Args1, MAI, MAC); 345 EXPECT_TRUE(AL.hasArg(OPT_A)); 346 EXPECT_TRUE(AL.hasArg(OPT_H)); 347 ASSERT_EQ((size_t)2, AL.getAllArgValues(OPT_B).size()); 348 EXPECT_EQ("foo", AL.getAllArgValues(OPT_B)[0]); 349 EXPECT_EQ("bar", AL.getAllArgValues(OPT_B)[1]); 350 ASSERT_TRUE(AL.hasArg(OPT_C)); 351 EXPECT_EQ("1", AL.getAllArgValues(OPT_C)[0]); 352 353 // Prefer a long option to a short option. 354 const char *Args2[] = {"-AB"}; 355 InputArgList AL2 = T.ParseArgs(Args2, MAI, MAC); 356 EXPECT_TRUE(!AL2.hasArg(OPT_A)); 357 EXPECT_TRUE(AL2.hasArg(OPT_AB)); 358 359 // Short options followed by a long option. We probably should disallow this. 360 const char *Args3[] = {"-AIblorp"}; 361 InputArgList AL3 = T.ParseArgs(Args3, MAI, MAC); 362 EXPECT_TRUE(AL3.hasArg(OPT_A)); 363 EXPECT_TRUE(AL3.hasArg(OPT_Blorp)); 364 } 365 366 TEST(Option, UnknownOptions) { 367 TestOptTable T; 368 unsigned MAI, MAC; 369 const char *Args[] = {"-u", "--long", "0"}; 370 for (int I = 0; I < 2; ++I) { 371 T.setGroupedShortOptions(I != 0); 372 InputArgList AL = T.ParseArgs(Args, MAI, MAC); 373 const std::vector<std::string> Unknown = AL.getAllArgValues(OPT_UNKNOWN); 374 ASSERT_EQ((size_t)2, Unknown.size()); 375 EXPECT_EQ("-u", Unknown[0]); 376 EXPECT_EQ("--long", Unknown[1]); 377 } 378 } 379