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