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()->SYCLIsDevice); 520 ASSERT_FALSE(Invocation.getLangOpts()->SYCLIsHost); 521 ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None); 522 523 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 524 525 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl")))); 526 ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); 527 } 528 529 TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagPresent) { 530 const char *Args[] = {"-sycl-std=2017"}; 531 532 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 533 534 ASSERT_FALSE(Diags->hasErrorOccurred()); 535 ASSERT_FALSE(Invocation.getLangOpts()->SYCLIsDevice); 536 ASSERT_FALSE(Invocation.getLangOpts()->SYCLIsHost); 537 ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None); 538 539 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 540 541 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-device")))); 542 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-host")))); 543 ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); 544 } 545 546 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagNotPresent) { 547 const char *Args[] = {"-fsycl-is-host"}; 548 549 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 550 551 ASSERT_FALSE(Diags->hasErrorOccurred()); 552 ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None); 553 554 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 555 556 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-host"))); 557 ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); 558 } 559 560 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagPresent) { 561 const char *Args[] = {"-fsycl-is-device", "-sycl-std=2017"}; 562 563 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 564 565 ASSERT_FALSE(Diags->hasErrorOccurred()); 566 ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_2017); 567 568 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 569 570 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device"))); 571 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-sycl-std=2017"))); 572 } 573 574 // Wide integer option. 575 576 TEST_F(CommandLineTest, WideIntegerHighValue) { 577 const char *Args[] = {"-fbuild-session-timestamp=1609827494445723662"}; 578 579 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 580 581 ASSERT_FALSE(Diags->hasErrorOccurred()); 582 ASSERT_EQ(Invocation.getHeaderSearchOpts().BuildSessionTimestamp, 583 1609827494445723662ull); 584 } 585 586 // Tree of boolean options that can be (directly or transitively) implied by 587 // their parent: 588 // 589 // * -cl-unsafe-math-optimizations 590 // * -cl-mad-enable 591 // * -menable-unsafe-fp-math 592 // * -freciprocal-math 593 594 TEST_F(CommandLineTest, ImpliedBoolOptionsNoFlagPresent) { 595 const char *Args[] = {""}; 596 597 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 598 ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath); 599 ASSERT_FALSE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 600 ASSERT_FALSE(Invocation.getLangOpts()->UnsafeFPMath); 601 ASSERT_FALSE(Invocation.getLangOpts()->AllowRecip); 602 603 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 604 605 // Not generated - missing. 606 ASSERT_THAT(GeneratedArgs, 607 Not(Contains(StrEq("-cl-unsafe-math-optimizations")))); 608 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 609 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 610 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 611 } 612 613 TEST_F(CommandLineTest, ImpliedBoolOptionsRootFlagPresent) { 614 const char *Args[] = {"-cl-unsafe-math-optimizations"}; 615 616 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 617 // Explicitly provided root flag. 618 ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath); 619 // Directly implied by explicitly provided root flag. 620 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 621 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 622 // Transitively implied by explicitly provided root flag. 623 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 624 625 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 626 627 // Generated - explicitly provided. 628 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations"))); 629 // Not generated - implied by the generated root flag. 630 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 631 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 632 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 633 } 634 635 TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) { 636 const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable", 637 "-menable-unsafe-fp-math", "-freciprocal-math"}; 638 639 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 640 ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath); 641 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 642 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 643 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 644 645 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 646 647 // Generated - explicitly provided. 648 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations"))); 649 // Not generated - implied by their generated parent. 650 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 651 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 652 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 653 } 654 655 TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) { 656 const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math", 657 "-freciprocal-math"}; 658 659 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 660 ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath); 661 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 662 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 663 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 664 665 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 666 // Not generated - missing. 667 ASSERT_THAT(GeneratedArgs, 668 Not(Contains(StrEq("-cl-unsafe-math-optimizations")))); 669 // Generated - explicitly provided. 670 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable"))); 671 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math"))); 672 // Not generated - implied by its generated parent. 673 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 674 } 675 676 TEST_F(CommandLineTest, PresentAndNotImpliedGenerated) { 677 const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math"}; 678 679 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 680 681 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 682 683 // Present options that were not implied are generated. 684 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable"))); 685 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math"))); 686 } 687 688 // Diagnostic option. 689 690 TEST_F(CommandLineTest, DiagnosticOptionPresent) { 691 const char *Args[] = {"-verify=xyz"}; 692 693 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 694 695 ASSERT_EQ(Invocation.getDiagnosticOpts().VerifyPrefixes, 696 std::vector<std::string>({"xyz"})); 697 698 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 699 700 ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-verify=xyz"), 1)); 701 } 702 703 // Option default depends on language standard. 704 705 TEST_F(CommandLineTest, DigraphsImplied) { 706 const char *Args[] = {""}; 707 708 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 709 ASSERT_TRUE(Invocation.getLangOpts()->Digraphs); 710 711 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 712 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-digraphs")))); 713 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs")))); 714 } 715 716 TEST_F(CommandLineTest, DigraphsDisabled) { 717 const char *Args[] = {"-fno-digraphs"}; 718 719 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 720 ASSERT_FALSE(Invocation.getLangOpts()->Digraphs); 721 722 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 723 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-digraphs"))); 724 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs")))); 725 } 726 727 TEST_F(CommandLineTest, DigraphsNotImplied) { 728 const char *Args[] = {"-std=c89"}; 729 730 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 731 ASSERT_FALSE(Invocation.getLangOpts()->Digraphs); 732 733 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 734 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-digraphs")))); 735 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs")))); 736 } 737 738 TEST_F(CommandLineTest, DigraphsEnabled) { 739 const char *Args[] = {"-std=c89", "-fdigraphs"}; 740 741 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 742 ASSERT_TRUE(Invocation.getLangOpts()->Digraphs); 743 744 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 745 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fdigraphs"))); 746 } 747 748 struct DummyModuleFileExtension 749 : public llvm::RTTIExtends<DummyModuleFileExtension, ModuleFileExtension> { 750 static char ID; 751 752 ModuleFileExtensionMetadata getExtensionMetadata() const override { 753 return {}; 754 }; 755 756 llvm::hash_code hashExtension(llvm::hash_code Code) const override { 757 return {}; 758 } 759 760 std::unique_ptr<ModuleFileExtensionWriter> 761 createExtensionWriter(ASTWriter &Writer) override { 762 return {}; 763 } 764 765 std::unique_ptr<ModuleFileExtensionReader> 766 createExtensionReader(const ModuleFileExtensionMetadata &Metadata, 767 ASTReader &Reader, serialization::ModuleFile &Mod, 768 const llvm::BitstreamCursor &Stream) override { 769 return {}; 770 } 771 }; 772 773 char DummyModuleFileExtension::ID = 0; 774 775 TEST_F(CommandLineTest, TestModuleFileExtension) { 776 const char *Args[] = {"-ftest-module-file-extension=first:2:1:0:first", 777 "-ftest-module-file-extension=second:3:2:1:second"}; 778 779 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 780 ASSERT_THAT(Invocation.getFrontendOpts().ModuleFileExtensions.size(), 2); 781 782 // Exercise the check that only serializes instances of 783 // TestModuleFileExtension by providing an instance of another 784 // ModuleFileExtension subclass. 785 Invocation.getFrontendOpts().ModuleFileExtensions.push_back( 786 std::make_shared<DummyModuleFileExtension>()); 787 788 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 789 790 ASSERT_THAT(GeneratedArgs, 791 ContainsN(HasSubstr("-ftest-module-file-extension="), 2)); 792 ASSERT_THAT( 793 GeneratedArgs, 794 Contains(StrEq("-ftest-module-file-extension=first:2:1:0:first"))); 795 ASSERT_THAT( 796 GeneratedArgs, 797 Contains(StrEq("-ftest-module-file-extension=second:3:2:1:second"))); 798 } 799 800 TEST_F(CommandLineTest, RoundTrip) { 801 // Testing one marshalled and one manually generated option from each 802 // CompilerInvocation member. 803 const char *Args[] = { 804 "-round-trip-args", 805 // LanguageOptions 806 "-std=c17", 807 "-fmax-tokens=10", 808 // TargetOptions 809 "-target-sdk-version=1.2.3", 810 "-meabi", 811 "4", 812 // DiagnosticOptions 813 "-Wundef-prefix=XY", 814 "-fdiagnostics-format", 815 "clang", 816 // HeaderSearchOptions 817 "-stdlib=libc++", 818 "-fimplicit-module-maps", 819 // PreprocessorOptions 820 "-DXY=AB", 821 "-include-pch", 822 "a.pch", 823 // AnalyzerOptions 824 "-analyzer-config", 825 "ctu-import-threshold=42", 826 "-unoptimized-cfg", 827 // MigratorOptions (no manually handled arguments) 828 "-no-ns-alloc-error", 829 // CodeGenOptions 830 "-debug-info-kind=limited", 831 "-debug-info-macro", 832 // DependencyOutputOptions 833 "--show-includes", 834 "-H", 835 // FileSystemOptions (no manually handled arguments) 836 "-working-directory", 837 "folder", 838 // FrontendOptions 839 "-load", 840 "plugin", 841 "-ast-merge", 842 // PreprocessorOutputOptions 843 "-dD", 844 "-CC", 845 }; 846 847 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 848 849 ASSERT_TRUE(Invocation.getLangOpts()->C17); 850 ASSERT_EQ(Invocation.getLangOpts()->MaxTokens, 10u); 851 852 ASSERT_EQ(Invocation.getTargetOpts().SDKVersion, llvm::VersionTuple(1, 2, 3)); 853 ASSERT_EQ(Invocation.getTargetOpts().EABIVersion, EABI::EABI4); 854 855 ASSERT_THAT(Invocation.getDiagnosticOpts().UndefPrefixes, 856 Contains(StrEq("XY"))); 857 ASSERT_EQ(Invocation.getDiagnosticOpts().getFormat(), 858 TextDiagnosticFormat::Clang); 859 860 ASSERT_TRUE(Invocation.getHeaderSearchOpts().UseLibcxx); 861 ASSERT_TRUE(Invocation.getHeaderSearchOpts().ImplicitModuleMaps); 862 863 ASSERT_THAT(Invocation.getPreprocessorOpts().Macros, 864 Contains(std::make_pair(std::string("XY=AB"), false))); 865 ASSERT_EQ(Invocation.getPreprocessorOpts().ImplicitPCHInclude, "a.pch"); 866 867 ASSERT_EQ(Invocation.getAnalyzerOpts()->Config["ctu-import-threshold"], "42"); 868 ASSERT_TRUE(Invocation.getAnalyzerOpts()->UnoptimizedCFG); 869 870 ASSERT_TRUE(Invocation.getMigratorOpts().NoNSAllocReallocError); 871 872 ASSERT_EQ(Invocation.getCodeGenOpts().getDebugInfo(), 873 codegenoptions::DebugInfoKind::LimitedDebugInfo); 874 ASSERT_TRUE(Invocation.getCodeGenOpts().MacroDebugInfo); 875 876 ASSERT_EQ(Invocation.getDependencyOutputOpts().ShowIncludesDest, 877 ShowIncludesDestination::Stdout); 878 ASSERT_TRUE(Invocation.getDependencyOutputOpts().ShowHeaderIncludes); 879 } 880 881 TEST_F(CommandLineTest, PluginArgsRoundTripDeterminism) { 882 const char *Args[] = { 883 "-plugin-arg-blink-gc-plugin", "no-members-in-stack-allocated", 884 "-plugin-arg-find-bad-constructs", "checked-ptr-as-trivial-member", 885 "-plugin-arg-find-bad-constructs", "check-ipc", 886 // Enable round-trip to ensure '-plugin-arg' generation is deterministic. 887 "-round-trip-args"}; 888 889 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); 890 } 891 } // anonymous namespace 892