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