1 //===- unittests/Frontend/CompilerInvocationTest.cpp - CI tests //---------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/Frontend/CompilerInvocation.h"
10 #include "clang/Basic/TargetOptions.h"
11 #include "clang/Frontend/CompilerInstance.h"
12 #include "clang/Frontend/TextDiagnosticBuffer.h"
13 #include "clang/Lex/PreprocessorOptions.h"
14 #include "clang/Serialization/ModuleFileExtension.h"
15 #include "llvm/Support/Host.h"
17 #include "gmock/gmock.h"
18 #include "gtest/gtest.h"
21 using namespace clang
;
23 using ::testing::Contains
;
24 using ::testing::HasSubstr
;
25 using ::testing::StrEq
;
28 class CommandLineTest
: public ::testing::Test
{
30 IntrusiveRefCntPtr
<DiagnosticsEngine
> Diags
;
31 SmallVector
<const char *, 32> GeneratedArgs
;
32 SmallVector
<std::string
, 32> GeneratedArgsStorage
;
33 CompilerInvocation Invocation
;
35 const char *operator()(const Twine
&Arg
) {
36 return GeneratedArgsStorage
.emplace_back(Arg
.str()).c_str();
40 : Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions(),
41 new TextDiagnosticBuffer())) {
46 std::string
describeContainsN(M InnerMatcher
, unsigned N
, bool Negation
) {
47 StringRef Contains
= Negation
? "doesn't contain" : "contains";
48 StringRef Instance
= N
== 1 ? " instance " : " instances ";
49 StringRef Element
= "of element that ";
51 std::ostringstream Inner
;
52 InnerMatcher
.impl().DescribeTo(&Inner
);
54 return (Contains
+ " exactly " + Twine(N
) + Instance
+ Element
+ Inner
.str())
58 MATCHER_P2(ContainsN
, InnerMatcher
, N
,
59 describeContainsN(InnerMatcher
, N
, negation
)) {
60 auto InnerMatches
= [this](const auto &Element
) {
61 ::testing::internal::DummyMatchResultListener InnerListener
;
62 return InnerMatcher
.impl().MatchAndExplain(Element
, &InnerListener
);
65 return count_if(arg
, InnerMatches
) == N
;
68 TEST(ContainsN
, Empty
) {
69 const char *Array
[] = {""};
71 ASSERT_THAT(Array
, ContainsN(StrEq("x"), 0));
72 ASSERT_THAT(Array
, Not(ContainsN(StrEq("x"), 1)));
73 ASSERT_THAT(Array
, Not(ContainsN(StrEq("x"), 2)));
76 TEST(ContainsN
, Zero
) {
77 const char *Array
[] = {"y"};
79 ASSERT_THAT(Array
, ContainsN(StrEq("x"), 0));
80 ASSERT_THAT(Array
, Not(ContainsN(StrEq("x"), 1)));
81 ASSERT_THAT(Array
, Not(ContainsN(StrEq("x"), 2)));
84 TEST(ContainsN
, One
) {
85 const char *Array
[] = {"a", "b", "x", "z"};
87 ASSERT_THAT(Array
, Not(ContainsN(StrEq("x"), 0)));
88 ASSERT_THAT(Array
, ContainsN(StrEq("x"), 1));
89 ASSERT_THAT(Array
, Not(ContainsN(StrEq("x"), 2)));
92 TEST(ContainsN
, Two
) {
93 const char *Array
[] = {"x", "a", "b", "x"};
95 ASSERT_THAT(Array
, Not(ContainsN(StrEq("x"), 0)));
96 ASSERT_THAT(Array
, Not(ContainsN(StrEq("x"), 1)));
97 ASSERT_THAT(Array
, ContainsN(StrEq("x"), 2));
100 // Boolean option with a keypath that defaults to true.
101 // The only flag with a negative spelling can set the keypath to false.
103 TEST_F(CommandLineTest
, BoolOptionDefaultTrueSingleFlagNotPresent
) {
104 const char *Args
[] = {""};
106 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
107 ASSERT_TRUE(Invocation
.getFrontendOpts().UseTemporary
);
109 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
111 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fno-temp-file"))));
114 TEST_F(CommandLineTest
, BoolOptionDefaultTrueSingleFlagPresent
) {
115 const char *Args
[] = {"-fno-temp-file"};
117 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
118 ASSERT_FALSE(Invocation
.getFrontendOpts().UseTemporary
);
120 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
122 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-fno-temp-file")));
125 TEST_F(CommandLineTest
, BoolOptionDefaultTrueSingleFlagUnknownPresent
) {
126 const char *Args
[] = {"-ftemp-file"};
129 ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
130 ASSERT_TRUE(Invocation
.getFrontendOpts().UseTemporary
);
133 // Boolean option with a keypath that defaults to true.
134 // The flag with negative spelling can set the keypath to false.
135 // The flag with positive spelling can reset the keypath to true.
137 TEST_F(CommandLineTest
, BoolOptionDefaultTruePresentNone
) {
138 const char *Args
[] = {""};
140 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
141 ASSERT_TRUE(Invocation
.getCodeGenOpts().Autolink
);
143 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
144 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fautolink"))));
145 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fno-autolink"))));
148 TEST_F(CommandLineTest
, BoolOptionDefaultTruePresentNegChange
) {
149 const char *Args
[] = {"-fno-autolink"};
151 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
152 ASSERT_FALSE(Invocation
.getCodeGenOpts().Autolink
);
154 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
155 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-fno-autolink")));
156 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fautolink"))));
159 TEST_F(CommandLineTest
, BoolOptionDefaultTruePresentPosReset
) {
160 const char *Args
[] = {"-fautolink"};
163 ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
164 ASSERT_TRUE(Invocation
.getCodeGenOpts().Autolink
);
167 // Boolean option with a keypath that defaults to false.
168 // The flag with negative spelling can set the keypath to true.
169 // The flag with positive spelling can reset the keypath to false.
171 TEST_F(CommandLineTest
, BoolOptionDefaultFalsePresentNone
) {
172 const char *Args
[] = {""};
174 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
175 ASSERT_FALSE(Invocation
.getCodeGenOpts().NoInlineLineTables
);
177 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
178 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-ginline-line-tables"))));
179 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-gno-inline-line-tables"))));
182 TEST_F(CommandLineTest
, BoolOptionDefaultFalsePresentNegChange
) {
183 const char *Args
[] = {"-gno-inline-line-tables"};
185 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
186 ASSERT_TRUE(Invocation
.getCodeGenOpts().NoInlineLineTables
);
188 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
189 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-gno-inline-line-tables")));
190 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-ginline-line-tables"))));
193 TEST_F(CommandLineTest
, BoolOptionDefaultFalsePresentPosReset
) {
194 const char *Args
[] = {"-ginline-line-tables"};
197 ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
198 ASSERT_FALSE(Invocation
.getCodeGenOpts().NoInlineLineTables
);
201 // Boolean option with a keypath that defaults to false.
202 // The flag with positive spelling can set the keypath to true.
203 // The flag with negative spelling can reset the keypath to false.
205 TEST_F(CommandLineTest
, BoolOptionDefaultFalsePresentNoneX
) {
206 const char *Args
[] = {""};
208 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
209 ASSERT_FALSE(Invocation
.getCodeGenOpts().CodeViewGHash
);
211 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
212 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-gcodeview-ghash"))));
213 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-gno-codeview-ghash"))));
216 TEST_F(CommandLineTest
, BoolOptionDefaultFalsePresentPosChange
) {
217 const char *Args
[] = {"-gcodeview-ghash"};
219 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
220 ASSERT_TRUE(Invocation
.getCodeGenOpts().CodeViewGHash
);
222 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
223 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-gcodeview-ghash")));
224 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-gno-codeview-ghash"))));
227 TEST_F(CommandLineTest
, BoolOptionDefaultFalsePresentNegReset
) {
228 const char *Args
[] = {"-gno-codeview-ghash"};
231 ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
232 ASSERT_FALSE(Invocation
.getCodeGenOpts().CodeViewGHash
);
235 // Boolean option with a keypath that defaults to an arbitrary expression.
236 // The flag with positive spelling can set the keypath to true.
237 // The flag with negative spelling can set the keypath to false.
239 static constexpr unsigned PassManagerDefault
=
240 !static_cast<unsigned>(LLVM_ENABLE_NEW_PASS_MANAGER
);
242 static constexpr const char *PassManagerResetByFlag
=
243 LLVM_ENABLE_NEW_PASS_MANAGER
? "-fno-legacy-pass-manager"
244 : "-flegacy-pass-manager";
246 static constexpr const char *PassManagerChangedByFlag
=
247 LLVM_ENABLE_NEW_PASS_MANAGER
? "-flegacy-pass-manager"
248 : "-fno-legacy-pass-manager";
250 TEST_F(CommandLineTest
, BoolOptionDefaultArbitraryTwoFlagsPresentNone
) {
251 const char *Args
= {""};
253 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
254 ASSERT_EQ(Invocation
.getCodeGenOpts().LegacyPassManager
, PassManagerDefault
);
256 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
258 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq(PassManagerResetByFlag
))));
259 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq(PassManagerChangedByFlag
))));
262 TEST_F(CommandLineTest
, BoolOptionDefaultArbitraryTwoFlagsPresentChange
) {
263 const char *Args
[] = {PassManagerChangedByFlag
};
265 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
266 ASSERT_EQ(Invocation
.getCodeGenOpts().LegacyPassManager
, !PassManagerDefault
);
268 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
269 ASSERT_THAT(GeneratedArgs
, Contains(StrEq(PassManagerChangedByFlag
)));
270 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq(PassManagerResetByFlag
))));
273 TEST_F(CommandLineTest
, BoolOptionDefaultArbitraryTwoFlagsPresentReset
) {
274 const char *Args
[] = {PassManagerResetByFlag
};
276 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
277 ASSERT_EQ(Invocation
.getCodeGenOpts().LegacyPassManager
, PassManagerDefault
);
279 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
280 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq(PassManagerResetByFlag
))));
281 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq(PassManagerChangedByFlag
))));
284 // Boolean option that gets the CC1Option flag from a let statement (which
285 // is applied **after** the record is defined):
287 // let Flags = [CC1Option] in {
288 // defm option : BoolOption<...>;
291 TEST_F(CommandLineTest
, BoolOptionCC1ViaLetPresentNone
) {
292 const char *Args
[] = {""};
294 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
295 ASSERT_FALSE(Invocation
.getCodeGenOpts().DebugPassManager
);
297 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
299 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fdebug-pass-manager"))));
300 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fno-debug-pass-manager"))));
303 TEST_F(CommandLineTest
, BoolOptionCC1ViaLetPresentPos
) {
304 const char *Args
[] = {"-fdebug-pass-manager"};
306 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
307 ASSERT_TRUE(Invocation
.getCodeGenOpts().DebugPassManager
);
309 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
311 ASSERT_THAT(GeneratedArgs
, ContainsN(StrEq("-fdebug-pass-manager"), 1));
312 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fno-debug-pass-manager"))));
315 TEST_F(CommandLineTest
, BoolOptionCC1ViaLetPresentNeg
) {
316 const char *Args
[] = {"-fno-debug-pass-manager"};
318 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
319 ASSERT_FALSE(Invocation
.getCodeGenOpts().DebugPassManager
);
321 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
323 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fno-debug-pass-manager"))));
324 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fdebug-pass-manager"))));
327 TEST_F(CommandLineTest
, CanGenerateCC1CommandLineFlag
) {
328 const char *Args
[] = {"-fmodules-strict-context-hash"};
330 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
332 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
334 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-fmodules-strict-context-hash")));
337 TEST_F(CommandLineTest
, CanGenerateCC1CommandLineSeparate
) {
338 const char *TripleCStr
= "i686-apple-darwin9";
339 const char *Args
[] = {"-triple", TripleCStr
};
341 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
343 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
345 ASSERT_THAT(GeneratedArgs
, Contains(StrEq(TripleCStr
)));
348 TEST_F(CommandLineTest
, CanGenerateCC1CommandLineSeparateRequiredPresent
) {
349 const std::string DefaultTriple
=
350 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
351 const char *Args
[] = {"-triple", DefaultTriple
.c_str()};
353 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
355 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
357 // Triple should always be emitted even if it is the default
358 ASSERT_THAT(GeneratedArgs
, Contains(StrEq(DefaultTriple
.c_str())));
361 TEST_F(CommandLineTest
, CanGenerateCC1CommandLineSeparateRequiredAbsent
) {
362 const std::string DefaultTriple
=
363 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
364 const char *Args
[] = {""};
366 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
368 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
370 // Triple should always be emitted even if it is the default
371 ASSERT_THAT(GeneratedArgs
, Contains(StrEq(DefaultTriple
.c_str())));
374 TEST_F(CommandLineTest
, SeparateEnumNonDefault
) {
375 const char *Args
[] = {"-mrelocation-model", "static"};
377 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
378 ASSERT_EQ(Invocation
.getCodeGenOpts().RelocationModel
, Reloc::Model::Static
);
380 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
382 // Non default relocation model.
383 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-mrelocation-model")));
384 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("static")));
385 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-mrelocation-model=static"))));
388 TEST_F(CommandLineTest
, SeparateEnumDefault
) {
389 const char *Args
[] = {"-mrelocation-model", "pic"};
391 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
392 ASSERT_EQ(Invocation
.getCodeGenOpts().RelocationModel
, Reloc::Model::PIC_
);
394 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
396 // Default relocation model.
397 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-mrelocation-model"))));
398 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("pic"))));
399 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-mrelocation-model=pic"))));
402 TEST_F(CommandLineTest
, JoinedEnumNonDefault
) {
403 const char *Args
[] = {"-fobjc-dispatch-method=non-legacy"};
405 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
406 ASSERT_EQ(Invocation
.getCodeGenOpts().getObjCDispatchMethod(),
407 CodeGenOptions::NonLegacy
);
409 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
411 ASSERT_THAT(GeneratedArgs
,
412 Contains(StrEq("-fobjc-dispatch-method=non-legacy")));
413 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fobjc-dispatch-method="))));
414 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("non-legacy"))));
417 TEST_F(CommandLineTest
, JoinedEnumDefault
) {
418 const char *Args
[] = {"-fobjc-dispatch-method=legacy"};
420 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
421 ASSERT_EQ(Invocation
.getCodeGenOpts().getObjCDispatchMethod(),
422 CodeGenOptions::Legacy
);
424 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
426 ASSERT_THAT(GeneratedArgs
,
427 Not(Contains(StrEq("-fobjc-dispatch-method=legacy"))));
428 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fobjc-dispatch-method="))));
429 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("legacy"))));
432 TEST_F(CommandLineTest
, StringVectorEmpty
) {
433 const char *Args
[] = {""};
435 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
436 ASSERT_TRUE(Invocation
.getFrontendOpts().ModuleMapFiles
.empty());
438 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
440 ASSERT_THAT(GeneratedArgs
, Not(Contains(HasSubstr("-fmodule-map-file"))));
443 TEST_F(CommandLineTest
, StringVectorSingle
) {
444 const char *Args
[] = {"-fmodule-map-file=a"};
446 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
447 ASSERT_EQ(Invocation
.getFrontendOpts().ModuleMapFiles
,
448 std::vector
<std::string
>({"a"}));
450 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
452 ASSERT_THAT(GeneratedArgs
, ContainsN(StrEq("-fmodule-map-file=a"), 1));
453 ASSERT_THAT(GeneratedArgs
, ContainsN(HasSubstr("-fmodule-map-file"), 1));
456 TEST_F(CommandLineTest
, StringVectorMultiple
) {
457 const char *Args
[] = {"-fmodule-map-file=a", "-fmodule-map-file=b"};
459 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
460 ASSERT_TRUE(Invocation
.getFrontendOpts().ModuleMapFiles
==
461 std::vector
<std::string
>({"a", "b"}));
463 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
465 ASSERT_THAT(GeneratedArgs
, ContainsN(StrEq("-fmodule-map-file=a"), 1));
466 ASSERT_THAT(GeneratedArgs
, ContainsN(StrEq("-fmodule-map-file=b"), 1));
467 ASSERT_THAT(GeneratedArgs
, ContainsN(HasSubstr("-fmodule-map-file"), 2));
470 // CommaJoined option with MarshallingInfoStringVector.
472 TEST_F(CommandLineTest
, StringVectorCommaJoinedNone
) {
473 const char *Args
[] = {""};
475 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
476 ASSERT_TRUE(Invocation
.getLangOpts()->CommentOpts
.BlockCommandNames
.empty());
478 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
480 ASSERT_THAT(GeneratedArgs
,
481 Not(Contains(HasSubstr("-fcomment-block-commands"))));
484 TEST_F(CommandLineTest
, StringVectorCommaJoinedSingle
) {
485 const char *Args
[] = {"-fcomment-block-commands=x,y"};
487 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
488 ASSERT_EQ(Invocation
.getLangOpts()->CommentOpts
.BlockCommandNames
,
489 std::vector
<std::string
>({"x", "y"}));
491 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
493 ASSERT_THAT(GeneratedArgs
,
494 ContainsN(StrEq("-fcomment-block-commands=x,y"), 1));
497 TEST_F(CommandLineTest
, StringVectorCommaJoinedMultiple
) {
498 const char *Args
[] = {"-fcomment-block-commands=x,y",
499 "-fcomment-block-commands=a,b"};
501 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
502 ASSERT_EQ(Invocation
.getLangOpts()->CommentOpts
.BlockCommandNames
,
503 std::vector
<std::string
>({"x", "y", "a", "b"}));
505 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
507 ASSERT_THAT(GeneratedArgs
,
508 ContainsN(StrEq("-fcomment-block-commands=x,y,a,b"), 1));
511 // A flag that should be parsed only if a condition is met.
513 TEST_F(CommandLineTest
, ConditionalParsingIfFalseFlagNotPresent
) {
514 const char *Args
[] = {""};
516 CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
);
518 ASSERT_FALSE(Diags
->hasErrorOccurred());
519 ASSERT_FALSE(Invocation
.getLangOpts()->SYCLIsDevice
);
520 ASSERT_FALSE(Invocation
.getLangOpts()->SYCLIsHost
);
521 ASSERT_EQ(Invocation
.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None
);
523 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
525 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fsycl"))));
526 ASSERT_THAT(GeneratedArgs
, Not(Contains(HasSubstr("-sycl-std="))));
529 TEST_F(CommandLineTest
, ConditionalParsingIfFalseFlagPresent
) {
530 const char *Args
[] = {"-sycl-std=2017"};
532 CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
);
534 ASSERT_FALSE(Diags
->hasErrorOccurred());
535 ASSERT_FALSE(Invocation
.getLangOpts()->SYCLIsDevice
);
536 ASSERT_FALSE(Invocation
.getLangOpts()->SYCLIsHost
);
537 ASSERT_EQ(Invocation
.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None
);
539 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
541 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fsycl-is-device"))));
542 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fsycl-is-host"))));
543 ASSERT_THAT(GeneratedArgs
, Not(Contains(HasSubstr("-sycl-std="))));
546 TEST_F(CommandLineTest
, ConditionalParsingIfTrueFlagNotPresent
) {
547 const char *Args
[] = {"-fsycl-is-host"};
549 CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
);
551 ASSERT_FALSE(Diags
->hasErrorOccurred());
552 ASSERT_EQ(Invocation
.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None
);
554 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
556 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-fsycl-is-host")));
557 ASSERT_THAT(GeneratedArgs
, Not(Contains(HasSubstr("-sycl-std="))));
560 TEST_F(CommandLineTest
, ConditionalParsingIfTrueFlagPresent
) {
561 const char *Args
[] = {"-fsycl-is-device", "-sycl-std=2017"};
563 CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
);
565 ASSERT_FALSE(Diags
->hasErrorOccurred());
566 ASSERT_EQ(Invocation
.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_2017
);
568 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
570 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-fsycl-is-device")));
571 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-sycl-std=2017")));
574 // Wide integer option.
576 TEST_F(CommandLineTest
, WideIntegerHighValue
) {
577 const char *Args
[] = {"-fbuild-session-timestamp=1609827494445723662"};
579 CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
);
581 ASSERT_FALSE(Diags
->hasErrorOccurred());
582 ASSERT_EQ(Invocation
.getHeaderSearchOpts().BuildSessionTimestamp
,
583 1609827494445723662ull);
586 // Tree of boolean options that can be (directly or transitively) implied by
589 // * -cl-unsafe-math-optimizations
591 // * -menable-unsafe-fp-math
592 // * -freciprocal-math
594 TEST_F(CommandLineTest
, ImpliedBoolOptionsNoFlagPresent
) {
595 const char *Args
[] = {""};
597 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
598 ASSERT_FALSE(Invocation
.getLangOpts()->CLUnsafeMath
);
599 ASSERT_FALSE(Invocation
.getCodeGenOpts().LessPreciseFPMAD
);
600 ASSERT_FALSE(Invocation
.getLangOpts()->UnsafeFPMath
);
601 ASSERT_FALSE(Invocation
.getLangOpts()->AllowRecip
);
603 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
605 // Not generated - missing.
606 ASSERT_THAT(GeneratedArgs
,
607 Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
608 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-cl-mad-enable"))));
609 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
610 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-freciprocal-math"))));
613 TEST_F(CommandLineTest
, ImpliedBoolOptionsRootFlagPresent
) {
614 const char *Args
[] = {"-cl-unsafe-math-optimizations"};
616 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
617 // Explicitly provided root flag.
618 ASSERT_TRUE(Invocation
.getLangOpts()->CLUnsafeMath
);
619 // Directly implied by explicitly provided root flag.
620 ASSERT_TRUE(Invocation
.getCodeGenOpts().LessPreciseFPMAD
);
621 ASSERT_TRUE(Invocation
.getLangOpts()->UnsafeFPMath
);
622 // Transitively implied by explicitly provided root flag.
623 ASSERT_TRUE(Invocation
.getLangOpts()->AllowRecip
);
625 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
627 // Generated - explicitly provided.
628 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-cl-unsafe-math-optimizations")));
629 // Not generated - implied by the generated root flag.
630 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-cl-mad-enable"))));
631 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
632 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-freciprocal-math"))));
635 TEST_F(CommandLineTest
, ImpliedBoolOptionsAllFlagsPresent
) {
636 const char *Args
[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable",
637 "-menable-unsafe-fp-math", "-freciprocal-math"};
639 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
640 ASSERT_TRUE(Invocation
.getLangOpts()->CLUnsafeMath
);
641 ASSERT_TRUE(Invocation
.getCodeGenOpts().LessPreciseFPMAD
);
642 ASSERT_TRUE(Invocation
.getLangOpts()->UnsafeFPMath
);
643 ASSERT_TRUE(Invocation
.getLangOpts()->AllowRecip
);
645 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
647 // Generated - explicitly provided.
648 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-cl-unsafe-math-optimizations")));
649 // Not generated - implied by their generated parent.
650 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-cl-mad-enable"))));
651 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
652 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-freciprocal-math"))));
655 TEST_F(CommandLineTest
, ImpliedBoolOptionsImpliedFlagsPresent
) {
656 const char *Args
[] = {"-cl-mad-enable", "-menable-unsafe-fp-math",
657 "-freciprocal-math"};
659 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
660 ASSERT_FALSE(Invocation
.getLangOpts()->CLUnsafeMath
);
661 ASSERT_TRUE(Invocation
.getCodeGenOpts().LessPreciseFPMAD
);
662 ASSERT_TRUE(Invocation
.getLangOpts()->UnsafeFPMath
);
663 ASSERT_TRUE(Invocation
.getLangOpts()->AllowRecip
);
665 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
666 // Not generated - missing.
667 ASSERT_THAT(GeneratedArgs
,
668 Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
669 // Generated - explicitly provided.
670 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-cl-mad-enable")));
671 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-menable-unsafe-fp-math")));
672 // Not generated - implied by its generated parent.
673 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-freciprocal-math"))));
676 TEST_F(CommandLineTest
, PresentAndNotImpliedGenerated
) {
677 const char *Args
[] = {"-cl-mad-enable", "-menable-unsafe-fp-math"};
679 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
681 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
683 // Present options that were not implied are generated.
684 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-cl-mad-enable")));
685 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-menable-unsafe-fp-math")));
688 // Diagnostic option.
690 TEST_F(CommandLineTest
, DiagnosticOptionPresent
) {
691 const char *Args
[] = {"-verify=xyz"};
693 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
695 ASSERT_EQ(Invocation
.getDiagnosticOpts().VerifyPrefixes
,
696 std::vector
<std::string
>({"xyz"}));
698 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
700 ASSERT_THAT(GeneratedArgs
, ContainsN(StrEq("-verify=xyz"), 1));
703 // Option default depends on language standard.
705 TEST_F(CommandLineTest
, DigraphsImplied
) {
706 const char *Args
[] = {""};
708 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
709 ASSERT_TRUE(Invocation
.getLangOpts()->Digraphs
);
711 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
712 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fno-digraphs"))));
713 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fdigraphs"))));
716 TEST_F(CommandLineTest
, DigraphsDisabled
) {
717 const char *Args
[] = {"-fno-digraphs"};
719 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
720 ASSERT_FALSE(Invocation
.getLangOpts()->Digraphs
);
722 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
723 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-fno-digraphs")));
724 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fdigraphs"))));
727 TEST_F(CommandLineTest
, DigraphsNotImplied
) {
728 const char *Args
[] = {"-std=c89"};
730 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
731 ASSERT_FALSE(Invocation
.getLangOpts()->Digraphs
);
733 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
734 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fno-digraphs"))));
735 ASSERT_THAT(GeneratedArgs
, Not(Contains(StrEq("-fdigraphs"))));
738 TEST_F(CommandLineTest
, DigraphsEnabled
) {
739 const char *Args
[] = {"-std=c89", "-fdigraphs"};
741 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
742 ASSERT_TRUE(Invocation
.getLangOpts()->Digraphs
);
744 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
745 ASSERT_THAT(GeneratedArgs
, Contains(StrEq("-fdigraphs")));
748 struct DummyModuleFileExtension
749 : public llvm::RTTIExtends
<DummyModuleFileExtension
, ModuleFileExtension
> {
752 ModuleFileExtensionMetadata
getExtensionMetadata() const override
{
756 llvm::hash_code
hashExtension(llvm::hash_code Code
) const override
{
760 std::unique_ptr
<ModuleFileExtensionWriter
>
761 createExtensionWriter(ASTWriter
&Writer
) override
{
765 std::unique_ptr
<ModuleFileExtensionReader
>
766 createExtensionReader(const ModuleFileExtensionMetadata
&Metadata
,
767 ASTReader
&Reader
, serialization::ModuleFile
&Mod
,
768 const llvm::BitstreamCursor
&Stream
) override
{
773 char DummyModuleFileExtension::ID
= 0;
775 TEST_F(CommandLineTest
, TestModuleFileExtension
) {
776 const char *Args
[] = {"-ftest-module-file-extension=first:2:1:0:first",
777 "-ftest-module-file-extension=second:3:2:1:second"};
779 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
780 ASSERT_THAT(Invocation
.getFrontendOpts().ModuleFileExtensions
.size(), 2);
782 // Exercise the check that only serializes instances of
783 // TestModuleFileExtension by providing an instance of another
784 // ModuleFileExtension subclass.
785 Invocation
.getFrontendOpts().ModuleFileExtensions
.push_back(
786 std::make_shared
<DummyModuleFileExtension
>());
788 Invocation
.generateCC1CommandLine(GeneratedArgs
, *this);
790 ASSERT_THAT(GeneratedArgs
,
791 ContainsN(HasSubstr("-ftest-module-file-extension="), 2));
794 Contains(StrEq("-ftest-module-file-extension=first:2:1:0:first")));
797 Contains(StrEq("-ftest-module-file-extension=second:3:2:1:second")));
800 TEST_F(CommandLineTest
, RoundTrip
) {
801 // Testing one marshalled and one manually generated option from each
802 // CompilerInvocation member.
803 const char *Args
[] = {
809 "-target-sdk-version=1.2.3",
814 "-fdiagnostics-format",
816 // HeaderSearchOptions
818 "-fimplicit-module-maps",
819 // PreprocessorOptions
825 "ctu-import-threshold=42",
827 // MigratorOptions (no manually handled arguments)
828 "-no-ns-alloc-error",
830 "-debug-info-kind=limited",
832 // DependencyOutputOptions
835 // FileSystemOptions (no manually handled arguments)
836 "-working-directory",
842 // PreprocessorOutputOptions
847 ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation
, Args
, *Diags
));
849 ASSERT_TRUE(Invocation
.getLangOpts()->C17
);
850 ASSERT_EQ(Invocation
.getLangOpts()->MaxTokens
, 10u);
852 ASSERT_EQ(Invocation
.getTargetOpts().SDKVersion
, llvm::VersionTuple(1, 2, 3));
853 ASSERT_EQ(Invocation
.getTargetOpts().EABIVersion
, EABI::EABI4
);
855 ASSERT_THAT(Invocation
.getDiagnosticOpts().UndefPrefixes
,
856 Contains(StrEq("XY")));
857 ASSERT_EQ(Invocation
.getDiagnosticOpts().getFormat(),
858 TextDiagnosticFormat::Clang
);
860 ASSERT_TRUE(Invocation
.getHeaderSearchOpts().UseLibcxx
);
861 ASSERT_TRUE(Invocation
.getHeaderSearchOpts().ImplicitModuleMaps
);
863 ASSERT_THAT(Invocation
.getPreprocessorOpts().Macros
,
864 Contains(std::make_pair(std::string("XY=AB"), false)));
865 ASSERT_EQ(Invocation
.getPreprocessorOpts().ImplicitPCHInclude
, "a.pch");
867 ASSERT_EQ(Invocation
.getAnalyzerOpts()->Config
["ctu-import-threshold"], "42");
868 ASSERT_TRUE(Invocation
.getAnalyzerOpts()->UnoptimizedCFG
);
870 ASSERT_TRUE(Invocation
.getMigratorOpts().NoNSAllocReallocError
);
872 ASSERT_EQ(Invocation
.getCodeGenOpts().getDebugInfo(),
873 codegenoptions::DebugInfoKind::LimitedDebugInfo
);
874 ASSERT_TRUE(Invocation
.getCodeGenOpts().MacroDebugInfo
);
876 ASSERT_EQ(Invocation
.getDependencyOutputOpts().ShowIncludesDest
,
877 ShowIncludesDestination::Stdout
);
878 ASSERT_TRUE(Invocation
.getDependencyOutputOpts().ShowHeaderIncludes
);
880 } // anonymous namespace