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