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