|  | //===- unittests/Lex/PPDependencyDirectivesTest.cpp -------------------------=// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/Basic/Diagnostic.h" | 
|  | #include "clang/Basic/DiagnosticOptions.h" | 
|  | #include "clang/Basic/FileManager.h" | 
|  | #include "clang/Basic/LangOptions.h" | 
|  | #include "clang/Basic/SourceManager.h" | 
|  | #include "clang/Basic/TargetInfo.h" | 
|  | #include "clang/Basic/TargetOptions.h" | 
|  | #include "clang/Lex/DependencyDirectivesScanner.h" | 
|  | #include "clang/Lex/HeaderSearch.h" | 
|  | #include "clang/Lex/HeaderSearchOptions.h" | 
|  | #include "clang/Lex/ModuleLoader.h" | 
|  | #include "clang/Lex/Preprocessor.h" | 
|  | #include "clang/Lex/PreprocessorOptions.h" | 
|  | #include "llvm/Testing/Support/Error.h" | 
|  | #include "gtest/gtest.h" | 
|  | #include <optional> | 
|  |  | 
|  | using namespace clang; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // The test fixture. | 
|  | class PPDependencyDirectivesTest : public ::testing::Test { | 
|  | protected: | 
|  | PPDependencyDirectivesTest() | 
|  | : FileMgr(FileMgrOpts), | 
|  | Diags(DiagnosticIDs::create(), DiagOpts, new IgnoringDiagConsumer()), | 
|  | SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions) { | 
|  | TargetOpts->Triple = "x86_64-apple-macos12"; | 
|  | Target = TargetInfo::CreateTargetInfo(Diags, *TargetOpts); | 
|  | } | 
|  |  | 
|  | FileSystemOptions FileMgrOpts; | 
|  | FileManager FileMgr; | 
|  | DiagnosticOptions DiagOpts; | 
|  | DiagnosticsEngine Diags; | 
|  | SourceManager SourceMgr; | 
|  | LangOptions LangOpts; | 
|  | std::shared_ptr<TargetOptions> TargetOpts; | 
|  | IntrusiveRefCntPtr<TargetInfo> Target; | 
|  | }; | 
|  |  | 
|  | class IncludeCollector : public PPCallbacks { | 
|  | public: | 
|  | Preprocessor &PP; | 
|  | SmallVectorImpl<StringRef> &IncludedFiles; | 
|  |  | 
|  | IncludeCollector(Preprocessor &PP, SmallVectorImpl<StringRef> &IncludedFiles) | 
|  | : PP(PP), IncludedFiles(IncludedFiles) {} | 
|  |  | 
|  | void LexedFileChanged(FileID FID, LexedFileChangeReason Reason, | 
|  | SrcMgr::CharacteristicKind FileType, FileID PrevFID, | 
|  | SourceLocation Loc) override { | 
|  | if (Reason != LexedFileChangeReason::EnterFile) | 
|  | return; | 
|  | if (FID == PP.getPredefinesFileID()) | 
|  | return; | 
|  | StringRef Filename = | 
|  | PP.getSourceManager().getSLocEntry(FID).getFile().getName(); | 
|  | IncludedFiles.push_back(Filename); | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST_F(PPDependencyDirectivesTest, MacroGuard) { | 
|  | // "head1.h" has a macro guard and should only be included once. | 
|  | // "head2.h" and "head3.h" have tokens following the macro check, they should | 
|  | // be included multiple times. | 
|  |  | 
|  | auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); | 
|  | VFS->addFile( | 
|  | "head1.h", 0, | 
|  | llvm::MemoryBuffer::getMemBuffer("#ifndef H1_H\n#define H1_H\n#endif\n")); | 
|  | VFS->addFile( | 
|  | "head2.h", 0, | 
|  | llvm::MemoryBuffer::getMemBuffer("#ifndef H2_H\n#define H2_H\n#endif\n\n" | 
|  | "extern int foo;\n")); | 
|  | VFS->addFile("head3.h", 0, | 
|  | llvm::MemoryBuffer::getMemBuffer( | 
|  | "#ifndef H3_H\n#define H3_H\n#endif\n\n" | 
|  | "#ifdef SOMEMAC\nextern int foo;\n#endif\n")); | 
|  | VFS->addFile("main.c", 0, | 
|  | llvm::MemoryBuffer::getMemBuffer( | 
|  | "#include \"head1.h\"\n#include \"head1.h\"\n" | 
|  | "#include \"head2.h\"\n#include \"head2.h\"\n" | 
|  | "#include \"head3.h\"\n#include \"head3.h\"\n")); | 
|  | FileMgr.setVirtualFileSystem(VFS); | 
|  |  | 
|  | OptionalFileEntryRef FE; | 
|  | ASSERT_THAT_ERROR(FileMgr.getFileRef("main.c").moveInto(FE), | 
|  | llvm::Succeeded()); | 
|  | SourceMgr.setMainFileID( | 
|  | SourceMgr.createFileID(*FE, SourceLocation(), SrcMgr::C_User)); | 
|  |  | 
|  | struct DepDirectives { | 
|  | SmallVector<dependency_directives_scan::Token> Tokens; | 
|  | SmallVector<dependency_directives_scan::Directive> Directives; | 
|  | }; | 
|  |  | 
|  | class TestDependencyDirectivesGetter : public DependencyDirectivesGetter { | 
|  | FileManager &FileMgr; | 
|  | SmallVector<std::unique_ptr<DepDirectives>> DepDirectivesObjects; | 
|  |  | 
|  | public: | 
|  | TestDependencyDirectivesGetter(FileManager &FileMgr) : FileMgr(FileMgr) {} | 
|  |  | 
|  | std::unique_ptr<DependencyDirectivesGetter> | 
|  | cloneFor(FileManager &FileMgr) override { | 
|  | return std::make_unique<TestDependencyDirectivesGetter>(FileMgr); | 
|  | } | 
|  |  | 
|  | std::optional<ArrayRef<dependency_directives_scan::Directive>> | 
|  | operator()(FileEntryRef File) override { | 
|  | DepDirectivesObjects.push_back(std::make_unique<DepDirectives>()); | 
|  | StringRef Input = (*FileMgr.getBufferForFile(File))->getBuffer(); | 
|  | bool Err = scanSourceForDependencyDirectives( | 
|  | Input, DepDirectivesObjects.back()->Tokens, | 
|  | DepDirectivesObjects.back()->Directives); | 
|  | EXPECT_FALSE(Err); | 
|  | return DepDirectivesObjects.back()->Directives; | 
|  | } | 
|  | }; | 
|  | TestDependencyDirectivesGetter GetDependencyDirectives(FileMgr); | 
|  |  | 
|  | PreprocessorOptions PPOpts; | 
|  | HeaderSearchOptions HSOpts; | 
|  | TrivialModuleLoader ModLoader; | 
|  | HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get()); | 
|  | Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, | 
|  | /*IILookup =*/nullptr, | 
|  | /*OwnsHeaderSearch =*/false); | 
|  | PP.Initialize(*Target); | 
|  |  | 
|  | PP.setDependencyDirectivesGetter(GetDependencyDirectives); | 
|  |  | 
|  | SmallVector<StringRef> IncludedFiles; | 
|  | PP.addPPCallbacks(std::make_unique<IncludeCollector>(PP, IncludedFiles)); | 
|  | PP.EnterMainSourceFile(); | 
|  | PP.LexTokensUntilEOF(); | 
|  |  | 
|  | SmallVector<std::string> IncludedFilesSlash; | 
|  | for (StringRef IncludedFile : IncludedFiles) | 
|  | IncludedFilesSlash.push_back( | 
|  | llvm::sys::path::convert_to_slash(IncludedFile)); | 
|  | SmallVector<std::string> ExpectedIncludes{ | 
|  | "main.c", "./head1.h", "./head2.h", "./head2.h", "./head3.h", "./head3.h", | 
|  | }; | 
|  | EXPECT_EQ(IncludedFilesSlash, ExpectedIncludes); | 
|  | } | 
|  |  | 
|  | } // anonymous namespace |