summaryrefslogtreecommitdiff
path: root/unittests/Tooling
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2016-07-23 20:44:14 +0000
committerDimitry Andric <dim@FreeBSD.org>2016-07-23 20:44:14 +0000
commit2b6b257f4e5503a7a2675bdb8735693db769f75c (patch)
treee85e046ae7003fe3bcc8b5454cd0fa3f7407b470 /unittests/Tooling
parentb4348ed0b7e90c0831b925fbee00b5f179a99796 (diff)
Notes
Diffstat (limited to 'unittests/Tooling')
-rw-r--r--unittests/Tooling/CMakeLists.txt15
-rw-r--r--unittests/Tooling/FixItTest.cpp232
-rw-r--r--unittests/Tooling/Makefile20
-rw-r--r--unittests/Tooling/QualTypeNamesTest.cpp222
-rw-r--r--unittests/Tooling/RecursiveASTVisitorTest.cpp26
-rw-r--r--unittests/Tooling/RefactoringTest.cpp165
-rw-r--r--unittests/Tooling/RewriterTest.cpp5
-rw-r--r--unittests/Tooling/ToolingTest.cpp57
8 files changed, 700 insertions, 42 deletions
diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt
index 33b2046ae9288..b4b3f404e2709 100644
--- a/unittests/Tooling/CMakeLists.txt
+++ b/unittests/Tooling/CMakeLists.txt
@@ -3,26 +3,35 @@ set(LLVM_LINK_COMPONENTS
Support
)
+# By default MSVC has a 2^16 limit on the number of sections in an object file,
+# and this needs more than that.
+if (MSVC)
+ set_source_files_properties(RecursiveASTVisitorTestExprVisitor.cpp PROPERTIES COMPILE_FLAGS /bigobj)
+endif()
+
add_clang_unittest(ToolingTests
CommentHandlerTest.cpp
CompilationDatabaseTest.cpp
+ FixItTest.cpp
LookupTest.cpp
- ToolingTest.cpp
+ QualTypeNamesTest.cpp
RecursiveASTVisitorTest.cpp
RecursiveASTVisitorTestCallVisitor.cpp
RecursiveASTVisitorTestDeclVisitor.cpp
RecursiveASTVisitorTestExprVisitor.cpp
RecursiveASTVisitorTestTypeLocVisitor.cpp
- RefactoringTest.cpp
- RewriterTest.cpp
RefactoringCallbacksTest.cpp
+ RefactoringTest.cpp
ReplacementsYamlTest.cpp
+ RewriterTest.cpp
+ ToolingTest.cpp
)
target_link_libraries(ToolingTests
clangAST
clangASTMatchers
clangBasic
+ clangFormat
clangFrontend
clangLex
clangRewrite
diff --git a/unittests/Tooling/FixItTest.cpp b/unittests/Tooling/FixItTest.cpp
new file mode 100644
index 0000000000000..365180e67fe6a
--- /dev/null
+++ b/unittests/Tooling/FixItTest.cpp
@@ -0,0 +1,232 @@
+//===- unittest/Tooling/FixitTest.cpp ------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang;
+
+using tooling::fixit::getText;
+using tooling::fixit::createRemoval;
+using tooling::fixit::createReplacement;
+
+namespace {
+
+struct CallsVisitor : TestVisitor<CallsVisitor> {
+ bool VisitCallExpr(CallExpr *Expr) {
+ OnCall(Expr, Context);
+ return true;
+ }
+
+ std::function<void(CallExpr *, ASTContext *Context)> OnCall;
+};
+
+std::string LocationToString(SourceLocation Loc, ASTContext *Context) {
+ return Loc.printToString(Context->getSourceManager());
+}
+
+TEST(FixItTest, getText) {
+ CallsVisitor Visitor;
+
+ Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+ EXPECT_EQ("foo(x, y)", getText(*CE, *Context));
+ EXPECT_EQ("foo(x, y)", getText(CE->getSourceRange(), *Context));
+
+ Expr *P0 = CE->getArg(0);
+ Expr *P1 = CE->getArg(1);
+ EXPECT_EQ("x", getText(*P0, *Context));
+ EXPECT_EQ("y", getText(*P1, *Context));
+ };
+ Visitor.runOver("void foo(int x, int y) { foo(x, y); }");
+
+ Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+ EXPECT_EQ("APPLY(foo, x, y)", getText(*CE, *Context));
+ };
+ Visitor.runOver("#define APPLY(f, x, y) f(x, y)\n"
+ "void foo(int x, int y) { APPLY(foo, x, y); }");
+}
+
+TEST(FixItTest, getTextWithMacro) {
+ CallsVisitor Visitor;
+
+ Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+ EXPECT_EQ("F OO", getText(*CE, *Context));
+ Expr *P0 = CE->getArg(0);
+ Expr *P1 = CE->getArg(1);
+ EXPECT_EQ("", getText(*P0, *Context));
+ EXPECT_EQ("", getText(*P1, *Context));
+ };
+ Visitor.runOver("#define F foo(\n"
+ "#define OO x, y)\n"
+ "void foo(int x, int y) { F OO ; }");
+
+ Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+ EXPECT_EQ("", getText(*CE, *Context));
+ Expr *P0 = CE->getArg(0);
+ Expr *P1 = CE->getArg(1);
+ EXPECT_EQ("x", getText(*P0, *Context));
+ EXPECT_EQ("y", getText(*P1, *Context));
+ };
+ Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n"
+ "void foo(int x, int y) { FOO(x,y) }");
+}
+
+TEST(FixItTest, createRemoval) {
+ CallsVisitor Visitor;
+
+ Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+ FixItHint Hint = createRemoval(*CE);
+ EXPECT_EQ("foo(x, y)", getText(Hint.RemoveRange.getAsRange(), *Context));
+ EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
+ EXPECT_TRUE(Hint.CodeToInsert.empty());
+
+ Expr *P0 = CE->getArg(0);
+ FixItHint Hint0 = createRemoval(*P0);
+ EXPECT_EQ("x", getText(Hint0.RemoveRange.getAsRange(), *Context));
+ EXPECT_TRUE(Hint0.InsertFromRange.isInvalid());
+ EXPECT_TRUE(Hint0.CodeToInsert.empty());
+
+ Expr *P1 = CE->getArg(1);
+ FixItHint Hint1 = createRemoval(*P1);
+ EXPECT_EQ("y", getText(Hint1.RemoveRange.getAsRange(), *Context));
+ EXPECT_TRUE(Hint1.InsertFromRange.isInvalid());
+ EXPECT_TRUE(Hint1.CodeToInsert.empty());
+ };
+ Visitor.runOver("void foo(int x, int y) { foo(x, y); }");
+
+ Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+ Expr *P0 = CE->getArg(0);
+ FixItHint Hint0 = createRemoval(*P0);
+ EXPECT_EQ("x + y", getText(Hint0.RemoveRange.getAsRange(), *Context));
+
+ Expr *P1 = CE->getArg(1);
+ FixItHint Hint1 = createRemoval(*P1);
+ EXPECT_EQ("y + x", getText(Hint1.RemoveRange.getAsRange(), *Context));
+ };
+ Visitor.runOver("void foo(int x, int y) { foo(x + y, y + x); }");
+}
+
+TEST(FixItTest, createRemovalWithMacro) {
+ CallsVisitor Visitor;
+
+ Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+ FixItHint Hint = createRemoval(*CE);
+ EXPECT_EQ("FOO", getText(Hint.RemoveRange.getAsRange(), *Context));
+ EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
+ EXPECT_TRUE(Hint.CodeToInsert.empty());
+
+ Expr *P0 = CE->getArg(0);
+ FixItHint Hint0 = createRemoval(*P0);
+ EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>",
+ LocationToString(Hint0.RemoveRange.getBegin(), Context));
+ EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>",
+ LocationToString(Hint0.RemoveRange.getEnd(), Context));
+ EXPECT_TRUE(Hint0.InsertFromRange.isInvalid());
+ EXPECT_TRUE(Hint0.CodeToInsert.empty());
+
+ Expr *P1 = CE->getArg(1);
+ FixItHint Hint1 = createRemoval(*P1);
+ EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:20>",
+ LocationToString(Hint1.RemoveRange.getBegin(), Context));
+ EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:20>",
+ LocationToString(Hint1.RemoveRange.getEnd(), Context));
+ EXPECT_TRUE(Hint1.InsertFromRange.isInvalid());
+ EXPECT_TRUE(Hint1.CodeToInsert.empty());
+ };
+ Visitor.runOver("#define FOO foo(1, 1)\n"
+ "void foo(int x, int y) { FOO; }");
+
+ Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+ FixItHint Hint = createRemoval(*CE);
+ EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:37>",
+ LocationToString(Hint.RemoveRange.getBegin(), Context));
+ EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:45>",
+ LocationToString(Hint.RemoveRange.getEnd(), Context));
+ EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
+ EXPECT_TRUE(Hint.CodeToInsert.empty());
+ };
+ Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n"
+ "void foo(int x, int y) { FOO(x,y) }");
+}
+
+TEST(FixItTest, createReplacement) {
+ CallsVisitor Visitor;
+
+ Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+ Expr *P0 = CE->getArg(0);
+ Expr *P1 = CE->getArg(1);
+ FixItHint Hint0 = createReplacement(*P0, *P1, *Context);
+ FixItHint Hint1 = createReplacement(*P1, *P0, *Context);
+
+ // Validate Hint0 fields.
+ EXPECT_EQ("x", getText(Hint0.RemoveRange.getAsRange(), *Context));
+ EXPECT_TRUE(Hint0.InsertFromRange.isInvalid());
+ EXPECT_EQ(Hint0.CodeToInsert, "y");
+
+ // Validate Hint1 fields.
+ EXPECT_EQ("y", getText(Hint1.RemoveRange.getAsRange(), *Context));
+ EXPECT_TRUE(Hint1.InsertFromRange.isInvalid());
+ EXPECT_EQ(Hint1.CodeToInsert, "x");
+ };
+
+ Visitor.runOver("void foo(int x, int y) { foo(x, y); }");
+
+ Visitor.runOver("#define APPLY(f, x, y) f(x, y)\n"
+ "void foo(int x, int y) { APPLY(foo, x, y); }");
+
+ Visitor.runOver("#define APPLY(f, P) f(P)\n"
+ "#define PAIR(x, y) x, y\n"
+ "void foo(int x, int y) { APPLY(foo, PAIR(x, y)); }\n");
+}
+
+TEST(FixItTest, createReplacementWithMacro) {
+ CallsVisitor Visitor;
+
+ Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+ Expr *P0 = CE->getArg(0);
+ Expr *P1 = CE->getArg(1);
+ FixItHint Hint = createReplacement(*P0, *P1, *Context);
+ EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>",
+ LocationToString(Hint.RemoveRange.getBegin(), Context));
+ EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>",
+ LocationToString(Hint.RemoveRange.getEnd(), Context));
+ EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
+ EXPECT_TRUE(Hint.CodeToInsert.empty());
+ };
+
+ Visitor.runOver("#define FOO foo(1, 1)\n"
+ "void foo(int x, int y) { FOO; }");
+
+ Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+ Expr *P0 = CE->getArg(0);
+ Expr *P1 = CE->getArg(1);
+ FixItHint Hint = createReplacement(*P0, *P1, *Context);
+ EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:2:30>",
+ LocationToString(Hint.RemoveRange.getBegin(), Context));
+ EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:2:30>",
+ LocationToString(Hint.RemoveRange.getEnd(), Context));
+ EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
+ EXPECT_EQ("y", Hint.CodeToInsert);
+ };
+ Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n"
+ "void foo(int x, int y) { FOO(x,y) }");
+
+ Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) {
+ Expr *P0 = CE->getArg(0);
+ Expr *P1 = CE->getArg(1);
+ FixItHint Hint = createReplacement(*P0, *P1, *Context);
+ EXPECT_EQ("x + y", getText(Hint.RemoveRange.getAsRange(), *Context));
+ EXPECT_TRUE(Hint.InsertFromRange.isInvalid());
+ EXPECT_EQ("y + x", Hint.CodeToInsert);
+ };
+ Visitor.runOver("void foo(int x, int y) { foo(x + y, y + x); }");
+}
+
+} // end anonymous namespace
diff --git a/unittests/Tooling/Makefile b/unittests/Tooling/Makefile
deleted file mode 100644
index 514e80bd03098..0000000000000
--- a/unittests/Tooling/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-##===- unittests/Tooling/Makefile --------------------------*- Makefile -*-===##
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-CLANG_LEVEL = ../..
-TESTNAME = Tooling
-include $(CLANG_LEVEL)/../../Makefile.config
-LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option
-USEDLIBS = clangTooling.a clangToolingCore.a clangFrontend.a \
- clangSerialization.a clangDriver.a \
- clangParse.a clangRewrite.a clangRewriteFrontend.a \
- clangSema.a clangAnalysis.a clangEdit.a \
- clangAST.a clangASTMatchers.a clangLex.a clangBasic.a
-
-include $(CLANG_LEVEL)/unittests/Makefile
diff --git a/unittests/Tooling/QualTypeNamesTest.cpp b/unittests/Tooling/QualTypeNamesTest.cpp
new file mode 100644
index 0000000000000..edd5060ba0e53
--- /dev/null
+++ b/unittests/Tooling/QualTypeNamesTest.cpp
@@ -0,0 +1,222 @@
+//===- unittest/Tooling/QualTypeNameTest.cpp ------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Core/QualTypeNames.h"
+#include "TestVisitor.h"
+using namespace clang;
+
+namespace {
+struct TypeNameVisitor : TestVisitor<TypeNameVisitor> {
+ llvm::StringMap<std::string> ExpectedQualTypeNames;
+ bool WithGlobalNsPrefix = false;
+
+ // ValueDecls are the least-derived decl with both a qualtype and a
+ // name.
+ bool traverseDecl(Decl *D) {
+ return true; // Always continue
+ }
+
+ bool VisitValueDecl(const ValueDecl *VD) {
+ std::string ExpectedName =
+ ExpectedQualTypeNames.lookup(VD->getNameAsString());
+ if (ExpectedName != "") {
+ std::string ActualName =
+ TypeName::getFullyQualifiedName(VD->getType(), *Context,
+ WithGlobalNsPrefix);
+ if (ExpectedName != ActualName) {
+ // A custom message makes it much easier to see what declaration
+ // failed compared to EXPECT_EQ.
+ EXPECT_TRUE(false) << "Typename::getFullyQualifiedName failed for "
+ << VD->getQualifiedNameAsString() << std::endl
+ << " Actual: " << ActualName << std::endl
+ << " Exepcted: " << ExpectedName;
+ }
+ }
+ return true;
+ }
+};
+
+// named namespaces inside anonymous namespaces
+
+TEST(QualTypeNameTest, getFullyQualifiedName) {
+ TypeNameVisitor Visitor;
+ // Simple case to test the test framework itself.
+ Visitor.ExpectedQualTypeNames["CheckInt"] = "int";
+
+ // Keeping the names of the variables whose types we check unique
+ // within the entire test--regardless of their own scope--makes it
+ // easier to diagnose test failures.
+
+ // Simple namespace qualifier
+ Visitor.ExpectedQualTypeNames["CheckA"] = "A::B::Class0";
+ // Lookup up the enclosing scopes, then down another one. (These
+ // appear as elaborated type in the AST. In that case--even if
+ // policy.SuppressScope = 0--qual_type.getAsString(policy) only
+ // gives the name as it appears in the source, not the full name.
+ Visitor.ExpectedQualTypeNames["CheckB"] = "A::B::C::Class1";
+ // Template parameter expansion.
+ Visitor.ExpectedQualTypeNames["CheckC"] =
+ "A::B::Template0<A::B::C::MyInt, A::B::AnotherClass>";
+ // Recursive template parameter expansion.
+ Visitor.ExpectedQualTypeNames["CheckD"] =
+ "A::B::Template0<A::B::Template1<A::B::C::MyInt, A::B::AnotherClass>, "
+ "A::B::Template0<int, long> >";
+ // Variadic Template expansion.
+ Visitor.ExpectedQualTypeNames["CheckE"] =
+ "A::Variadic<int, A::B::Template0<int, char>, "
+ "A::B::Template1<int, long>, A::B::C::MyInt>";
+ // Using declarations should be fully expanded.
+ Visitor.ExpectedQualTypeNames["CheckF"] = "A::B::Class0";
+ // Elements found within "using namespace foo;" should be fully
+ // expanded.
+ Visitor.ExpectedQualTypeNames["CheckG"] = "A::B::C::MyInt";
+ // Type inside function
+ Visitor.ExpectedQualTypeNames["CheckH"] = "struct X";
+ // Anonymous Namespaces
+ Visitor.ExpectedQualTypeNames["CheckI"] = "aClass";
+ // Keyword inclusion with namespaces
+ Visitor.ExpectedQualTypeNames["CheckJ"] = "struct A::aStruct";
+ // Anonymous Namespaces nested in named namespaces and vice-versa.
+ Visitor.ExpectedQualTypeNames["CheckK"] = "D::aStruct";
+ // Namespace alias
+ Visitor.ExpectedQualTypeNames["CheckL"] = "A::B::C::MyInt";
+ Visitor.ExpectedQualTypeNames["non_dependent_type_var"] =
+ "Foo<X>::non_dependent_type";
+ Visitor.ExpectedQualTypeNames["AnEnumVar"] = "EnumScopeClass::AnEnum";
+ Visitor.ExpectedQualTypeNames["AliasTypeVal"] = "A::B::C::InnerAlias<int>";
+ Visitor.ExpectedQualTypeNames["CheckM"] = "const A::B::Class0 *";
+ Visitor.ExpectedQualTypeNames["CheckN"] = "const X *";
+ Visitor.runOver(
+ "int CheckInt;\n"
+ "template <typename T>\n"
+ "class OuterTemplateClass { };\n"
+ "namespace A {\n"
+ " namespace B {\n"
+ " class Class0 { };\n"
+ " namespace C {\n"
+ " typedef int MyInt;"
+ " template <typename T>\n"
+ " using InnerAlias = OuterTemplateClass<T>;\n"
+ " InnerAlias<int> AliasTypeVal;\n"
+ " }\n"
+ " template<class X, class Y> class Template0;"
+ " template<class X, class Y> class Template1;"
+ " typedef B::Class0 AnotherClass;\n"
+ " void Function1(Template0<C::MyInt,\n"
+ " AnotherClass> CheckC);\n"
+ " void Function2(Template0<Template1<C::MyInt, AnotherClass>,\n"
+ " Template0<int, long> > CheckD);\n"
+ " void Function3(const B::Class0* CheckM);\n"
+ " }\n"
+ "template<typename... Values> class Variadic {};\n"
+ "Variadic<int, B::Template0<int, char>, "
+ " B::Template1<int, long>, "
+ " B::C::MyInt > CheckE;\n"
+ " namespace BC = B::C;\n"
+ " BC::MyInt CheckL;\n"
+ "}\n"
+ "using A::B::Class0;\n"
+ "void Function(Class0 CheckF);\n"
+ "using namespace A::B::C;\n"
+ "void Function(MyInt CheckG);\n"
+ "void f() {\n"
+ " struct X {} CheckH;\n"
+ "}\n"
+ "struct X;\n"
+ "void f(const ::X* CheckN) {}\n"
+ "namespace {\n"
+ " class aClass {};\n"
+ " aClass CheckI;\n"
+ "}\n"
+ "namespace A {\n"
+ " struct aStruct {} CheckJ;\n"
+ "}\n"
+ "namespace {\n"
+ " namespace D {\n"
+ " namespace {\n"
+ " class aStruct {};\n"
+ " aStruct CheckK;\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "template<class T> struct Foo {\n"
+ " typedef typename T::A dependent_type;\n"
+ " typedef int non_dependent_type;\n"
+ " dependent_type dependent_type_var;\n"
+ " non_dependent_type non_dependent_type_var;\n"
+ "};\n"
+ "struct X { typedef int A; };"
+ "Foo<X> var;"
+ "void F() {\n"
+ " var.dependent_type_var = 0;\n"
+ "var.non_dependent_type_var = 0;\n"
+ "}\n"
+ "class EnumScopeClass {\n"
+ "public:\n"
+ " enum AnEnum { ZERO, ONE };\n"
+ "};\n"
+ "EnumScopeClass::AnEnum AnEnumVar;\n",
+ TypeNameVisitor::Lang_CXX11
+);
+
+ TypeNameVisitor Complex;
+ Complex.ExpectedQualTypeNames["CheckTX"] = "B::TX";
+ Complex.runOver(
+ "namespace A {"
+ " struct X {};"
+ "}"
+ "using A::X;"
+ "namespace fake_std {"
+ " template<class... Types > class tuple {};"
+ "}"
+ "namespace B {"
+ " using fake_std::tuple;"
+ " typedef tuple<X> TX;"
+ " TX CheckTX;"
+ " struct A { typedef int X; };"
+ "}");
+
+ TypeNameVisitor GlobalNsPrefix;
+ GlobalNsPrefix.WithGlobalNsPrefix = true;
+ GlobalNsPrefix.ExpectedQualTypeNames["IntVal"] = "int";
+ GlobalNsPrefix.ExpectedQualTypeNames["BoolVal"] = "bool";
+ GlobalNsPrefix.ExpectedQualTypeNames["XVal"] = "::A::B::X";
+ GlobalNsPrefix.ExpectedQualTypeNames["IntAliasVal"] = "::A::B::Alias<int>";
+ GlobalNsPrefix.ExpectedQualTypeNames["ZVal"] = "::A::B::Y::Z";
+ GlobalNsPrefix.ExpectedQualTypeNames["GlobalZVal"] = "::Z";
+ GlobalNsPrefix.ExpectedQualTypeNames["CheckK"] = "D::aStruct";
+ GlobalNsPrefix.runOver(
+ "namespace A {\n"
+ " namespace B {\n"
+ " int IntVal;\n"
+ " bool BoolVal;\n"
+ " struct X {};\n"
+ " X XVal;\n"
+ " template <typename T> class CCC { };\n"
+ " template <typename T>\n"
+ " using Alias = CCC<T>;\n"
+ " Alias<int> IntAliasVal;\n"
+ " struct Y { struct Z {}; };\n"
+ " Y::Z ZVal;\n"
+ " }\n"
+ "}\n"
+ "struct Z {};\n"
+ "Z GlobalZVal;\n"
+ "namespace {\n"
+ " namespace D {\n"
+ " namespace {\n"
+ " class aStruct {};\n"
+ " aStruct CheckK;\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ );
+}
+
+} // end anonymous namespace
diff --git a/unittests/Tooling/RecursiveASTVisitorTest.cpp b/unittests/Tooling/RecursiveASTVisitorTest.cpp
index c28704532a6ed..991ae8bb7f3d2 100644
--- a/unittests/Tooling/RecursiveASTVisitorTest.cpp
+++ b/unittests/Tooling/RecursiveASTVisitorTest.cpp
@@ -42,13 +42,13 @@ TEST(RecursiveASTVisitor, VisitsLambdaExpr) {
LambdaExprVisitor Visitor;
Visitor.ExpectMatch("", 1, 12);
EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
- LambdaExprVisitor::Lang_CXX11));
+ LambdaExprVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, TraverseLambdaBodyCanBeOverridden) {
LambdaExprVisitor Visitor;
EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
- LambdaExprVisitor::Lang_CXX11));
+ LambdaExprVisitor::Lang_CXX11));
EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
}
@@ -92,8 +92,7 @@ private:
TEST(RecursiveASTVisitor, LambdaClosureTypesAreImplicit) {
ClassVisitor Visitor;
- EXPECT_TRUE(Visitor.runOver("auto lambda = []{};",
- ClassVisitor::Lang_CXX11));
+ EXPECT_TRUE(Visitor.runOver("auto lambda = []{};", ClassVisitor::Lang_CXX11));
EXPECT_TRUE(Visitor.sawOnlyImplicitLambdaClasses());
}
@@ -134,4 +133,23 @@ TEST(RecursiveASTVisitor, AttributesAreVisited) {
"};\n"));
}
+// Check to ensure that VarDecls are visited.
+class VarDeclVisitor : public ExpectedLocationVisitor<VarDeclVisitor> {
+public:
+ bool VisitVarDecl(VarDecl *VD) {
+ Match(VD->getNameAsString(), VD->getLocStart());
+ return true;
+ }
+};
+
+TEST(RecursiveASTVisitor, ArrayInitializersAreVisited) {
+ VarDeclVisitor Visitor;
+ Visitor.ExpectMatch("__i0", 1, 8);
+ EXPECT_TRUE(
+ Visitor.runOver("struct MyClass {\n"
+ " int c[1];\n"
+ " static MyClass Create() { return MyClass(); }\n"
+ "};\n"));
+}
+
} // end anonymous namespace
diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp
index ff11aeae117ab..df96bb159dea7 100644
--- a/unittests/Tooling/RefactoringTest.cpp
+++ b/unittests/Tooling/RefactoringTest.cpp
@@ -18,6 +18,7 @@
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Format/Format.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
@@ -166,6 +167,39 @@ TEST_F(ReplacementTest, ApplyAllFailsIfOneApplyFails) {
EXPECT_EQ("z", Context.getRewrittenText(IDz));
}
+TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) {
+ // Column limit is 20.
+ std::string Code1 = "Long *a =\n"
+ " new Long();\n"
+ "long x = 1;";
+ std::string Expected1 = "auto a = new Long();\n"
+ "long x =\n"
+ " 12345678901;";
+ std::string Code2 = "int x = 123;\n"
+ "int y = 0;";
+ std::string Expected2 = "int x =\n"
+ " 1234567890123;\n"
+ "int y = 10;";
+ FileID ID1 = Context.createInMemoryFile("format_1.cpp", Code1);
+ FileID ID2 = Context.createInMemoryFile("format_2.cpp", Code2);
+
+ tooling::Replacements Replaces;
+ // Scrambled the order of replacements.
+ Replaces.insert(tooling::Replacement(
+ Context.Sources, Context.getLocation(ID2, 1, 12), 0, "4567890123"));
+ Replaces.insert(tooling::Replacement(
+ Context.Sources, Context.getLocation(ID1, 1, 1), 6, "auto "));
+ Replaces.insert(tooling::Replacement(
+ Context.Sources, Context.getLocation(ID2, 2, 9), 1, "10"));
+ Replaces.insert(tooling::Replacement(
+ Context.Sources, Context.getLocation(ID1, 3, 10), 1, "12345678901"));
+
+ EXPECT_TRUE(formatAndApplyAllReplacements(
+ Replaces, Context.Rewrite, "{BasedOnStyle: LLVM, ColumnLimit: 20}"));
+ EXPECT_EQ(Expected1, Context.getRewrittenText(ID1));
+ EXPECT_EQ(Expected2, Context.getRewrittenText(ID2));
+}
+
TEST(ShiftedCodePositionTest, FindsNewCodePosition) {
Replacements Replaces;
Replaces.insert(Replacement("", 0, 1, ""));
@@ -418,6 +452,108 @@ TEST(Range, contains) {
EXPECT_FALSE(Range(0, 10).contains(Range(0, 11)));
}
+TEST(Range, CalculateRangesOfReplacements) {
+ // Before: aaaabbbbbbz
+ // After : bbbbbbzzzzzzoooooooooooooooo
+ Replacements Replaces;
+ Replaces.insert(Replacement("foo", 0, 4, ""));
+ Replaces.insert(Replacement("foo", 10, 1, "zzzzzz"));
+ Replaces.insert(Replacement("foo", 11, 0, "oooooooooooooooo"));
+
+ std::vector<Range> Ranges = calculateChangedRanges(Replaces);
+
+ EXPECT_EQ(2ul, Ranges.size());
+ EXPECT_TRUE(Ranges[0].getOffset() == 0);
+ EXPECT_TRUE(Ranges[0].getLength() == 0);
+ EXPECT_TRUE(Ranges[1].getOffset() == 6);
+ EXPECT_TRUE(Ranges[1].getLength() == 22);
+}
+
+TEST(Range, RangesAfterReplacements) {
+ std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)};
+ Replacements Replaces = {Replacement("foo", 0, 2, "1234")};
+ std::vector<Range> Expected = {Range(0, 4), Range(7, 2), Range(12, 5)};
+ EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
+}
+
+TEST(Range, RangesBeforeReplacements) {
+ std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)};
+ Replacements Replaces = {Replacement("foo", 20, 2, "1234")};
+ std::vector<Range> Expected = {Range(5, 2), Range(10, 5), Range(20, 4)};
+ EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
+}
+
+TEST(Range, NotAffectedByReplacements) {
+ std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
+ Replacements Replaces = {Replacement("foo", 3, 2, "12"),
+ Replacement("foo", 12, 2, "12"),
+ Replacement("foo", 20, 5, "")};
+ std::vector<Range> Expected = {Range(0, 2), Range(3, 4), Range(10, 5),
+ Range(20, 0)};
+ EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
+}
+
+TEST(Range, RangesWithNonOverlappingReplacements) {
+ std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(10, 5)};
+ Replacements Replaces = {Replacement("foo", 3, 1, ""),
+ Replacement("foo", 6, 1, "123"),
+ Replacement("foo", 20, 2, "12345")};
+ std::vector<Range> Expected = {Range(0, 2), Range(3, 0), Range(4, 4),
+ Range(11, 5), Range(21, 5)};
+ EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
+}
+
+TEST(Range, RangesWithOverlappingReplacements) {
+ std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5),
+ Range(30, 5)};
+ Replacements Replaces = {
+ Replacement("foo", 1, 3, ""), Replacement("foo", 6, 1, "123"),
+ Replacement("foo", 13, 3, "1"), Replacement("foo", 25, 15, "")};
+ std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(12, 5),
+ Range(22, 0)};
+ EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
+}
+
+TEST(Range, MergeIntoOneRange) {
+ std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
+ Replacements Replaces = {Replacement("foo", 1, 15, "1234567890")};
+ std::vector<Range> Expected = {Range(0, 15)};
+ EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
+}
+
+TEST(Range, ReplacementsStartingAtRangeOffsets) {
+ std::vector<Range> Ranges = {Range(0, 2), Range(5, 5), Range(15, 5)};
+ Replacements Replaces = {
+ Replacement("foo", 0, 2, "12"), Replacement("foo", 5, 1, "123"),
+ Replacement("foo", 7, 4, "12345"), Replacement("foo", 15, 10, "12")};
+ std::vector<Range> Expected = {Range(0, 2), Range(5, 9), Range(18, 2)};
+ EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
+}
+
+TEST(Range, ReplacementsEndingAtRangeEnds) {
+ std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)};
+ Replacements Replaces = {Replacement("foo", 6, 1, "123"),
+ Replacement("foo", 17, 3, "12")};
+ std::vector<Range> Expected = {Range(0, 2), Range(5, 4), Range(17, 4)};
+ EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
+}
+
+TEST(Range, AjacentReplacements) {
+ std::vector<Range> Ranges = {Range(0, 0), Range(15, 5)};
+ Replacements Replaces = {Replacement("foo", 1, 2, "123"),
+ Replacement("foo", 12, 3, "1234")};
+ std::vector<Range> Expected = {Range(0, 0), Range(1, 3), Range(13, 9)};
+ EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
+}
+
+TEST(Range, MergeRangesAfterReplacements) {
+ std::vector<Range> Ranges = {Range(8, 0), Range(5, 2), Range(9, 0), Range(0, 1)};
+ Replacements Replaces = {Replacement("foo", 1, 3, ""),
+ Replacement("foo", 7, 0, "12"), Replacement("foo", 9, 2, "")};
+ std::vector<Range> Expected = {Range(0, 1), Range(2, 4), Range(7, 0), Range(8, 0)};
+ EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges));
+}
+
TEST(DeduplicateTest, removesDuplicates) {
std::vector<Replacement> Input;
Input.push_back(Replacement("fileA", 50, 0, " foo "));
@@ -504,27 +640,32 @@ protected:
StringRef Result, const Replacements &First,
const Replacements &Second) {
// These are mainly to verify the test itself and make it easier to read.
- std::string AfterFirst = applyAllReplacements(Code, First);
- std::string InSequenceRewrite = applyAllReplacements(AfterFirst, Second);
- EXPECT_EQ(Intermediate, AfterFirst);
- EXPECT_EQ(Result, InSequenceRewrite);
+ auto AfterFirst = applyAllReplacements(Code, First);
+ EXPECT_TRUE(static_cast<bool>(AfterFirst));
+ auto InSequenceRewrite = applyAllReplacements(*AfterFirst, Second);
+ EXPECT_TRUE(static_cast<bool>(InSequenceRewrite));
+ EXPECT_EQ(Intermediate, *AfterFirst);
+ EXPECT_EQ(Result, *InSequenceRewrite);
tooling::Replacements Merged = mergeReplacements(First, Second);
- std::string MergedRewrite = applyAllReplacements(Code, Merged);
- EXPECT_EQ(InSequenceRewrite, MergedRewrite);
- if (InSequenceRewrite != MergedRewrite)
+ auto MergedRewrite = applyAllReplacements(Code, Merged);
+ EXPECT_TRUE(static_cast<bool>(MergedRewrite));
+ EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
+ if (*InSequenceRewrite != *MergedRewrite)
for (tooling::Replacement M : Merged)
llvm::errs() << M.getOffset() << " " << M.getLength() << " "
<< M.getReplacementText() << "\n";
}
void mergeAndTestRewrite(StringRef Code, const Replacements &First,
const Replacements &Second) {
- std::string InSequenceRewrite =
- applyAllReplacements(applyAllReplacements(Code, First), Second);
+ auto AfterFirst = applyAllReplacements(Code, First);
+ EXPECT_TRUE(static_cast<bool>(AfterFirst));
+ auto InSequenceRewrite = applyAllReplacements(*AfterFirst, Second);
tooling::Replacements Merged = mergeReplacements(First, Second);
- std::string MergedRewrite = applyAllReplacements(Code, Merged);
- EXPECT_EQ(InSequenceRewrite, MergedRewrite);
- if (InSequenceRewrite != MergedRewrite)
+ auto MergedRewrite = applyAllReplacements(Code, Merged);
+ EXPECT_TRUE(static_cast<bool>(MergedRewrite));
+ EXPECT_EQ(*InSequenceRewrite, *MergedRewrite);
+ if (*InSequenceRewrite != *MergedRewrite)
for (tooling::Replacement M : Merged)
llvm::errs() << M.getOffset() << " " << M.getLength() << " "
<< M.getReplacementText() << "\n";
diff --git a/unittests/Tooling/RewriterTest.cpp b/unittests/Tooling/RewriterTest.cpp
index 93f69eb9fa0c3..e8afedb01174f 100644
--- a/unittests/Tooling/RewriterTest.cpp
+++ b/unittests/Tooling/RewriterTest.cpp
@@ -41,8 +41,9 @@ TEST(Rewriter, AdjacentInsertAndDelete) {
Replacements Replaces;
Replaces.insert(Replacement("<file>", 6, 6, ""));
Replaces.insert(Replacement("<file>", 6, 0, "replaced\n"));
- EXPECT_EQ("line1\nreplaced\nline3\nline4",
- applyAllReplacements("line1\nline2\nline3\nline4", Replaces));
+ auto Rewritten = applyAllReplacements("line1\nline2\nline3\nline4", Replaces);
+ EXPECT_TRUE(static_cast<bool>(Rewritten));
+ EXPECT_EQ("line1\nreplaced\nline3\nline4", *Rewritten);
}
} // end namespace
diff --git a/unittests/Tooling/ToolingTest.cpp b/unittests/Tooling/ToolingTest.cpp
index c4b174f183da8..10ac0c33ed85d 100644
--- a/unittests/Tooling/ToolingTest.cpp
+++ b/unittests/Tooling/ToolingTest.cpp
@@ -241,7 +241,7 @@ TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) {
struct SkipBodyConsumer : public clang::ASTConsumer {
/// Skip the 'skipMe' function.
bool shouldSkipFunctionBody(Decl *D) override {
- FunctionDecl *F = dyn_cast<FunctionDecl>(D);
+ NamedDecl *F = dyn_cast<NamedDecl>(D);
return F && F->getNameAsString() == "skipMe";
}
};
@@ -255,10 +255,65 @@ struct SkipBodyAction : public clang::ASTFrontendAction {
};
TEST(runToolOnCode, TestSkipFunctionBody) {
+ std::vector<std::string> Args = {"-std=c++11"};
+ std::vector<std::string> Args2 = {"-fno-delayed-template-parsing"};
+
EXPECT_TRUE(runToolOnCode(new SkipBodyAction,
"int skipMe() { an_error_here }"));
EXPECT_FALSE(runToolOnCode(new SkipBodyAction,
"int skipMeNot() { an_error_here }"));
+
+ // Test constructors with initializers
+ EXPECT_TRUE(runToolOnCodeWithArgs(
+ new SkipBodyAction,
+ "struct skipMe { skipMe() : an_error() { more error } };", Args));
+ EXPECT_TRUE(runToolOnCodeWithArgs(
+ new SkipBodyAction, "struct skipMe { skipMe(); };"
+ "skipMe::skipMe() : an_error([](){;}) { more error }",
+ Args));
+ EXPECT_TRUE(runToolOnCodeWithArgs(
+ new SkipBodyAction, "struct skipMe { skipMe(); };"
+ "skipMe::skipMe() : an_error{[](){;}} { more error }",
+ Args));
+ EXPECT_TRUE(runToolOnCodeWithArgs(
+ new SkipBodyAction,
+ "struct skipMe { skipMe(); };"
+ "skipMe::skipMe() : a<b<c>(e)>>(), f{}, g() { error }",
+ Args));
+ EXPECT_TRUE(runToolOnCodeWithArgs(
+ new SkipBodyAction, "struct skipMe { skipMe() : bases()... { error } };",
+ Args));
+
+ EXPECT_FALSE(runToolOnCodeWithArgs(
+ new SkipBodyAction, "struct skipMeNot { skipMeNot() : an_error() { } };",
+ Args));
+ EXPECT_FALSE(runToolOnCodeWithArgs(new SkipBodyAction,
+ "struct skipMeNot { skipMeNot(); };"
+ "skipMeNot::skipMeNot() : an_error() { }",
+ Args));
+
+ // Try/catch
+ EXPECT_TRUE(runToolOnCode(
+ new SkipBodyAction,
+ "void skipMe() try { an_error() } catch(error) { error };"));
+ EXPECT_TRUE(runToolOnCode(
+ new SkipBodyAction,
+ "struct S { void skipMe() try { an_error() } catch(error) { error } };"));
+ EXPECT_TRUE(
+ runToolOnCode(new SkipBodyAction,
+ "void skipMe() try { an_error() } catch(error) { error; }"
+ "catch(error) { error } catch (error) { }"));
+ EXPECT_FALSE(runToolOnCode(
+ new SkipBodyAction,
+ "void skipMe() try something;")); // don't crash while parsing
+
+ // Template
+ EXPECT_TRUE(runToolOnCode(
+ new SkipBodyAction, "template<typename T> int skipMe() { an_error_here }"
+ "int x = skipMe<int>();"));
+ EXPECT_FALSE(runToolOnCodeWithArgs(
+ new SkipBodyAction,
+ "template<typename T> int skipMeNot() { an_error_here }", Args2));
}
TEST(runToolOnCodeWithArgs, TestNoDepFile) {