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