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/Basic/TargetOptions.h" 11 #include "clang/Frontend/CompilerInstance.h" 12 #include "clang/Frontend/TextDiagnosticBuffer.h" 13 #include "clang/Lex/PreprocessorOptions.h" 14 #include "clang/Serialization/ModuleFileExtension.h" 15 #include "llvm/Support/Host.h" 16 17 #include "gmock/gmock.h" 18 #include "gtest/gtest.h" 19 20 using namespace llvm; 21 using namespace clang; 22 23 using ::testing::Contains; 24 using ::testing::HasSubstr; 25 using ::testing::StrEq; 26 27 namespace { 28 class CommandLineTest : public ::testing::Test { 29 public: 30 IntrusiveRefCntPtr<DiagnosticsEngine> Diags; 31 SmallVector<const char *, 32> GeneratedArgs; 32 SmallVector<std::string, 32> GeneratedArgsStorage; 33 CompilerInvocation Invocation; 34 35 const char *operator()(const Twine &Arg) { 36 return GeneratedArgsStorage.emplace_back(Arg.str()).c_str(); 37 } 38 39 CommandLineTest() 40 : Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions(), 41 new TextDiagnosticBuffer())) { 42 } 43 }; 44 45 template <typename M> 46 std::string describeContainsN(M InnerMatcher, unsigned N, bool Negation) { 47 StringRef Contains = Negation ? "doesn't contain" : "contains"; 48 StringRef Instance = N == 1 ? " instance " : " instances "; 49 StringRef Element = "of element that "; 50 51 std::ostringstream Inner; 52 InnerMatcher.impl().DescribeTo(&Inner); 53 54 return (Contains + " exactly " + Twine(N) + Instance + Element + Inner.str()) 55 .str(); 56 } 57 58 MATCHER_P2(ContainsN, InnerMatcher, N, 59 describeContainsN(InnerMatcher, N, negation)) { 60 auto InnerMatches = [this](const auto &Element) { 61 ::testing::internal::DummyMatchResultListener InnerListener; 62 return InnerMatcher.impl().MatchAndExplain(Element, &InnerListener); 63 }; 64 65 return count_if(arg, InnerMatches) == N; 66 } 67 68 TEST(ContainsN, Empty) { 69 const char *Array[] = {""}; 70 71 ASSERT_THAT(Array, ContainsN(StrEq("x"), 0)); 72 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1))); 73 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2))); 74 } 75 76 TEST(ContainsN, Zero) { 77 const char *Array[] = {"y"}; 78 79 ASSERT_THAT(Array, ContainsN(StrEq("x"), 0)); 80 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1))); 81 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2))); 82 } 83 84 TEST(ContainsN, One) { 85 const char *Array[] = {"a", "b", "x", "z"}; 86 87 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 0))); 88 ASSERT_THAT(Array, ContainsN(StrEq("x"), 1)); 89 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2))); 90 } 91 92 TEST(ContainsN, Two) { 93 const char *Array[] = {"x", "a", "b", "x"}; 94 95 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 0))); 96 ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1))); 97 ASSERT_THAT(Array, ContainsN(StrEq("x"), 2)); 98 } 99 100 // Boolean option with a keypath that defaults to true. 101 // The only flag with a negative spelling can set the keypath to false. 102 103 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagNotPresent) { 104 const char *Args[] = {""}; 105 106 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 107 ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary); 108 109 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 110 111 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-temp-file")))); 112 } 113 114 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagPresent) { 115 const char *Args[] = {"-fno-temp-file"}; 116 117 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 118 ASSERT_FALSE(Invocation.getFrontendOpts().UseTemporary); 119 120 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 121 122 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-temp-file"))); 123 } 124 125 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagUnknownPresent) { 126 const char *Args[] = {"-ftemp-file"}; 127 128 // Driver-only flag. 129 ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 130 ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary); 131 } 132 133 // Boolean option with a keypath that defaults to true. 134 // The flag with negative spelling can set the keypath to false. 135 // The flag with positive spelling can reset the keypath to true. 136 137 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNone) { 138 const char *Args[] = {""}; 139 140 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 141 ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink); 142 143 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 144 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink")))); 145 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-autolink")))); 146 } 147 148 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNegChange) { 149 const char *Args[] = {"-fno-autolink"}; 150 151 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 152 ASSERT_FALSE(Invocation.getCodeGenOpts().Autolink); 153 154 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 155 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-autolink"))); 156 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink")))); 157 } 158 159 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentPosReset) { 160 const char *Args[] = {"-fautolink"}; 161 162 // Driver-only flag. 163 ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 164 ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink); 165 } 166 167 // Boolean option with a keypath that defaults to false. 168 // The flag with negative spelling can set the keypath to true. 169 // The flag with positive spelling can reset the keypath to false. 170 171 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNone) { 172 const char *Args[] = {""}; 173 174 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 175 ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables); 176 177 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 178 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables")))); 179 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-inline-line-tables")))); 180 } 181 182 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegChange) { 183 const char *Args[] = {"-gno-inline-line-tables"}; 184 185 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 186 ASSERT_TRUE(Invocation.getCodeGenOpts().NoInlineLineTables); 187 188 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 189 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gno-inline-line-tables"))); 190 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables")))); 191 } 192 193 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosReset) { 194 const char *Args[] = {"-ginline-line-tables"}; 195 196 // Driver-only flag. 197 ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 198 ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables); 199 } 200 201 // Boolean option with a keypath that defaults to false. 202 // The flag with positive spelling can set the keypath to true. 203 // The flag with negative spelling can reset the keypath to false. 204 205 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNoneX) { 206 const char *Args[] = {""}; 207 208 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 209 ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash); 210 211 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 212 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gcodeview-ghash")))); 213 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash")))); 214 } 215 216 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosChange) { 217 const char *Args[] = {"-gcodeview-ghash"}; 218 219 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 220 ASSERT_TRUE(Invocation.getCodeGenOpts().CodeViewGHash); 221 222 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 223 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gcodeview-ghash"))); 224 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash")))); 225 } 226 227 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegReset) { 228 const char *Args[] = {"-gno-codeview-ghash"}; 229 230 // Driver-only flag. 231 ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 232 ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash); 233 } 234 235 // Boolean option with a keypath that defaults to an arbitrary expression. 236 // The flag with positive spelling can set the keypath to true. 237 // The flag with negative spelling can set the keypath to false. 238 239 static constexpr unsigned PassManagerDefault = 240 !static_cast<unsigned>(LLVM_ENABLE_NEW_PASS_MANAGER); 241 242 static constexpr const char *PassManagerResetByFlag = 243 LLVM_ENABLE_NEW_PASS_MANAGER ? "-fno-legacy-pass-manager" 244 : "-flegacy-pass-manager"; 245 246 static constexpr const char *PassManagerChangedByFlag = 247 LLVM_ENABLE_NEW_PASS_MANAGER ? "-flegacy-pass-manager" 248 : "-fno-legacy-pass-manager"; 249 250 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentNone) { 251 const char *Args = {""}; 252 253 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 254 ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault); 255 256 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 257 258 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag)))); 259 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag)))); 260 } 261 262 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentChange) { 263 const char *Args[] = {PassManagerChangedByFlag}; 264 265 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 266 ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, !PassManagerDefault); 267 268 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 269 ASSERT_THAT(GeneratedArgs, Contains(StrEq(PassManagerChangedByFlag))); 270 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag)))); 271 } 272 273 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentReset) { 274 const char *Args[] = {PassManagerResetByFlag}; 275 276 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 277 ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault); 278 279 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 280 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag)))); 281 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag)))); 282 } 283 284 // Boolean option that gets the CC1Option flag from a let statement (which 285 // is applied **after** the record is defined): 286 // 287 // let Flags = [CC1Option] in { 288 // defm option : BoolOption<...>; 289 // } 290 291 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentNone) { 292 const char *Args[] = {""}; 293 294 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 295 ASSERT_FALSE(Invocation.getCodeGenOpts().DebugPassManager); 296 297 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 298 299 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdebug-pass-manager")))); 300 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager")))); 301 } 302 303 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentPos) { 304 const char *Args[] = {"-fdebug-pass-manager"}; 305 306 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 307 ASSERT_TRUE(Invocation.getCodeGenOpts().DebugPassManager); 308 309 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 310 311 ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fdebug-pass-manager"), 1)); 312 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager")))); 313 } 314 315 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentNeg) { 316 const char *Args[] = {"-fno-debug-pass-manager"}; 317 318 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 319 ASSERT_FALSE(Invocation.getCodeGenOpts().DebugPassManager); 320 321 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 322 323 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager")))); 324 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdebug-pass-manager")))); 325 } 326 327 TEST_F(CommandLineTest, CanGenerateCC1CommandLineFlag) { 328 const char *Args[] = {"-fmodules-strict-context-hash"}; 329 330 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 331 332 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 333 334 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash"))); 335 } 336 337 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparate) { 338 const char *TripleCStr = "i686-apple-darwin9"; 339 const char *Args[] = {"-triple", TripleCStr}; 340 341 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 342 343 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 344 345 ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr))); 346 } 347 348 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredPresent) { 349 const std::string DefaultTriple = 350 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); 351 const char *Args[] = {"-triple", DefaultTriple.c_str()}; 352 353 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 354 355 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 356 357 // Triple should always be emitted even if it is the default 358 ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str()))); 359 } 360 361 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredAbsent) { 362 const std::string DefaultTriple = 363 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); 364 const char *Args[] = {""}; 365 366 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 367 368 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 369 370 // Triple should always be emitted even if it is the default 371 ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str()))); 372 } 373 374 TEST_F(CommandLineTest, SeparateEnumNonDefault) { 375 const char *Args[] = {"-mrelocation-model", "static"}; 376 377 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 378 ASSERT_EQ(Invocation.getCodeGenOpts().RelocationModel, Reloc::Model::Static); 379 380 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 381 382 // Non default relocation model. 383 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-mrelocation-model"))); 384 ASSERT_THAT(GeneratedArgs, Contains(StrEq("static"))); 385 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model=static")))); 386 } 387 388 TEST_F(CommandLineTest, SeparateEnumDefault) { 389 const char *Args[] = {"-mrelocation-model", "pic"}; 390 391 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 392 ASSERT_EQ(Invocation.getCodeGenOpts().RelocationModel, Reloc::Model::PIC_); 393 394 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 395 396 // Default relocation model. 397 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model")))); 398 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("pic")))); 399 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model=pic")))); 400 } 401 402 TEST_F(CommandLineTest, JoinedEnumNonDefault) { 403 const char *Args[] = {"-fobjc-dispatch-method=non-legacy"}; 404 405 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 406 ASSERT_EQ(Invocation.getCodeGenOpts().getObjCDispatchMethod(), 407 CodeGenOptions::NonLegacy); 408 409 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 410 411 ASSERT_THAT(GeneratedArgs, 412 Contains(StrEq("-fobjc-dispatch-method=non-legacy"))); 413 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fobjc-dispatch-method=")))); 414 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("non-legacy")))); 415 } 416 417 TEST_F(CommandLineTest, JoinedEnumDefault) { 418 const char *Args[] = {"-fobjc-dispatch-method=legacy"}; 419 420 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 421 ASSERT_EQ(Invocation.getCodeGenOpts().getObjCDispatchMethod(), 422 CodeGenOptions::Legacy); 423 424 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 425 426 ASSERT_THAT(GeneratedArgs, 427 Not(Contains(StrEq("-fobjc-dispatch-method=legacy")))); 428 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fobjc-dispatch-method=")))); 429 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("legacy")))); 430 } 431 432 TEST_F(CommandLineTest, StringVectorEmpty) { 433 const char *Args[] = {""}; 434 435 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 436 ASSERT_TRUE(Invocation.getFrontendOpts().ModuleMapFiles.empty()); 437 438 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 439 440 ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-fmodule-map-file")))); 441 } 442 443 TEST_F(CommandLineTest, StringVectorSingle) { 444 const char *Args[] = {"-fmodule-map-file=a"}; 445 446 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 447 ASSERT_EQ(Invocation.getFrontendOpts().ModuleMapFiles, 448 std::vector<std::string>({"a"})); 449 450 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 451 452 ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=a"), 1)); 453 ASSERT_THAT(GeneratedArgs, ContainsN(HasSubstr("-fmodule-map-file"), 1)); 454 } 455 456 TEST_F(CommandLineTest, StringVectorMultiple) { 457 const char *Args[] = {"-fmodule-map-file=a", "-fmodule-map-file=b"}; 458 459 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 460 ASSERT_TRUE(Invocation.getFrontendOpts().ModuleMapFiles == 461 std::vector<std::string>({"a", "b"})); 462 463 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 464 465 ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=a"), 1)); 466 ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=b"), 1)); 467 ASSERT_THAT(GeneratedArgs, ContainsN(HasSubstr("-fmodule-map-file"), 2)); 468 } 469 470 // CommaJoined option with MarshallingInfoStringVector. 471 472 TEST_F(CommandLineTest, StringVectorCommaJoinedNone) { 473 const char *Args[] = {""}; 474 475 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 476 ASSERT_TRUE(Invocation.getLangOpts()->CommentOpts.BlockCommandNames.empty()); 477 478 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 479 480 ASSERT_THAT(GeneratedArgs, 481 Not(Contains(HasSubstr("-fcomment-block-commands")))); 482 } 483 484 TEST_F(CommandLineTest, StringVectorCommaJoinedSingle) { 485 const char *Args[] = {"-fcomment-block-commands=x,y"}; 486 487 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 488 ASSERT_EQ(Invocation.getLangOpts()->CommentOpts.BlockCommandNames, 489 std::vector<std::string>({"x", "y"})); 490 491 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 492 493 ASSERT_THAT(GeneratedArgs, 494 ContainsN(StrEq("-fcomment-block-commands=x,y"), 1)); 495 } 496 497 TEST_F(CommandLineTest, StringVectorCommaJoinedMultiple) { 498 const char *Args[] = {"-fcomment-block-commands=x,y", 499 "-fcomment-block-commands=a,b"}; 500 501 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 502 ASSERT_EQ(Invocation.getLangOpts()->CommentOpts.BlockCommandNames, 503 std::vector<std::string>({"x", "y", "a", "b"})); 504 505 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 506 507 ASSERT_THAT(GeneratedArgs, 508 ContainsN(StrEq("-fcomment-block-commands=x,y,a,b"), 1)); 509 } 510 511 // A flag that should be parsed only if a condition is met. 512 513 TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagNotPresent) { 514 const char *Args[] = {""}; 515 516 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 517 518 ASSERT_FALSE(Diags->hasErrorOccurred()); 519 ASSERT_FALSE(Invocation.getLangOpts()->SYCL); 520 ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None); 521 522 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 523 524 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl")))); 525 ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); 526 } 527 528 TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagPresent) { 529 const char *Args[] = {"-sycl-std=2017"}; 530 531 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 532 533 ASSERT_FALSE(Diags->hasErrorOccurred()); 534 ASSERT_FALSE(Invocation.getLangOpts()->SYCL); 535 ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None); 536 537 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 538 539 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl")))); 540 ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); 541 } 542 543 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagNotPresent) { 544 const char *Args[] = {"-fsycl"}; 545 546 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 547 548 ASSERT_FALSE(Diags->hasErrorOccurred()); 549 ASSERT_TRUE(Invocation.getLangOpts()->SYCL); 550 ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None); 551 552 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 553 554 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl"))); 555 ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); 556 } 557 558 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagPresent) { 559 const char *Args[] = {"-fsycl", "-sycl-std=2017"}; 560 561 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 562 563 ASSERT_FALSE(Diags->hasErrorOccurred()); 564 ASSERT_TRUE(Invocation.getLangOpts()->SYCL); 565 ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_2017); 566 567 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 568 569 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl"))); 570 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-sycl-std=2017"))); 571 } 572 573 // Wide integer option. 574 575 TEST_F(CommandLineTest, WideIntegerHighValue) { 576 const char *Args[] = {"-fbuild-session-timestamp=1609827494445723662"}; 577 578 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 579 580 ASSERT_FALSE(Diags->hasErrorOccurred()); 581 ASSERT_EQ(Invocation.getHeaderSearchOpts().BuildSessionTimestamp, 582 1609827494445723662ull); 583 } 584 585 // Tree of boolean options that can be (directly or transitively) implied by 586 // their parent: 587 // 588 // * -cl-unsafe-math-optimizations 589 // * -cl-mad-enable 590 // * -menable-unsafe-fp-math 591 // * -freciprocal-math 592 593 TEST_F(CommandLineTest, ImpliedBoolOptionsNoFlagPresent) { 594 const char *Args[] = {""}; 595 596 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 597 ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath); 598 ASSERT_FALSE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 599 ASSERT_FALSE(Invocation.getLangOpts()->UnsafeFPMath); 600 ASSERT_FALSE(Invocation.getLangOpts()->AllowRecip); 601 602 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 603 604 // Not generated - missing. 605 ASSERT_THAT(GeneratedArgs, 606 Not(Contains(StrEq("-cl-unsafe-math-optimizations")))); 607 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 608 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 609 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 610 } 611 612 TEST_F(CommandLineTest, ImpliedBoolOptionsRootFlagPresent) { 613 const char *Args[] = {"-cl-unsafe-math-optimizations"}; 614 615 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 616 // Explicitly provided root flag. 617 ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath); 618 // Directly implied by explicitly provided root flag. 619 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 620 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 621 // Transitively implied by explicitly provided root flag. 622 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 623 624 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 625 626 // Generated - explicitly provided. 627 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations"))); 628 // Not generated - implied by the generated root flag. 629 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 630 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 631 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 632 } 633 634 TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) { 635 const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable", 636 "-menable-unsafe-fp-math", "-freciprocal-math"}; 637 638 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 639 ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath); 640 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 641 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 642 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 643 644 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 645 646 // Generated - explicitly provided. 647 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations"))); 648 // Not generated - implied by their generated parent. 649 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 650 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 651 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 652 } 653 654 TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) { 655 const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math", 656 "-freciprocal-math"}; 657 658 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 659 ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath); 660 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 661 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 662 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 663 664 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 665 // Not generated - missing. 666 ASSERT_THAT(GeneratedArgs, 667 Not(Contains(StrEq("-cl-unsafe-math-optimizations")))); 668 // Generated - explicitly provided. 669 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable"))); 670 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math"))); 671 // Not generated - implied by its generated parent. 672 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 673 } 674 675 TEST_F(CommandLineTest, PresentAndNotImpliedGenerated) { 676 const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math"}; 677 678 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 679 680 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 681 682 // Present options that were not implied are generated. 683 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable"))); 684 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math"))); 685 } 686 687 // Diagnostic option. 688 689 TEST_F(CommandLineTest, DiagnosticOptionPresent) { 690 const char *Args[] = {"-verify=xyz"}; 691 692 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 693 694 ASSERT_EQ(Invocation.getDiagnosticOpts().VerifyPrefixes, 695 std::vector<std::string>({"xyz"})); 696 697 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 698 699 ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-verify=xyz"), 1)); 700 } 701 702 // Option default depends on language standard. 703 704 TEST_F(CommandLineTest, DigraphsImplied) { 705 const char *Args[] = {""}; 706 707 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 708 ASSERT_TRUE(Invocation.getLangOpts()->Digraphs); 709 710 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 711 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-digraphs")))); 712 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs")))); 713 } 714 715 TEST_F(CommandLineTest, DigraphsDisabled) { 716 const char *Args[] = {"-fno-digraphs"}; 717 718 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 719 ASSERT_FALSE(Invocation.getLangOpts()->Digraphs); 720 721 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 722 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-digraphs"))); 723 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs")))); 724 } 725 726 TEST_F(CommandLineTest, DigraphsNotImplied) { 727 const char *Args[] = {"-std=c89"}; 728 729 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 730 ASSERT_FALSE(Invocation.getLangOpts()->Digraphs); 731 732 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 733 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-digraphs")))); 734 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs")))); 735 } 736 737 TEST_F(CommandLineTest, DigraphsEnabled) { 738 const char *Args[] = {"-std=c89", "-fdigraphs"}; 739 740 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 741 ASSERT_TRUE(Invocation.getLangOpts()->Digraphs); 742 743 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 744 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fdigraphs"))); 745 } 746 747 struct DummyModuleFileExtension 748 : public llvm::RTTIExtends<DummyModuleFileExtension, ModuleFileExtension> { 749 static char ID; 750 751 ModuleFileExtensionMetadata getExtensionMetadata() const override { 752 return {}; 753 }; 754 755 llvm::hash_code hashExtension(llvm::hash_code Code) const override { 756 return {}; 757 } 758 759 std::unique_ptr<ModuleFileExtensionWriter> 760 createExtensionWriter(ASTWriter &Writer) override { 761 return {}; 762 } 763 764 std::unique_ptr<ModuleFileExtensionReader> 765 createExtensionReader(const ModuleFileExtensionMetadata &Metadata, 766 ASTReader &Reader, serialization::ModuleFile &Mod, 767 const llvm::BitstreamCursor &Stream) override { 768 return {}; 769 } 770 }; 771 772 char DummyModuleFileExtension::ID = 0; 773 774 TEST_F(CommandLineTest, TestModuleFileExtension) { 775 const char *Args[] = {"-ftest-module-file-extension=first:2:1:0:first", 776 "-ftest-module-file-extension=second:3:2:1:second"}; 777 778 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 779 ASSERT_THAT(Invocation.getFrontendOpts().ModuleFileExtensions.size(), 2); 780 781 // Exercise the check that only serializes instances of 782 // TestModuleFileExtension by providing an instance of another 783 // ModuleFileExtension subclass. 784 Invocation.getFrontendOpts().ModuleFileExtensions.push_back( 785 std::make_shared<DummyModuleFileExtension>()); 786 787 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 788 789 ASSERT_THAT(GeneratedArgs, 790 ContainsN(HasSubstr("-ftest-module-file-extension="), 2)); 791 ASSERT_THAT( 792 GeneratedArgs, 793 Contains(StrEq("-ftest-module-file-extension=first:2:1:0:first"))); 794 ASSERT_THAT( 795 GeneratedArgs, 796 Contains(StrEq("-ftest-module-file-extension=second:3:2:1:second"))); 797 } 798 799 TEST_F(CommandLineTest, RoundTrip) { 800 // Testing one marshalled and one manually generated option from each 801 // CompilerInvocation member. 802 const char *Args[] = { 803 "-round-trip-args", 804 // LanguageOptions 805 "-std=c17", 806 "-fmax-tokens=10", 807 // TargetOptions 808 "-target-sdk-version=1.2.3", 809 "-meabi", 810 "4", 811 // DiagnosticOptions 812 "-Wundef-prefix=XY", 813 "-fdiagnostics-format", 814 "clang", 815 // HeaderSearchOptions 816 "-stdlib=libc++", 817 "-fimplicit-module-maps", 818 // PreprocessorOptions 819 "-DXY=AB", 820 "-include-pch", 821 "a.pch", 822 // AnalyzerOptions 823 "-analyzer-config", 824 "ctu-import-threshold=42", 825 "-unoptimized-cfg", 826 // MigratorOptions (no manually handled arguments) 827 "-no-ns-alloc-error", 828 // CodeGenOptions 829 "-debug-info-kind=limited", 830 "-debug-info-macro", 831 // DependencyOutputOptions 832 "--show-includes", 833 "-H", 834 // FileSystemOptions (no manually handled arguments) 835 "-working-directory", 836 "folder", 837 // FrontendOptions 838 "-load", 839 "plugin", 840 "-ast-merge", 841 // PreprocessorOutputOptions 842 "-dD", 843 "-CC", 844 }; 845 846 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 847 848 ASSERT_TRUE(Invocation.getLangOpts()->C17); 849 ASSERT_EQ(Invocation.getLangOpts()->MaxTokens, 10u); 850 851 ASSERT_EQ(Invocation.getTargetOpts().SDKVersion, llvm::VersionTuple(1, 2, 3)); 852 ASSERT_EQ(Invocation.getTargetOpts().EABIVersion, EABI::EABI4); 853 854 ASSERT_THAT(Invocation.getDiagnosticOpts().UndefPrefixes, 855 Contains(StrEq("XY"))); 856 ASSERT_EQ(Invocation.getDiagnosticOpts().getFormat(), 857 TextDiagnosticFormat::Clang); 858 859 ASSERT_TRUE(Invocation.getHeaderSearchOpts().UseLibcxx); 860 ASSERT_TRUE(Invocation.getHeaderSearchOpts().ImplicitModuleMaps); 861 862 ASSERT_THAT(Invocation.getPreprocessorOpts().Macros, 863 Contains(std::make_pair(std::string("XY=AB"), false))); 864 ASSERT_EQ(Invocation.getPreprocessorOpts().ImplicitPCHInclude, "a.pch"); 865 866 ASSERT_EQ(Invocation.getAnalyzerOpts()->Config["ctu-import-threshold"], "42"); 867 ASSERT_TRUE(Invocation.getAnalyzerOpts()->UnoptimizedCFG); 868 869 ASSERT_TRUE(Invocation.getMigratorOpts().NoNSAllocReallocError); 870 871 ASSERT_EQ(Invocation.getCodeGenOpts().getDebugInfo(), 872 codegenoptions::DebugInfoKind::LimitedDebugInfo); 873 ASSERT_TRUE(Invocation.getCodeGenOpts().MacroDebugInfo); 874 875 ASSERT_EQ(Invocation.getDependencyOutputOpts().ShowIncludesDest, 876 ShowIncludesDestination::Stdout); 877 ASSERT_TRUE(Invocation.getDependencyOutputOpts().ShowHeaderIncludes); 878 } 879 } // anonymous namespace 880