xref: /llvm-project/clang/unittests/Frontend/CompilerInvocationTest.cpp (revision 6baa9769ed573741290fb186d02df7cf676fc8de)
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   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
92   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink"))));
93   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-autolink"))));
94 }
95 
96 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNegChange) {
97   const char *Args[] = {"-fno-autolink"};
98 
99   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
100   ASSERT_FALSE(Diags->hasErrorOccurred());
101   ASSERT_FALSE(Invocation.getCodeGenOpts().Autolink);
102 
103   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
104   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-autolink")));
105   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink"))));
106 }
107 
108 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentPosReset) {
109   const char *Args[] = {"-fautolink"};
110 
111   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
112   ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag.
113   ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink);
114 }
115 
116 // Boolean option with a keypath that defaults to false.
117 // The flag with negative spelling can set the keypath to true.
118 // The flag with positive spelling can reset the keypath to false.
119 
120 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNone) {
121   const char *Args[] = {""};
122 
123   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
124   ASSERT_FALSE(Diags->hasErrorOccurred());
125   ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables);
126 
127   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
128   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables"))));
129   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-inline-line-tables"))));
130 }
131 
132 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegChange) {
133   const char *Args[] = {"-gno-inline-line-tables"};
134 
135   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
136   ASSERT_FALSE(Diags->hasErrorOccurred());
137   ASSERT_TRUE(Invocation.getCodeGenOpts().NoInlineLineTables);
138 
139   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
140   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gno-inline-line-tables")));
141   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables"))));
142 }
143 
144 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosReset) {
145   const char *Args[] = {"-ginline-line-tables"};
146 
147   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
148   ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag.
149   ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables);
150 }
151 
152 // Boolean option with a keypath that defaults to false.
153 // The flag with positive spelling can set the keypath to true.
154 // The flag with negative spelling can reset the keypath to false.
155 
156 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNoneX) {
157   const char *Args[] = {""};
158 
159   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
160   ASSERT_FALSE(Diags->hasErrorOccurred());
161   ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash);
162 
163   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
164   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gcodeview-ghash"))));
165   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash"))));
166 }
167 
168 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosChange) {
169   const char *Args[] = {"-gcodeview-ghash"};
170 
171   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
172   ASSERT_FALSE(Diags->hasErrorOccurred());
173   ASSERT_TRUE(Invocation.getCodeGenOpts().CodeViewGHash);
174 
175   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
176   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gcodeview-ghash")));
177   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash"))));
178 }
179 
180 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegReset) {
181   const char *Args[] = {"-gno-codeview-ghash"};
182 
183   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
184   ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag.
185   ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash);
186 }
187 
188 // Boolean option with a keypath that defaults to an arbitrary expression.
189 // The flag with positive spelling can set the keypath to true.
190 // The flag with negative spelling can set the keypath to false.
191 
192 static constexpr unsigned PassManagerDefault =
193     !static_cast<unsigned>(LLVM_ENABLE_NEW_PASS_MANAGER);
194 
195 static constexpr const char *PassManagerResetByFlag =
196     LLVM_ENABLE_NEW_PASS_MANAGER ? "-fno-legacy-pass-manager"
197                                  : "-flegacy-pass-manager";
198 
199 static constexpr const char *PassManagerChangedByFlag =
200     LLVM_ENABLE_NEW_PASS_MANAGER ? "-flegacy-pass-manager"
201                                  : "-fno-legacy-pass-manager";
202 
203 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentNone) {
204   const char *Args = {""};
205 
206   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
207 
208   ASSERT_FALSE(Diags->hasErrorOccurred());
209   ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault);
210 
211   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
212 
213   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag))));
214   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag))));
215 }
216 
217 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentChange) {
218   const char *Args[] = {PassManagerChangedByFlag};
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(PassManagerChangedByFlag)));
226   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag))));
227 }
228 
229 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentReset) {
230   const char *Args[] = {PassManagerResetByFlag};
231 
232   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
233   ASSERT_FALSE(Diags->hasErrorOccurred());
234   ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault);
235 
236   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
237   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag))));
238   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag))));
239 }
240 
241 TEST_F(CommandLineTest, CanGenerateCC1CommandLineFlag) {
242   const char *Args[] = {"-fmodules-strict-context-hash"};
243 
244   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
245 
246   ASSERT_FALSE(Diags->hasErrorOccurred());
247 
248   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
249 
250   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash")));
251 }
252 
253 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparate) {
254   const char *TripleCStr = "i686-apple-darwin9";
255   const char *Args[] = {"-triple", TripleCStr};
256 
257   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
258 
259   ASSERT_FALSE(Diags->hasErrorOccurred());
260 
261   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
262 
263   ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr)));
264 }
265 
266 TEST_F(CommandLineTest,  CanGenerateCC1CommandLineSeparateRequiredPresent) {
267   const std::string DefaultTriple =
268       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
269   const char *Args[] = {"-triple", DefaultTriple.c_str()};
270 
271   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
272 
273   ASSERT_FALSE(Diags->hasErrorOccurred());
274 
275   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
276 
277   // Triple should always be emitted even if it is the default
278   ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str())));
279 }
280 
281 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredAbsent) {
282   const std::string DefaultTriple =
283       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
284   const char *Args[] = {""};
285 
286   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
287 
288   ASSERT_FALSE(Diags->hasErrorOccurred());
289 
290   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
291 
292   // Triple should always be emitted even if it is the default
293   ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str())));
294 }
295 
296 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateEnumNonDefault) {
297   const char *Args[] = {"-mrelocation-model", "static"};
298 
299   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
300 
301   ASSERT_FALSE(Diags->hasErrorOccurred());
302 
303   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
304 
305   // Non default relocation model.
306   ASSERT_THAT(GeneratedArgs, Contains(StrEq("static")));
307 }
308 
309 TEST_F(CommandLineTest, CanGenerateCC1COmmandLineSeparateEnumDefault) {
310   const char *Args[] = {"-mrelocation-model", "pic"};
311 
312   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
313 
314   ASSERT_FALSE(Diags->hasErrorOccurred());
315 
316   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
317 
318   // Default relocation model.
319   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("pic"))));
320 }
321 
322 // Tree of boolean options that can be (directly or transitively) implied by
323 // their parent:
324 //
325 //   * -cl-unsafe-math-optimizations
326 //     * -cl-mad-enable
327 //     * -menable-unsafe-fp-math
328 //       * -freciprocal-math
329 
330 TEST_F(CommandLineTest, ImpliedBoolOptionsNoFlagPresent) {
331   const char *Args[] = {""};
332 
333   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
334 
335   ASSERT_FALSE(Diags->hasErrorOccurred());
336   ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath);
337   ASSERT_FALSE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
338   ASSERT_FALSE(Invocation.getLangOpts()->UnsafeFPMath);
339   ASSERT_FALSE(Invocation.getLangOpts()->AllowRecip);
340 
341   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
342 
343   // Not generated - missing.
344   ASSERT_THAT(GeneratedArgs,
345               Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
346   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
347   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
348   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
349 }
350 
351 TEST_F(CommandLineTest, ImpliedBoolOptionsRootFlagPresent) {
352   const char *Args[] = {"-cl-unsafe-math-optimizations"};
353 
354   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
355 
356   ASSERT_FALSE(Diags->hasErrorOccurred());
357   // Explicitly provided root flag.
358   ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath);
359   // Directly implied by explicitly provided root flag.
360   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
361   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
362   // Transitively implied by explicitly provided root flag.
363   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
364 
365   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
366 
367   // Generated - explicitly provided.
368   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations")));
369   // Not generated - implied by the generated root flag.
370   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
371   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
372   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
373 }
374 
375 TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) {
376   const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable",
377                         "-menable-unsafe-fp-math", "-freciprocal-math"};
378 
379   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
380 
381   ASSERT_FALSE(Diags->hasErrorOccurred());
382   ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath);
383   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
384   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
385   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
386 
387   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
388 
389   // Generated - explicitly provided.
390   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations")));
391   // Not generated - implied by their generated parent.
392   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
393   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
394   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
395 }
396 
397 TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) {
398   const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math",
399                         "-freciprocal-math"};
400 
401   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
402   ASSERT_FALSE(Diags->hasErrorOccurred());
403   ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath);
404   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
405   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
406   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
407 
408   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
409   // Not generated - missing.
410   ASSERT_THAT(GeneratedArgs,
411               Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
412   // Generated - explicitly provided.
413   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable")));
414   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math")));
415   // Not generated - implied by its generated parent.
416   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
417 }
418 
419 TEST_F(CommandLineTest, PresentAndNotImpliedGenerated) {
420   const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math"};
421 
422   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
423 
424   ASSERT_FALSE(Diags->hasErrorOccurred());
425 
426   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
427 
428   // Present options that were not implied are generated.
429   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable")));
430   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math")));
431 }
432 } // anonymous namespace
433