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" |
24 | #include <optional> |
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, DiagOpts, new IgnoringDiagConsumer()), |
36 | SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions) { |
37 | TargetOpts->Triple = "x86_64-apple-macos12" ; |
38 | Target = TargetInfo::CreateTargetInfo(Diags, Opts&: *TargetOpts); |
39 | } |
40 | |
41 | FileSystemOptions FileMgrOpts; |
42 | FileManager FileMgr; |
43 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID; |
44 | DiagnosticOptions DiagOpts; |
45 | DiagnosticsEngine Diags; |
46 | SourceManager SourceMgr; |
47 | LangOptions LangOpts; |
48 | std::shared_ptr<TargetOptions> TargetOpts; |
49 | IntrusiveRefCntPtr<TargetInfo> Target; |
50 | }; |
51 | |
52 | class IncludeCollector : public PPCallbacks { |
53 | public: |
54 | Preprocessor &PP; |
55 | SmallVectorImpl<StringRef> &IncludedFiles; |
56 | |
57 | IncludeCollector(Preprocessor &PP, SmallVectorImpl<StringRef> &IncludedFiles) |
58 | : PP(PP), IncludedFiles(IncludedFiles) {} |
59 | |
60 | void LexedFileChanged(FileID FID, LexedFileChangeReason Reason, |
61 | SrcMgr::CharacteristicKind FileType, FileID PrevFID, |
62 | SourceLocation Loc) override { |
63 | if (Reason != LexedFileChangeReason::EnterFile) |
64 | return; |
65 | if (FID == PP.getPredefinesFileID()) |
66 | return; |
67 | StringRef Filename = |
68 | PP.getSourceManager().getSLocEntry(FID).getFile().getName(); |
69 | IncludedFiles.push_back(Elt: Filename); |
70 | } |
71 | }; |
72 | |
73 | TEST_F(PPDependencyDirectivesTest, MacroGuard) { |
74 | // "head1.h" has a macro guard and should only be included once. |
75 | // "head2.h" and "head3.h" have tokens following the macro check, they should |
76 | // be included multiple times. |
77 | |
78 | auto VFS = new llvm::vfs::InMemoryFileSystem(); |
79 | VFS->addFile( |
80 | Path: "head1.h" , ModificationTime: 0, |
81 | Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "#ifndef H1_H\n#define H1_H\n#endif\n" )); |
82 | VFS->addFile( |
83 | Path: "head2.h" , ModificationTime: 0, |
84 | Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "#ifndef H2_H\n#define H2_H\n#endif\n\n" |
85 | "extern int foo;\n" )); |
86 | VFS->addFile(Path: "head3.h" , ModificationTime: 0, |
87 | Buffer: llvm::MemoryBuffer::getMemBuffer( |
88 | InputData: "#ifndef H3_H\n#define H3_H\n#endif\n\n" |
89 | "#ifdef SOMEMAC\nextern int foo;\n#endif\n" )); |
90 | VFS->addFile(Path: "main.c" , ModificationTime: 0, |
91 | Buffer: llvm::MemoryBuffer::getMemBuffer( |
92 | InputData: "#include \"head1.h\"\n#include \"head1.h\"\n" |
93 | "#include \"head2.h\"\n#include \"head2.h\"\n" |
94 | "#include \"head3.h\"\n#include \"head3.h\"\n" )); |
95 | FileMgr.setVirtualFileSystem(VFS); |
96 | |
97 | OptionalFileEntryRef FE; |
98 | ASSERT_THAT_ERROR(FileMgr.getFileRef("main.c" ).moveInto(FE), |
99 | llvm::Succeeded()); |
100 | SourceMgr.setMainFileID( |
101 | SourceMgr.createFileID(SourceFile: *FE, IncludePos: SourceLocation(), FileCharacter: SrcMgr::C_User)); |
102 | |
103 | struct DepDirectives { |
104 | SmallVector<dependency_directives_scan::Token> Tokens; |
105 | SmallVector<dependency_directives_scan::Directive> Directives; |
106 | }; |
107 | |
108 | class TestDependencyDirectivesGetter : public DependencyDirectivesGetter { |
109 | FileManager &FileMgr; |
110 | SmallVector<std::unique_ptr<DepDirectives>> DepDirectivesObjects; |
111 | |
112 | public: |
113 | TestDependencyDirectivesGetter(FileManager &FileMgr) : FileMgr(FileMgr) {} |
114 | |
115 | std::unique_ptr<DependencyDirectivesGetter> |
116 | cloneFor(FileManager &FileMgr) override { |
117 | return std::make_unique<TestDependencyDirectivesGetter>(args&: FileMgr); |
118 | } |
119 | |
120 | std::optional<ArrayRef<dependency_directives_scan::Directive>> |
121 | operator()(FileEntryRef File) override { |
122 | DepDirectivesObjects.push_back(Elt: std::make_unique<DepDirectives>()); |
123 | StringRef Input = (*FileMgr.getBufferForFile(Entry: File))->getBuffer(); |
124 | bool Err = scanSourceForDependencyDirectives( |
125 | Input, Tokens&: DepDirectivesObjects.back()->Tokens, |
126 | Directives&: DepDirectivesObjects.back()->Directives); |
127 | EXPECT_FALSE(Err); |
128 | return DepDirectivesObjects.back()->Directives; |
129 | } |
130 | }; |
131 | TestDependencyDirectivesGetter GetDependencyDirectives(FileMgr); |
132 | |
133 | PreprocessorOptions PPOpts; |
134 | HeaderSearchOptions HSOpts; |
135 | TrivialModuleLoader ModLoader; |
136 | HeaderSearch (HSOpts, SourceMgr, Diags, LangOpts, Target.get()); |
137 | Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, |
138 | /*IILookup =*/nullptr, |
139 | /*OwnsHeaderSearch =*/false); |
140 | PP.Initialize(Target: *Target); |
141 | |
142 | PP.setDependencyDirectivesGetter(GetDependencyDirectives); |
143 | |
144 | SmallVector<StringRef> IncludedFiles; |
145 | PP.addPPCallbacks(C: std::make_unique<IncludeCollector>(args&: PP, args&: IncludedFiles)); |
146 | PP.EnterMainSourceFile(); |
147 | PP.LexTokensUntilEOF(); |
148 | |
149 | SmallVector<std::string> IncludedFilesSlash; |
150 | for (StringRef IncludedFile : IncludedFiles) |
151 | IncludedFilesSlash.push_back( |
152 | Elt: llvm::sys::path::convert_to_slash(path: IncludedFile)); |
153 | SmallVector<std::string> ExpectedIncludes{ |
154 | "main.c" , "./head1.h" , "./head2.h" , "./head2.h" , "./head3.h" , "./head3.h" , |
155 | }; |
156 | EXPECT_EQ(IncludedFilesSlash, ExpectedIncludes); |
157 | } |
158 | |
159 | } // anonymous namespace |
160 | |