aboutsummaryrefslogtreecommitdiff
path: root/unittests/Frontend
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/Frontend')
-rw-r--r--unittests/Frontend/ASTUnitTest.cpp87
-rw-r--r--unittests/Frontend/CMakeLists.txt5
-rw-r--r--unittests/Frontend/CompilerInstanceTest.cpp74
-rw-r--r--unittests/Frontend/PCHPreambleTest.cpp200
-rw-r--r--unittests/Frontend/ParsedSourceLocationTest.cpp37
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