159 lines · cpp
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-exception6//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 26using namespace clang;27 28namespace {29 30// The test fixture.31class PPDependencyDirectivesTest : public ::testing::Test {32protected:33 PPDependencyDirectivesTest()34 : FileMgr(FileMgrOpts),35 Diags(DiagnosticIDs::create(), DiagOpts, 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 DiagnosticOptions DiagOpts;44 DiagnosticsEngine Diags;45 SourceManager SourceMgr;46 LangOptions LangOpts;47 std::shared_ptr<TargetOptions> TargetOpts;48 IntrusiveRefCntPtr<TargetInfo> Target;49};50 51class IncludeCollector : public PPCallbacks {52public: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 72TEST_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 should75 // be included multiple times.76 77 auto VFS = llvm::makeIntrusiveRefCnt<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 96 OptionalFileEntryRef FE;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 107 class TestDependencyDirectivesGetter : public DependencyDirectivesGetter {108 FileManager &FileMgr;109 SmallVector<std::unique_ptr<DepDirectives>> DepDirectivesObjects;110 111 public:112 TestDependencyDirectivesGetter(FileManager &FileMgr) : FileMgr(FileMgr) {}113 114 std::unique_ptr<DependencyDirectivesGetter>115 cloneFor(FileManager &FileMgr) override {116 return std::make_unique<TestDependencyDirectivesGetter>(FileMgr);117 }118 119 std::optional<ArrayRef<dependency_directives_scan::Directive>>120 operator()(FileEntryRef File) override {121 DepDirectivesObjects.push_back(std::make_unique<DepDirectives>());122 StringRef Input = (*FileMgr.getBufferForFile(File))->getBuffer();123 bool Err = scanSourceForDependencyDirectives(124 Input, DepDirectivesObjects.back()->Tokens,125 DepDirectivesObjects.back()->Directives);126 EXPECT_FALSE(Err);127 return DepDirectivesObjects.back()->Directives;128 }129 };130 TestDependencyDirectivesGetter GetDependencyDirectives(FileMgr);131 132 PreprocessorOptions PPOpts;133 HeaderSearchOptions HSOpts;134 TrivialModuleLoader ModLoader;135 HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get());136 Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader,137 /*IILookup =*/nullptr,138 /*OwnsHeaderSearch =*/false);139 PP.Initialize(*Target);140 141 PP.setDependencyDirectivesGetter(GetDependencyDirectives);142 143 SmallVector<StringRef> IncludedFiles;144 PP.addPPCallbacks(std::make_unique<IncludeCollector>(PP, IncludedFiles));145 PP.EnterMainSourceFile();146 PP.LexTokensUntilEOF();147 148 SmallVector<std::string> IncludedFilesSlash;149 for (StringRef IncludedFile : IncludedFiles)150 IncludedFilesSlash.push_back(151 llvm::sys::path::convert_to_slash(IncludedFile));152 SmallVector<std::string> ExpectedIncludes{153 "main.c", "./head1.h", "./head2.h", "./head2.h", "./head3.h", "./head3.h",154 };155 EXPECT_EQ(IncludedFilesSlash, ExpectedIncludes);156}157 158} // anonymous namespace159