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