//===- 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/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticBuffer.h" #include "llvm/Support/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; namespace { class CommandLineTest : public ::testing::Test { public: IntrusiveRefCntPtr Diags; SmallVector GeneratedArgs; SmallVector GeneratedArgsStorage; CompilerInvocation Invocation; const char *operator()(const Twine &Arg) { return GeneratedArgsStorage.emplace_back(Arg.str()).c_str(); } CommandLineTest() : Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions(), new TextDiagnosticBuffer())) { } }; template 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)); } // 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, 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. static constexpr unsigned PassManagerDefault = !static_cast(LLVM_ENABLE_NEW_PASS_MANAGER); static constexpr const char *PassManagerResetByFlag = LLVM_ENABLE_NEW_PASS_MANAGER ? "-fno-legacy-pass-manager" : "-flegacy-pass-manager"; static constexpr const char *PassManagerChangedByFlag = LLVM_ENABLE_NEW_PASS_MANAGER ? "-flegacy-pass-manager" : "-fno-legacy-pass-manager"; TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentNone) { const char *Args = {""}; ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault); Invocation.generateCC1CommandLine(GeneratedArgs, *this); ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag)))); ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag)))); } TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentChange) { const char *Args[] = {PassManagerChangedByFlag}; ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, !PassManagerDefault); Invocation.generateCC1CommandLine(GeneratedArgs, *this); ASSERT_THAT(GeneratedArgs, Contains(StrEq(PassManagerChangedByFlag))); ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag)))); } TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentReset) { const char *Args[] = {PassManagerResetByFlag}; ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags)); ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault); Invocation.generateCC1CommandLine(GeneratedArgs, *this); ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag)))); ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag)))); } // 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({"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({"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({"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({"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()->SYCL); 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()->SYCL); 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, ConditionalParsingIfTrueFlagNotPresent) { const char *Args[] = {"-fsycl"}; CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); ASSERT_FALSE(Diags->hasErrorOccurred()); ASSERT_TRUE(Invocation.getLangOpts()->SYCL); ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None); Invocation.generateCC1CommandLine(GeneratedArgs, *this); ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl"))); ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std=")))); } TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagPresent) { const char *Args[] = {"-fsycl", "-sycl-std=2017"}; CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); ASSERT_FALSE(Diags->hasErrorOccurred()); ASSERT_TRUE(Invocation.getLangOpts()->SYCL); ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_2017); Invocation.generateCC1CommandLine(GeneratedArgs, *this); ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl"))); 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 // * -menable-unsafe-fp-math // * -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("-menable-unsafe-fp-math")))); 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("-menable-unsafe-fp-math")))); ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); } TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) { const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable", "-menable-unsafe-fp-math", "-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("-menable-unsafe-fp-math")))); ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); } TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) { const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math", "-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("-menable-unsafe-fp-math"))); // 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", "-menable-unsafe-fp-math"}; 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("-menable-unsafe-fp-math"))); } // 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({"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"))); } } // anonymous namespace