| 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 | |