1 //===- unittests/Lex/PPDependencyDirectivesTest.cpp -------------------------=//
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
7 //===----------------------------------------------------------------------===//
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"
26 using namespace clang
;
31 class PPDependencyDirectivesTest
: public ::testing::Test
{
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
);
41 FileSystemOptions FileMgrOpts
;
43 IntrusiveRefCntPtr
<DiagnosticIDs
> DiagID
;
44 DiagnosticsEngine Diags
;
45 SourceManager SourceMgr
;
47 std::shared_ptr
<TargetOptions
> TargetOpts
;
48 IntrusiveRefCntPtr
<TargetInfo
> Target
;
51 class IncludeCollector
: public PPCallbacks
{
54 SmallVectorImpl
<StringRef
> &IncludedFiles
;
56 IncludeCollector(Preprocessor
&PP
, SmallVectorImpl
<StringRef
> &IncludedFiles
)
57 : PP(PP
), IncludedFiles(IncludedFiles
) {}
59 void LexedFileChanged(FileID FID
, LexedFileChangeReason Reason
,
60 SrcMgr::CharacteristicKind FileType
, FileID PrevFID
,
61 SourceLocation Loc
) override
{
62 if (Reason
!= LexedFileChangeReason::EnterFile
)
64 if (FID
== PP
.getPredefinesFileID())
67 PP
.getSourceManager().getSLocEntry(FID
).getFile().getName();
68 IncludedFiles
.push_back(Filename
);
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.
77 auto VFS
= new llvm::vfs::InMemoryFileSystem();
80 llvm::MemoryBuffer::getMemBuffer("#ifndef H1_H\n#define H1_H\n#endif\n"));
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
);
96 OptionalFileEntryRef FE
;
97 ASSERT_THAT_ERROR(FileMgr
.getFileRef("main.c").moveInto(FE
),
99 SourceMgr
.setMainFileID(
100 SourceMgr
.createFileID(*FE
, SourceLocation(), SrcMgr::C_User
));
102 struct DepDirectives
{
103 SmallVector
<dependency_directives_scan::Token
> Tokens
;
104 SmallVector
<dependency_directives_scan::Directive
> Directives
;
106 SmallVector
<std::unique_ptr
<DepDirectives
>> DepDirectivesObjects
;
108 auto getDependencyDirectives
= [&](FileEntryRef File
)
109 -> std::optional
<ArrayRef
<dependency_directives_scan::Directive
>> {
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
);
116 return llvm::ArrayRef(DepDirectivesObjects
.back()->Directives
);
119 auto PPOpts
= std::make_shared
<PreprocessorOptions
>();
120 PPOpts
->DependencyDirectivesForFile
= [&](FileEntryRef File
)
121 -> std::optional
<ArrayRef
<dependency_directives_scan::Directive
>> {
122 return getDependencyDirectives(File
);
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
);
133 SmallVector
<StringRef
> IncludedFiles
;
134 PP
.addPPCallbacks(std::make_unique
<IncludeCollector
>(PP
, IncludedFiles
));
135 PP
.EnterMainSourceFile();
136 PP
.LexTokensUntilEOF();
138 SmallVector
<std::string
> IncludedFilesSlash
;
139 for (StringRef IncludedFile
: IncludedFiles
)
140 IncludedFilesSlash
.push_back(
141 llvm::sys::path::convert_to_slash(IncludedFile
));
142 SmallVector
<std::string
> ExpectedIncludes
{
143 "main.c", "./head1.h", "./head2.h", "./head2.h", "./head3.h", "./head3.h",
145 EXPECT_EQ(IncludedFilesSlash
, ExpectedIncludes
);
148 } // anonymous namespace