xref: /llvm-project/clang/unittests/Frontend/CompilerInvocationTest.cpp (revision 6f26a6de489e66830c3181b747f6b18e439f36be)
1 //===- unittests/Frontend/CompilerInvocationTest.cpp - CI tests //---------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/Frontend/CompilerInvocation.h"
10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/Frontend/TextDiagnosticBuffer.h"
12 #include "llvm/Support/Host.h"
13 
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16 
17 using namespace llvm;
18 using namespace clang;
19 
20 using ::testing::Contains;
21 using ::testing::StrEq;
22 
23 namespace {
24 class CommandLineTest : public ::testing::Test {
25 public:
26   IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
27   SmallVector<const char *, 32> GeneratedArgs;
28   SmallVector<std::string, 32> GeneratedArgsStorage;
29   CompilerInvocation Invocation;
30 
31   const char *operator()(const Twine &Arg) {
32     return GeneratedArgsStorage.emplace_back(Arg.str()).c_str();
33   }
34 
35   CommandLineTest()
36       : Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions(),
37                                                   new TextDiagnosticBuffer())) {
38   }
39 };
40 
41 // Boolean option with a keypath that defaults to true.
42 // The only flag with a negative spelling can set the keypath to false.
43 
44 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagNotPresent) {
45   const char *Args[] = {""};
46 
47   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
48 
49   ASSERT_FALSE(Diags->hasErrorOccurred());
50   ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary);
51 
52   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
53 
54   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-temp-file"))));
55 }
56 
57 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagPresent) {
58   const char *Args[] = {"-fno-temp-file"};
59 
60   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
61 
62   ASSERT_FALSE(Diags->hasErrorOccurred());
63   ASSERT_FALSE(Invocation.getFrontendOpts().UseTemporary);
64 
65   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
66 
67   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-temp-file")));
68 }
69 
70 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagUnknownPresent) {
71   const char *Args[] = {"-ftemp-file"};
72 
73   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
74 
75   // Driver-only flag.
76   ASSERT_TRUE(Diags->hasErrorOccurred());
77   ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary);
78 }
79 
80 // Boolean option with a keypath that defaults to true.
81 // The flag with negative spelling can set the keypath to false.
82 // The flag with positive spelling can reset the keypath to true.
83 
84 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNone) {
85   const char *Args[] = {""};
86 
87   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
88   ASSERT_FALSE(Diags->hasErrorOccurred());
89   ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink);
90 
91   // TODO: Test argument generation.
92 }
93 
94 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNegChange) {
95   const char *Args[] = {"-fno-autolink"};
96 
97   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
98   ASSERT_FALSE(Diags->hasErrorOccurred());
99   ASSERT_FALSE(Invocation.getCodeGenOpts().Autolink);
100 
101   // TODO: Test argument generation.
102 }
103 
104 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentPosReset) {
105   const char *Args[] = {"-fautolink"};
106 
107   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
108   ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag.
109   ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink);
110 }
111 
112 // Boolean option with a keypath that defaults to false.
113 // The flag with negative spelling can set the keypath to true.
114 // The flag with positive spelling can reset the keypath to false.
115 
116 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNone) {
117   const char *Args[] = {""};
118 
119   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
120   ASSERT_FALSE(Diags->hasErrorOccurred());
121   ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables);
122 
123   // TODO: Test argument generation.
124 }
125 
126 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegChange) {
127   const char *Args[] = {"-gno-inline-line-tables"};
128 
129   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
130   ASSERT_FALSE(Diags->hasErrorOccurred());
131   ASSERT_TRUE(Invocation.getCodeGenOpts().NoInlineLineTables);
132 
133   // TODO: Test argument generation.
134 }
135 
136 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosReset) {
137   const char *Args[] = {"-ginline-line-tables"};
138 
139   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
140   ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag.
141   ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables);
142 }
143 
144 // Boolean option with a keypath that defaults to false.
145 // The flag with positive spelling can set the keypath to true.
146 // The flag with negative spelling can reset the keypath to false.
147 
148 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNoneX) {
149   const char *Args[] = {""};
150 
151   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
152   ASSERT_FALSE(Diags->hasErrorOccurred());
153   ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash);
154 
155   // TODO: Test argument generation.
156 }
157 
158 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosChange) {
159   const char *Args[] = {"-gcodeview-ghash"};
160 
161   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
162   ASSERT_FALSE(Diags->hasErrorOccurred());
163   ASSERT_TRUE(Invocation.getCodeGenOpts().CodeViewGHash);
164 
165   // TODO: Test argument generation.
166 }
167 
168 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegReset) {
169   const char *Args[] = {"-gno-codeview-ghash"};
170 
171   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
172   ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag.
173   ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash);
174 }
175 
176 // Boolean option with a keypath that defaults to an arbitrary expression.
177 // The flag with positive spelling can set the keypath to true.
178 // The flag with negative spelling can set the keypath to false.
179 
180 static constexpr unsigned PassManagerDefault =
181     !static_cast<unsigned>(LLVM_ENABLE_NEW_PASS_MANAGER);
182 
183 static constexpr const char *PassManagerResetByFlag =
184     LLVM_ENABLE_NEW_PASS_MANAGER ? "-fno-legacy-pass-manager"
185                                  : "-flegacy-pass-manager";
186 
187 static constexpr const char *PassManagerChangedByFlag =
188     LLVM_ENABLE_NEW_PASS_MANAGER ? "-flegacy-pass-manager"
189                                  : "-fno-legacy-pass-manager";
190 
191 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentNone) {
192   const char *Args = {""};
193 
194   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
195 
196   ASSERT_FALSE(Diags->hasErrorOccurred());
197   ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault);
198 
199   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
200 
201   ASSERT_THAT(GeneratedArgs, Contains(StrEq(PassManagerResetByFlag)));
202   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag))));
203 }
204 
205 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentChange) {
206   const char *Args[] = {PassManagerChangedByFlag};
207 
208   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
209   ASSERT_FALSE(Diags->hasErrorOccurred());
210   ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, !PassManagerDefault);
211 
212   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
213   ASSERT_THAT(GeneratedArgs, Contains(StrEq(PassManagerChangedByFlag)));
214   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag))));
215 }
216 
217 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentReset) {
218   const char *Args[] = {PassManagerResetByFlag};
219 
220   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
221   ASSERT_FALSE(Diags->hasErrorOccurred());
222   ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault);
223 
224   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
225   ASSERT_THAT(GeneratedArgs, Contains(StrEq(PassManagerResetByFlag)));
226   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag))));
227 }
228 
229 TEST_F(CommandLineTest, CanGenerateCC1CommandLineFlag) {
230   const char *Args[] = {"-fmodules-strict-context-hash"};
231 
232   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
233 
234   ASSERT_FALSE(Diags->hasErrorOccurred());
235 
236   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
237 
238   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash")));
239 }
240 
241 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparate) {
242   const char *TripleCStr = "i686-apple-darwin9";
243   const char *Args[] = {"-triple", TripleCStr};
244 
245   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
246 
247   ASSERT_FALSE(Diags->hasErrorOccurred());
248 
249   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
250 
251   ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr)));
252 }
253 
254 TEST_F(CommandLineTest,  CanGenerateCC1CommandLineSeparateRequiredPresent) {
255   const std::string DefaultTriple =
256       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
257   const char *Args[] = {"-triple", DefaultTriple.c_str()};
258 
259   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
260 
261   ASSERT_FALSE(Diags->hasErrorOccurred());
262 
263   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
264 
265   // Triple should always be emitted even if it is the default
266   ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str())));
267 }
268 
269 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredAbsent) {
270   const std::string DefaultTriple =
271       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
272   const char *Args[] = {""};
273 
274   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
275 
276   ASSERT_FALSE(Diags->hasErrorOccurred());
277 
278   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
279 
280   // Triple should always be emitted even if it is the default
281   ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str())));
282 }
283 
284 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateEnumNonDefault) {
285   const char *Args[] = {"-mrelocation-model", "static"};
286 
287   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
288 
289   ASSERT_FALSE(Diags->hasErrorOccurred());
290 
291   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
292 
293   // Non default relocation model.
294   ASSERT_THAT(GeneratedArgs, Contains(StrEq("static")));
295 }
296 
297 TEST_F(CommandLineTest, CanGenerateCC1COmmandLineSeparateEnumDefault) {
298   const char *Args[] = {"-mrelocation-model", "pic"};
299 
300   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
301 
302   ASSERT_FALSE(Diags->hasErrorOccurred());
303 
304   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
305 
306   // Default relocation model.
307   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("pic"))));
308 }
309 
310 // Tree of boolean options that can be (directly or transitively) implied by
311 // their parent:
312 //
313 //   * -cl-unsafe-math-optimizations
314 //     * -cl-mad-enable
315 //     * -menable-unsafe-fp-math
316 //       * -freciprocal-math
317 
318 TEST_F(CommandLineTest, ImpliedBoolOptionsNoFlagPresent) {
319   const char *Args[] = {""};
320 
321   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
322 
323   ASSERT_FALSE(Diags->hasErrorOccurred());
324   ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath);
325   ASSERT_FALSE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
326   ASSERT_FALSE(Invocation.getLangOpts()->UnsafeFPMath);
327   ASSERT_FALSE(Invocation.getLangOpts()->AllowRecip);
328 
329   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
330 
331   // Not generated - missing.
332   ASSERT_THAT(GeneratedArgs,
333               Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
334   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
335   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
336   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
337 }
338 
339 TEST_F(CommandLineTest, ImpliedBoolOptionsRootFlagPresent) {
340   const char *Args[] = {"-cl-unsafe-math-optimizations"};
341 
342   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
343 
344   ASSERT_FALSE(Diags->hasErrorOccurred());
345   // Explicitly provided root flag.
346   ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath);
347   // Directly implied by explicitly provided root flag.
348   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
349   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
350   // Transitively implied by explicitly provided root flag.
351   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
352 
353   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
354 
355   // Generated - explicitly provided.
356   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations")));
357   // Not generated - implied by the generated root flag.
358   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
359   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
360   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
361 }
362 
363 TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) {
364   const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable",
365                         "-menable-unsafe-fp-math", "-freciprocal-math"};
366 
367   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
368 
369   ASSERT_FALSE(Diags->hasErrorOccurred());
370   ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath);
371   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
372   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
373   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
374 
375   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
376 
377   // Generated - explicitly provided.
378   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations")));
379   // Not generated - implied by their generated parent.
380   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
381   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
382   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
383 }
384 
385 TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) {
386   const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math",
387                         "-freciprocal-math"};
388 
389   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
390   ASSERT_FALSE(Diags->hasErrorOccurred());
391   ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath);
392   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
393   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
394   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
395 
396   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
397   // Not generated - missing.
398   ASSERT_THAT(GeneratedArgs,
399               Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
400   // Generated - explicitly provided.
401   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable")));
402   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math")));
403   // Not generated - implied by its generated parent.
404   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
405 }
406 
407 TEST_F(CommandLineTest, PresentAndNotImpliedGenerated) {
408   const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math"};
409 
410   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
411 
412   ASSERT_FALSE(Diags->hasErrorOccurred());
413 
414   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
415 
416   // Present options that were not implied are generated.
417   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable")));
418   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math")));
419 }
420 } // anonymous namespace
421