1 //===- unittests/Frontend/CompilerInvocationTest.cpp - CI 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 "clang/Frontend/CompilerInvocation.h" 10 #include "clang/Frontend/CompilerInstance.h" 11 #include "clang/Frontend/TextDiagnosticBuffer.h" 12 #include "llvm/Support/Host.h" 13 14 #include "gmock/gmock.h" 15 #include "gtest/gtest.h" 16 17 using namespace llvm; 18 using namespace clang; 19 20 using ::testing::Contains; 21 using ::testing::HasSubstr; 22 using ::testing::StrEq; 23 24 namespace { 25 class CommandLineTest : public ::testing::Test { 26 public: 27 IntrusiveRefCntPtr<DiagnosticsEngine> Diags; 28 SmallVector<const char *, 32> GeneratedArgs; 29 SmallVector<std::string, 32> GeneratedArgsStorage; 30 CompilerInvocation Invocation; 31 32 const char *operator()(const Twine &Arg) { 33 return GeneratedArgsStorage.emplace_back(Arg.str()).c_str(); 34 } 35 36 CommandLineTest() 37 : Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions(), 38 new TextDiagnosticBuffer())) { 39 } 40 }; 41 42 template <typename M> 43 std::string describeContainsN(M InnerMatcher, unsigned N, bool Negation) { 44 StringRef Contains = Negation ? "doesn't contain" : "contains"; 45 StringRef Instance = N == 1 ? " instance " : " instances "; 46 StringRef Element = "of element that "; 47 48 std::ostringstream Inner; 49 InnerMatcher.impl().DescribeTo(&Inner); 50 51 return (Contains + " exactly " + Twine(N) + Instance + Element + Inner.str()) 52 .str(); 53 } 54 55 MATCHER_P2(ContainsN, InnerMatcher, N, 56 describeContainsN(InnerMatcher, N, negation)) { 57 auto InnerMatches = [this](const auto &Element) { 58 ::testing::internal::DummyMatchResultListener InnerListener; 59 return InnerMatcher.impl().MatchAndExplain(Element, &InnerListener); 60 }; 61 62 return count_if(arg, InnerMatches) == N; 63 } 64 65 TEST(ContainsN, Empty) { 66 const char *Array[] = {""}; 67 68 ASSERT_THAT(Array, ContainsN(StrEq("x"), 0)); 69 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1))); 70 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2))); 71 } 72 73 TEST(ContainsN, Zero) { 74 const char *Array[] = {"y"}; 75 76 ASSERT_THAT(Array, ContainsN(StrEq("x"), 0)); 77 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1))); 78 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2))); 79 } 80 81 TEST(ContainsN, One) { 82 const char *Array[] = {"a", "b", "x", "z"}; 83 84 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 0))); 85 ASSERT_THAT(Array, ContainsN(StrEq("x"), 1)); 86 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2))); 87 } 88 89 TEST(ContainsN, Two) { 90 const char *Array[] = {"x", "a", "b", "x"}; 91 92 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 0))); 93 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1))); 94 ASSERT_THAT(Array, ContainsN(StrEq("x"), 2)); 95 } 96 97 // Boolean option with a keypath that defaults to true. 98 // The only flag with a negative spelling can set the keypath to false. 99 100 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagNotPresent) { 101 const char *Args[] = {""}; 102 103 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 104 105 ASSERT_FALSE(Diags->hasErrorOccurred()); 106 ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary); 107 108 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 109 110 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-temp-file")))); 111 } 112 113 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagPresent) { 114 const char *Args[] = {"-fno-temp-file"}; 115 116 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 117 118 ASSERT_FALSE(Diags->hasErrorOccurred()); 119 ASSERT_FALSE(Invocation.getFrontendOpts().UseTemporary); 120 121 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 122 123 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-temp-file"))); 124 } 125 126 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagUnknownPresent) { 127 const char *Args[] = {"-ftemp-file"}; 128 129 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 130 131 // Driver-only flag. 132 ASSERT_TRUE(Diags->hasErrorOccurred()); 133 ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary); 134 } 135 136 // Boolean option with a keypath that defaults to true. 137 // The flag with negative spelling can set the keypath to false. 138 // The flag with positive spelling can reset the keypath to true. 139 140 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNone) { 141 const char *Args[] = {""}; 142 143 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 144 ASSERT_FALSE(Diags->hasErrorOccurred()); 145 ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink); 146 147 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 148 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink")))); 149 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-autolink")))); 150 } 151 152 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNegChange) { 153 const char *Args[] = {"-fno-autolink"}; 154 155 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 156 ASSERT_FALSE(Diags->hasErrorOccurred()); 157 ASSERT_FALSE(Invocation.getCodeGenOpts().Autolink); 158 159 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 160 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-autolink"))); 161 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink")))); 162 } 163 164 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentPosReset) { 165 const char *Args[] = {"-fautolink"}; 166 167 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 168 ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag. 169 ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink); 170 } 171 172 // Boolean option with a keypath that defaults to false. 173 // The flag with negative spelling can set the keypath to true. 174 // The flag with positive spelling can reset the keypath to false. 175 176 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNone) { 177 const char *Args[] = {""}; 178 179 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 180 ASSERT_FALSE(Diags->hasErrorOccurred()); 181 ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables); 182 183 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 184 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables")))); 185 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-inline-line-tables")))); 186 } 187 188 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegChange) { 189 const char *Args[] = {"-gno-inline-line-tables"}; 190 191 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 192 ASSERT_FALSE(Diags->hasErrorOccurred()); 193 ASSERT_TRUE(Invocation.getCodeGenOpts().NoInlineLineTables); 194 195 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 196 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gno-inline-line-tables"))); 197 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables")))); 198 } 199 200 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosReset) { 201 const char *Args[] = {"-ginline-line-tables"}; 202 203 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 204 ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag. 205 ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables); 206 } 207 208 // Boolean option with a keypath that defaults to false. 209 // The flag with positive spelling can set the keypath to true. 210 // The flag with negative spelling can reset the keypath to false. 211 212 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNoneX) { 213 const char *Args[] = {""}; 214 215 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 216 ASSERT_FALSE(Diags->hasErrorOccurred()); 217 ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash); 218 219 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 220 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gcodeview-ghash")))); 221 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash")))); 222 } 223 224 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosChange) { 225 const char *Args[] = {"-gcodeview-ghash"}; 226 227 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 228 ASSERT_FALSE(Diags->hasErrorOccurred()); 229 ASSERT_TRUE(Invocation.getCodeGenOpts().CodeViewGHash); 230 231 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 232 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gcodeview-ghash"))); 233 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash")))); 234 } 235 236 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegReset) { 237 const char *Args[] = {"-gno-codeview-ghash"}; 238 239 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 240 ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag. 241 ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash); 242 } 243 244 // Boolean option with a keypath that defaults to an arbitrary expression. 245 // The flag with positive spelling can set the keypath to true. 246 // The flag with negative spelling can set the keypath to false. 247 248 static constexpr unsigned PassManagerDefault = 249 !static_cast<unsigned>(LLVM_ENABLE_NEW_PASS_MANAGER); 250 251 static constexpr const char *PassManagerResetByFlag = 252 LLVM_ENABLE_NEW_PASS_MANAGER ? "-fno-legacy-pass-manager" 253 : "-flegacy-pass-manager"; 254 255 static constexpr const char *PassManagerChangedByFlag = 256 LLVM_ENABLE_NEW_PASS_MANAGER ? "-flegacy-pass-manager" 257 : "-fno-legacy-pass-manager"; 258 259 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentNone) { 260 const char *Args = {""}; 261 262 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 263 264 ASSERT_FALSE(Diags->hasErrorOccurred()); 265 ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault); 266 267 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 268 269 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag)))); 270 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag)))); 271 } 272 273 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentChange) { 274 const char *Args[] = {PassManagerChangedByFlag}; 275 276 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 277 ASSERT_FALSE(Diags->hasErrorOccurred()); 278 ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, !PassManagerDefault); 279 280 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 281 ASSERT_THAT(GeneratedArgs, Contains(StrEq(PassManagerChangedByFlag))); 282 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag)))); 283 } 284 285 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentReset) { 286 const char *Args[] = {PassManagerResetByFlag}; 287 288 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 289 ASSERT_FALSE(Diags->hasErrorOccurred()); 290 ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault); 291 292 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 293 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag)))); 294 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag)))); 295 } 296 297 // Boolean option that gets the CC1Option flag from a let statement (which 298 // is applied **after** the record is defined): 299 // 300 // let Flags = [CC1Option] in { 301 // defm option : BoolOption<...>; 302 // } 303 304 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentNone) { 305 const char *Args[] = {""}; 306 307 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 308 309 ASSERT_FALSE(Diags->hasErrorOccurred()); 310 ASSERT_FALSE(Invocation.getCodeGenOpts().DebugPassManager); 311 312 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 313 314 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdebug-pass-manager")))); 315 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager")))); 316 } 317 318 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentPos) { 319 const char *Args[] = {"-fdebug-pass-manager"}; 320 321 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 322 323 ASSERT_FALSE(Diags->hasErrorOccurred()); 324 ASSERT_TRUE(Invocation.getCodeGenOpts().DebugPassManager); 325 326 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 327 328 ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fdebug-pass-manager"), 1)); 329 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager")))); 330 } 331 332 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentNeg) { 333 const char *Args[] = {"-fno-debug-pass-manager"}; 334 335 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 336 337 ASSERT_FALSE(Diags->hasErrorOccurred()); 338 ASSERT_FALSE(Invocation.getCodeGenOpts().DebugPassManager); 339 340 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 341 342 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager")))); 343 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdebug-pass-manager")))); 344 } 345 346 TEST_F(CommandLineTest, CanGenerateCC1CommandLineFlag) { 347 const char *Args[] = {"-fmodules-strict-context-hash"}; 348 349 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 350 351 ASSERT_FALSE(Diags->hasErrorOccurred()); 352 353 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 354 355 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash"))); 356 } 357 358 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparate) { 359 const char *TripleCStr = "i686-apple-darwin9"; 360 const char *Args[] = {"-triple", TripleCStr}; 361 362 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 363 364 ASSERT_FALSE(Diags->hasErrorOccurred()); 365 366 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 367 368 ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr))); 369 } 370 371 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredPresent) { 372 const std::string DefaultTriple = 373 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); 374 const char *Args[] = {"-triple", DefaultTriple.c_str()}; 375 376 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 377 378 ASSERT_FALSE(Diags->hasErrorOccurred()); 379 380 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 381 382 // Triple should always be emitted even if it is the default 383 ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str()))); 384 } 385 386 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredAbsent) { 387 const std::string DefaultTriple = 388 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); 389 const char *Args[] = {""}; 390 391 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 392 393 ASSERT_FALSE(Diags->hasErrorOccurred()); 394 395 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 396 397 // Triple should always be emitted even if it is the default 398 ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str()))); 399 } 400 401 TEST_F(CommandLineTest, SeparateEnumNonDefault) { 402 const char *Args[] = {"-mrelocation-model", "static"}; 403 404 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 405 406 ASSERT_FALSE(Diags->hasErrorOccurred()); 407 ASSERT_EQ(Invocation.getCodeGenOpts().RelocationModel, Reloc::Model::Static); 408 409 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 410 411 // Non default relocation model. 412 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-mrelocation-model"))); 413 ASSERT_THAT(GeneratedArgs, Contains(StrEq("static"))); 414 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model=static")))); 415 } 416 417 TEST_F(CommandLineTest, SeparateEnumDefault) { 418 const char *Args[] = {"-mrelocation-model", "pic"}; 419 420 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 421 422 ASSERT_FALSE(Diags->hasErrorOccurred()); 423 ASSERT_EQ(Invocation.getCodeGenOpts().RelocationModel, Reloc::Model::PIC_); 424 425 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 426 427 // Default relocation model. 428 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model")))); 429 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("pic")))); 430 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model=pic")))); 431 } 432 433 TEST_F(CommandLineTest, JoinedEnumNonDefault) { 434 const char *Args[] = {"-fobjc-dispatch-method=non-legacy"}; 435 436 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 437 438 ASSERT_FALSE(Diags->hasErrorOccurred()); 439 ASSERT_EQ(Invocation.getCodeGenOpts().getObjCDispatchMethod(), 440 CodeGenOptions::NonLegacy); 441 442 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 443 444 ASSERT_THAT(GeneratedArgs, 445 Contains(StrEq("-fobjc-dispatch-method=non-legacy"))); 446 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fobjc-dispatch-method=")))); 447 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("non-legacy")))); 448 } 449 450 TEST_F(CommandLineTest, JoinedEnumDefault) { 451 const char *Args[] = {"-fobjc-dispatch-method=legacy"}; 452 453 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 454 455 ASSERT_FALSE(Diags->hasErrorOccurred()); 456 ASSERT_EQ(Invocation.getCodeGenOpts().getObjCDispatchMethod(), 457 CodeGenOptions::Legacy); 458 459 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 460 461 ASSERT_THAT(GeneratedArgs, 462 Not(Contains(StrEq("-fobjc-dispatch-method=legacy")))); 463 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fobjc-dispatch-method=")))); 464 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("legacy")))); 465 } 466 467 TEST_F(CommandLineTest, StringVectorEmpty) { 468 const char *Args[] = {""}; 469 470 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 471 472 ASSERT_FALSE(Diags->hasErrorOccurred()); 473 ASSERT_TRUE(Invocation.getFrontendOpts().ModuleMapFiles.empty()); 474 475 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 476 477 ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-fmodule-map-file")))); 478 } 479 480 TEST_F(CommandLineTest, StringVectorSingle) { 481 const char *Args[] = {"-fmodule-map-file=a"}; 482 483 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 484 485 ASSERT_FALSE(Diags->hasErrorOccurred()); 486 ASSERT_EQ(Invocation.getFrontendOpts().ModuleMapFiles, 487 std::vector<std::string>({"a"})); 488 489 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 490 491 ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=a"), 1)); 492 ASSERT_THAT(GeneratedArgs, ContainsN(HasSubstr("-fmodule-map-file"), 1)); 493 } 494 495 TEST_F(CommandLineTest, StringVectorMultiple) { 496 const char *Args[] = {"-fmodule-map-file=a", "-fmodule-map-file=b"}; 497 498 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 499 500 ASSERT_FALSE(Diags->hasErrorOccurred()); 501 ASSERT_TRUE(Invocation.getFrontendOpts().ModuleMapFiles == 502 std::vector<std::string>({"a", "b"})); 503 504 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 505 506 ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=a"), 1)); 507 ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=b"), 1)); 508 ASSERT_THAT(GeneratedArgs, ContainsN(HasSubstr("-fmodule-map-file"), 2)); 509 } 510 511 // CommaJoined option with MarshallingInfoStringVector. 512 513 TEST_F(CommandLineTest, StringVectorCommaJoinedNone) { 514 const char *Args[] = {""}; 515 516 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 517 518 ASSERT_FALSE(Diags->hasErrorOccurred()); 519 ASSERT_TRUE(Invocation.getLangOpts()->CommentOpts.BlockCommandNames.empty()); 520 521 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 522 523 ASSERT_THAT(GeneratedArgs, 524 Not(Contains(HasSubstr("-fcomment-block-commands")))); 525 } 526 527 TEST_F(CommandLineTest, StringVectorCommaJoinedSingle) { 528 const char *Args[] = {"-fcomment-block-commands=x,y"}; 529 530 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 531 532 ASSERT_FALSE(Diags->hasErrorOccurred()); 533 ASSERT_EQ(Invocation.getLangOpts()->CommentOpts.BlockCommandNames, 534 std::vector<std::string>({"x", "y"})); 535 536 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 537 538 ASSERT_THAT(GeneratedArgs, 539 ContainsN(StrEq("-fcomment-block-commands=x,y"), 1)); 540 } 541 542 TEST_F(CommandLineTest, StringVectorCommaJoinedMultiple) { 543 const char *Args[] = {"-fcomment-block-commands=x,y", 544 "-fcomment-block-commands=a,b"}; 545 546 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 547 548 ASSERT_FALSE(Diags->hasErrorOccurred()); 549 ASSERT_EQ(Invocation.getLangOpts()->CommentOpts.BlockCommandNames, 550 std::vector<std::string>({"x", "y", "a", "b"})); 551 552 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 553 554 ASSERT_THAT(GeneratedArgs, 555 ContainsN(StrEq("-fcomment-block-commands=x,y,a,b"), 1)); 556 } 557 558 // A flag that should be parsed only if a condition is met. 559 560 TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagNotPresent) { 561 const char *Args[] = {""}; 562 563 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 564 565 ASSERT_FALSE(Diags->hasErrorOccurred()); 566 ASSERT_FALSE(Invocation.getLangOpts()->SYCL); 567 ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None); 568 569 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 570 571 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl")))); 572 ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); 573 } 574 575 TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagPresent) { 576 const char *Args[] = {"-sycl-std=2017"}; 577 578 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 579 580 ASSERT_FALSE(Diags->hasErrorOccurred()); 581 ASSERT_FALSE(Invocation.getLangOpts()->SYCL); 582 ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None); 583 584 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 585 586 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl")))); 587 ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); 588 } 589 590 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagNotPresent) { 591 const char *Args[] = {"-fsycl"}; 592 593 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 594 595 ASSERT_FALSE(Diags->hasErrorOccurred()); 596 ASSERT_TRUE(Invocation.getLangOpts()->SYCL); 597 ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None); 598 599 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 600 601 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl"))); 602 ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); 603 } 604 605 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagPresent) { 606 const char *Args[] = {"-fsycl", "-sycl-std=2017"}; 607 608 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 609 610 ASSERT_FALSE(Diags->hasErrorOccurred()); 611 ASSERT_TRUE(Invocation.getLangOpts()->SYCL); 612 ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_2017); 613 614 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 615 616 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl"))); 617 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-sycl-std=2017"))); 618 } 619 620 // Wide integer option. 621 622 TEST_F(CommandLineTest, WideIntegerHighValue) { 623 const char *Args[] = {"-fbuild-session-timestamp=1609827494445723662"}; 624 625 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 626 627 ASSERT_FALSE(Diags->hasErrorOccurred()); 628 ASSERT_EQ(Invocation.getHeaderSearchOpts().BuildSessionTimestamp, 629 1609827494445723662ull); 630 } 631 632 // Tree of boolean options that can be (directly or transitively) implied by 633 // their parent: 634 // 635 // * -cl-unsafe-math-optimizations 636 // * -cl-mad-enable 637 // * -menable-unsafe-fp-math 638 // * -freciprocal-math 639 640 TEST_F(CommandLineTest, ImpliedBoolOptionsNoFlagPresent) { 641 const char *Args[] = {""}; 642 643 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 644 645 ASSERT_FALSE(Diags->hasErrorOccurred()); 646 ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath); 647 ASSERT_FALSE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 648 ASSERT_FALSE(Invocation.getLangOpts()->UnsafeFPMath); 649 ASSERT_FALSE(Invocation.getLangOpts()->AllowRecip); 650 651 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 652 653 // Not generated - missing. 654 ASSERT_THAT(GeneratedArgs, 655 Not(Contains(StrEq("-cl-unsafe-math-optimizations")))); 656 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 657 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 658 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 659 } 660 661 TEST_F(CommandLineTest, ImpliedBoolOptionsRootFlagPresent) { 662 const char *Args[] = {"-cl-unsafe-math-optimizations"}; 663 664 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 665 666 ASSERT_FALSE(Diags->hasErrorOccurred()); 667 // Explicitly provided root flag. 668 ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath); 669 // Directly implied by explicitly provided root flag. 670 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 671 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 672 // Transitively implied by explicitly provided root flag. 673 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 674 675 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 676 677 // Generated - explicitly provided. 678 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations"))); 679 // Not generated - implied by the generated root flag. 680 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 681 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 682 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 683 } 684 685 TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) { 686 const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable", 687 "-menable-unsafe-fp-math", "-freciprocal-math"}; 688 689 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 690 691 ASSERT_FALSE(Diags->hasErrorOccurred()); 692 ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath); 693 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 694 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 695 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 696 697 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 698 699 // Generated - explicitly provided. 700 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations"))); 701 // Not generated - implied by their generated parent. 702 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 703 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 704 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 705 } 706 707 TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) { 708 const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math", 709 "-freciprocal-math"}; 710 711 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 712 ASSERT_FALSE(Diags->hasErrorOccurred()); 713 ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath); 714 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 715 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 716 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 717 718 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 719 // Not generated - missing. 720 ASSERT_THAT(GeneratedArgs, 721 Not(Contains(StrEq("-cl-unsafe-math-optimizations")))); 722 // Generated - explicitly provided. 723 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable"))); 724 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math"))); 725 // Not generated - implied by its generated parent. 726 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 727 } 728 729 TEST_F(CommandLineTest, PresentAndNotImpliedGenerated) { 730 const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math"}; 731 732 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 733 734 ASSERT_FALSE(Diags->hasErrorOccurred()); 735 736 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 737 738 // Present options that were not implied are generated. 739 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable"))); 740 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math"))); 741 } 742 } // anonymous namespace 743