236
|
1 //===- unittests/Lex/PPDependencyDirectivesTest.cpp -------------------------=//
|
|
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/Basic/Diagnostic.h"
|
|
10 #include "clang/Basic/DiagnosticOptions.h"
|
|
11 #include "clang/Basic/FileManager.h"
|
|
12 #include "clang/Basic/LangOptions.h"
|
|
13 #include "clang/Basic/SourceManager.h"
|
|
14 #include "clang/Basic/TargetInfo.h"
|
|
15 #include "clang/Basic/TargetOptions.h"
|
|
16 #include "clang/Lex/DependencyDirectivesScanner.h"
|
|
17 #include "clang/Lex/HeaderSearch.h"
|
|
18 #include "clang/Lex/HeaderSearchOptions.h"
|
|
19 #include "clang/Lex/ModuleLoader.h"
|
|
20 #include "clang/Lex/Preprocessor.h"
|
|
21 #include "clang/Lex/PreprocessorOptions.h"
|
|
22 #include "llvm/Testing/Support/Error.h"
|
|
23 #include "gtest/gtest.h"
|
252
|
24 #include <optional>
|
236
|
25
|
|
26 using namespace clang;
|
|
27
|
|
28 namespace {
|
|
29
|
|
30 // The test fixture.
|
|
31 class PPDependencyDirectivesTest : public ::testing::Test {
|
|
32 protected:
|
|
33 PPDependencyDirectivesTest()
|
|
34 : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()),
|
|
35 Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()),
|
|
36 SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions) {
|
|
37 TargetOpts->Triple = "x86_64-apple-macos12";
|
|
38 Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
|
|
39 }
|
|
40
|
|
41 FileSystemOptions FileMgrOpts;
|
|
42 FileManager FileMgr;
|
|
43 IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
|
|
44 DiagnosticsEngine Diags;
|
|
45 SourceManager SourceMgr;
|
|
46 LangOptions LangOpts;
|
|
47 std::shared_ptr<TargetOptions> TargetOpts;
|
|
48 IntrusiveRefCntPtr<TargetInfo> Target;
|
|
49 };
|
|
50
|
|
51 class IncludeCollector : public PPCallbacks {
|
|
52 public:
|
|
53 Preprocessor &PP;
|
|
54 SmallVectorImpl<StringRef> &IncludedFiles;
|
|
55
|
|
56 IncludeCollector(Preprocessor &PP, SmallVectorImpl<StringRef> &IncludedFiles)
|
|
57 : PP(PP), IncludedFiles(IncludedFiles) {}
|
|
58
|
|
59 void LexedFileChanged(FileID FID, LexedFileChangeReason Reason,
|
|
60 SrcMgr::CharacteristicKind FileType, FileID PrevFID,
|
|
61 SourceLocation Loc) override {
|
|
62 if (Reason != LexedFileChangeReason::EnterFile)
|
|
63 return;
|
|
64 if (FID == PP.getPredefinesFileID())
|
|
65 return;
|
|
66 StringRef Filename =
|
|
67 PP.getSourceManager().getSLocEntry(FID).getFile().getName();
|
|
68 IncludedFiles.push_back(Filename);
|
|
69 }
|
|
70 };
|
|
71
|
|
72 TEST_F(PPDependencyDirectivesTest, MacroGuard) {
|
|
73 // "head1.h" has a macro guard and should only be included once.
|
|
74 // "head2.h" and "head3.h" have tokens following the macro check, they should
|
|
75 // be included multiple times.
|
|
76
|
|
77 auto VFS = new llvm::vfs::InMemoryFileSystem();
|
|
78 VFS->addFile(
|
|
79 "head1.h", 0,
|
|
80 llvm::MemoryBuffer::getMemBuffer("#ifndef H1_H\n#define H1_H\n#endif\n"));
|
|
81 VFS->addFile(
|
|
82 "head2.h", 0,
|
|
83 llvm::MemoryBuffer::getMemBuffer("#ifndef H2_H\n#define H2_H\n#endif\n\n"
|
|
84 "extern int foo;\n"));
|
|
85 VFS->addFile("head3.h", 0,
|
|
86 llvm::MemoryBuffer::getMemBuffer(
|
|
87 "#ifndef H3_H\n#define H3_H\n#endif\n\n"
|
|
88 "#ifdef SOMEMAC\nextern int foo;\n#endif\n"));
|
|
89 VFS->addFile("main.c", 0,
|
|
90 llvm::MemoryBuffer::getMemBuffer(
|
|
91 "#include \"head1.h\"\n#include \"head1.h\"\n"
|
|
92 "#include \"head2.h\"\n#include \"head2.h\"\n"
|
|
93 "#include \"head3.h\"\n#include \"head3.h\"\n"));
|
|
94 FileMgr.setVirtualFileSystem(VFS);
|
|
95
|
252
|
96 OptionalFileEntryRef FE;
|
236
|
97 ASSERT_THAT_ERROR(FileMgr.getFileRef("main.c").moveInto(FE),
|
|
98 llvm::Succeeded());
|
|
99 SourceMgr.setMainFileID(
|
|
100 SourceMgr.createFileID(*FE, SourceLocation(), SrcMgr::C_User));
|
|
101
|
|
102 struct DepDirectives {
|
|
103 SmallVector<dependency_directives_scan::Token> Tokens;
|
|
104 SmallVector<dependency_directives_scan::Directive> Directives;
|
|
105 };
|
|
106 SmallVector<std::unique_ptr<DepDirectives>> DepDirectivesObjects;
|
|
107
|
|
108 auto getDependencyDirectives = [&](FileEntryRef File)
|
252
|
109 -> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
|
236
|
110 DepDirectivesObjects.push_back(std::make_unique<DepDirectives>());
|
|
111 StringRef Input = (*FileMgr.getBufferForFile(File))->getBuffer();
|
|
112 bool Err = scanSourceForDependencyDirectives(
|
|
113 Input, DepDirectivesObjects.back()->Tokens,
|
|
114 DepDirectivesObjects.back()->Directives);
|
|
115 EXPECT_FALSE(Err);
|
252
|
116 return llvm::ArrayRef(DepDirectivesObjects.back()->Directives);
|
236
|
117 };
|
|
118
|
|
119 auto PPOpts = std::make_shared<PreprocessorOptions>();
|
|
120 PPOpts->DependencyDirectivesForFile = [&](FileEntryRef File)
|
252
|
121 -> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
|
236
|
122 return getDependencyDirectives(File);
|
|
123 };
|
|
124
|
|
125 TrivialModuleLoader ModLoader;
|
|
126 HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
|
|
127 Diags, LangOpts, Target.get());
|
|
128 Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader,
|
|
129 /*IILookup =*/nullptr,
|
|
130 /*OwnsHeaderSearch =*/false);
|
|
131 PP.Initialize(*Target);
|
|
132
|
|
133 SmallVector<StringRef> IncludedFiles;
|
|
134 PP.addPPCallbacks(std::make_unique<IncludeCollector>(PP, IncludedFiles));
|
|
135 PP.EnterMainSourceFile();
|
|
136 while (true) {
|
|
137 Token tok;
|
|
138 PP.Lex(tok);
|
|
139 if (tok.is(tok::eof))
|
|
140 break;
|
|
141 }
|
|
142
|
|
143 SmallVector<StringRef> ExpectedIncludes{
|
|
144 "main.c", "./head1.h", "./head2.h", "./head2.h", "./head3.h", "./head3.h",
|
|
145 };
|
|
146 EXPECT_EQ(IncludedFiles, ExpectedIncludes);
|
|
147 }
|
|
148
|
|
149 } // anonymous namespace
|