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 "llvm/Support/Host.h"
15 
16 #include "gmock/gmock.h"
17 #include "gtest/gtest.h"
18 
19 using namespace llvm;
20 using namespace clang;
21 
22 using ::testing::Contains;
23 using ::testing::HasSubstr;
24 using ::testing::StrEq;
25 
26 namespace {
27 class CommandLineTest : public ::testing::Test {
28 public:
29   IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
30   SmallVector<const char *, 32> GeneratedArgs;
31   SmallVector<std::string, 32> GeneratedArgsStorage;
32   CompilerInvocation Invocation;
33 
34   const char *operator()(const Twine &Arg) {
35     return GeneratedArgsStorage.emplace_back(Arg.str()).c_str();
36   }
37 
38   CommandLineTest()
39       : Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions(),
40                                                   new TextDiagnosticBuffer())) {
41   }
42 };
43 
44 template <typename M>
45 std::string describeContainsN(M InnerMatcher, unsigned N, bool Negation) {
46   StringRef Contains = Negation ? "doesn't contain" : "contains";
47   StringRef Instance = N == 1 ? " instance " : " instances ";
48   StringRef Element = "of element that ";
49 
50   std::ostringstream Inner;
51   InnerMatcher.impl().DescribeTo(&Inner);
52 
53   return (Contains + " exactly " + Twine(N) + Instance + Element + Inner.str())
54       .str();
55 }
56 
57 MATCHER_P2(ContainsN, InnerMatcher, N,
58            describeContainsN(InnerMatcher, N, negation)) {
59   auto InnerMatches = [this](const auto &Element) {
60     ::testing::internal::DummyMatchResultListener InnerListener;
61     return InnerMatcher.impl().MatchAndExplain(Element, &InnerListener);
62   };
63 
64   return count_if(arg, InnerMatches) == N;
65 }
66 
67 TEST(ContainsN, Empty) {
68   const char *Array[] = {""};
69 
70   ASSERT_THAT(Array, ContainsN(StrEq("x"), 0));
71   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1)));
72   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2)));
73 }
74 
75 TEST(ContainsN, Zero) {
76   const char *Array[] = {"y"};
77 
78   ASSERT_THAT(Array, ContainsN(StrEq("x"), 0));
79   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1)));
80   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2)));
81 }
82 
83 TEST(ContainsN, One) {
84   const char *Array[] = {"a", "b", "x", "z"};
85 
86   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 0)));
87   ASSERT_THAT(Array, ContainsN(StrEq("x"), 1));
88   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 2)));
89 }
90 
91 TEST(ContainsN, Two) {
92   const char *Array[] = {"x", "a", "b", "x"};
93 
94   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 0)));
95   ASSERT_THAT(Array, Not(ContainsN(StrEq("x"), 1)));
96   ASSERT_THAT(Array, ContainsN(StrEq("x"), 2));
97 }
98 
99 // Boolean option with a keypath that defaults to true.
100 // The only flag with a negative spelling can set the keypath to false.
101 
102 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagNotPresent) {
103   const char *Args[] = {""};
104 
105   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
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   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
117   ASSERT_FALSE(Invocation.getFrontendOpts().UseTemporary);
118 
119   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
120 
121   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-temp-file")));
122 }
123 
124 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagUnknownPresent) {
125   const char *Args[] = {"-ftemp-file"};
126 
127   // Driver-only flag.
128   ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
129   ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary);
130 }
131 
132 // Boolean option with a keypath that defaults to true.
133 // The flag with negative spelling can set the keypath to false.
134 // The flag with positive spelling can reset the keypath to true.
135 
136 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNone) {
137   const char *Args[] = {""};
138 
139   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
140   ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink);
141 
142   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
143   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink"))));
144   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-autolink"))));
145 }
146 
147 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNegChange) {
148   const char *Args[] = {"-fno-autolink"};
149 
150   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
151   ASSERT_FALSE(Invocation.getCodeGenOpts().Autolink);
152 
153   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
154   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-autolink")));
155   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink"))));
156 }
157 
158 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentPosReset) {
159   const char *Args[] = {"-fautolink"};
160 
161   // Driver-only flag.
162   ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
163   ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink);
164 }
165 
166 // Boolean option with a keypath that defaults to false.
167 // The flag with negative spelling can set the keypath to true.
168 // The flag with positive spelling can reset the keypath to false.
169 
170 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNone) {
171   const char *Args[] = {""};
172 
173   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
174   ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables);
175 
176   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
177   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables"))));
178   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-inline-line-tables"))));
179 }
180 
181 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegChange) {
182   const char *Args[] = {"-gno-inline-line-tables"};
183 
184   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
185   ASSERT_TRUE(Invocation.getCodeGenOpts().NoInlineLineTables);
186 
187   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
188   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gno-inline-line-tables")));
189   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables"))));
190 }
191 
192 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosReset) {
193   const char *Args[] = {"-ginline-line-tables"};
194 
195   // Driver-only flag.
196   ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
197   ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables);
198 }
199 
200 // Boolean option with a keypath that defaults to false.
201 // The flag with positive spelling can set the keypath to true.
202 // The flag with negative spelling can reset the keypath to false.
203 
204 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNoneX) {
205   const char *Args[] = {""};
206 
207   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
208   ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash);
209 
210   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
211   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gcodeview-ghash"))));
212   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash"))));
213 }
214 
215 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosChange) {
216   const char *Args[] = {"-gcodeview-ghash"};
217 
218   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
219   ASSERT_TRUE(Invocation.getCodeGenOpts().CodeViewGHash);
220 
221   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
222   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gcodeview-ghash")));
223   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash"))));
224 }
225 
226 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegReset) {
227   const char *Args[] = {"-gno-codeview-ghash"};
228 
229   // Driver-only flag.
230   ASSERT_FALSE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
231   ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash);
232 }
233 
234 // Boolean option with a keypath that defaults to an arbitrary expression.
235 // The flag with positive spelling can set the keypath to true.
236 // The flag with negative spelling can set the keypath to false.
237 
238 static constexpr unsigned PassManagerDefault =
239     !static_cast<unsigned>(LLVM_ENABLE_NEW_PASS_MANAGER);
240 
241 static constexpr const char *PassManagerResetByFlag =
242     LLVM_ENABLE_NEW_PASS_MANAGER ? "-fno-legacy-pass-manager"
243                                  : "-flegacy-pass-manager";
244 
245 static constexpr const char *PassManagerChangedByFlag =
246     LLVM_ENABLE_NEW_PASS_MANAGER ? "-flegacy-pass-manager"
247                                  : "-fno-legacy-pass-manager";
248 
249 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentNone) {
250   const char *Args = {""};
251 
252   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
253   ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault);
254 
255   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
256 
257   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag))));
258   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag))));
259 }
260 
261 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentChange) {
262   const char *Args[] = {PassManagerChangedByFlag};
263 
264   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
265   ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, !PassManagerDefault);
266 
267   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
268   ASSERT_THAT(GeneratedArgs, Contains(StrEq(PassManagerChangedByFlag)));
269   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag))));
270 }
271 
272 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentReset) {
273   const char *Args[] = {PassManagerResetByFlag};
274 
275   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
276   ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault);
277 
278   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
279   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag))));
280   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag))));
281 }
282 
283 // Boolean option that gets the CC1Option flag from a let statement (which
284 // is applied **after** the record is defined):
285 //
286 //   let Flags = [CC1Option] in {
287 //     defm option : BoolOption<...>;
288 //   }
289 
290 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentNone) {
291   const char *Args[] = {""};
292 
293   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
294   ASSERT_FALSE(Invocation.getCodeGenOpts().DebugPassManager);
295 
296   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
297 
298   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdebug-pass-manager"))));
299   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager"))));
300 }
301 
302 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentPos) {
303   const char *Args[] = {"-fdebug-pass-manager"};
304 
305   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
306   ASSERT_TRUE(Invocation.getCodeGenOpts().DebugPassManager);
307 
308   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
309 
310   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fdebug-pass-manager"), 1));
311   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager"))));
312 }
313 
314 TEST_F(CommandLineTest, BoolOptionCC1ViaLetPresentNeg) {
315   const char *Args[] = {"-fno-debug-pass-manager"};
316 
317   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
318   ASSERT_FALSE(Invocation.getCodeGenOpts().DebugPassManager);
319 
320   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
321 
322   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-debug-pass-manager"))));
323   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdebug-pass-manager"))));
324 }
325 
326 TEST_F(CommandLineTest, CanGenerateCC1CommandLineFlag) {
327   const char *Args[] = {"-fmodules-strict-context-hash"};
328 
329   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
330 
331   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
332 
333   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash")));
334 }
335 
336 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparate) {
337   const char *TripleCStr = "i686-apple-darwin9";
338   const char *Args[] = {"-triple", TripleCStr};
339 
340   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
341 
342   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
343 
344   ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr)));
345 }
346 
347 TEST_F(CommandLineTest,  CanGenerateCC1CommandLineSeparateRequiredPresent) {
348   const std::string DefaultTriple =
349       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
350   const char *Args[] = {"-triple", DefaultTriple.c_str()};
351 
352   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
353 
354   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
355 
356   // Triple should always be emitted even if it is the default
357   ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str())));
358 }
359 
360 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredAbsent) {
361   const std::string DefaultTriple =
362       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
363   const char *Args[] = {""};
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, SeparateEnumNonDefault) {
374   const char *Args[] = {"-mrelocation-model", "static"};
375 
376   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
377   ASSERT_EQ(Invocation.getCodeGenOpts().RelocationModel, Reloc::Model::Static);
378 
379   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
380 
381   // Non default relocation model.
382   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-mrelocation-model")));
383   ASSERT_THAT(GeneratedArgs, Contains(StrEq("static")));
384   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model=static"))));
385 }
386 
387 TEST_F(CommandLineTest, SeparateEnumDefault) {
388   const char *Args[] = {"-mrelocation-model", "pic"};
389 
390   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
391   ASSERT_EQ(Invocation.getCodeGenOpts().RelocationModel, Reloc::Model::PIC_);
392 
393   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
394 
395   // Default relocation model.
396   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model"))));
397   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("pic"))));
398   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-mrelocation-model=pic"))));
399 }
400 
401 TEST_F(CommandLineTest, JoinedEnumNonDefault) {
402   const char *Args[] = {"-fobjc-dispatch-method=non-legacy"};
403 
404   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
405   ASSERT_EQ(Invocation.getCodeGenOpts().getObjCDispatchMethod(),
406             CodeGenOptions::NonLegacy);
407 
408   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
409 
410   ASSERT_THAT(GeneratedArgs,
411               Contains(StrEq("-fobjc-dispatch-method=non-legacy")));
412   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fobjc-dispatch-method="))));
413   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("non-legacy"))));
414 }
415 
416 TEST_F(CommandLineTest, JoinedEnumDefault) {
417   const char *Args[] = {"-fobjc-dispatch-method=legacy"};
418 
419   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
420   ASSERT_EQ(Invocation.getCodeGenOpts().getObjCDispatchMethod(),
421             CodeGenOptions::Legacy);
422 
423   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
424 
425   ASSERT_THAT(GeneratedArgs,
426               Not(Contains(StrEq("-fobjc-dispatch-method=legacy"))));
427   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fobjc-dispatch-method="))));
428   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("legacy"))));
429 }
430 
431 TEST_F(CommandLineTest, StringVectorEmpty) {
432   const char *Args[] = {""};
433 
434   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
435   ASSERT_TRUE(Invocation.getFrontendOpts().ModuleMapFiles.empty());
436 
437   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
438 
439   ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-fmodule-map-file"))));
440 }
441 
442 TEST_F(CommandLineTest, StringVectorSingle) {
443   const char *Args[] = {"-fmodule-map-file=a"};
444 
445   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
446   ASSERT_EQ(Invocation.getFrontendOpts().ModuleMapFiles,
447             std::vector<std::string>({"a"}));
448 
449   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
450 
451   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=a"), 1));
452   ASSERT_THAT(GeneratedArgs, ContainsN(HasSubstr("-fmodule-map-file"), 1));
453 }
454 
455 TEST_F(CommandLineTest, StringVectorMultiple) {
456   const char *Args[] = {"-fmodule-map-file=a", "-fmodule-map-file=b"};
457 
458   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
459   ASSERT_TRUE(Invocation.getFrontendOpts().ModuleMapFiles ==
460               std::vector<std::string>({"a", "b"}));
461 
462   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
463 
464   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=a"), 1));
465   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-fmodule-map-file=b"), 1));
466   ASSERT_THAT(GeneratedArgs, ContainsN(HasSubstr("-fmodule-map-file"), 2));
467 }
468 
469 // CommaJoined option with MarshallingInfoStringVector.
470 
471 TEST_F(CommandLineTest, StringVectorCommaJoinedNone) {
472   const char *Args[] = {""};
473 
474   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
475   ASSERT_TRUE(Invocation.getLangOpts()->CommentOpts.BlockCommandNames.empty());
476 
477   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
478 
479   ASSERT_THAT(GeneratedArgs,
480               Not(Contains(HasSubstr("-fcomment-block-commands"))));
481 }
482 
483 TEST_F(CommandLineTest, StringVectorCommaJoinedSingle) {
484   const char *Args[] = {"-fcomment-block-commands=x,y"};
485 
486   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
487   ASSERT_EQ(Invocation.getLangOpts()->CommentOpts.BlockCommandNames,
488             std::vector<std::string>({"x", "y"}));
489 
490   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
491 
492   ASSERT_THAT(GeneratedArgs,
493               ContainsN(StrEq("-fcomment-block-commands=x,y"), 1));
494 }
495 
496 TEST_F(CommandLineTest, StringVectorCommaJoinedMultiple) {
497   const char *Args[] = {"-fcomment-block-commands=x,y",
498                         "-fcomment-block-commands=a,b"};
499 
500   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
501   ASSERT_EQ(Invocation.getLangOpts()->CommentOpts.BlockCommandNames,
502             std::vector<std::string>({"x", "y", "a", "b"}));
503 
504   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
505 
506   ASSERT_THAT(GeneratedArgs,
507               ContainsN(StrEq("-fcomment-block-commands=x,y,a,b"), 1));
508 }
509 
510 // A flag that should be parsed only if a condition is met.
511 
512 TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagNotPresent) {
513   const char *Args[] = {""};
514 
515   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
516 
517   ASSERT_FALSE(Diags->hasErrorOccurred());
518   ASSERT_FALSE(Invocation.getLangOpts()->SYCL);
519   ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None);
520 
521   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
522 
523   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl"))));
524   ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std="))));
525 }
526 
527 TEST_F(CommandLineTest, ConditionalParsingIfFalseFlagPresent) {
528   const char *Args[] = {"-sycl-std=2017"};
529 
530   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
531 
532   ASSERT_FALSE(Diags->hasErrorOccurred());
533   ASSERT_FALSE(Invocation.getLangOpts()->SYCL);
534   ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None);
535 
536   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
537 
538   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fsycl"))));
539   ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std="))));
540 }
541 
542 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagNotPresent) {
543   const char *Args[] = {"-fsycl"};
544 
545   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
546 
547   ASSERT_FALSE(Diags->hasErrorOccurred());
548   ASSERT_TRUE(Invocation.getLangOpts()->SYCL);
549   ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_None);
550 
551   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
552 
553   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl")));
554   ASSERT_THAT(GeneratedArgs, Not(Contains(HasSubstr("-sycl-std="))));
555 }
556 
557 TEST_F(CommandLineTest, ConditionalParsingIfTrueFlagPresent) {
558   const char *Args[] = {"-fsycl", "-sycl-std=2017"};
559 
560   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
561 
562   ASSERT_FALSE(Diags->hasErrorOccurred());
563   ASSERT_TRUE(Invocation.getLangOpts()->SYCL);
564   ASSERT_EQ(Invocation.getLangOpts()->getSYCLVersion(), LangOptions::SYCL_2017);
565 
566   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
567 
568   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fsycl")));
569   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-sycl-std=2017")));
570 }
571 
572 // Wide integer option.
573 
574 TEST_F(CommandLineTest, WideIntegerHighValue) {
575   const char *Args[] = {"-fbuild-session-timestamp=1609827494445723662"};
576 
577   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
578 
579   ASSERT_FALSE(Diags->hasErrorOccurred());
580   ASSERT_EQ(Invocation.getHeaderSearchOpts().BuildSessionTimestamp,
581             1609827494445723662ull);
582 }
583 
584 // Tree of boolean options that can be (directly or transitively) implied by
585 // their parent:
586 //
587 //   * -cl-unsafe-math-optimizations
588 //     * -cl-mad-enable
589 //     * -menable-unsafe-fp-math
590 //       * -freciprocal-math
591 
592 TEST_F(CommandLineTest, ImpliedBoolOptionsNoFlagPresent) {
593   const char *Args[] = {""};
594 
595   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
596   ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath);
597   ASSERT_FALSE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
598   ASSERT_FALSE(Invocation.getLangOpts()->UnsafeFPMath);
599   ASSERT_FALSE(Invocation.getLangOpts()->AllowRecip);
600 
601   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
602 
603   // Not generated - missing.
604   ASSERT_THAT(GeneratedArgs,
605               Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
606   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
607   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
608   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
609 }
610 
611 TEST_F(CommandLineTest, ImpliedBoolOptionsRootFlagPresent) {
612   const char *Args[] = {"-cl-unsafe-math-optimizations"};
613 
614   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
615   // Explicitly provided root flag.
616   ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath);
617   // Directly implied by explicitly provided root flag.
618   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
619   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
620   // Transitively implied by explicitly provided root flag.
621   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
622 
623   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
624 
625   // Generated - explicitly provided.
626   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations")));
627   // Not generated - implied by the generated root flag.
628   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
629   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
630   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
631 }
632 
633 TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) {
634   const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable",
635                         "-menable-unsafe-fp-math", "-freciprocal-math"};
636 
637   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
638   ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath);
639   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
640   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
641   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
642 
643   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
644 
645   // Generated - explicitly provided.
646   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations")));
647   // Not generated - implied by their generated parent.
648   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
649   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
650   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
651 }
652 
653 TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) {
654   const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math",
655                         "-freciprocal-math"};
656 
657   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
658   ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath);
659   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
660   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
661   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
662 
663   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
664   // Not generated - missing.
665   ASSERT_THAT(GeneratedArgs,
666               Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
667   // Generated - explicitly provided.
668   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable")));
669   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math")));
670   // Not generated - implied by its generated parent.
671   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
672 }
673 
674 TEST_F(CommandLineTest, PresentAndNotImpliedGenerated) {
675   const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math"};
676 
677   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
678 
679   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
680 
681   // Present options that were not implied are generated.
682   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable")));
683   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math")));
684 }
685 
686 // Diagnostic option.
687 
688 TEST_F(CommandLineTest, DiagnosticOptionPresent) {
689   const char *Args[] = {"-verify=xyz"};
690 
691   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
692 
693   ASSERT_EQ(Invocation.getDiagnosticOpts().VerifyPrefixes,
694             std::vector<std::string>({"xyz"}));
695 
696   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
697 
698   ASSERT_THAT(GeneratedArgs, ContainsN(StrEq("-verify=xyz"), 1));
699 }
700 
701 // Option default depends on language standard.
702 
703 TEST_F(CommandLineTest, DigraphsImplied) {
704   const char *Args[] = {""};
705 
706   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
707   ASSERT_TRUE(Invocation.getLangOpts()->Digraphs);
708 
709   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
710   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-digraphs"))));
711   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs"))));
712 }
713 
714 TEST_F(CommandLineTest, DigraphsDisabled) {
715   const char *Args[] = {"-fno-digraphs"};
716 
717   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
718   ASSERT_FALSE(Invocation.getLangOpts()->Digraphs);
719 
720   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
721   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-digraphs")));
722   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs"))));
723 }
724 
725 TEST_F(CommandLineTest, DigraphsNotImplied) {
726   const char *Args[] = {"-std=c89"};
727 
728   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
729   ASSERT_FALSE(Invocation.getLangOpts()->Digraphs);
730 
731   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
732   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-digraphs"))));
733   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fdigraphs"))));
734 }
735 
736 TEST_F(CommandLineTest, DigraphsEnabled) {
737   const char *Args[] = {"-std=c89", "-fdigraphs"};
738 
739   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
740   ASSERT_TRUE(Invocation.getLangOpts()->Digraphs);
741 
742   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
743   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fdigraphs")));
744 }
745 
746 TEST_F(CommandLineTest, RoundTrip) {
747   // Testing one marshalled and one manually generated option from each
748   // CompilerInvocation member.
749   const char *Args[] = {
750       "-round-trip-args",
751       // LanguageOptions
752       "-std=c17",
753       "-fmax-tokens=10",
754       // TargetOptions
755       "-target-sdk-version=1.2.3",
756       "-meabi",
757       "4",
758       // DiagnosticOptions
759       "-Wundef-prefix=XY",
760       "-fdiagnostics-format",
761       "clang",
762       // HeaderSearchOptions
763       "-stdlib=libc++",
764       "-fimplicit-module-maps",
765       // PreprocessorOptions
766       "-DXY=AB",
767       "-include-pch",
768       "a.pch",
769       // AnalyzerOptions
770       "-analyzer-config",
771       "ctu-import-threshold=42",
772       "-unoptimized-cfg",
773       // MigratorOptions (no manually handled arguments)
774       "-no-ns-alloc-error",
775       // CodeGenOptions
776       "-debug-info-kind=limited",
777       "-debug-info-macro",
778       // DependencyOutputOptions
779       "--show-includes",
780       "-H",
781       // FileSystemOptions (no manually handled arguments)
782       "-working-directory",
783       "folder",
784       // FrontendOptions
785       "-load",
786       "plugin",
787       "-ast-merge",
788       // PreprocessorOutputOptions
789       "-dD",
790       "-CC",
791   };
792 
793   ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
794 
795   ASSERT_TRUE(Invocation.getLangOpts()->C17);
796   ASSERT_EQ(Invocation.getLangOpts()->MaxTokens, 10u);
797 
798   ASSERT_EQ(Invocation.getTargetOpts().SDKVersion, llvm::VersionTuple(1, 2, 3));
799   ASSERT_EQ(Invocation.getTargetOpts().EABIVersion, EABI::EABI4);
800 
801   ASSERT_THAT(Invocation.getDiagnosticOpts().UndefPrefixes,
802               Contains(StrEq("XY")));
803   ASSERT_EQ(Invocation.getDiagnosticOpts().getFormat(),
804             TextDiagnosticFormat::Clang);
805 
806   ASSERT_TRUE(Invocation.getHeaderSearchOpts().UseLibcxx);
807   ASSERT_TRUE(Invocation.getHeaderSearchOpts().ImplicitModuleMaps);
808 
809   ASSERT_THAT(Invocation.getPreprocessorOpts().Macros,
810               Contains(std::make_pair(std::string("XY=AB"), false)));
811   ASSERT_EQ(Invocation.getPreprocessorOpts().ImplicitPCHInclude, "a.pch");
812 
813   ASSERT_EQ(Invocation.getAnalyzerOpts()->Config["ctu-import-threshold"], "42");
814   ASSERT_TRUE(Invocation.getAnalyzerOpts()->UnoptimizedCFG);
815 
816   ASSERT_TRUE(Invocation.getMigratorOpts().NoNSAllocReallocError);
817 
818   ASSERT_EQ(Invocation.getCodeGenOpts().getDebugInfo(),
819             codegenoptions::DebugInfoKind::LimitedDebugInfo);
820   ASSERT_TRUE(Invocation.getCodeGenOpts().MacroDebugInfo);
821 
822   ASSERT_EQ(Invocation.getDependencyOutputOpts().ShowIncludesDest,
823             ShowIncludesDestination::Stdout);
824   ASSERT_TRUE(Invocation.getDependencyOutputOpts().ShowHeaderIncludes);
825 }
826 } // anonymous namespace
827