summaryrefslogtreecommitdiffstats
path: root/unittests/Tooling
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/Tooling')
-rw-r--r--unittests/Tooling/CMakeLists.txt1
-rw-r--r--unittests/Tooling/CompilationDatabaseTest.cpp50
-rw-r--r--unittests/Tooling/Makefile2
-rw-r--r--unittests/Tooling/RecursiveASTVisitorTest.cpp120
-rw-r--r--unittests/Tooling/RefactoringTest.cpp193
-rw-r--r--unittests/Tooling/ReplacementsYamlTest.cpp106
-rw-r--r--unittests/Tooling/RewriterTestContext.h42
-rw-r--r--unittests/Tooling/TestVisitor.h11
-rw-r--r--unittests/Tooling/ToolingTest.cpp165
9 files changed, 627 insertions, 63 deletions
diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt
index 245c059..33d7617 100644
--- a/unittests/Tooling/CMakeLists.txt
+++ b/unittests/Tooling/CMakeLists.txt
@@ -14,6 +14,7 @@ add_clang_unittest(ToolingTests
RefactoringTest.cpp
RewriterTest.cpp
RefactoringCallbacksTest.cpp
+ ReplacementsYamlTest.cpp
)
target_link_libraries(ToolingTests
diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp
index c453b05..c575dff 100644
--- a/unittests/Tooling/CompilationDatabaseTest.cpp
+++ b/unittests/Tooling/CompilationDatabaseTest.cpp
@@ -14,7 +14,7 @@
#include "clang/Tooling/FileMatchTrie.h"
#include "clang/Tooling/JSONCompilationDatabase.h"
#include "clang/Tooling/Tooling.h"
-#include "llvm/Support/PathV2.h"
+#include "llvm/Support/Path.h"
#include "gtest/gtest.h"
namespace clang {
@@ -450,18 +450,20 @@ TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) {
TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) {
int Argc = 5;
- const char *Argv[] = { "1", "2", "--\0no-constant-folding", "3", "4" };
+ const char *Argv[] = {
+ "1", "2", "--\0no-constant-folding", "-DDEF3", "-DDEF4"
+ };
OwningPtr<FixedCompilationDatabase> Database(
FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
- ASSERT_TRUE(Database);
+ ASSERT_TRUE(Database.isValid());
std::vector<CompileCommand> Result =
Database->getCompileCommands("source");
ASSERT_EQ(1ul, Result.size());
ASSERT_EQ(".", Result[0].Directory);
std::vector<std::string> CommandLine;
CommandLine.push_back("clang-tool");
- CommandLine.push_back("3");
- CommandLine.push_back("4");
+ CommandLine.push_back("-DDEF3");
+ CommandLine.push_back("-DDEF4");
CommandLine.push_back("source");
ASSERT_EQ(CommandLine, Result[0].CommandLine);
EXPECT_EQ(2, Argc);
@@ -472,7 +474,7 @@ TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
const char *Argv[] = { "1", "2", "--\0no-constant-folding" };
OwningPtr<FixedCompilationDatabase> Database(
FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
- ASSERT_TRUE(Database);
+ ASSERT_TRUE(Database.isValid());
std::vector<CompileCommand> Result =
Database->getCompileCommands("source");
ASSERT_EQ(1ul, Result.size());
@@ -484,5 +486,41 @@ TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
EXPECT_EQ(2, Argc);
}
+TEST(ParseFixedCompilationDatabase, HandlesPositionalArgs) {
+ const char *Argv[] = {"1", "2", "--", "-c", "somefile.cpp", "-DDEF3"};
+ int Argc = sizeof(Argv) / sizeof(char*);
+ OwningPtr<FixedCompilationDatabase> Database(
+ FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
+ ASSERT_TRUE(Database.isValid());
+ std::vector<CompileCommand> Result =
+ Database->getCompileCommands("source");
+ ASSERT_EQ(1ul, Result.size());
+ ASSERT_EQ(".", Result[0].Directory);
+ std::vector<std::string> Expected;
+ Expected.push_back("clang-tool");
+ Expected.push_back("-c");
+ Expected.push_back("-DDEF3");
+ Expected.push_back("source");
+ ASSERT_EQ(Expected, Result[0].CommandLine);
+ EXPECT_EQ(2, Argc);
+}
+
+TEST(ParseFixedCompilationDatabase, HandlesArgv0) {
+ const char *Argv[] = {"1", "2", "--", "mytool", "somefile.cpp"};
+ int Argc = sizeof(Argv) / sizeof(char*);
+ OwningPtr<FixedCompilationDatabase> Database(
+ FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
+ ASSERT_TRUE(Database.isValid());
+ std::vector<CompileCommand> Result =
+ Database->getCompileCommands("source");
+ ASSERT_EQ(1ul, Result.size());
+ ASSERT_EQ(".", Result[0].Directory);
+ std::vector<std::string> Expected;
+ Expected.push_back("clang-tool");
+ Expected.push_back("source");
+ ASSERT_EQ(Expected, Result[0].CommandLine);
+ EXPECT_EQ(2, Argc);
+}
+
} // end namespace tooling
} // end namespace clang
diff --git a/unittests/Tooling/Makefile b/unittests/Tooling/Makefile
index 06fdf88..9d36f1f 100644
--- a/unittests/Tooling/Makefile
+++ b/unittests/Tooling/Makefile
@@ -10,7 +10,7 @@
CLANG_LEVEL = ../..
TESTNAME = Tooling
include $(CLANG_LEVEL)/../../Makefile.config
-LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option
USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
clangParse.a clangRewriteCore.a clangRewriteFrontend.a \
clangSema.a clangAnalysis.a clangEdit.a \
diff --git a/unittests/Tooling/RecursiveASTVisitorTest.cpp b/unittests/Tooling/RecursiveASTVisitorTest.cpp
index 81be190..3234767 100644
--- a/unittests/Tooling/RecursiveASTVisitorTest.cpp
+++ b/unittests/Tooling/RecursiveASTVisitorTest.cpp
@@ -9,6 +9,8 @@
#include "TestVisitor.h"
+#include <stack>
+
namespace clang {
class TypeLocVisitor : public ExpectedLocationVisitor<TypeLocVisitor> {
@@ -35,6 +37,17 @@ public:
}
};
+class ParmVarDeclVisitorForImplicitCode :
+ public ExpectedLocationVisitor<ParmVarDeclVisitorForImplicitCode> {
+public:
+ bool shouldVisitImplicitCode() const { return true; }
+
+ bool VisitParmVarDecl(ParmVarDecl *ParamVar) {
+ Match(ParamVar->getNameAsString(), ParamVar->getLocStart());
+ return true;
+ }
+};
+
class CXXMemberCallVisitor
: public ExpectedLocationVisitor<CXXMemberCallVisitor> {
public:
@@ -79,6 +92,42 @@ public:
}
};
+class LambdaExprVisitor : public ExpectedLocationVisitor<LambdaExprVisitor> {
+public:
+ bool VisitLambdaExpr(LambdaExpr *Lambda) {
+ PendingBodies.push(Lambda);
+ Match("", Lambda->getIntroducerRange().getBegin());
+ return true;
+ }
+ /// For each call to VisitLambdaExpr, we expect a subsequent call (with
+ /// proper nesting) to TraverseLambdaBody.
+ bool TraverseLambdaBody(LambdaExpr *Lambda) {
+ EXPECT_FALSE(PendingBodies.empty());
+ EXPECT_EQ(PendingBodies.top(), Lambda);
+ PendingBodies.pop();
+ return TraverseStmt(Lambda->getBody());
+ }
+ /// Determine whether TraverseLambdaBody has been called for every call to
+ /// VisitLambdaExpr.
+ bool allBodiesHaveBeenTraversed() const {
+ return PendingBodies.empty();
+ }
+private:
+ std::stack<LambdaExpr *> PendingBodies;
+};
+
+// Matches the (optional) capture-default of a lambda-introducer.
+class LambdaDefaultCaptureVisitor
+ : public ExpectedLocationVisitor<LambdaDefaultCaptureVisitor> {
+public:
+ bool VisitLambdaExpr(LambdaExpr *Lambda) {
+ if (Lambda->getCaptureDefault() != LCD_None) {
+ Match("", Lambda->getCaptureDefaultLoc());
+ }
+ return true;
+ }
+};
+
class TemplateArgumentLocTraverser
: public ExpectedLocationVisitor<TemplateArgumentLocTraverser> {
public:
@@ -106,6 +155,24 @@ public:
}
};
+// Test RAV visits parameter variable declaration of the implicit
+// copy assignment operator and implicit copy constructor.
+TEST(RecursiveASTVisitor, VisitsParmVarDeclForImplicitCode) {
+ ParmVarDeclVisitorForImplicitCode Visitor;
+ // Match parameter variable name of implicit copy assignment operator and
+ // implicit copy constructor.
+ // This parameter name does not have a valid IdentifierInfo, and shares
+ // same SourceLocation with its class declaration, so we match an empty name
+ // with the class' source location.
+ Visitor.ExpectMatch("", 1, 7);
+ Visitor.ExpectMatch("", 3, 7);
+ EXPECT_TRUE(Visitor.runOver(
+ "class X {};\n"
+ "void foo(X a, X b) {a = b;}\n"
+ "class Y {};\n"
+ "void bar(Y a) {Y b = a;}"));
+}
+
TEST(RecursiveASTVisitor, VisitsBaseClassDeclarations) {
TypeLocVisitor Visitor;
Visitor.ExpectMatch("class X", 1, 30);
@@ -150,7 +217,8 @@ TEST(RecursiveASTVisitor, VisitsCXXForRangeStmtRange) {
Visitor.ExpectMatch("x", 2, 30);
EXPECT_TRUE(Visitor.runOver(
"int x[5];\n"
- "void f() { for (int i : x) { x[0] = 1; } }"));
+ "void f() { for (int i : x) { x[0] = 1; } }",
+ DeclRefExprVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, VisitsCXXForRangeStmtLoopVariable) {
@@ -158,7 +226,8 @@ TEST(RecursiveASTVisitor, VisitsCXXForRangeStmtLoopVariable) {
Visitor.ExpectMatch("i", 2, 17);
EXPECT_TRUE(Visitor.runOver(
"int x[5];\n"
- "void f() { for (int i : x) {} }"));
+ "void f() { for (int i : x) {} }",
+ VarDeclVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, VisitsCallExpr) {
@@ -461,4 +530,51 @@ TEST(RecursiveASTVisitor, VisitsCompoundLiteralType) {
TypeLocVisitor::Lang_C));
}
+TEST(RecursiveASTVisitor, VisitsLambdaExpr) {
+ LambdaExprVisitor Visitor;
+ Visitor.ExpectMatch("", 1, 12);
+ EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
+ LambdaExprVisitor::Lang_CXX11));
+}
+
+TEST(RecursiveASTVisitor, TraverseLambdaBodyCanBeOverridden) {
+ LambdaExprVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
+ LambdaExprVisitor::Lang_CXX11));
+ EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
+}
+
+TEST(RecursiveASTVisitor, HasCaptureDefaultLoc) {
+ LambdaDefaultCaptureVisitor Visitor;
+ Visitor.ExpectMatch("", 1, 20);
+ EXPECT_TRUE(Visitor.runOver("void f() { int a; [=]{a;}; }",
+ LambdaDefaultCaptureVisitor::Lang_CXX11));
+}
+
+// Checks for lambda classes that are not marked as implicitly-generated.
+// (There should be none.)
+class ClassVisitor : public ExpectedLocationVisitor<ClassVisitor> {
+public:
+ ClassVisitor() : SawNonImplicitLambdaClass(false) {}
+ bool VisitCXXRecordDecl(CXXRecordDecl* record) {
+ if (record->isLambda() && !record->isImplicit())
+ SawNonImplicitLambdaClass = true;
+ return true;
+ }
+
+ bool sawOnlyImplicitLambdaClasses() const {
+ return !SawNonImplicitLambdaClass;
+ }
+
+private:
+ bool SawNonImplicitLambdaClass;
+};
+
+TEST(RecursiveASTVisitor, LambdaClosureTypesAreImplicit) {
+ ClassVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver("auto lambda = []{};",
+ ClassVisitor::Lang_CXX11));
+ EXPECT_TRUE(Visitor.sawOnlyImplicitLambdaClasses());
+}
+
} // end namespace clang
diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp
index 3e0d728..8c7bfa1 100644
--- a/unittests/Tooling/RefactoringTest.cpp
+++ b/unittests/Tooling/RefactoringTest.cpp
@@ -120,6 +120,21 @@ TEST_F(ReplacementTest, CanApplyReplacements) {
EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID));
}
+// FIXME: Remove this test case when Replacements is implemented as std::vector
+// instead of std::set. The other ReplacementTest tests will need to be updated
+// at that point as well.
+TEST_F(ReplacementTest, VectorCanApplyReplacements) {
+ FileID ID = Context.createInMemoryFile("input.cpp",
+ "line1\nline2\nline3\nline4");
+ std::vector<Replacement> Replaces;
+ Replaces.push_back(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
+ 5, "replaced"));
+ Replaces.push_back(
+ Replacement(Context.Sources, Context.getLocation(ID, 3, 1), 5, "other"));
+ EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
+ EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID));
+}
+
TEST_F(ReplacementTest, SkipsDuplicateReplacements) {
FileID ID = Context.createInMemoryFile("input.cpp",
"line1\nline2\nline3\nline4");
@@ -151,37 +166,87 @@ TEST_F(ReplacementTest, ApplyAllFailsIfOneApplyFails) {
EXPECT_EQ("z", Context.getRewrittenText(IDz));
}
+TEST(ShiftedCodePositionTest, FindsNewCodePosition) {
+ Replacements Replaces;
+ Replaces.insert(Replacement("", 0, 1, ""));
+ Replaces.insert(Replacement("", 4, 3, " "));
+ // Assume ' int i;' is turned into 'int i;' and cursor is located at '|'.
+ EXPECT_EQ(0u, shiftedCodePosition(Replaces, 0)); // |int i;
+ EXPECT_EQ(0u, shiftedCodePosition(Replaces, 1)); // |nt i;
+ EXPECT_EQ(1u, shiftedCodePosition(Replaces, 2)); // i|t i;
+ EXPECT_EQ(2u, shiftedCodePosition(Replaces, 3)); // in| i;
+ EXPECT_EQ(3u, shiftedCodePosition(Replaces, 4)); // int| i;
+ EXPECT_EQ(4u, shiftedCodePosition(Replaces, 5)); // int | i;
+ EXPECT_EQ(4u, shiftedCodePosition(Replaces, 6)); // int |i;
+ EXPECT_EQ(4u, shiftedCodePosition(Replaces, 7)); // int |;
+ EXPECT_EQ(5u, shiftedCodePosition(Replaces, 8)); // int i|
+}
+
+// FIXME: Remove this test case when Replacements is implemented as std::vector
+// instead of std::set. The other ReplacementTest tests will need to be updated
+// at that point as well.
+TEST(ShiftedCodePositionTest, VectorFindsNewCodePositionWithInserts) {
+ std::vector<Replacement> Replaces;
+ Replaces.push_back(Replacement("", 0, 1, ""));
+ Replaces.push_back(Replacement("", 4, 3, " "));
+ // Assume ' int i;' is turned into 'int i;' and cursor is located at '|'.
+ EXPECT_EQ(0u, shiftedCodePosition(Replaces, 0)); // |int i;
+ EXPECT_EQ(0u, shiftedCodePosition(Replaces, 1)); // |nt i;
+ EXPECT_EQ(1u, shiftedCodePosition(Replaces, 2)); // i|t i;
+ EXPECT_EQ(2u, shiftedCodePosition(Replaces, 3)); // in| i;
+ EXPECT_EQ(3u, shiftedCodePosition(Replaces, 4)); // int| i;
+ EXPECT_EQ(4u, shiftedCodePosition(Replaces, 5)); // int | i;
+ EXPECT_EQ(4u, shiftedCodePosition(Replaces, 6)); // int |i;
+ EXPECT_EQ(4u, shiftedCodePosition(Replaces, 7)); // int |;
+ EXPECT_EQ(5u, shiftedCodePosition(Replaces, 8)); // int i|
+}
+
+TEST(ShiftedCodePositionTest, FindsNewCodePositionWithInserts) {
+ Replacements Replaces;
+ Replaces.insert(Replacement("", 4, 0, "\"\n\""));
+ // Assume '"12345678"' is turned into '"1234"\n"5678"'.
+ EXPECT_EQ(4u, shiftedCodePosition(Replaces, 4)); // "123|5678"
+ EXPECT_EQ(8u, shiftedCodePosition(Replaces, 5)); // "1234|678"
+}
+
class FlushRewrittenFilesTest : public ::testing::Test {
- public:
- FlushRewrittenFilesTest() {
- std::string ErrorInfo;
- TemporaryDirectory = llvm::sys::Path::GetTemporaryDirectory(&ErrorInfo);
- assert(ErrorInfo.empty());
- }
+public:
+ FlushRewrittenFilesTest() {}
~FlushRewrittenFilesTest() {
- std::string ErrorInfo;
- TemporaryDirectory.eraseFromDisk(true, &ErrorInfo);
- assert(ErrorInfo.empty());
+ for (llvm::StringMap<std::string>::iterator I = TemporaryFiles.begin(),
+ E = TemporaryFiles.end();
+ I != E; ++I) {
+ llvm::StringRef Name = I->second;
+ llvm::error_code EC = llvm::sys::fs::remove(Name);
+ (void)EC;
+ assert(!EC);
+ }
}
FileID createFile(llvm::StringRef Name, llvm::StringRef Content) {
- SmallString<1024> Path(TemporaryDirectory.str());
- llvm::sys::path::append(Path, Name);
- std::string ErrorInfo;
- llvm::raw_fd_ostream OutStream(Path.c_str(),
- ErrorInfo, llvm::raw_fd_ostream::F_Binary);
- assert(ErrorInfo.empty());
+ SmallString<1024> Path;
+ int FD;
+ llvm::error_code EC =
+ llvm::sys::fs::createTemporaryFile(Name, "", FD, Path);
+ assert(!EC);
+ (void)EC;
+
+ llvm::raw_fd_ostream OutStream(FD, true);
OutStream << Content;
OutStream.close();
const FileEntry *File = Context.Files.getFile(Path);
assert(File != NULL);
+
+ StringRef Found = TemporaryFiles.GetOrCreateValue(Name, Path.str()).second;
+ assert(Found == Path);
+ (void)Found;
return Context.Sources.createFileID(File, SourceLocation(), SrcMgr::C_User);
}
std::string getFileContentFromDisk(llvm::StringRef Name) {
- SmallString<1024> Path(TemporaryDirectory.str());
- llvm::sys::path::append(Path, Name);
+ std::string Path = TemporaryFiles.lookup(Name);
+ assert(!Path.empty());
// We need to read directly from the FileManager without relaying through
// a FileEntry, as otherwise we'd read through an already opened file
// descriptor, which might not see the changes made.
@@ -190,7 +255,7 @@ class FlushRewrittenFilesTest : public ::testing::Test {
return Context.Files.getBufferForFile(Path, NULL)->getBuffer();
}
- llvm::sys::Path TemporaryDirectory;
+ llvm::StringMap<std::string> TemporaryFiles;
RewriterTestContext Context;
};
@@ -301,5 +366,97 @@ TEST(Replacement, TemplatedFunctionCall) {
expectReplacementAt(CallToF.Replace, "input.cc", 43, 8);
}
+TEST(Range, overlaps) {
+ EXPECT_TRUE(Range(10, 10).overlapsWith(Range(0, 11)));
+ EXPECT_TRUE(Range(0, 11).overlapsWith(Range(10, 10)));
+ EXPECT_FALSE(Range(10, 10).overlapsWith(Range(0, 10)));
+ EXPECT_FALSE(Range(0, 10).overlapsWith(Range(10, 10)));
+ EXPECT_TRUE(Range(0, 10).overlapsWith(Range(2, 6)));
+ EXPECT_TRUE(Range(2, 6).overlapsWith(Range(0, 10)));
+}
+
+TEST(Range, contains) {
+ EXPECT_TRUE(Range(0, 10).contains(Range(0, 10)));
+ EXPECT_TRUE(Range(0, 10).contains(Range(2, 6)));
+ EXPECT_FALSE(Range(2, 6).contains(Range(0, 10)));
+ EXPECT_FALSE(Range(0, 10).contains(Range(0, 11)));
+}
+
+TEST(DeduplicateTest, removesDuplicates) {
+ std::vector<Replacement> Input;
+ Input.push_back(Replacement("fileA", 50, 0, " foo "));
+ Input.push_back(Replacement("fileA", 10, 3, " bar "));
+ Input.push_back(Replacement("fileA", 10, 2, " bar ")); // Length differs
+ Input.push_back(Replacement("fileA", 9, 3, " bar ")); // Offset differs
+ Input.push_back(Replacement("fileA", 50, 0, " foo ")); // Duplicate
+ Input.push_back(Replacement("fileA", 51, 3, " bar "));
+ Input.push_back(Replacement("fileB", 51, 3, " bar ")); // Filename differs!
+ Input.push_back(Replacement("fileA", 51, 3, " moo ")); // Replacement text
+ // differs!
+
+ std::vector<Replacement> Expected;
+ Expected.push_back(Replacement("fileA", 9, 3, " bar "));
+ Expected.push_back(Replacement("fileA", 10, 2, " bar "));
+ Expected.push_back(Replacement("fileA", 10, 3, " bar "));
+ Expected.push_back(Replacement("fileA", 50, 0, " foo "));
+ Expected.push_back(Replacement("fileA", 51, 3, " bar "));
+ Expected.push_back(Replacement("fileA", 51, 3, " moo "));
+ Expected.push_back(Replacement("fileB", 51, 3, " bar "));
+
+ std::vector<Range> Conflicts; // Ignored for this test
+ deduplicate(Input, Conflicts);
+
+ ASSERT_TRUE(Expected == Input);
+}
+
+TEST(DeduplicateTest, detectsConflicts) {
+ {
+ std::vector<Replacement> Input;
+ Input.push_back(Replacement("fileA", 0, 5, " foo "));
+ Input.push_back(Replacement("fileA", 0, 5, " foo ")); // Duplicate not a
+ // conflict.
+ Input.push_back(Replacement("fileA", 2, 6, " bar "));
+ Input.push_back(Replacement("fileA", 7, 3, " moo "));
+
+ std::vector<Range> Conflicts;
+ deduplicate(Input, Conflicts);
+
+ // One duplicate is removed and the remaining three items form one
+ // conflicted range.
+ ASSERT_EQ(3u, Input.size());
+ ASSERT_EQ(1u, Conflicts.size());
+ ASSERT_EQ(0u, Conflicts.front().getOffset());
+ ASSERT_EQ(3u, Conflicts.front().getLength());
+ }
+ {
+ std::vector<Replacement> Input;
+
+ // Expected sorted order is shown. It is the sorted order to which the
+ // returned conflict info refers to.
+ Input.push_back(Replacement("fileA", 0, 5, " foo ")); // 0
+ Input.push_back(Replacement("fileA", 5, 5, " bar ")); // 1
+ Input.push_back(Replacement("fileA", 6, 0, " bar ")); // 3
+ Input.push_back(Replacement("fileA", 5, 5, " moo ")); // 2
+ Input.push_back(Replacement("fileA", 7, 2, " bar ")); // 4
+ Input.push_back(Replacement("fileA", 15, 5, " golf ")); // 5
+ Input.push_back(Replacement("fileA", 16, 5, " bag ")); // 6
+ Input.push_back(Replacement("fileA", 10, 3, " club ")); // 7
+
+ // #3 is special in that it is completely contained by another conflicting
+ // Replacement. #4 ensures #3 hasn't messed up the conflicting range size.
+
+ std::vector<Range> Conflicts;
+ deduplicate(Input, Conflicts);
+
+ // No duplicates
+ ASSERT_EQ(8u, Input.size());
+ ASSERT_EQ(2u, Conflicts.size());
+ ASSERT_EQ(1u, Conflicts[0].getOffset());
+ ASSERT_EQ(4u, Conflicts[0].getLength());
+ ASSERT_EQ(6u, Conflicts[1].getOffset());
+ ASSERT_EQ(2u, Conflicts[1].getLength());
+ }
+}
+
} // end namespace tooling
} // end namespace clang
diff --git a/unittests/Tooling/ReplacementsYamlTest.cpp b/unittests/Tooling/ReplacementsYamlTest.cpp
new file mode 100644
index 0000000..a20dde7
--- /dev/null
+++ b/unittests/Tooling/ReplacementsYamlTest.cpp
@@ -0,0 +1,106 @@
+//===- unittests/Tooling/ReplacementsYamlTest.cpp - Serialization tests ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for serialization of Replacements.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang::tooling;
+
+TEST(ReplacementsYamlTest, serializesReplacements) {
+
+ TranslationUnitReplacements Doc;
+
+ Doc.MainSourceFile = "/path/to/source.cpp";
+ Doc.Context = "some context";
+ Doc.Replacements
+ .push_back(Replacement("/path/to/file1.h", 232, 56, "replacement #1"));
+ Doc.Replacements
+ .push_back(Replacement("/path/to/file2.h", 301, 2, "replacement #2"));
+
+ std::string YamlContent;
+ llvm::raw_string_ostream YamlContentStream(YamlContent);
+
+ yaml::Output YAML(YamlContentStream);
+ YAML << Doc;
+
+ // NOTE: If this test starts to fail for no obvious reason, check whitespace.
+ ASSERT_STREQ("---\n"
+ "MainSourceFile: /path/to/source.cpp\n"
+ "Context: some context\n"
+ "Replacements: \n" // Extra whitespace here!
+ " - FilePath: /path/to/file1.h\n"
+ " Offset: 232\n"
+ " Length: 56\n"
+ " ReplacementText: 'replacement #1'\n"
+ " - FilePath: /path/to/file2.h\n"
+ " Offset: 301\n"
+ " Length: 2\n"
+ " ReplacementText: 'replacement #2'\n"
+ "...\n",
+ YamlContentStream.str().c_str());
+}
+
+TEST(ReplacementsYamlTest, deserializesReplacements) {
+ std::string YamlContent = "---\n"
+ "MainSourceFile: /path/to/source.cpp\n"
+ "Context: some context\n"
+ "Replacements:\n"
+ " - FilePath: /path/to/file1.h\n"
+ " Offset: 232\n"
+ " Length: 56\n"
+ " ReplacementText: 'replacement #1'\n"
+ " - FilePath: /path/to/file2.h\n"
+ " Offset: 301\n"
+ " Length: 2\n"
+ " ReplacementText: 'replacement #2'\n"
+ "...\n";
+ TranslationUnitReplacements DocActual;
+ yaml::Input YAML(YamlContent);
+ YAML >> DocActual;
+ ASSERT_FALSE(YAML.error());
+ ASSERT_EQ(2u, DocActual.Replacements.size());
+ ASSERT_EQ("/path/to/source.cpp", DocActual.MainSourceFile);
+ ASSERT_EQ("some context", DocActual.Context);
+ ASSERT_EQ("/path/to/file1.h", DocActual.Replacements[0].getFilePath());
+ ASSERT_EQ(232u, DocActual.Replacements[0].getOffset());
+ ASSERT_EQ(56u, DocActual.Replacements[0].getLength());
+ ASSERT_EQ("replacement #1", DocActual.Replacements[0].getReplacementText());
+ ASSERT_EQ("/path/to/file2.h", DocActual.Replacements[1].getFilePath());
+ ASSERT_EQ(301u, DocActual.Replacements[1].getOffset());
+ ASSERT_EQ(2u, DocActual.Replacements[1].getLength());
+ ASSERT_EQ("replacement #2", DocActual.Replacements[1].getReplacementText());
+}
+
+TEST(ReplacementsYamlTest, deserializesWithoutContext) {
+ // Make sure a doc can be read without the context field.
+ std::string YamlContent = "---\n"
+ "MainSourceFile: /path/to/source.cpp\n"
+ "Replacements:\n"
+ " - FilePath: target_file.h\n"
+ " Offset: 1\n"
+ " Length: 10\n"
+ " ReplacementText: replacement\n"
+ "...\n";
+ TranslationUnitReplacements DocActual;
+ yaml::Input YAML(YamlContent);
+ YAML >> DocActual;
+ ASSERT_FALSE(YAML.error());
+ ASSERT_EQ("/path/to/source.cpp", DocActual.MainSourceFile);
+ ASSERT_EQ(1u, DocActual.Replacements.size());
+ ASSERT_EQ(std::string(), DocActual.Context);
+ ASSERT_EQ("target_file.h", DocActual.Replacements[0].getFilePath());
+ ASSERT_EQ(1u, DocActual.Replacements[0].getOffset());
+ ASSERT_EQ(10u, DocActual.Replacements[0].getLength());
+ ASSERT_EQ("replacement", DocActual.Replacements[0].getReplacementText());
+}
diff --git a/unittests/Tooling/RewriterTestContext.h b/unittests/Tooling/RewriterTestContext.h
index 13c4202..841cd0f 100644
--- a/unittests/Tooling/RewriterTestContext.h
+++ b/unittests/Tooling/RewriterTestContext.h
@@ -45,12 +45,7 @@ class RewriterTestContext {
Diagnostics.setClient(&DiagnosticPrinter, false);
}
- ~RewriterTestContext() {
- if (!TemporaryDirectory.empty()) {
- uint32_t RemovedCount = 0;
- llvm::sys::fs::remove_all(TemporaryDirectory.str(), RemovedCount);
- }
- }
+ ~RewriterTestContext() {}
FileID createInMemoryFile(StringRef Name, StringRef Content) {
const llvm::MemoryBuffer *Source =
@@ -62,26 +57,25 @@ class RewriterTestContext {
return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
}
+ // FIXME: this code is mostly a duplicate of
+ // unittests/Tooling/RefactoringTest.cpp. Figure out a way to share it.
FileID createOnDiskFile(StringRef Name, StringRef Content) {
- if (TemporaryDirectory.empty()) {
- int FD;
- bool error =
- llvm::sys::fs::unique_file("rewriter-test-%%-%%-%%-%%/anchor", FD,
- TemporaryDirectory);
- assert(!error); (void)error;
- llvm::raw_fd_ostream Closer(FD, /*shouldClose=*/true);
- TemporaryDirectory = llvm::sys::path::parent_path(TemporaryDirectory);
- }
- SmallString<1024> Path(TemporaryDirectory);
- llvm::sys::path::append(Path, Name);
- std::string ErrorInfo;
- llvm::raw_fd_ostream OutStream(Path.c_str(),
- ErrorInfo, llvm::raw_fd_ostream::F_Binary);
- assert(ErrorInfo.empty());
+ SmallString<1024> Path;
+ int FD;
+ llvm::error_code EC =
+ llvm::sys::fs::createTemporaryFile(Name, "", FD, Path);
+ assert(!EC);
+ (void)EC;
+
+ llvm::raw_fd_ostream OutStream(FD, true);
OutStream << Content;
OutStream.close();
const FileEntry *File = Files.getFile(Path);
assert(File != NULL);
+
+ StringRef Found = TemporaryFiles.GetOrCreateValue(Name, Path.str()).second;
+ assert(Found == Path);
+ (void)Found;
return Sources.createFileID(File, SourceLocation(), SrcMgr::C_User);
}
@@ -101,8 +95,8 @@ class RewriterTestContext {
}
std::string getFileContentFromDisk(StringRef Name) {
- SmallString<1024> Path(TemporaryDirectory.str());
- llvm::sys::path::append(Path, Name);
+ std::string Path = TemporaryFiles.lookup(Name);
+ assert(!Path.empty());
// We need to read directly from the FileManager without relaying through
// a FileEntry, as otherwise we'd read through an already opened file
// descriptor, which might not see the changes made.
@@ -120,7 +114,7 @@ class RewriterTestContext {
Rewriter Rewrite;
// Will be set once on disk files are generated.
- SmallString<128> TemporaryDirectory;
+ llvm::StringMap<std::string> TemporaryFiles;
};
} // end namespace clang
diff --git a/unittests/Tooling/TestVisitor.h b/unittests/Tooling/TestVisitor.h
index ce3246a..ec751c3 100644
--- a/unittests/Tooling/TestVisitor.h
+++ b/unittests/Tooling/TestVisitor.h
@@ -31,7 +31,7 @@ namespace clang {
/// This is a drop-in replacement for RecursiveASTVisitor itself, with the
/// additional capability of running it over a snippet of code.
///
-/// Visits template instantiations (but not implicit code) by default.
+/// Visits template instantiations and implicit code by default.
template <typename T>
class TestVisitor : public RecursiveASTVisitor<T> {
public:
@@ -39,14 +39,15 @@ public:
virtual ~TestVisitor() { }
- enum Language { Lang_C, Lang_CXX };
+ enum Language { Lang_C, Lang_CXX98, Lang_CXX11, Lang_CXX=Lang_CXX98 };
/// \brief Runs the current AST visitor over the given code.
bool runOver(StringRef Code, Language L = Lang_CXX) {
std::vector<std::string> Args;
switch (L) {
case Lang_C: Args.push_back("-std=c99"); break;
- case Lang_CXX: Args.push_back("-std=c++98"); break;
+ case Lang_CXX98: Args.push_back("-std=c++98"); break;
+ case Lang_CXX11: Args.push_back("-std=c++11"); break;
}
return tooling::runToolOnCodeWithArgs(CreateTestAction(), Code, Args);
}
@@ -55,6 +56,10 @@ public:
return true;
}
+ bool shouldVisitImplicitCode() const {
+ return true;
+ }
+
protected:
virtual ASTFrontendAction* CreateTestAction() {
return new TestAction(this);
diff --git a/unittests/Tooling/ToolingTest.cpp b/unittests/Tooling/ToolingTest.cpp
index a9319f2..2c4a8a7 100644
--- a/unittests/Tooling/ToolingTest.cpp
+++ b/unittests/Tooling/ToolingTest.cpp
@@ -10,12 +10,14 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclGroup.h"
+#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
+#include "llvm/ADT/STLExtras.h"
#include <string>
namespace clang {
@@ -83,6 +85,18 @@ class FindClassDeclXConsumer : public clang::ASTConsumer {
private:
bool *FoundClassDeclX;
};
+bool FindClassDeclX(ASTUnit *AST) {
+ for (std::vector<Decl *>::iterator i = AST->top_level_begin(),
+ e = AST->top_level_end();
+ i != e; ++i) {
+ if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) {
+ if (Record->getName() == "X") {
+ return true;
+ }
+ }
+ }
+ return false;
+}
} // end namespace
TEST(runToolOnCode, FindsClassDecl) {
@@ -97,6 +111,16 @@ TEST(runToolOnCode, FindsClassDecl) {
EXPECT_FALSE(FoundClassDeclX);
}
+TEST(buildASTFromCode, FindsClassDecl) {
+ OwningPtr<ASTUnit> AST(buildASTFromCode("class X;"));
+ ASSERT_TRUE(AST.get());
+ EXPECT_TRUE(FindClassDeclX(AST.get()));
+
+ AST.reset(buildASTFromCode("class Y;"));
+ ASSERT_TRUE(AST.get());
+ EXPECT_FALSE(FindClassDeclX(AST.get()));
+}
+
TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
OwningPtr<FrontendActionFactory> Factory(
newFrontendActionFactory<SyntaxOnlyAction>());
@@ -119,32 +143,62 @@ TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
}
TEST(ToolInvocation, TestMapVirtualFile) {
- clang::FileManager Files((clang::FileSystemOptions()));
+ IntrusiveRefCntPtr<clang::FileManager> Files(
+ new clang::FileManager(clang::FileSystemOptions()));
std::vector<std::string> Args;
Args.push_back("tool-executable");
Args.push_back("-Idef");
Args.push_back("-fsyntax-only");
Args.push_back("test.cpp");
- clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, &Files);
+ clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
+ Files.getPtr());
Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
Invocation.mapVirtualFile("def/abc", "\n");
EXPECT_TRUE(Invocation.run());
}
-struct VerifyEndCallback : public EndOfSourceFileCallback {
- VerifyEndCallback() : Called(0), Matched(false) {}
- virtual void run() {
- ++Called;
+TEST(ToolInvocation, TestVirtualModulesCompilation) {
+ // FIXME: Currently, this only tests that we don't exit with an error if a
+ // mapped module.map is found on the include path. In the future, expand this
+ // test to run a full modules enabled compilation, so we make sure we can
+ // rerun modules compilations with a virtual file system.
+ IntrusiveRefCntPtr<clang::FileManager> Files(
+ new clang::FileManager(clang::FileSystemOptions()));
+ std::vector<std::string> Args;
+ Args.push_back("tool-executable");
+ Args.push_back("-Idef");
+ Args.push_back("-fsyntax-only");
+ Args.push_back("test.cpp");
+ clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction,
+ Files.getPtr());
+ Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
+ Invocation.mapVirtualFile("def/abc", "\n");
+ // Add a module.map file in the include directory of our header, so we trigger
+ // the module.map header search logic.
+ Invocation.mapVirtualFile("def/module.map", "\n");
+ EXPECT_TRUE(Invocation.run());
+}
+
+struct VerifyEndCallback : public SourceFileCallbacks {
+ VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {}
+ virtual bool handleBeginSource(CompilerInstance &CI,
+ StringRef Filename) LLVM_OVERRIDE {
+ ++BeginCalled;
+ return true;
+ }
+ virtual void handleEndSource() {
+ ++EndCalled;
}
ASTConsumer *newASTConsumer() {
return new FindTopLevelDeclConsumer(&Matched);
}
- unsigned Called;
+ unsigned BeginCalled;
+ unsigned EndCalled;
bool Matched;
};
#if !defined(_WIN32)
-TEST(newFrontendActionFactory, InjectsEndOfSourceFileCallback) {
+TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
VerifyEndCallback EndCallback;
FixedCompilationDatabase Compilations("/", std::vector<std::string>());
@@ -159,7 +213,8 @@ TEST(newFrontendActionFactory, InjectsEndOfSourceFileCallback) {
Tool.run(newFrontendActionFactory(&EndCallback, &EndCallback));
EXPECT_TRUE(EndCallback.Matched);
- EXPECT_EQ(2u, EndCallback.Called);
+ EXPECT_EQ(2u, EndCallback.BeginCalled);
+ EXPECT_EQ(2u, EndCallback.EndCalled);
}
#endif
@@ -186,5 +241,97 @@ TEST(runToolOnCode, TestSkipFunctionBody) {
"int skipMeNot() { an_error_here }"));
}
+struct CheckSyntaxOnlyAdjuster: public ArgumentsAdjuster {
+ bool &Found;
+ bool &Ran;
+
+ CheckSyntaxOnlyAdjuster(bool &Found, bool &Ran) : Found(Found), Ran(Ran) { }
+
+ virtual CommandLineArguments
+ Adjust(const CommandLineArguments &Args) LLVM_OVERRIDE {
+ Ran = true;
+ for (unsigned I = 0, E = Args.size(); I != E; ++I) {
+ if (Args[I] == "-fsyntax-only") {
+ Found = true;
+ break;
+ }
+ }
+ return Args;
+ }
+};
+
+TEST(ClangToolTest, ArgumentAdjusters) {
+ FixedCompilationDatabase Compilations("/", std::vector<std::string>());
+
+ ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
+ Tool.mapVirtualFile("/a.cc", "void a() {}");
+
+ bool Found = false;
+ bool Ran = false;
+ Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran));
+ Tool.run(newFrontendActionFactory<SyntaxOnlyAction>());
+ EXPECT_TRUE(Ran);
+ EXPECT_TRUE(Found);
+
+ Ran = Found = false;
+ Tool.clearArgumentsAdjusters();
+ Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran));
+ Tool.appendArgumentsAdjuster(new ClangSyntaxOnlyAdjuster());
+ Tool.run(newFrontendActionFactory<SyntaxOnlyAction>());
+ EXPECT_TRUE(Ran);
+ EXPECT_FALSE(Found);
+}
+
+#ifndef _WIN32
+TEST(ClangToolTest, BuildASTs) {
+ FixedCompilationDatabase Compilations("/", std::vector<std::string>());
+
+ std::vector<std::string> Sources;
+ Sources.push_back("/a.cc");
+ Sources.push_back("/b.cc");
+ ClangTool Tool(Compilations, Sources);
+
+ Tool.mapVirtualFile("/a.cc", "void a() {}");
+ Tool.mapVirtualFile("/b.cc", "void b() {}");
+
+ std::vector<ASTUnit *> ASTs;
+ EXPECT_EQ(0, Tool.buildASTs(ASTs));
+ EXPECT_EQ(2u, ASTs.size());
+
+ llvm::DeleteContainerPointers(ASTs);
+}
+
+struct TestDiagnosticConsumer : public DiagnosticConsumer {
+ TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {}
+ virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+ const Diagnostic &Info) {
+ ++NumDiagnosticsSeen;
+ }
+ unsigned NumDiagnosticsSeen;
+};
+
+TEST(ClangToolTest, InjectDiagnosticConsumer) {
+ FixedCompilationDatabase Compilations("/", std::vector<std::string>());
+ ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
+ Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
+ TestDiagnosticConsumer Consumer;
+ Tool.setDiagnosticConsumer(&Consumer);
+ Tool.run(newFrontendActionFactory<SyntaxOnlyAction>());
+ EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
+}
+
+TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) {
+ FixedCompilationDatabase Compilations("/", std::vector<std::string>());
+ ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc"));
+ Tool.mapVirtualFile("/a.cc", "int x = undeclared;");
+ TestDiagnosticConsumer Consumer;
+ Tool.setDiagnosticConsumer(&Consumer);
+ std::vector<ASTUnit*> ASTs;
+ Tool.buildASTs(ASTs);
+ EXPECT_EQ(1u, ASTs.size());
+ EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen);
+}
+#endif
+
} // end namespace tooling
} // end namespace clang
OpenPOWER on IntegriCloud