[SCCP] Avoid modifying AdditionalUsers while iterating over it
[llvm-project.git] / clang / unittests / Lex / PPCallbacksTest.cpp
blob5581f9fb82f349c0d562cd8f68c1f5ab55b873d7
1 //===- unittests/Lex/PPCallbacksTest.cpp - PPCallbacks 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 //===--------------------------------------------------------------===//
9 #include "clang/Lex/Preprocessor.h"
10 #include "clang/AST/ASTConsumer.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/Basic/Diagnostic.h"
13 #include "clang/Basic/DiagnosticOptions.h"
14 #include "clang/Basic/FileManager.h"
15 #include "clang/Basic/LangOptions.h"
16 #include "clang/Basic/SourceManager.h"
17 #include "clang/Basic/TargetInfo.h"
18 #include "clang/Basic/TargetOptions.h"
19 #include "clang/Lex/HeaderSearch.h"
20 #include "clang/Lex/HeaderSearchOptions.h"
21 #include "clang/Lex/ModuleLoader.h"
22 #include "clang/Lex/PreprocessorOptions.h"
23 #include "clang/Parse/Parser.h"
24 #include "clang/Sema/Sema.h"
25 #include "llvm/ADT/SmallString.h"
26 #include "llvm/Support/Path.h"
27 #include "gtest/gtest.h"
29 using namespace clang;
31 namespace {
33 // Stub to collect data from InclusionDirective callbacks.
34 class InclusionDirectiveCallbacks : public PPCallbacks {
35 public:
36 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
37 StringRef FileName, bool IsAngled,
38 CharSourceRange FilenameRange, const FileEntry *File,
39 StringRef SearchPath, StringRef RelativePath,
40 const Module *Imported,
41 SrcMgr::CharacteristicKind FileType) override {
42 this->HashLoc = HashLoc;
43 this->IncludeTok = IncludeTok;
44 this->FileName = FileName.str();
45 this->IsAngled = IsAngled;
46 this->FilenameRange = FilenameRange;
47 this->File = File;
48 this->SearchPath = SearchPath.str();
49 this->RelativePath = RelativePath.str();
50 this->Imported = Imported;
51 this->FileType = FileType;
54 SourceLocation HashLoc;
55 Token IncludeTok;
56 SmallString<16> FileName;
57 bool IsAngled;
58 CharSourceRange FilenameRange;
59 const FileEntry* File;
60 SmallString<16> SearchPath;
61 SmallString<16> RelativePath;
62 const Module* Imported;
63 SrcMgr::CharacteristicKind FileType;
66 class CondDirectiveCallbacks : public PPCallbacks {
67 public:
68 struct Result {
69 SourceRange ConditionRange;
70 ConditionValueKind ConditionValue;
72 Result(SourceRange R, ConditionValueKind K)
73 : ConditionRange(R), ConditionValue(K) {}
76 std::vector<Result> Results;
78 void If(SourceLocation Loc, SourceRange ConditionRange,
79 ConditionValueKind ConditionValue) override {
80 Results.emplace_back(ConditionRange, ConditionValue);
83 void Elif(SourceLocation Loc, SourceRange ConditionRange,
84 ConditionValueKind ConditionValue, SourceLocation IfLoc) override {
85 Results.emplace_back(ConditionRange, ConditionValue);
89 // Stub to collect data from PragmaOpenCLExtension callbacks.
90 class PragmaOpenCLExtensionCallbacks : public PPCallbacks {
91 public:
92 typedef struct {
93 SmallString<16> Name;
94 unsigned State;
95 } CallbackParameters;
97 PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {}
99 void PragmaOpenCLExtension(clang::SourceLocation NameLoc,
100 const clang::IdentifierInfo *Name,
101 clang::SourceLocation StateLoc,
102 unsigned State) override {
103 this->NameLoc = NameLoc;
104 this->Name = Name->getName();
105 this->StateLoc = StateLoc;
106 this->State = State;
109 SourceLocation NameLoc;
110 SmallString<16> Name;
111 SourceLocation StateLoc;
112 unsigned State;
115 // PPCallbacks test fixture.
116 class PPCallbacksTest : public ::testing::Test {
117 protected:
118 PPCallbacksTest()
119 : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
120 FileMgr(FileSystemOptions(), InMemoryFileSystem),
121 DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()),
122 Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()),
123 SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions()) {
124 TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
125 Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
128 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
129 FileManager FileMgr;
130 IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
131 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
132 DiagnosticsEngine Diags;
133 SourceManager SourceMgr;
134 LangOptions LangOpts;
135 std::shared_ptr<TargetOptions> TargetOpts;
136 IntrusiveRefCntPtr<TargetInfo> Target;
138 // Register a header path as a known file and add its location
139 // to search path.
140 void AddFakeHeader(HeaderSearch &HeaderInfo, const char *HeaderPath,
141 bool IsSystemHeader) {
142 // Tell FileMgr about header.
143 InMemoryFileSystem->addFile(HeaderPath, 0,
144 llvm::MemoryBuffer::getMemBuffer("\n"));
146 // Add header's parent path to search path.
147 StringRef SearchPath = llvm::sys::path::parent_path(HeaderPath);
148 auto DE = FileMgr.getOptionalDirectoryRef(SearchPath);
149 DirectoryLookup DL(*DE, SrcMgr::C_User, false);
150 HeaderInfo.AddSearchPath(DL, IsSystemHeader);
153 // Get the raw source string of the range.
154 StringRef GetSourceString(CharSourceRange Range) {
155 const char* B = SourceMgr.getCharacterData(Range.getBegin());
156 const char* E = SourceMgr.getCharacterData(Range.getEnd());
158 return StringRef(B, E - B);
161 StringRef GetSourceStringToEnd(CharSourceRange Range) {
162 const char *B = SourceMgr.getCharacterData(Range.getBegin());
163 const char *E = SourceMgr.getCharacterData(Range.getEnd());
165 return StringRef(
167 E - B + Lexer::MeasureTokenLength(Range.getEnd(), SourceMgr, LangOpts));
170 // Run lexer over SourceText and collect FilenameRange from
171 // the InclusionDirective callback.
172 CharSourceRange InclusionDirectiveFilenameRange(const char *SourceText,
173 const char *HeaderPath,
174 bool SystemHeader) {
175 std::unique_ptr<llvm::MemoryBuffer> Buf =
176 llvm::MemoryBuffer::getMemBuffer(SourceText);
177 SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
179 TrivialModuleLoader ModLoader;
181 HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
182 Diags, LangOpts, Target.get());
183 AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
185 Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
186 SourceMgr, HeaderInfo, ModLoader,
187 /*IILookup =*/nullptr,
188 /*OwnsHeaderSearch =*/false);
189 return InclusionDirectiveCallback(PP)->FilenameRange;
192 SrcMgr::CharacteristicKind InclusionDirectiveCharacteristicKind(
193 const char *SourceText, const char *HeaderPath, bool SystemHeader) {
194 std::unique_ptr<llvm::MemoryBuffer> Buf =
195 llvm::MemoryBuffer::getMemBuffer(SourceText);
196 SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
198 TrivialModuleLoader ModLoader;
200 HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
201 Diags, LangOpts, Target.get());
202 AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
204 Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
205 SourceMgr, HeaderInfo, ModLoader,
206 /*IILookup =*/nullptr,
207 /*OwnsHeaderSearch =*/false);
208 return InclusionDirectiveCallback(PP)->FileType;
211 InclusionDirectiveCallbacks *InclusionDirectiveCallback(Preprocessor &PP) {
212 PP.Initialize(*Target);
213 InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks;
214 PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
216 // Lex source text.
217 PP.EnterMainSourceFile();
219 while (true) {
220 Token Tok;
221 PP.Lex(Tok);
222 if (Tok.is(tok::eof))
223 break;
226 // Callbacks have been executed at this point -- return filename range.
227 return Callbacks;
230 std::vector<CondDirectiveCallbacks::Result>
231 DirectiveExprRange(StringRef SourceText) {
232 TrivialModuleLoader ModLoader;
233 std::unique_ptr<llvm::MemoryBuffer> Buf =
234 llvm::MemoryBuffer::getMemBuffer(SourceText);
235 SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
236 HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
237 Diags, LangOpts, Target.get());
238 Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
239 SourceMgr, HeaderInfo, ModLoader,
240 /*IILookup =*/nullptr,
241 /*OwnsHeaderSearch =*/false);
242 PP.Initialize(*Target);
243 auto *Callbacks = new CondDirectiveCallbacks;
244 PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
246 // Lex source text.
247 PP.EnterMainSourceFile();
249 while (true) {
250 Token Tok;
251 PP.Lex(Tok);
252 if (Tok.is(tok::eof))
253 break;
256 return Callbacks->Results;
259 PragmaOpenCLExtensionCallbacks::CallbackParameters
260 PragmaOpenCLExtensionCall(const char *SourceText) {
261 LangOptions OpenCLLangOpts;
262 OpenCLLangOpts.OpenCL = 1;
264 std::unique_ptr<llvm::MemoryBuffer> SourceBuf =
265 llvm::MemoryBuffer::getMemBuffer(SourceText, "test.cl");
266 SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf)));
268 TrivialModuleLoader ModLoader;
269 HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
270 Diags, OpenCLLangOpts, Target.get());
272 Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags,
273 OpenCLLangOpts, SourceMgr, HeaderInfo, ModLoader,
274 /*IILookup =*/nullptr,
275 /*OwnsHeaderSearch =*/false);
276 PP.Initialize(*Target);
278 // parser actually sets correct pragma handlers for preprocessor
279 // according to LangOptions, so we init Parser to register opencl
280 // pragma handlers
281 ASTContext Context(OpenCLLangOpts, SourceMgr, PP.getIdentifierTable(),
282 PP.getSelectorTable(), PP.getBuiltinInfo());
283 Context.InitBuiltinTypes(*Target);
285 ASTConsumer Consumer;
286 Sema S(PP, Context, Consumer);
287 Parser P(PP, S, false);
288 PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks;
289 PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
291 // Lex source text.
292 PP.EnterMainSourceFile();
293 while (true) {
294 Token Tok;
295 PP.Lex(Tok);
296 if (Tok.is(tok::eof))
297 break;
300 PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal = {
301 Callbacks->Name,
302 Callbacks->State
304 return RetVal;
308 TEST_F(PPCallbacksTest, UserFileCharacteristics) {
309 const char *Source = "#include \"quoted.h\"\n";
311 SrcMgr::CharacteristicKind Kind =
312 InclusionDirectiveCharacteristicKind(Source, "/quoted.h", false);
314 ASSERT_EQ(SrcMgr::CharacteristicKind::C_User, Kind);
317 TEST_F(PPCallbacksTest, QuotedFilename) {
318 const char* Source =
319 "#include \"quoted.h\"\n";
321 CharSourceRange Range =
322 InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
324 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
327 TEST_F(PPCallbacksTest, AngledFilename) {
328 const char* Source =
329 "#include <angled.h>\n";
331 CharSourceRange Range =
332 InclusionDirectiveFilenameRange(Source, "/angled.h", true);
334 ASSERT_EQ("<angled.h>", GetSourceString(Range));
337 TEST_F(PPCallbacksTest, QuotedInMacro) {
338 const char* Source =
339 "#define MACRO_QUOTED \"quoted.h\"\n"
340 "#include MACRO_QUOTED\n";
342 CharSourceRange Range =
343 InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
345 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
348 TEST_F(PPCallbacksTest, AngledInMacro) {
349 const char* Source =
350 "#define MACRO_ANGLED <angled.h>\n"
351 "#include MACRO_ANGLED\n";
353 CharSourceRange Range =
354 InclusionDirectiveFilenameRange(Source, "/angled.h", true);
356 ASSERT_EQ("<angled.h>", GetSourceString(Range));
359 TEST_F(PPCallbacksTest, StringizedMacroArgument) {
360 const char* Source =
361 "#define MACRO_STRINGIZED(x) #x\n"
362 "#include MACRO_STRINGIZED(quoted.h)\n";
364 CharSourceRange Range =
365 InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
367 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
370 TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) {
371 const char* Source =
372 "#define MACRO_ANGLED <angled.h>\n"
373 "#define MACRO_CONCAT(x, y) x ## _ ## y\n"
374 "#include MACRO_CONCAT(MACRO, ANGLED)\n";
376 CharSourceRange Range =
377 InclusionDirectiveFilenameRange(Source, "/angled.h", false);
379 ASSERT_EQ("<angled.h>", GetSourceString(Range));
382 TEST_F(PPCallbacksTest, TrigraphFilename) {
383 const char* Source =
384 "#include \"tri\?\?-graph.h\"\n";
386 CharSourceRange Range =
387 InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
389 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
392 TEST_F(PPCallbacksTest, TrigraphInMacro) {
393 const char* Source =
394 "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n"
395 "#include MACRO_TRIGRAPH\n";
397 CharSourceRange Range =
398 InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
400 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
403 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaEnabled) {
404 const char* Source =
405 "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n";
407 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
408 PragmaOpenCLExtensionCall(Source);
410 ASSERT_EQ("cl_khr_fp64", Parameters.Name);
411 unsigned ExpectedState = 1;
412 ASSERT_EQ(ExpectedState, Parameters.State);
415 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) {
416 const char* Source =
417 "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n";
419 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
420 PragmaOpenCLExtensionCall(Source);
422 ASSERT_EQ("cl_khr_fp16", Parameters.Name);
423 unsigned ExpectedState = 0;
424 ASSERT_EQ(ExpectedState, Parameters.State);
427 TEST_F(PPCallbacksTest, DirectiveExprRanges) {
428 const auto &Results1 = DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n");
429 EXPECT_EQ(Results1.size(), 1U);
430 EXPECT_EQ(
431 GetSourceStringToEnd(CharSourceRange(Results1[0].ConditionRange, false)),
432 "FLUZZY_FLOOF");
434 const auto &Results2 = DirectiveExprRange("#if 1 + 4 < 7\n#endif\n");
435 EXPECT_EQ(Results2.size(), 1U);
436 EXPECT_EQ(
437 GetSourceStringToEnd(CharSourceRange(Results2[0].ConditionRange, false)),
438 "1 + 4 < 7");
440 const auto &Results3 = DirectiveExprRange("#if 1 + \\\n 2\n#endif\n");
441 EXPECT_EQ(Results3.size(), 1U);
442 EXPECT_EQ(
443 GetSourceStringToEnd(CharSourceRange(Results3[0].ConditionRange, false)),
444 "1 + \\\n 2");
446 const auto &Results4 = DirectiveExprRange("#if 0\n#elif FLOOFY\n#endif\n");
447 EXPECT_EQ(Results4.size(), 2U);
448 EXPECT_EQ(
449 GetSourceStringToEnd(CharSourceRange(Results4[0].ConditionRange, false)),
450 "0");
451 EXPECT_EQ(
452 GetSourceStringToEnd(CharSourceRange(Results4[1].ConditionRange, false)),
453 "FLOOFY");
455 const auto &Results5 = DirectiveExprRange("#if 1\n#elif FLOOFY\n#endif\n");
456 EXPECT_EQ(Results5.size(), 2U);
457 EXPECT_EQ(
458 GetSourceStringToEnd(CharSourceRange(Results5[0].ConditionRange, false)),
459 "1");
460 EXPECT_EQ(
461 GetSourceStringToEnd(CharSourceRange(Results5[1].ConditionRange, false)),
462 "FLOOFY");
464 const auto &Results6 =
465 DirectiveExprRange("#if defined(FLUZZY_FLOOF)\n#endif\n");
466 EXPECT_EQ(Results6.size(), 1U);
467 EXPECT_EQ(
468 GetSourceStringToEnd(CharSourceRange(Results6[0].ConditionRange, false)),
469 "defined(FLUZZY_FLOOF)");
471 const auto &Results7 =
472 DirectiveExprRange("#if 1\n#elif defined(FLOOFY)\n#endif\n");
473 EXPECT_EQ(Results7.size(), 2U);
474 EXPECT_EQ(
475 GetSourceStringToEnd(CharSourceRange(Results7[0].ConditionRange, false)),
476 "1");
477 EXPECT_EQ(
478 GetSourceStringToEnd(CharSourceRange(Results7[1].ConditionRange, false)),
479 "defined(FLOOFY)");
481 const auto &Results8 =
482 DirectiveExprRange("#define FLOOFY 0\n#if __FILE__ > FLOOFY\n#endif\n");
483 EXPECT_EQ(Results8.size(), 1U);
484 EXPECT_EQ(
485 GetSourceStringToEnd(CharSourceRange(Results8[0].ConditionRange, false)),
486 "__FILE__ > FLOOFY");
487 EXPECT_EQ(
488 Lexer::getSourceText(CharSourceRange(Results8[0].ConditionRange, false),
489 SourceMgr, LangOpts),
490 "__FILE__ > FLOOFY");
493 } // namespace