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