diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:44:14 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:44:14 +0000 |
commit | 2b6b257f4e5503a7a2675bdb8735693db769f75c (patch) | |
tree | e85e046ae7003fe3bcc8b5454cd0fa3f7407b470 /unittests/Tooling | |
parent | b4348ed0b7e90c0831b925fbee00b5f179a99796 (diff) |
Notes
Diffstat (limited to 'unittests/Tooling')
-rw-r--r-- | unittests/Tooling/CMakeLists.txt | 15 | ||||
-rw-r--r-- | unittests/Tooling/FixItTest.cpp | 232 | ||||
-rw-r--r-- | unittests/Tooling/Makefile | 20 | ||||
-rw-r--r-- | unittests/Tooling/QualTypeNamesTest.cpp | 222 | ||||
-rw-r--r-- | unittests/Tooling/RecursiveASTVisitorTest.cpp | 26 | ||||
-rw-r--r-- | unittests/Tooling/RefactoringTest.cpp | 165 | ||||
-rw-r--r-- | unittests/Tooling/RewriterTest.cpp | 5 | ||||
-rw-r--r-- | unittests/Tooling/ToolingTest.cpp | 57 |
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) { |