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