summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/unittests
diff options
context:
space:
mode:
authorGuillaume Papin <guillaume.papin@epitech.eu>2013-08-22 14:49:07 +0000
committerGuillaume Papin <guillaume.papin@epitech.eu>2013-08-22 14:49:07 +0000
commiteffabf96916b766b9aa2324daf006b4da281fa57 (patch)
treee4de1a5e50f9080c5b249bbfea48fc1064575199 /clang-tools-extra/unittests
parent5371e90f7a0f21676318d42574b52522d06d79b3 (diff)
downloadbcm5719-llvm-effabf96916b766b9aa2324daf006b4da281fa57.tar.gz
bcm5719-llvm-effabf96916b766b9aa2324daf006b4da281fa57.zip
cpp11-migrate: Add a class to support include directives modifications
The IncludeDirectives class helps with detecting and modifying #include directives. For now it allows the users to add angled-includes in a source file. This is a start for this class that will evolve in the future to add more functionality. This should fix the reverted commit r188791 (buildbot failures on Windows). llvm-svn: 189017
Diffstat (limited to 'clang-tools-extra/unittests')
-rw-r--r--clang-tools-extra/unittests/cpp11-migrate/CMakeLists.txt1
-rw-r--r--clang-tools-extra/unittests/cpp11-migrate/IncludeDirectivesTest.cpp413
-rw-r--r--clang-tools-extra/unittests/cpp11-migrate/VirtualFileHelper.h20
3 files changed, 427 insertions, 7 deletions
diff --git a/clang-tools-extra/unittests/cpp11-migrate/CMakeLists.txt b/clang-tools-extra/unittests/cpp11-migrate/CMakeLists.txt
index 1f647d5dc19..fcf678d3490 100644
--- a/clang-tools-extra/unittests/cpp11-migrate/CMakeLists.txt
+++ b/clang-tools-extra/unittests/cpp11-migrate/CMakeLists.txt
@@ -13,6 +13,7 @@ add_extra_unittest(Cpp11MigrateTests
PerfSupportTest.cpp
TransformTest.cpp
UniqueHeaderNameTest.cpp
+ IncludeDirectivesTest.cpp
)
target_link_libraries(Cpp11MigrateTests
diff --git a/clang-tools-extra/unittests/cpp11-migrate/IncludeDirectivesTest.cpp b/clang-tools-extra/unittests/cpp11-migrate/IncludeDirectivesTest.cpp
new file mode 100644
index 00000000000..269ea377326
--- /dev/null
+++ b/clang-tools-extra/unittests/cpp11-migrate/IncludeDirectivesTest.cpp
@@ -0,0 +1,413 @@
+//===- cpp11-migrate/IncludeDirectivesTest.cpp ----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Core/IncludeDirectives.h"
+#include "gtest/gtest.h"
+#include "VirtualFileHelper.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+using namespace clang;
+
+/// \brief A convenience method around \c tooling::runToolOnCodeWithArgs() that
+/// adds the current directory to the include search paths.
+static void applyActionOnCode(FrontendAction *ToolAction, StringRef Code) {
+ SmallString<128> CurrentDir;
+ ASSERT_FALSE(llvm::sys::fs::current_path(CurrentDir));
+
+ // Add the current directory to the header search paths so angled includes can
+ // find them.
+ std::vector<std::string> Args;
+ Args.push_back("-I");
+ Args.push_back(CurrentDir.str().str());
+
+ // mapVirtualFile() needs absolute path for the input file as well.
+ SmallString<128> InputFile(CurrentDir);
+ sys::path::append(InputFile, "input.cc");
+
+ ASSERT_TRUE(
+ tooling::runToolOnCodeWithArgs(ToolAction, Code, Args, InputFile.str()));
+}
+
+namespace {
+class TestAddIncludeAction : public PreprocessOnlyAction {
+public:
+ TestAddIncludeAction(StringRef Include, tooling::Replacements &Replaces,
+ const char *HeaderToModify = 0)
+ : Include(Include), Replaces(Replaces), HeaderToModify(HeaderToModify) {
+ // some headers that the tests can include
+ mapVirtualHeader("foo-inner.h", "#pragma once\n");
+ mapVirtualHeader("foo.h", "#pragma once\n"
+ "#include <virtual/foo-inner.h>\n");
+ mapVirtualHeader("bar-inner.h", "#pragma once\n");
+ mapVirtualHeader("bar.h", "#pragma once\n"
+ "#include <virtual/bar-inner.h>\n");
+ mapVirtualHeader("xmacro.def", "X(Val1)\n"
+ "X(Val2)\n"
+ "X(Val3)\n");
+ }
+
+ /// \brief Make \p FileName an absolute path.
+ ///
+ /// Header files are mapped in the 'virtual/' subdirectory of the current
+ /// working directory. The current working directory is used because it's
+ /// important to map files with absolute paths.
+ ///
+ /// When used in conjunction with \c applyActionOnCode() (which adds the
+ /// current working directory to the header search paths) it is possible to
+ /// refer to the headers by using 'virtual/\<FileName\>'.
+ std::string makeHeaderFileName(StringRef FileName) const {
+ SmallString<128> Path;
+ llvm::error_code EC = llvm::sys::fs::current_path(Path);
+ assert(!EC);
+ (void)EC;
+
+ sys::path::append(Path, "virtual");
+ sys::path::append(Path, FileName);
+ return Path.str().str();
+ }
+
+ /// \brief Map additional header files.
+ ///
+ /// \sa makeHeaderFileName()
+ void mapVirtualHeader(StringRef FileName, StringRef Content) {
+ VFHelper.mapFile(makeHeaderFileName(FileName), Content);
+ }
+
+private:
+ virtual bool BeginSourceFileAction(CompilerInstance &CI,
+ StringRef FileName) LLVM_OVERRIDE {
+ if (!PreprocessOnlyAction::BeginSourceFileAction(CI, FileName))
+ return false;
+ VFHelper.mapVirtualFiles(CI.getSourceManager());
+
+ FileToModify =
+ HeaderToModify ? makeHeaderFileName(HeaderToModify) : FileName.str();
+
+ FileIncludes.reset(new IncludeDirectives(CI));
+ return true;
+ }
+
+ virtual void EndSourceFileAction() LLVM_OVERRIDE {
+ const tooling::Replacement &Replace =
+ FileIncludes->addAngledInclude(FileToModify, Include);
+ if (Replace.isApplicable())
+ Replaces.insert(Replace);
+ }
+
+ StringRef Include;
+ VirtualFileHelper VFHelper;
+ tooling::Replacements &Replaces;
+ OwningPtr<IncludeDirectives> FileIncludes;
+ std::string FileToModify;
+ // if non-null, add the include directives in this file instead of the main
+ // file.
+ const char *HeaderToModify;
+};
+
+std::string addIncludeInCode(StringRef Include, StringRef Code) {
+ tooling::Replacements Replaces;
+
+ applyActionOnCode(new TestAddIncludeAction(Include, Replaces), Code);
+
+ if (::testing::Test::HasFailure())
+ return "<<unexpected error from applyActionOnCode()>>";
+
+ return tooling::applyAllReplacements(Code, Replaces);
+}
+} // end anonymous namespace
+
+TEST(IncludeDirectivesTest2, endOfLinesVariants) {
+ EXPECT_EQ("#include <virtual/foo.h>\n"
+ "#include <bar>\n",
+ addIncludeInCode("bar", "#include <virtual/foo.h>\n"));
+ EXPECT_EQ("#include <virtual/foo.h>\r\n"
+ "#include <bar>\r\n",
+ addIncludeInCode("bar", "#include <virtual/foo.h>\r\n"));
+ EXPECT_EQ("#include <virtual/foo.h>\r"
+ "#include <bar>\r",
+ addIncludeInCode("bar", "#include <virtual/foo.h>\r"));
+}
+
+TEST(IncludeDirectivesTest, ppToken) {
+ EXPECT_EQ("#define FOO <virtual/foo.h>\n"
+ "#include FOO\n"
+ "#include <bar>\n"
+ "int i;\n",
+ addIncludeInCode("bar", "#define FOO <virtual/foo.h>\n"
+ "#include FOO\n"
+ "int i;\n"));
+}
+
+TEST(IncludeDirectivesTest, noFileHeader) {
+ EXPECT_EQ("#include <bar>\n"
+ "\n"
+ "int foo;\n",
+ addIncludeInCode("bar", "int foo;\n"));
+}
+
+TEST(IncludeDirectivesTest, commentBeforeTopMostCode) {
+ EXPECT_EQ("#include <bar>\n"
+ "\n"
+ "// Foo\n"
+ "int foo;\n",
+ addIncludeInCode("bar", "// Foo\n"
+ "int foo;\n"));
+}
+
+TEST(IncludeDirectivesTest, multiLineComment) {
+ EXPECT_EQ("#include <virtual/foo.h> /* \n */\n"
+ "#include <bar>\n",
+ addIncludeInCode("bar", "#include <virtual/foo.h> /* \n */\n"));
+ EXPECT_EQ("#include <virtual/foo.h> /* \n */"
+ "\n#include <bar>",
+ addIncludeInCode("bar", "#include <virtual/foo.h> /* \n */"));
+}
+
+TEST(IncludeDirectivesTest, multilineCommentWithTrailingSpace) {
+ EXPECT_EQ("#include <virtual/foo.h> /*\n*/ \n"
+ "#include <bar>\n",
+ addIncludeInCode("bar", "#include <virtual/foo.h> /*\n*/ \n"));
+ EXPECT_EQ("#include <virtual/foo.h> /*\n*/ "
+ "\n#include <bar>",
+ addIncludeInCode("bar", "#include <virtual/foo.h> /*\n*/ "));
+}
+
+TEST(IncludeDirectivesTest, fileHeaders) {
+ EXPECT_EQ("// this is a header\n"
+ "// some license stuff here\n"
+ "\n"
+ "#include <bar>\n"
+ "\n"
+ "/// \\brief Foo\n"
+ "int foo;\n",
+ addIncludeInCode("bar", "// this is a header\n"
+ "// some license stuff here\n"
+ "\n"
+ "/// \\brief Foo\n"
+ "int foo;\n"));
+}
+
+TEST(IncludeDirectivesTest, preferablyAngledNextToAngled) {
+ EXPECT_EQ("#include <virtual/foo.h>\n"
+ "#include <bar>\n"
+ "#include \"virtual/bar.h\"\n",
+ addIncludeInCode("bar", "#include <virtual/foo.h>\n"
+ "#include \"virtual/bar.h\"\n"));
+ EXPECT_EQ("#include \"virtual/foo.h\"\n"
+ "#include \"virtual/bar.h\"\n"
+ "#include <bar>\n",
+ addIncludeInCode("bar", "#include \"virtual/foo.h\"\n"
+ "#include \"virtual/bar.h\"\n"));
+}
+
+TEST(IncludeDirectivesTest, avoidDuplicates) {
+ EXPECT_EQ("#include <virtual/foo.h>\n",
+ addIncludeInCode("virtual/foo.h", "#include <virtual/foo.h>\n"));
+}
+
+// Tests includes in the middle of the code are ignored.
+TEST(IncludeDirectivesTest, ignoreHeadersMeantForMultipleInclusion) {
+ std::string Expected = "#include \"virtual/foo.h\"\n"
+ "#include <bar>\n"
+ "\n"
+ "enum Kind {\n"
+ "#define X(A) K_##A,\n"
+ "#include \"virtual/xmacro.def\"\n"
+ "#undef X\n"
+ " K_NUM_KINDS\n"
+ "};\n";
+ std::string Result =
+ addIncludeInCode("bar", "#include \"virtual/foo.h\"\n"
+ "\n"
+ "enum Kind {\n"
+ "#define X(A) K_##A,\n"
+ "#include \"virtual/xmacro.def\"\n"
+ "#undef X\n"
+ " K_NUM_KINDS\n"
+ "};\n");
+ EXPECT_EQ(Expected, Result);
+}
+
+namespace {
+TestAddIncludeAction *makeIndirectTestsAction(const char *HeaderToModify,
+ tooling::Replacements &Replaces) {
+ StringRef IncludeToAdd = "virtual/c.h";
+ TestAddIncludeAction *TestAction =
+ new TestAddIncludeAction(IncludeToAdd, Replaces, HeaderToModify);
+ TestAction->mapVirtualHeader("c.h", "#pragma once\n");
+ TestAction->mapVirtualHeader("a.h", "#pragma once\n"
+ "#include <virtual/c.h>\n");
+ TestAction->mapVirtualHeader("b.h", "#pragma once\n");
+ return TestAction;
+}
+} // end anonymous namespace
+
+TEST(IncludeDirectivesTest, indirectIncludes) {
+ // In TestAddIncludeAction 'foo.h' includes 'foo-inner.h'. Check that we
+ // aren't including foo-inner.h again.
+ EXPECT_EQ(
+ "#include <virtual/foo.h>\n",
+ addIncludeInCode("virtual/foo-inner.h", "#include <virtual/foo.h>\n"));
+
+ tooling::Replacements Replaces;
+ StringRef Code = "#include <virtual/a.h>\n"
+ "#include <virtual/b.h>\n";
+
+ // a.h already includes c.h
+ {
+ FrontendAction *Action = makeIndirectTestsAction("a.h", Replaces);
+ ASSERT_NO_FATAL_FAILURE(applyActionOnCode(Action, Code));
+ EXPECT_EQ(unsigned(0), Replaces.size());
+ }
+
+ // c.h is included before b.h but b.h doesn't include c.h directly, so check
+ // that it will be inserted.
+ {
+ FrontendAction *Action = makeIndirectTestsAction("b.h", Replaces);
+ ASSERT_NO_FATAL_FAILURE(applyActionOnCode(Action, Code));
+ EXPECT_EQ("#include <virtual/c.h>\n\n\n",
+ tooling::applyAllReplacements("\n", Replaces));
+ }
+}
+
+/// \brief Convenience method to test header guards detection implementation.
+static std::string addIncludeInGuardedHeader(StringRef IncludeToAdd,
+ StringRef GuardedHeaderCode) {
+ const char *GuardedHeaderName = "guarded.h";
+ tooling::Replacements Replaces;
+ TestAddIncludeAction *TestAction =
+ new TestAddIncludeAction(IncludeToAdd, Replaces, GuardedHeaderName);
+ TestAction->mapVirtualHeader(GuardedHeaderName, GuardedHeaderCode);
+
+ applyActionOnCode(TestAction, "#include <virtual/guarded.h>\n");
+ if (::testing::Test::HasFailure())
+ return "<<unexpected error from applyActionOnCode()>>";
+
+ return tooling::applyAllReplacements(GuardedHeaderCode, Replaces);
+}
+
+TEST(IncludeDirectivesTest, insertInsideIncludeGuard) {
+ EXPECT_EQ("#ifndef GUARD_H\n"
+ "#define GUARD_H\n"
+ "\n"
+ "#include <foo>\n"
+ "\n"
+ "struct foo {};\n"
+ "\n"
+ "#endif // GUARD_H\n",
+ addIncludeInGuardedHeader("foo", "#ifndef GUARD_H\n"
+ "#define GUARD_H\n"
+ "\n"
+ "struct foo {};\n"
+ "\n"
+ "#endif // GUARD_H\n"));
+}
+
+TEST(IncludeDirectivesTest, guardAndHeader) {
+ EXPECT_EQ("// File header\n"
+ "\n"
+ "#ifndef GUARD_H\n"
+ "#define GUARD_H\n"
+ "\n"
+ "#include <foo>\n"
+ "\n"
+ "struct foo {};\n"
+ "\n"
+ "#endif // GUARD_H\n",
+ addIncludeInGuardedHeader("foo", "// File header\n"
+ "\n"
+ "#ifndef GUARD_H\n"
+ "#define GUARD_H\n"
+ "\n"
+ "struct foo {};\n"
+ "\n"
+ "#endif // GUARD_H\n"));
+}
+
+TEST(IncludeDirectivesTest, fullHeaderFitsAsAPreamble) {
+ EXPECT_EQ("#ifndef GUARD_H\n"
+ "#define GUARD_H\n"
+ "\n"
+ "#include <foo>\n"
+ "\n"
+ "#define FOO 1\n"
+ "\n"
+ "#endif // GUARD_H\n",
+ addIncludeInGuardedHeader("foo", "#ifndef GUARD_H\n"
+ "#define GUARD_H\n"
+ "\n"
+ "#define FOO 1\n"
+ "\n"
+ "#endif // GUARD_H\n"));
+}
+
+TEST(IncludeDirectivesTest, codeBeforeIfndef) {
+ EXPECT_EQ("#include <foo>\n"
+ "\n"
+ "int bar;\n"
+ "\n"
+ "#ifndef GUARD_H\n"
+ "#define GUARD_H\n"
+ "\n"
+ "struct foo;"
+ "\n"
+ "#endif // GUARD_H\n",
+ addIncludeInGuardedHeader("foo", "int bar;\n"
+ "\n"
+ "#ifndef GUARD_H\n"
+ "#define GUARD_H\n"
+ "\n"
+ "struct foo;"
+ "\n"
+ "#endif // GUARD_H\n"));
+}
+
+TEST(IncludeDirectivesTest, codeAfterEndif) {
+ EXPECT_EQ("#include <foo>\n"
+ "\n"
+ "#ifndef GUARD_H\n"
+ "#define GUARD_H\n"
+ "\n"
+ "struct foo;"
+ "\n"
+ "#endif // GUARD_H\n"
+ "\n"
+ "int bar;\n",
+ addIncludeInGuardedHeader("foo", "#ifndef GUARD_H\n"
+ "#define GUARD_H\n"
+ "\n"
+ "struct foo;"
+ "\n"
+ "#endif // GUARD_H\n"
+ "\n"
+ "int bar;\n"));
+}
+
+TEST(IncludeDirectivesTest, headerGuardWithInclude) {
+ EXPECT_EQ("#ifndef GUARD_H\n"
+ "#define GUARD_H\n"
+ "\n"
+ "#include <virtual/bar.h>\n"
+ "#include <foo>\n"
+ "\n"
+ "struct foo;\n"
+ "\n"
+ "#endif // GUARD_H\n",
+ addIncludeInGuardedHeader("foo", "#ifndef GUARD_H\n"
+ "#define GUARD_H\n"
+ "\n"
+ "#include <virtual/bar.h>\n"
+ "\n"
+ "struct foo;\n"
+ "\n"
+ "#endif // GUARD_H\n"));
+}
diff --git a/clang-tools-extra/unittests/cpp11-migrate/VirtualFileHelper.h b/clang-tools-extra/unittests/cpp11-migrate/VirtualFileHelper.h
index 4ae29fbe0c0..093ace1d0dc 100644
--- a/clang-tools-extra/unittests/cpp11-migrate/VirtualFileHelper.h
+++ b/clang-tools-extra/unittests/cpp11-migrate/VirtualFileHelper.h
@@ -27,8 +27,8 @@ namespace clang {
/// map virtual files conveniently.
class VirtualFileHelper {
struct VirtualFile {
- llvm::StringRef FileName;
- llvm::StringRef Code;
+ std::string FileName;
+ std::string Code;
};
public:
@@ -49,15 +49,21 @@ public:
/// mapped to it.
SourceManager &getNewSourceManager() {
Sources.reset(new SourceManager(Diagnostics, Files));
- for (llvm::SmallVectorImpl<VirtualFile>::iterator I = VirtualFiles.begin(),
- E = VirtualFiles.end();
+ mapVirtualFiles(*Sources);
+ return *Sources;
+ }
+
+ /// \brief Map the virtual file contents in the given \c SourceManager.
+ void mapVirtualFiles(SourceManager &SM) const {
+ for (llvm::SmallVectorImpl<VirtualFile>::const_iterator
+ I = VirtualFiles.begin(),
+ E = VirtualFiles.end();
I != E; ++I) {
llvm::MemoryBuffer *Buf = llvm::MemoryBuffer::getMemBuffer(I->Code);
- const FileEntry *Entry = Files.getVirtualFile(
+ const FileEntry *Entry = SM.getFileManager().getVirtualFile(
I->FileName, Buf->getBufferSize(), /*ModificationTime=*/0);
- Sources->overrideFileContents(Entry, Buf);
+ SM.overrideFileContents(Entry, Buf);
}
- return *Sources;
}
private:
OpenPOWER on IntegriCloud