|  | //===- unittests/Frontend/CompilerInvocationTest.cpp - CI tests //---------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/Frontend/CompilerInvocation.h" | 
|  | #include "clang/Basic/TargetOptions.h" | 
|  | #include "clang/Frontend/CompilerInstance.h" | 
|  | #include "clang/Frontend/TextDiagnosticBuffer.h" | 
|  | #include "clang/Lex/PreprocessorOptions.h" | 
|  | #include "clang/Serialization/ModuleFileExtension.h" | 
|  | #include "llvm/Support/VirtualFileSystem.h" | 
|  | #include "llvm/TargetParser/Host.h" | 
|  |  | 
|  | #include "gmock/gmock.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using namespace llvm; | 
|  | using namespace clang; | 
|  |  | 
|  | using ::testing::Contains; | 
|  | using ::testing::HasSubstr; | 
|  | using ::testing::StrEq; | 
|  | using ::testing::StartsWith; | 
|  |  | 
|  | namespace { | 
|  | class CommandLineTest : public ::testing::Test { | 
|  | public: | 
|  | DiagnosticOptions DiagOpts; | 
|  | IntrusiveRefCntPtr<DiagnosticsEngine> Diags; | 
|  | SmallVector<const char *, 32> GeneratedArgs; | 
|  | BumpPtrAllocator Alloc; | 
|  | StringSaver StringPool; | 
|  | CompilerInvocation Invocation; | 
|  |  | 
|  | const char *operator()(const Twine &Arg) { | 
|  | return StringPool.save(Arg).data(); | 
|  | } | 
|  |  | 
|  | CommandLineTest() | 
|  | : Diags(CompilerInstance::createDiagnostics( | 
|  | *llvm::vfs::getRealFileSystem(), DiagOpts, | 
|  | new TextDiagnosticBuffer())), | 
|  | StringPool(Alloc) {} | 
|  | }; | 
|  |  | 
|  | template <typename M> | 
|  | std::string describeContainsN(M InnerMatcher, unsigned N, bool Negation) { | 
|  | StringRef Contains = Negation ? "doesn't contain" : "contains"; | 
|  | StringRef Instance = N == 1 ? " instance " : " instances "; | 
|  | StringRef Element = "of element that "; | 
|  |  | 
|  | std::ostringstream Inner; | 
|  | InnerMatcher.impl().DescribeTo(&Inner); | 
|  |  | 
|  | return (Contains + " exactly " + Twine(N) + Instance + Element + Inner.str()) | 
|  | .str(); | 
|  | } | 
|  |  | 
|  | MATCHER_P2(ContainsN, InnerMatcher, N, | 
|  | describeContainsN(InnerMatcher, N, negation)) { | 
|  | auto InnerMatches = [this](const auto &Element) { | 
|  | ::testing::internal::DummyMatchResultListener InnerListener; | 
|  | return InnerMatcher.impl().MatchAndExplain(Element, &InnerListener); | 
|  | }; | 
|  |  | 
|  | return count_if(arg, InnerMatches) == N; | 
|  | } | 
|  |  | 
|  | TEST(ContainsN, Empty) { | 
|  | const char *Array[] = {""}; | 
|  |  | 
|  | ASSERT_THAT(Array, ContainsN(StrEq("x"), 0)); | 
|  | ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1))); | 
|  | ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2))); | 
|  | } | 
|  |  | 
|  | TEST(ContainsN, Zero) { | 
|  | const char *Array[] = {"y"}; | 
|  |  | 
|  | ASSERT_THAT(Array, ContainsN(StrEq("x"), 0)); | 
|  | ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1))); | 
|  | ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2))); | 
|  | } | 
|  |  | 
|  | TEST(ContainsN, One) { | 
|  | const char *Array[] = {"a", "b", "x", "z"}; | 
|  |  | 
|  | ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 0))); | 
|  | ASSERT_THAT(Array, ContainsN(StrEq("x"), 1)); | 
|  | ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2))); | 
|  | } | 
|  |  | 
|  | TEST(ContainsN, Two) { | 
|  | const char *Array[] = {"x", "a", "b", "x"}; | 
|  |  | 
|  | ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 0))); | 
|  | ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1))); | 
|  | ASSERT_THAT(Array, ContainsN(StrEq("x"), 2)); | 
|  | } | 
|  |  | 
|  | // Copy constructor/assignment perform deep copy of reference-counted pointers. | 
|  |  | 
|  | TEST(CompilerInvocationTest, DeepCopyConstructor) { | 
|  | CompilerInvocation A; | 
|  | A.getAnalyzerOpts().Config["Key"] = "Old"; | 
|  |  | 
|  | CompilerInvocation B(A); | 
|  | B.getAnalyzerOpts().Config["Key"] = "New"; | 
|  |  | 
|  | ASSERT_EQ(A.getAnalyzerOpts().Config["Key"], "Old"); | 
|  | } | 
|  |  | 
|  | TEST(CompilerInvocationTest, DeepCopyAssignment) { | 
|  | CompilerInvocation A; | 
|  | A.getAnalyzerOpts().Config["Key"] = "Old"; | 
|  |  | 
|  | CompilerInvocation B; | 
|  | B = A; | 
|  | B.getAnalyzerOpts().Config["Key"] = "New"; | 
|  |  | 
|  | ASSERT_EQ(A.getAnalyzerOpts().Config["Key"], "Old"); | 
|  | } | 
|  |  | 
|  | TEST(CompilerInvocationTest, CopyOnWriteConstructor) { | 
|  | CowCompilerInvocation A; | 
|  | A.getMutFrontendOpts().OutputFile = "x.o"; | 
|  |  | 
|  | // B's FrontendOptions are initially shared with A. | 
|  | CowCompilerInvocation B(A); | 
|  | EXPECT_EQ(&A.getFrontendOpts(), &B.getFrontendOpts()); | 
|  |  | 
|  | // Modifying A's FrontendOptions creates new copy, does not affect other opts. | 
|  | A.getMutFrontendOpts().OutputFile = "y.o"; | 
|  | EXPECT_NE(&A.getFrontendOpts(), &B.getFrontendOpts()); | 
|  | EXPECT_EQ(&A.getCodeGenOpts(), &B.getCodeGenOpts()); | 
|  |  | 
|  | // The new copy reflects the modification, old instance remains unchanged. | 
|  | EXPECT_EQ(A.getFrontendOpts().OutputFile, "y.o"); | 
|  | EXPECT_EQ(B.getFrontendOpts().OutputFile, "x.o"); | 
|  | } | 
|  |  | 
|  | TEST(CompilerInvocationTest, CopyOnWriteAssignment) { | 
|  | CowCompilerInvocation A; | 
|  | A.getMutFrontendOpts().OutputFile = "x.o"; | 
|  |  | 
|  | // B's FrontendOptions are initially independent of A. | 
|  | CowCompilerInvocation B; | 
|  | EXPECT_NE(&A.getFrontendOpts(), &B.getFrontendOpts()); | 
|  |  | 
|  | // B's FrontendOptions are shared with A after assignment. | 
|  | B = A; | 
|  | EXPECT_EQ(&A.getFrontendOpts(), &B.getFrontendOpts()); | 
|  |  | 
|  | // Modifying A's FrontendOptions creates new copy, does not affect other opts. | 
|  | A.getMutFrontendOpts().OutputFile = "y.o"; | 
|  | EXPECT_NE(&A.getFrontendOpts(), &B.getFrontendOpts()); | 
|  | EXPECT_EQ(&A.getCodeGenOpts(), &B.getCodeGenOpts()); | 
|  |  | 
|  | // The new copy reflects the modification, old instance remains unchanged. | 
|  | EXPECT_EQ(A.getFrontendOpts().OutputFile, "y.o"); | 
|  | EXPECT_EQ(B.getFrontendOpts().OutputFile, "x.o"); | 
|  | } | 
|  |  | 
|  | // Boolean option with a keypath that defaults to true. | 
|  | // The only flag with a negative spelling can set the keypath to false. | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagNotPresent) { | 
|  | const char *Args[] = {""}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-temp-file")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagPresent) { | 
|  | const char *Args[] = {"-fno-temp-file"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_FALSE(Invocation.getFrontendOpts().UseTemporary); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-temp-file"))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, CC1FlagPresentWhenDoingRoundTrip) { | 
|  | const char *Args[] = {"-cc1", "-round-trip-args"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  |  | 
|  | ASSERT_THAT(std::string(Invocation.getCodeGenOpts().CmdArgs.begin(), | 
|  | Invocation.getCodeGenOpts().CmdArgs.end()), | 
|  | StartsWith("-cc1")); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, CC1FlagPresentWhenNotDoingRoundTrip) { | 
|  | const char *Args[] = {"-cc1", "-no-round-trip-args"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  |  | 
|  | ASSERT_THAT(std::string(Invocation.getCodeGenOpts().CmdArgs.begin(), | 
|  | Invocation.getCodeGenOpts().CmdArgs.end()), | 
|  | StartsWith("-cc1")); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagUnknownPresent) { | 
|  | const char *Args[] = {"-ftemp-file"}; | 
|  |  | 
|  | // Driver-only flag. | 
|  | ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary); | 
|  | } | 
|  |  | 
|  | // Boolean option with a keypath that defaults to true. | 
|  | // The flag with negative spelling can set the keypath to false. | 
|  | // The flag with positive spelling can reset the keypath to true. | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNone) { | 
|  | const char *Args[] = {""}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-autolink")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNegChange) { | 
|  | const char *Args[] = {"-fno-autolink"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_FALSE(Invocation.getCodeGenOpts().Autolink); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-autolink"))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultTruePresentPosReset) { | 
|  | const char *Args[] = {"-fautolink"}; | 
|  |  | 
|  | // Driver-only flag. | 
|  | ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink); | 
|  | } | 
|  |  | 
|  | // Boolean option with a keypath that defaults to false. | 
|  | // The flag with negative spelling can set the keypath to true. | 
|  | // The flag with positive spelling can reset the keypath to false. | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNone) { | 
|  | const char *Args[] = {""}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-inline-line-tables")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegChange) { | 
|  | const char *Args[] = {"-gno-inline-line-tables"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getCodeGenOpts().NoInlineLineTables); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gno-inline-line-tables"))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosReset) { | 
|  | const char *Args[] = {"-ginline-line-tables"}; | 
|  |  | 
|  | // Driver-only flag. | 
|  | ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables); | 
|  | } | 
|  |  | 
|  | // Boolean option with a keypath that defaults to false. | 
|  | // The flag with positive spelling can set the keypath to true. | 
|  | // The flag with negative spelling can reset the keypath to false. | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNoneX) { | 
|  | const char *Args[] = {""}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gcodeview-ghash")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosChange) { | 
|  | const char *Args[] = {"-gcodeview-ghash"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getCodeGenOpts().CodeViewGHash); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gcodeview-ghash"))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegReset) { | 
|  | const char *Args[] = {"-gno-codeview-ghash"}; | 
|  |  | 
|  | // Driver-only flag. | 
|  | ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash); | 
|  | } | 
|  |  | 
|  | // Boolean option with a keypath that defaults to an arbitrary expression. | 
|  | // The flag with positive spelling can set the keypath to true. | 
|  | // The flag with negative spelling can set the keypath to false. | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentNone) { | 
|  | const char *Args = {""}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_EQ(Invocation.getCodeGenOpts().ClearASTBeforeBackend, false); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-no-clear-ast-before-backend")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-clear-ast-before-backend")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentChange) { | 
|  | const char *Args[] = {"-clear-ast-before-backend"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_EQ(Invocation.getCodeGenOpts().ClearASTBeforeBackend, true); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-clear-ast-before-backend"))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-no-clear-ast-before-backend")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentReset) { | 
|  | const char *Args[] = {"-no-clear-ast-before-backend"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_EQ(Invocation.getCodeGenOpts().ClearASTBeforeBackend, false); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-no-clear-ast-before-backend")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-clear-ast-before-backend")))); | 
|  | } | 
|  |  | 
|  | // Boolean option that gets the CC1Option flag from a let statement (which | 
|  | // is applied **after** the record is defined): | 
|  | // | 
|  | //   let Flags = [CC1Option] in { | 
|  | //     defm option : BoolOption<...>; | 
|  | //   } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentNone) { | 
|  | const char *Args[] = {""}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_FALSE(Invocation.getCodeGenOpts().DebugPassManager); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdebug-pass-manager")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentPos) { | 
|  | const char *Args[] = {"-fdebug-pass-manager"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getCodeGenOpts().DebugPassManager); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fdebug-pass-manager"), 1)); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentNeg) { | 
|  | const char *Args[] = {"-fno-debug-pass-manager"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_FALSE(Invocation.getCodeGenOpts().DebugPassManager); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdebug-pass-manager")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, CanGenerateCC1CommandLineFlag) { | 
|  | const char *Args[] = {"-fmodules-strict-context-hash"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash"))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparate) { | 
|  | const char *TripleCStr = "i686-apple-darwin9"; | 
|  | const char *Args[] = {"-triple", TripleCStr}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest,  CanGenerateCC1CommandLineSeparateRequiredPresent) { | 
|  | const std::string DefaultTriple = | 
|  | llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); | 
|  | const char *Args[] = {"-triple", DefaultTriple.c_str()}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | // Triple should always be emitted even if it is the default | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str()))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredAbsent) { | 
|  | const std::string DefaultTriple = | 
|  | llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); | 
|  | const char *Args[] = {""}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | // Triple should always be emitted even if it is the default | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str()))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, SeparateEnumNonDefault) { | 
|  | const char *Args[] = {"-mrelocation-model", "static"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_EQ(Invocation.getCodeGenOpts().RelocationModel, Reloc::Model::Static); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | // Non default relocation model. | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-mrelocation-model"))); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("static"))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model=static")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, SeparateEnumDefault) { | 
|  | const char *Args[] = {"-mrelocation-model", "pic"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_EQ(Invocation.getCodeGenOpts().RelocationModel, Reloc::Model::PIC_); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | // Default relocation model. | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("pic")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model=pic")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, JoinedEnumNonDefault) { | 
|  | const char *Args[] = {"-fobjc-dispatch-method=non-legacy"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_EQ(Invocation.getCodeGenOpts().getObjCDispatchMethod(), | 
|  | CodeGenOptions::NonLegacy); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, | 
|  | Contains(StrEq("-fobjc-dispatch-method=non-legacy"))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fobjc-dispatch-method=")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("non-legacy")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, JoinedEnumDefault) { | 
|  | const char *Args[] = {"-fobjc-dispatch-method=legacy"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_EQ(Invocation.getCodeGenOpts().getObjCDispatchMethod(), | 
|  | CodeGenOptions::Legacy); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, | 
|  | Not(Contains(StrEq("-fobjc-dispatch-method=legacy")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fobjc-dispatch-method=")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("legacy")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, StringVectorEmpty) { | 
|  | const char *Args[] = {""}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getFrontendOpts().ModuleMapFiles.empty()); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-fmodule-map-file")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, StringVectorSingle) { | 
|  | const char *Args[] = {"-fmodule-map-file=a"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_EQ(Invocation.getFrontendOpts().ModuleMapFiles, | 
|  | std::vector<std::string>({"a"})); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=a"), 1)); | 
|  | ASSERT_THAT(GeneratedArgs, ContainsN(HasSubstr("-fmodule-map-file"), 1)); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, StringVectorMultiple) { | 
|  | const char *Args[] = {"-fmodule-map-file=a", "-fmodule-map-file=b"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getFrontendOpts().ModuleMapFiles == | 
|  | std::vector<std::string>({"a", "b"})); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=a"), 1)); | 
|  | ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=b"), 1)); | 
|  | ASSERT_THAT(GeneratedArgs, ContainsN(HasSubstr("-fmodule-map-file"), 2)); | 
|  | } | 
|  |  | 
|  | // CommaJoined option with MarshallingInfoStringVector. | 
|  |  | 
|  | TEST_F(CommandLineTest, StringVectorCommaJoinedNone) { | 
|  | const char *Args[] = {""}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().CommentOpts.BlockCommandNames.empty()); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, | 
|  | Not(Contains(HasSubstr("-fcomment-block-commands")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, StringVectorCommaJoinedSingle) { | 
|  | const char *Args[] = {"-fcomment-block-commands=x,y"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_EQ(Invocation.getLangOpts().CommentOpts.BlockCommandNames, | 
|  | std::vector<std::string>({"x", "y"})); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, | 
|  | ContainsN(StrEq("-fcomment-block-commands=x,y"), 1)); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, StringVectorCommaJoinedMultiple) { | 
|  | const char *Args[] = {"-fcomment-block-commands=x,y", | 
|  | "-fcomment-block-commands=a,b"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_EQ(Invocation.getLangOpts().CommentOpts.BlockCommandNames, | 
|  | std::vector<std::string>({"x", "y", "a", "b"})); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, | 
|  | ContainsN(StrEq("-fcomment-block-commands=x,y,a,b"), 1)); | 
|  | } | 
|  |  | 
|  | // A flag that should be parsed only if a condition is met. | 
|  |  | 
|  | TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagNotPresent) { | 
|  | const char *Args[] = {""}; | 
|  |  | 
|  | CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); | 
|  |  | 
|  | ASSERT_FALSE(Diags->hasErrorOccurred()); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().SYCLIsDevice); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().SYCLIsHost); | 
|  | ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_None); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagPresent) { | 
|  | const char *Args[] = {"-sycl-std=2017"}; | 
|  |  | 
|  | CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); | 
|  |  | 
|  | ASSERT_FALSE(Diags->hasErrorOccurred()); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().SYCLIsDevice); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().SYCLIsHost); | 
|  | ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_None); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-device")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-host")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, ConditionalParsingIfNonsenseSyclStdArg) { | 
|  | const char *Args[] = {"-fsycl-is-device", "-sycl-std=garbage"}; | 
|  |  | 
|  | CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); | 
|  |  | 
|  | ASSERT_TRUE(Diags->hasErrorOccurred()); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().SYCLIsDevice); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().SYCLIsHost); | 
|  | ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_None); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device"))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-host")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, ConditionalParsingIfOddSyclStdArg1) { | 
|  | const char *Args[] = {"-fsycl-is-device", "-sycl-std=121"}; | 
|  |  | 
|  | CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); | 
|  |  | 
|  | ASSERT_FALSE(Diags->hasErrorOccurred()); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().SYCLIsDevice); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().SYCLIsHost); | 
|  | ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_2017); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device"))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-host")))); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(HasSubstr("-sycl-std=2017"))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, ConditionalParsingIfOddSyclStdArg2) { | 
|  | const char *Args[] = {"-fsycl-is-device", "-sycl-std=1.2.1"}; | 
|  |  | 
|  | CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); | 
|  |  | 
|  | ASSERT_FALSE(Diags->hasErrorOccurred()); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().SYCLIsDevice); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().SYCLIsHost); | 
|  | ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_2017); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device"))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-host")))); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(HasSubstr("-sycl-std=2017"))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, ConditionalParsingIfOddSyclStdArg3) { | 
|  | const char *Args[] = {"-fsycl-is-device", "-sycl-std=sycl-1.2.1"}; | 
|  |  | 
|  | CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); | 
|  |  | 
|  | ASSERT_FALSE(Diags->hasErrorOccurred()); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().SYCLIsDevice); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().SYCLIsHost); | 
|  | ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_2017); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device"))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-host")))); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(HasSubstr("-sycl-std=2017"))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagNotPresentHost) { | 
|  | const char *Args[] = {"-fsycl-is-host"}; | 
|  |  | 
|  | CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); | 
|  |  | 
|  | ASSERT_FALSE(Diags->hasErrorOccurred()); | 
|  | ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), | 
|  | LangOptions::SYCL_Default); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-host"))); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(HasSubstr("-sycl-std="))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagNotPresentDevice) { | 
|  | const char *Args[] = {"-fsycl-is-device"}; | 
|  |  | 
|  | CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); | 
|  |  | 
|  | ASSERT_FALSE(Diags->hasErrorOccurred()); | 
|  | ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), | 
|  | LangOptions::SYCL_Default); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device"))); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(HasSubstr("-sycl-std="))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagPresent) { | 
|  | const char *Args[] = {"-fsycl-is-device", "-sycl-std=2017"}; | 
|  |  | 
|  | CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); | 
|  |  | 
|  | ASSERT_FALSE(Diags->hasErrorOccurred()); | 
|  | ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_2017); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device"))); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-sycl-std=2017"))); | 
|  | } | 
|  |  | 
|  | // Wide integer option. | 
|  |  | 
|  | TEST_F(CommandLineTest, WideIntegerHighValue) { | 
|  | const char *Args[] = {"-fbuild-session-timestamp=1609827494445723662"}; | 
|  |  | 
|  | CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); | 
|  |  | 
|  | ASSERT_FALSE(Diags->hasErrorOccurred()); | 
|  | ASSERT_EQ(Invocation.getHeaderSearchOpts().BuildSessionTimestamp, | 
|  | 1609827494445723662ull); | 
|  | } | 
|  |  | 
|  | // Tree of boolean options that can be (directly or transitively) implied by | 
|  | // their parent: | 
|  | // | 
|  | //   * -cl-unsafe-math-optimizations | 
|  | //     * -cl-mad-enable | 
|  | //     * -funsafe-math-optimizations | 
|  | //       * -freciprocal-math | 
|  |  | 
|  | TEST_F(CommandLineTest, ImpliedBoolOptionsNoFlagPresent) { | 
|  | const char *Args[] = {""}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().CLUnsafeMath); | 
|  | ASSERT_FALSE(Invocation.getCodeGenOpts().LessPreciseFPMAD); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().UnsafeFPMath); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().AllowRecip); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | // Not generated - missing. | 
|  | ASSERT_THAT(GeneratedArgs, | 
|  | Not(Contains(StrEq("-cl-unsafe-math-optimizations")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); | 
|  | ASSERT_THAT(GeneratedArgs, | 
|  | Not(Contains(StrEq("-funsafe-math-optimizations")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, ImpliedBoolOptionsRootFlagPresent) { | 
|  | const char *Args[] = {"-cl-unsafe-math-optimizations"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | // Explicitly provided root flag. | 
|  | ASSERT_TRUE(Invocation.getLangOpts().CLUnsafeMath); | 
|  | // Directly implied by explicitly provided root flag. | 
|  | ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().UnsafeFPMath); | 
|  | // Transitively implied by explicitly provided root flag. | 
|  | ASSERT_TRUE(Invocation.getLangOpts().AllowRecip); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | // Generated - explicitly provided. | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations"))); | 
|  | // Not generated - implied by the generated root flag. | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); | 
|  | ASSERT_THAT(GeneratedArgs, | 
|  | Not(Contains(StrEq("-funsafe-math-optimizations")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) { | 
|  | const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable", | 
|  | "-funsafe-math-optimizations", "-freciprocal-math"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().CLUnsafeMath); | 
|  | ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().UnsafeFPMath); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().AllowRecip); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | // Generated - explicitly provided. | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations"))); | 
|  | // Not generated - implied by their generated parent. | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); | 
|  | ASSERT_THAT(GeneratedArgs, | 
|  | Not(Contains(StrEq("-funsafe-math-optimizations")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) { | 
|  | const char *Args[] = {"-cl-mad-enable", "-funsafe-math-optimizations", | 
|  | "-freciprocal-math"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().CLUnsafeMath); | 
|  | ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().UnsafeFPMath); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().AllowRecip); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | // Not generated - missing. | 
|  | ASSERT_THAT(GeneratedArgs, | 
|  | Not(Contains(StrEq("-cl-unsafe-math-optimizations")))); | 
|  | // Generated - explicitly provided. | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable"))); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-funsafe-math-optimizations"))); | 
|  | // Not generated - implied by its generated parent. | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, PresentAndNotImpliedGenerated) { | 
|  | const char *Args[] = {"-cl-mad-enable", "-funsafe-math-optimizations"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | // Present options that were not implied are generated. | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable"))); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-funsafe-math-optimizations"))); | 
|  | } | 
|  |  | 
|  | // Diagnostic option. | 
|  |  | 
|  | TEST_F(CommandLineTest, DiagnosticOptionPresent) { | 
|  | const char *Args[] = {"-verify=xyz"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  |  | 
|  | ASSERT_EQ(Invocation.getDiagnosticOpts().VerifyPrefixes, | 
|  | std::vector<std::string>({"xyz"})); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-verify=xyz"), 1)); | 
|  | } | 
|  |  | 
|  | // Option default depends on language standard. | 
|  |  | 
|  | TEST_F(CommandLineTest, DigraphsImplied) { | 
|  | const char *Args[] = {""}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().Digraphs); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-digraphs")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, DigraphsDisabled) { | 
|  | const char *Args[] = {"-fno-digraphs"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().Digraphs); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-digraphs"))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, DigraphsNotImplied) { | 
|  | const char *Args[] = {"-std=c89"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_FALSE(Invocation.getLangOpts().Digraphs); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-digraphs")))); | 
|  | ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs")))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, DigraphsEnabled) { | 
|  | const char *Args[] = {"-std=c89", "-fdigraphs"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_TRUE(Invocation.getLangOpts().Digraphs); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fdigraphs"))); | 
|  | } | 
|  |  | 
|  | struct DummyModuleFileExtension | 
|  | : public llvm::RTTIExtends<DummyModuleFileExtension, ModuleFileExtension> { | 
|  | static char ID; | 
|  |  | 
|  | ModuleFileExtensionMetadata getExtensionMetadata() const override { | 
|  | return {}; | 
|  | }; | 
|  |  | 
|  | void hashExtension(ExtensionHashBuilder &HBuilder) const override {} | 
|  |  | 
|  | std::unique_ptr<ModuleFileExtensionWriter> | 
|  | createExtensionWriter(ASTWriter &Writer) override { | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<ModuleFileExtensionReader> | 
|  | createExtensionReader(const ModuleFileExtensionMetadata &Metadata, | 
|  | ASTReader &Reader, serialization::ModuleFile &Mod, | 
|  | const llvm::BitstreamCursor &Stream) override { | 
|  | return {}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | char DummyModuleFileExtension::ID = 0; | 
|  |  | 
|  | TEST_F(CommandLineTest, TestModuleFileExtension) { | 
|  | const char *Args[] = {"-ftest-module-file-extension=first:2:1:0:first", | 
|  | "-ftest-module-file-extension=second:3:2:1:second"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | ASSERT_THAT(Invocation.getFrontendOpts().ModuleFileExtensions.size(), 2); | 
|  |  | 
|  | // Exercise the check that only serializes instances of | 
|  | // TestModuleFileExtension by providing an instance of another | 
|  | // ModuleFileExtension subclass. | 
|  | Invocation.getFrontendOpts().ModuleFileExtensions.push_back( | 
|  | std::make_shared<DummyModuleFileExtension>()); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  |  | 
|  | ASSERT_THAT(GeneratedArgs, | 
|  | ContainsN(HasSubstr("-ftest-module-file-extension="), 2)); | 
|  | ASSERT_THAT( | 
|  | GeneratedArgs, | 
|  | Contains(StrEq("-ftest-module-file-extension=first:2:1:0:first"))); | 
|  | ASSERT_THAT( | 
|  | GeneratedArgs, | 
|  | Contains(StrEq("-ftest-module-file-extension=second:3:2:1:second"))); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, RoundTrip) { | 
|  | // Testing one marshalled and one manually generated option from each | 
|  | // CompilerInvocation member. | 
|  | const char *Args[] = { | 
|  | "-round-trip-args", | 
|  | // LanguageOptions | 
|  | "-std=c17", | 
|  | "-fmax-tokens=10", | 
|  | // TargetOptions | 
|  | "-target-sdk-version=1.2.3", | 
|  | "-meabi", | 
|  | "4", | 
|  | // DiagnosticOptions | 
|  | "-Wundef-prefix=XY", | 
|  | "-fdiagnostics-format", | 
|  | "clang", | 
|  | // HeaderSearchOptions | 
|  | "-stdlib=libc++", | 
|  | "-fimplicit-module-maps", | 
|  | // PreprocessorOptions | 
|  | "-DXY=AB", | 
|  | "-include-pch", | 
|  | "a.pch", | 
|  | // AnalyzerOptions | 
|  | "-analyzer-config", | 
|  | "ctu-import-threshold=42", | 
|  | "-unoptimized-cfg", | 
|  | // MigratorOptions (no manually handled arguments) | 
|  | "-no-ns-alloc-error", | 
|  | // CodeGenOptions | 
|  | "-debug-info-kind=limited", | 
|  | "-debug-info-macro", | 
|  | // DependencyOutputOptions | 
|  | "--show-includes", | 
|  | "-H", | 
|  | // FileSystemOptions (no manually handled arguments) | 
|  | "-working-directory", | 
|  | "folder", | 
|  | // FrontendOptions | 
|  | "-load", | 
|  | "plugin", | 
|  | "-ast-merge", | 
|  | // PreprocessorOutputOptions | 
|  | "-dD", | 
|  | "-CC", | 
|  | }; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  |  | 
|  | ASSERT_TRUE(Invocation.getLangOpts().C17); | 
|  | ASSERT_EQ(Invocation.getLangOpts().MaxTokens, 10u); | 
|  |  | 
|  | ASSERT_EQ(Invocation.getTargetOpts().SDKVersion, llvm::VersionTuple(1, 2, 3)); | 
|  | ASSERT_EQ(Invocation.getTargetOpts().EABIVersion, EABI::EABI4); | 
|  |  | 
|  | ASSERT_THAT(Invocation.getDiagnosticOpts().UndefPrefixes, | 
|  | Contains(StrEq("XY"))); | 
|  | ASSERT_EQ(Invocation.getDiagnosticOpts().getFormat(), | 
|  | TextDiagnosticFormat::Clang); | 
|  |  | 
|  | ASSERT_TRUE(Invocation.getHeaderSearchOpts().UseLibcxx); | 
|  | ASSERT_TRUE(Invocation.getHeaderSearchOpts().ImplicitModuleMaps); | 
|  |  | 
|  | ASSERT_THAT(Invocation.getPreprocessorOpts().Macros, | 
|  | Contains(std::make_pair(std::string("XY=AB"), false))); | 
|  | ASSERT_EQ(Invocation.getPreprocessorOpts().ImplicitPCHInclude, "a.pch"); | 
|  |  | 
|  | ASSERT_EQ(Invocation.getAnalyzerOpts().Config["ctu-import-threshold"], "42"); | 
|  | ASSERT_TRUE(Invocation.getAnalyzerOpts().UnoptimizedCFG); | 
|  |  | 
|  | ASSERT_TRUE(Invocation.getMigratorOpts().NoNSAllocReallocError); | 
|  |  | 
|  | ASSERT_EQ(Invocation.getCodeGenOpts().getDebugInfo(), | 
|  | codegenoptions::DebugInfoKind::LimitedDebugInfo); | 
|  | ASSERT_TRUE(Invocation.getCodeGenOpts().MacroDebugInfo); | 
|  |  | 
|  | ASSERT_EQ(Invocation.getDependencyOutputOpts().ShowIncludesDest, | 
|  | ShowIncludesDestination::Stdout); | 
|  | ASSERT_TRUE(Invocation.getDependencyOutputOpts().ShowHeaderIncludes); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, PluginArgsRoundTripDeterminism) { | 
|  | const char *Args[] = { | 
|  | "-plugin-arg-blink-gc-plugin", "no-members-in-stack-allocated", | 
|  | "-plugin-arg-find-bad-constructs", "checked-ptr-as-trivial-member", | 
|  | "-plugin-arg-find-bad-constructs", "check-ipc", | 
|  | // Enable round-trip to ensure '-plugin-arg' generation is deterministic. | 
|  | "-round-trip-args"}; | 
|  |  | 
|  | ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | } | 
|  |  | 
|  | TEST_F(CommandLineTest, WarningSuppressionMappings) { | 
|  | const char *Args[] = {"--warning-suppression-mappings=foo.txt"}; | 
|  |  | 
|  | EXPECT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); | 
|  | EXPECT_EQ(Invocation.getDiagnosticOpts().DiagnosticSuppressionMappingsFile, | 
|  | "foo.txt"); | 
|  |  | 
|  | Invocation.generateCC1CommandLine(GeneratedArgs, *this); | 
|  | EXPECT_THAT(GeneratedArgs, Contains(StrEq(Args[0]))); | 
|  | } | 
|  | } // anonymous namespace |