diff options
Diffstat (limited to 'unittests/Frontend')
| -rw-r--r-- | unittests/Frontend/ASTUnitTest.cpp | 87 | ||||
| -rw-r--r-- | unittests/Frontend/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | unittests/Frontend/CompilerInstanceTest.cpp | 74 | ||||
| -rw-r--r-- | unittests/Frontend/PCHPreambleTest.cpp | 200 | ||||
| -rw-r--r-- | unittests/Frontend/ParsedSourceLocationTest.cpp | 37 |
5 files changed, 403 insertions, 0 deletions
diff --git a/unittests/Frontend/ASTUnitTest.cpp b/unittests/Frontend/ASTUnitTest.cpp new file mode 100644 index 0000000000000..4f529cf55de98 --- /dev/null +++ b/unittests/Frontend/ASTUnitTest.cpp @@ -0,0 +1,87 @@ +//===- unittests/Frontend/ASTUnitTest.cpp - ASTUnit tests -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <fstream> + +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/PCHContainerOperations.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ToolOutputFile.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { + +TEST(ASTUnit, SaveLoadPreservesLangOptionsInPrintingPolicy) { + // Check that the printing policy is restored with the correct language + // options when loading an ASTUnit from a file. To this end, an ASTUnit + // for a C++ translation unit is set up and written to a temporary file. + + // By default `UseVoidForZeroParams` is true for non-C++ language options, + // thus we can check this field after loading the ASTUnit to deduce whether + // the correct (C++) language options were used when setting up the printing + // policy. + + { + PrintingPolicy PolicyWithDefaultLangOpt(LangOptions{}); + EXPECT_TRUE(PolicyWithDefaultLangOpt.UseVoidForZeroParams); + } + + int FD; + llvm::SmallString<256> InputFileName; + ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("ast-unit", "cpp", FD, InputFileName)); + ToolOutputFile input_file(InputFileName, FD); + input_file.os() << ""; + + const char* Args[] = {"clang", "-xc++", InputFileName.c_str()}; + + IntrusiveRefCntPtr<DiagnosticsEngine> Diags = + CompilerInstance::createDiagnostics(new DiagnosticOptions()); + + std::shared_ptr<CompilerInvocation> CInvok = + createInvocationFromCommandLine(Args, Diags); + + if (!CInvok) + FAIL() << "could not create compiler invocation"; + + FileManager *FileMgr = + new FileManager(FileSystemOptions(), vfs::getRealFileSystem()); + auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); + + std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation( + CInvok, PCHContainerOps, Diags, FileMgr); + + if (!AST) + FAIL() << "failed to create ASTUnit"; + + EXPECT_FALSE(AST->getASTContext().getPrintingPolicy().UseVoidForZeroParams); + + llvm::SmallString<256> ASTFileName; + ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("ast-unit", "ast", FD, ASTFileName)); + ToolOutputFile ast_file(ASTFileName, FD); + AST->Save(ASTFileName.str()); + + EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName)); + + std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile( + ASTFileName.str(), PCHContainerOps->getRawReader(), ASTUnit::LoadEverything, Diags, + FileSystemOptions(), /*UseDebugInfo=*/false); + + if (!AU) + FAIL() << "failed to load ASTUnit"; + + EXPECT_FALSE(AU->getASTContext().getPrintingPolicy().UseVoidForZeroParams); +} + +} // anonymous namespace diff --git a/unittests/Frontend/CMakeLists.txt b/unittests/Frontend/CMakeLists.txt index 674f77bd0135c..f3c4336ea22fa 100644 --- a/unittests/Frontend/CMakeLists.txt +++ b/unittests/Frontend/CMakeLists.txt @@ -3,10 +3,15 @@ set(LLVM_LINK_COMPONENTS ) add_clang_unittest(FrontendTests + ASTUnitTest.cpp + CompilerInstanceTest.cpp FrontendActionTest.cpp CodeGenActionTest.cpp + ParsedSourceLocationTest.cpp + PCHPreambleTest.cpp ) target_link_libraries(FrontendTests + PRIVATE clangAST clangBasic clangFrontend diff --git a/unittests/Frontend/CompilerInstanceTest.cpp b/unittests/Frontend/CompilerInstanceTest.cpp new file mode 100644 index 0000000000000..b2d9f8bcf00e4 --- /dev/null +++ b/unittests/Frontend/CompilerInstanceTest.cpp @@ -0,0 +1,74 @@ +//===- unittests/Frontend/CompilerInstanceTest.cpp - CI tests -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ToolOutputFile.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { + +TEST(CompilerInstance, DefaultVFSOverlayFromInvocation) { + // Create a temporary VFS overlay yaml file. + int FD; + SmallString<256> FileName; + ASSERT_FALSE(sys::fs::createTemporaryFile("vfs", "yaml", FD, FileName)); + ToolOutputFile File(FileName, FD); + + SmallString<256> CurrentPath; + sys::fs::current_path(CurrentPath); + sys::fs::make_absolute(CurrentPath, FileName); + + // Mount the VFS file itself on the path 'virtual.file'. Makes this test + // a bit shorter than creating a new dummy file just for this purpose. + const std::string CurrentPathStr = CurrentPath.str(); + const std::string FileNameStr = FileName.str(); + const char *VFSYaml = "{ 'version': 0, 'roots': [\n" + " { 'name': '%s',\n" + " 'type': 'directory',\n" + " 'contents': [\n" + " { 'name': 'vfs-virtual.file', 'type': 'file',\n" + " 'external-contents': '%s'\n" + " }\n" + " ]\n" + " }\n" + "]}\n"; + File.os() << format(VFSYaml, CurrentPathStr.c_str(), FileName.c_str()); + File.os().flush(); + + // Create a CompilerInvocation that uses this overlay file. + const std::string VFSArg = "-ivfsoverlay" + FileNameStr; + const char *Args[] = {"clang", VFSArg.c_str(), "-xc++", "-"}; + + IntrusiveRefCntPtr<DiagnosticsEngine> Diags = + CompilerInstance::createDiagnostics(new DiagnosticOptions()); + + std::shared_ptr<CompilerInvocation> CInvok = + createInvocationFromCommandLine(Args, Diags); + + if (!CInvok) + FAIL() << "could not create compiler invocation"; + // Create a minimal CompilerInstance which should use the VFS we specified + // in the CompilerInvocation (as we don't explicitly set our own). + CompilerInstance Instance; + Instance.setDiagnostics(Diags.get()); + Instance.setInvocation(CInvok); + Instance.createFileManager(); + + // Check if the virtual file exists which means that our VFS is used by the + // CompilerInstance. + ASSERT_TRUE(Instance.getFileManager().getFile("vfs-virtual.file")); +} + +} // anonymous namespace diff --git a/unittests/Frontend/PCHPreambleTest.cpp b/unittests/Frontend/PCHPreambleTest.cpp new file mode 100644 index 0000000000000..162a281b04d49 --- /dev/null +++ b/unittests/Frontend/PCHPreambleTest.cpp @@ -0,0 +1,200 @@ +//====-- unittests/Frontend/PCHPreambleTest.cpp - FrontendAction tests ---====// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { + +class ReadCountingInMemoryFileSystem : public vfs::InMemoryFileSystem +{ + std::map<std::string, unsigned> ReadCounts; + +public: + ErrorOr<std::unique_ptr<vfs::File>> openFileForRead(const Twine &Path) override + { + SmallVector<char, 128> PathVec; + Path.toVector(PathVec); + llvm::sys::path::remove_dots(PathVec, true); + ++ReadCounts[std::string(PathVec.begin(), PathVec.end())]; + return InMemoryFileSystem::openFileForRead(Path); + } + + unsigned GetReadCount(const Twine &Path) const + { + auto it = ReadCounts.find(Path.str()); + return it == ReadCounts.end() ? 0 : it->second; + } +}; + +class PCHPreambleTest : public ::testing::Test { + IntrusiveRefCntPtr<ReadCountingInMemoryFileSystem> VFS; + StringMap<std::string> RemappedFiles; + std::shared_ptr<PCHContainerOperations> PCHContainerOpts; + FileSystemOptions FSOpts; + +public: + void SetUp() override { + VFS = new ReadCountingInMemoryFileSystem(); + // We need the working directory to be set to something absolute, + // otherwise it ends up being inadvertently set to the current + // working directory in the real file system due to a series of + // unfortunate conditions interacting badly. + // What's more, this path *must* be absolute on all (real) + // filesystems, so just '/' won't work (e.g. on Win32). + VFS->setCurrentWorkingDirectory("//./"); + } + + void TearDown() override { + } + + void AddFile(const std::string &Filename, const std::string &Contents) { + ::time_t now; + ::time(&now); + VFS->addFile(Filename, now, MemoryBuffer::getMemBufferCopy(Contents, Filename)); + } + + void RemapFile(const std::string &Filename, const std::string &Contents) { + RemappedFiles[Filename] = Contents; + } + + std::unique_ptr<ASTUnit> ParseAST(const std::string &EntryFile) { + PCHContainerOpts = std::make_shared<PCHContainerOperations>(); + std::shared_ptr<CompilerInvocation> CI(new CompilerInvocation); + CI->getFrontendOpts().Inputs.push_back( + FrontendInputFile(EntryFile, FrontendOptions::getInputKindForExtension( + llvm::sys::path::extension(EntryFile).substr(1)))); + + CI->getTargetOpts().Triple = "i386-unknown-linux-gnu"; + + CI->getPreprocessorOpts().RemappedFileBuffers = GetRemappedFiles(); + + PreprocessorOptions &PPOpts = CI->getPreprocessorOpts(); + PPOpts.RemappedFilesKeepOriginalName = true; + + IntrusiveRefCntPtr<DiagnosticsEngine> + Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions, new DiagnosticConsumer)); + + FileManager *FileMgr = new FileManager(FSOpts, VFS); + + std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation( + CI, PCHContainerOpts, Diags, FileMgr, false, false, + /*PrecompilePreambleAfterNParses=*/1); + return AST; + } + + bool ReparseAST(const std::unique_ptr<ASTUnit> &AST) { + bool reparseFailed = AST->Reparse(PCHContainerOpts, GetRemappedFiles(), VFS); + return !reparseFailed; + } + + unsigned GetFileReadCount(const std::string &Filename) const { + return VFS->GetReadCount(Filename); + } + +private: + std::vector<std::pair<std::string, llvm::MemoryBuffer *>> + GetRemappedFiles() const { + std::vector<std::pair<std::string, llvm::MemoryBuffer *>> Remapped; + for (const auto &RemappedFile : RemappedFiles) { + std::unique_ptr<MemoryBuffer> buf = MemoryBuffer::getMemBufferCopy( + RemappedFile.second, RemappedFile.first()); + Remapped.emplace_back(RemappedFile.first(), buf.release()); + } + return Remapped; + } +}; + +TEST_F(PCHPreambleTest, ReparseWithOverriddenFileDoesNotInvalidatePreamble) { + std::string Header1 = "//./header1.h"; + std::string Header2 = "//./header2.h"; + std::string MainName = "//./main.cpp"; + AddFile(Header1, ""); + AddFile(Header2, "#pragma once"); + AddFile(MainName, + "#include \"//./foo/../header1.h\"\n" + "#include \"//./foo/../header2.h\"\n" + "int main() { return ZERO; }"); + RemapFile(Header1, "static const int ZERO = 0;\n"); + + std::unique_ptr<ASTUnit> AST(ParseAST(MainName)); + ASSERT_TRUE(AST.get()); + ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); + + unsigned initialCounts[] = { + GetFileReadCount(MainName), + GetFileReadCount(Header1), + GetFileReadCount(Header2) + }; + + ASSERT_TRUE(ReparseAST(AST)); + + ASSERT_NE(initialCounts[0], GetFileReadCount(MainName)); + ASSERT_EQ(initialCounts[1], GetFileReadCount(Header1)); + ASSERT_EQ(initialCounts[2], GetFileReadCount(Header2)); +} + +TEST_F(PCHPreambleTest, ParseWithBom) { + std::string Header = "//./header.h"; + std::string Main = "//./main.cpp"; + AddFile(Header, "int random() { return 4; }"); + AddFile(Main, + "\xef\xbb\xbf" + "#include \"//./header.h\"\n" + "int main() { return random() -2; }"); + + std::unique_ptr<ASTUnit> AST(ParseAST(Main)); + ASSERT_TRUE(AST.get()); + ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); + + unsigned HeaderReadCount = GetFileReadCount(Header); + + ASSERT_TRUE(ReparseAST(AST)); + ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); + + // Check preamble PCH was really reused + ASSERT_EQ(HeaderReadCount, GetFileReadCount(Header)); + + // Remove BOM + RemapFile(Main, + "#include \"//./header.h\"\n" + "int main() { return random() -2; }"); + + ASSERT_TRUE(ReparseAST(AST)); + ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); + + ASSERT_LE(HeaderReadCount, GetFileReadCount(Header)); + HeaderReadCount = GetFileReadCount(Header); + + // Add BOM back + RemapFile(Main, + "\xef\xbb\xbf" + "#include \"//./header.h\"\n" + "int main() { return random() -2; }"); + + ASSERT_TRUE(ReparseAST(AST)); + ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); + + ASSERT_LE(HeaderReadCount, GetFileReadCount(Header)); +} + +} // anonymous namespace diff --git a/unittests/Frontend/ParsedSourceLocationTest.cpp b/unittests/Frontend/ParsedSourceLocationTest.cpp new file mode 100644 index 0000000000000..0cbdc7e1d5a24 --- /dev/null +++ b/unittests/Frontend/ParsedSourceLocationTest.cpp @@ -0,0 +1,37 @@ +//===- unittests/Frontend/ParsedSourceLocationTest.cpp - ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CommandLineSourceLoc.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { + +TEST(ParsedSourceRange, ParseTest) { + auto Check = [](StringRef Value, StringRef Filename, unsigned BeginLine, + unsigned BeginColumn, unsigned EndLine, unsigned EndColumn) { + Optional<ParsedSourceRange> PSR = ParsedSourceRange::fromString(Value); + ASSERT_TRUE(PSR); + EXPECT_EQ(PSR->FileName, Filename); + EXPECT_EQ(PSR->Begin.first, BeginLine); + EXPECT_EQ(PSR->Begin.second, BeginColumn); + EXPECT_EQ(PSR->End.first, EndLine); + EXPECT_EQ(PSR->End.second, EndColumn); + }; + + Check("/Users/test/a-b.cpp:1:2", "/Users/test/a-b.cpp", 1, 2, 1, 2); + Check("/Users/test/a-b.cpp:1:2-3:4", "/Users/test/a-b.cpp", 1, 2, 3, 4); + + Check("C:/Users/bob/a-b.cpp:1:2", "C:/Users/bob/a-b.cpp", 1, 2, 1, 2); + Check("C:/Users/bob/a-b.cpp:1:2-3:4", "C:/Users/bob/a-b.cpp", 1, 2, 3, 4); +} + +} // anonymous namespace |
