xref: /llvm-project/clang/unittests/Frontend/CompilerInvocationTest.cpp (revision a828fb463ed9f6085849bb3a4f225b3c84e7cf29)
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/Frontend/CompilerInstance.h"
11 #include "clang/Frontend/TextDiagnosticBuffer.h"
12 #include "llvm/Support/Host.h"
13 
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16 
17 using namespace llvm;
18 using namespace clang;
19 
20 using ::testing::Contains;
21 using ::testing::HasSubstr;
22 using ::testing::StrEq;
23 
24 namespace {
25 class CommandLineTest : public ::testing::Test {
26 public:
27   IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
28   SmallVector<const char *, 32> GeneratedArgs;
29   SmallVector<std::string, 32> GeneratedArgsStorage;
30   CompilerInvocation Invocation;
31 
32   const char *operator()(const Twine &Arg) {
33     return GeneratedArgsStorage.emplace_back(Arg.str()).c_str();
34   }
35 
36   CommandLineTest()
37       : Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions(),
38                                                   new TextDiagnosticBuffer())) {
39   }
40 };
41 
42 template <typename M>
43 std::string describeContainsN(M InnerMatcher, unsigned N, bool Negation) {
44   StringRef Contains = Negation ? "doesn't contain" : "contains";
45   StringRef Instance = N == 1 ? " instance " : " instances ";
46   StringRef Element = "of element that ";
47 
48   std::ostringstream Inner;
49   InnerMatcher.impl().DescribeTo(&Inner);
50 
51   return (Contains + " exactly " + Twine(N) + Instance + Element + Inner.str())
52       .str();
53 }
54 
55 MATCHER_P2(ContainsN, InnerMatcher, N,
56            describeContainsN(InnerMatcher, N, negation)) {
57   auto InnerMatches = [this](const auto &Element) {
58     ::testing::internal::DummyMatchResultListener InnerListener;
59     return InnerMatcher.impl().MatchAndExplain(Element, &InnerListener);
60   };
61 
62   return count_if(arg, InnerMatches) == N;
63 }
64 
65 TEST(ContainsN, Empty) {
66   const char *Array[] = {""};
67 
68   ASSERT_THAT(Array, ContainsN(StrEq("x"), 0));
69   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1)));
70   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2)));
71 }
72 
73 TEST(ContainsN, Zero) {
74   const char *Array[] = {"y"};
75 
76   ASSERT_THAT(Array, ContainsN(StrEq("x"), 0));
77   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1)));
78   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2)));
79 }
80 
81 TEST(ContainsN, One) {
82   const char *Array[] = {"a", "b", "x", "z"};
83 
84   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 0)));
85   ASSERT_THAT(Array, ContainsN(StrEq("x"), 1));
86   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2)));
87 }
88 
89 TEST(ContainsN, Two) {
90   const char *Array[] = {"x", "a", "b", "x"};
91 
92   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 0)));
93   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1)));
94   ASSERT_THAT(Array, ContainsN(StrEq("x"), 2));
95 }
96 
97 // Boolean option with a keypath that defaults to true.
98 // The only flag with a negative spelling can set the keypath to false.
99 
100 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagNotPresent) {
101   const char *Args[] = {""};
102 
103   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
104 
105   ASSERT_FALSE(Diags->hasErrorOccurred());
106   ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary);
107 
108   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
109 
110   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-temp-file"))));
111 }
112 
113 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagPresent) {
114   const char *Args[] = {"-fno-temp-file"};
115 
116   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
117 
118   ASSERT_FALSE(Diags->hasErrorOccurred());
119   ASSERT_FALSE(Invocation.getFrontendOpts().UseTemporary);
120 
121   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
122 
123   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-temp-file")));
124 }
125 
126 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagUnknownPresent) {
127   const char *Args[] = {"-ftemp-file"};
128 
129   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
130 
131   // Driver-only flag.
132   ASSERT_TRUE(Diags->hasErrorOccurred());
133   ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary);
134 }
135 
136 // Boolean option with a keypath that defaults to true.
137 // The flag with negative spelling can set the keypath to false.
138 // The flag with positive spelling can reset the keypath to true.
139 
140 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNone) {
141   const char *Args[] = {""};
142 
143   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
144   ASSERT_FALSE(Diags->hasErrorOccurred());
145   ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink);
146 
147   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
148   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink"))));
149   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-autolink"))));
150 }
151 
152 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNegChange) {
153   const char *Args[] = {"-fno-autolink"};
154 
155   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
156   ASSERT_FALSE(Diags->hasErrorOccurred());
157   ASSERT_FALSE(Invocation.getCodeGenOpts().Autolink);
158 
159   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
160   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-autolink")));
161   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink"))));
162 }
163 
164 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentPosReset) {
165   const char *Args[] = {"-fautolink"};
166 
167   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
168   ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag.
169   ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink);
170 }
171 
172 // Boolean option with a keypath that defaults to false.
173 // The flag with negative spelling can set the keypath to true.
174 // The flag with positive spelling can reset the keypath to false.
175 
176 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNone) {
177   const char *Args[] = {""};
178 
179   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
180   ASSERT_FALSE(Diags->hasErrorOccurred());
181   ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables);
182 
183   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
184   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables"))));
185   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-inline-line-tables"))));
186 }
187 
188 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegChange) {
189   const char *Args[] = {"-gno-inline-line-tables"};
190 
191   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
192   ASSERT_FALSE(Diags->hasErrorOccurred());
193   ASSERT_TRUE(Invocation.getCodeGenOpts().NoInlineLineTables);
194 
195   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
196   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gno-inline-line-tables")));
197   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables"))));
198 }
199 
200 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosReset) {
201   const char *Args[] = {"-ginline-line-tables"};
202 
203   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
204   ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag.
205   ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables);
206 }
207 
208 // Boolean option with a keypath that defaults to false.
209 // The flag with positive spelling can set the keypath to true.
210 // The flag with negative spelling can reset the keypath to false.
211 
212 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNoneX) {
213   const char *Args[] = {""};
214 
215   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
216   ASSERT_FALSE(Diags->hasErrorOccurred());
217   ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash);
218 
219   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
220   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gcodeview-ghash"))));
221   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash"))));
222 }
223 
224 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosChange) {
225   const char *Args[] = {"-gcodeview-ghash"};
226 
227   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
228   ASSERT_FALSE(Diags->hasErrorOccurred());
229   ASSERT_TRUE(Invocation.getCodeGenOpts().CodeViewGHash);
230 
231   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
232   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gcodeview-ghash")));
233   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash"))));
234 }
235 
236 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegReset) {
237   const char *Args[] = {"-gno-codeview-ghash"};
238 
239   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
240   ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag.
241   ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash);
242 }
243 
244 // Boolean option with a keypath that defaults to an arbitrary expression.
245 // The flag with positive spelling can set the keypath to true.
246 // The flag with negative spelling can set the keypath to false.
247 
248 static constexpr unsigned PassManagerDefault =
249     !static_cast<unsigned>(LLVM_ENABLE_NEW_PASS_MANAGER);
250 
251 static constexpr const char *PassManagerResetByFlag =
252     LLVM_ENABLE_NEW_PASS_MANAGER ? "-fno-legacy-pass-manager"
253                                  : "-flegacy-pass-manager";
254 
255 static constexpr const char *PassManagerChangedByFlag =
256     LLVM_ENABLE_NEW_PASS_MANAGER ? "-flegacy-pass-manager"
257                                  : "-fno-legacy-pass-manager";
258 
259 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentNone) {
260   const char *Args = {""};
261 
262   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
263 
264   ASSERT_FALSE(Diags->hasErrorOccurred());
265   ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault);
266 
267   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
268 
269   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag))));
270   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag))));
271 }
272 
273 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentChange) {
274   const char *Args[] = {PassManagerChangedByFlag};
275 
276   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
277   ASSERT_FALSE(Diags->hasErrorOccurred());
278   ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, !PassManagerDefault);
279 
280   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
281   ASSERT_THAT(GeneratedArgs, Contains(StrEq(PassManagerChangedByFlag)));
282   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag))));
283 }
284 
285 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentReset) {
286   const char *Args[] = {PassManagerResetByFlag};
287 
288   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
289   ASSERT_FALSE(Diags->hasErrorOccurred());
290   ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault);
291 
292   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
293   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag))));
294   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag))));
295 }
296 
297 // Boolean option that gets the CC1Option flag from a let statement (which
298 // is applied **after** the record is defined):
299 //
300 //   let Flags = [CC1Option] in {
301 //     defm option : BoolOption<...>;
302 //   }
303 
304 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentNone) {
305   const char *Args[] = {""};
306 
307   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
308 
309   ASSERT_FALSE(Diags->hasErrorOccurred());
310   ASSERT_FALSE(Invocation.getCodeGenOpts().DebugPassManager);
311 
312   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
313 
314   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdebug-pass-manager"))));
315   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager"))));
316 }
317 
318 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentPos) {
319   const char *Args[] = {"-fdebug-pass-manager"};
320 
321   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
322 
323   ASSERT_FALSE(Diags->hasErrorOccurred());
324   ASSERT_TRUE(Invocation.getCodeGenOpts().DebugPassManager);
325 
326   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
327 
328   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fdebug-pass-manager"), 1));
329   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager"))));
330 }
331 
332 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentNeg) {
333   const char *Args[] = {"-fno-debug-pass-manager"};
334 
335   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
336 
337   ASSERT_FALSE(Diags->hasErrorOccurred());
338   ASSERT_FALSE(Invocation.getCodeGenOpts().DebugPassManager);
339 
340   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
341 
342   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager"))));
343   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdebug-pass-manager"))));
344 }
345 
346 TEST_F(CommandLineTest, CanGenerateCC1CommandLineFlag) {
347   const char *Args[] = {"-fmodules-strict-context-hash"};
348 
349   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
350 
351   ASSERT_FALSE(Diags->hasErrorOccurred());
352 
353   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
354 
355   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash")));
356 }
357 
358 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparate) {
359   const char *TripleCStr = "i686-apple-darwin9";
360   const char *Args[] = {"-triple", TripleCStr};
361 
362   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
363 
364   ASSERT_FALSE(Diags->hasErrorOccurred());
365 
366   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
367 
368   ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr)));
369 }
370 
371 TEST_F(CommandLineTest,  CanGenerateCC1CommandLineSeparateRequiredPresent) {
372   const std::string DefaultTriple =
373       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
374   const char *Args[] = {"-triple", DefaultTriple.c_str()};
375 
376   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
377 
378   ASSERT_FALSE(Diags->hasErrorOccurred());
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, CanGenerateCC1CommandLineSeparateRequiredAbsent) {
387   const std::string DefaultTriple =
388       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
389   const char *Args[] = {""};
390 
391   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
392 
393   ASSERT_FALSE(Diags->hasErrorOccurred());
394 
395   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
396 
397   // Triple should always be emitted even if it is the default
398   ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str())));
399 }
400 
401 TEST_F(CommandLineTest, SeparateEnumNonDefault) {
402   const char *Args[] = {"-mrelocation-model", "static"};
403 
404   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
405 
406   ASSERT_FALSE(Diags->hasErrorOccurred());
407   ASSERT_EQ(Invocation.getCodeGenOpts().RelocationModel, Reloc::Model::Static);
408 
409   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
410 
411   // Non default relocation model.
412   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-mrelocation-model")));
413   ASSERT_THAT(GeneratedArgs, Contains(StrEq("static")));
414   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model=static"))));
415 }
416 
417 TEST_F(CommandLineTest, SeparateEnumDefault) {
418   const char *Args[] = {"-mrelocation-model", "pic"};
419 
420   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
421 
422   ASSERT_FALSE(Diags->hasErrorOccurred());
423   ASSERT_EQ(Invocation.getCodeGenOpts().RelocationModel, Reloc::Model::PIC_);
424 
425   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
426 
427   // Default relocation model.
428   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model"))));
429   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("pic"))));
430   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model=pic"))));
431 }
432 
433 TEST_F(CommandLineTest, JoinedEnumNonDefault) {
434   const char *Args[] = {"-fobjc-dispatch-method=non-legacy"};
435 
436   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
437 
438   ASSERT_FALSE(Diags->hasErrorOccurred());
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   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
454 
455   ASSERT_FALSE(Diags->hasErrorOccurred());
456   ASSERT_EQ(Invocation.getCodeGenOpts().getObjCDispatchMethod(),
457             CodeGenOptions::Legacy);
458 
459   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
460 
461   ASSERT_THAT(GeneratedArgs,
462               Not(Contains(StrEq("-fobjc-dispatch-method=legacy"))));
463   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fobjc-dispatch-method="))));
464   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("legacy"))));
465 }
466 
467 TEST_F(CommandLineTest, StringVectorEmpty) {
468   const char *Args[] = {""};
469 
470   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
471 
472   ASSERT_FALSE(Diags->hasErrorOccurred());
473   ASSERT_TRUE(Invocation.getFrontendOpts().ModuleMapFiles.empty());
474 
475   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
476 
477   ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-fmodule-map-file"))));
478 }
479 
480 TEST_F(CommandLineTest, StringVectorSingle) {
481   const char *Args[] = {"-fmodule-map-file=a"};
482 
483   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
484 
485   ASSERT_FALSE(Diags->hasErrorOccurred());
486   ASSERT_EQ(Invocation.getFrontendOpts().ModuleMapFiles,
487             std::vector<std::string>({"a"}));
488 
489   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
490 
491   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=a"), 1));
492   ASSERT_THAT(GeneratedArgs, ContainsN(HasSubstr("-fmodule-map-file"), 1));
493 }
494 
495 TEST_F(CommandLineTest, StringVectorMultiple) {
496   const char *Args[] = {"-fmodule-map-file=a", "-fmodule-map-file=b"};
497 
498   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
499 
500   ASSERT_FALSE(Diags->hasErrorOccurred());
501   ASSERT_TRUE(Invocation.getFrontendOpts().ModuleMapFiles ==
502               std::vector<std::string>({"a", "b"}));
503 
504   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
505 
506   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=a"), 1));
507   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=b"), 1));
508   ASSERT_THAT(GeneratedArgs, ContainsN(HasSubstr("-fmodule-map-file"), 2));
509 }
510 
511 // CommaJoined option with MarshallingInfoStringVector.
512 
513 TEST_F(CommandLineTest, StringVectorCommaJoinedNone) {
514   const char *Args[] = {""};
515 
516   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
517 
518   ASSERT_FALSE(Diags->hasErrorOccurred());
519   ASSERT_TRUE(Invocation.getLangOpts()->CommentOpts.BlockCommandNames.empty());
520 
521   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
522 
523   ASSERT_THAT(GeneratedArgs,
524               Not(Contains(HasSubstr("-fcomment-block-commands"))));
525 }
526 
527 TEST_F(CommandLineTest, StringVectorCommaJoinedSingle) {
528   const char *Args[] = {"-fcomment-block-commands=x,y"};
529 
530   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
531 
532   ASSERT_FALSE(Diags->hasErrorOccurred());
533   ASSERT_EQ(Invocation.getLangOpts()->CommentOpts.BlockCommandNames,
534             std::vector<std::string>({"x", "y"}));
535 
536   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
537 
538   ASSERT_THAT(GeneratedArgs,
539               ContainsN(StrEq("-fcomment-block-commands=x,y"), 1));
540 }
541 
542 TEST_F(CommandLineTest, StringVectorCommaJoinedMultiple) {
543   const char *Args[] = {"-fcomment-block-commands=x,y",
544                         "-fcomment-block-commands=a,b"};
545 
546   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
547 
548   ASSERT_FALSE(Diags->hasErrorOccurred());
549   ASSERT_EQ(Invocation.getLangOpts()->CommentOpts.BlockCommandNames,
550             std::vector<std::string>({"x", "y", "a", "b"}));
551 
552   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
553 
554   ASSERT_THAT(GeneratedArgs,
555               ContainsN(StrEq("-fcomment-block-commands=x,y,a,b"), 1));
556 }
557 
558 // A flag that should be parsed only if a condition is met.
559 
560 TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagNotPresent) {
561   const char *Args[] = {""};
562 
563   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
564 
565   ASSERT_FALSE(Diags->hasErrorOccurred());
566   ASSERT_FALSE(Invocation.getLangOpts()->SYCL);
567   ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None);
568 
569   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
570 
571   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl"))));
572   ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std="))));
573 }
574 
575 TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagPresent) {
576   const char *Args[] = {"-sycl-std=2017"};
577 
578   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
579 
580   ASSERT_FALSE(Diags->hasErrorOccurred());
581   ASSERT_FALSE(Invocation.getLangOpts()->SYCL);
582   ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None);
583 
584   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
585 
586   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl"))));
587   ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std="))));
588 }
589 
590 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagNotPresent) {
591   const char *Args[] = {"-fsycl"};
592 
593   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
594 
595   ASSERT_FALSE(Diags->hasErrorOccurred());
596   ASSERT_TRUE(Invocation.getLangOpts()->SYCL);
597   ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None);
598 
599   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
600 
601   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl")));
602   ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std="))));
603 }
604 
605 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagPresent) {
606   const char *Args[] = {"-fsycl", "-sycl-std=2017"};
607 
608   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
609 
610   ASSERT_FALSE(Diags->hasErrorOccurred());
611   ASSERT_TRUE(Invocation.getLangOpts()->SYCL);
612   ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_2017);
613 
614   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
615 
616   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl")));
617   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-sycl-std=2017")));
618 }
619 
620 // Wide integer option.
621 
622 TEST_F(CommandLineTest, WideIntegerHighValue) {
623   const char *Args[] = {"-fbuild-session-timestamp=1609827494445723662"};
624 
625   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
626 
627   ASSERT_FALSE(Diags->hasErrorOccurred());
628   ASSERT_EQ(Invocation.getHeaderSearchOpts().BuildSessionTimestamp,
629             1609827494445723662ull);
630 }
631 
632 // Tree of boolean options that can be (directly or transitively) implied by
633 // their parent:
634 //
635 //   * -cl-unsafe-math-optimizations
636 //     * -cl-mad-enable
637 //     * -menable-unsafe-fp-math
638 //       * -freciprocal-math
639 
640 TEST_F(CommandLineTest, ImpliedBoolOptionsNoFlagPresent) {
641   const char *Args[] = {""};
642 
643   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
644 
645   ASSERT_FALSE(Diags->hasErrorOccurred());
646   ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath);
647   ASSERT_FALSE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
648   ASSERT_FALSE(Invocation.getLangOpts()->UnsafeFPMath);
649   ASSERT_FALSE(Invocation.getLangOpts()->AllowRecip);
650 
651   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
652 
653   // Not generated - missing.
654   ASSERT_THAT(GeneratedArgs,
655               Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
656   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
657   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
658   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
659 }
660 
661 TEST_F(CommandLineTest, ImpliedBoolOptionsRootFlagPresent) {
662   const char *Args[] = {"-cl-unsafe-math-optimizations"};
663 
664   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
665 
666   ASSERT_FALSE(Diags->hasErrorOccurred());
667   // Explicitly provided root flag.
668   ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath);
669   // Directly implied by explicitly provided root flag.
670   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
671   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
672   // Transitively implied by explicitly provided root flag.
673   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
674 
675   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
676 
677   // Generated - explicitly provided.
678   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations")));
679   // Not generated - implied by the generated root flag.
680   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
681   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
682   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
683 }
684 
685 TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) {
686   const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable",
687                         "-menable-unsafe-fp-math", "-freciprocal-math"};
688 
689   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
690 
691   ASSERT_FALSE(Diags->hasErrorOccurred());
692   ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath);
693   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
694   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
695   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
696 
697   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
698 
699   // Generated - explicitly provided.
700   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations")));
701   // Not generated - implied by their generated parent.
702   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
703   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
704   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
705 }
706 
707 TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) {
708   const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math",
709                         "-freciprocal-math"};
710 
711   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
712   ASSERT_FALSE(Diags->hasErrorOccurred());
713   ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath);
714   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
715   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
716   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
717 
718   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
719   // Not generated - missing.
720   ASSERT_THAT(GeneratedArgs,
721               Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
722   // Generated - explicitly provided.
723   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable")));
724   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math")));
725   // Not generated - implied by its generated parent.
726   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
727 }
728 
729 TEST_F(CommandLineTest, PresentAndNotImpliedGenerated) {
730   const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math"};
731 
732   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
733 
734   ASSERT_FALSE(Diags->hasErrorOccurred());
735 
736   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
737 
738   // Present options that were not implied are generated.
739   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable")));
740   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math")));
741 }
742 } // anonymous namespace
743