xref: /llvm-project/clang/unittests/Frontend/CompilerInvocationTest.cpp (revision bdd10d9d249bd1c2a45e3de56a5accd97e953458)
1 //===- unittests/Frontend/CompilerInvocationTest.cpp - CI tests //---------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/Frontend/CompilerInvocation.h"
10 #include "clang/Basic/TargetOptions.h"
11 #include "clang/Frontend/CompilerInstance.h"
12 #include "clang/Frontend/TextDiagnosticBuffer.h"
13 #include "clang/Lex/PreprocessorOptions.h"
14 #include "clang/Serialization/ModuleFileExtension.h"
15 #include "llvm/Support/VirtualFileSystem.h"
16 #include "llvm/TargetParser/Host.h"
17 
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
20 
21 using namespace llvm;
22 using namespace clang;
23 
24 using ::testing::Contains;
25 using ::testing::HasSubstr;
26 using ::testing::StrEq;
27 using ::testing::StartsWith;
28 
29 namespace {
30 class CommandLineTest : public ::testing::Test {
31 public:
32   IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
33   SmallVector<const char *, 32> GeneratedArgs;
34   SmallVector<std::string, 32> GeneratedArgsStorage;
35   CompilerInvocation Invocation;
36 
37   const char *operator()(const Twine &Arg) {
38     return GeneratedArgsStorage.emplace_back(Arg.str()).c_str();
39   }
40 
41   CommandLineTest()
42       : Diags(CompilerInstance::createDiagnostics(
43             *llvm::vfs::getRealFileSystem(), new DiagnosticOptions(),
44             new TextDiagnosticBuffer())) {}
45 };
46 
47 template <typename M>
48 std::string describeContainsN(M InnerMatcher, unsigned N, bool Negation) {
49   StringRef Contains = Negation ? "doesn't contain" : "contains";
50   StringRef Instance = N == 1 ? " instance " : " instances ";
51   StringRef Element = "of element that ";
52 
53   std::ostringstream Inner;
54   InnerMatcher.impl().DescribeTo(&Inner);
55 
56   return (Contains + " exactly " + Twine(N) + Instance + Element + Inner.str())
57       .str();
58 }
59 
60 MATCHER_P2(ContainsN, InnerMatcher, N,
61            describeContainsN(InnerMatcher, N, negation)) {
62   auto InnerMatches = [this](const auto &Element) {
63     ::testing::internal::DummyMatchResultListener InnerListener;
64     return InnerMatcher.impl().MatchAndExplain(Element, &InnerListener);
65   };
66 
67   return count_if(arg, InnerMatches) == N;
68 }
69 
70 TEST(ContainsN, Empty) {
71   const char *Array[] = {""};
72 
73   ASSERT_THAT(Array, ContainsN(StrEq("x"), 0));
74   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1)));
75   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2)));
76 }
77 
78 TEST(ContainsN, Zero) {
79   const char *Array[] = {"y"};
80 
81   ASSERT_THAT(Array, ContainsN(StrEq("x"), 0));
82   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1)));
83   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2)));
84 }
85 
86 TEST(ContainsN, One) {
87   const char *Array[] = {"a", "b", "x", "z"};
88 
89   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 0)));
90   ASSERT_THAT(Array, ContainsN(StrEq("x"), 1));
91   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2)));
92 }
93 
94 TEST(ContainsN, Two) {
95   const char *Array[] = {"x", "a", "b", "x"};
96 
97   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 0)));
98   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1)));
99   ASSERT_THAT(Array, ContainsN(StrEq("x"), 2));
100 }
101 
102 // Copy constructor/assignment perform deep copy of reference-counted pointers.
103 
104 TEST(CompilerInvocationTest, DeepCopyConstructor) {
105   CompilerInvocation A;
106   A.getAnalyzerOpts().Config["Key"] = "Old";
107 
108   CompilerInvocation B(A);
109   B.getAnalyzerOpts().Config["Key"] = "New";
110 
111   ASSERT_EQ(A.getAnalyzerOpts().Config["Key"], "Old");
112 }
113 
114 TEST(CompilerInvocationTest, DeepCopyAssignment) {
115   CompilerInvocation A;
116   A.getAnalyzerOpts().Config["Key"] = "Old";
117 
118   CompilerInvocation B;
119   B = A;
120   B.getAnalyzerOpts().Config["Key"] = "New";
121 
122   ASSERT_EQ(A.getAnalyzerOpts().Config["Key"], "Old");
123 }
124 
125 TEST(CompilerInvocationTest, CopyOnWriteConstructor) {
126   CowCompilerInvocation A;
127   A.getMutFrontendOpts().OutputFile = "x.o";
128 
129   // B's FrontendOptions are initially shared with A.
130   CowCompilerInvocation B(A);
131   EXPECT_EQ(&A.getFrontendOpts(), &B.getFrontendOpts());
132 
133   // Modifying A's FrontendOptions creates new copy, does not affect other opts.
134   A.getMutFrontendOpts().OutputFile = "y.o";
135   EXPECT_NE(&A.getFrontendOpts(), &B.getFrontendOpts());
136   EXPECT_EQ(&A.getCodeGenOpts(), &B.getCodeGenOpts());
137 
138   // The new copy reflects the modification, old instance remains unchanged.
139   EXPECT_EQ(A.getFrontendOpts().OutputFile, "y.o");
140   EXPECT_EQ(B.getFrontendOpts().OutputFile, "x.o");
141 }
142 
143 TEST(CompilerInvocationTest, CopyOnWriteAssignment) {
144   CowCompilerInvocation A;
145   A.getMutFrontendOpts().OutputFile = "x.o";
146 
147   // B's FrontendOptions are initially independent of A.
148   CowCompilerInvocation B;
149   EXPECT_NE(&A.getFrontendOpts(), &B.getFrontendOpts());
150 
151   // B's FrontendOptions are shared with A after assignment.
152   B = A;
153   EXPECT_EQ(&A.getFrontendOpts(), &B.getFrontendOpts());
154 
155   // Modifying A's FrontendOptions creates new copy, does not affect other opts.
156   A.getMutFrontendOpts().OutputFile = "y.o";
157   EXPECT_NE(&A.getFrontendOpts(), &B.getFrontendOpts());
158   EXPECT_EQ(&A.getCodeGenOpts(), &B.getCodeGenOpts());
159 
160   // The new copy reflects the modification, old instance remains unchanged.
161   EXPECT_EQ(A.getFrontendOpts().OutputFile, "y.o");
162   EXPECT_EQ(B.getFrontendOpts().OutputFile, "x.o");
163 }
164 
165 // Boolean option with a keypath that defaults to true.
166 // The only flag with a negative spelling can set the keypath to false.
167 
168 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagNotPresent) {
169   const char *Args[] = {""};
170 
171   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
172   ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary);
173 
174   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
175 
176   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-temp-file"))));
177 }
178 
179 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagPresent) {
180   const char *Args[] = {"-fno-temp-file"};
181 
182   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
183   ASSERT_FALSE(Invocation.getFrontendOpts().UseTemporary);
184 
185   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
186 
187   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-temp-file")));
188 }
189 
190 TEST_F(CommandLineTest, CC1FlagPresentWhenDoingRoundTrip) {
191   const char *Args[] = {"-cc1", "-round-trip-args"};
192 
193   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
194 
195   ASSERT_THAT(std::string(Invocation.getCodeGenOpts().CmdArgs.begin(),
196                           Invocation.getCodeGenOpts().CmdArgs.end()),
197               StartsWith("-cc1"));
198 }
199 
200 TEST_F(CommandLineTest, CC1FlagPresentWhenNotDoingRoundTrip) {
201   const char *Args[] = {"-cc1", "-no-round-trip-args"};
202 
203   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
204 
205   ASSERT_THAT(std::string(Invocation.getCodeGenOpts().CmdArgs.begin(),
206                           Invocation.getCodeGenOpts().CmdArgs.end()),
207               StartsWith("-cc1"));
208 }
209 
210 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagUnknownPresent) {
211   const char *Args[] = {"-ftemp-file"};
212 
213   // Driver-only flag.
214   ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
215   ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary);
216 }
217 
218 // Boolean option with a keypath that defaults to true.
219 // The flag with negative spelling can set the keypath to false.
220 // The flag with positive spelling can reset the keypath to true.
221 
222 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNone) {
223   const char *Args[] = {""};
224 
225   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
226   ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink);
227 
228   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
229   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink"))));
230   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-autolink"))));
231 }
232 
233 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNegChange) {
234   const char *Args[] = {"-fno-autolink"};
235 
236   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
237   ASSERT_FALSE(Invocation.getCodeGenOpts().Autolink);
238 
239   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
240   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-autolink")));
241   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink"))));
242 }
243 
244 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentPosReset) {
245   const char *Args[] = {"-fautolink"};
246 
247   // Driver-only flag.
248   ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
249   ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink);
250 }
251 
252 // Boolean option with a keypath that defaults to false.
253 // The flag with negative spelling can set the keypath to true.
254 // The flag with positive spelling can reset the keypath to false.
255 
256 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNone) {
257   const char *Args[] = {""};
258 
259   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
260   ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables);
261 
262   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
263   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables"))));
264   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-inline-line-tables"))));
265 }
266 
267 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegChange) {
268   const char *Args[] = {"-gno-inline-line-tables"};
269 
270   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
271   ASSERT_TRUE(Invocation.getCodeGenOpts().NoInlineLineTables);
272 
273   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
274   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gno-inline-line-tables")));
275   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables"))));
276 }
277 
278 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosReset) {
279   const char *Args[] = {"-ginline-line-tables"};
280 
281   // Driver-only flag.
282   ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
283   ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables);
284 }
285 
286 // Boolean option with a keypath that defaults to false.
287 // The flag with positive spelling can set the keypath to true.
288 // The flag with negative spelling can reset the keypath to false.
289 
290 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNoneX) {
291   const char *Args[] = {""};
292 
293   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
294   ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash);
295 
296   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
297   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gcodeview-ghash"))));
298   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash"))));
299 }
300 
301 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosChange) {
302   const char *Args[] = {"-gcodeview-ghash"};
303 
304   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
305   ASSERT_TRUE(Invocation.getCodeGenOpts().CodeViewGHash);
306 
307   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
308   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gcodeview-ghash")));
309   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash"))));
310 }
311 
312 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegReset) {
313   const char *Args[] = {"-gno-codeview-ghash"};
314 
315   // Driver-only flag.
316   ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
317   ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash);
318 }
319 
320 // Boolean option with a keypath that defaults to an arbitrary expression.
321 // The flag with positive spelling can set the keypath to true.
322 // The flag with negative spelling can set the keypath to false.
323 
324 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentNone) {
325   const char *Args = {""};
326 
327   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
328   ASSERT_EQ(Invocation.getCodeGenOpts().ClearASTBeforeBackend, false);
329 
330   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
331 
332   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-no-clear-ast-before-backend"))));
333   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-clear-ast-before-backend"))));
334 }
335 
336 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentChange) {
337   const char *Args[] = {"-clear-ast-before-backend"};
338 
339   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
340   ASSERT_EQ(Invocation.getCodeGenOpts().ClearASTBeforeBackend, true);
341 
342   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
343   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-clear-ast-before-backend")));
344   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-no-clear-ast-before-backend"))));
345 }
346 
347 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentReset) {
348   const char *Args[] = {"-no-clear-ast-before-backend"};
349 
350   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
351   ASSERT_EQ(Invocation.getCodeGenOpts().ClearASTBeforeBackend, false);
352 
353   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
354   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-no-clear-ast-before-backend"))));
355   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-clear-ast-before-backend"))));
356 }
357 
358 // Boolean option that gets the CC1Option flag from a let statement (which
359 // is applied **after** the record is defined):
360 //
361 //   let Flags = [CC1Option] in {
362 //     defm option : BoolOption<...>;
363 //   }
364 
365 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentNone) {
366   const char *Args[] = {""};
367 
368   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
369   ASSERT_FALSE(Invocation.getCodeGenOpts().DebugPassManager);
370 
371   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
372 
373   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdebug-pass-manager"))));
374   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager"))));
375 }
376 
377 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentPos) {
378   const char *Args[] = {"-fdebug-pass-manager"};
379 
380   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
381   ASSERT_TRUE(Invocation.getCodeGenOpts().DebugPassManager);
382 
383   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
384 
385   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fdebug-pass-manager"), 1));
386   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager"))));
387 }
388 
389 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentNeg) {
390   const char *Args[] = {"-fno-debug-pass-manager"};
391 
392   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
393   ASSERT_FALSE(Invocation.getCodeGenOpts().DebugPassManager);
394 
395   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
396 
397   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager"))));
398   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdebug-pass-manager"))));
399 }
400 
401 TEST_F(CommandLineTest, CanGenerateCC1CommandLineFlag) {
402   const char *Args[] = {"-fmodules-strict-context-hash"};
403 
404   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
405 
406   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
407 
408   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash")));
409 }
410 
411 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparate) {
412   const char *TripleCStr = "i686-apple-darwin9";
413   const char *Args[] = {"-triple", TripleCStr};
414 
415   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
416 
417   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
418 
419   ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr)));
420 }
421 
422 TEST_F(CommandLineTest,  CanGenerateCC1CommandLineSeparateRequiredPresent) {
423   const std::string DefaultTriple =
424       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
425   const char *Args[] = {"-triple", DefaultTriple.c_str()};
426 
427   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
428 
429   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
430 
431   // Triple should always be emitted even if it is the default
432   ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str())));
433 }
434 
435 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredAbsent) {
436   const std::string DefaultTriple =
437       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
438   const char *Args[] = {""};
439 
440   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
441 
442   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
443 
444   // Triple should always be emitted even if it is the default
445   ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str())));
446 }
447 
448 TEST_F(CommandLineTest, SeparateEnumNonDefault) {
449   const char *Args[] = {"-mrelocation-model", "static"};
450 
451   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
452   ASSERT_EQ(Invocation.getCodeGenOpts().RelocationModel, Reloc::Model::Static);
453 
454   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
455 
456   // Non default relocation model.
457   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-mrelocation-model")));
458   ASSERT_THAT(GeneratedArgs, Contains(StrEq("static")));
459   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model=static"))));
460 }
461 
462 TEST_F(CommandLineTest, SeparateEnumDefault) {
463   const char *Args[] = {"-mrelocation-model", "pic"};
464 
465   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
466   ASSERT_EQ(Invocation.getCodeGenOpts().RelocationModel, Reloc::Model::PIC_);
467 
468   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
469 
470   // Default relocation model.
471   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model"))));
472   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("pic"))));
473   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model=pic"))));
474 }
475 
476 TEST_F(CommandLineTest, JoinedEnumNonDefault) {
477   const char *Args[] = {"-fobjc-dispatch-method=non-legacy"};
478 
479   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
480   ASSERT_EQ(Invocation.getCodeGenOpts().getObjCDispatchMethod(),
481             CodeGenOptions::NonLegacy);
482 
483   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
484 
485   ASSERT_THAT(GeneratedArgs,
486               Contains(StrEq("-fobjc-dispatch-method=non-legacy")));
487   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fobjc-dispatch-method="))));
488   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("non-legacy"))));
489 }
490 
491 TEST_F(CommandLineTest, JoinedEnumDefault) {
492   const char *Args[] = {"-fobjc-dispatch-method=legacy"};
493 
494   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
495   ASSERT_EQ(Invocation.getCodeGenOpts().getObjCDispatchMethod(),
496             CodeGenOptions::Legacy);
497 
498   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
499 
500   ASSERT_THAT(GeneratedArgs,
501               Not(Contains(StrEq("-fobjc-dispatch-method=legacy"))));
502   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fobjc-dispatch-method="))));
503   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("legacy"))));
504 }
505 
506 TEST_F(CommandLineTest, StringVectorEmpty) {
507   const char *Args[] = {""};
508 
509   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
510   ASSERT_TRUE(Invocation.getFrontendOpts().ModuleMapFiles.empty());
511 
512   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
513 
514   ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-fmodule-map-file"))));
515 }
516 
517 TEST_F(CommandLineTest, StringVectorSingle) {
518   const char *Args[] = {"-fmodule-map-file=a"};
519 
520   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
521   ASSERT_EQ(Invocation.getFrontendOpts().ModuleMapFiles,
522             std::vector<std::string>({"a"}));
523 
524   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
525 
526   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=a"), 1));
527   ASSERT_THAT(GeneratedArgs, ContainsN(HasSubstr("-fmodule-map-file"), 1));
528 }
529 
530 TEST_F(CommandLineTest, StringVectorMultiple) {
531   const char *Args[] = {"-fmodule-map-file=a", "-fmodule-map-file=b"};
532 
533   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
534   ASSERT_TRUE(Invocation.getFrontendOpts().ModuleMapFiles ==
535               std::vector<std::string>({"a", "b"}));
536 
537   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
538 
539   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=a"), 1));
540   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=b"), 1));
541   ASSERT_THAT(GeneratedArgs, ContainsN(HasSubstr("-fmodule-map-file"), 2));
542 }
543 
544 // CommaJoined option with MarshallingInfoStringVector.
545 
546 TEST_F(CommandLineTest, StringVectorCommaJoinedNone) {
547   const char *Args[] = {""};
548 
549   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
550   ASSERT_TRUE(Invocation.getLangOpts().CommentOpts.BlockCommandNames.empty());
551 
552   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
553 
554   ASSERT_THAT(GeneratedArgs,
555               Not(Contains(HasSubstr("-fcomment-block-commands"))));
556 }
557 
558 TEST_F(CommandLineTest, StringVectorCommaJoinedSingle) {
559   const char *Args[] = {"-fcomment-block-commands=x,y"};
560 
561   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
562   ASSERT_EQ(Invocation.getLangOpts().CommentOpts.BlockCommandNames,
563             std::vector<std::string>({"x", "y"}));
564 
565   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
566 
567   ASSERT_THAT(GeneratedArgs,
568               ContainsN(StrEq("-fcomment-block-commands=x,y"), 1));
569 }
570 
571 TEST_F(CommandLineTest, StringVectorCommaJoinedMultiple) {
572   const char *Args[] = {"-fcomment-block-commands=x,y",
573                         "-fcomment-block-commands=a,b"};
574 
575   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
576   ASSERT_EQ(Invocation.getLangOpts().CommentOpts.BlockCommandNames,
577             std::vector<std::string>({"x", "y", "a", "b"}));
578 
579   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
580 
581   ASSERT_THAT(GeneratedArgs,
582               ContainsN(StrEq("-fcomment-block-commands=x,y,a,b"), 1));
583 }
584 
585 // A flag that should be parsed only if a condition is met.
586 
587 TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagNotPresent) {
588   const char *Args[] = {""};
589 
590   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
591 
592   ASSERT_FALSE(Diags->hasErrorOccurred());
593   ASSERT_FALSE(Invocation.getLangOpts().SYCLIsDevice);
594   ASSERT_FALSE(Invocation.getLangOpts().SYCLIsHost);
595   ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_None);
596 
597   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
598 
599   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl"))));
600   ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std="))));
601 }
602 
603 TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagPresent) {
604   const char *Args[] = {"-sycl-std=2017"};
605 
606   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
607 
608   ASSERT_FALSE(Diags->hasErrorOccurred());
609   ASSERT_FALSE(Invocation.getLangOpts().SYCLIsDevice);
610   ASSERT_FALSE(Invocation.getLangOpts().SYCLIsHost);
611   ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_None);
612 
613   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
614 
615   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-device"))));
616   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-host"))));
617   ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std="))));
618 }
619 
620 TEST_F(CommandLineTest, ConditionalParsingIfNonsenseSyclStdArg) {
621   const char *Args[] = {"-fsycl-is-device", "-sycl-std=garbage"};
622 
623   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
624 
625   ASSERT_TRUE(Diags->hasErrorOccurred());
626   ASSERT_TRUE(Invocation.getLangOpts().SYCLIsDevice);
627   ASSERT_FALSE(Invocation.getLangOpts().SYCLIsHost);
628   ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_None);
629 
630   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
631 
632   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device")));
633   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-host"))));
634   ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std="))));
635 }
636 
637 TEST_F(CommandLineTest, ConditionalParsingIfOddSyclStdArg1) {
638   const char *Args[] = {"-fsycl-is-device", "-sycl-std=121"};
639 
640   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
641 
642   ASSERT_FALSE(Diags->hasErrorOccurred());
643   ASSERT_TRUE(Invocation.getLangOpts().SYCLIsDevice);
644   ASSERT_FALSE(Invocation.getLangOpts().SYCLIsHost);
645   ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_2017);
646 
647   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
648 
649   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device")));
650   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-host"))));
651   ASSERT_THAT(GeneratedArgs, Contains(HasSubstr("-sycl-std=2017")));
652 }
653 
654 TEST_F(CommandLineTest, ConditionalParsingIfOddSyclStdArg2) {
655   const char *Args[] = {"-fsycl-is-device", "-sycl-std=1.2.1"};
656 
657   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
658 
659   ASSERT_FALSE(Diags->hasErrorOccurred());
660   ASSERT_TRUE(Invocation.getLangOpts().SYCLIsDevice);
661   ASSERT_FALSE(Invocation.getLangOpts().SYCLIsHost);
662   ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_2017);
663 
664   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
665 
666   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device")));
667   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-host"))));
668   ASSERT_THAT(GeneratedArgs, Contains(HasSubstr("-sycl-std=2017")));
669 }
670 
671 TEST_F(CommandLineTest, ConditionalParsingIfOddSyclStdArg3) {
672   const char *Args[] = {"-fsycl-is-device", "-sycl-std=sycl-1.2.1"};
673 
674   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
675 
676   ASSERT_FALSE(Diags->hasErrorOccurred());
677   ASSERT_TRUE(Invocation.getLangOpts().SYCLIsDevice);
678   ASSERT_FALSE(Invocation.getLangOpts().SYCLIsHost);
679   ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_2017);
680 
681   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
682 
683   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device")));
684   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl-is-host"))));
685   ASSERT_THAT(GeneratedArgs, Contains(HasSubstr("-sycl-std=2017")));
686 }
687 
688 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagNotPresentHost) {
689   const char *Args[] = {"-fsycl-is-host"};
690 
691   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
692 
693   ASSERT_FALSE(Diags->hasErrorOccurred());
694   ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(),
695             LangOptions::SYCL_Default);
696 
697   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
698 
699   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-host")));
700   ASSERT_THAT(GeneratedArgs, Contains(HasSubstr("-sycl-std=")));
701 }
702 
703 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagNotPresentDevice) {
704   const char *Args[] = {"-fsycl-is-device"};
705 
706   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
707 
708   ASSERT_FALSE(Diags->hasErrorOccurred());
709   ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(),
710             LangOptions::SYCL_Default);
711 
712   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
713 
714   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device")));
715   ASSERT_THAT(GeneratedArgs, Contains(HasSubstr("-sycl-std=")));
716 }
717 
718 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagPresent) {
719   const char *Args[] = {"-fsycl-is-device", "-sycl-std=2017"};
720 
721   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
722 
723   ASSERT_FALSE(Diags->hasErrorOccurred());
724   ASSERT_EQ(Invocation.getLangOpts().getSYCLVersion(), LangOptions::SYCL_2017);
725 
726   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
727 
728   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl-is-device")));
729   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-sycl-std=2017")));
730 }
731 
732 // Wide integer option.
733 
734 TEST_F(CommandLineTest, WideIntegerHighValue) {
735   const char *Args[] = {"-fbuild-session-timestamp=1609827494445723662"};
736 
737   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
738 
739   ASSERT_FALSE(Diags->hasErrorOccurred());
740   ASSERT_EQ(Invocation.getHeaderSearchOpts().BuildSessionTimestamp,
741             1609827494445723662ull);
742 }
743 
744 // Tree of boolean options that can be (directly or transitively) implied by
745 // their parent:
746 //
747 //   * -cl-unsafe-math-optimizations
748 //     * -cl-mad-enable
749 //     * -funsafe-math-optimizations
750 //       * -freciprocal-math
751 
752 TEST_F(CommandLineTest, ImpliedBoolOptionsNoFlagPresent) {
753   const char *Args[] = {""};
754 
755   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
756   ASSERT_FALSE(Invocation.getLangOpts().CLUnsafeMath);
757   ASSERT_FALSE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
758   ASSERT_FALSE(Invocation.getLangOpts().UnsafeFPMath);
759   ASSERT_FALSE(Invocation.getLangOpts().AllowRecip);
760 
761   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
762 
763   // Not generated - missing.
764   ASSERT_THAT(GeneratedArgs,
765               Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
766   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
767   ASSERT_THAT(GeneratedArgs,
768               Not(Contains(StrEq("-funsafe-math-optimizations"))));
769   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
770 }
771 
772 TEST_F(CommandLineTest, ImpliedBoolOptionsRootFlagPresent) {
773   const char *Args[] = {"-cl-unsafe-math-optimizations"};
774 
775   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
776   // Explicitly provided root flag.
777   ASSERT_TRUE(Invocation.getLangOpts().CLUnsafeMath);
778   // Directly implied by explicitly provided root flag.
779   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
780   ASSERT_TRUE(Invocation.getLangOpts().UnsafeFPMath);
781   // Transitively implied by explicitly provided root flag.
782   ASSERT_TRUE(Invocation.getLangOpts().AllowRecip);
783 
784   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
785 
786   // Generated - explicitly provided.
787   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations")));
788   // Not generated - implied by the generated root flag.
789   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
790   ASSERT_THAT(GeneratedArgs,
791               Not(Contains(StrEq("-funsafe-math-optimizations"))));
792   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
793 }
794 
795 TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) {
796   const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable",
797                         "-funsafe-math-optimizations", "-freciprocal-math"};
798 
799   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
800   ASSERT_TRUE(Invocation.getLangOpts().CLUnsafeMath);
801   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
802   ASSERT_TRUE(Invocation.getLangOpts().UnsafeFPMath);
803   ASSERT_TRUE(Invocation.getLangOpts().AllowRecip);
804 
805   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
806 
807   // Generated - explicitly provided.
808   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations")));
809   // Not generated - implied by their generated parent.
810   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
811   ASSERT_THAT(GeneratedArgs,
812               Not(Contains(StrEq("-funsafe-math-optimizations"))));
813   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
814 }
815 
816 TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) {
817   const char *Args[] = {"-cl-mad-enable", "-funsafe-math-optimizations",
818                         "-freciprocal-math"};
819 
820   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
821   ASSERT_FALSE(Invocation.getLangOpts().CLUnsafeMath);
822   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
823   ASSERT_TRUE(Invocation.getLangOpts().UnsafeFPMath);
824   ASSERT_TRUE(Invocation.getLangOpts().AllowRecip);
825 
826   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
827   // Not generated - missing.
828   ASSERT_THAT(GeneratedArgs,
829               Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
830   // Generated - explicitly provided.
831   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable")));
832   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-funsafe-math-optimizations")));
833   // Not generated - implied by its generated parent.
834   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
835 }
836 
837 TEST_F(CommandLineTest, PresentAndNotImpliedGenerated) {
838   const char *Args[] = {"-cl-mad-enable", "-funsafe-math-optimizations"};
839 
840   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
841 
842   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
843 
844   // Present options that were not implied are generated.
845   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable")));
846   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-funsafe-math-optimizations")));
847 }
848 
849 // Diagnostic option.
850 
851 TEST_F(CommandLineTest, DiagnosticOptionPresent) {
852   const char *Args[] = {"-verify=xyz"};
853 
854   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
855 
856   ASSERT_EQ(Invocation.getDiagnosticOpts().VerifyPrefixes,
857             std::vector<std::string>({"xyz"}));
858 
859   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
860 
861   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-verify=xyz"), 1));
862 }
863 
864 // Option default depends on language standard.
865 
866 TEST_F(CommandLineTest, DigraphsImplied) {
867   const char *Args[] = {""};
868 
869   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
870   ASSERT_TRUE(Invocation.getLangOpts().Digraphs);
871 
872   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
873   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-digraphs"))));
874   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs"))));
875 }
876 
877 TEST_F(CommandLineTest, DigraphsDisabled) {
878   const char *Args[] = {"-fno-digraphs"};
879 
880   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
881   ASSERT_FALSE(Invocation.getLangOpts().Digraphs);
882 
883   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
884   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-digraphs")));
885   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs"))));
886 }
887 
888 TEST_F(CommandLineTest, DigraphsNotImplied) {
889   const char *Args[] = {"-std=c89"};
890 
891   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
892   ASSERT_FALSE(Invocation.getLangOpts().Digraphs);
893 
894   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
895   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-digraphs"))));
896   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs"))));
897 }
898 
899 TEST_F(CommandLineTest, DigraphsEnabled) {
900   const char *Args[] = {"-std=c89", "-fdigraphs"};
901 
902   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
903   ASSERT_TRUE(Invocation.getLangOpts().Digraphs);
904 
905   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
906   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fdigraphs")));
907 }
908 
909 struct DummyModuleFileExtension
910     : public llvm::RTTIExtends<DummyModuleFileExtension, ModuleFileExtension> {
911   static char ID;
912 
913   ModuleFileExtensionMetadata getExtensionMetadata() const override {
914     return {};
915   };
916 
917   void hashExtension(ExtensionHashBuilder &HBuilder) const override {}
918 
919   std::unique_ptr<ModuleFileExtensionWriter>
920   createExtensionWriter(ASTWriter &Writer) override {
921     return {};
922   }
923 
924   std::unique_ptr<ModuleFileExtensionReader>
925   createExtensionReader(const ModuleFileExtensionMetadata &Metadata,
926                         ASTReader &Reader, serialization::ModuleFile &Mod,
927                         const llvm::BitstreamCursor &Stream) override {
928     return {};
929   }
930 };
931 
932 char DummyModuleFileExtension::ID = 0;
933 
934 TEST_F(CommandLineTest, TestModuleFileExtension) {
935   const char *Args[] = {"-ftest-module-file-extension=first:2:1:0:first",
936                         "-ftest-module-file-extension=second:3:2:1:second"};
937 
938   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
939   ASSERT_THAT(Invocation.getFrontendOpts().ModuleFileExtensions.size(), 2);
940 
941   // Exercise the check that only serializes instances of
942   // TestModuleFileExtension by providing an instance of another
943   // ModuleFileExtension subclass.
944   Invocation.getFrontendOpts().ModuleFileExtensions.push_back(
945       std::make_shared<DummyModuleFileExtension>());
946 
947   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
948 
949   ASSERT_THAT(GeneratedArgs,
950               ContainsN(HasSubstr("-ftest-module-file-extension="), 2));
951   ASSERT_THAT(
952       GeneratedArgs,
953       Contains(StrEq("-ftest-module-file-extension=first:2:1:0:first")));
954   ASSERT_THAT(
955       GeneratedArgs,
956       Contains(StrEq("-ftest-module-file-extension=second:3:2:1:second")));
957 }
958 
959 TEST_F(CommandLineTest, RoundTrip) {
960   // Testing one marshalled and one manually generated option from each
961   // CompilerInvocation member.
962   const char *Args[] = {
963       "-round-trip-args",
964       // LanguageOptions
965       "-std=c17",
966       "-fmax-tokens=10",
967       // TargetOptions
968       "-target-sdk-version=1.2.3",
969       "-meabi",
970       "4",
971       // DiagnosticOptions
972       "-Wundef-prefix=XY",
973       "-fdiagnostics-format",
974       "clang",
975       // HeaderSearchOptions
976       "-stdlib=libc++",
977       "-fimplicit-module-maps",
978       // PreprocessorOptions
979       "-DXY=AB",
980       "-include-pch",
981       "a.pch",
982       // AnalyzerOptions
983       "-analyzer-config",
984       "ctu-import-threshold=42",
985       "-unoptimized-cfg",
986       // MigratorOptions (no manually handled arguments)
987       "-no-ns-alloc-error",
988       // CodeGenOptions
989       "-debug-info-kind=limited",
990       "-debug-info-macro",
991       // DependencyOutputOptions
992       "--show-includes",
993       "-H",
994       // FileSystemOptions (no manually handled arguments)
995       "-working-directory",
996       "folder",
997       // FrontendOptions
998       "-load",
999       "plugin",
1000       "-ast-merge",
1001       // PreprocessorOutputOptions
1002       "-dD",
1003       "-CC",
1004   };
1005 
1006   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
1007 
1008   ASSERT_TRUE(Invocation.getLangOpts().C17);
1009   ASSERT_EQ(Invocation.getLangOpts().MaxTokens, 10u);
1010 
1011   ASSERT_EQ(Invocation.getTargetOpts().SDKVersion, llvm::VersionTuple(1, 2, 3));
1012   ASSERT_EQ(Invocation.getTargetOpts().EABIVersion, EABI::EABI4);
1013 
1014   ASSERT_THAT(Invocation.getDiagnosticOpts().UndefPrefixes,
1015               Contains(StrEq("XY")));
1016   ASSERT_EQ(Invocation.getDiagnosticOpts().getFormat(),
1017             TextDiagnosticFormat::Clang);
1018 
1019   ASSERT_TRUE(Invocation.getHeaderSearchOpts().UseLibcxx);
1020   ASSERT_TRUE(Invocation.getHeaderSearchOpts().ImplicitModuleMaps);
1021 
1022   ASSERT_THAT(Invocation.getPreprocessorOpts().Macros,
1023               Contains(std::make_pair(std::string("XY=AB"), false)));
1024   ASSERT_EQ(Invocation.getPreprocessorOpts().ImplicitPCHInclude, "a.pch");
1025 
1026   ASSERT_EQ(Invocation.getAnalyzerOpts().Config["ctu-import-threshold"], "42");
1027   ASSERT_TRUE(Invocation.getAnalyzerOpts().UnoptimizedCFG);
1028 
1029   ASSERT_TRUE(Invocation.getMigratorOpts().NoNSAllocReallocError);
1030 
1031   ASSERT_EQ(Invocation.getCodeGenOpts().getDebugInfo(),
1032             codegenoptions::DebugInfoKind::LimitedDebugInfo);
1033   ASSERT_TRUE(Invocation.getCodeGenOpts().MacroDebugInfo);
1034 
1035   ASSERT_EQ(Invocation.getDependencyOutputOpts().ShowIncludesDest,
1036             ShowIncludesDestination::Stdout);
1037   ASSERT_TRUE(Invocation.getDependencyOutputOpts().ShowHeaderIncludes);
1038 }
1039 
1040 TEST_F(CommandLineTest, PluginArgsRoundTripDeterminism) {
1041   const char *Args[] = {
1042       "-plugin-arg-blink-gc-plugin", "no-members-in-stack-allocated",
1043       "-plugin-arg-find-bad-constructs", "checked-ptr-as-trivial-member",
1044       "-plugin-arg-find-bad-constructs", "check-ipc",
1045       // Enable round-trip to ensure '-plugin-arg' generation is deterministic.
1046       "-round-trip-args"};
1047 
1048   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
1049 }
1050 
1051 TEST_F(CommandLineTest, WarningSuppressionMappings) {
1052   const char *Args[] = {"--warning-suppression-mappings=foo.txt"};
1053 
1054   EXPECT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
1055   EXPECT_EQ(Invocation.getDiagnosticOpts().DiagnosticSuppressionMappingsFile,
1056             "foo.txt");
1057 
1058   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
1059   EXPECT_THAT(GeneratedArgs, Contains(StrEq(Args[0])));
1060 }
1061 } // anonymous namespace
1062