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