diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:18:08 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:18:08 +0000 |
commit | bab175ec4b075c8076ba14c762900392533f6ee4 (patch) | |
tree | 01f4f29419a2cb10abe13c1e63cd2a66068b0137 /unittests | |
parent | 8b7a8012d223fac5d17d16a66bb39168a9a1dfc0 (diff) |
Notes
Diffstat (limited to 'unittests')
44 files changed, 3620 insertions, 1453 deletions
diff --git a/unittests/AST/ASTImporterTest.cpp b/unittests/AST/ASTImporterTest.cpp index 3cc38fb55b2e..57b41f12b0ba 100644 --- a/unittests/AST/ASTImporterTest.cpp +++ b/unittests/AST/ASTImporterTest.cpp @@ -71,8 +71,7 @@ testImport(const std::string &FromCode, Language FromLang, ToCtx.getSourceManager().getFileManager().getVirtualFileSystem().get()); vfs::InMemoryFileSystem *MFS = static_cast<vfs::InMemoryFileSystem *>( OFS->overlays_begin()->get()); - MFS->addFile(InputFileName, 0, - llvm::MemoryBuffer::getMemBuffer(FromCode.c_str())); + MFS->addFile(InputFileName, 0, llvm::MemoryBuffer::getMemBuffer(FromCode)); ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx, FromAST->getFileManager(), false); @@ -456,5 +455,39 @@ TEST(ImportExpr, ImportInitListExpr) { } +const internal::VariadicDynCastAllOfMatcher<Expr, VAArgExpr> vaArgExpr; + +TEST(ImportExpr, ImportVAArgExpr) { + MatchVerifier<Decl> Verifier; + EXPECT_TRUE( + testImport( + "void declToImport(__builtin_va_list list, ...) {" + " (void)__builtin_va_arg(list, int); }", + Lang_CXX, "", Lang_CXX, Verifier, + functionDecl( + hasBody( + compoundStmt( + has( + cStyleCastExpr( + hasSourceExpression( + vaArgExpr())))))))); +} + + +TEST(ImportType, ImportAtomicType) { + MatchVerifier<Decl> Verifier; + EXPECT_TRUE(testImport("void declToImport() { typedef _Atomic(int) a_int; }", + Lang_CXX11, "", Lang_CXX11, Verifier, + functionDecl( + hasBody( + compoundStmt( + has( + declStmt( + has( + typedefDecl( + has(atomicType())))))))))); +} + + } // end namespace ast_matchers } // end namespace clang diff --git a/unittests/AST/ASTTypeTraitsTest.cpp b/unittests/AST/ASTTypeTraitsTest.cpp index 436cd7751411..722c468f30f2 100644 --- a/unittests/AST/ASTTypeTraitsTest.cpp +++ b/unittests/AST/ASTTypeTraitsTest.cpp @@ -163,7 +163,7 @@ TEST(DynTypedNode, StmtDump) { TEST(DynTypedNode, DeclPrint) { PrintVerifier Verifier; - Verifier.expectString("void f() {\n}\n\n"); + Verifier.expectString("void f() {\n}\n"); EXPECT_TRUE(Verifier.match("void f() {}", functionDecl())); } diff --git a/unittests/AST/ASTVectorTest.cpp b/unittests/AST/ASTVectorTest.cpp index 55c06d0071fe..359d2f423259 100644 --- a/unittests/AST/ASTVectorTest.cpp +++ b/unittests/AST/ASTVectorTest.cpp @@ -11,7 +11,6 @@ // //===----------------------------------------------------------------------===// -#include "llvm/Support/Compiler.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTVector.h" #include "clang/Basic/Builtins.h" diff --git a/unittests/AST/CommentParser.cpp b/unittests/AST/CommentParser.cpp index f6ef9b9b7cea..a185f73971df 100644 --- a/unittests/AST/CommentParser.cpp +++ b/unittests/AST/CommentParser.cpp @@ -20,7 +20,6 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Allocator.h" #include "gtest/gtest.h" -#include <vector> using namespace llvm; using namespace clang; diff --git a/unittests/AST/DeclPrinterTest.cpp b/unittests/AST/DeclPrinterTest.cpp index d06fbfea6f5e..e5a09a31f69f 100644 --- a/unittests/AST/DeclPrinterTest.cpp +++ b/unittests/AST/DeclPrinterTest.cpp @@ -45,7 +45,7 @@ public: PrintMatch() : NumFoundDecls(0) {} void run(const MatchFinder::MatchResult &Result) override { - const Decl *D = Result.Nodes.getDeclAs<Decl>("id"); + const Decl *D = Result.Nodes.getNodeAs<Decl>("id"); if (!D || D->isImplicit()) return; NumFoundDecls++; @@ -254,24 +254,21 @@ TEST(DeclPrinter, TestCXXRecordDecl1) { ASSERT_TRUE(PrintedDeclCXX98Matches( "class A { int a; };", "A", - "class A {\n}")); - // Should be: with semicolon, with { ... } + "class A {}")); } TEST(DeclPrinter, TestCXXRecordDecl2) { ASSERT_TRUE(PrintedDeclCXX98Matches( "struct A { int a; };", "A", - "struct A {\n}")); - // Should be: with semicolon, with { ... } + "struct A {}")); } TEST(DeclPrinter, TestCXXRecordDecl3) { ASSERT_TRUE(PrintedDeclCXX98Matches( "union A { int a; };", "A", - "union A {\n}")); - // Should be: with semicolon, with { ... } + "union A {}")); } TEST(DeclPrinter, TestCXXRecordDecl4) { @@ -279,8 +276,7 @@ TEST(DeclPrinter, TestCXXRecordDecl4) { "class Z { int a; };" "class A : Z { int b; };", "A", - "class A : Z {\n}")); - // Should be: with semicolon, with { ... } + "class A : Z {}")); } TEST(DeclPrinter, TestCXXRecordDecl5) { @@ -288,8 +284,7 @@ TEST(DeclPrinter, TestCXXRecordDecl5) { "struct Z { int a; };" "struct A : Z { int b; };", "A", - "struct A : Z {\n}")); - // Should be: with semicolon, with { ... } + "struct A : Z {}")); } TEST(DeclPrinter, TestCXXRecordDecl6) { @@ -297,8 +292,7 @@ TEST(DeclPrinter, TestCXXRecordDecl6) { "class Z { int a; };" "class A : public Z { int b; };", "A", - "class A : public Z {\n}")); - // Should be: with semicolon, with { ... } + "class A : public Z {}")); } TEST(DeclPrinter, TestCXXRecordDecl7) { @@ -306,8 +300,7 @@ TEST(DeclPrinter, TestCXXRecordDecl7) { "class Z { int a; };" "class A : protected Z { int b; };", "A", - "class A : protected Z {\n}")); - // Should be: with semicolon, with { ... } + "class A : protected Z {}")); } TEST(DeclPrinter, TestCXXRecordDecl8) { @@ -315,8 +308,7 @@ TEST(DeclPrinter, TestCXXRecordDecl8) { "class Z { int a; };" "class A : private Z { int b; };", "A", - "class A : private Z {\n}")); - // Should be: with semicolon, with { ... } + "class A : private Z {}")); } TEST(DeclPrinter, TestCXXRecordDecl9) { @@ -324,8 +316,7 @@ TEST(DeclPrinter, TestCXXRecordDecl9) { "class Z { int a; };" "class A : virtual Z { int b; };", "A", - "class A : virtual Z {\n}")); - // Should be: with semicolon, with { ... } + "class A : virtual Z {}")); } TEST(DeclPrinter, TestCXXRecordDecl10) { @@ -333,8 +324,7 @@ TEST(DeclPrinter, TestCXXRecordDecl10) { "class Z { int a; };" "class A : virtual public Z { int b; };", "A", - "class A : virtual public Z {\n}")); - // Should be: with semicolon, with { ... } + "class A : virtual public Z {}")); } TEST(DeclPrinter, TestCXXRecordDecl11) { @@ -343,8 +333,7 @@ TEST(DeclPrinter, TestCXXRecordDecl11) { "class Y : virtual public Z { int b; };" "class A : virtual public Z, private Y { int c; };", "A", - "class A : virtual public Z, private Y {\n}")); - // Should be: with semicolon, with { ... } + "class A : virtual public Z, private Y {}")); } TEST(DeclPrinter, TestFunctionDecl1) { @@ -352,7 +341,6 @@ TEST(DeclPrinter, TestFunctionDecl1) { "void A();", "A", "void A()")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl2) { @@ -360,7 +348,6 @@ TEST(DeclPrinter, TestFunctionDecl2) { "void A() {}", "A", "void A()")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl3) { @@ -369,7 +356,6 @@ TEST(DeclPrinter, TestFunctionDecl3) { "void A() { Z(); }", "A", "void A()")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl4) { @@ -377,7 +363,6 @@ TEST(DeclPrinter, TestFunctionDecl4) { "extern void A();", "A", "extern void A()")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl5) { @@ -385,7 +370,6 @@ TEST(DeclPrinter, TestFunctionDecl5) { "static void A();", "A", "static void A()")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl6) { @@ -393,7 +377,6 @@ TEST(DeclPrinter, TestFunctionDecl6) { "inline void A();", "A", "inline void A()")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl7) { @@ -401,7 +384,6 @@ TEST(DeclPrinter, TestFunctionDecl7) { "constexpr int A(int a);", "A", "constexpr int A(int a)")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl8) { @@ -409,7 +391,6 @@ TEST(DeclPrinter, TestFunctionDecl8) { "void A(int a);", "A", "void A(int a)")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl9) { @@ -417,7 +398,6 @@ TEST(DeclPrinter, TestFunctionDecl9) { "void A(...);", "A", "void A(...)")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl10) { @@ -425,7 +405,6 @@ TEST(DeclPrinter, TestFunctionDecl10) { "void A(int a, ...);", "A", "void A(int a, ...)")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl11) { @@ -435,7 +414,6 @@ TEST(DeclPrinter, TestFunctionDecl11) { "void A(int a, pInt b, ssize_t c);", "A", "void A(int a, pInt b, ssize_t c)")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl12) { @@ -443,7 +421,6 @@ TEST(DeclPrinter, TestFunctionDecl12) { "void A(int a, int b = 0);", "A", "void A(int a, int b = 0)")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl13) { @@ -451,7 +428,7 @@ TEST(DeclPrinter, TestFunctionDecl13) { "void (*A(int a))(int b);", "A", "void (*A(int a))(int)")); - // Should be: with semicolon, with parameter name (?) + // Should be: with parameter name (?) } TEST(DeclPrinter, TestFunctionDecl14) { @@ -461,8 +438,7 @@ TEST(DeclPrinter, TestFunctionDecl14) { "template<>" "void A(int N) { }", functionDecl(hasName("A"), isExplicitTemplateSpecialization()).bind("id"), - "void A(int N)")); - // WRONG; Should be: "template <> void A(int N);")); + "template<> void A<int>(int N)")); } @@ -555,7 +531,6 @@ TEST(DeclPrinter, TestCXXConstructorDecl10) { "};", cxxConstructorDecl(ofClass(hasName("A"))).bind("id"), "A<T...>(const A<T...> &a)")); - // WRONG; Should be: "A(const A<T...> &a);" } TEST(DeclPrinter, TestCXXConstructorDecl11) { @@ -565,8 +540,7 @@ TEST(DeclPrinter, TestCXXConstructorDecl11) { " A(T&&... ts) : T(ts)... {}" "};", cxxConstructorDecl(ofClass(hasName("A"))).bind("id"), - "A<T...>(T &&...ts) : T(ts)...")); - // WRONG; Should be: "A(T &&...ts) : T(ts)... {}" + "A<T...>(T &&...ts) : T(ts)... {}")); } TEST(DeclPrinter, TestCXXDestructorDecl1) { @@ -623,7 +597,6 @@ TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction1) { "};", cxxMethodDecl(ofClass(hasName("Z"))).bind("id"), "void *operator new(std::size_t)")); - // Should be: with semicolon } TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction2) { @@ -634,7 +607,6 @@ TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction2) { "};", cxxMethodDecl(ofClass(hasName("Z"))).bind("id"), "void *operator new[](std::size_t)")); - // Should be: with semicolon } TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction3) { @@ -644,7 +616,7 @@ TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction3) { "};", cxxMethodDecl(ofClass(hasName("Z"))).bind("id"), "void operator delete(void *) noexcept")); - // Should be: with semicolon, without noexcept? + // Should be: without noexcept? } TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction4) { @@ -654,7 +626,6 @@ TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction4) { "};", cxxMethodDecl(ofClass(hasName("Z"))).bind("id"), "void operator delete(void *)")); - // Should be: with semicolon } TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction5) { @@ -664,7 +635,7 @@ TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction5) { "};", cxxMethodDecl(ofClass(hasName("Z"))).bind("id"), "void operator delete[](void *) noexcept")); - // Should be: with semicolon, without noexcept? + // Should be: without noexcept? } TEST(DeclPrinter, TestCXXMethodDecl_Operator1) { @@ -686,7 +657,6 @@ TEST(DeclPrinter, TestCXXMethodDecl_Operator1) { Expected.append("void operator"); Expected.append(OperatorNames[i]); Expected.append("(Z z)"); - // Should be: with semicolon ASSERT_TRUE(PrintedDeclCXX98Matches( Code, @@ -710,7 +680,6 @@ TEST(DeclPrinter, TestCXXMethodDecl_Operator2) { Expected.append("void operator"); Expected.append(OperatorNames[i]); Expected.append("()"); - // Should be: with semicolon ASSERT_TRUE(PrintedDeclCXX98Matches( Code, @@ -726,7 +695,6 @@ TEST(DeclPrinter, TestCXXMethodDecl1) { "};", "A", "void A(int a)")); - // Should be: with semicolon } TEST(DeclPrinter, TestCXXMethodDecl2) { @@ -736,7 +704,6 @@ TEST(DeclPrinter, TestCXXMethodDecl2) { "};", "A", "virtual void A(int a)")); - // Should be: with semicolon } TEST(DeclPrinter, TestCXXMethodDecl3) { @@ -749,7 +716,6 @@ TEST(DeclPrinter, TestCXXMethodDecl3) { "};", "ZZ::A", "void A(int a)")); - // Should be: with semicolon // TODO: should we print "virtual"? } @@ -760,7 +726,6 @@ TEST(DeclPrinter, TestCXXMethodDecl4) { "};", "A", "inline void A(int a)")); - // Should be: with semicolon } TEST(DeclPrinter, TestCXXMethodDecl5) { @@ -770,7 +735,6 @@ TEST(DeclPrinter, TestCXXMethodDecl5) { "};", "A", "virtual void A(int a) = 0")); - // Should be: with semicolon } TEST(DeclPrinter, TestCXXMethodDecl_CVQualifier1) { @@ -780,7 +744,6 @@ TEST(DeclPrinter, TestCXXMethodDecl_CVQualifier1) { "};", "A", "void A(int a) const")); - // Should be: with semicolon } TEST(DeclPrinter, TestCXXMethodDecl_CVQualifier2) { @@ -790,7 +753,6 @@ TEST(DeclPrinter, TestCXXMethodDecl_CVQualifier2) { "};", "A", "void A(int a) volatile")); - // Should be: with semicolon } TEST(DeclPrinter, TestCXXMethodDecl_CVQualifier3) { @@ -800,7 +762,6 @@ TEST(DeclPrinter, TestCXXMethodDecl_CVQualifier3) { "};", "A", "void A(int a) const volatile")); - // Should be: with semicolon } TEST(DeclPrinter, TestCXXMethodDecl_RefQualifier1) { @@ -810,7 +771,6 @@ TEST(DeclPrinter, TestCXXMethodDecl_RefQualifier1) { "};", "A", "void A(int a) &")); - // Should be: with semicolon } TEST(DeclPrinter, TestCXXMethodDecl_RefQualifier2) { @@ -820,7 +780,6 @@ TEST(DeclPrinter, TestCXXMethodDecl_RefQualifier2) { "};", "A", "void A(int a) &&")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification1) { @@ -830,7 +789,6 @@ TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification1) { "};", "A", "void A(int a) throw()")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification2) { @@ -840,7 +798,6 @@ TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification2) { "};", "A", "void A(int a) throw(int)")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification3) { @@ -851,7 +808,6 @@ TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification3) { "};", "A", "void A(int a) throw(ZZ, int)")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification4) { @@ -861,7 +817,6 @@ TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification4) { "};", "A", "void A(int a) noexcept")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification5) { @@ -942,8 +897,7 @@ TEST(DeclPrinter, TestClassTemplateDecl1) { "template<typename T>" "struct A { T a; };", classTemplateDecl(hasName("A")).bind("id"), - "template <typename T> struct A {\n}")); - // Should be: with semicolon, with { ... } + "template <typename T> struct A {}")); } TEST(DeclPrinter, TestClassTemplateDecl2) { @@ -951,8 +905,7 @@ TEST(DeclPrinter, TestClassTemplateDecl2) { "template<typename T = int>" "struct A { T a; };", classTemplateDecl(hasName("A")).bind("id"), - "template <typename T = int> struct A {\n}")); - // Should be: with semicolon, with { ... } + "template <typename T = int> struct A {}")); } TEST(DeclPrinter, TestClassTemplateDecl3) { @@ -960,8 +913,7 @@ TEST(DeclPrinter, TestClassTemplateDecl3) { "template<class T>" "struct A { T a; };", classTemplateDecl(hasName("A")).bind("id"), - "template <class T> struct A {\n}")); - // Should be: with semicolon, with { ... } + "template <class T> struct A {}")); } TEST(DeclPrinter, TestClassTemplateDecl4) { @@ -969,8 +921,7 @@ TEST(DeclPrinter, TestClassTemplateDecl4) { "template<typename T, typename U>" "struct A { T a; U b; };", classTemplateDecl(hasName("A")).bind("id"), - "template <typename T, typename U> struct A {\n}")); - // Should be: with semicolon, with { ... } + "template <typename T, typename U> struct A {}")); } TEST(DeclPrinter, TestClassTemplateDecl5) { @@ -978,8 +929,7 @@ TEST(DeclPrinter, TestClassTemplateDecl5) { "template<int N>" "struct A { int a[N]; };", classTemplateDecl(hasName("A")).bind("id"), - "template <int N> struct A {\n}")); - // Should be: with semicolon, with { ... } + "template <int N> struct A {}")); } TEST(DeclPrinter, TestClassTemplateDecl6) { @@ -987,8 +937,7 @@ TEST(DeclPrinter, TestClassTemplateDecl6) { "template<int N = 42>" "struct A { int a[N]; };", classTemplateDecl(hasName("A")).bind("id"), - "template <int N = 42> struct A {\n}")); - // Should be: with semicolon, with { ... } + "template <int N = 42> struct A {}")); } TEST(DeclPrinter, TestClassTemplateDecl7) { @@ -997,16 +946,14 @@ TEST(DeclPrinter, TestClassTemplateDecl7) { "template<MyInt N>" "struct A { int a[N]; };", classTemplateDecl(hasName("A")).bind("id"), - "template <MyInt N> struct A {\n}")); - // Should be: with semicolon, with { ... } + "template <MyInt N> struct A {}")); } TEST(DeclPrinter, TestClassTemplateDecl8) { ASSERT_TRUE(PrintedDeclCXX98Matches( "template<template<typename U> class T> struct A { };", classTemplateDecl(hasName("A")).bind("id"), - "template <template <typename U> class T> struct A {\n}")); - // Should be: with semicolon, with { ... } + "template <template <typename U> class T> struct A {}")); } TEST(DeclPrinter, TestClassTemplateDecl9) { @@ -1014,8 +961,7 @@ TEST(DeclPrinter, TestClassTemplateDecl9) { "template<typename T> struct Z { };" "template<template<typename U> class T = Z> struct A { };", classTemplateDecl(hasName("A")).bind("id"), - "template <template <typename U> class T> struct A {\n}")); - // Should be: with semicolon, with { ... } + "template <template <typename U> class T> struct A {}")); } TEST(DeclPrinter, TestClassTemplateDecl10) { @@ -1023,8 +969,7 @@ TEST(DeclPrinter, TestClassTemplateDecl10) { "template<typename... T>" "struct A { int a; };", classTemplateDecl(hasName("A")).bind("id"), - "template <typename ...T> struct A {\n}")); - // Should be: with semicolon, with { ... } + "template <typename ...T> struct A {}")); } TEST(DeclPrinter, TestClassTemplateDecl11) { @@ -1032,8 +977,7 @@ TEST(DeclPrinter, TestClassTemplateDecl11) { "template<typename... T>" "struct A : public T... { int a; };", classTemplateDecl(hasName("A")).bind("id"), - "template <typename ...T> struct A : public T... {\n}")); - // Should be: with semicolon, with { ... } + "template <typename ...T> struct A : public T... {}")); } TEST(DeclPrinter, TestClassTemplatePartialSpecializationDecl1) { @@ -1043,8 +987,7 @@ TEST(DeclPrinter, TestClassTemplatePartialSpecializationDecl1) { "template<typename T>" "struct A<T, int> { T a; };", classTemplateSpecializationDecl().bind("id"), - "struct A {\n}")); - // WRONG; Should be: "template<typename T> struct A<T, int> { ... }" + "template <typename T> struct A<T, int> {}")); } TEST(DeclPrinter, TestClassTemplatePartialSpecializationDecl2) { @@ -1054,7 +997,7 @@ TEST(DeclPrinter, TestClassTemplatePartialSpecializationDecl2) { "template<typename T>" "struct A<T *> { T a; };", classTemplateSpecializationDecl().bind("id"), - "struct A {\n}")); + "template <typename T> struct A<type-parameter-0-0 *> {}")); // WRONG; Should be: "template<typename T> struct A<T *> { ... }" } @@ -1065,8 +1008,7 @@ TEST(DeclPrinter, TestClassTemplateSpecializationDecl1) { "template<>" "struct A<int> { int a; };", classTemplateSpecializationDecl().bind("id"), - "struct A {\n}")); - // WRONG; Should be: "template<> struct A<int> { ... }" + "template<> struct A<int> {}")); } TEST(DeclPrinter, TestFunctionTemplateDecl1) { @@ -1075,7 +1017,6 @@ TEST(DeclPrinter, TestFunctionTemplateDecl1) { "void A(T &t);", functionTemplateDecl(hasName("A")).bind("id"), "template <typename T> void A(T &t)")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionTemplateDecl2) { @@ -1084,7 +1025,6 @@ TEST(DeclPrinter, TestFunctionTemplateDecl2) { "void A(T &t) { }", functionTemplateDecl(hasName("A")).bind("id"), "template <typename T> void A(T &t)")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionTemplateDecl3) { @@ -1093,7 +1033,6 @@ TEST(DeclPrinter, TestFunctionTemplateDecl3) { "void A(T... a);", functionTemplateDecl(hasName("A")).bind("id"), "template <typename ...T> void A(T ...a)")); - // Should be: with semicolon. } TEST(DeclPrinter, TestFunctionTemplateDecl4) { @@ -1101,7 +1040,6 @@ TEST(DeclPrinter, TestFunctionTemplateDecl4) { "struct Z { template<typename T> void A(T t); };", functionTemplateDecl(hasName("A")).bind("id"), "template <typename T> void A(T t)")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionTemplateDecl5) { @@ -1109,7 +1047,6 @@ TEST(DeclPrinter, TestFunctionTemplateDecl5) { "struct Z { template<typename T> void A(T t) {} };", functionTemplateDecl(hasName("A")).bind("id"), "template <typename T> void A(T t)")); - // Should be: with semicolon } TEST(DeclPrinter, TestFunctionTemplateDecl6) { @@ -1119,7 +1056,6 @@ TEST(DeclPrinter, TestFunctionTemplateDecl6) { "};", functionTemplateDecl(hasName("A")).bind("id"), "template <typename U> void A(U t)")); - // Should be: with semicolon } TEST(DeclPrinter, TestTemplateArgumentList1) { diff --git a/unittests/AST/ExternalASTSourceTest.cpp b/unittests/AST/ExternalASTSourceTest.cpp index 4f42dcf10336..4b3bb3e2b69b 100644 --- a/unittests/AST/ExternalASTSourceTest.cpp +++ b/unittests/AST/ExternalASTSourceTest.cpp @@ -17,6 +17,7 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendActions.h" +#include "clang/Lex/PreprocessorOptions.h" #include "gtest/gtest.h" using namespace clang; diff --git a/unittests/AST/PostOrderASTVisitor.cpp b/unittests/AST/PostOrderASTVisitor.cpp index 012f63a48ade..2921f3ead50e 100644 --- a/unittests/AST/PostOrderASTVisitor.cpp +++ b/unittests/AST/PostOrderASTVisitor.cpp @@ -34,6 +34,11 @@ namespace { bool shouldTraversePostOrder() const { return VisitPostOrder; } + bool VisitUnaryOperator(UnaryOperator *Op) { + VisitedNodes.push_back(Op->getOpcodeStr(Op->getOpcode())); + return true; + } + bool VisitBinaryOperator(BinaryOperator *Op) { VisitedNodes.push_back(Op->getOpcodeStr()); return true; @@ -76,7 +81,7 @@ TEST(RecursiveASTVisitor, PostOrderTraversal) { auto ASTUnit = tooling::buildASTFromCode( "class A {" " class B {" - " int foo() { while(4) { int i = 9; } return (1 + 3) + 2; }" + " int foo() { while(4) { int i = 9; int j = -5; } return (1 + 3) + 2; }" " };" "};" ); @@ -86,9 +91,9 @@ TEST(RecursiveASTVisitor, PostOrderTraversal) { RecordingVisitor Visitor(true); Visitor.TraverseTranslationUnitDecl(TU); - std::vector<std::string> expected = { - "4", "9", "i", "1", "3", "+", "2", "+", "return", "A::B::foo", "A::B", "A" - }; + std::vector<std::string> expected = {"4", "9", "i", "5", "-", + "j", "1", "3", "+", "2", + "+", "return", "A::B::foo", "A::B", "A"}; // Compare the list of actually visited nodes // with the expected list of visited nodes. ASSERT_EQ(expected.size(), Visitor.VisitedNodes.size()); diff --git a/unittests/AST/SourceLocationTest.cpp b/unittests/AST/SourceLocationTest.cpp index 9fae8d862aef..add85c366024 100644 --- a/unittests/AST/SourceLocationTest.cpp +++ b/unittests/AST/SourceLocationTest.cpp @@ -148,6 +148,96 @@ TEST(VarDecl, VMTypeFixedVarDeclRange) { varDecl(), Lang_C89)); } +TEST(TypeLoc, IntRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 1); + EXPECT_TRUE(Verifier.match("int a;", typeLoc())); +} + +TEST(TypeLoc, LongRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 1); + EXPECT_TRUE(Verifier.match("long a;", typeLoc())); +} + +TEST(TypeLoc, LongDoubleRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 6); + EXPECT_TRUE(Verifier.match("long double a;", typeLoc())); +} + +TEST(TypeLoc, DoubleLongRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 8); + EXPECT_TRUE(Verifier.match("double long a;", typeLoc())); +} + +TEST(TypeLoc, LongIntRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 6); + EXPECT_TRUE(Verifier.match("long int a;", typeLoc())); +} + +TEST(TypeLoc, IntLongRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 5); + EXPECT_TRUE(Verifier.match("int long a;", typeLoc())); +} + +TEST(TypeLoc, UnsignedIntRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 10); + EXPECT_TRUE(Verifier.match("unsigned int a;", typeLoc())); +} + +TEST(TypeLoc, IntUnsignedRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 5); + EXPECT_TRUE(Verifier.match("int unsigned a;", typeLoc())); +} + +TEST(TypeLoc, LongLongRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 6); + EXPECT_TRUE(Verifier.match("long long a;", typeLoc())); +} + +TEST(TypeLoc, UnsignedLongLongRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 15); + EXPECT_TRUE(Verifier.match("unsigned long long a;", typeLoc())); +} + +TEST(TypeLoc, LongUnsignedLongRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 15); + EXPECT_TRUE(Verifier.match("long unsigned long a;", typeLoc())); +} + +TEST(TypeLoc, LongLongUnsignedRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 11); + EXPECT_TRUE(Verifier.match("long long unsigned a;", typeLoc())); +} + +TEST(TypeLoc, ConstLongLongRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 7, 1, 12); + EXPECT_TRUE(Verifier.match("const long long a = 0;", typeLoc())); +} + +TEST(TypeLoc, LongConstLongRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 12); + EXPECT_TRUE(Verifier.match("long const long a = 0;", typeLoc())); +} + +TEST(TypeLoc, LongLongConstRange) { + RangeVerifier<TypeLoc> Verifier; + Verifier.expectRange(1, 1, 1, 6); + EXPECT_TRUE(Verifier.match("long long const a = 0;", typeLoc())); +} + TEST(CXXConstructorDecl, NoRetFunTypeLocRange) { RangeVerifier<CXXConstructorDecl> Verifier; Verifier.expectRange(1, 11, 1, 13); diff --git a/unittests/AST/StmtPrinterTest.cpp b/unittests/AST/StmtPrinterTest.cpp index bc7fd54e4ad9..12b203236c99 100644 --- a/unittests/AST/StmtPrinterTest.cpp +++ b/unittests/AST/StmtPrinterTest.cpp @@ -45,7 +45,7 @@ public: PrintMatch() : NumFoundStmts(0) {} void run(const MatchFinder::MatchResult &Result) override { - const Stmt *S = Result.Nodes.getStmtAs<Stmt>("id"); + const Stmt *S = Result.Nodes.getNodeAs<Stmt>("id"); if (!S) return; NumFoundStmts++; diff --git a/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index 82c5139a78f5..6037127feb52 100644 --- a/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -669,7 +669,7 @@ TEST(Matcher, VarDecl_Storage) { TEST(Matcher, VarDecl_StorageDuration) { std::string T = - "void f() { int x; static int y; } int a;"; + "void f() { int x; static int y; } int a;static int b;extern int c;"; EXPECT_TRUE(matches(T, varDecl(hasName("x"), hasAutomaticStorageDuration()))); EXPECT_TRUE( @@ -679,6 +679,8 @@ TEST(Matcher, VarDecl_StorageDuration) { EXPECT_TRUE(matches(T, varDecl(hasName("y"), hasStaticStorageDuration()))); EXPECT_TRUE(matches(T, varDecl(hasName("a"), hasStaticStorageDuration()))); + EXPECT_TRUE(matches(T, varDecl(hasName("b"), hasStaticStorageDuration()))); + EXPECT_TRUE(matches(T, varDecl(hasName("c"), hasStaticStorageDuration()))); EXPECT_TRUE(notMatches(T, varDecl(hasName("x"), hasStaticStorageDuration()))); // FIXME: It is really hard to test with thread_local itself because not all @@ -842,6 +844,21 @@ TEST(IsExternC, MatchesExternCFunctionDeclarations) { EXPECT_TRUE(notMatches("void f() {}", functionDecl(isExternC()))); } +TEST(IsExternC, MatchesExternCVariableDeclarations) { + EXPECT_TRUE(matches("extern \"C\" int i;", varDecl(isExternC()))); + EXPECT_TRUE(matches("extern \"C\" { int i; }", varDecl(isExternC()))); + EXPECT_TRUE(notMatches("int i;", varDecl(isExternC()))); +} + +TEST(IsStaticStorageClass, MatchesStaticDeclarations) { + EXPECT_TRUE( + matches("static void f() {}", functionDecl(isStaticStorageClass()))); + EXPECT_TRUE(matches("static int i = 1;", varDecl(isStaticStorageClass()))); + EXPECT_TRUE(notMatches("int i = 1;", varDecl(isStaticStorageClass()))); + EXPECT_TRUE(notMatches("extern int i;", varDecl(isStaticStorageClass()))); + EXPECT_TRUE(notMatches("void f() {}", functionDecl(isStaticStorageClass()))); +} + TEST(IsDefaulted, MatchesDefaultedFunctionDeclarations) { EXPECT_TRUE(notMatches("class A { ~A(); };", functionDecl(hasName("~A"), isDefaulted()))); @@ -1387,6 +1404,16 @@ TEST(Member, BitFields) { fieldDecl(isBitField(), hasBitWidth(2), hasName("a")))); } +TEST(Member, InClassInitializer) { + EXPECT_TRUE( + matches("class C { int a = 2; int b; };", + fieldDecl(hasInClassInitializer(integerLiteral(equals(2))), + hasName("a")))); + EXPECT_TRUE( + notMatches("class C { int a = 2; int b; };", + fieldDecl(hasInClassInitializer(anything()), hasName("b")))); +} + TEST(Member, UnderstandsAccess) { EXPECT_TRUE(matches( "struct A { int i; };", fieldDecl(isPublic(), hasName("i")))); @@ -1931,5 +1958,21 @@ TEST(NullPointerConstants, Basic) { EXPECT_TRUE(notMatches("int i = 0;", expr(nullPointerConstant()))); } +TEST(HasExternalFormalLinkage, Basic) { + EXPECT_TRUE(matches("int a = 0;", namedDecl(hasExternalFormalLinkage()))); + EXPECT_TRUE( + notMatches("static int a = 0;", namedDecl(hasExternalFormalLinkage()))); + EXPECT_TRUE(notMatches("static void f(void) { int a = 0; }", + namedDecl(hasExternalFormalLinkage()))); + EXPECT_TRUE(matches("void f(void) { int a = 0; }", + namedDecl(hasExternalFormalLinkage()))); + + // Despite having internal semantic linkage, the anonymous namespace member + // has external linkage because the member has a unique name in all + // translation units. + EXPECT_TRUE(matches("namespace { int a = 0; }", + namedDecl(hasExternalFormalLinkage()))); +} + } // namespace ast_matchers } // namespace clang diff --git a/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/unittests/ASTMatchers/ASTMatchersNodeTest.cpp index 6c8a5e000f49..dd45ca3ced92 100644 --- a/unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -528,6 +528,11 @@ TEST(Matcher, ConstructorCall) { EXPECT_TRUE(matches("class X {}; void x(int) { X x; }", Constructor)); } +TEST(Match, ConstructorInitializers) { + EXPECT_TRUE(matches("class C { int i; public: C(int ii) : i(ii) {} };", + cxxCtorInitializer(forField(hasName("i"))))); +} + TEST(Matcher, ThisExpr) { EXPECT_TRUE( matches("struct X { int a; int f () { return a; } };", cxxThisExpr())); @@ -589,7 +594,7 @@ TEST(MaterializeTemporaryExpr, MatchesTemporary) { materializeTemporaryExpr())); EXPECT_TRUE( - notMatches(ClassString + + matches(ClassString + "string GetStringByValue();" "void run() { int k = GetStringByValue().length(); }", materializeTemporaryExpr())); diff --git a/unittests/ASTMatchers/ASTMatchersTest.h b/unittests/ASTMatchers/ASTMatchersTest.h index 4b3387c0ba72..fdb273c672da 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.h +++ b/unittests/ASTMatchers/ASTMatchersTest.h @@ -189,7 +189,7 @@ testing::AssertionResult matchesConditionallyWithCuda( // avoid constructing a full system triple. std::vector<std::string> Args = { "-xcuda", "-fno-ms-extensions", "--cuda-host-only", "-nocudainc", - "-target", "nvptx64-unknown-unknown", CompileArg}; + "-target", "x86_64-unknown-unknown", CompileArg}; if (!runToolOnCodeWithArgs(Factory->create(), CudaHeader + Code, Args)) { return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; diff --git a/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp index 4149d4fd0b11..67a4a3b2fc09 100644 --- a/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -241,6 +241,36 @@ TEST(HasDeclaration, HasDeclarationOfTemplateSpecializationType) { hasDeclaration(namedDecl(hasName("A")))))))); } +TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) { + EXPECT_TRUE( + matches("int *A = new int();", + cxxNewExpr(hasDeclaration(functionDecl(parameterCountIs(1)))))); +} + +TEST(HasUnqualifiedDesugaredType, DesugarsUsing) { + EXPECT_TRUE( + matches("struct A {}; using B = A; B b;", + varDecl(hasType(hasUnqualifiedDesugaredType(recordType()))))); + EXPECT_TRUE( + matches("struct A {}; using B = A; using C = B; C b;", + varDecl(hasType(hasUnqualifiedDesugaredType(recordType()))))); +} + +TEST(HasUnderlyingDecl, Matches) { + EXPECT_TRUE(matches("namespace N { template <class T> void f(T t); }" + "template <class T> void g() { using N::f; f(T()); }", + unresolvedLookupExpr(hasAnyDeclaration( + namedDecl(hasUnderlyingDecl(hasName("::N::f"))))))); + EXPECT_TRUE(matches( + "namespace N { template <class T> void f(T t); }" + "template <class T> void g() { N::f(T()); }", + unresolvedLookupExpr(hasAnyDeclaration(namedDecl(hasName("::N::f")))))); + EXPECT_TRUE(notMatches( + "namespace N { template <class T> void f(T t); }" + "template <class T> void g() { using N::f; f(T()); }", + unresolvedLookupExpr(hasAnyDeclaration(namedDecl(hasName("::N::f")))))); +} + TEST(HasType, TakesQualTypeMatcherAndMatchesExpr) { TypeMatcher ClassX = hasDeclaration(recordDecl(hasName("X"))); EXPECT_TRUE( @@ -543,6 +573,14 @@ TEST(Matcher, MatchesTypeTemplateArgument) { asString("int")))))); } +TEST(Matcher, MatchesTemplateTemplateArgument) { + EXPECT_TRUE(matches("template<template <typename> class S> class X {};" + "template<typename T> class Y {};" + "X<Y> xi;", + classTemplateSpecializationDecl(hasAnyTemplateArgument( + refersToTemplate(templateName()))))); +} + TEST(Matcher, MatchesDeclarationReferenceTemplateArgument) { EXPECT_TRUE(matches( "struct B { int next; };" @@ -594,6 +632,14 @@ TEST(Matcher, MatchesSpecificArgument) { "A<int, bool> a;", templateSpecializationType(hasTemplateArgument( 1, refersToType(asString("int")))))); + + EXPECT_TRUE(matches( + "template<typename T> void f() {};" + "void func() { f<int>(); }", + functionDecl(hasTemplateArgument(0, refersToType(asString("int")))))); + EXPECT_TRUE(notMatches( + "template<typename T> void f() {};", + functionDecl(hasTemplateArgument(0, refersToType(asString("int")))))); } TEST(TemplateArgument, Matches) { @@ -603,6 +649,94 @@ TEST(TemplateArgument, Matches) { EXPECT_TRUE(matches( "template<typename T> struct C {}; C<int> c;", templateSpecializationType(hasAnyTemplateArgument(templateArgument())))); + + EXPECT_TRUE(matches( + "template<typename T> void f() {};" + "void func() { f<int>(); }", + functionDecl(hasAnyTemplateArgument(templateArgument())))); +} + +TEST(TemplateTypeParmDecl, CXXMethodDecl) { + const char input[] = + "template<typename T>\n" + "class Class {\n" + " void method();\n" + "};\n" + "template<typename U>\n" + "void Class<U>::method() {}\n"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T")))); + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U")))); +} + +TEST(TemplateTypeParmDecl, VarDecl) { + const char input[] = + "template<typename T>\n" + "class Class {\n" + " static T pi;\n" + "};\n" + "template<typename U>\n" + "U Class<U>::pi = U(3.1415926535897932385);\n"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T")))); + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U")))); +} + +TEST(TemplateTypeParmDecl, VarTemplatePartialSpecializationDecl) { + const char input[] = + "template<typename T>\n" + "struct Struct {\n" + " template<typename T2> static int field;\n" + "};\n" + "template<typename U>\n" + "template<typename U2>\n" + "int Struct<U>::field<U2*> = 123;\n"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T")))); + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T2")))); + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U")))); + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U2")))); +} + +TEST(TemplateTypeParmDecl, ClassTemplatePartialSpecializationDecl) { + const char input[] = + "template<typename T>\n" + "class Class {\n" + " template<typename T2> struct Struct;\n" + "};\n" + "template<typename U>\n" + "template<typename U2>\n" + "struct Class<U>::Struct<U2*> {};\n"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T")))); + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T2")))); + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U")))); + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U2")))); +} + +TEST(TemplateTypeParmDecl, EnumDecl) { + const char input[] = + "template<typename T>\n" + "struct Struct {\n" + " enum class Enum : T;\n" + "};\n" + "template<typename U>\n" + "enum class Struct<U>::Enum : U {\n" + " e1,\n" + " e2\n" + "};\n"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T")))); + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U")))); +} + +TEST(TemplateTypeParmDecl, RecordDecl) { + const char input[] = + "template<typename T>\n" + "class Class {\n" + " struct Struct;\n" + "};\n" + "template<typename U>\n" + "struct Class<U>::Struct {\n" + " U field;\n" + "};\n"; + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T")))); + EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U")))); } TEST(RefersToIntegralType, Matches) { @@ -2051,5 +2185,41 @@ TEST(Matcher, ForEachOverriden) { EXPECT_TRUE(notMatches(Code2, ForEachOverriddenInClass("A1"))); } +TEST(Matcher, HasAnyDeclaration) { + std::string Fragment = "void foo(int p1);" + "void foo(int *p2);" + "void bar(int p3);" + "template <typename T> void baz(T t) { foo(t); }"; + + EXPECT_TRUE( + matches(Fragment, unresolvedLookupExpr(hasAnyDeclaration(functionDecl( + hasParameter(0, parmVarDecl(hasName("p1")))))))); + EXPECT_TRUE( + matches(Fragment, unresolvedLookupExpr(hasAnyDeclaration(functionDecl( + hasParameter(0, parmVarDecl(hasName("p2")))))))); + EXPECT_TRUE( + notMatches(Fragment, unresolvedLookupExpr(hasAnyDeclaration(functionDecl( + hasParameter(0, parmVarDecl(hasName("p3")))))))); + EXPECT_TRUE(notMatches(Fragment, unresolvedLookupExpr(hasAnyDeclaration( + functionDecl(hasName("bar")))))); +} + +TEST(SubstTemplateTypeParmType, HasReplacementType) +{ + std::string Fragment = "template<typename T>" + "double F(T t);" + "int i;" + "double j = F(i);"; + EXPECT_TRUE(matches(Fragment, substTemplateTypeParmType(hasReplacementType( + qualType(asString("int")))))); + EXPECT_TRUE(notMatches(Fragment, substTemplateTypeParmType(hasReplacementType( + qualType(asString("double")))))); + EXPECT_TRUE( + notMatches("template<int N>" + "double F();" + "double j = F<5>();", + substTemplateTypeParmType(hasReplacementType(qualType())))); +} + } // namespace ast_matchers } // namespace clang diff --git a/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/unittests/ASTMatchers/Dynamic/ParserTest.cpp index 553427c9a403..d241f5ba2f86 100644 --- a/unittests/ASTMatchers/Dynamic/ParserTest.cpp +++ b/unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -11,7 +11,6 @@ #include "clang/ASTMatchers/Dynamic/Parser.h" #include "clang/ASTMatchers/Dynamic/Registry.h" #include "llvm/ADT/Optional.h" -#include "llvm/ADT/StringMap.h" #include "gtest/gtest.h" #include <string> #include <vector> diff --git a/unittests/Analysis/CFGTest.cpp b/unittests/Analysis/CFGTest.cpp index a8d397e9a534..e691110050a2 100644 --- a/unittests/Analysis/CFGTest.cpp +++ b/unittests/Analysis/CFGTest.cpp @@ -18,6 +18,41 @@ namespace clang { namespace analysis { namespace { +enum BuildResult { + ToolFailed, + ToolRan, + SawFunctionBody, + BuiltCFG, +}; + +class CFGCallback : public ast_matchers::MatchFinder::MatchCallback { +public: + BuildResult TheBuildResult = ToolRan; + + void run(const ast_matchers::MatchFinder::MatchResult &Result) override { + const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func"); + Stmt *Body = Func->getBody(); + if (!Body) + return; + TheBuildResult = SawFunctionBody; + if (CFG::buildCFG(nullptr, Body, Result.Context, CFG::BuildOptions())) + TheBuildResult = BuiltCFG; + } +}; + +BuildResult BuildCFG(const char *Code) { + CFGCallback Callback; + + ast_matchers::MatchFinder Finder; + Finder.addMatcher(ast_matchers::functionDecl().bind("func"), &Callback); + std::unique_ptr<tooling::FrontendActionFactory> Factory( + tooling::newFrontendActionFactory(&Finder)); + std::vector<std::string> Args = {"-std=c++11", "-fno-delayed-template-parsing"}; + if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args)) + return ToolFailed; + return Callback.TheBuildResult; +} + // Constructing a CFG for a range-based for over a dependent type fails (but // should not crash). TEST(CFG, RangeBasedForOverDependentType) { @@ -27,30 +62,17 @@ TEST(CFG, RangeBasedForOverDependentType) { " for (const Foo *TheFoo : Range) {\n" " }\n" "}\n"; + EXPECT_EQ(SawFunctionBody, BuildCFG(Code)); +} - class CFGCallback : public ast_matchers::MatchFinder::MatchCallback { - public: - bool SawFunctionBody = false; - - void run(const ast_matchers::MatchFinder::MatchResult &Result) override { - const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func"); - Stmt *Body = Func->getBody(); - if (!Body) - return; - SawFunctionBody = true; - std::unique_ptr<CFG> cfg = - CFG::buildCFG(nullptr, Body, Result.Context, CFG::BuildOptions()); - EXPECT_EQ(nullptr, cfg); - } - } Callback; - - ast_matchers::MatchFinder Finder; - Finder.addMatcher(ast_matchers::functionDecl().bind("func"), &Callback); - std::unique_ptr<tooling::FrontendActionFactory> Factory( - tooling::newFrontendActionFactory(&Finder)); - std::vector<std::string> Args = {"-std=c++11", "-fno-delayed-template-parsing"}; - ASSERT_TRUE(tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args)); - EXPECT_TRUE(Callback.SawFunctionBody); +// Constructing a CFG containing a delete expression on a dependent type should +// not crash. +TEST(CFG, DeleteExpressionOnDependentType) { + const char *Code = "template<class T>\n" + "void f(T t) {\n" + " delete t;\n" + "}\n"; + EXPECT_EQ(BuiltCFG, BuildCFG(Code)); } } // namespace diff --git a/unittests/Basic/FileManagerTest.cpp b/unittests/Basic/FileManagerTest.cpp index d8d85dd76c38..4cf21cd8459a 100644 --- a/unittests/Basic/FileManagerTest.cpp +++ b/unittests/Basic/FileManagerTest.cpp @@ -52,7 +52,7 @@ public: } // Implement FileSystemStatCache::getStat(). - LookupResult getStat(const char *Path, FileData &Data, bool isFile, + LookupResult getStat(StringRef Path, FileData &Data, bool isFile, std::unique_ptr<vfs::File> *F, vfs::FileSystem &FS) override { if (StatCalls.count(Path) != 0) { @@ -82,14 +82,14 @@ TEST_F(FileManagerTest, getVirtualFileSetsTheDirFieldCorrectly) { const DirectoryEntry *dir = file->getDir(); ASSERT_TRUE(dir != nullptr); - EXPECT_STREQ(".", dir->getName()); + EXPECT_EQ(".", dir->getName()); file = manager.getVirtualFile("x/y/z.cpp", 42, 0); ASSERT_TRUE(file != nullptr); dir = file->getDir(); ASSERT_TRUE(dir != nullptr); - EXPECT_STREQ("x/y", dir->getName()); + EXPECT_EQ("x/y", dir->getName()); } // Before any virtual file is added, no virtual directory exists. @@ -115,11 +115,11 @@ TEST_F(FileManagerTest, getVirtualFileCreatesDirectoryEntriesForAncestors) { const DirectoryEntry *dir = manager.getDirectory("virtual/dir"); ASSERT_TRUE(dir != nullptr); - EXPECT_STREQ("virtual/dir", dir->getName()); + EXPECT_EQ("virtual/dir", dir->getName()); dir = manager.getDirectory("virtual"); ASSERT_TRUE(dir != nullptr); - EXPECT_STREQ("virtual", dir->getName()); + EXPECT_EQ("virtual", dir->getName()); } // getFile() returns non-NULL if a real file exists at the given path. @@ -140,11 +140,11 @@ TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) { const FileEntry *file = manager.getFile("/tmp/test"); ASSERT_TRUE(file != nullptr); - EXPECT_STREQ("/tmp/test", file->getName()); + EXPECT_EQ("/tmp/test", file->getName()); const DirectoryEntry *dir = file->getDir(); ASSERT_TRUE(dir != nullptr); - EXPECT_STREQ("/tmp", dir->getName()); + EXPECT_EQ("/tmp", dir->getName()); #ifdef LLVM_ON_WIN32 file = manager.getFile(FileName); @@ -152,7 +152,7 @@ TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) { dir = file->getDir(); ASSERT_TRUE(dir != NULL); - EXPECT_STREQ(DirName, dir->getName()); + EXPECT_EQ(DirName, dir->getName()); #endif } @@ -164,11 +164,11 @@ TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingVirtualFile) { manager.getVirtualFile("virtual/dir/bar.h", 100, 0); const FileEntry *file = manager.getFile("virtual/dir/bar.h"); ASSERT_TRUE(file != nullptr); - EXPECT_STREQ("virtual/dir/bar.h", file->getName()); + EXPECT_EQ("virtual/dir/bar.h", file->getName()); const DirectoryEntry *dir = file->getDir(); ASSERT_TRUE(dir != nullptr); - EXPECT_STREQ("virtual/dir", dir->getName()); + EXPECT_EQ("virtual/dir", dir->getName()); } // getFile() returns different FileEntries for different paths when diff --git a/unittests/Basic/VirtualFileSystemTest.cpp b/unittests/Basic/VirtualFileSystemTest.cpp index 3b26488a7fd9..580343d93ea1 100644 --- a/unittests/Basic/VirtualFileSystemTest.cpp +++ b/unittests/Basic/VirtualFileSystemTest.cpp @@ -12,7 +12,6 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/Host.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" #include "llvm/Support/SourceMgr.h" #include "gtest/gtest.h" #include <map> @@ -116,20 +115,23 @@ public: } void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { - vfs::Status S(Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0, - 1024, sys::fs::file_type::regular_file, Perms); + vfs::Status S(Path, UniqueID(FSID, FileID++), + std::chrono::system_clock::now(), 0, 0, 1024, + sys::fs::file_type::regular_file, Perms); addEntry(Path, S); } void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { - vfs::Status S(Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0, - 0, sys::fs::file_type::directory_file, Perms); + vfs::Status S(Path, UniqueID(FSID, FileID++), + std::chrono::system_clock::now(), 0, 0, 0, + sys::fs::file_type::directory_file, Perms); addEntry(Path, S); } void addSymlink(StringRef Path) { - vfs::Status S(Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0, - 0, sys::fs::file_type::symlink_file, sys::fs::all_all); + vfs::Status S(Path, UniqueID(FSID, FileID++), + std::chrono::system_clock::now(), 0, 0, 0, + sys::fs::file_type::symlink_file, sys::fs::all_all); addEntry(Path, S); } }; diff --git a/unittests/Driver/CMakeLists.txt b/unittests/Driver/CMakeLists.txt index 2df1eb24fd44..b9c52f7a53f9 100644 --- a/unittests/Driver/CMakeLists.txt +++ b/unittests/Driver/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS ) add_clang_unittest(ClangDriverTests + DistroTest.cpp ToolChainTest.cpp MultilibTest.cpp ) diff --git a/unittests/Driver/DistroTest.cpp b/unittests/Driver/DistroTest.cpp new file mode 100644 index 000000000000..e3686e498060 --- /dev/null +++ b/unittests/Driver/DistroTest.cpp @@ -0,0 +1,305 @@ +//===- unittests/Driver/DistroTest.cpp --- ToolChains tests ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Unit tests for Distro detection. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Distro.h" +#include "clang/Basic/VirtualFileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" +using namespace clang; +using namespace clang::driver; + +namespace { + +// The tests include all release-related files for each distribution +// in the VFS, in order to make sure that earlier tests do not +// accidentally result in incorrect distribution guess. + +TEST(DistroTest, DetectUbuntu) { + vfs::InMemoryFileSystem UbuntuTrustyFileSystem; + // Ubuntu uses Debian Sid version. + UbuntuTrustyFileSystem.addFile("/etc/debian_version", 0, + llvm::MemoryBuffer::getMemBuffer("jessie/sid\n")); + UbuntuTrustyFileSystem.addFile("/etc/lsb-release", 0, + llvm::MemoryBuffer::getMemBuffer("DISTRIB_ID=Ubuntu\n" + "DISTRIB_RELEASE=14.04\n" + "DISTRIB_CODENAME=trusty\n" + "DISTRIB_DESCRIPTION=\"Ubuntu 14.04 LTS\"\n")); + UbuntuTrustyFileSystem.addFile("/etc/os-release", 0, + llvm::MemoryBuffer::getMemBuffer("NAME=\"Ubuntu\"\n" + "VERSION=\"14.04, Trusty Tahr\"\n" + "ID=ubuntu\n" + "ID_LIKE=debian\n" + "PRETTY_NAME=\"Ubuntu 14.04 LTS\"\n" + "VERSION_ID=\"14.04\"\n" + "HOME_URL=\"http://www.ubuntu.com/\"\n" + "SUPPORT_URL=\"http://help.ubuntu.com/\"\n" + "BUG_REPORT_URL=\"http://bugs.launchpad.net/ubuntu/\"\n")); + + Distro UbuntuTrusty{UbuntuTrustyFileSystem}; + ASSERT_EQ(Distro(Distro::UbuntuTrusty), UbuntuTrusty); + ASSERT_TRUE(UbuntuTrusty.IsUbuntu()); + ASSERT_FALSE(UbuntuTrusty.IsRedhat()); + ASSERT_FALSE(UbuntuTrusty.IsOpenSUSE()); + ASSERT_FALSE(UbuntuTrusty.IsDebian()); + + vfs::InMemoryFileSystem UbuntuYakketyFileSystem; + UbuntuYakketyFileSystem.addFile("/etc/debian_version", 0, + llvm::MemoryBuffer::getMemBuffer("stretch/sid\n")); + UbuntuYakketyFileSystem.addFile("/etc/lsb-release", 0, + llvm::MemoryBuffer::getMemBuffer("DISTRIB_ID=Ubuntu\n" + "DISTRIB_RELEASE=16.10\n" + "DISTRIB_CODENAME=yakkety\n" + "DISTRIB_DESCRIPTION=\"Ubuntu 16.10\"\n")); + UbuntuYakketyFileSystem.addFile("/etc/os-release", 0, + llvm::MemoryBuffer::getMemBuffer("NAME=\"Ubuntu\"\n" + "VERSION=\"16.10 (Yakkety Yak)\"\n" + "ID=ubuntu\n" + "ID_LIKE=debian\n" + "PRETTY_NAME=\"Ubuntu 16.10\"\n" + "VERSION_ID=\"16.10\"\n" + "HOME_URL=\"http://www.ubuntu.com/\"\n" + "SUPPORT_URL=\"http://help.ubuntu.com/\"\n" + "BUG_REPORT_URL=\"http://bugs.launchpad.net/ubuntu/\"\n" + "PRIVACY_POLICY_URL=\"http://www.ubuntu.com/legal/terms-and-policies/privacy-policy\"\n" + "VERSION_CODENAME=yakkety\n" + "UBUNTU_CODENAME=yakkety\n")); + + Distro UbuntuYakkety{UbuntuYakketyFileSystem}; + ASSERT_EQ(Distro(Distro::UbuntuYakkety), UbuntuYakkety); + ASSERT_TRUE(UbuntuYakkety.IsUbuntu()); + ASSERT_FALSE(UbuntuYakkety.IsRedhat()); + ASSERT_FALSE(UbuntuYakkety.IsOpenSUSE()); + ASSERT_FALSE(UbuntuYakkety.IsDebian()); +} + +TEST(DistroTest, DetectRedhat) { + vfs::InMemoryFileSystem Fedora25FileSystem; + Fedora25FileSystem.addFile("/etc/system-release-cpe", 0, + llvm::MemoryBuffer::getMemBuffer("cpe:/o:fedoraproject:fedora:25\n")); + // Both files are symlinks to fedora-release. + Fedora25FileSystem.addFile("/etc/system-release", 0, + llvm::MemoryBuffer::getMemBuffer("Fedora release 25 (Twenty Five)\n")); + Fedora25FileSystem.addFile("/etc/redhat-release", 0, + llvm::MemoryBuffer::getMemBuffer("Fedora release 25 (Twenty Five)\n")); + Fedora25FileSystem.addFile("/etc/fedora-release", 0, + llvm::MemoryBuffer::getMemBuffer("Fedora release 25 (Twenty Five)\n")); + Fedora25FileSystem.addFile("/etc/os-release", 0, + llvm::MemoryBuffer::getMemBuffer("NAME=Fedora\n" + "VERSION=\"25 (Twenty Five)\"\n" + "ID=fedora\n" + "VERSION_ID=25\n" + "PRETTY_NAME=\"Fedora 25 (Twenty Five)\"\n" + "ANSI_COLOR=\"0;34\"\n" + "CPE_NAME=\"cpe:/o:fedoraproject:fedora:25\"\n" + "HOME_URL=\"https://fedoraproject.org/\"\n" + "BUG_REPORT_URL=\"https://bugzilla.redhat.com/\"\n" + "REDHAT_BUGZILLA_PRODUCT=\"Fedora\"\n" + "REDHAT_BUGZILLA_PRODUCT_VERSION=25\n" + "REDHAT_SUPPORT_PRODUCT=\"Fedora\"\n" + "REDHAT_SUPPORT_PRODUCT_VERSION=25\n" + "PRIVACY_POLICY_URL=https://fedoraproject.org/wiki/Legal:PrivacyPolicy\n")); + Distro Fedora25{Fedora25FileSystem}; + ASSERT_EQ(Distro(Distro::Fedora), Fedora25); + ASSERT_FALSE(Fedora25.IsUbuntu()); + ASSERT_TRUE(Fedora25.IsRedhat()); + ASSERT_FALSE(Fedora25.IsOpenSUSE()); + ASSERT_FALSE(Fedora25.IsDebian()); + + vfs::InMemoryFileSystem CentOS7FileSystem; + CentOS7FileSystem.addFile("/etc/system-release-cpe", 0, + llvm::MemoryBuffer::getMemBuffer("cpe:/o:centos:centos:7\n")); + // Both files are symlinks to centos-release. + CentOS7FileSystem.addFile("/etc/system-release", 0, + llvm::MemoryBuffer::getMemBuffer("CentOS Linux release 7.2.1511 (Core) \n")); + CentOS7FileSystem.addFile("/etc/redhat-release", 0, + llvm::MemoryBuffer::getMemBuffer("CentOS Linux release 7.2.1511 (Core) \n")); + CentOS7FileSystem.addFile("/etc/centos-release", 0, + llvm::MemoryBuffer::getMemBuffer("CentOS Linux release 7.2.1511 (Core) \n")); + CentOS7FileSystem.addFile("/etc/centos-release-upstream", 0, + llvm::MemoryBuffer::getMemBuffer("Derived from Red Hat Enterprise Linux 7.2 (Source)\n")); + CentOS7FileSystem.addFile("/etc/os-release", 0, + llvm::MemoryBuffer::getMemBuffer("NAME=\"CentOS Linux\"\n" + "VERSION=\"7 (Core)\"\n" + "ID=\"centos\"\n" + "ID_LIKE=\"rhel fedora\"\n" + "VERSION_ID=\"7\"\n" + "PRETTY_NAME=\"CentOS Linux 7 (Core)\"\n" + "ANSI_COLOR=\"0;31\"\n" + "CPE_NAME=\"cpe:/o:centos:centos:7\"\n" + "HOME_URL=\"https://www.centos.org/\"\n" + "BUG_REPORT_URL=\"https://bugs.centos.org/\"\n" + "\n" + "CENTOS_MANTISBT_PROJECT=\"CentOS-7\"\n" + "CENTOS_MANTISBT_PROJECT_VERSION=\"7\"\n" + "REDHAT_SUPPORT_PRODUCT=\"centos\"\n" + "REDHAT_SUPPORT_PRODUCT_VERSION=\"7\"\n")); + + Distro CentOS7{CentOS7FileSystem}; + ASSERT_EQ(Distro(Distro::RHEL7), CentOS7); + ASSERT_FALSE(CentOS7.IsUbuntu()); + ASSERT_TRUE(CentOS7.IsRedhat()); + ASSERT_FALSE(CentOS7.IsOpenSUSE()); + ASSERT_FALSE(CentOS7.IsDebian()); +} + +TEST(DistroTest, DetectOpenSUSE) { + vfs::InMemoryFileSystem OpenSUSELeap421FileSystem; + OpenSUSELeap421FileSystem.addFile("/etc/SuSE-release", 0, + llvm::MemoryBuffer::getMemBuffer("openSUSE 42.1 (x86_64)\n" + "VERSION = 42.1\n" + "CODENAME = Malachite\n" + "# /etc/SuSE-release is deprecated and will be removed in the future, use /etc/os-release instead\n")); + OpenSUSELeap421FileSystem.addFile("/etc/os-release", 0, + llvm::MemoryBuffer::getMemBuffer("NAME=\"openSUSE Leap\"\n" + "VERSION=\"42.1\"\n" + "VERSION_ID=\"42.1\"\n" + "PRETTY_NAME=\"openSUSE Leap 42.1 (x86_64)\"\n" + "ID=opensuse\n" + "ANSI_COLOR=\"0;32\"\n" + "CPE_NAME=\"cpe:/o:opensuse:opensuse:42.1\"\n" + "BUG_REPORT_URL=\"https://bugs.opensuse.org\"\n" + "HOME_URL=\"https://opensuse.org/\"\n" + "ID_LIKE=\"suse\"\n")); + + Distro OpenSUSELeap421{OpenSUSELeap421FileSystem}; + ASSERT_EQ(Distro(Distro::OpenSUSE), OpenSUSELeap421); + ASSERT_FALSE(OpenSUSELeap421.IsUbuntu()); + ASSERT_FALSE(OpenSUSELeap421.IsRedhat()); + ASSERT_TRUE(OpenSUSELeap421.IsOpenSUSE()); + ASSERT_FALSE(OpenSUSELeap421.IsDebian()); + + vfs::InMemoryFileSystem OpenSUSE132FileSystem; + OpenSUSE132FileSystem.addFile("/etc/SuSE-release", 0, + llvm::MemoryBuffer::getMemBuffer("openSUSE 13.2 (x86_64)\n" + "VERSION = 13.2\n" + "CODENAME = Harlequin\n" + "# /etc/SuSE-release is deprecated and will be removed in the future, use /etc/os-release instead\n")); + OpenSUSE132FileSystem.addFile("/etc/os-release", 0, + llvm::MemoryBuffer::getMemBuffer("NAME=openSUSE\n" + "VERSION=\"13.2 (Harlequin)\"\n" + "VERSION_ID=\"13.2\"\n" + "PRETTY_NAME=\"openSUSE 13.2 (Harlequin) (x86_64)\"\n" + "ID=opensuse\n" + "ANSI_COLOR=\"0;32\"\n" + "CPE_NAME=\"cpe:/o:opensuse:opensuse:13.2\"\n" + "BUG_REPORT_URL=\"https://bugs.opensuse.org\"\n" + "HOME_URL=\"https://opensuse.org/\"\n" + "ID_LIKE=\"suse\"\n")); + + Distro OpenSUSE132{OpenSUSE132FileSystem}; + ASSERT_EQ(Distro(Distro::OpenSUSE), OpenSUSE132); + ASSERT_FALSE(OpenSUSE132.IsUbuntu()); + ASSERT_FALSE(OpenSUSE132.IsRedhat()); + ASSERT_TRUE(OpenSUSE132.IsOpenSUSE()); + ASSERT_FALSE(OpenSUSE132.IsDebian()); + + vfs::InMemoryFileSystem SLES10FileSystem; + SLES10FileSystem.addFile("/etc/SuSE-release", 0, + llvm::MemoryBuffer::getMemBuffer("SUSE Linux Enterprise Server 10 (x86_64)\n" + "VERSION = 10\n" + "PATCHLEVEL = 4\n")); + SLES10FileSystem.addFile("/etc/lsb_release", 0, + llvm::MemoryBuffer::getMemBuffer("LSB_VERSION=\"core-2.0-noarch:core-3.0-noarch:core-2.0-x86_64:core-3.0-x86_64\"\n")); + + // SLES10 is unsupported and therefore evaluates to unknown + Distro SLES10{SLES10FileSystem}; + ASSERT_EQ(Distro(Distro::UnknownDistro), SLES10); + ASSERT_FALSE(SLES10.IsUbuntu()); + ASSERT_FALSE(SLES10.IsRedhat()); + ASSERT_FALSE(SLES10.IsOpenSUSE()); + ASSERT_FALSE(SLES10.IsDebian()); +} + +TEST(DistroTest, DetectDebian) { + vfs::InMemoryFileSystem DebianJessieFileSystem; + DebianJessieFileSystem.addFile("/etc/debian_version", 0, + llvm::MemoryBuffer::getMemBuffer("8.6\n")); + DebianJessieFileSystem.addFile("/etc/os-release", 0, + llvm::MemoryBuffer::getMemBuffer("PRETTY_NAME=\"Debian GNU/Linux 8 (jessie)\"\n" + "NAME=\"Debian GNU/Linux\"\n" + "VERSION_ID=\"8\"\n" + "VERSION=\"8 (jessie)\"\n" + "ID=debian\n" + "HOME_URL=\"http://www.debian.org/\"\n" + "SUPPORT_URL=\"http://www.debian.org/support\"\n" + "BUG_REPORT_URL=\"https://bugs.debian.org/\"\n")); + + Distro DebianJessie{DebianJessieFileSystem}; + ASSERT_EQ(Distro(Distro::DebianJessie), DebianJessie); + ASSERT_FALSE(DebianJessie.IsUbuntu()); + ASSERT_FALSE(DebianJessie.IsRedhat()); + ASSERT_FALSE(DebianJessie.IsOpenSUSE()); + ASSERT_TRUE(DebianJessie.IsDebian()); + + vfs::InMemoryFileSystem DebianStretchSidFileSystem; + DebianStretchSidFileSystem.addFile("/etc/debian_version", 0, + llvm::MemoryBuffer::getMemBuffer("stretch/sid\n")); + DebianStretchSidFileSystem.addFile("/etc/os-release", 0, + llvm::MemoryBuffer::getMemBuffer("PRETTY_NAME=\"Debian GNU/Linux stretch/sid\"\n" + "NAME=\"Debian GNU/Linux\"\n" + "ID=debian\n" + "HOME_URL=\"http://www.debian.org/\"\n" + "SUPPORT_URL=\"http://www.debian.org/support\"\n" + "BUG_REPORT_URL=\"https://bugs.debian.org/\"\n")); + + Distro DebianStretchSid{DebianStretchSidFileSystem}; + ASSERT_EQ(Distro(Distro::DebianStretch), DebianStretchSid); + ASSERT_FALSE(DebianStretchSid.IsUbuntu()); + ASSERT_FALSE(DebianStretchSid.IsRedhat()); + ASSERT_FALSE(DebianStretchSid.IsOpenSUSE()); + ASSERT_TRUE(DebianStretchSid.IsDebian()); +} + +TEST(DistroTest, DetectExherbo) { + vfs::InMemoryFileSystem ExherboFileSystem; + ExherboFileSystem.addFile("/etc/exherbo-release", 0, // (ASCII art) + llvm::MemoryBuffer::getMemBuffer("")); + ExherboFileSystem.addFile("/etc/os-release", 0, + llvm::MemoryBuffer::getMemBuffer("NAME=\"Exherbo\"\n" + "PRETTY_NAME=\"Exherbo Linux\"\n" + "ID=\"exherbo\"\n" + "ANSI_COLOR=\"0;32\"\n" + "HOME_URL=\"https://www.exherbo.org/\"\n" + "SUPPORT_URL=\"irc://irc.freenode.net/#exherbo\"\n" + "BUG_REPORT_URL=\"https://bugs.exherbo.org/\"\n")); + + Distro Exherbo{ExherboFileSystem}; + ASSERT_EQ(Distro(Distro::Exherbo), Exherbo); + ASSERT_FALSE(Exherbo.IsUbuntu()); + ASSERT_FALSE(Exherbo.IsRedhat()); + ASSERT_FALSE(Exherbo.IsOpenSUSE()); + ASSERT_FALSE(Exherbo.IsDebian()); +} + +TEST(DistroTest, DetectArchLinux) { + vfs::InMemoryFileSystem ArchLinuxFileSystem; + ArchLinuxFileSystem.addFile("/etc/arch-release", 0, // (empty) + llvm::MemoryBuffer::getMemBuffer("")); + ArchLinuxFileSystem.addFile("/etc/os-release", 0, + llvm::MemoryBuffer::getMemBuffer("NAME=\"Arch Linux\"\n" + "ID=arch\n" + "PRETTY_NAME=\"Arch Linux\"\n" + "ANSI_COLOR=\"0;36\"\n" + "HOME_URL=\"https://www.archlinux.org/\"\n" + "SUPPORT_URL=\"https://bbs.archlinux.org/\"\n" + "BUG_REPORT_URL=\"https://bugs.archlinux.org/\"\n")); + + Distro ArchLinux{ArchLinuxFileSystem}; + ASSERT_EQ(Distro(Distro::ArchLinux), ArchLinux); + ASSERT_FALSE(ArchLinux.IsUbuntu()); + ASSERT_FALSE(ArchLinux.IsRedhat()); + ASSERT_FALSE(ArchLinux.IsOpenSUSE()); + ASSERT_FALSE(ArchLinux.IsDebian()); +} + +} // end anonymous namespace diff --git a/unittests/Driver/ToolChainTest.cpp b/unittests/Driver/ToolChainTest.cpp index ef21e2d17c6c..f7ba3eeab21b 100644 --- a/unittests/Driver/ToolChainTest.cpp +++ b/unittests/Driver/ToolChainTest.cpp @@ -117,4 +117,29 @@ TEST(ToolChainTest, VFSGCCInstallationRelativeDir) { S); } +TEST(ToolChainTest, DefaultDriverMode) { + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + struct TestDiagnosticConsumer : public DiagnosticConsumer {}; + DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); + IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( + new vfs::InMemoryFileSystem); + + Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, + InMemoryFileSystem); + Driver CXXDriver("/home/test/bin/clang++", "arm-linux-gnueabi", Diags, + InMemoryFileSystem); + Driver CLDriver("/home/test/bin/clang-cl", "arm-linux-gnueabi", Diags, + InMemoryFileSystem); + + std::unique_ptr<Compilation> CC(CCDriver.BuildCompilation({"foo.cpp"})); + std::unique_ptr<Compilation> CXX(CXXDriver.BuildCompilation({"foo.cpp"})); + std::unique_ptr<Compilation> CL(CLDriver.BuildCompilation({"foo.cpp"})); + + EXPECT_TRUE(CCDriver.CCCIsCC()); + EXPECT_TRUE(CXXDriver.CCCIsCXX()); + EXPECT_TRUE(CLDriver.IsCLMode()); +} + } // end anonymous namespace diff --git a/unittests/Format/CMakeLists.txt b/unittests/Format/CMakeLists.txt index 240be6ead2a5..eb7756a0ba56 100644 --- a/unittests/Format/CMakeLists.txt +++ b/unittests/Format/CMakeLists.txt @@ -7,6 +7,7 @@ add_clang_unittest(FormatTests FormatTest.cpp FormatTestJava.cpp FormatTestJS.cpp + FormatTestObjC.cpp FormatTestProto.cpp FormatTestSelective.cpp SortImportsTestJS.cpp diff --git a/unittests/Format/CleanupTest.cpp b/unittests/Format/CleanupTest.cpp index 5f85c53b780a..6ac5f695d437 100644 --- a/unittests/Format/CleanupTest.cpp +++ b/unittests/Format/CleanupTest.cpp @@ -9,11 +9,15 @@ #include "clang/Format/Format.h" +#include "../Tooling/ReplacementTest.h" #include "../Tooling/RewriterTestContext.h" #include "clang/Tooling/Core/Replacement.h" #include "gtest/gtest.h" +using clang::tooling::ReplacementTest; +using clang::tooling::toReplacements; + namespace clang { namespace format { namespace { @@ -29,6 +33,15 @@ protected: EXPECT_TRUE(static_cast<bool>(Result)); return *Result; } + + // Returns code after cleanup around \p Offsets. + std::string cleanupAroundOffsets(llvm::ArrayRef<unsigned> Offsets, + llvm::StringRef Code) { + std::vector<tooling::Range> Ranges; + for (auto Offset : Offsets) + Ranges.push_back(tooling::Range(Offset, 0)); + return cleanup(Code, Ranges); + } }; TEST_F(CleanupTest, DeleteEmptyNamespaces) { @@ -43,12 +56,7 @@ TEST_F(CleanupTest, DeleteEmptyNamespaces) { std::string Expected = "\n\n\n\n\nnamespace C {\n" "namespace D { int i; }\n \n" "}"; - std::vector<tooling::Range> Ranges; - Ranges.push_back(tooling::Range(28, 0)); - Ranges.push_back(tooling::Range(91, 6)); - Ranges.push_back(tooling::Range(132, 0)); - std::string Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + EXPECT_EQ(Expected, cleanupAroundOffsets({28, 91, 132}, Code)); } TEST_F(CleanupTest, NamespaceWithSyntaxError) { @@ -64,8 +72,7 @@ TEST_F(CleanupTest, NamespaceWithSyntaxError) { "namespace D int i; }\n \n" "}"; std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size())); - std::string Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + EXPECT_EQ(Expected, cleanup(Code, Ranges)); } TEST_F(CleanupTest, EmptyNamespaceNotAffected) { @@ -76,9 +83,7 @@ TEST_F(CleanupTest, EmptyNamespaceNotAffected) { std::string Expected = "namespace A {\n\n" "namespace {\n\n}}"; // Set the changed range to be the second "\n". - std::vector<tooling::Range> Ranges(1, tooling::Range(14, 0)); - std::string Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + EXPECT_EQ(Expected, cleanupAroundOffsets({14}, Code)); } TEST_F(CleanupTest, EmptyNamespaceWithCommentsNoBreakBeforeBrace) { @@ -114,55 +119,94 @@ TEST_F(CleanupTest, EmptyNamespaceWithCommentsBreakBeforeBrace) { EXPECT_EQ(Expected, Result); } +TEST_F(CleanupTest, EmptyNamespaceAroundConditionalCompilation) { + std::string Code = "#ifdef A\n" + "int a;\n" + "int b;\n" + "#else\n" + "#endif\n" + "namespace {}"; + std::string Expected = "#ifdef A\n" + "int a;\n" + "int b;\n" + "#else\n" + "#endif\n"; + std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size())); + FormatStyle Style = getLLVMStyle(); + std::string Result = cleanup(Code, Ranges, Style); + EXPECT_EQ(Expected, Result); +} + TEST_F(CleanupTest, CtorInitializationSimpleRedundantComma) { std::string Code = "class A {\nA() : , {} };"; std::string Expected = "class A {\nA() {} };"; - std::vector<tooling::Range> Ranges; - Ranges.push_back(tooling::Range(17, 0)); - Ranges.push_back(tooling::Range(19, 0)); - std::string Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + EXPECT_EQ(Expected, cleanupAroundOffsets({17, 19}, Code)); Code = "class A {\nA() : x(1), {} };"; Expected = "class A {\nA() : x(1) {} };"; - Ranges.clear(); - Ranges.push_back(tooling::Range(23, 0)); - Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + EXPECT_EQ(Expected, cleanupAroundOffsets({23}, Code)); Code = "class A {\nA() :,,,,{} };"; Expected = "class A {\nA() {} };"; - Ranges.clear(); - Ranges.push_back(tooling::Range(15, 0)); - Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + EXPECT_EQ(Expected, cleanupAroundOffsets({15}, Code)); } -TEST_F(CleanupTest, ListSimpleRedundantComma) { +TEST_F(CleanupTest, CtorInitializationSimpleRedundantColon) { + std::string Code = "class A {\nA() : =default; };"; + std::string Expected = "class A {\nA() =default; };"; + EXPECT_EQ(Expected, cleanupAroundOffsets({15}, Code)); + + Code = "class A {\nA() : , =default; };"; + Expected = "class A {\nA() =default; };"; + EXPECT_EQ(Expected, cleanupAroundOffsets({15}, Code)); +} + +TEST_F(CleanupTest, ListRedundantComma) { std::string Code = "void f() { std::vector<int> v = {1,2,,,3,{4,5}}; }"; std::string Expected = "void f() { std::vector<int> v = {1,2,3,{4,5}}; }"; - std::vector<tooling::Range> Ranges; - Ranges.push_back(tooling::Range(40, 0)); - std::string Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + EXPECT_EQ(Expected, cleanupAroundOffsets({40}, Code)); Code = "int main() { f(1,,2,3,,4);}"; Expected = "int main() { f(1,2,3,4);}"; - Ranges.clear(); - Ranges.push_back(tooling::Range(17, 0)); - Ranges.push_back(tooling::Range(22, 0)); - Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + EXPECT_EQ(Expected, cleanupAroundOffsets({17, 22}, Code)); +} + +TEST_F(CleanupTest, TrailingCommaInParens) { + std::string Code = "int main() { f(,1,,2,3,f(1,2,),4,,);}"; + std::string Expected = "int main() { f(1,2,3,f(1,2),4);}"; + EXPECT_EQ(Expected, cleanupAroundOffsets({15, 18, 29, 33}, Code)); +} + +TEST_F(CleanupTest, TrailingCommaInBraces) { + // Trainling comma is allowed in brace list. + // If there was trailing comma in the original code, then trailing comma is + // preserved. In this example, element between the last two commas is deleted + // causing the second-last comma to be redundant. + std::string Code = "void f() { std::vector<int> v = {1,2,3,,}; }"; + std::string Expected = "void f() { std::vector<int> v = {1,2,3,}; }"; + EXPECT_EQ(Expected, cleanupAroundOffsets({39}, Code)); + + // If there was no trailing comma in the original code, then trainling comma + // introduced by replacements should be cleaned up. In this example, the + // element after the last comma is deleted causing the last comma to be + // redundant. + Code = "void f() { std::vector<int> v = {1,2,3,}; }"; + // FIXME: redundant trailing comma should be removed. + Expected = "void f() { std::vector<int> v = {1,2,3,}; }"; + EXPECT_EQ(Expected, cleanupAroundOffsets({39}, Code)); + + // Still no trailing comma in the original code, but two elements are deleted, + // which makes it seems like there was trailing comma. + Code = "void f() { std::vector<int> v = {1, 2, 3, , }; }"; + // FIXME: redundant trailing comma should also be removed. + Expected = "void f() { std::vector<int> v = {1, 2, 3, }; }"; + EXPECT_EQ(Expected, cleanupAroundOffsets({42, 44}, Code)); } TEST_F(CleanupTest, CtorInitializationBracesInParens) { std::string Code = "class A {\nA() : x({1}),, {} };"; std::string Expected = "class A {\nA() : x({1}) {} };"; - std::vector<tooling::Range> Ranges; - Ranges.push_back(tooling::Range(24, 0)); - Ranges.push_back(tooling::Range(26, 0)); - std::string Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + EXPECT_EQ(Expected, cleanupAroundOffsets({24, 26}, Code)); } TEST_F(CleanupTest, RedundantCommaNotInAffectedRanges) { @@ -184,44 +228,35 @@ TEST_F(CleanupTest, RedundantCommaNotInAffectedRanges) { EXPECT_EQ(Expected, Result); } -// FIXME: delete comments too. -TEST_F(CleanupTest, CtorInitializationCommentAroundCommas) { - // Remove redundant commas around comment. - std::string Code = "class A {\nA() : x({1}), /* comment */, {} };"; - std::string Expected = "class A {\nA() : x({1}) /* comment */ {} };"; - std::vector<tooling::Range> Ranges; - Ranges.push_back(tooling::Range(25, 0)); - Ranges.push_back(tooling::Range(40, 0)); - std::string Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); +TEST_F(CleanupTest, RemoveCommentsAroundDeleteCode) { + std::string Code = + "class A {\nA() : x({1}), /* comment */, /* comment */ {} };"; + std::string Expected = "class A {\nA() : x({1}) {} };"; + EXPECT_EQ(Expected, cleanupAroundOffsets({25, 40}, Code)); - // Remove trailing comma and ignore comment. - Code = "class A {\nA() : x({1}), // comment\n{} };"; - Expected = "class A {\nA() : x({1}) // comment\n{} };"; - Ranges = std::vector<tooling::Range>(1, tooling::Range(25, 0)); - Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + Code = "class A {\nA() : x({1}), // comment\n {} };"; + Expected = "class A {\nA() : x({1})\n {} };"; + EXPECT_EQ(Expected, cleanupAroundOffsets({25}, Code)); - // Remove trailing comma and ignore comment. Code = "class A {\nA() : x({1}), // comment\n , y(1),{} };"; - Expected = "class A {\nA() : x({1}), // comment\n y(1){} };"; - Ranges = std::vector<tooling::Range>(1, tooling::Range(38, 0)); - Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + Expected = "class A {\nA() : x({1}), y(1){} };"; + EXPECT_EQ(Expected, cleanupAroundOffsets({38}, Code)); - // Remove trailing comma and ignore comment. Code = "class A {\nA() : x({1}), \n/* comment */, y(1),{} };"; - Expected = "class A {\nA() : x({1}), \n/* comment */ y(1){} };"; - Ranges = std::vector<tooling::Range>(1, tooling::Range(40, 0)); - Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + Expected = "class A {\nA() : x({1}), \n y(1){} };"; + EXPECT_EQ(Expected, cleanupAroundOffsets({40}, Code)); - // Remove trailing comma and ignore comment. Code = "class A {\nA() : , // comment\n y(1),{} };"; Expected = "class A {\nA() : // comment\n y(1){} };"; - Ranges = std::vector<tooling::Range>(1, tooling::Range(17, 0)); - Result = cleanup(Code, Ranges); - EXPECT_EQ(Expected, Result); + EXPECT_EQ(Expected, cleanupAroundOffsets({17}, Code)); + + Code = "class A {\nA() // comment\n : ,,{} };"; + Expected = "class A {\nA() // comment\n {} };"; + EXPECT_EQ(Expected, cleanupAroundOffsets({30}, Code)); + + Code = "class A {\nA() // comment\n : ,,=default; };"; + Expected = "class A {\nA() // comment\n =default; };"; + EXPECT_EQ(Expected, cleanupAroundOffsets({30}, Code)); } TEST_F(CleanupTest, CtorInitializerInNamespace) { @@ -241,15 +276,19 @@ TEST_F(CleanupTest, CtorInitializerInNamespace) { EXPECT_EQ(Expected, Result); } -class CleanUpReplacementsTest : public ::testing::Test { +class CleanUpReplacementsTest : public ReplacementTest { protected: tooling::Replacement createReplacement(unsigned Offset, unsigned Length, StringRef Text) { return tooling::Replacement(FileName, Offset, Length, Text); } - tooling::Replacement createInsertion(StringRef HeaderName) { - return createReplacement(UINT_MAX, 0, HeaderName); + tooling::Replacement createInsertion(StringRef IncludeDirective) { + return createReplacement(UINT_MAX, 0, IncludeDirective); + } + + tooling::Replacement createDeletion(StringRef HeaderName) { + return createReplacement(UINT_MAX, 1, HeaderName); } inline std::string apply(StringRef Code, @@ -304,9 +343,9 @@ TEST_F(CleanUpReplacementsTest, FixOnlyAffectedCodeAfterReplacements) { "namespace D { int i; }\n\n" "int x= 0;" "}"; - tooling::Replacements Replaces = { - createReplacement(getOffset(Code, 3, 3), 6, ""), - createReplacement(getOffset(Code, 9, 34), 6, "")}; + tooling::Replacements Replaces = + toReplacements({createReplacement(getOffset(Code, 3, 3), 6, ""), + createReplacement(getOffset(Code, 9, 34), 6, "")}); EXPECT_EQ(Expected, formatAndApply(Code, Replaces)); } @@ -315,7 +354,8 @@ TEST_F(CleanUpReplacementsTest, NoExistingIncludeWithoutDefine) { std::string Code = "int main() {}"; std::string Expected = "#include \"a.h\"\n" "int main() {}"; - tooling::Replacements Replaces = {createInsertion("#include \"a.h\"")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include \"a.h\"")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -332,7 +372,8 @@ TEST_F(CleanUpReplacementsTest, NoExistingIncludeWithDefine) { "#define MMM 123\n" "#endif"; - tooling::Replacements Replaces = {createInsertion("#include \"b.h\"")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include \"b.h\"")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -357,7 +398,8 @@ TEST_F(CleanUpReplacementsTest, InsertBeforeCategoryWithLowerPriority) { "#define MMM 123\n" "#endif"; - tooling::Replacements Replaces = {createInsertion("#include \"a.h\"")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include \"a.h\"")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -369,7 +411,8 @@ TEST_F(CleanUpReplacementsTest, InsertAfterMainHeader) { "#include <a>\n" "\n" "int main() {}"; - tooling::Replacements Replaces = {createInsertion("#include <a>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <a>")}); Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -382,7 +425,8 @@ TEST_F(CleanUpReplacementsTest, InsertBeforeSystemHeaderLLVM) { "#include <memory>\n" "\n" "int main() {}"; - tooling::Replacements Replaces = {createInsertion("#include \"z.h\"")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include \"z.h\"")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -394,7 +438,8 @@ TEST_F(CleanUpReplacementsTest, InsertAfterSystemHeaderGoogle) { "#include \"z.h\"\n" "\n" "int main() {}"; - tooling::Replacements Replaces = {createInsertion("#include \"z.h\"")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include \"z.h\"")}); Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -412,8 +457,9 @@ TEST_F(CleanUpReplacementsTest, InsertOneIncludeLLVMStyle) { "#include \"clang/Format/Format.h\"\n" "#include \"llvm/x/y.h\"\n" "#include <memory>\n"; - tooling::Replacements Replaces = {createInsertion("#include \"d.h\""), - createInsertion("#include \"llvm/x/y.h\"")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include \"d.h\""), + createInsertion("#include \"llvm/x/y.h\"")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -430,8 +476,9 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesLLVMStyle) { "#include \"clang/Format/Format.h\"\n" "#include <memory>\n" "#include <list>\n"; - tooling::Replacements Replaces = {createInsertion("#include <list>"), - createInsertion("#include \"new/new.h\"")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <list>"), + createInsertion("#include \"new/new.h\"")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -447,7 +494,8 @@ TEST_F(CleanUpReplacementsTest, InsertNewSystemIncludeGoogleStyle) { "\n" "#include \"y/a.h\"\n" "#include \"z/b.h\"\n"; - tooling::Replacements Replaces = {createInsertion("#include <vector>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -467,8 +515,9 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleIncludesGoogleStyle) { "#include \"y/a.h\"\n" "#include \"z/b.h\"\n" "#include \"x/x.h\"\n"; - tooling::Replacements Replaces = {createInsertion("#include <list>"), - createInsertion("#include \"x/x.h\"")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <list>"), + createInsertion("#include \"x/x.h\"")}); Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -482,12 +531,11 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortLLVM) { "#include <list>\n" "#include <vector>\n" "int x;"; - tooling::Replacements Replaces = {createInsertion("#include \"a.h\""), - createInsertion("#include \"c.h\""), - createInsertion("#include \"b.h\""), - createInsertion("#include <vector>"), - createInsertion("#include <list>"), - createInsertion("#include \"fix.h\"")}; + tooling::Replacements Replaces = toReplacements( + {createInsertion("#include \"a.h\""), createInsertion("#include \"c.h\""), + createInsertion("#include \"b.h\""), + createInsertion("#include <vector>"), createInsertion("#include <list>"), + createInsertion("#include \"fix.h\"")}); EXPECT_EQ(Expected, formatAndApply(Code, Replaces)); } @@ -500,12 +548,11 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortGoogle) { "#include \"b.h\"\n" "#include \"c.h\"\n" "int x;"; - tooling::Replacements Replaces = {createInsertion("#include \"a.h\""), - createInsertion("#include \"c.h\""), - createInsertion("#include \"b.h\""), - createInsertion("#include <vector>"), - createInsertion("#include <list>"), - createInsertion("#include \"fix.h\"")}; + tooling::Replacements Replaces = toReplacements( + {createInsertion("#include \"a.h\""), createInsertion("#include \"c.h\""), + createInsertion("#include \"b.h\""), + createInsertion("#include <vector>"), createInsertion("#include <list>"), + createInsertion("#include \"fix.h\"")}); Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp); EXPECT_EQ(Expected, formatAndApply(Code, Replaces)); } @@ -526,13 +573,12 @@ TEST_F(CleanUpReplacementsTest, FormatCorrectLineWhenHeadersAreInserted) { "int a;\n" "int b;\n" "int a;"; - tooling::Replacements Replaces = { - createReplacement(getOffset(Code, 4, 8), 1, "b"), - createInsertion("#include <vector>"), - createInsertion("#include <list>"), - createInsertion("#include \"clang/x/x.h\""), - createInsertion("#include \"y.h\""), - createInsertion("#include \"x.h\"")}; + tooling::Replacements Replaces = toReplacements( + {createReplacement(getOffset(Code, 4, 8), 1, "b"), + createInsertion("#include <vector>"), createInsertion("#include <list>"), + createInsertion("#include \"clang/x/x.h\""), + createInsertion("#include \"y.h\""), + createInsertion("#include \"x.h\"")}); EXPECT_EQ(Expected, formatAndApply(Code, Replaces)); } @@ -544,7 +590,8 @@ TEST_F(CleanUpReplacementsTest, NotConfusedByDefine) { "void f() {}\n" "#define A \\\n" " int i;"; - tooling::Replacements Replaces = {createInsertion("#include <vector>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); EXPECT_EQ(Expected, formatAndApply(Code, Replaces)); } @@ -556,7 +603,8 @@ TEST_F(CleanUpReplacementsTest, SkippedTopComment) { "\n" " // comment\n" "#include <vector>\n"; - tooling::Replacements Replaces = {createInsertion("#include <vector>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -574,7 +622,8 @@ TEST_F(CleanUpReplacementsTest, SkippedMixedComments) { "* comment\n" "*/\n" "#include <vector>\n"; - tooling::Replacements Replaces = {createInsertion("#include <vector>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -592,7 +641,8 @@ TEST_F(CleanUpReplacementsTest, MultipleBlockCommentsInOneLine) { "\n\n" "/* c1 */ /*c2 */\n" "#include <vector>\n"; - tooling::Replacements Replaces = {createInsertion("#include <vector>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -614,7 +664,8 @@ TEST_F(CleanUpReplacementsTest, CodeAfterComments) { "\n" "#include <vector>\n" "int x;\n"; - tooling::Replacements Replaces = {createInsertion("#include <vector>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -626,7 +677,8 @@ TEST_F(CleanUpReplacementsTest, FakeHeaderGuardIfDef) { "#include <vector>\n" "#ifdef X\n" "#define X\n"; - tooling::Replacements Replaces = {createInsertion("#include <vector>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -642,7 +694,8 @@ TEST_F(CleanUpReplacementsTest, RealHeaderGuardAfterComments) { "#include <vector>\n" "int x;\n" "#define Y 1\n"; - tooling::Replacements Replaces = {createInsertion("#include <vector>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -656,7 +709,21 @@ TEST_F(CleanUpReplacementsTest, IfNDefWithNoDefine) { "#ifndef X\n" "int x;\n" "#define Y 1\n"; - tooling::Replacements Replaces = {createInsertion("#include <vector>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, FakeHeaderGuard) { + std::string Code = "// comment \n" + "#ifndef X\n" + "#define 1\n"; + std::string Expected = "// comment \n" + "#include <vector>\n" + "#ifndef X\n" + "#define 1\n"; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -678,23 +745,34 @@ TEST_F(CleanUpReplacementsTest, HeaderGuardWithComment) { "#include <vector>\n" "int x;\n" "#define Y 1\n"; - tooling::Replacements Replaces = {createInsertion("#include <vector>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } TEST_F(CleanUpReplacementsTest, EmptyCode) { std::string Code = ""; std::string Expected = "#include <vector>\n"; - tooling::Replacements Replaces = {createInsertion("#include <vector>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } -// FIXME: although this case does not crash, the insertion is wrong. A '\n' -// should be inserted between the two #includes. TEST_F(CleanUpReplacementsTest, NoNewLineAtTheEndOfCode) { std::string Code = "#include <map>"; - std::string Expected = "#include <map>#include <vector>\n"; - tooling::Replacements Replaces = {createInsertion("#include <vector>")}; + std::string Expected = "#include <map>\n#include <vector>\n"; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, NoNewLineAtTheEndOfCodeMultipleInsertions) { + std::string Code = "#include <map>"; + std::string Expected = + "#include <map>\n#include <string>\n#include <vector>\n"; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <string>"), + createInsertion("#include <vector>")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -703,8 +781,9 @@ TEST_F(CleanUpReplacementsTest, SkipExistingHeaders) { "#include <vector>\n"; std::string Expected = "#include \"a.h\"\n" "#include <vector>\n"; - tooling::Replacements Replaces = {createInsertion("#include <vector>"), - createInsertion("#include \"a.h\"")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <vector>"), + createInsertion("#include \"a.h\"")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } @@ -716,8 +795,171 @@ TEST_F(CleanUpReplacementsTest, AddIncludesWithDifferentForms) { "#include \"vector\"\n" "#include <vector>\n" "#include <a.h>\n"; - tooling::Replacements Replaces = {createInsertion("#include \"vector\""), - createInsertion("#include <a.h>")}; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include \"vector\""), + createInsertion("#include <a.h>")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, SimpleDeleteIncludes) { + std::string Code = "#include \"abc.h\"\n" + "#include \"xyz.h\" // comment\n" + "#include \"xyz\"\n" + "int x;\n"; + std::string Expected = "#include \"xyz\"\n" + "int x;\n"; + tooling::Replacements Replaces = + toReplacements({createDeletion("abc.h"), createDeletion("xyz.h")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, DeleteAllCode) { + std::string Code = "#include \"xyz.h\"\n" + "#include <xyz.h>"; + std::string Expected = ""; + tooling::Replacements Replaces = toReplacements({createDeletion("xyz.h")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, DeleteAllIncludesWithSameNameIfNoType) { + std::string Code = "#include \"xyz.h\"\n" + "#include \"xyz\"\n" + "#include <xyz.h>\n"; + std::string Expected = "#include \"xyz\"\n"; + tooling::Replacements Replaces = toReplacements({createDeletion("xyz.h")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, OnlyDeleteHeaderWithType) { + std::string Code = "#include \"xyz.h\"\n" + "#include \"xyz\"\n" + "#include <xyz.h>"; + std::string Expected = "#include \"xyz.h\"\n" + "#include \"xyz\"\n"; + tooling::Replacements Replaces = toReplacements({createDeletion("<xyz.h>")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, InsertionAndDeleteHeader) { + std::string Code = "#include \"a.h\"\n" + "\n" + "#include <vector>\n"; + std::string Expected = "#include \"a.h\"\n" + "\n" + "#include <map>\n"; + tooling::Replacements Replaces = toReplacements( + {createDeletion("<vector>"), createInsertion("#include <map>")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, NoInsertionAfterCode) { + std::string Code = "#include \"a.h\"\n" + "void f() {}\n" + "#include \"b.h\"\n"; + std::string Expected = "#include \"a.h\"\n" + "#include \"c.h\"\n" + "void f() {}\n" + "#include \"b.h\"\n"; + tooling::Replacements Replaces = toReplacements( + {createInsertion("#include \"c.h\"")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, NoInsertionInStringLiteral) { + std::string Code = "#include \"a.h\"\n" + "const char[] = R\"(\n" + "#include \"b.h\"\n" + ")\";\n"; + std::string Expected = "#include \"a.h\"\n" + "#include \"c.h\"\n" + "const char[] = R\"(\n" + "#include \"b.h\"\n" + ")\";\n"; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include \"c.h\"")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, NoInsertionAfterOtherDirective) { + std::string Code = "#include \"a.h\"\n" + "#ifdef X\n" + "#include \"b.h\"\n" + "#endif\n"; + std::string Expected = "#include \"a.h\"\n" + "#include \"c.h\"\n" + "#ifdef X\n" + "#include \"b.h\"\n" + "#endif\n"; + tooling::Replacements Replaces = toReplacements( + {createInsertion("#include \"c.h\"")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, CanInsertAfterLongSystemInclude) { + std::string Code = "#include \"a.h\"\n" + "// comment\n\n" + "#include <a/b/c/d/e.h>\n"; + std::string Expected = "#include \"a.h\"\n" + "// comment\n\n" + "#include <a/b/c/d/e.h>\n" + "#include <x.h>\n"; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include <x.h>")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, CanInsertAfterComment) { + std::string Code = "#include \"a.h\"\n" + "// Comment\n" + "\n" + "/* Comment */\n" + "// Comment\n" + "\n" + "#include \"b.h\"\n"; + std::string Expected = "#include \"a.h\"\n" + "// Comment\n" + "\n" + "/* Comment */\n" + "// Comment\n" + "\n" + "#include \"b.h\"\n" + "#include \"c.h\"\n"; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include \"c.h\"")}); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, LongCommentsInTheBeginningOfFile) { + std::string Code = "// Loooooooooooooooooooooooooong comment\n" + "// Loooooooooooooooooooooooooong comment\n" + "// Loooooooooooooooooooooooooong comment\n" + "#include <string>\n" + "#include <vector>\n" + "\n" + "#include \"a.h\"\n" + "#include \"b.h\"\n"; + std::string Expected = "// Loooooooooooooooooooooooooong comment\n" + "// Loooooooooooooooooooooooooong comment\n" + "// Loooooooooooooooooooooooooong comment\n" + "#include <string>\n" + "#include <vector>\n" + "\n" + "#include \"a.h\"\n" + "#include \"b.h\"\n" + "#include \"third.h\"\n"; + tooling::Replacements Replaces = + toReplacements({createInsertion("#include \"third.h\"")}); + Style = format::getGoogleStyle(format::FormatStyle::LanguageKind::LK_Cpp); + EXPECT_EQ(Expected, apply(Code, Replaces)); +} + +TEST_F(CleanUpReplacementsTest, CanDeleteAfterCode) { + std::string Code = "#include \"a.h\"\n" + "void f() {}\n" + "#include \"b.h\"\n"; + std::string Expected = "#include \"a.h\"\n" + "void f() {}\n"; + tooling::Replacements Replaces = toReplacements({createDeletion("\"b.h\"")}); EXPECT_EQ(Expected, apply(Code, Replaces)); } diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 8d46ba6efcfe..6f9df680eef5 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -9,7 +9,7 @@ #include "clang/Format/Format.h" -#include "../Tooling/RewriterTestContext.h" +#include "../Tooling/ReplacementTest.h" #include "FormatTestUtils.h" #include "clang/Frontend/TextDiagnosticPrinter.h" @@ -19,6 +19,9 @@ #define DEBUG_TYPE "format-test" +using clang::tooling::ReplacementTest; +using clang::tooling::toReplacements; + namespace clang { namespace format { namespace { @@ -273,6 +276,30 @@ TEST_F(FormatTest, RemovesEmptyLines) { "int i;\n" "\n" "} // namespace")); + + FormatStyle Style = getLLVMStyle(); + Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All; + Style.MaxEmptyLinesToKeep = 2; + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterClass = true; + Style.BraceWrapping.AfterFunction = true; + Style.KeepEmptyLinesAtTheStartOfBlocks = false; + + EXPECT_EQ("class Foo\n" + "{\n" + " Foo() {}\n" + "\n" + " void funk() {}\n" + "};", + format("class Foo\n" + "{\n" + " Foo()\n" + " {\n" + " }\n" + "\n" + " void funk() {}\n" + "};", + Style)); } TEST_F(FormatTest, RecognizesBinaryOperatorKeywords) { @@ -1110,6 +1137,12 @@ TEST_F(FormatTest, KeepsParameterWithTrailingCommentsOnTheirOwnLine) { format("SomeFunction(a, // comment\n" " b,\n" " c); // comment")); + EXPECT_EQ("aaaaaaaaaa(aaaa(aaaa,\n" + " aaaa), //\n" + " aaaa, bbbbb);", + format("aaaaaaaaaa(aaaa(aaaa,\n" + "aaaa), //\n" + "aaaa, bbbbb);")); } TEST_F(FormatTest, RemovesTrailingWhitespaceOfComments) { @@ -1934,6 +1967,10 @@ TEST_F(FormatTest, UnderstandsAccessSpecifiers) { verifyFormat("{\n" " signals.set(); // This needs indentation.\n" "}"); + verifyFormat("void f() {\n" + "label:\n" + " signals.baz();\n" + "}"); } TEST_F(FormatTest, SeparatesLogicalBlocks) { @@ -2456,42 +2493,6 @@ TEST_F(FormatTest, FormatTryCatchBraceStyles) { Style); } -TEST_F(FormatTest, FormatObjCTryCatch) { - verifyFormat("@try {\n" - " f();\n" - "} @catch (NSException e) {\n" - " @throw;\n" - "} @finally {\n" - " exit(42);\n" - "}"); - verifyFormat("DEBUG({\n" - " @try {\n" - " } @finally {\n" - " }\n" - "});\n"); -} - -TEST_F(FormatTest, FormatObjCAutoreleasepool) { - FormatStyle Style = getLLVMStyle(); - verifyFormat("@autoreleasepool {\n" - " f();\n" - "}\n" - "@autoreleasepool {\n" - " f();\n" - "}\n", - Style); - Style.BreakBeforeBraces = FormatStyle::BS_Allman; - verifyFormat("@autoreleasepool\n" - "{\n" - " f();\n" - "}\n" - "@autoreleasepool\n" - "{\n" - " f();\n" - "}\n", - Style); -} - TEST_F(FormatTest, StaticInitializers) { verifyFormat("static SomeClass SC = {1, 'a'};"); @@ -4540,12 +4541,13 @@ TEST_F(FormatTest, ParenthesesAndOperandAlignment) { TEST_F(FormatTest, BreaksConditionalExpressions) { verifyFormat( - "aaaa(aaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaa\n" - " ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" - " : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);"); + "aaaa(aaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaa ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);"); verifyFormat( - "aaaa(aaaaaaaaaaaaaaaaaaaa, aaaaaaa ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" - " : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);"); + "aaaa(aaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaa ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);"); verifyFormat( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaa ? aaaa(aaaaaa)\n" " : aaaaaaaaaaaaa);"); @@ -4595,11 +4597,12 @@ TEST_F(FormatTest, BreaksConditionalExpressions) { " ? aaaa\n" " : bbbb;"); verifyFormat("unsigned Indent =\n" - " format(TheLine.First, IndentForLevel[TheLine.Level] >= 0\n" - " ? IndentForLevel[TheLine.Level]\n" - " : TheLine * 2,\n" + " format(TheLine.First,\n" + " IndentForLevel[TheLine.Level] >= 0\n" + " ? IndentForLevel[TheLine.Level]\n" + " : TheLine * 2,\n" " TheLine.InPPDirective, PreviousEndOfLineColumn);", - getLLVMStyleWithColumns(70)); + getLLVMStyleWithColumns(60)); verifyFormat("bool aaaaaa = aaaaaaaaaaaaa //\n" " ? aaaaaaaaaaaaaaa\n" " : bbbbbbbbbbbbbbb //\n" @@ -4674,13 +4677,14 @@ TEST_F(FormatTest, BreaksConditionalExpressionsAfterOperator) { Style.BreakBeforeTernaryOperators = false; Style.ColumnLimit = 70; verifyFormat( - "aaaa(aaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaa ?\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa :\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", + "aaaa(aaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaa ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa :\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", Style); verifyFormat( - "aaaa(aaaaaaaaaaaaaaaaaaaa, aaaaaaa ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa :\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", + "aaaa(aaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaa ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa :\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", Style); verifyFormat( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaa ? aaaa(aaaaaa) :\n" @@ -4736,13 +4740,13 @@ TEST_F(FormatTest, BreaksConditionalExpressionsAfterOperator) { " b :\n" " c);", Style); - verifyFormat( - "unsigned Indent =\n" - " format(TheLine.First, IndentForLevel[TheLine.Level] >= 0 ?\n" - " IndentForLevel[TheLine.Level] :\n" - " TheLine * 2,\n" - " TheLine.InPPDirective, PreviousEndOfLineColumn);", - Style); + verifyFormat("unsigned Indent =\n" + " format(TheLine.First,\n" + " IndentForLevel[TheLine.Level] >= 0 ?\n" + " IndentForLevel[TheLine.Level] :\n" + " TheLine * 2,\n" + " TheLine.InPPDirective, PreviousEndOfLineColumn);", + Style); verifyFormat("bool aaaaaa = aaaaaaaaaaaaa ? //\n" " aaaaaaaaaaaaaaa :\n" " bbbbbbbbbbbbbbb ? //\n" @@ -4795,6 +4799,8 @@ TEST_F(FormatTest, DeclarationsOfMultipleVariables) { verifyFormat("aaaaaaaaa *a = aaaaaaaaaaaaaaaaaaa, *b = bbbbbbbbbbbbbbbbbbb,\n" " *b = bbbbbbbbbbbbbbbbbbb, *d = ddddddddddddddddddd;", Style); + verifyFormat("vector<int*> a, b;", Style); + verifyFormat("for (int *p, *q; p != q; p = p->next) {\n}", Style); } TEST_F(FormatTest, ConditionalExpressionsInBrackets) { @@ -5086,6 +5092,9 @@ TEST_F(FormatTest, AlignsPipes) { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa << aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" " << aaaaaaaaaaaaaaaaaaaaaaaaaaaa;"); verifyFormat( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()\n" + " << aaaaaaaaaaaaaaaaaaaaaaaaaaaa << aaaaaaaaaaaaaaaaaaaaaaaaaaaa;"); + verifyFormat( "llvm::outs() << \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n" " \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"\n" " << \"ccccccccccccccccccccccccccccccccccccccccccccccccc\";"); @@ -5103,29 +5112,6 @@ TEST_F(FormatTest, AlignsPipes) { "llvm::errs() << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaa);"); - verifyFormat("return out << \"somepacket = {\\n\"\n" - " << \" aaaaaa = \" << pkt.aaaaaa << \"\\n\"\n" - " << \" bbbb = \" << pkt.bbbb << \"\\n\"\n" - " << \" cccccc = \" << pkt.cccccc << \"\\n\"\n" - " << \" ddd = [\" << pkt.ddd << \"]\\n\"\n" - " << \"}\";"); - - verifyFormat("llvm::outs() << \"aaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaa\n" - " << \"aaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaa\n" - " << \"aaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaa;"); - verifyFormat( - "llvm::outs() << \"aaaaaaaaaaaaaaaaa = \" << aaaaaaaaaaaaaaaaa\n" - " << \"bbbbbbbbbbbbbbbbb = \" << bbbbbbbbbbbbbbbbb\n" - " << \"ccccccccccccccccc = \" << ccccccccccccccccc\n" - " << \"ddddddddddddddddd = \" << ddddddddddddddddd\n" - " << \"eeeeeeeeeeeeeeeee = \" << eeeeeeeeeeeeeeeee;"); - verifyFormat("llvm::outs() << aaaaaaaaaaaaaaaaaaaaaaaa << \"=\"\n" - " << bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;"); - verifyFormat( - "void f() {\n" - " llvm::outs() << \"aaaaaaaaaaaaaaaaaaaa: \"\n" - " << aaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaa);\n" - "}"); verifyFormat("llvm::outs() << \"aaaaaaaaaaaaaaaa: \"\n" " << aaaaaaaa.aaaaaaaaaaaa(aaa)->aaaaaaaaaaaaaa();"); verifyFormat("llvm::errs() << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" @@ -5136,22 +5122,6 @@ TEST_F(FormatTest, AlignsPipes) { " bbb)\n" " << a << b;"); - // Breaking before the first "<<" is generally not desirable. - verifyFormat( - "llvm::errs()\n" - " << \"aaaaaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" - " << \"aaaaaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" - " << \"aaaaaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" - " << \"aaaaaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaaaaaaaaaaaaaa;", - getLLVMStyleWithColumns(70)); - verifyFormat("llvm::errs() << \"aaaaaaaaaaaaaaaaaaa: \"\n" - " << aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" - " << \"aaaaaaaaaaaaaaaaaaa: \"\n" - " << aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" - " << \"aaaaaaaaaaaaaaaaaaa: \"\n" - " << aaaaaaaaaaaaaaaaaaaaaaaaaaaa;", - getLLVMStyleWithColumns(70)); - // But sometimes, breaking before the first "<<" is desirable. verifyFormat("Diag(aaaaaaaaaaaaaaaaaaaa, aaaaaaaa)\n" " << aaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaa);"); @@ -5197,6 +5167,65 @@ TEST_F(FormatTest, AlignsPipes) { verifyFormat("llvm::errs() << \"\\n\" << bbbbbbbbbbbbbbbbbbbbbb << \"\\n\";"); } +TEST_F(FormatTest, KeepStringLabelValuePairsOnALine) { + verifyFormat("return out << \"somepacket = {\\n\"\n" + " << \" aaaaaa = \" << pkt.aaaaaa << \"\\n\"\n" + " << \" bbbb = \" << pkt.bbbb << \"\\n\"\n" + " << \" cccccc = \" << pkt.cccccc << \"\\n\"\n" + " << \" ddd = [\" << pkt.ddd << \"]\\n\"\n" + " << \"}\";"); + + verifyFormat("llvm::outs() << \"aaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaa\n" + " << \"aaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaa\n" + " << \"aaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaa;"); + verifyFormat( + "llvm::outs() << \"aaaaaaaaaaaaaaaaa = \" << aaaaaaaaaaaaaaaaa\n" + " << \"bbbbbbbbbbbbbbbbb = \" << bbbbbbbbbbbbbbbbb\n" + " << \"ccccccccccccccccc = \" << ccccccccccccccccc\n" + " << \"ddddddddddddddddd = \" << ddddddddddddddddd\n" + " << \"eeeeeeeeeeeeeeeee = \" << eeeeeeeeeeeeeeeee;"); + verifyFormat("llvm::outs() << aaaaaaaaaaaaaaaaaaaaaaaa << \"=\"\n" + " << bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;"); + verifyFormat( + "void f() {\n" + " llvm::outs() << \"aaaaaaaaaaaaaaaaaaaa: \"\n" + " << aaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaa);\n" + "}"); + + // Breaking before the first "<<" is generally not desirable. + verifyFormat( + "llvm::errs()\n" + " << \"aaaaaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " << \"aaaaaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " << \"aaaaaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " << \"aaaaaaaaaaaaaaaaaaa: \" << aaaaaaaaaaaaaaaaaaaaaaaaaaaa;", + getLLVMStyleWithColumns(70)); + verifyFormat("llvm::errs() << \"aaaaaaaaaaaaaaaaaaa: \"\n" + " << aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " << \"aaaaaaaaaaaaaaaaaaa: \"\n" + " << aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " << \"aaaaaaaaaaaaaaaaaaa: \"\n" + " << aaaaaaaaaaaaaaaaaaaaaaaaaaaa;", + getLLVMStyleWithColumns(70)); + + verifyFormat("string v = \"aaaaaaaaaaaaaaaa: \" + aaaaaaaaaaaaaaaa +\n" + " \"aaaaaaaaaaaaaaaa: \" + aaaaaaaaaaaaaaaa +\n" + " \"aaaaaaaaaaaaaaaa: \" + aaaaaaaaaaaaaaaa;"); + verifyFormat("string v = StrCat(\"aaaaaaaaaaaaaaaa: \", aaaaaaaaaaaaaaaa,\n" + " \"aaaaaaaaaaaaaaaa: \", aaaaaaaaaaaaaaaa,\n" + " \"aaaaaaaaaaaaaaaa: \", aaaaaaaaaaaaaaaa);"); + verifyFormat("string v = \"aaaaaaaaaaaaaaaa: \" +\n" + " (aaaa + aaaa);", + getLLVMStyleWithColumns(40)); + verifyFormat("string v = StrCat(\"aaaaaaaaaaaa: \" +\n" + " (aaaaaaa + aaaaa));", + getLLVMStyleWithColumns(40)); + verifyFormat( + "string v = StrCat(\"aaaaaaaaaaaaaaaaaaaaaaaaaaa: \",\n" + " SomeFunction(aaaaaaaaaaaa, aaaaaaaa.aaaaaaa),\n" + " bbbbbbbbbbbbbbbbbbbbbbb);"); +} + TEST_F(FormatTest, UnderstandsEquals) { verifyFormat( "aaaaaaaaaaaaaaaaa =\n" @@ -5492,6 +5521,18 @@ TEST_F(FormatTest, UnderstandsTemplateParameters) { verifyFormat("< < < < < < < < < < < < < < < < < < < < < < < < < < < < < <"); } +TEST_F(FormatTest, BitshiftOperatorWidth) { + EXPECT_EQ("int a = 1 << 2; /* foo\n" + " bar */", + format("int a=1<<2; /* foo\n" + " bar */")); + + EXPECT_EQ("int b = 256 >> 1; /* foo\n" + " bar */", + format("int b =256>>1 ; /* foo\n" + " bar */")); +} + TEST_F(FormatTest, UnderstandsBinaryOperators) { verifyFormat("COMPARE(a, ==, b);"); verifyFormat("auto s = sizeof...(Ts) - 1;"); @@ -5628,6 +5669,9 @@ TEST_F(FormatTest, UnderstandsFunctionRefQualification) { verifyFormat("SomeType MemberFunction(const Deleted &) && final {}"); verifyFormat("SomeType MemberFunction(const Deleted &) && override {}"); verifyFormat("SomeType MemberFunction(const Deleted &) const &;"); + verifyFormat("template <typename T>\n" + "void F(T) && = delete;", + getGoogleStyle()); FormatStyle AlignLeft = getLLVMStyle(); AlignLeft.PointerAlignment = FormatStyle::PAS_Left; @@ -5780,7 +5824,7 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { FormatStyle Left = getLLVMStyle(); Left.PointerAlignment = FormatStyle::PAS_Left; verifyFormat("x = *a(x) = *a(y);", Left); - verifyFormat("for (;; * = b) {\n}", Left); + verifyFormat("for (;; *a = b) {\n}", Left); verifyFormat("return *this += 1;", Left); verifyIndependentOfContext("a = *(x + y);"); @@ -5845,11 +5889,12 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyFormat("foo<b & 1>();"); verifyFormat("decltype(*::std::declval<const T &>()) void F();"); verifyFormat( - "template <class T, class = typename std::enable_if<\n" - " std::is_integral<T>::value &&\n" - " (sizeof(T) > 1 || sizeof(T) < 8)>::type>\n" + "template <class T,\n" + " class = typename std::enable_if<\n" + " std::is_integral<T>::value &&\n" + " (sizeof(T) > 1 || sizeof(T) < 8)>::type>\n" "void F();", - getLLVMStyleWithColumns(76)); + getLLVMStyleWithColumns(70)); verifyFormat( "template <class T,\n" " class = typename ::std::enable_if<\n" @@ -5861,6 +5906,7 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyIndependentOfContext("MACRO(auto *a);"); verifyIndependentOfContext("MACRO(const A *a);"); verifyIndependentOfContext("MACRO('0' <= c && c <= '9');"); + verifyFormat("void f() { f(float{1}, a * a); }"); // FIXME: Is there a way to make this work? // verifyIndependentOfContext("MACRO(A *a);"); @@ -6481,6 +6527,19 @@ TEST_F(FormatTest, LayoutCxx11BraceInitializers) { "};"); verifyFormat("#define A {a, a},"); + // Cases where distinguising braced lists and blocks is hard. + verifyFormat("vector<int> v{12} GUARDED_BY(mutex);"); + verifyFormat("void f() {\n" + " return; // comment\n" + "}\n" + "SomeType t;"); + verifyFormat("void f() {\n" + " if (a) {\n" + " f();\n" + " }\n" + "}\n" + "SomeType t;"); + // In combination with BinPackArguments = false. FormatStyle NoBinPacking = getLLVMStyle(); NoBinPacking.BinPackArguments = false; @@ -6595,7 +6654,7 @@ TEST_F(FormatTest, LayoutCxx11BraceInitializers) { "std::this_thread::sleep_for(\n" " std::chrono::nanoseconds{ std::chrono::seconds{ 1 } } / 5);", ExtraSpaces); - verifyFormat("std::vector<MyValues> aaaaaaaaaaaaaaaaaaa{\n" + verifyFormat("std::vector<MyValues> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{\n" " aaaaaaa,\n" " aaaaaaaaaa,\n" " aaaaa,\n" @@ -6733,6 +6792,26 @@ TEST_F(FormatTest, FormatsBracedListsInColumnLayout) { " 1, 22, 333, 4444, 55555, 666666, 7777777,\n" " 1, 22, 333, 4444, 55555, 666666, 7777777,\n" " 1, 22, 333, 4444, 55555, 666666, 7777777});"); + + // Allow "single-column" layout even if that violates the column limit. There + // isn't going to be a better way. + verifyFormat("std::vector<int> a = {\n" + " aaaaaaaa,\n" + " aaaaaaaa,\n" + " aaaaaaaa,\n" + " aaaaaaaa,\n" + " aaaaaaaaaa,\n" + " aaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaa};", + getLLVMStyleWithColumns(30)); + verifyFormat("vector<int> aaaa = {\n" + " aaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaa.aaaaaaa,\n" + " aaaaaa.aaaaaaa,\n" + " aaaaaa.aaaaaaa,\n" + " aaaaaa.aaaaaaa,\n" + "};"); } TEST_F(FormatTest, PullTrivialFunctionDefinitionsIntoSingleLine) { @@ -7170,6 +7249,30 @@ TEST_F(FormatTest, SpecialTokensAtEndOfLine) { verifyFormat("operator"); } +TEST_F(FormatTest, SkipsDeeplyNestedLines) { + // This code would be painfully slow to format if we didn't skip it. + std::string Code("A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(\n" // 20x + "A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(\n" + "A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(\n" + "A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(\n" + "A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(A(\n" + "A(1, 1)\n" + ", 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)\n" // 10x + ", 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)\n" + ", 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)\n" + ", 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)\n" + ", 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)\n" + ", 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)\n" + ", 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)\n" + ", 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)\n" + ", 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)\n" + ", 1), 1), 1), 1), 1), 1), 1), 1), 1), 1);\n"); + // Deeply nested part is untouched, rest is formatted. + EXPECT_EQ(std::string("int i;\n") + Code + "int j;\n", + format(std::string("int i;\n") + Code + "int j;\n", + getLLVMStyle(), IC_ExpectIncomplete)); +} + //===----------------------------------------------------------------------===// // Objective-C tests. //===----------------------------------------------------------------------===// @@ -7240,684 +7343,6 @@ TEST_F(FormatTest, FormatForObjectiveCMethodDecls) { verifyGoogleFormat("- foo:(int)foo;"); } -TEST_F(FormatTest, FormatObjCInterface) { - verifyFormat("@interface Foo : NSObject <NSSomeDelegate> {\n" - "@public\n" - " int field1;\n" - "@protected\n" - " int field2;\n" - "@private\n" - " int field3;\n" - "@package\n" - " int field4;\n" - "}\n" - "+ (id)init;\n" - "@end"); - - verifyGoogleFormat("@interface Foo : NSObject<NSSomeDelegate> {\n" - " @public\n" - " int field1;\n" - " @protected\n" - " int field2;\n" - " @private\n" - " int field3;\n" - " @package\n" - " int field4;\n" - "}\n" - "+ (id)init;\n" - "@end"); - - verifyFormat("@interface /* wait for it */ Foo\n" - "+ (id)init;\n" - "// Look, a comment!\n" - "- (int)answerWith:(int)i;\n" - "@end"); - - verifyFormat("@interface Foo\n" - "@end\n" - "@interface Bar\n" - "@end"); - - verifyFormat("@interface Foo : Bar\n" - "+ (id)init;\n" - "@end"); - - verifyFormat("@interface Foo : /**/ Bar /**/ <Baz, /**/ Quux>\n" - "+ (id)init;\n" - "@end"); - - verifyGoogleFormat("@interface Foo : Bar<Baz, Quux>\n" - "+ (id)init;\n" - "@end"); - - verifyFormat("@interface Foo (HackStuff)\n" - "+ (id)init;\n" - "@end"); - - verifyFormat("@interface Foo ()\n" - "+ (id)init;\n" - "@end"); - - verifyFormat("@interface Foo (HackStuff) <MyProtocol>\n" - "+ (id)init;\n" - "@end"); - - verifyGoogleFormat("@interface Foo (HackStuff)<MyProtocol>\n" - "+ (id)init;\n" - "@end"); - - verifyFormat("@interface Foo {\n" - " int _i;\n" - "}\n" - "+ (id)init;\n" - "@end"); - - verifyFormat("@interface Foo : Bar {\n" - " int _i;\n" - "}\n" - "+ (id)init;\n" - "@end"); - - verifyFormat("@interface Foo : Bar <Baz, Quux> {\n" - " int _i;\n" - "}\n" - "+ (id)init;\n" - "@end"); - - verifyFormat("@interface Foo (HackStuff) {\n" - " int _i;\n" - "}\n" - "+ (id)init;\n" - "@end"); - - verifyFormat("@interface Foo () {\n" - " int _i;\n" - "}\n" - "+ (id)init;\n" - "@end"); - - verifyFormat("@interface Foo (HackStuff) <MyProtocol> {\n" - " int _i;\n" - "}\n" - "+ (id)init;\n" - "@end"); - - FormatStyle OnePerLine = getGoogleStyle(); - OnePerLine.BinPackParameters = false; - verifyFormat("@interface aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ()<\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa> {\n" - "}", - OnePerLine); -} - -TEST_F(FormatTest, FormatObjCImplementation) { - verifyFormat("@implementation Foo : NSObject {\n" - "@public\n" - " int field1;\n" - "@protected\n" - " int field2;\n" - "@private\n" - " int field3;\n" - "@package\n" - " int field4;\n" - "}\n" - "+ (id)init {\n}\n" - "@end"); - - verifyGoogleFormat("@implementation Foo : NSObject {\n" - " @public\n" - " int field1;\n" - " @protected\n" - " int field2;\n" - " @private\n" - " int field3;\n" - " @package\n" - " int field4;\n" - "}\n" - "+ (id)init {\n}\n" - "@end"); - - verifyFormat("@implementation Foo\n" - "+ (id)init {\n" - " if (true)\n" - " return nil;\n" - "}\n" - "// Look, a comment!\n" - "- (int)answerWith:(int)i {\n" - " return i;\n" - "}\n" - "+ (int)answerWith:(int)i {\n" - " return i;\n" - "}\n" - "@end"); - - verifyFormat("@implementation Foo\n" - "@end\n" - "@implementation Bar\n" - "@end"); - - EXPECT_EQ("@implementation Foo : Bar\n" - "+ (id)init {\n}\n" - "- (void)foo {\n}\n" - "@end", - format("@implementation Foo : Bar\n" - "+(id)init{}\n" - "-(void)foo{}\n" - "@end")); - - verifyFormat("@implementation Foo {\n" - " int _i;\n" - "}\n" - "+ (id)init {\n}\n" - "@end"); - - verifyFormat("@implementation Foo : Bar {\n" - " int _i;\n" - "}\n" - "+ (id)init {\n}\n" - "@end"); - - verifyFormat("@implementation Foo (HackStuff)\n" - "+ (id)init {\n}\n" - "@end"); - verifyFormat("@implementation ObjcClass\n" - "- (void)method;\n" - "{}\n" - "@end"); -} - -TEST_F(FormatTest, FormatObjCProtocol) { - verifyFormat("@protocol Foo\n" - "@property(weak) id delegate;\n" - "- (NSUInteger)numberOfThings;\n" - "@end"); - - verifyFormat("@protocol MyProtocol <NSObject>\n" - "- (NSUInteger)numberOfThings;\n" - "@end"); - - verifyGoogleFormat("@protocol MyProtocol<NSObject>\n" - "- (NSUInteger)numberOfThings;\n" - "@end"); - - verifyFormat("@protocol Foo;\n" - "@protocol Bar;\n"); - - verifyFormat("@protocol Foo\n" - "@end\n" - "@protocol Bar\n" - "@end"); - - verifyFormat("@protocol myProtocol\n" - "- (void)mandatoryWithInt:(int)i;\n" - "@optional\n" - "- (void)optional;\n" - "@required\n" - "- (void)required;\n" - "@optional\n" - "@property(assign) int madProp;\n" - "@end\n"); - - verifyFormat("@property(nonatomic, assign, readonly)\n" - " int *looooooooooooooooooooooooooooongNumber;\n" - "@property(nonatomic, assign, readonly)\n" - " NSString *looooooooooooooooooooooooooooongName;"); - - verifyFormat("@implementation PR18406\n" - "}\n" - "@end"); -} - -TEST_F(FormatTest, FormatObjCMethodDeclarations) { - verifyFormat("- (void)doSomethingWith:(GTMFoo *)theFoo\n" - " rect:(NSRect)theRect\n" - " interval:(float)theInterval {\n" - "}"); - verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n" - " longKeyword:(NSRect)theRect\n" - " longerKeyword:(float)theInterval\n" - " error:(NSError **)theError {\n" - "}"); - verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n" - " longKeyword:(NSRect)theRect\n" - " evenLongerKeyword:(float)theInterval\n" - " error:(NSError **)theError {\n" - "}"); - verifyFormat("- (instancetype)initXxxxxx:(id<x>)x\n" - " y:(id<yyyyyyyyyyyyyyyyyyyy>)y\n" - " NS_DESIGNATED_INITIALIZER;", - getLLVMStyleWithColumns(60)); - - // Continuation indent width should win over aligning colons if the function - // name is long. - FormatStyle continuationStyle = getGoogleStyle(); - continuationStyle.ColumnLimit = 40; - continuationStyle.IndentWrappedFunctionNames = true; - verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n" - " dontAlignNamef:(NSRect)theRect {\n" - "}", - continuationStyle); - - // Make sure we don't break aligning for short parameter names. - verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n" - " aShortf:(NSRect)theRect {\n" - "}", - continuationStyle); -} - -TEST_F(FormatTest, FormatObjCMethodExpr) { - verifyFormat("[foo bar:baz];"); - verifyFormat("return [foo bar:baz];"); - verifyFormat("return (a)[foo bar:baz];"); - verifyFormat("f([foo bar:baz]);"); - verifyFormat("f(2, [foo bar:baz]);"); - verifyFormat("f(2, a ? b : c);"); - verifyFormat("[[self initWithInt:4] bar:[baz quux:arrrr]];"); - - // Unary operators. - verifyFormat("int a = +[foo bar:baz];"); - verifyFormat("int a = -[foo bar:baz];"); - verifyFormat("int a = ![foo bar:baz];"); - verifyFormat("int a = ~[foo bar:baz];"); - verifyFormat("int a = ++[foo bar:baz];"); - verifyFormat("int a = --[foo bar:baz];"); - verifyFormat("int a = sizeof [foo bar:baz];"); - verifyFormat("int a = alignof [foo bar:baz];", getGoogleStyle()); - verifyFormat("int a = &[foo bar:baz];"); - verifyFormat("int a = *[foo bar:baz];"); - // FIXME: Make casts work, without breaking f()[4]. - // verifyFormat("int a = (int)[foo bar:baz];"); - // verifyFormat("return (int)[foo bar:baz];"); - // verifyFormat("(void)[foo bar:baz];"); - verifyFormat("return (MyType *)[self.tableView cellForRowAtIndexPath:cell];"); - - // Binary operators. - verifyFormat("[foo bar:baz], [foo bar:baz];"); - verifyFormat("[foo bar:baz] = [foo bar:baz];"); - verifyFormat("[foo bar:baz] *= [foo bar:baz];"); - verifyFormat("[foo bar:baz] /= [foo bar:baz];"); - verifyFormat("[foo bar:baz] %= [foo bar:baz];"); - verifyFormat("[foo bar:baz] += [foo bar:baz];"); - verifyFormat("[foo bar:baz] -= [foo bar:baz];"); - verifyFormat("[foo bar:baz] <<= [foo bar:baz];"); - verifyFormat("[foo bar:baz] >>= [foo bar:baz];"); - verifyFormat("[foo bar:baz] &= [foo bar:baz];"); - verifyFormat("[foo bar:baz] ^= [foo bar:baz];"); - verifyFormat("[foo bar:baz] |= [foo bar:baz];"); - verifyFormat("[foo bar:baz] ? [foo bar:baz] : [foo bar:baz];"); - verifyFormat("[foo bar:baz] || [foo bar:baz];"); - verifyFormat("[foo bar:baz] && [foo bar:baz];"); - verifyFormat("[foo bar:baz] | [foo bar:baz];"); - verifyFormat("[foo bar:baz] ^ [foo bar:baz];"); - verifyFormat("[foo bar:baz] & [foo bar:baz];"); - verifyFormat("[foo bar:baz] == [foo bar:baz];"); - verifyFormat("[foo bar:baz] != [foo bar:baz];"); - verifyFormat("[foo bar:baz] >= [foo bar:baz];"); - verifyFormat("[foo bar:baz] <= [foo bar:baz];"); - verifyFormat("[foo bar:baz] > [foo bar:baz];"); - verifyFormat("[foo bar:baz] < [foo bar:baz];"); - verifyFormat("[foo bar:baz] >> [foo bar:baz];"); - verifyFormat("[foo bar:baz] << [foo bar:baz];"); - verifyFormat("[foo bar:baz] - [foo bar:baz];"); - verifyFormat("[foo bar:baz] + [foo bar:baz];"); - verifyFormat("[foo bar:baz] * [foo bar:baz];"); - verifyFormat("[foo bar:baz] / [foo bar:baz];"); - verifyFormat("[foo bar:baz] % [foo bar:baz];"); - // Whew! - - verifyFormat("return in[42];"); - verifyFormat("for (auto v : in[1]) {\n}"); - verifyFormat("for (int i = 0; i < in[a]; ++i) {\n}"); - verifyFormat("for (int i = 0; in[a] < i; ++i) {\n}"); - verifyFormat("for (int i = 0; i < n; ++i, ++in[a]) {\n}"); - verifyFormat("for (int i = 0; i < n; ++i, in[a]++) {\n}"); - verifyFormat("for (int i = 0; i < f(in[a]); ++i, in[a]++) {\n}"); - verifyFormat("for (id foo in [self getStuffFor:bla]) {\n" - "}"); - verifyFormat("[self aaaaa:MACRO(a, b:, c:)];"); - verifyFormat("[self aaaaa:(1 + 2) bbbbb:3];"); - verifyFormat("[self aaaaa:(Type)a bbbbb:3];"); - - verifyFormat("[self stuffWithInt:(4 + 2) float:4.5];"); - verifyFormat("[self stuffWithInt:a ? b : c float:4.5];"); - verifyFormat("[self stuffWithInt:a ? [self foo:bar] : c];"); - verifyFormat("[self stuffWithInt:a ? (e ? f : g) : c];"); - verifyFormat("[cond ? obj1 : obj2 methodWithParam:param]"); - verifyFormat("[button setAction:@selector(zoomOut:)];"); - verifyFormat("[color getRed:&r green:&g blue:&b alpha:&a];"); - - verifyFormat("arr[[self indexForFoo:a]];"); - verifyFormat("throw [self errorFor:a];"); - verifyFormat("@throw [self errorFor:a];"); - - verifyFormat("[(id)foo bar:(id)baz quux:(id)snorf];"); - verifyFormat("[(id)foo bar:(id) ? baz : quux];"); - verifyFormat("4 > 4 ? (id)a : (id)baz;"); - - // This tests that the formatter doesn't break after "backing" but before ":", - // which would be at 80 columns. - verifyFormat( - "void f() {\n" - " if ((self = [super initWithContentRect:contentRect\n" - " styleMask:styleMask ?: otherMask\n" - " backing:NSBackingStoreBuffered\n" - " defer:YES]))"); - - verifyFormat( - "[foo checkThatBreakingAfterColonWorksOk:\n" - " [bar ifItDoes:reduceOverallLineLengthLikeInThisCase]];"); - - verifyFormat("[myObj short:arg1 // Force line break\n" - " longKeyword:arg2 != nil ? arg2 : @\"longKeyword\"\n" - " evenLongerKeyword:arg3 ?: @\"evenLongerKeyword\"\n" - " error:arg4];"); - verifyFormat( - "void f() {\n" - " popup_window_.reset([[RenderWidgetPopupWindow alloc]\n" - " initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,\n" - " pos.width(), pos.height())\n" - " styleMask:NSBorderlessWindowMask\n" - " backing:NSBackingStoreBuffered\n" - " defer:NO]);\n" - "}"); - verifyFormat( - "void f() {\n" - " popup_wdow_.reset([[RenderWidgetPopupWindow alloc]\n" - " iniithContentRect:NSMakRet(origin_global.x, origin_global.y,\n" - " pos.width(), pos.height())\n" - " syeMask:NSBorderlessWindowMask\n" - " bking:NSBackingStoreBuffered\n" - " der:NO]);\n" - "}", - getLLVMStyleWithColumns(70)); - verifyFormat( - "void f() {\n" - " popup_window_.reset([[RenderWidgetPopupWindow alloc]\n" - " initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,\n" - " pos.width(), pos.height())\n" - " styleMask:NSBorderlessWindowMask\n" - " backing:NSBackingStoreBuffered\n" - " defer:NO]);\n" - "}", - getChromiumStyle(FormatStyle::LK_Cpp)); - verifyFormat("[contentsContainer replaceSubview:[subviews objectAtIndex:0]\n" - " with:contentsNativeView];"); - - verifyFormat( - "[pboard addTypes:[NSArray arrayWithObject:kBookmarkButtonDragType]\n" - " owner:nillllll];"); - - verifyFormat( - "[pboard setData:[NSData dataWithBytes:&button length:sizeof(button)]\n" - " forType:kBookmarkButtonDragType];"); - - verifyFormat("[defaultCenter addObserver:self\n" - " selector:@selector(willEnterFullscreen)\n" - " name:kWillEnterFullscreenNotification\n" - " object:nil];"); - verifyFormat("[image_rep drawInRect:drawRect\n" - " fromRect:NSZeroRect\n" - " operation:NSCompositeCopy\n" - " fraction:1.0\n" - " respectFlipped:NO\n" - " hints:nil];"); - verifyFormat("[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];"); - verifyFormat("[aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaa)\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];"); - verifyFormat("[aaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaa[aaaaaaaaaaaaaaaaaaaaa]\n" - " aaaaaaaaaaaaaaaaaaaaaa];"); - verifyFormat("[call aaaaaaaa.aaaaaa.aaaaaaaa.aaaaaaaa.aaaaaaaa.aaaaaaaa\n" - " .aaaaaaaa];", // FIXME: Indentation seems off. - getLLVMStyleWithColumns(60)); - - verifyFormat( - "scoped_nsobject<NSTextField> message(\n" - " // The frame will be fixed up when |-setMessageText:| is called.\n" - " [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)]);"); - verifyFormat("[self aaaaaa:bbbbbbbbbbbbb\n" - " aaaaaaaaaa:bbbbbbbbbbbbbbbbb\n" - " aaaaa:bbbbbbbbbbb + bbbbbbbbbbbb\n" - " aaaa:bbb];"); - verifyFormat("[self param:function( //\n" - " parameter)]"); - verifyFormat( - "[self aaaaaaaaaa:aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa |\n" - " aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa |\n" - " aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa];"); - - // FIXME: This violates the column limit. - verifyFormat( - "[aaaaaaaaaaaaaaaaaaaaaaaaa\n" - " aaaaaaaaaaaaaaaaa:aaaaaaaa\n" - " aaa:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];", - getLLVMStyleWithColumns(60)); - - // Variadic parameters. - verifyFormat( - "NSArray *myStrings = [NSArray stringarray:@\"a\", @\"b\", nil];"); - verifyFormat( - "[self aaaaaaaaaaaaa:aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa,\n" - " aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa,\n" - " aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa];"); - verifyFormat("[self // break\n" - " a:a\n" - " aaa:aaa];"); - verifyFormat("bool a = ([aaaaaaaa aaaaa] == aaaaaaaaaaaaaaaaa ||\n" - " [aaaaaaaa aaaaa] == aaaaaaaaaaaaaaaaaaaa);"); -} - -TEST_F(FormatTest, ObjCAt) { - verifyFormat("@autoreleasepool"); - verifyFormat("@catch"); - verifyFormat("@class"); - verifyFormat("@compatibility_alias"); - verifyFormat("@defs"); - verifyFormat("@dynamic"); - verifyFormat("@encode"); - verifyFormat("@end"); - verifyFormat("@finally"); - verifyFormat("@implementation"); - verifyFormat("@import"); - verifyFormat("@interface"); - verifyFormat("@optional"); - verifyFormat("@package"); - verifyFormat("@private"); - verifyFormat("@property"); - verifyFormat("@protected"); - verifyFormat("@protocol"); - verifyFormat("@public"); - verifyFormat("@required"); - verifyFormat("@selector"); - verifyFormat("@synchronized"); - verifyFormat("@synthesize"); - verifyFormat("@throw"); - verifyFormat("@try"); - - EXPECT_EQ("@interface", format("@ interface")); - - // The precise formatting of this doesn't matter, nobody writes code like - // this. - verifyFormat("@ /*foo*/ interface"); -} - -TEST_F(FormatTest, ObjCSnippets) { - verifyFormat("@autoreleasepool {\n" - " foo();\n" - "}"); - verifyFormat("@class Foo, Bar;"); - verifyFormat("@compatibility_alias AliasName ExistingClass;"); - verifyFormat("@dynamic textColor;"); - verifyFormat("char *buf1 = @encode(int *);"); - verifyFormat("char *buf1 = @encode(typeof(4 * 5));"); - verifyFormat("char *buf1 = @encode(int **);"); - verifyFormat("Protocol *proto = @protocol(p1);"); - verifyFormat("SEL s = @selector(foo:);"); - verifyFormat("@synchronized(self) {\n" - " f();\n" - "}"); - - verifyFormat("@synthesize dropArrowPosition = dropArrowPosition_;"); - verifyGoogleFormat("@synthesize dropArrowPosition = dropArrowPosition_;"); - - verifyFormat("@property(assign, nonatomic) CGFloat hoverAlpha;"); - verifyFormat("@property(assign, getter=isEditable) BOOL editable;"); - verifyGoogleFormat("@property(assign, getter=isEditable) BOOL editable;"); - verifyFormat("@property (assign, getter=isEditable) BOOL editable;", - getMozillaStyle()); - verifyFormat("@property BOOL editable;", getMozillaStyle()); - verifyFormat("@property (assign, getter=isEditable) BOOL editable;", - getWebKitStyle()); - verifyFormat("@property BOOL editable;", getWebKitStyle()); - - verifyFormat("@import foo.bar;\n" - "@import baz;"); -} - -TEST_F(FormatTest, ObjCForIn) { - verifyFormat("- (void)test {\n" - " for (NSString *n in arrayOfStrings) {\n" - " foo(n);\n" - " }\n" - "}"); - verifyFormat("- (void)test {\n" - " for (NSString *n in (__bridge NSArray *)arrayOfStrings) {\n" - " foo(n);\n" - " }\n" - "}"); -} - -TEST_F(FormatTest, ObjCLiterals) { - verifyFormat("@\"String\""); - verifyFormat("@1"); - verifyFormat("@+4.8"); - verifyFormat("@-4"); - verifyFormat("@1LL"); - verifyFormat("@.5"); - verifyFormat("@'c'"); - verifyFormat("@true"); - - verifyFormat("NSNumber *smallestInt = @(-INT_MAX - 1);"); - verifyFormat("NSNumber *piOverTwo = @(M_PI / 2);"); - verifyFormat("NSNumber *favoriteColor = @(Green);"); - verifyFormat("NSString *path = @(getenv(\"PATH\"));"); - - verifyFormat("[dictionary setObject:@(1) forKey:@\"number\"];"); -} - -TEST_F(FormatTest, ObjCDictLiterals) { - verifyFormat("@{"); - verifyFormat("@{}"); - verifyFormat("@{@\"one\" : @1}"); - verifyFormat("return @{@\"one\" : @1;"); - verifyFormat("@{@\"one\" : @1}"); - - verifyFormat("@{@\"one\" : @{@2 : @1}}"); - verifyFormat("@{\n" - " @\"one\" : @{@2 : @1},\n" - "}"); - - verifyFormat("@{1 > 2 ? @\"one\" : @\"two\" : 1 > 2 ? @1 : @2}"); - verifyIncompleteFormat("[self setDict:@{}"); - verifyIncompleteFormat("[self setDict:@{@1 : @2}"); - verifyFormat("NSLog(@\"%@\", @{@1 : @2, @2 : @3}[@1]);"); - verifyFormat( - "NSDictionary *masses = @{@\"H\" : @1.0078, @\"He\" : @4.0026};"); - verifyFormat( - "NSDictionary *settings = @{AVEncoderKey : @(AVAudioQualityMax)};"); - - verifyFormat("NSDictionary *d = @{\n" - " @\"nam\" : NSUserNam(),\n" - " @\"dte\" : [NSDate date],\n" - " @\"processInfo\" : [NSProcessInfo processInfo]\n" - "};"); - verifyFormat( - "@{\n" - " NSFontAttributeNameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee : " - "regularFont,\n" - "};"); - verifyGoogleFormat( - "@{\n" - " NSFontAttributeNameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee : " - "regularFont,\n" - "};"); - verifyFormat( - "@{\n" - " NSFontAttributeNameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee :\n" - " reeeeeeeeeeeeeeeeeeeeeeeegularFont,\n" - "};"); - - // We should try to be robust in case someone forgets the "@". - verifyFormat("NSDictionary *d = {\n" - " @\"nam\" : NSUserNam(),\n" - " @\"dte\" : [NSDate date],\n" - " @\"processInfo\" : [NSProcessInfo processInfo]\n" - "};"); - verifyFormat("NSMutableDictionary *dictionary =\n" - " [NSMutableDictionary dictionaryWithDictionary:@{\n" - " aaaaaaaaaaaaaaaaaaaaa : aaaaaaaaaaaaa,\n" - " bbbbbbbbbbbbbbbbbb : bbbbb,\n" - " cccccccccccccccc : ccccccccccccccc\n" - " }];"); - - // Ensure that casts before the key are kept on the same line as the key. - verifyFormat( - "NSDictionary *d = @{\n" - " (aaaaaaaa id)aaaaaaaaa : (aaaaaaaa id)aaaaaaaaaaaaaaaaaaaaaaaa,\n" - " (aaaaaaaa id)aaaaaaaaaaaaaa : (aaaaaaaa id)aaaaaaaaaaaaaa,\n" - "};"); -} - -TEST_F(FormatTest, ObjCArrayLiterals) { - verifyIncompleteFormat("@["); - verifyFormat("@[]"); - verifyFormat( - "NSArray *array = @[ @\" Hey \", NSApp, [NSNumber numberWithInt:42] ];"); - verifyFormat("return @[ @3, @[], @[ @4, @5 ] ];"); - verifyFormat("NSArray *array = @[ [foo description] ];"); - - verifyFormat( - "NSArray *some_variable = @[\n" - " aaaa == bbbbbbbbbbb ? @\"aaaaaaaaaaaa\" : @\"aaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - "];"); - verifyFormat( - "NSArray *some_variable = @[\n" - " aaaa == bbbbbbbbbbb ? @\"aaaaaaaaaaaa\" : @\"aaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaa\", @\"aaaaaaaaaaaaaaaa\", @\"aaaaaaaaaaaaaaaa\"\n" - "];"); - verifyFormat("NSArray *some_variable = @[\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - "];"); - verifyFormat("NSArray *array = @[\n" - " @\"a\",\n" - " @\"a\",\n" // Trailing comma -> one per line. - "];"); - - // We should try to be robust in case someone forgets the "@". - verifyFormat("NSArray *some_variable = [\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - "];"); - verifyFormat( - "- (NSAttributedString *)attributedStringForSegment:(NSUInteger)segment\n" - " index:(NSUInteger)index\n" - " nonDigitAttributes:\n" - " (NSDictionary *)noDigitAttributes;"); - verifyFormat("[someFunction someLooooooooooooongParameter:@[\n" - " NSBundle.mainBundle.infoDictionary[@\"a\"]\n" - "]];"); -} TEST_F(FormatTest, BreaksStringLiterals) { EXPECT_EQ("\"some text \"\n" @@ -10184,6 +9609,7 @@ TEST_F(FormatTest, ParsesConfigurationBools) { CHECK_PARSE_BOOL(SpacesInContainerLiterals); CHECK_PARSE_BOOL(SpacesInCStyleCastParentheses); CHECK_PARSE_BOOL(SpaceAfterCStyleCast); + CHECK_PARSE_BOOL(SpaceAfterTemplateKeyword); CHECK_PARSE_BOOL(SpaceBeforeAssignmentOperators); CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterClass); @@ -10920,6 +10346,7 @@ TEST_F(FormatTest, FormatsLambdas) { verifyFormat("int c = [&a, &a, a] { [=, a, b, &c] { return b++; }(); }();\n"); verifyFormat("auto c = {[&a, &a, a] { [=, a, b, &c] { return b++; }(); }}\n"); verifyFormat("auto c = {[&a, &a, a] { [=, a, b, &c] {}(); }}\n"); + verifyFormat("int x = f(*+[] {});"); verifyFormat("void f() {\n" " other(x.begin(), x.end(), [&](int, int) { return 1; });\n" "}\n"); @@ -11147,6 +10574,8 @@ TEST_F(FormatTest, FormatsBlocks) { " }\n" "});"); verifyFormat("Block b = ^int *(A *a, B *b) {}"); + verifyFormat("BOOL (^aaa)(void) = ^BOOL {\n" + "};"); FormatStyle FourIndent = getLLVMStyle(); FourIndent.ObjCBlockIndentWidth = 4; @@ -11322,6 +10751,12 @@ TEST_F(FormatTest, SpacesInAngles) { verifyFormat("A<A<int>>();", Spaces); } +TEST_F(FormatTest, SpaceAfterTemplateKeyword) { + FormatStyle Style = getLLVMStyle(); + Style.SpaceAfterTemplateKeyword = false; + verifyFormat("template<int> void foo();", Style); +} + TEST_F(FormatTest, TripleAngleBrackets) { verifyFormat("f<<<1, 1>>>();"); verifyFormat("f<<<1, 1, 1, s>>>();"); @@ -11332,6 +10767,8 @@ TEST_F(FormatTest, TripleAngleBrackets) { EXPECT_EQ("f<param><<<1, 1>>>();", format("f< param > <<< 1, 1 >>> ();")); verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaa<<<\n 1, 1>>>();"); + verifyFormat("aaaaaaaaaaaaaaa<aaaaaaaaa, aaaaaaaaaa, aaaaaaaaaaaaaa>\n" + " <<<aaaaaaaaa, aaaaaaaaaa, aaaaaaaaaaaaaaaaaa>>>();"); } TEST_F(FormatTest, MergeLessLessAtEnd) { @@ -11483,9 +10920,25 @@ TEST_F(FormatTest, FormatsTableGenCode) { verifyFormat("include \"a.td\"\ninclude \"b.td\"", Style); } -// Since this test case uses UNIX-style file path. We disable it for MS -// compiler. -#if !defined(_MSC_VER) && !defined(__MINGW32__) +TEST_F(FormatTest, ArrayOfTemplates) { + EXPECT_EQ("auto a = new unique_ptr<int>[10];", + format("auto a = new unique_ptr<int > [ 10];")); + + FormatStyle Spaces = getLLVMStyle(); + Spaces.SpacesInSquareBrackets = true; + EXPECT_EQ("auto a = new unique_ptr<int>[ 10 ];", + format("auto a = new unique_ptr<int > [10];", Spaces)); +} + +TEST_F(FormatTest, ArrayAsTemplateType) { + EXPECT_EQ("auto a = unique_ptr<Foo<Bar>[10]>;", + format("auto a = unique_ptr < Foo < Bar>[ 10]> ;")); + + FormatStyle Spaces = getLLVMStyle(); + Spaces.SpacesInSquareBrackets = true; + EXPECT_EQ("auto a = unique_ptr<Foo<Bar>[ 10 ]>;", + format("auto a = unique_ptr < Foo < Bar>[10]> ;", Spaces)); +} TEST(FormatStyle, GetStyleOfFile) { vfs::InMemoryFileSystem FS; @@ -11495,13 +10948,13 @@ TEST(FormatStyle, GetStyleOfFile) { llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: LLVM"))); ASSERT_TRUE( FS.addFile("/a/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;"))); - auto Style1 = getStyle("file", "/a/.clang-format", "Google", &FS); + auto Style1 = getStyle("file", "/a/.clang-format", "Google", "", &FS); ASSERT_EQ(Style1, getLLVMStyle()); // Test 2: fallback to default. ASSERT_TRUE( FS.addFile("/b/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;"))); - auto Style2 = getStyle("file", "/b/test.cpp", "Mozilla", &FS); + auto Style2 = getStyle("file", "/b/test.cpp", "Mozilla", "", &FS); ASSERT_EQ(Style2, getMozillaStyle()); // Test 3: format file in parent directory. @@ -11510,23 +10963,10 @@ TEST(FormatStyle, GetStyleOfFile) { llvm::MemoryBuffer::getMemBuffer("BasedOnStyle: Google"))); ASSERT_TRUE(FS.addFile("/c/sub/sub/sub/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;"))); - auto Style3 = getStyle("file", "/c/sub/sub/sub/test.cpp", "LLVM", &FS); + auto Style3 = getStyle("file", "/c/sub/sub/sub/test.cpp", "LLVM", "", &FS); ASSERT_EQ(Style3, getGoogleStyle()); } -#endif // _MSC_VER - -class ReplacementTest : public ::testing::Test { -protected: - tooling::Replacement createReplacement(SourceLocation Start, unsigned Length, - llvm::StringRef ReplacementText) { - return tooling::Replacement(Context.Sources, Start, Length, - ReplacementText); - } - - RewriterTestContext Context; -}; - TEST_F(ReplacementTest, FormatCodeAfterReplacements) { // Column limit is 20. std::string Code = "Type *a =\n" @@ -11541,15 +10981,15 @@ TEST_F(ReplacementTest, FormatCodeAfterReplacements) { " mm);\n" "int bad = format ;"; FileID ID = Context.createInMemoryFile("format.cpp", Code); - tooling::Replacements Replaces; - Replaces.insert(tooling::Replacement( - Context.Sources, Context.getLocation(ID, 1, 1), 6, "auto ")); - Replaces.insert(tooling::Replacement( - Context.Sources, Context.getLocation(ID, 3, 10), 1, "nullptr")); - Replaces.insert(tooling::Replacement( - Context.Sources, Context.getLocation(ID, 4, 3), 1, "nullptr")); - Replaces.insert(tooling::Replacement( - Context.Sources, Context.getLocation(ID, 4, 13), 1, "nullptr")); + tooling::Replacements Replaces = toReplacements( + {tooling::Replacement(Context.Sources, Context.getLocation(ID, 1, 1), 6, + "auto "), + tooling::Replacement(Context.Sources, Context.getLocation(ID, 3, 10), 1, + "nullptr"), + tooling::Replacement(Context.Sources, Context.getLocation(ID, 4, 3), 1, + "nullptr"), + tooling::Replacement(Context.Sources, Context.getLocation(ID, 4, 13), 1, + "nullptr")}); format::FormatStyle Style = format::getLLVMStyle(); Style.ColumnLimit = 20; // Set column limit to 20 to increase readibility. @@ -11576,9 +11016,9 @@ TEST_F(ReplacementTest, SortIncludesAfterReplacement) { " return 0;\n" "}"; FileID ID = Context.createInMemoryFile("fix.cpp", Code); - tooling::Replacements Replaces; - Replaces.insert(tooling::Replacement( - Context.Sources, Context.getLocation(ID, 1, 1), 0, "#include \"b.h\"\n")); + tooling::Replacements Replaces = toReplacements( + {tooling::Replacement(Context.Sources, Context.getLocation(ID, 1, 1), 0, + "#include \"b.h\"\n")}); format::FormatStyle Style = format::getLLVMStyle(); Style.SortIncludes = true; @@ -11590,6 +11030,17 @@ TEST_F(ReplacementTest, SortIncludesAfterReplacement) { EXPECT_EQ(Expected, *Result); } +TEST_F(FormatTest, AllignTrailingComments) { + EXPECT_EQ("#define MACRO(V) \\\n" + " V(Rt2) /* one more char */ \\\n" + " V(Rs) /* than here */ \\\n" + "/* comment 3 */\n", + format("#define MACRO(V)\\\n" + "V(Rt2) /* one more char */ \\\n" + "V(Rs) /* than here */ \\\n" + "/* comment 3 */ \\\n", + getLLVMStyleWithColumns(40))); +} } // end namespace } // end namespace format } // end namespace clang diff --git a/unittests/Format/FormatTestJS.cpp b/unittests/Format/FormatTestJS.cpp index 2819383a3585..6f494db71d15 100644 --- a/unittests/Format/FormatTestJS.cpp +++ b/unittests/Format/FormatTestJS.cpp @@ -135,6 +135,8 @@ TEST_F(FormatTestJS, ReservedWords) { verifyFormat("x.in() = 1;"); verifyFormat("x.let() = 1;"); verifyFormat("x.var() = 1;"); + verifyFormat("x.for() = 1;"); + verifyFormat("x.as() = 1;"); verifyFormat("x = {\n" " a: 12,\n" " interface: 1,\n" @@ -147,6 +149,21 @@ TEST_F(FormatTestJS, ReservedWords) { verifyFormat("x = interface instanceof y;"); } +TEST_F(FormatTestJS, ReservedWordsMethods) { + verifyFormat( + "class X {\n" + " delete() {\n" + " x();\n" + " }\n" + " interface() {\n" + " x();\n" + " }\n" + " let() {\n" + " x();\n" + " }\n" + "}\n"); +} + TEST_F(FormatTestJS, CppKeywords) { // Make sure we don't mess stuff up because of C++ keywords. verifyFormat("return operator && (aa);"); @@ -161,7 +178,11 @@ TEST_F(FormatTestJS, ES6DestructuringAssignment) { } TEST_F(FormatTestJS, ContainerLiterals) { - verifyFormat("var x = {y: function(a) { return a; }};"); + verifyFormat("var x = {\n" + " y: function(a) {\n" + " return a;\n" + " }\n" + "};"); verifyFormat("return {\n" " link: function() {\n" " f(); //\n" @@ -212,7 +233,11 @@ TEST_F(FormatTestJS, ContainerLiterals) { verifyFormat("x = foo && {a: 123};"); // Arrow functions in object literals. - verifyFormat("var x = {y: (a) => { return a; }};"); + verifyFormat("var x = {\n" + " y: (a) => {\n" + " return a;\n" + " }\n" + "};"); verifyFormat("var x = {y: (a) => a};"); // Computed keys. @@ -234,6 +259,13 @@ TEST_F(FormatTestJS, ContainerLiterals) { " b: b,\n" " 'c': c,\n" "};"); + + // Dict literals can skip the label names. + verifyFormat("var x = {\n" + " aaa,\n" + " aaa,\n" + " aaa,\n" + "};"); } TEST_F(FormatTestJS, MethodsInObjectLiterals) { @@ -324,13 +356,48 @@ TEST_F(FormatTestJS, FormatsNamespaces) { "}\n"); } +TEST_F(FormatTestJS, NamespacesMayNotWrap) { + verifyFormat("declare namespace foobarbaz {\n" + "}\n", getGoogleJSStyleWithColumns(18)); + verifyFormat("declare module foobarbaz {\n" + "}\n", getGoogleJSStyleWithColumns(15)); + verifyFormat("namespace foobarbaz {\n" + "}\n", getGoogleJSStyleWithColumns(10)); + verifyFormat("module foobarbaz {\n" + "}\n", getGoogleJSStyleWithColumns(7)); +} + +TEST_F(FormatTestJS, AmbientDeclarations) { + FormatStyle NineCols = getGoogleJSStyleWithColumns(9); + verifyFormat( + "declare class\n" + " X {}", + NineCols); + verifyFormat( + "declare function\n" + "x();", // TODO(martinprobst): should ideally be indented. + NineCols); + verifyFormat( + "declare enum X {\n" + "}", + NineCols); + verifyFormat( + "declare let\n" + " x: number;", + NineCols); +} + TEST_F(FormatTestJS, FormatsFreestandingFunctions) { verifyFormat("function outer1(a, b) {\n" - " function inner1(a, b) { return a; }\n" + " function inner1(a, b) {\n" + " return a;\n" + " }\n" " inner1(a, b);\n" "}\n" "function outer2(a, b) {\n" - " function inner2(a, b) { return a; }\n" + " function inner2(a, b) {\n" + " return a;\n" + " }\n" " inner2(a, b);\n" "}"); verifyFormat("function f() {}"); @@ -341,6 +408,8 @@ TEST_F(FormatTestJS, GeneratorFunctions) { " let x = 1;\n" " yield x;\n" " yield* something();\n" + " yield [1, 2];\n" + " yield {a: 1};\n" "}"); verifyFormat("function*\n" " f() {\n" @@ -350,8 +419,15 @@ TEST_F(FormatTestJS, GeneratorFunctions) { " yield 1;\n" "}\n"); verifyFormat("class X {\n" - " * generatorMethod() { yield x; }\n" + " * generatorMethod() {\n" + " yield x;\n" + " }\n" "}"); + verifyFormat("var x = {\n" + " a: function*() {\n" + " //\n" + " }\n" + "}\n"); } TEST_F(FormatTestJS, AsyncFunctions) { @@ -366,7 +442,9 @@ TEST_F(FormatTestJS, AsyncFunctions) { " return fetch(x);\n" "}"); verifyFormat("class X {\n" - " async asyncMethod() { return fetch(1); }\n" + " async asyncMethod() {\n" + " return fetch(1);\n" + " }\n" "}"); verifyFormat("function initialize() {\n" " // Comment.\n" @@ -423,8 +501,10 @@ TEST_F(FormatTestJS, ColumnLayoutForArrayLiterals) { } TEST_F(FormatTestJS, FunctionLiterals) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_JavaScript); + Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Inline; verifyFormat("doFoo(function() {});"); - verifyFormat("doFoo(function() { return 1; });"); + verifyFormat("doFoo(function() { return 1; });", Style); verifyFormat("var func = function() {\n" " return 1;\n" "};"); @@ -438,7 +518,8 @@ TEST_F(FormatTestJS, FunctionLiterals) { " getAttribute: function(key) { return this[key]; },\n" " style: {direction: ''}\n" " }\n" - "};"); + "};", + Style); verifyFormat("abc = xyz ? function() {\n" " return 1;\n" "} : function() {\n" @@ -476,13 +557,6 @@ TEST_F(FormatTestJS, FunctionLiterals) { " // code\n" " });"); - verifyFormat("f({a: function() { return 1; }});", - getGoogleJSStyleWithColumns(33)); - verifyFormat("f({\n" - " a: function() { return 1; }\n" - "});", - getGoogleJSStyleWithColumns(32)); - verifyFormat("return {\n" " a: function SomeFunction() {\n" " // ...\n" @@ -510,6 +584,15 @@ TEST_F(FormatTestJS, FunctionLiterals) { " .doSomethingElse(\n" " // break\n" " );"); + + Style.ColumnLimit = 33; + verifyFormat("f({a: function() { return 1; }});", Style); + Style.ColumnLimit = 32; + verifyFormat("f({\n" + " a: function() { return 1; }\n" + "});", + Style); + } TEST_F(FormatTestJS, InliningFunctionLiterals) { @@ -570,6 +653,8 @@ TEST_F(FormatTestJS, InliningFunctionLiterals) { } TEST_F(FormatTestJS, MultipleFunctionLiterals) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_JavaScript); + Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All; verifyFormat("promise.then(\n" " function success() {\n" " doFoo();\n" @@ -606,7 +691,8 @@ TEST_F(FormatTestJS, MultipleFunctionLiterals) { " .thenCatch(function(error) {\n" " body();\n" " body();\n" - " });"); + " });", + Style); verifyFormat("getSomeLongPromise()\n" " .then(function(value) {\n" " body();\n" @@ -619,7 +705,8 @@ TEST_F(FormatTestJS, MultipleFunctionLiterals) { verifyFormat("getSomeLongPromise()\n" " .then(function(value) { body(); })\n" - " .thenCatch(function(error) { body(); });"); + " .thenCatch(function(error) { body(); });", + Style); verifyFormat("return [aaaaaaaaaaaaaaaaaaaaaa]\n" " .aaaaaaa(function() {\n" @@ -633,7 +720,9 @@ TEST_F(FormatTestJS, ArrowFunctions) { " return a;\n" "};"); verifyFormat("var x = (a) => {\n" - " function y() { return 42; }\n" + " function y() {\n" + " return 42;\n" + " }\n" " return a;\n" "};"); verifyFormat("var x = (a: type): {some: type} => {\n" @@ -686,7 +775,9 @@ TEST_F(FormatTestJS, WrapRespectsAutomaticSemicolonInsertion) { // would change due to automatic semicolon insertion. // See http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1. verifyFormat("return aaaaa;", getGoogleJSStyleWithColumns(10)); + verifyFormat("return /* hello! */ aaaaa;", getGoogleJSStyleWithColumns(10)); verifyFormat("continue aaaaa;", getGoogleJSStyleWithColumns(10)); + verifyFormat("continue /* hello! */ aaaaa;", getGoogleJSStyleWithColumns(10)); verifyFormat("break aaaaa;", getGoogleJSStyleWithColumns(10)); verifyFormat("throw aaaaa;", getGoogleJSStyleWithColumns(10)); verifyFormat("aaaaaaaaa++;", getGoogleJSStyleWithColumns(10)); @@ -745,6 +836,18 @@ TEST_F(FormatTestJS, AutomaticSemicolonInsertionHeuristic) { "String"); verifyFormat("function f(@Foo bar) {}", "function f(@Foo\n" " bar) {}"); + verifyFormat("a = true\n" + "return 1", + "a = true\n" + " return 1"); + verifyFormat("a = 's'\n" + "return 1", + "a = 's'\n" + " return 1"); + verifyFormat("a = null\n" + "return 1", + "a = null\n" + " return 1"); } TEST_F(FormatTestJS, ClosureStyleCasts) { @@ -899,8 +1002,16 @@ TEST_F(FormatTestJS, TypeAnnotations) { verifyFormat("((a: string, b: number): string => a + b);"); verifyFormat("var x: (y: number) => string;"); verifyFormat("var x: P<string, (a: number) => string>;"); - verifyFormat("var x = {y: function(): z { return 1; }};"); - verifyFormat("var x = {y: function(): {a: number} { return 1; }};"); + verifyFormat("var x = {\n" + " y: function(): z {\n" + " return 1;\n" + " }\n" + "};"); + verifyFormat("var x = {\n" + " y: function(): {a: number} {\n" + " return 1;\n" + " }\n" + "};"); verifyFormat("function someFunc(args: string[]):\n" " {longReturnValue: string[]} {}", getGoogleJSStyleWithColumns(60)); @@ -928,7 +1039,7 @@ TEST_F(FormatTestJS, ClassDeclarations) { verifyFormat("class C {\n ['x' + 2]: string = 12;\n}"); verifyFormat("class C {\n private x: string = 12;\n}"); verifyFormat("class C {\n private static x: string = 12;\n}"); - verifyFormat("class C {\n static x(): string { return 'asd'; }\n}"); + verifyFormat("class C {\n static x(): string {\n return 'asd';\n }\n}"); verifyFormat("class C extends P implements I {}"); verifyFormat("class C extends p.P implements i.I {}"); verifyFormat("class Test {\n" @@ -1091,7 +1202,9 @@ TEST_F(FormatTestJS, Modules) { verifyFormat("export default () => {};"); verifyFormat("export interface Foo { foo: number; }\n" "export class Bar {\n" - " blah(): string { return this.blah; };\n" + " blah(): string {\n" + " return this.blah;\n" + " };\n" "}"); } @@ -1122,7 +1235,7 @@ TEST_F(FormatTestJS, ImportWrapping) { TEST_F(FormatTestJS, TemplateStrings) { // Keeps any whitespace/indentation within the template string. verifyFormat("var x = `hello\n" - " ${ name }\n" + " ${name}\n" " !`;", "var x = `hello\n" " ${ name }\n" @@ -1206,6 +1319,27 @@ TEST_F(FormatTestJS, TemplateStrings) { "var y;", "var x = ` \\` a`;\n" "var y;"); + // Escaped dollar. + verifyFormat("var x = ` \\${foo}`;\n"); +} + +TEST_F(FormatTestJS, TemplateStringASI) { + verifyFormat("var x = `hello${world}`;", "var x = `hello${\n" + " world\n" + "}`;"); +} + +TEST_F(FormatTestJS, NestedTemplateStrings) { + verifyFormat( + "var x = `<ul>${xs.map(x => `<li>${x}</li>`).join('\\n')}</ul>`;"); + verifyFormat("var x = `he${({text: 'll'}.text)}o`;"); + + // Crashed at some point. + verifyFormat("}"); +} + +TEST_F(FormatTestJS, TaggedTemplateStrings) { + verifyFormat("var x = html`<ul>`;"); } TEST_F(FormatTestJS, CastSyntax) { @@ -1218,6 +1352,11 @@ TEST_F(FormatTestJS, CastSyntax) { " 1, //\n" " 2\n" "];"); + verifyFormat("var x = [{x: 1} as type];"); + verifyFormat("x = x as [a, b];"); + verifyFormat("x = x as {a: string};"); + verifyFormat("x = x as (string);"); + verifyFormat("x = x! as (string);"); } TEST_F(FormatTestJS, TypeArguments) { @@ -1318,6 +1457,21 @@ TEST_F(FormatTestJS, RequoteStringsSingle) { "let x = \"single\";\n"); } +TEST_F(FormatTestJS, RequoteAndIndent) { + verifyFormat("let x = someVeryLongFunctionThatGoesOnAndOn(\n" + " 'double quoted string that needs wrapping');", + "let x = someVeryLongFunctionThatGoesOnAndOn(" + "\"double quoted string that needs wrapping\");"); + + verifyFormat("let x =\n" + " 'foo\\'oo';\n" + "let x =\n" + " 'foo\\'oo';", + "let x=\"foo'oo\";\n" + "let x=\"foo'oo\";", + getGoogleJSStyleWithColumns(15)); +} + TEST_F(FormatTestJS, RequoteStringsDouble) { FormatStyle DoubleQuotes = getGoogleStyle(FormatStyle::LK_JavaScript); DoubleQuotes.JavaScriptQuotes = FormatStyle::JSQS_Double; @@ -1346,6 +1500,7 @@ TEST_F(FormatTestJS, NonNullAssertionOperator) { verifyFormat("let x = !foo;\n"); verifyFormat("let x = foo[0]!;\n"); verifyFormat("let x = (foo)!;\n"); + verifyFormat("let x = foo! - 1;\n"); verifyFormat("let x = {foo: 1}!;\n"); } @@ -1358,5 +1513,11 @@ TEST_F(FormatTestJS, Conditional) { "}"); } +TEST_F(FormatTestJS, ImportComments) { + verifyFormat("import {x} from 'x'; // from some location", + getGoogleJSStyleWithColumns(25)); + verifyFormat("// taze: x from 'location'", getGoogleJSStyleWithColumns(10)); +} + } // end namespace tooling } // end namespace clang diff --git a/unittests/Format/FormatTestObjC.cpp b/unittests/Format/FormatTestObjC.cpp new file mode 100644 index 000000000000..6a530f921e6d --- /dev/null +++ b/unittests/Format/FormatTestObjC.cpp @@ -0,0 +1,822 @@ +//===- unittest/Format/FormatTestObjC.cpp - Formatting unit tests----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Format/Format.h" + +#include "../Tooling/ReplacementTest.h" +#include "FormatTestUtils.h" + +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/MemoryBuffer.h" +#include "gtest/gtest.h" + +#define DEBUG_TYPE "format-test" + +using clang::tooling::ReplacementTest; +using clang::tooling::toReplacements; + +namespace clang { +namespace format { +namespace { + +class FormatTestObjC : public ::testing::Test { +protected: + FormatTestObjC() { + Style = getLLVMStyle(); + Style.Language = FormatStyle::LK_ObjC; + } + + enum IncompleteCheck { + IC_ExpectComplete, + IC_ExpectIncomplete, + IC_DoNotCheck + }; + + std::string format(llvm::StringRef Code, + IncompleteCheck CheckIncomplete = IC_ExpectComplete) { + DEBUG(llvm::errs() << "---\n"); + DEBUG(llvm::errs() << Code << "\n\n"); + std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size())); + bool IncompleteFormat = false; + tooling::Replacements Replaces = + reformat(Style, Code, Ranges, "<stdin>", &IncompleteFormat); + if (CheckIncomplete != IC_DoNotCheck) { + bool ExpectedIncompleteFormat = CheckIncomplete == IC_ExpectIncomplete; + EXPECT_EQ(ExpectedIncompleteFormat, IncompleteFormat) << Code << "\n\n"; + } + auto Result = applyAllReplacements(Code, Replaces); + EXPECT_TRUE(static_cast<bool>(Result)); + DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); + return *Result; + } + + void verifyFormat(StringRef Code) { + EXPECT_EQ(Code.str(), format(test::messUp(Code))); + } + + void verifyIncompleteFormat(StringRef Code) { + EXPECT_EQ(Code.str(), format(test::messUp(Code), IC_ExpectIncomplete)); + } + + FormatStyle Style; +}; + +TEST_F(FormatTestObjC, DetectsObjCInHeaders) { + Style = getStyle("LLVM", "a.h", "none", "@interface\n" + "- (id)init;"); + EXPECT_EQ(FormatStyle::LK_ObjC, Style.Language); + Style = getStyle("LLVM", "a.h", "none", "@interface\n" + "+ (id)init;"); + EXPECT_EQ(FormatStyle::LK_ObjC, Style.Language); + + // No recognizable ObjC. + Style = getStyle("LLVM", "a.h", "none", "void f() {}"); + EXPECT_EQ(FormatStyle::LK_Cpp, Style.Language); +} + +TEST_F(FormatTestObjC, FormatObjCTryCatch) { + verifyFormat("@try {\n" + " f();\n" + "} @catch (NSException e) {\n" + " @throw;\n" + "} @finally {\n" + " exit(42);\n" + "}"); + verifyFormat("DEBUG({\n" + " @try {\n" + " } @finally {\n" + " }\n" + "});\n"); +} + +TEST_F(FormatTestObjC, FormatObjCAutoreleasepool) { + verifyFormat("@autoreleasepool {\n" + " f();\n" + "}\n" + "@autoreleasepool {\n" + " f();\n" + "}\n"); + Style.BreakBeforeBraces = FormatStyle::BS_Allman; + verifyFormat("@autoreleasepool\n" + "{\n" + " f();\n" + "}\n" + "@autoreleasepool\n" + "{\n" + " f();\n" + "}\n"); +} + +TEST_F(FormatTestObjC, FormatObjCInterface) { + verifyFormat("@interface Foo : NSObject <NSSomeDelegate> {\n" + "@public\n" + " int field1;\n" + "@protected\n" + " int field2;\n" + "@private\n" + " int field3;\n" + "@package\n" + " int field4;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface /* wait for it */ Foo\n" + "+ (id)init;\n" + "// Look, a comment!\n" + "- (int)answerWith:(int)i;\n" + "@end"); + + verifyFormat("@interface Foo\n" + "@end\n" + "@interface Bar\n" + "@end"); + + verifyFormat("@interface Foo : Bar\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo : /**/ Bar /**/ <Baz, /**/ Quux>\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo (HackStuff)\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo ()\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo (HackStuff) <MyProtocol>\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo : Bar {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo : Bar <Baz, Quux> {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo (HackStuff) {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo () {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo (HackStuff) <MyProtocol> {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + Style = getGoogleStyle(FormatStyle::LK_ObjC); + verifyFormat("@interface Foo : NSObject<NSSomeDelegate> {\n" + " @public\n" + " int field1;\n" + " @protected\n" + " int field2;\n" + " @private\n" + " int field3;\n" + " @package\n" + " int field4;\n" + "}\n" + "+ (id)init;\n" + "@end"); + verifyFormat("@interface Foo : Bar<Baz, Quux>\n" + "+ (id)init;\n" + "@end"); + verifyFormat("@interface Foo (HackStuff)<MyProtocol>\n" + "+ (id)init;\n" + "@end"); + Style.BinPackParameters = false; + Style.ColumnLimit = 80; + verifyFormat("@interface aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ()<\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa> {\n" + "}"); +} + +TEST_F(FormatTestObjC, FormatObjCImplementation) { + verifyFormat("@implementation Foo : NSObject {\n" + "@public\n" + " int field1;\n" + "@protected\n" + " int field2;\n" + "@private\n" + " int field3;\n" + "@package\n" + " int field4;\n" + "}\n" + "+ (id)init {\n}\n" + "@end"); + + verifyFormat("@implementation Foo\n" + "+ (id)init {\n" + " if (true)\n" + " return nil;\n" + "}\n" + "// Look, a comment!\n" + "- (int)answerWith:(int)i {\n" + " return i;\n" + "}\n" + "+ (int)answerWith:(int)i {\n" + " return i;\n" + "}\n" + "@end"); + + verifyFormat("@implementation Foo\n" + "@end\n" + "@implementation Bar\n" + "@end"); + + EXPECT_EQ("@implementation Foo : Bar\n" + "+ (id)init {\n}\n" + "- (void)foo {\n}\n" + "@end", + format("@implementation Foo : Bar\n" + "+(id)init{}\n" + "-(void)foo{}\n" + "@end")); + + verifyFormat("@implementation Foo {\n" + " int _i;\n" + "}\n" + "+ (id)init {\n}\n" + "@end"); + + verifyFormat("@implementation Foo : Bar {\n" + " int _i;\n" + "}\n" + "+ (id)init {\n}\n" + "@end"); + + verifyFormat("@implementation Foo (HackStuff)\n" + "+ (id)init {\n}\n" + "@end"); + verifyFormat("@implementation ObjcClass\n" + "- (void)method;\n" + "{}\n" + "@end"); + + Style = getGoogleStyle(FormatStyle::LK_ObjC); + verifyFormat("@implementation Foo : NSObject {\n" + " @public\n" + " int field1;\n" + " @protected\n" + " int field2;\n" + " @private\n" + " int field3;\n" + " @package\n" + " int field4;\n" + "}\n" + "+ (id)init {\n}\n" + "@end"); +} + +TEST_F(FormatTestObjC, FormatObjCProtocol) { + verifyFormat("@protocol Foo\n" + "@property(weak) id delegate;\n" + "- (NSUInteger)numberOfThings;\n" + "@end"); + + verifyFormat("@protocol MyProtocol <NSObject>\n" + "- (NSUInteger)numberOfThings;\n" + "@end"); + + verifyFormat("@protocol Foo;\n" + "@protocol Bar;\n"); + + verifyFormat("@protocol Foo\n" + "@end\n" + "@protocol Bar\n" + "@end"); + + verifyFormat("@protocol myProtocol\n" + "- (void)mandatoryWithInt:(int)i;\n" + "@optional\n" + "- (void)optional;\n" + "@required\n" + "- (void)required;\n" + "@optional\n" + "@property(assign) int madProp;\n" + "@end\n"); + + verifyFormat("@property(nonatomic, assign, readonly)\n" + " int *looooooooooooooooooooooooooooongNumber;\n" + "@property(nonatomic, assign, readonly)\n" + " NSString *looooooooooooooooooooooooooooongName;"); + + verifyFormat("@implementation PR18406\n" + "}\n" + "@end"); + + Style = getGoogleStyle(FormatStyle::LK_ObjC); + verifyFormat("@protocol MyProtocol<NSObject>\n" + "- (NSUInteger)numberOfThings;\n" + "@end"); +} + +TEST_F(FormatTestObjC, FormatObjCMethodDeclarations) { + verifyFormat("- (void)doSomethingWith:(GTMFoo *)theFoo\n" + " rect:(NSRect)theRect\n" + " interval:(float)theInterval {\n" + "}"); + verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n" + " longKeyword:(NSRect)theRect\n" + " longerKeyword:(float)theInterval\n" + " error:(NSError **)theError {\n" + "}"); + verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n" + " longKeyword:(NSRect)theRect\n" + " evenLongerKeyword:(float)theInterval\n" + " error:(NSError **)theError {\n" + "}"); + Style.ColumnLimit = 60; + verifyFormat("- (instancetype)initXxxxxx:(id<x>)x\n" + " y:(id<yyyyyyyyyyyyyyyyyyyy>)y\n" + " NS_DESIGNATED_INITIALIZER;"); + verifyFormat("- (void)drawRectOn:(id)surface\n" + " ofSize:(size_t)height\n" + " :(size_t)width;"); + + // Continuation indent width should win over aligning colons if the function + // name is long. + Style = getGoogleStyle(FormatStyle::LK_ObjC); + Style.ColumnLimit = 40; + Style.IndentWrappedFunctionNames = true; + verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n" + " dontAlignNamef:(NSRect)theRect {\n" + "}"); + + // Make sure we don't break aligning for short parameter names. + verifyFormat("- (void)shortf:(GTMFoo *)theFoo\n" + " aShortf:(NSRect)theRect {\n" + "}"); + + // Format pairs correctly. + Style.ColumnLimit = 80; + verifyFormat("- (void)drawRectOn:(id)surface\n" + " ofSize:(aaaaaaaa)height\n" + " :(size_t)width\n" + " atOrigin:(size_t)x\n" + " :(size_t)y\n" + " aaaaa:(a)yyy\n" + " bbb:(d)cccc;"); + verifyFormat("- (void)drawRectOn:(id)surface ofSize:(aaa)height:(bbb)width;"); +} + +TEST_F(FormatTestObjC, FormatObjCMethodExpr) { + verifyFormat("[foo bar:baz];"); + verifyFormat("return [foo bar:baz];"); + verifyFormat("return (a)[foo bar:baz];"); + verifyFormat("f([foo bar:baz]);"); + verifyFormat("f(2, [foo bar:baz]);"); + verifyFormat("f(2, a ? b : c);"); + verifyFormat("[[self initWithInt:4] bar:[baz quux:arrrr]];"); + + // Unary operators. + verifyFormat("int a = +[foo bar:baz];"); + verifyFormat("int a = -[foo bar:baz];"); + verifyFormat("int a = ![foo bar:baz];"); + verifyFormat("int a = ~[foo bar:baz];"); + verifyFormat("int a = ++[foo bar:baz];"); + verifyFormat("int a = --[foo bar:baz];"); + verifyFormat("int a = sizeof [foo bar:baz];"); + verifyFormat("int a = alignof [foo bar:baz];"); + verifyFormat("int a = &[foo bar:baz];"); + verifyFormat("int a = *[foo bar:baz];"); + // FIXME: Make casts work, without breaking f()[4]. + // verifyFormat("int a = (int)[foo bar:baz];"); + // verifyFormat("return (int)[foo bar:baz];"); + // verifyFormat("(void)[foo bar:baz];"); + verifyFormat("return (MyType *)[self.tableView cellForRowAtIndexPath:cell];"); + + // Binary operators. + verifyFormat("[foo bar:baz], [foo bar:baz];"); + verifyFormat("[foo bar:baz] = [foo bar:baz];"); + verifyFormat("[foo bar:baz] *= [foo bar:baz];"); + verifyFormat("[foo bar:baz] /= [foo bar:baz];"); + verifyFormat("[foo bar:baz] %= [foo bar:baz];"); + verifyFormat("[foo bar:baz] += [foo bar:baz];"); + verifyFormat("[foo bar:baz] -= [foo bar:baz];"); + verifyFormat("[foo bar:baz] <<= [foo bar:baz];"); + verifyFormat("[foo bar:baz] >>= [foo bar:baz];"); + verifyFormat("[foo bar:baz] &= [foo bar:baz];"); + verifyFormat("[foo bar:baz] ^= [foo bar:baz];"); + verifyFormat("[foo bar:baz] |= [foo bar:baz];"); + verifyFormat("[foo bar:baz] ? [foo bar:baz] : [foo bar:baz];"); + verifyFormat("[foo bar:baz] || [foo bar:baz];"); + verifyFormat("[foo bar:baz] && [foo bar:baz];"); + verifyFormat("[foo bar:baz] | [foo bar:baz];"); + verifyFormat("[foo bar:baz] ^ [foo bar:baz];"); + verifyFormat("[foo bar:baz] & [foo bar:baz];"); + verifyFormat("[foo bar:baz] == [foo bar:baz];"); + verifyFormat("[foo bar:baz] != [foo bar:baz];"); + verifyFormat("[foo bar:baz] >= [foo bar:baz];"); + verifyFormat("[foo bar:baz] <= [foo bar:baz];"); + verifyFormat("[foo bar:baz] > [foo bar:baz];"); + verifyFormat("[foo bar:baz] < [foo bar:baz];"); + verifyFormat("[foo bar:baz] >> [foo bar:baz];"); + verifyFormat("[foo bar:baz] << [foo bar:baz];"); + verifyFormat("[foo bar:baz] - [foo bar:baz];"); + verifyFormat("[foo bar:baz] + [foo bar:baz];"); + verifyFormat("[foo bar:baz] * [foo bar:baz];"); + verifyFormat("[foo bar:baz] / [foo bar:baz];"); + verifyFormat("[foo bar:baz] % [foo bar:baz];"); + // Whew! + + verifyFormat("return in[42];"); + verifyFormat("for (auto v : in[1]) {\n}"); + verifyFormat("for (int i = 0; i < in[a]; ++i) {\n}"); + verifyFormat("for (int i = 0; in[a] < i; ++i) {\n}"); + verifyFormat("for (int i = 0; i < n; ++i, ++in[a]) {\n}"); + verifyFormat("for (int i = 0; i < n; ++i, in[a]++) {\n}"); + verifyFormat("for (int i = 0; i < f(in[a]); ++i, in[a]++) {\n}"); + verifyFormat("for (id foo in [self getStuffFor:bla]) {\n" + "}"); + verifyFormat("[self aaaaa:MACRO(a, b:, c:)];"); + verifyFormat("[self aaaaa:(1 + 2) bbbbb:3];"); + verifyFormat("[self aaaaa:(Type)a bbbbb:3];"); + + verifyFormat("[self stuffWithInt:(4 + 2) float:4.5];"); + verifyFormat("[self stuffWithInt:a ? b : c float:4.5];"); + verifyFormat("[self stuffWithInt:a ? [self foo:bar] : c];"); + verifyFormat("[self stuffWithInt:a ? (e ? f : g) : c];"); + verifyFormat("[cond ? obj1 : obj2 methodWithParam:param]"); + verifyFormat("[button setAction:@selector(zoomOut:)];"); + verifyFormat("[color getRed:&r green:&g blue:&b alpha:&a];"); + + verifyFormat("arr[[self indexForFoo:a]];"); + verifyFormat("throw [self errorFor:a];"); + verifyFormat("@throw [self errorFor:a];"); + + verifyFormat("[(id)foo bar:(id)baz quux:(id)snorf];"); + verifyFormat("[(id)foo bar:(id) ? baz : quux];"); + verifyFormat("4 > 4 ? (id)a : (id)baz;"); + + // This tests that the formatter doesn't break after "backing" but before ":", + // which would be at 80 columns. + verifyFormat( + "void f() {\n" + " if ((self = [super initWithContentRect:contentRect\n" + " styleMask:styleMask ?: otherMask\n" + " backing:NSBackingStoreBuffered\n" + " defer:YES]))"); + + verifyFormat( + "[foo checkThatBreakingAfterColonWorksOk:\n" + " [bar ifItDoes:reduceOverallLineLengthLikeInThisCase]];"); + + verifyFormat("[myObj short:arg1 // Force line break\n" + " longKeyword:arg2 != nil ? arg2 : @\"longKeyword\"\n" + " evenLongerKeyword:arg3 ?: @\"evenLongerKeyword\"\n" + " error:arg4];"); + verifyFormat( + "void f() {\n" + " popup_window_.reset([[RenderWidgetPopupWindow alloc]\n" + " initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,\n" + " pos.width(), pos.height())\n" + " styleMask:NSBorderlessWindowMask\n" + " backing:NSBackingStoreBuffered\n" + " defer:NO]);\n" + "}"); + verifyFormat("[contentsContainer replaceSubview:[subviews objectAtIndex:0]\n" + " with:contentsNativeView];"); + + verifyFormat( + "[pboard addTypes:[NSArray arrayWithObject:kBookmarkButtonDragType]\n" + " owner:nillllll];"); + + verifyFormat( + "[pboard setData:[NSData dataWithBytes:&button length:sizeof(button)]\n" + " forType:kBookmarkButtonDragType];"); + + verifyFormat("[defaultCenter addObserver:self\n" + " selector:@selector(willEnterFullscreen)\n" + " name:kWillEnterFullscreenNotification\n" + " object:nil];"); + verifyFormat("[image_rep drawInRect:drawRect\n" + " fromRect:NSZeroRect\n" + " operation:NSCompositeCopy\n" + " fraction:1.0\n" + " respectFlipped:NO\n" + " hints:nil];"); + verifyFormat("[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];"); + verifyFormat("[aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaa)\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];"); + verifyFormat("[aaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaa[aaaaaaaaaaaaaaaaaaaaa]\n" + " aaaaaaaaaaaaaaaaaaaaaa];"); + + verifyFormat( + "scoped_nsobject<NSTextField> message(\n" + " // The frame will be fixed up when |-setMessageText:| is called.\n" + " [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)]);"); + verifyFormat("[self aaaaaa:bbbbbbbbbbbbb\n" + " aaaaaaaaaa:bbbbbbbbbbbbbbbbb\n" + " aaaaa:bbbbbbbbbbb + bbbbbbbbbbbb\n" + " aaaa:bbb];"); + verifyFormat("[self param:function( //\n" + " parameter)]"); + verifyFormat( + "[self aaaaaaaaaa:aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa |\n" + " aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa |\n" + " aaaaaaaaaaaaaaa | aaaaaaaaaaaaaaa];"); + + // Variadic parameters. + verifyFormat( + "NSArray *myStrings = [NSArray stringarray:@\"a\", @\"b\", nil];"); + verifyFormat( + "[self aaaaaaaaaaaaa:aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa,\n" + " aaaaaaaaaaaaaaa, aaaaaaaaaaaaaaa];"); + verifyFormat("[self // break\n" + " a:a\n" + " aaa:aaa];"); + verifyFormat("bool a = ([aaaaaaaa aaaaa] == aaaaaaaaaaaaaaaaa ||\n" + " [aaaaaaaa aaaaa] == aaaaaaaaaaaaaaaaaaaa);"); + + // Formats pair-parameters. + verifyFormat("[I drawRectOn:surface ofSize:aa:bbb atOrigin:cc:dd];"); + verifyFormat("[I drawRectOn:surface //\n" + " ofSize:aa:bbb\n" + " atOrigin:cc:dd];"); + + Style.ColumnLimit = 70; + verifyFormat( + "void f() {\n" + " popup_wdow_.reset([[RenderWidgetPopupWindow alloc]\n" + " iniithContentRect:NSMakRet(origin_global.x, origin_global.y,\n" + " pos.width(), pos.height())\n" + " syeMask:NSBorderlessWindowMask\n" + " bking:NSBackingStoreBuffered\n" + " der:NO]);\n" + "}"); + + Style.ColumnLimit = 60; + verifyFormat("[call aaaaaaaa.aaaaaa.aaaaaaaa.aaaaaaaa.aaaaaaaa.aaaaaaaa\n" + " .aaaaaaaa];"); // FIXME: Indentation seems off. + // FIXME: This violates the column limit. + verifyFormat( + "[aaaaaaaaaaaaaaaaaaaaaaaaa\n" + " aaaaaaaaaaaaaaaaa:aaaaaaaa\n" + " aaa:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa];"); + + Style = getChromiumStyle(FormatStyle::LK_ObjC); + Style.ColumnLimit = 80; + verifyFormat( + "void f() {\n" + " popup_window_.reset([[RenderWidgetPopupWindow alloc]\n" + " initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,\n" + " pos.width(), pos.height())\n" + " styleMask:NSBorderlessWindowMask\n" + " backing:NSBackingStoreBuffered\n" + " defer:NO]);\n" + "}"); +} + +TEST_F(FormatTestObjC, ObjCAt) { + verifyFormat("@autoreleasepool"); + verifyFormat("@catch"); + verifyFormat("@class"); + verifyFormat("@compatibility_alias"); + verifyFormat("@defs"); + verifyFormat("@dynamic"); + verifyFormat("@encode"); + verifyFormat("@end"); + verifyFormat("@finally"); + verifyFormat("@implementation"); + verifyFormat("@import"); + verifyFormat("@interface"); + verifyFormat("@optional"); + verifyFormat("@package"); + verifyFormat("@private"); + verifyFormat("@property"); + verifyFormat("@protected"); + verifyFormat("@protocol"); + verifyFormat("@public"); + verifyFormat("@required"); + verifyFormat("@selector"); + verifyFormat("@synchronized"); + verifyFormat("@synthesize"); + verifyFormat("@throw"); + verifyFormat("@try"); + + EXPECT_EQ("@interface", format("@ interface")); + + // The precise formatting of this doesn't matter, nobody writes code like + // this. + verifyFormat("@ /*foo*/ interface"); +} + +TEST_F(FormatTestObjC, ObjCSnippets) { + verifyFormat("@autoreleasepool {\n" + " foo();\n" + "}"); + verifyFormat("@class Foo, Bar;"); + verifyFormat("@compatibility_alias AliasName ExistingClass;"); + verifyFormat("@dynamic textColor;"); + verifyFormat("char *buf1 = @encode(int *);"); + verifyFormat("char *buf1 = @encode(typeof(4 * 5));"); + verifyFormat("char *buf1 = @encode(int **);"); + verifyFormat("Protocol *proto = @protocol(p1);"); + verifyFormat("SEL s = @selector(foo:);"); + verifyFormat("@synchronized(self) {\n" + " f();\n" + "}"); + + verifyFormat("@import foo.bar;\n" + "@import baz;"); + + verifyFormat("@synthesize dropArrowPosition = dropArrowPosition_;"); + + verifyFormat("@property(assign, nonatomic) CGFloat hoverAlpha;"); + verifyFormat("@property(assign, getter=isEditable) BOOL editable;"); + + Style = getMozillaStyle(); + verifyFormat("@property (assign, getter=isEditable) BOOL editable;"); + verifyFormat("@property BOOL editable;"); + + Style = getWebKitStyle(); + verifyFormat("@property (assign, getter=isEditable) BOOL editable;"); + verifyFormat("@property BOOL editable;"); + + Style = getGoogleStyle(FormatStyle::LK_ObjC); + verifyFormat("@synthesize dropArrowPosition = dropArrowPosition_;"); + verifyFormat("@property(assign, getter=isEditable) BOOL editable;"); +} + +TEST_F(FormatTestObjC, ObjCForIn) { + verifyFormat("- (void)test {\n" + " for (NSString *n in arrayOfStrings) {\n" + " foo(n);\n" + " }\n" + "}"); + verifyFormat("- (void)test {\n" + " for (NSString *n in (__bridge NSArray *)arrayOfStrings) {\n" + " foo(n);\n" + " }\n" + "}"); +} + +TEST_F(FormatTestObjC, ObjCLiterals) { + verifyFormat("@\"String\""); + verifyFormat("@1"); + verifyFormat("@+4.8"); + verifyFormat("@-4"); + verifyFormat("@1LL"); + verifyFormat("@.5"); + verifyFormat("@'c'"); + verifyFormat("@true"); + + verifyFormat("NSNumber *smallestInt = @(-INT_MAX - 1);"); + verifyFormat("NSNumber *piOverTwo = @(M_PI / 2);"); + verifyFormat("NSNumber *favoriteColor = @(Green);"); + verifyFormat("NSString *path = @(getenv(\"PATH\"));"); + + verifyFormat("[dictionary setObject:@(1) forKey:@\"number\"];"); +} + +TEST_F(FormatTestObjC, ObjCDictLiterals) { + verifyFormat("@{"); + verifyFormat("@{}"); + verifyFormat("@{@\"one\" : @1}"); + verifyFormat("return @{@\"one\" : @1;"); + verifyFormat("@{@\"one\" : @1}"); + + verifyFormat("@{@\"one\" : @{@2 : @1}}"); + verifyFormat("@{\n" + " @\"one\" : @{@2 : @1},\n" + "}"); + + verifyFormat("@{1 > 2 ? @\"one\" : @\"two\" : 1 > 2 ? @1 : @2}"); + verifyIncompleteFormat("[self setDict:@{}"); + verifyIncompleteFormat("[self setDict:@{@1 : @2}"); + verifyFormat("NSLog(@\"%@\", @{@1 : @2, @2 : @3}[@1]);"); + verifyFormat( + "NSDictionary *masses = @{@\"H\" : @1.0078, @\"He\" : @4.0026};"); + verifyFormat( + "NSDictionary *settings = @{AVEncoderKey : @(AVAudioQualityMax)};"); + + verifyFormat("NSDictionary *d = @{\n" + " @\"nam\" : NSUserNam(),\n" + " @\"dte\" : [NSDate date],\n" + " @\"processInfo\" : [NSProcessInfo processInfo]\n" + "};"); + verifyFormat( + "@{\n" + " NSFontAttributeNameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee : " + "regularFont,\n" + "};"); + verifyFormat( + "@{\n" + " NSFontAttributeNameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee :\n" + " reeeeeeeeeeeeeeeeeeeeeeeegularFont,\n" + "};"); + + // We should try to be robust in case someone forgets the "@". + verifyFormat("NSDictionary *d = {\n" + " @\"nam\" : NSUserNam(),\n" + " @\"dte\" : [NSDate date],\n" + " @\"processInfo\" : [NSProcessInfo processInfo]\n" + "};"); + verifyFormat("NSMutableDictionary *dictionary =\n" + " [NSMutableDictionary dictionaryWithDictionary:@{\n" + " aaaaaaaaaaaaaaaaaaaaa : aaaaaaaaaaaaa,\n" + " bbbbbbbbbbbbbbbbbb : bbbbb,\n" + " cccccccccccccccc : ccccccccccccccc\n" + " }];"); + + // Ensure that casts before the key are kept on the same line as the key. + verifyFormat( + "NSDictionary *d = @{\n" + " (aaaaaaaa id)aaaaaaaaa : (aaaaaaaa id)aaaaaaaaaaaaaaaaaaaaaaaa,\n" + " (aaaaaaaa id)aaaaaaaaaaaaaa : (aaaaaaaa id)aaaaaaaaaaaaaa,\n" + "};"); + + Style = getGoogleStyle(FormatStyle::LK_ObjC); + verifyFormat( + "@{\n" + " NSFontAttributeNameeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee : " + "regularFont,\n" + "};"); +} + +TEST_F(FormatTestObjC, ObjCArrayLiterals) { + verifyIncompleteFormat("@["); + verifyFormat("@[]"); + verifyFormat( + "NSArray *array = @[ @\" Hey \", NSApp, [NSNumber numberWithInt:42] ];"); + verifyFormat("return @[ @3, @[], @[ @4, @5 ] ];"); + verifyFormat("NSArray *array = @[ [foo description] ];"); + + verifyFormat( + "NSArray *some_variable = @[\n" + " aaaa == bbbbbbbbbbb ? @\"aaaaaaaaaaaa\" : @\"aaaaaaaaaaaaaa\",\n" + " @\"aaaaaaaaaaaaaaaaa\",\n" + " @\"aaaaaaaaaaaaaaaaa\",\n" + " @\"aaaaaaaaaaaaaaaaa\",\n" + "];"); + verifyFormat( + "NSArray *some_variable = @[\n" + " aaaa == bbbbbbbbbbb ? @\"aaaaaaaaaaaa\" : @\"aaaaaaaaaaaaaa\",\n" + " @\"aaaaaaaaaaaaaaaa\", @\"aaaaaaaaaaaaaaaa\", @\"aaaaaaaaaaaaaaaa\"\n" + "];"); + verifyFormat("NSArray *some_variable = @[\n" + " @\"aaaaaaaaaaaaaaaaa\",\n" + " @\"aaaaaaaaaaaaaaaaa\",\n" + " @\"aaaaaaaaaaaaaaaaa\",\n" + " @\"aaaaaaaaaaaaaaaaa\",\n" + "];"); + verifyFormat("NSArray *array = @[\n" + " @\"a\",\n" + " @\"a\",\n" // Trailing comma -> one per line. + "];"); + + // We should try to be robust in case someone forgets the "@". + verifyFormat("NSArray *some_variable = [\n" + " @\"aaaaaaaaaaaaaaaaa\",\n" + " @\"aaaaaaaaaaaaaaaaa\",\n" + " @\"aaaaaaaaaaaaaaaaa\",\n" + " @\"aaaaaaaaaaaaaaaaa\",\n" + "];"); + verifyFormat( + "- (NSAttributedString *)attributedStringForSegment:(NSUInteger)segment\n" + " index:(NSUInteger)index\n" + " nonDigitAttributes:\n" + " (NSDictionary *)noDigitAttributes;"); + verifyFormat("[someFunction someLooooooooooooongParameter:@[\n" + " NSBundle.mainBundle.infoDictionary[@\"a\"]\n" + "]];"); +} +} // end namespace +} // end namespace format +} // end namespace clang diff --git a/unittests/Format/SortImportsTestJS.cpp b/unittests/Format/SortImportsTestJS.cpp index 77c37e337ddf..7e766e1969e1 100644 --- a/unittests/Format/SortImportsTestJS.cpp +++ b/unittests/Format/SortImportsTestJS.cpp @@ -70,6 +70,26 @@ TEST_F(SortImportsTestJS, BasicSorting) { "let x = 1;"); } +TEST_F(SortImportsTestJS, DefaultBinding) { + verifySort("import A from 'a';\n" + "import B from 'b';\n" + "\n" + "let x = 1;", + "import B from 'b';\n" + "import A from 'a';\n" + "let x = 1;"); +} + +TEST_F(SortImportsTestJS, DefaultAndNamedBinding) { + verifySort("import A, {a} from 'a';\n" + "import B, {b} from 'b';\n" + "\n" + "let x = 1;", + "import B, {b} from 'b';\n" + "import A, {a} from 'a';\n" + "let x = 1;"); +} + TEST_F(SortImportsTestJS, WrappedImportStatements) { verifySort("import {sym1, sym2} from 'a';\n" "import {sym} from 'b';\n" @@ -101,6 +121,16 @@ TEST_F(SortImportsTestJS, Comments) { "import {sym} from 'b'; // from //foo:bar\n" "// A very important import follows.\n" "import {sym} from 'a'; /* more comments */\n"); + verifySort("import {sym} from 'a';\n" + "import {sym} from 'b';\n" + "\n" + "/** Comment on variable. */\n" + "const x = 1;\n", + "import {sym} from 'b';\n" + "import {sym} from 'a';\n" + "\n" + "/** Comment on variable. */\n" + "const x = 1;\n"); } TEST_F(SortImportsTestJS, SortStar) { @@ -190,40 +220,29 @@ TEST_F(SortImportsTestJS, SideEffectImports) { } TEST_F(SortImportsTestJS, AffectedRange) { - // Sort excluding a suffix. - verifySort("import {sym} from 'b';\n" + // Affected range inside of import statements. + verifySort("import {sym} from 'a';\n" + "import {sym} from 'b';\n" "import {sym} from 'c';\n" - "import {sym} from 'a';\n" + "\n" "let x = 1;", "import {sym} from 'c';\n" "import {sym} from 'b';\n" "import {sym} from 'a';\n" "let x = 1;", 0, 30); - // Sort excluding a prefix. + // Affected range outside of import statements. verifySort("import {sym} from 'c';\n" - "import {sym} from 'a';\n" - "import {sym} from 'b';\n" - "\n" - "let x = 1;", - "import {sym} from 'c';\n" "import {sym} from 'b';\n" "import {sym} from 'a';\n" "\n" "let x = 1;", - 30, 0); - // Sort a range within imports. - verifySort("import {sym} from 'c';\n" - "import {sym} from 'a';\n" - "import {sym} from 'b';\n" - "import {sym} from 'c';\n" - "let x = 1;", "import {sym} from 'c';\n" "import {sym} from 'b';\n" "import {sym} from 'a';\n" - "import {sym} from 'c';\n" + "\n" "let x = 1;", - 24, 30); + 70, 1); } TEST_F(SortImportsTestJS, SortingCanShrink) { diff --git a/unittests/Format/SortIncludesTest.cpp b/unittests/Format/SortIncludesTest.cpp index 13a1b9adeeec..c3c56a813041 100644 --- a/unittests/Format/SortIncludesTest.cpp +++ b/unittests/Format/SortIncludesTest.cpp @@ -24,10 +24,11 @@ protected: return std::vector<tooling::Range>(1, tooling::Range(0, Code.size())); } - std::string sort(StringRef Code, StringRef FileName = "input.cpp") { - auto Ranges = GetCodeRange(Code); - auto Sorted = - applyAllReplacements(Code, sortIncludes(Style, Code, Ranges, FileName)); + std::string sort(StringRef Code, std::vector<tooling::Range> Ranges, + StringRef FileName = "input.cc") { + auto Replaces = sortIncludes(Style, Code, Ranges, FileName); + Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); + auto Sorted = applyAllReplacements(Code, Replaces); EXPECT_TRUE(static_cast<bool>(Sorted)); auto Result = applyAllReplacements( *Sorted, reformat(Style, *Sorted, Ranges, FileName)); @@ -35,6 +36,10 @@ protected: return *Result; } + std::string sort(StringRef Code, StringRef FileName = "input.cpp") { + return sort(Code, GetCodeRange(Code), FileName); + } + unsigned newCursor(llvm::StringRef Code, unsigned Cursor) { sortIncludes(Style, Code, GetCodeRange(Code), "input.cpp", &Cursor); return Cursor; @@ -51,16 +56,24 @@ TEST_F(SortIncludesTest, BasicSorting) { sort("#include \"a.h\"\n" "#include \"c.h\"\n" "#include \"b.h\"\n")); + + EXPECT_EQ("// comment\n" + "#include <a>\n" + "#include <b>\n", + sort("// comment\n" + "#include <b>\n" + "#include <a>\n", + {tooling::Range(25, 1)})); } TEST_F(SortIncludesTest, NoReplacementsForValidIncludes) { // Identical #includes have led to a failure with an unstable sort. std::string Code = "#include <a>\n" "#include <b>\n" - "#include <b>\n" - "#include <b>\n" - "#include <b>\n" - "#include <c>\n"; + "#include <c>\n" + "#include <d>\n" + "#include <e>\n" + "#include <f>\n"; EXPECT_TRUE(sortIncludes(Style, Code, GetCodeRange(Code), "a.cc").empty()); } @@ -286,6 +299,82 @@ TEST_F(SortIncludesTest, CalculatesCorrectCursorPosition) { EXPECT_EQ(10u, newCursor(Code, 43)); } +TEST_F(SortIncludesTest, DeduplicateIncludes) { + EXPECT_EQ("#include <a>\n" + "#include <b>\n" + "#include <c>\n", + sort("#include <a>\n" + "#include <b>\n" + "#include <b>\n" + "#include <b>\n" + "#include <b>\n" + "#include <c>\n")); +} + +TEST_F(SortIncludesTest, SortAndDeduplicateIncludes) { + EXPECT_EQ("#include <a>\n" + "#include <b>\n" + "#include <c>\n", + sort("#include <b>\n" + "#include <a>\n" + "#include <b>\n" + "#include <b>\n" + "#include <c>\n" + "#include <b>\n")); +} + +TEST_F(SortIncludesTest, CalculatesCorrectCursorPositionAfterDeduplicate) { + std::string Code = "#include <b>\n" // Start of line: 0 + "#include <a>\n" // Start of line: 13 + "#include <b>\n" // Start of line: 26 + "#include <b>\n" // Start of line: 39 + "#include <c>\n" // Start of line: 52 + "#include <b>\n"; // Start of line: 65 + std::string Expected = "#include <a>\n" // Start of line: 0 + "#include <b>\n" // Start of line: 13 + "#include <c>\n"; // Start of line: 26 + EXPECT_EQ(Expected, sort(Code)); + // Cursor on 'i' in "#include <a>". + EXPECT_EQ(1u, newCursor(Code, 14)); + // Cursor on 'b' in "#include <b>". + EXPECT_EQ(23u, newCursor(Code, 10)); + EXPECT_EQ(23u, newCursor(Code, 36)); + EXPECT_EQ(23u, newCursor(Code, 49)); + EXPECT_EQ(23u, newCursor(Code, 36)); + EXPECT_EQ(23u, newCursor(Code, 75)); + // Cursor on '#' in "#include <c>". + EXPECT_EQ(26u, newCursor(Code, 52)); +} + +TEST_F(SortIncludesTest, DeduplicateLocallyInEachBlock) { + EXPECT_EQ("#include <a>\n" + "#include <b>\n" + "\n" + "#include <b>\n" + "#include <c>\n", + sort("#include <a>\n" + "#include <b>\n" + "\n" + "#include <c>\n" + "#include <b>\n" + "#include <b>\n")); +} + +TEST_F(SortIncludesTest, ValidAffactedRangesAfterDeduplicatingIncludes) { + std::string Code = "#include <a>\n" + "#include <b>\n" + "#include <a>\n" + "#include <a>\n" + "\n" + " int x ;"; + std::vector<tooling::Range> Ranges = {tooling::Range(0, 52)}; + auto Replaces = sortIncludes(Style, Code, Ranges, "input.cpp"); + Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); + EXPECT_EQ(1u, Ranges.size()); + EXPECT_EQ(0u, Ranges[0].getOffset()); + EXPECT_EQ(26u, Ranges[0].getLength()); +} + } // end namespace } // end namespace format } // end namespace clang diff --git a/unittests/Frontend/CodeGenActionTest.cpp b/unittests/Frontend/CodeGenActionTest.cpp index 71446fd4d79d..356b5130fcbe 100644 --- a/unittests/Frontend/CodeGenActionTest.cpp +++ b/unittests/Frontend/CodeGenActionTest.cpp @@ -11,9 +11,10 @@ // //===----------------------------------------------------------------------===// -#include "clang/Frontend/CompilerInstance.h" -#include "clang/CodeGen/CodeGenAction.h" #include "clang/CodeGen/BackendUtil.h" +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/PreprocessorOptions.h" #include "gtest/gtest.h" using namespace llvm; diff --git a/unittests/Frontend/FrontendActionTest.cpp b/unittests/Frontend/FrontendActionTest.cpp index 90afd774f1ad..c3e6adb6324d 100644 --- a/unittests/Frontend/FrontendActionTest.cpp +++ b/unittests/Frontend/FrontendActionTest.cpp @@ -7,13 +7,15 @@ // //===----------------------------------------------------------------------===// -#include "clang/Frontend/FrontendAction.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendActions.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" #include "clang/Sema/Sema.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/MemoryBuffer.h" @@ -191,4 +193,66 @@ TEST(PreprocessorFrontendAction, EndSourceFile) { ASSERT_TRUE(TestAction.SeenEnd); } +class TypoExternalSemaSource : public ExternalSemaSource { + CompilerInstance &CI; + +public: + TypoExternalSemaSource(CompilerInstance &CI) : CI(CI) {} + + TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind, + Scope *S, CXXScopeSpec *SS, + CorrectionCandidateCallback &CCC, + DeclContext *MemberContext, bool EnteringContext, + const ObjCObjectPointerType *OPT) override { + // Generate a fake typo correction with one attached note. + ASTContext &Ctx = CI.getASTContext(); + TypoCorrection TC(DeclarationName(&Ctx.Idents.get("moo"))); + unsigned DiagID = Ctx.getDiagnostics().getCustomDiagID( + DiagnosticsEngine::Note, "This is a note"); + TC.addExtraDiagnostic(PartialDiagnostic(DiagID, Ctx.getDiagAllocator())); + return TC; + } +}; + +struct TypoDiagnosticConsumer : public DiagnosticConsumer { + void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) override { + // Capture errors and notes. There should be one of each. + if (DiagLevel == DiagnosticsEngine::Error) { + assert(Error.empty()); + Info.FormatDiagnostic(Error); + } else { + assert(Note.empty()); + Info.FormatDiagnostic(Note); + } + } + SmallString<32> Error; + SmallString<32> Note; +}; + +TEST(ASTFrontendAction, ExternalSemaSource) { + auto *Invocation = new CompilerInvocation; + Invocation->getLangOpts()->CPlusPlus = true; + Invocation->getPreprocessorOpts().addRemappedFile( + "test.cc", MemoryBuffer::getMemBuffer("void fooo();\n" + "int main() { foo(); }") + .release()); + Invocation->getFrontendOpts().Inputs.push_back( + FrontendInputFile("test.cc", IK_CXX)); + Invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly; + Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu"; + CompilerInstance Compiler; + Compiler.setInvocation(Invocation); + auto *TDC = new TypoDiagnosticConsumer; + Compiler.createDiagnostics(TDC, /*ShouldOwnClient=*/true); + Compiler.setExternalSemaSource(new TypoExternalSemaSource(Compiler)); + + SyntaxOnlyAction TestAction; + ASSERT_TRUE(Compiler.ExecuteAction(TestAction)); + // There should be one error correcting to 'moo' and a note attached to it. + EXPECT_EQ("use of undeclared identifier 'foo'; did you mean 'moo'?", + TDC->Error.str().str()); + EXPECT_EQ("This is a note", TDC->Note.str().str()); +} + } // anonymous namespace diff --git a/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp b/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp index 33f1740bea8a..891c88da302e 100644 --- a/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp +++ b/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp @@ -14,6 +14,23 @@ namespace clang { namespace ento { +TEST(StaticAnalyzerOptions, getRegisteredCheckers) { + auto IsDebugChecker = [](StringRef CheckerName) { + return CheckerName.startswith("debug"); + }; + auto IsAlphaChecker = [](StringRef CheckerName) { + return CheckerName.startswith("alpha"); + }; + const auto &AllCheckers = + AnalyzerOptions::getRegisteredCheckers(/*IncludeExperimental=*/true); + EXPECT_FALSE(llvm::any_of(AllCheckers, IsDebugChecker)); + EXPECT_TRUE(llvm::any_of(AllCheckers, IsAlphaChecker)); + + const auto &StableCheckers = AnalyzerOptions::getRegisteredCheckers(); + EXPECT_FALSE(llvm::any_of(StableCheckers, IsDebugChecker)); + EXPECT_FALSE(llvm::any_of(StableCheckers, IsAlphaChecker)); +} + TEST(StaticAnalyzerOptions, SearchInParentPackageTests) { AnalyzerOptions Opts; Opts.Config["Outer.Inner.CheckerOne:Option"] = "true"; diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt index b4b3f404e270..185f43b6a8c5 100644 --- a/unittests/Tooling/CMakeLists.txt +++ b/unittests/Tooling/CMakeLists.txt @@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS # 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(RecursiveASTVisitorTest.cpp PROPERTIES COMPILE_FLAGS /bigobj) set_source_files_properties(RecursiveASTVisitorTestExprVisitor.cpp PROPERTIES COMPILE_FLAGS /bigobj) endif() diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp index 380d86fc5660..1a6fffec9392 100644 --- a/unittests/Tooling/CompilationDatabaseTest.cpp +++ b/unittests/Tooling/CompilationDatabaseTest.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "clang/AST/ASTConsumer.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclGroup.h" #include "clang/Frontend/FrontendAction.h" @@ -22,9 +21,10 @@ namespace tooling { static void expectFailure(StringRef JSONDatabase, StringRef Explanation) { std::string ErrorMessage; - EXPECT_EQ(nullptr, JSONCompilationDatabase::loadFromBuffer(JSONDatabase, - ErrorMessage)) - << "Expected an error because of: " << Explanation.str(); + EXPECT_EQ(nullptr, + JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage, + JSONCommandLineSyntax::Gnu)) + << "Expected an error because of: " << Explanation.str(); } TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) { @@ -44,12 +44,15 @@ TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) { expectFailure("[{\"directory\":\"\",\"command\":[],\"file\":\"\"}]", "Command not string"); expectFailure("[{\"directory\":\"\",\"arguments\":[[]],\"file\":\"\"}]", "Arguments contain non-string"); + expectFailure("[{\"output\":[]}]", "Expected strings as value."); } static std::vector<std::string> getAllFiles(StringRef JSONDatabase, - std::string &ErrorMessage) { + std::string &ErrorMessage, + JSONCommandLineSyntax Syntax) { std::unique_ptr<CompilationDatabase> Database( - JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage)); + JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage, + Syntax)); if (!Database) { ADD_FAILURE() << ErrorMessage; return std::vector<std::string>(); @@ -57,10 +60,12 @@ static std::vector<std::string> getAllFiles(StringRef JSONDatabase, return Database->getAllFiles(); } -static std::vector<CompileCommand> getAllCompileCommands(StringRef JSONDatabase, - std::string &ErrorMessage) { +static std::vector<CompileCommand> +getAllCompileCommands(JSONCommandLineSyntax Syntax, StringRef JSONDatabase, + std::string &ErrorMessage) { std::unique_ptr<CompilationDatabase> Database( - JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage)); + JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage, + Syntax)); if (!Database) { ADD_FAILURE() << ErrorMessage; return std::vector<CompileCommand>(); @@ -71,7 +76,8 @@ static std::vector<CompileCommand> getAllCompileCommands(StringRef JSONDatabase, TEST(JSONCompilationDatabase, GetAllFiles) { std::string ErrorMessage; EXPECT_EQ(std::vector<std::string>(), - getAllFiles("[]", ErrorMessage)) << ErrorMessage; + getAllFiles("[]", ErrorMessage, JSONCommandLineSyntax::Gnu)) + << ErrorMessage; std::vector<std::string> expected_files; SmallString<16> PathStorage; @@ -79,54 +85,70 @@ TEST(JSONCompilationDatabase, GetAllFiles) { expected_files.push_back(PathStorage.str()); llvm::sys::path::native("//net/dir/file2", PathStorage); expected_files.push_back(PathStorage.str()); - EXPECT_EQ(expected_files, getAllFiles( - "[{\"directory\":\"//net/dir\"," - "\"command\":\"command\"," - "\"file\":\"file1\"}," - " {\"directory\":\"//net/dir\"," - "\"command\":\"command\"," - "\"file\":\"file2\"}]", - ErrorMessage)) << ErrorMessage; + EXPECT_EQ(expected_files, + getAllFiles("[{\"directory\":\"//net/dir\"," + "\"command\":\"command\"," + "\"file\":\"file1\"}," + " {\"directory\":\"//net/dir\"," + "\"command\":\"command\"," + "\"file\":\"file2\"}]", + ErrorMessage, JSONCommandLineSyntax::Gnu)) + << ErrorMessage; } TEST(JSONCompilationDatabase, GetAllCompileCommands) { std::string ErrorMessage; - EXPECT_EQ(0u, - getAllCompileCommands("[]", ErrorMessage).size()) << ErrorMessage; + EXPECT_EQ( + 0u, getAllCompileCommands(JSONCommandLineSyntax::Gnu, "[]", ErrorMessage) + .size()) + << ErrorMessage; StringRef Directory1("//net/dir1"); StringRef FileName1("file1"); StringRef Command1("command1"); + StringRef Output1("file1.o"); StringRef Directory2("//net/dir2"); StringRef FileName2("file2"); StringRef Command2("command2"); + StringRef Output2(""); std::vector<CompileCommand> Commands = getAllCompileCommands( - ("[{\"directory\":\"" + Directory1 + "\"," + - "\"command\":\"" + Command1 + "\"," - "\"file\":\"" + FileName1 + "\"}," - " {\"directory\":\"" + Directory2 + "\"," + - "\"command\":\"" + Command2 + "\"," - "\"file\":\"" + FileName2 + "\"}]").str(), + JSONCommandLineSyntax::Gnu, + ("[{\"directory\":\"" + Directory1 + "\"," + "\"command\":\"" + Command1 + + "\"," + "\"file\":\"" + + FileName1 + "\", \"output\":\"" + + Output1 + "\"}," + " {\"directory\":\"" + + Directory2 + "\"," + "\"command\":\"" + Command2 + "\"," + "\"file\":\"" + + FileName2 + "\"}]") + .str(), ErrorMessage); EXPECT_EQ(2U, Commands.size()) << ErrorMessage; EXPECT_EQ(Directory1, Commands[0].Directory) << ErrorMessage; EXPECT_EQ(FileName1, Commands[0].Filename) << ErrorMessage; + EXPECT_EQ(Output1, Commands[0].Output) << ErrorMessage; ASSERT_EQ(1u, Commands[0].CommandLine.size()); EXPECT_EQ(Command1, Commands[0].CommandLine[0]) << ErrorMessage; EXPECT_EQ(Directory2, Commands[1].Directory) << ErrorMessage; EXPECT_EQ(FileName2, Commands[1].Filename) << ErrorMessage; + EXPECT_EQ(Output2, Commands[1].Output) << ErrorMessage; ASSERT_EQ(1u, Commands[1].CommandLine.size()); EXPECT_EQ(Command2, Commands[1].CommandLine[0]) << ErrorMessage; // Check that order is preserved. Commands = getAllCompileCommands( - ("[{\"directory\":\"" + Directory2 + "\"," + - "\"command\":\"" + Command2 + "\"," - "\"file\":\"" + FileName2 + "\"}," - " {\"directory\":\"" + Directory1 + "\"," + - "\"command\":\"" + Command1 + "\"," - "\"file\":\"" + FileName1 + "\"}]").str(), + JSONCommandLineSyntax::Gnu, + ("[{\"directory\":\"" + Directory2 + "\"," + "\"command\":\"" + Command2 + + "\"," + "\"file\":\"" + + FileName2 + "\"}," + " {\"directory\":\"" + + Directory1 + "\"," + "\"command\":\"" + Command1 + "\"," + "\"file\":\"" + + FileName1 + "\"}]") + .str(), ErrorMessage); EXPECT_EQ(2U, Commands.size()) << ErrorMessage; EXPECT_EQ(Directory2, Commands[0].Directory) << ErrorMessage; @@ -143,7 +165,8 @@ static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName, StringRef JSONDatabase, std::string &ErrorMessage) { std::unique_ptr<CompilationDatabase> Database( - JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage)); + JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage, + JSONCommandLineSyntax::Gnu)); if (!Database) return CompileCommand(); std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName); diff --git a/unittests/Tooling/LookupTest.cpp b/unittests/Tooling/LookupTest.cpp index d847a298fff2..cc3922d01b51 100644 --- a/unittests/Tooling/LookupTest.cpp +++ b/unittests/Tooling/LookupTest.cpp @@ -14,10 +14,18 @@ using namespace clang; namespace { struct GetDeclsVisitor : TestVisitor<GetDeclsVisitor> { std::function<void(CallExpr *)> OnCall; + std::function<void(RecordTypeLoc)> OnRecordTypeLoc; SmallVector<Decl *, 4> DeclStack; bool VisitCallExpr(CallExpr *Expr) { - OnCall(Expr); + if (OnCall) + OnCall(Expr); + return true; + } + + bool VisitRecordTypeLoc(RecordTypeLoc Loc) { + if (OnRecordTypeLoc) + OnRecordTypeLoc(Loc); return true; } @@ -29,7 +37,7 @@ struct GetDeclsVisitor : TestVisitor<GetDeclsVisitor> { } }; -TEST(LookupTest, replaceNestedName) { +TEST(LookupTest, replaceNestedFunctionName) { GetDeclsVisitor Visitor; auto replaceCallExpr = [&](const CallExpr *Expr, @@ -103,6 +111,55 @@ TEST(LookupTest, replaceNestedName) { }; Visitor.runOver( "namespace a { int foo(); }\nusing a::foo;\nauto f = foo();\n"); + + Visitor.OnCall = [&](CallExpr *Expr) { + EXPECT_EQ("c::bar", replaceCallExpr(Expr, "::a::c::bar")); + }; + Visitor.runOver("namespace a { namespace b { void foo(); } }\n" + "namespace a { namespace b { namespace {" + "void f() { foo(); }" + "} } }\n"); + + Visitor.OnCall = [&](CallExpr *Expr) { + EXPECT_EQ("x::bar", replaceCallExpr(Expr, "::a::x::bar")); + }; + Visitor.runOver("namespace a { namespace b { void foo(); } }\n" + "namespace a { namespace b { namespace c {" + "void f() { foo(); }" + "} } }\n"); +} + +TEST(LookupTest, replaceNestedClassName) { + GetDeclsVisitor Visitor; + + auto replaceRecordTypeLoc = [&](RecordTypeLoc Loc, + StringRef ReplacementString) { + const auto *FD = cast<CXXRecordDecl>(Loc.getDecl()); + return tooling::replaceNestedName( + nullptr, Visitor.DeclStack.back()->getDeclContext(), FD, + ReplacementString); + }; + + Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) { + // Filter Types by name since there are other `RecordTypeLoc` in the test + // file. + if (Type.getDecl()->getQualifiedNameAsString() == "a::b::Foo") + EXPECT_EQ("x::Bar", replaceRecordTypeLoc(Type, "::a::x::Bar")); + }; + Visitor.runOver("namespace a { namespace b {\n" + "class Foo;\n" + "namespace c { Foo f();; }\n" + "} }\n"); + + Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) { + // Filter Types by name since there are other `RecordTypeLoc` in the test + // file. + // `a::b::Foo` in using shadow decl is not `TypeLoc`. + if (Type.getDecl()->getQualifiedNameAsString() == "a::b::Foo") + EXPECT_EQ("Bar", replaceRecordTypeLoc(Type, "::a::x::Bar")); + }; + Visitor.runOver("namespace a { namespace b { class Foo {}; } }\n" + "namespace c { using a::b::Foo; Foo f();; }\n"); } } // end anonymous namespace diff --git a/unittests/Tooling/RecursiveASTVisitorTest.cpp b/unittests/Tooling/RecursiveASTVisitorTest.cpp index 991ae8bb7f3d..7e08f9619c1c 100644 --- a/unittests/Tooling/RecursiveASTVisitorTest.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTest.cpp @@ -133,23 +133,21 @@ TEST(RecursiveASTVisitor, AttributesAreVisited) { "};\n")); } -// Check to ensure that VarDecls are visited. -class VarDeclVisitor : public ExpectedLocationVisitor<VarDeclVisitor> { +// Check to ensure that implicit default argument expressions are visited. +class IntegerLiteralVisitor + : public ExpectedLocationVisitor<IntegerLiteralVisitor> { public: - bool VisitVarDecl(VarDecl *VD) { - Match(VD->getNameAsString(), VD->getLocStart()); + bool VisitIntegerLiteral(const IntegerLiteral *IL) { + Match("literal", IL->getLocation()); 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")); +TEST(RecursiveASTVisitor, DefaultArgumentsAreVisited) { + IntegerLiteralVisitor Visitor; + Visitor.ExpectMatch("literal", 1, 15, 2); + EXPECT_TRUE(Visitor.runOver("int f(int i = 1);\n" + "static int k = f();\n")); } } // end anonymous namespace diff --git a/unittests/Tooling/RecursiveASTVisitorTestCallVisitor.cpp b/unittests/Tooling/RecursiveASTVisitorTestCallVisitor.cpp index f8ff5bdc7879..b981585450e1 100644 --- a/unittests/Tooling/RecursiveASTVisitorTestCallVisitor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTestCallVisitor.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "TestVisitor.h" -#include <stack> using namespace clang; diff --git a/unittests/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp b/unittests/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp index 02676a737ab1..63bfb8b2e6c1 100644 --- a/unittests/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "TestVisitor.h" -#include <stack> using namespace clang; diff --git a/unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp b/unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp index 6af5906c3fd4..5f1dd65222ba 100644 --- a/unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "TestVisitor.h" -#include <stack> using namespace clang; @@ -162,10 +161,21 @@ TEST(RecursiveASTVisitor, CanSkipImplicitMemberInitializations) { class DeclRefExprVisitor : public ExpectedLocationVisitor<DeclRefExprVisitor> { public: + DeclRefExprVisitor() : ShouldVisitImplicitCode(false) {} + + bool shouldVisitImplicitCode() const { return ShouldVisitImplicitCode; } + + void setShouldVisitImplicitCode(bool NewValue) { + ShouldVisitImplicitCode = NewValue; + } + bool VisitDeclRefExpr(DeclRefExpr *Reference) { Match(Reference->getNameInfo().getAsString(), Reference->getLocation()); return true; } + +private: + bool ShouldVisitImplicitCode; }; TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArguments) { @@ -192,6 +202,43 @@ TEST(RecursiveASTVisitor, VisitsCallExpr) { "void x(); void y() { x(); }")); } +TEST(RecursiveASTVisitor, VisitsExplicitLambdaCaptureInit) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("i", 1, 20); + EXPECT_TRUE(Visitor.runOver( + "void f() { int i; [i]{}; }", + DeclRefExprVisitor::Lang_CXX11)); +} + +TEST(RecursiveASTVisitor, VisitsUseOfImplicitLambdaCapture) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("i", 1, 24); + EXPECT_TRUE(Visitor.runOver( + "void f() { int i; [=]{ i; }; }", + DeclRefExprVisitor::Lang_CXX11)); +} + +TEST(RecursiveASTVisitor, VisitsImplicitLambdaCaptureInit) { + DeclRefExprVisitor Visitor; + Visitor.setShouldVisitImplicitCode(true); + // We're expecting the "i" in the lambda to be visited twice: + // - Once for the DeclRefExpr in the lambda capture initialization (whose + // source code location is set to the first use of the variable). + // - Once for the DeclRefExpr for the use of "i" inside the lambda. + Visitor.ExpectMatch("i", 1, 24, /*Times=*/2); + EXPECT_TRUE(Visitor.runOver( + "void f() { int i; [=]{ i; }; }", + DeclRefExprVisitor::Lang_CXX11)); +} + +TEST(RecursiveASTVisitor, VisitsLambdaInitCaptureInit) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("i", 1, 24); + EXPECT_TRUE(Visitor.runOver( + "void f() { int i; [a = i + 1]{}; }", + DeclRefExprVisitor::Lang_CXX14)); +} + /* FIXME: According to Richard Smith this is a bug in the AST. TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArgumentsInInstantiation) { DeclRefExprVisitor Visitor; diff --git a/unittests/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp b/unittests/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp index 63e2e8b6024c..dc2adaf4da0c 100644 --- a/unittests/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "TestVisitor.h" -#include <stack> using namespace clang; diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp index df96bb159dea..c29f8d7d7123 100644 --- a/unittests/Tooling/RefactoringTest.cpp +++ b/unittests/Tooling/RefactoringTest.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "ReplacementTest.h" #include "RewriterTestContext.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -18,6 +19,7 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/VirtualFileSystem.h" #include "clang/Format/Format.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" @@ -26,22 +28,11 @@ #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/SmallString.h" -#include "llvm/Support/Path.h" #include "gtest/gtest.h" namespace clang { namespace tooling { -class ReplacementTest : public ::testing::Test { - protected: - Replacement createReplacement(SourceLocation Start, unsigned Length, - llvm::StringRef ReplacementText) { - return Replacement(Context.Sources, Start, Length, ReplacementText); - } - - RewriterTestContext Context; -}; - TEST_F(ReplacementTest, CanDeleteAllText) { FileID ID = Context.createInMemoryFile("input.cpp", "text"); SourceLocation Location = Context.getLocation(ID, 1, 1); @@ -109,62 +100,412 @@ TEST_F(ReplacementTest, ReturnsInvalidPath) { EXPECT_TRUE(Replace2.getFilePath().empty()); } +// Checks that an llvm::Error instance contains a ReplacementError with expected +// error code, expected new replacement, and expected existing replacement. +static bool checkReplacementError( + llvm::Error&& Error, replacement_error ExpectedErr, + llvm::Optional<Replacement> ExpectedExisting, + llvm::Optional<Replacement> ExpectedNew) { + if (!Error) { + llvm::errs() << "Error is a success."; + return false; + } + std::string ErrorMessage; + llvm::raw_string_ostream OS(ErrorMessage); + llvm::handleAllErrors(std::move(Error), [&](const ReplacementError &RE) { + llvm::errs() << "Handling error...\n"; + if (ExpectedErr != RE.get()) + OS << "Unexpected error code: " << int(RE.get()) << "\n"; + if (ExpectedExisting != RE.getExistingReplacement()) { + OS << "Expected Existing != Actual Existing.\n"; + if (ExpectedExisting.hasValue()) + OS << "Expected existing replacement: " << ExpectedExisting->toString() + << "\n"; + if (RE.getExistingReplacement().hasValue()) + OS << "Actual existing replacement: " + << RE.getExistingReplacement()->toString() << "\n"; + } + if (ExpectedNew != RE.getNewReplacement()) { + OS << "Expected New != Actual New.\n"; + if (ExpectedNew.hasValue()) + OS << "Expected new replacement: " << ExpectedNew->toString() << "\n"; + if (RE.getNewReplacement().hasValue()) + OS << "Actual new replacement: " << RE.getNewReplacement()->toString() + << "\n"; + } + }); + OS.flush(); + if (ErrorMessage.empty()) return true; + llvm::errs() << ErrorMessage; + return false; +} + +TEST_F(ReplacementTest, FailAddReplacements) { + Replacements Replaces; + Replacement Deletion("x.cc", 0, 10, "3"); + auto Err = Replaces.add(Deletion); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Replacement OverlappingReplacement("x.cc", 0, 2, "a"); + Err = Replaces.add(OverlappingReplacement); + EXPECT_TRUE(checkReplacementError(std::move(Err), + replacement_error::overlap_conflict, + Deletion, OverlappingReplacement)); + + Replacement ContainedReplacement("x.cc", 2, 2, "a"); + Err = Replaces.add(Replacement(ContainedReplacement)); + EXPECT_TRUE(checkReplacementError(std::move(Err), + replacement_error::overlap_conflict, + Deletion, ContainedReplacement)); + + Replacement WrongPathReplacement("y.cc", 20, 2, ""); + Err = Replaces.add(WrongPathReplacement); + EXPECT_TRUE(checkReplacementError(std::move(Err), + replacement_error::wrong_file_path, + Deletion, WrongPathReplacement)); + + EXPECT_EQ(1u, Replaces.size()); + EXPECT_EQ(Deletion, *Replaces.begin()); +} + +TEST_F(ReplacementTest, DeletionInReplacements) { + Replacements Replaces; + Replacement R("x.cc", 0, 10, "3"); + auto Err = Replaces.add(R); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 0, 2, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 2, 2, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + EXPECT_EQ(1u, Replaces.size()); + EXPECT_EQ(R, *Replaces.begin()); +} + +TEST_F(ReplacementTest, OverlappingReplacements) { + Replacements Replaces; + auto Err = Replaces.add(Replacement("x.cc", 0, 3, "345")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 2, 3, "543")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + EXPECT_EQ(1u, Replaces.size()); + EXPECT_EQ(Replacement("x.cc", 0, 5, "34543"), *Replaces.begin()); + + Err = Replaces.add(Replacement("x.cc", 2, 1, "5")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + EXPECT_EQ(1u, Replaces.size()); + EXPECT_EQ(Replacement("x.cc", 0, 5, "34543"), *Replaces.begin()); +} + +TEST_F(ReplacementTest, AddAdjacentInsertionAndReplacement) { + Replacements Replaces; + // Test adding an insertion at the offset of an existing replacement. + auto Err = Replaces.add(Replacement("x.cc", 10, 3, "replace")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 10, 0, "insert")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + EXPECT_EQ(Replaces.size(), 2u); + + Replaces.clear(); + // Test overlap with an existing insertion. + Err = Replaces.add(Replacement("x.cc", 10, 0, "insert")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 10, 3, "replace")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + EXPECT_EQ(Replaces.size(), 2u); +} + +TEST_F(ReplacementTest, MergeNewDeletions) { + Replacements Replaces; + Replacement ContainingReplacement("x.cc", 0, 10, ""); + auto Err = Replaces.add(ContainingReplacement); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Err = Replaces.add(Replacement("x.cc", 5, 3, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Err = Replaces.add(Replacement("x.cc", 0, 10, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Err = Replaces.add(Replacement("x.cc", 5, 5, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + EXPECT_EQ(1u, Replaces.size()); + EXPECT_EQ(*Replaces.begin(), ContainingReplacement); +} + +TEST_F(ReplacementTest, MergeOverlappingButNotAdjacentReplacement) { + Replacements Replaces; + auto Err = Replaces.add(Replacement("x.cc", 0, 2, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Err = Replaces.add(Replacement("x.cc", 5, 5, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Replacement After = Replacement("x.cc", 10, 5, ""); + Err = Replaces.add(After); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Replacement ContainingReplacement("x.cc", 0, 10, ""); + Err = Replaces.add(ContainingReplacement); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + EXPECT_EQ(2u, Replaces.size()); + EXPECT_EQ(*Replaces.begin(), ContainingReplacement); + EXPECT_EQ(*(++Replaces.begin()), After); +} + +TEST_F(ReplacementTest, InsertionBeforeMergedDeletions) { + Replacements Replaces; + + Replacement Insertion("x.cc", 0, 0, "123"); + auto Err = Replaces.add(Insertion); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Err = Replaces.add(Replacement("x.cc", 5, 5, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Replacement Deletion("x.cc", 0, 10, ""); + Err = Replaces.add(Deletion); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + EXPECT_EQ(2u, Replaces.size()); + EXPECT_EQ(*Replaces.begin(), Insertion); + EXPECT_EQ(*(++Replaces.begin()), Deletion); +} + +TEST_F(ReplacementTest, MergeOverlappingDeletions) { + Replacements Replaces; + auto Err = Replaces.add(Replacement("x.cc", 0, 2, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Err = Replaces.add(Replacement("x.cc", 0, 5, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + EXPECT_EQ(1u, Replaces.size()); + EXPECT_EQ(Replacement("x.cc", 0, 5, ""), *Replaces.begin()); + + Err = Replaces.add(Replacement("x.cc", 1, 5, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + EXPECT_EQ(1u, Replaces.size()); + EXPECT_EQ(Replacement("x.cc", 0, 6, ""), *Replaces.begin()); +} + +TEST_F(ReplacementTest, FailedMergeExistingDeletions) { + Replacements Replaces; + Replacement First("x.cc", 0, 2, ""); + auto Err = Replaces.add(First); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Replacement Second("x.cc", 5, 5, ""); + Err = Replaces.add(Second); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Err = Replaces.add(Replacement("x.cc", 1, 10, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + EXPECT_EQ(1u, Replaces.size()); + EXPECT_EQ(Replacement("x.cc", 0, 11, ""), *Replaces.begin()); +} + +TEST_F(ReplacementTest, FailAddRegression) { + Replacements Replaces; + // Create two replacements, where the second one is an insertion of the empty + // string exactly at the end of the first one. + auto Err = Replaces.add(Replacement("x.cc", 0, 10, "1")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 10, 0, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + // Make sure we find the overlap with the first entry when inserting a + // replacement that ends exactly at the seam of the existing replacements. + Replacement OverlappingReplacement("x.cc", 5, 5, "fail"); + Err = Replaces.add(OverlappingReplacement); + EXPECT_TRUE(checkReplacementError(std::move(Err), + replacement_error::overlap_conflict, + *Replaces.begin(), OverlappingReplacement)); + + Err = Replaces.add(Replacement("x.cc", 10, 0, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); +} + +TEST_F(ReplacementTest, InsertAtOffsetOfReplacement) { + Replacements Replaces; + auto Err = Replaces.add(Replacement("x.cc", 10, 2, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 10, 0, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + EXPECT_EQ(Replaces.size(), 2u); + + Replaces.clear(); + Err = Replaces.add(Replacement("x.cc", 10, 0, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 10, 2, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + EXPECT_EQ(Replaces.size(), 2u); +} + +TEST_F(ReplacementTest, AddInsertAtOtherInsertWhenOderIndependent) { + Replacements Replaces; + auto Err = Replaces.add(Replacement("x.cc", 10, 0, "a")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Replacement ConflictInsertion("x.cc", 10, 0, "b"); + Err = Replaces.add(ConflictInsertion); + EXPECT_TRUE(checkReplacementError(std::move(Err), + replacement_error::insert_conflict, + *Replaces.begin(), ConflictInsertion)); + + Replaces.clear(); + Err = Replaces.add(Replacement("x.cc", 10, 0, "a")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 10, 0, "aa")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + EXPECT_EQ(1u, Replaces.size()); + EXPECT_EQ(Replacement("x.cc", 10, 0, "aaa"), *Replaces.begin()); + + Replaces.clear(); + Err = Replaces.add(Replacement("x.cc", 10, 0, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 10, 3, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 10, 0, "")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + EXPECT_EQ(2u, Replaces.size()); + EXPECT_EQ(Replacement("x.cc", 10, 0, ""), *Replaces.begin()); + EXPECT_EQ(Replacement("x.cc", 10, 3, ""), *std::next(Replaces.begin())); +} + +TEST_F(ReplacementTest, InsertBetweenAdjacentReplacements) { + Replacements Replaces; + auto Err = Replaces.add(Replacement("x.cc", 10, 5, "a")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 8, 2, "a")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + Err = Replaces.add(Replacement("x.cc", 10, 0, "b")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); +} + TEST_F(ReplacementTest, CanApplyReplacements) { FileID ID = Context.createInMemoryFile("input.cpp", "line1\nline2\nline3\nline4"); - Replacements Replaces; - Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), - 5, "replaced")); - Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 3, 1), - 5, "other")); + Replacements Replaces = + toReplacements({Replacement(Context.Sources, + Context.getLocation(ID, 2, 1), 5, "replaced"), + 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)); } -// 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) { +// Verifies that replacement/deletion is applied before insertion at the same +// offset. +TEST_F(ReplacementTest, InsertAndDelete) { 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")); + Replacements Replaces = toReplacements( + {Replacement(Context.Sources, Context.getLocation(ID, 2, 1), 6, ""), + Replacement(Context.Sources, Context.getLocation(ID, 2, 1), 0, + "other\n")}); EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite)); - EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID)); + EXPECT_EQ("line1\nother\nline3\nline4", Context.getRewrittenText(ID)); +} + +TEST_F(ReplacementTest, AdjacentReplacements) { + FileID ID = Context.createInMemoryFile("input.cpp", + "ab"); + Replacements Replaces = toReplacements( + {Replacement(Context.Sources, Context.getLocation(ID, 1, 1), 1, "x"), + Replacement(Context.Sources, Context.getLocation(ID, 1, 2), 1, "y")}); + EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite)); + EXPECT_EQ("xy", Context.getRewrittenText(ID)); } -TEST_F(ReplacementTest, SkipsDuplicateReplacements) { +TEST_F(ReplacementTest, AddDuplicateReplacements) { FileID ID = Context.createInMemoryFile("input.cpp", "line1\nline2\nline3\nline4"); - Replacements Replaces; - Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), - 5, "replaced")); - Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), - 5, "replaced")); - Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), - 5, "replaced")); + auto Replaces = toReplacements({Replacement( + Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced")}); + + auto Err = Replaces.add(Replacement( + Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + + Err = Replaces.add(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), + 5, "replaced")); + EXPECT_TRUE(!Err); + llvm::consumeError(std::move(Err)); + EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite)); EXPECT_EQ("line1\nreplaced\nline3\nline4", Context.getRewrittenText(ID)); } -TEST_F(ReplacementTest, ApplyAllFailsIfOneApplyFails) { - // This test depends on the value of the file name of an invalid source - // location being in the range ]a, z[. - FileID IDa = Context.createInMemoryFile("a.cpp", "text"); - FileID IDz = Context.createInMemoryFile("z.cpp", "text"); - Replacements Replaces; - Replaces.insert(Replacement(Context.Sources, Context.getLocation(IDa, 1, 1), - 4, "a")); - Replaces.insert(Replacement(Context.Sources, SourceLocation(), - 5, "2")); - Replaces.insert(Replacement(Context.Sources, Context.getLocation(IDz, 1, 1), - 4, "z")); +TEST_F(ReplacementTest, FailOrderDependentReplacements) { + FileID ID = Context.createInMemoryFile("input.cpp", + "line1\nline2\nline3\nline4"); + auto Replaces = toReplacements({Replacement( + Context.Sources, Context.getLocation(ID, 2, 1), 5, "other")}); + + Replacement ConflictReplacement(Context.Sources, + Context.getLocation(ID, 2, 1), 5, "rehto"); + auto Err = Replaces.add(ConflictReplacement); + EXPECT_TRUE(checkReplacementError(std::move(Err), + replacement_error::overlap_conflict, + *Replaces.begin(), ConflictReplacement)); + + EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite)); + EXPECT_EQ("line1\nother\nline3\nline4", Context.getRewrittenText(ID)); +} + +TEST_F(ReplacementTest, InvalidSourceLocationFailsApplyAll) { + Replacements Replaces = + toReplacements({Replacement(Context.Sources, SourceLocation(), 5, "2")}); + EXPECT_FALSE(applyAllReplacements(Replaces, Context.Rewrite)); - EXPECT_EQ("a", Context.getRewrittenText(IDa)); - EXPECT_EQ("z", Context.getRewrittenText(IDz)); } TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) { @@ -180,76 +521,66 @@ TEST_F(ReplacementTest, MultipleFilesReplaceAndFormat) { 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); + StringRef File1 = "format_1.cpp"; + StringRef File2 = "format_2.cpp"; + FileID ID1 = Context.createInMemoryFile(File1, Code1); + FileID ID2 = Context.createInMemoryFile(File2, 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}")); + std::map<std::string, Replacements> FileToReplaces; + FileToReplaces[File1] = toReplacements( + {tooling::Replacement(Context.Sources, Context.getLocation(ID1, 1, 1), 6, + "auto "), + tooling::Replacement(Context.Sources, Context.getLocation(ID1, 3, 10), 1, + "12345678901")}); + FileToReplaces[File2] = toReplacements( + {tooling::Replacement(Context.Sources, Context.getLocation(ID2, 1, 12), 0, + "4567890123"), + tooling::Replacement(Context.Sources, Context.getLocation(ID2, 2, 9), 1, + "10")}); + EXPECT_TRUE( + formatAndApplyAllReplacements(FileToReplaces, 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, "")); - Replaces.insert(Replacement("", 4, 3, " ")); + Replacements Replaces = + toReplacements({Replacement("", 0, 1, ""), 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(3u, shiftedCodePosition(Replaces, 5)); // int | i; - EXPECT_EQ(3u, 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(3u, shiftedCodePosition(Replaces, 5)); // int | i; - EXPECT_EQ(3u, shiftedCodePosition(Replaces, 6)); // int |i; - EXPECT_EQ(4u, shiftedCodePosition(Replaces, 7)); // int |; - EXPECT_EQ(5u, shiftedCodePosition(Replaces, 8)); // int i| + EXPECT_EQ(0u, Replaces.getShiftedCodePosition(0)); // |int i; + EXPECT_EQ(0u, Replaces.getShiftedCodePosition(1)); // |nt i; + EXPECT_EQ(1u, Replaces.getShiftedCodePosition(2)); // i|t i; + EXPECT_EQ(2u, Replaces.getShiftedCodePosition(3)); // in| i; + EXPECT_EQ(3u, Replaces.getShiftedCodePosition(4)); // int| i; + EXPECT_EQ(3u, Replaces.getShiftedCodePosition(5)); // int | i; + EXPECT_EQ(3u, Replaces.getShiftedCodePosition(6)); // int |i; + EXPECT_EQ(4u, Replaces.getShiftedCodePosition(7)); // int |; + EXPECT_EQ(5u, Replaces.getShiftedCodePosition(8)); // int i| } TEST(ShiftedCodePositionTest, FindsNewCodePositionWithInserts) { - Replacements Replaces; - Replaces.insert(Replacement("", 4, 0, "\"\n\"")); + Replacements Replaces = toReplacements({Replacement("", 4, 0, "\"\n\"")}); // Assume '"12345678"' is turned into '"1234"\n"5678"'. - EXPECT_EQ(3u, shiftedCodePosition(Replaces, 3)); // "123|5678" - EXPECT_EQ(7u, shiftedCodePosition(Replaces, 4)); // "1234|678" - EXPECT_EQ(8u, shiftedCodePosition(Replaces, 5)); // "12345|78" + EXPECT_EQ(3u, Replaces.getShiftedCodePosition(3)); // "123|5678" + EXPECT_EQ(7u, Replaces.getShiftedCodePosition(4)); // "1234|678" + EXPECT_EQ(8u, Replaces.getShiftedCodePosition(5)); // "12345|78" } TEST(ShiftedCodePositionTest, FindsNewCodePositionInReplacedText) { - Replacements Replaces; // Replace the first four characters with "abcd". - Replaces.insert(Replacement("", 0, 4, "abcd")); + auto Replaces = toReplacements({Replacement("", 0, 4, "abcd")}); for (unsigned i = 0; i < 3; ++i) - EXPECT_EQ(i, shiftedCodePosition(Replaces, i)); + EXPECT_EQ(i, Replaces.getShiftedCodePosition(i)); +} + +TEST(ShiftedCodePositionTest, NoReplacementText) { + Replacements Replaces = toReplacements({Replacement("", 0, 42, "")}); + EXPECT_EQ(0u, Replaces.getShiftedCodePosition(0)); + EXPECT_EQ(0u, Replaces.getShiftedCodePosition(39)); + EXPECT_EQ(3u, Replaces.getShiftedCodePosition(45)); + EXPECT_EQ(0u, Replaces.getShiftedCodePosition(42)); } class FlushRewrittenFilesTest : public ::testing::Test { @@ -305,9 +636,8 @@ public: TEST_F(FlushRewrittenFilesTest, StoresChangesOnDisk) { FileID ID = createFile("input.cpp", "line1\nline2\nline3\nline4"); - Replacements Replaces; - Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1), - 5, "replaced")); + Replacements Replaces = toReplacements({Replacement( + Context.Sources, Context.getLocation(ID, 2, 1), 5, "replaced")}); EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite)); EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles()); EXPECT_EQ("line1\nreplaced\nline3\nline4", @@ -455,12 +785,11 @@ TEST(Range, contains) { 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")); + Replacements Replaces = toReplacements( + {Replacement("foo", 0, 4, ""), Replacement("foo", 10, 1, "zzzzzz"), + Replacement("foo", 11, 0, "oooooooooooooooo")}); - std::vector<Range> Ranges = calculateChangedRanges(Replaces); + std::vector<Range> Ranges = Replaces.getAffectedRanges(); EXPECT_EQ(2ul, Ranges.size()); EXPECT_TRUE(Ranges[0].getOffset() == 0); @@ -469,25 +798,43 @@ TEST(Range, CalculateRangesOfReplacements) { EXPECT_TRUE(Ranges[1].getLength() == 22); } +TEST(Range, CalculateRangesOfInsertionAroundReplacement) { + Replacements Replaces = toReplacements( + {Replacement("foo", 0, 2, ""), Replacement("foo", 0, 0, "ba")}); + + std::vector<Range> Ranges = Replaces.getAffectedRanges(); + + EXPECT_EQ(1ul, Ranges.size()); + EXPECT_EQ(0u, Ranges[0].getOffset()); + EXPECT_EQ(2u, Ranges[0].getLength()); +} + +TEST(Range, RangesAfterEmptyReplacements) { + std::vector<Range> Ranges = {Range(5, 6), Range(10, 5)}; + Replacements Replaces; + std::vector<Range> Expected = {Range(5, 10)}; + EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); +} + TEST(Range, RangesAfterReplacements) { std::vector<Range> Ranges = {Range(5, 2), Range(10, 5)}; - Replacements Replaces = {Replacement("foo", 0, 2, "1234")}; + Replacements Replaces = toReplacements({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")}; + Replacements Replaces = toReplacements({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, "")}; + Replacements Replaces = toReplacements({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)); @@ -495,9 +842,9 @@ TEST(Range, NotAffectedByReplacements) { 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")}; + Replacements Replaces = toReplacements({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)); @@ -506,9 +853,9 @@ TEST(Range, RangesWithNonOverlappingReplacements) { 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, "")}; + Replacements Replaces = toReplacements( + {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)); @@ -516,122 +863,52 @@ TEST(Range, RangesWithOverlappingReplacements) { TEST(Range, MergeIntoOneRange) { std::vector<Range> Ranges = {Range(0, 2), Range(5, 2), Range(15, 5)}; - Replacements Replaces = {Replacement("foo", 1, 15, "1234567890")}; + Replacements Replaces = + toReplacements({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")}; + Replacements Replaces = toReplacements( + {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")}; + Replacements Replaces = toReplacements( + {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")}; + Replacements Replaces = toReplacements( + {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)}; + Replacements Replaces = toReplacements({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 ")); - 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("fileB", 60, 1, " bar ")); - Input.push_back(Replacement("fileA", 60, 2, " bar ")); - 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", 60, 1, " bar ")); - Expected.push_back(Replacement("fileA", 60, 2, " bar ")); - - std::vector<Range> Conflicts; // Ignored for this test - deduplicate(Input, Conflicts); - - EXPECT_EQ(3U, Conflicts.size()); - EXPECT_EQ(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()); - } +TEST(Range, ConflictingRangesBeforeReplacements) { + std::vector<Range> Ranges = {Range(8, 3), Range(5, 4), Range(9, 1)}; + Replacements Replaces = toReplacements({Replacement("foo", 1, 3, "")}); + std::vector<Range> Expected = {Range(1, 0), Range(2, 6)}; + EXPECT_EQ(Expected, calculateRangesAfterReplacements(Replaces, Ranges)); } class MergeReplacementsTest : public ::testing::Test { @@ -647,7 +924,7 @@ protected: EXPECT_EQ(Intermediate, *AfterFirst); EXPECT_EQ(Result, *InSequenceRewrite); - tooling::Replacements Merged = mergeReplacements(First, Second); + tooling::Replacements Merged = First.merge(Second); auto MergedRewrite = applyAllReplacements(Code, Merged); EXPECT_TRUE(static_cast<bool>(MergedRewrite)); EXPECT_EQ(*InSequenceRewrite, *MergedRewrite); @@ -661,7 +938,7 @@ protected: auto AfterFirst = applyAllReplacements(Code, First); EXPECT_TRUE(static_cast<bool>(AfterFirst)); auto InSequenceRewrite = applyAllReplacements(*AfterFirst, Second); - tooling::Replacements Merged = mergeReplacements(First, Second); + tooling::Replacements Merged = First.merge(Second); auto MergedRewrite = applyAllReplacements(Code, Merged); EXPECT_TRUE(static_cast<bool>(MergedRewrite)); EXPECT_EQ(*InSequenceRewrite, *MergedRewrite); @@ -674,62 +951,142 @@ protected: TEST_F(MergeReplacementsTest, Offsets) { mergeAndTestRewrite("aaa", "aabab", "cacabab", - {{"", 2, 0, "b"}, {"", 3, 0, "b"}}, - {{"", 0, 0, "c"}, {"", 1, 0, "c"}}); + toReplacements({{"", 2, 0, "b"}, {"", 3, 0, "b"}}), + toReplacements({{"", 0, 0, "c"}, {"", 1, 0, "c"}})); mergeAndTestRewrite("aaa", "babaa", "babacac", - {{"", 0, 0, "b"}, {"", 1, 0, "b"}}, - {{"", 4, 0, "c"}, {"", 5, 0, "c"}}); - mergeAndTestRewrite("aaaa", "aaa", "aac", {{"", 1, 1, ""}}, - {{"", 2, 1, "c"}}); + toReplacements({{"", 0, 0, "b"}, {"", 1, 0, "b"}}), + toReplacements({{"", 4, 0, "c"}, {"", 5, 0, "c"}})); + mergeAndTestRewrite("aaaa", "aaa", "aac", toReplacements({{"", 1, 1, ""}}), + toReplacements({{"", 2, 1, "c"}})); mergeAndTestRewrite("aa", "bbabba", "bbabcba", - {{"", 0, 0, "bb"}, {"", 1, 0, "bb"}}, {{"", 4, 0, "c"}}); + toReplacements({{"", 0, 0, "bb"}, {"", 1, 0, "bb"}}), + toReplacements({{"", 4, 0, "c"}})); } TEST_F(MergeReplacementsTest, Concatenations) { // Basic concatenations. It is important to merge these into a single // replacement to ensure the correct order. - EXPECT_EQ((Replacements{{"", 0, 0, "ab"}}), - mergeReplacements({{"", 0, 0, "a"}}, {{"", 1, 0, "b"}})); - EXPECT_EQ((Replacements{{"", 0, 0, "ba"}}), - mergeReplacements({{"", 0, 0, "a"}}, {{"", 0, 0, "b"}})); - mergeAndTestRewrite("", "a", "ab", {{"", 0, 0, "a"}}, {{"", 1, 0, "b"}}); - mergeAndTestRewrite("", "a", "ba", {{"", 0, 0, "a"}}, {{"", 0, 0, "b"}}); + { + auto First = toReplacements({{"", 0, 0, "a"}}); + auto Second = toReplacements({{"", 1, 0, "b"}}); + EXPECT_EQ(toReplacements({{"", 0, 0, "ab"}}), First.merge(Second)); + } + { + auto First = toReplacements({{"", 0, 0, "a"}}); + auto Second = toReplacements({{"", 0, 0, "b"}}); + EXPECT_EQ(toReplacements({{"", 0, 0, "ba"}}), First.merge(Second)); + } + mergeAndTestRewrite("", "a", "ab", toReplacements({{"", 0, 0, "a"}}), + toReplacements({{"", 1, 0, "b"}})); + mergeAndTestRewrite("", "a", "ba", toReplacements({{"", 0, 0, "a"}}), + toReplacements({{"", 0, 0, "b"}})); } TEST_F(MergeReplacementsTest, NotChangingLengths) { - mergeAndTestRewrite("aaaa", "abba", "acca", {{"", 1, 2, "bb"}}, - {{"", 1, 2, "cc"}}); - mergeAndTestRewrite("aaaa", "abba", "abcc", {{"", 1, 2, "bb"}}, - {{"", 2, 2, "cc"}}); - mergeAndTestRewrite("aaaa", "abba", "ccba", {{"", 1, 2, "bb"}}, - {{"", 0, 2, "cc"}}); + mergeAndTestRewrite("aaaa", "abba", "acca", + toReplacements({{"", 1, 2, "bb"}}), + toReplacements({{"", 1, 2, "cc"}})); + mergeAndTestRewrite("aaaa", "abba", "abcc", + toReplacements({{"", 1, 2, "bb"}}), + toReplacements({{"", 2, 2, "cc"}})); + mergeAndTestRewrite("aaaa", "abba", "ccba", + toReplacements({{"", 1, 2, "bb"}}), + toReplacements({{"", 0, 2, "cc"}})); mergeAndTestRewrite("aaaaaa", "abbdda", "abccda", - {{"", 1, 2, "bb"}, {"", 3, 2, "dd"}}, {{"", 2, 2, "cc"}}); + toReplacements({{"", 1, 2, "bb"}, {"", 3, 2, "dd"}}), + toReplacements({{"", 2, 2, "cc"}})); } TEST_F(MergeReplacementsTest, OverlappingRanges) { mergeAndTestRewrite("aaa", "bbd", "bcbcd", - {{"", 0, 1, "bb"}, {"", 1, 2, "d"}}, - {{"", 1, 0, "c"}, {"", 2, 0, "c"}}); + toReplacements({{"", 0, 1, "bb"}, {"", 1, 2, "d"}}), + toReplacements({{"", 1, 0, "c"}, {"", 2, 0, "c"}})); - mergeAndTestRewrite("aaaa", "aabbaa", "acccca", {{"", 2, 0, "bb"}}, - {{"", 1, 4, "cccc"}}); + mergeAndTestRewrite("aaaa", "aabbaa", "acccca", + toReplacements({{"", 2, 0, "bb"}}), + toReplacements({{"", 1, 4, "cccc"}})); mergeAndTestRewrite("aaaa", "aababa", "acccca", - {{"", 2, 0, "b"}, {"", 3, 0, "b"}}, {{"", 1, 4, "cccc"}}); - mergeAndTestRewrite("aaaaaa", "abbbba", "abba", {{"", 1, 4, "bbbb"}}, - {{"", 2, 2, ""}}); - mergeAndTestRewrite("aaaa", "aa", "cc", {{"", 1, 1, ""}, {"", 2, 1, ""}}, - {{"", 0, 2, "cc"}}); - mergeAndTestRewrite("aa", "abbba", "abcbcba", {{"", 1, 0, "bbb"}}, - {{"", 2, 0, "c"}, {"", 3, 0, "c"}}); - - mergeAndTestRewrite("aaa", "abbab", "ccdd", - {{"", 0, 1, ""}, {"", 2, 0, "bb"}, {"", 3, 0, "b"}}, - {{"", 0, 2, "cc"}, {"", 2, 3, "dd"}}); - mergeAndTestRewrite("aa", "babbab", "ccdd", - {{"", 0, 0, "b"}, {"", 1, 0, "bb"}, {"", 2, 0, "b"}}, - {{"", 0, 3, "cc"}, {"", 3, 3, "dd"}}); + toReplacements({{"", 2, 0, "b"}, {"", 3, 0, "b"}}), + toReplacements({{"", 1, 4, "cccc"}})); + mergeAndTestRewrite("aaaaaa", "abbbba", "abba", + toReplacements({{"", 1, 4, "bbbb"}}), + toReplacements({{"", 2, 2, ""}})); + mergeAndTestRewrite("aaaa", "aa", "cc", + toReplacements({{"", 1, 1, ""}, {"", 2, 1, ""}}), + toReplacements({{"", 0, 2, "cc"}})); + mergeAndTestRewrite("aa", "abbba", "abcbcba", + toReplacements({{"", 1, 0, "bbb"}}), + toReplacements({{"", 2, 0, "c"}, {"", 3, 0, "c"}})); + + mergeAndTestRewrite( + "aaa", "abbab", "ccdd", + toReplacements({{"", 0, 1, ""}, {"", 2, 0, "bb"}, {"", 3, 0, "b"}}), + toReplacements({{"", 0, 2, "cc"}, {"", 2, 3, "dd"}})); + mergeAndTestRewrite( + "aa", "babbab", "ccdd", + toReplacements({{"", 0, 0, "b"}, {"", 1, 0, "bb"}, {"", 2, 0, "b"}}), + toReplacements({{"", 0, 3, "cc"}, {"", 3, 3, "dd"}})); +} + +TEST(DeduplicateByFileTest, PathsWithDots) { + std::map<std::string, Replacements> FileToReplaces; + llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS( + new vfs::InMemoryFileSystem()); + FileManager FileMgr(FileSystemOptions(), VFS); +#if !defined(LLVM_ON_WIN32) + StringRef Path1 = "a/b/.././c.h"; + StringRef Path2 = "a/c.h"; +#else + StringRef Path1 = "a\\b\\..\\.\\c.h"; + StringRef Path2 = "a\\c.h"; +#endif + EXPECT_TRUE(VFS->addFile(Path1, 0, llvm::MemoryBuffer::getMemBuffer(""))); + EXPECT_TRUE(VFS->addFile(Path2, 0, llvm::MemoryBuffer::getMemBuffer(""))); + FileToReplaces[Path1] = Replacements(); + FileToReplaces[Path2] = Replacements(); + FileToReplaces = groupReplacementsByFile(FileMgr, FileToReplaces); + EXPECT_EQ(1u, FileToReplaces.size()); + EXPECT_EQ(Path1, FileToReplaces.begin()->first); +} + +TEST(DeduplicateByFileTest, PathWithDotSlash) { + std::map<std::string, Replacements> FileToReplaces; + llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS( + new vfs::InMemoryFileSystem()); + FileManager FileMgr(FileSystemOptions(), VFS); +#if !defined(LLVM_ON_WIN32) + StringRef Path1 = "./a/b/c.h"; + StringRef Path2 = "a/b/c.h"; +#else + StringRef Path1 = ".\\a\\b\\c.h"; + StringRef Path2 = "a\\b\\c.h"; +#endif + EXPECT_TRUE(VFS->addFile(Path1, 0, llvm::MemoryBuffer::getMemBuffer(""))); + EXPECT_TRUE(VFS->addFile(Path2, 0, llvm::MemoryBuffer::getMemBuffer(""))); + FileToReplaces[Path1] = Replacements(); + FileToReplaces[Path2] = Replacements(); + FileToReplaces = groupReplacementsByFile(FileMgr, FileToReplaces); + EXPECT_EQ(1u, FileToReplaces.size()); + EXPECT_EQ(Path1, FileToReplaces.begin()->first); +} + +TEST(DeduplicateByFileTest, NonExistingFilePath) { + std::map<std::string, Replacements> FileToReplaces; + llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS( + new vfs::InMemoryFileSystem()); + FileManager FileMgr(FileSystemOptions(), VFS); +#if !defined(LLVM_ON_WIN32) + StringRef Path1 = "./a/b/c.h"; + StringRef Path2 = "a/b/c.h"; +#else + StringRef Path1 = ".\\a\\b\\c.h"; + StringRef Path2 = "a\\b\\c.h"; +#endif + FileToReplaces[Path1] = Replacements(); + FileToReplaces[Path2] = Replacements(); + FileToReplaces = groupReplacementsByFile(FileMgr, FileToReplaces); + EXPECT_TRUE(FileToReplaces.empty()); } } // end namespace tooling diff --git a/unittests/Tooling/ReplacementTest.h b/unittests/Tooling/ReplacementTest.h new file mode 100644 index 000000000000..b6fe5c79b7be --- /dev/null +++ b/unittests/Tooling/ReplacementTest.h @@ -0,0 +1,56 @@ +//===- unittest/Tooling/ReplacementTest.h - Replacements related test------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines utility class and function for Replacement related tests. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_UNITTESTS_TOOLING_REPLACEMENTTESTBASE_H +#define LLVM_CLANG_UNITTESTS_TOOLING_REPLACEMENTTESTBASE_H + +#include "RewriterTestContext.h" +#include "clang/Tooling/Core/Replacement.h" +#include "gtest/gtest.h" + +namespace clang { +namespace tooling { + +/// \brief Converts a set of replacements to Replacements class. +/// \return A Replacements class containing \p Replaces on success; otherwise, +/// an empty Replacements is returned. +inline tooling::Replacements +toReplacements(const std::set<tooling::Replacement> &Replaces) { + tooling::Replacements Result; + for (const auto &R : Replaces) { + auto Err = Result.add(R); + EXPECT_TRUE(!Err); + if (Err) { + llvm::errs() << llvm::toString(std::move(Err)) << "\n"; + return tooling::Replacements(); + } + } + return Result; +} + +/// \brief A utility class for replacement related tests. +class ReplacementTest : public ::testing::Test { +protected: + tooling::Replacement createReplacement(SourceLocation Start, unsigned Length, + llvm::StringRef ReplacementText) { + return tooling::Replacement(Context.Sources, Start, Length, + ReplacementText); + } + + RewriterTestContext Context; +}; + +} // namespace tooling +} // namespace clang + +#endif // LLVM_CLANG_UNITTESTS_TOOLING_REPLACEMENTTESTBASE_H diff --git a/unittests/Tooling/RewriterTest.cpp b/unittests/Tooling/RewriterTest.cpp index e8afedb01174..4305d421e1cb 100644 --- a/unittests/Tooling/RewriterTest.cpp +++ b/unittests/Tooling/RewriterTest.cpp @@ -39,8 +39,11 @@ TEST(Rewriter, ContinuesOverwritingFilesOnError) { TEST(Rewriter, AdjacentInsertAndDelete) { Replacements Replaces; - Replaces.insert(Replacement("<file>", 6, 6, "")); - Replaces.insert(Replacement("<file>", 6, 0, "replaced\n")); + auto Err = Replaces.add(Replacement("<file>", 6, 6, "")); + EXPECT_TRUE(!Err); + Replaces = + Replaces.merge(Replacements(Replacement("<file>", 6, 0, "replaced\n"))); + auto Rewritten = applyAllReplacements("line1\nline2\nline3\nline4", Replaces); EXPECT_TRUE(static_cast<bool>(Rewritten)); EXPECT_EQ("line1\nreplaced\nline3\nline4", *Rewritten); diff --git a/unittests/Tooling/TestVisitor.h b/unittests/Tooling/TestVisitor.h index f4a00394487b..a762ec8b1453 100644 --- a/unittests/Tooling/TestVisitor.h +++ b/unittests/Tooling/TestVisitor.h @@ -43,6 +43,7 @@ public: Lang_C, Lang_CXX98, Lang_CXX11, + Lang_CXX14, Lang_OBJC, Lang_OBJCXX11, Lang_CXX = Lang_CXX98 @@ -55,6 +56,7 @@ public: case Lang_C: Args.push_back("-std=c99"); break; case Lang_CXX98: Args.push_back("-std=c++98"); break; case Lang_CXX11: Args.push_back("-std=c++11"); break; + case Lang_CXX14: Args.push_back("-std=c++14"); break; case Lang_OBJC: Args.push_back("-ObjC"); break; case Lang_OBJCXX11: Args.push_back("-ObjC++"); @@ -127,9 +129,12 @@ public: /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'. /// /// Any number of expected matches can be set by calling this repeatedly. - /// Each is expected to be matched exactly once. - void ExpectMatch(Twine Match, unsigned Line, unsigned Column) { - ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column)); + /// Each is expected to be matched 'Times' number of times. (This is useful in + /// cases in which different AST nodes can match at the same source code + /// location.) + void ExpectMatch(Twine Match, unsigned Line, unsigned Column, + unsigned Times = 1) { + ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column, Times)); } /// \brief Checks that all expected matches have been found. @@ -200,14 +205,17 @@ protected: }; struct ExpectedMatch { - ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber) - : Candidate(Name, LineNumber, ColumnNumber), Found(false) {} + ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber, + unsigned Times) + : Candidate(Name, LineNumber, ColumnNumber), TimesExpected(Times), + TimesSeen(0) {} void UpdateFor(StringRef Name, FullSourceLoc Location, SourceManager &SM) { if (Candidate.Matches(Name, Location)) { - EXPECT_TRUE(!Found); - Found = true; - } else if (!Found && Candidate.PartiallyMatches(Name, Location)) { + EXPECT_LT(TimesSeen, TimesExpected); + ++TimesSeen; + } else if (TimesSeen < TimesExpected && + Candidate.PartiallyMatches(Name, Location)) { llvm::raw_string_ostream Stream(PartialMatches); Stream << ", partial match: \"" << Name << "\" at "; Location.print(Stream, SM); @@ -215,7 +223,7 @@ protected: } void ExpectFound() const { - EXPECT_TRUE(Found) + EXPECT_EQ(TimesExpected, TimesSeen) << "Expected \"" << Candidate.ExpectedName << "\" at " << Candidate.LineNumber << ":" << Candidate.ColumnNumber << PartialMatches; @@ -223,7 +231,8 @@ protected: MatchCandidate Candidate; std::string PartialMatches; - bool Found; + unsigned TimesExpected; + unsigned TimesSeen; }; std::vector<MatchCandidate> DisallowedMatches; diff --git a/unittests/Tooling/ToolingTest.cpp b/unittests/Tooling/ToolingTest.cpp index 10ac0c33ed85..68b5ed21059b 100644 --- a/unittests/Tooling/ToolingTest.cpp +++ b/unittests/Tooling/ToolingTest.cpp @@ -18,8 +18,9 @@ #include "clang/Tooling/Tooling.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Config/llvm-config.h" -#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" #include "gtest/gtest.h" #include <algorithm> #include <string> @@ -331,6 +332,44 @@ TEST(runToolOnCodeWithArgs, TestNoDepFile) { EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str())); } +struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction { + CheckColoredDiagnosticsAction(bool ShouldShowColor) + : ShouldShowColor(ShouldShowColor) {} + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, + StringRef) override { + if (Compiler.getDiagnosticOpts().ShowColors != ShouldShowColor) + Compiler.getDiagnostics().Report( + Compiler.getDiagnostics().getCustomDiagID( + DiagnosticsEngine::Fatal, + "getDiagnosticOpts().ShowColors != ShouldShowColor")); + return llvm::make_unique<ASTConsumer>(); + } + +private: + bool ShouldShowColor = true; +}; + +TEST(runToolOnCodeWithArgs, DiagnosticsColor) { + + EXPECT_TRUE(runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(true), "", + {"-fcolor-diagnostics"})); + EXPECT_TRUE(runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(false), + "", {"-fno-color-diagnostics"})); + EXPECT_TRUE( + runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(true), "", + {"-fno-color-diagnostics", "-fcolor-diagnostics"})); + EXPECT_TRUE( + runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(false), "", + {"-fcolor-diagnostics", "-fno-color-diagnostics"})); + EXPECT_TRUE(runToolOnCodeWithArgs( + new CheckColoredDiagnosticsAction(true), "", + {"-fno-color-diagnostics", "-fdiagnostics-color=always"})); + + // Check that this test would fail if ShowColors is not what it should. + EXPECT_FALSE(runToolOnCodeWithArgs(new CheckColoredDiagnosticsAction(false), + "", {"-fcolor-diagnostics"})); +} + TEST(ClangToolTest, ArgumentAdjusters) { FixedCompilationDatabase Compilations("/", std::vector<std::string>()); diff --git a/unittests/libclang/LibclangTest.cpp b/unittests/libclang/LibclangTest.cpp index d5c78272af54..f2a96d6be6c1 100644 --- a/unittests/libclang/LibclangTest.cpp +++ b/unittests/libclang/LibclangTest.cpp @@ -14,6 +14,9 @@ #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" #include <fstream> +#include <functional> +#include <map> +#include <memory> #include <set> #define DEBUG_TYPE "libclang-test" @@ -349,21 +352,25 @@ TEST(libclang, ModuleMapDescriptor) { clang_ModuleMapDescriptor_dispose(MMD); } -class LibclangReparseTest : public ::testing::Test { +class LibclangParseTest : public ::testing::Test { std::set<std::string> Files; + typedef std::unique_ptr<std::string> fixed_addr_string; + std::map<fixed_addr_string, fixed_addr_string> UnsavedFileContents; public: std::string TestDir; CXIndex Index; CXTranslationUnit ClangTU; unsigned TUFlags; + std::vector<CXUnsavedFile> UnsavedFiles; void SetUp() override { llvm::SmallString<256> Dir; ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("libclang-test", Dir)); TestDir = Dir.str(); TUFlags = CXTranslationUnit_DetailedPreprocessingRecord | - clang_defaultEditingTranslationUnitOptions(); + clang_defaultEditingTranslationUnitOptions(); Index = clang_createIndex(0, 0); + ClangTU = nullptr; } void TearDown() override { clang_disposeTranslationUnit(ClangTU); @@ -384,6 +391,77 @@ public: OS << Contents; assert(OS.good()); } + void MapUnsavedFile(std::string Filename, const std::string &Contents) { + if (!llvm::sys::path::is_absolute(Filename)) { + llvm::SmallString<256> Path(TestDir); + llvm::sys::path::append(Path, Filename); + Filename = Path.str(); + } + auto it = UnsavedFileContents.insert(std::make_pair( + fixed_addr_string(new std::string(Filename)), + fixed_addr_string(new std::string(Contents)))); + UnsavedFiles.push_back({ + it.first->first->c_str(), // filename + it.first->second->c_str(), // contents + it.first->second->size() // length + }); + } + template<typename F> + void Traverse(const F &TraversalFunctor) { + CXCursor TuCursor = clang_getTranslationUnitCursor(ClangTU); + std::reference_wrapper<const F> FunctorRef = std::cref(TraversalFunctor); + clang_visitChildren(TuCursor, + &TraverseStateless<std::reference_wrapper<const F>>, + &FunctorRef); + } +private: + template<typename TState> + static CXChildVisitResult TraverseStateless(CXCursor cx, CXCursor parent, + CXClientData data) { + TState *State = static_cast<TState*>(data); + return State->get()(cx, parent); + } +}; + +TEST_F(LibclangParseTest, AllSkippedRanges) { + std::string Header = "header.h", Main = "main.cpp"; + WriteFile(Header, + "#ifdef MANGOS\n" + "printf(\"mmm\");\n" + "#endif"); + WriteFile(Main, + "#include \"header.h\"\n" + "#ifdef KIWIS\n" + "printf(\"mmm!!\");\n" + "#endif"); + + ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, + nullptr, 0, TUFlags); + + CXSourceRangeList *Ranges = clang_getAllSkippedRanges(ClangTU); + EXPECT_EQ(2U, Ranges->count); + + CXSourceLocation cxl; + unsigned line; + cxl = clang_getRangeStart(Ranges->ranges[0]); + clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); + EXPECT_EQ(1U, line); + cxl = clang_getRangeEnd(Ranges->ranges[0]); + clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); + EXPECT_EQ(3U, line); + + cxl = clang_getRangeStart(Ranges->ranges[1]); + clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); + EXPECT_EQ(2U, line); + cxl = clang_getRangeEnd(Ranges->ranges[1]); + clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr); + EXPECT_EQ(4U, line); + + clang_disposeSourceRangeList(Ranges); +} + +class LibclangReparseTest : public LibclangParseTest { +public: void DisplayDiagnostics() { unsigned NumDiagnostics = clang_getNumDiagnostics(ClangTU); for (unsigned i = 0; i < NumDiagnostics; ++i) { |