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