diff options
Diffstat (limited to 'unittests')
48 files changed, 1370 insertions, 1855 deletions
diff --git a/unittests/AST/ASTContextParentMapTest.cpp b/unittests/AST/ASTContextParentMapTest.cpp index a39189620b..f06f32bf76 100644 --- a/unittests/AST/ASTContextParentMapTest.cpp +++ b/unittests/AST/ASTContextParentMapTest.cpp @@ -17,6 +17,9 @@ #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Tooling/Tooling.h" #include "gtest/gtest.h" +#include "gmock/gmock.h" + +using testing::ElementsAre; namespace clang { namespace ast_matchers { @@ -78,5 +81,30 @@ TEST(GetParents, ReturnsMultipleParentsInTemplateInstantiations) { hasAncestor(cxxRecordDecl(unless(isTemplateInstantiation()))))))); } +TEST(GetParents, RespectsTraversalScope) { + auto AST = + tooling::buildASTFromCode("struct foo { int bar; };", "foo.cpp", + std::make_shared<PCHContainerOperations>()); + auto &Ctx = AST->getASTContext(); + auto &TU = *Ctx.getTranslationUnitDecl(); + auto &Foo = *TU.lookup(&Ctx.Idents.get("foo")).front(); + auto &Bar = *cast<DeclContext>(Foo).lookup(&Ctx.Idents.get("bar")).front(); + + using ast_type_traits::DynTypedNode; + // Initially, scope is the whole TU. + EXPECT_THAT(Ctx.getParents(Bar), ElementsAre(DynTypedNode::create(Foo))); + EXPECT_THAT(Ctx.getParents(Foo), ElementsAre(DynTypedNode::create(TU))); + + // Restrict the scope, now some parents are gone. + Ctx.setTraversalScope({&Foo}); + EXPECT_THAT(Ctx.getParents(Bar), ElementsAre(DynTypedNode::create(Foo))); + EXPECT_THAT(Ctx.getParents(Foo), ElementsAre()); + + // Reset the scope, we get back the original results. + Ctx.setTraversalScope({&TU}); + EXPECT_THAT(Ctx.getParents(Bar), ElementsAre(DynTypedNode::create(Foo))); + EXPECT_THAT(Ctx.getParents(Foo), ElementsAre(DynTypedNode::create(TU))); +} + } // end namespace ast_matchers } // end namespace clang diff --git a/unittests/AST/ASTImporterTest.cpp b/unittests/AST/ASTImporterTest.cpp index 366ff7f0e0..0450cb41a4 100644 --- a/unittests/AST/ASTImporterTest.cpp +++ b/unittests/AST/ASTImporterTest.cpp @@ -37,10 +37,10 @@ createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, std::unique_ptr<llvm::MemoryBuffer> &&Buffer) { assert(ToAST); ASTContext &ToCtx = ToAST->getASTContext(); - auto *OFS = static_cast<vfs::OverlayFileSystem *>( + auto *OFS = static_cast<llvm::vfs::OverlayFileSystem *>( ToCtx.getSourceManager().getFileManager().getVirtualFileSystem().get()); - auto *MFS = - static_cast<vfs::InMemoryFileSystem *>(OFS->overlays_begin()->get()); + auto *MFS = static_cast<llvm::vfs::InMemoryFileSystem *>( + OFS->overlays_begin()->get()); MFS->addFile(FileName, 0, std::move(Buffer)); } @@ -140,6 +140,7 @@ class TestImportBase : public ParameterizedTestsFixture { if (!Imported) return testing::AssertionFailure() << "Import failed, nullptr returned!"; + return Verifier.match(Imported, WrapperMatcher); } @@ -502,7 +503,6 @@ TEST_P(CanonicalRedeclChain, ShouldBeSameForAllDeclInTheChain) { EXPECT_THAT(RedeclsD1, ::testing::ContainerEq(RedeclsD2)); } - TEST_P(ImportExpr, ImportStringLiteral) { MatchVerifier<Decl> Verifier; testImport( @@ -719,19 +719,18 @@ TEST_P(ImportExpr, ImportDesignatedInitExpr) { initListExpr( has(designatedInitExpr( designatorCountIs(2), - has(floatLiteral(equals(1.0))), - has(integerLiteral(equals(2))))), + hasDescendant(floatLiteral(equals(1.0))), + hasDescendant(integerLiteral(equals(2))))), has(designatedInitExpr( designatorCountIs(2), - has(floatLiteral(equals(2.0))), - has(integerLiteral(equals(2))))), + hasDescendant(floatLiteral(equals(2.0))), + hasDescendant(integerLiteral(equals(2))))), has(designatedInitExpr( designatorCountIs(2), - has(floatLiteral(equals(1.0))), - has(integerLiteral(equals(0))))))))); + hasDescendant(floatLiteral(equals(1.0))), + hasDescendant(integerLiteral(equals(0))))))))); } - TEST_P(ImportExpr, ImportPredefinedExpr) { MatchVerifier<Decl> Verifier; // __func__ expands as StringLiteral("declToImport") @@ -2482,7 +2481,7 @@ TEST_P(ImportFriendFunctions, ImportFriendChangesLookup) { LookupRes = ToTU->noload_lookup(ToName); EXPECT_EQ(LookupRes.size(), 1u); EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u); - + auto *ToFriendF = cast<FunctionDecl>(Import(FromFriendF, Lang_CXX)); LookupRes = ToTU->noload_lookup(ToName); EXPECT_EQ(LookupRes.size(), 1u); @@ -2758,7 +2757,7 @@ TEST_P(ASTImporterTestBase, ImportOfEquivalentRecord) { ToR2 = Import(FromR, Lang_CXX); } - + EXPECT_EQ(ToR1, ToR2); } @@ -2922,8 +2921,9 @@ TEST_P(ASTImporterTestBase, ImportUnnamedFieldsInCorrectOrder) { ASSERT_FALSE(FromField->getDeclName()); auto *ToField = cast_or_null<FieldDecl>(Import(FromField, Lang_CXX11)); EXPECT_TRUE(ToField); - unsigned ToIndex = ASTImporter::getFieldIndex(ToField); - EXPECT_EQ(ToIndex, FromIndex + 1); + Optional<unsigned> ToIndex = ASTImporter::getFieldIndex(ToField); + EXPECT_TRUE(ToIndex); + EXPECT_EQ(*ToIndex, FromIndex); ++FromIndex; } @@ -3725,6 +3725,77 @@ TEST_P(ImportFunctionTemplateSpecializations, DefinitionThenPrototype) { EXPECT_EQ(To1->getPreviousDecl(), To0); } +TEST_P(ASTImporterTestBase, + ImportShouldNotReportFalseODRErrorWhenRecordIsBeingDefined) { + { + Decl *FromTU = getTuDecl( + R"( + template <typename T> + struct B; + )", + Lang_CXX, "input0.cc"); + auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match( + FromTU, classTemplateDecl(hasName("B"))); + + Import(FromD, Lang_CXX); + } + + { + Decl *FromTU = getTuDecl( + R"( + template <typename T> + struct B { + void f(); + B* b; + }; + )", + Lang_CXX, "input1.cc"); + FunctionDecl *FromD = FirstDeclMatcher<FunctionDecl>().match( + FromTU, functionDecl(hasName("f"))); + Import(FromD, Lang_CXX); + auto *FromCTD = FirstDeclMatcher<ClassTemplateDecl>().match( + FromTU, classTemplateDecl(hasName("B"))); + auto *ToCTD = cast<ClassTemplateDecl>(Import(FromCTD, Lang_CXX)); + EXPECT_TRUE(ToCTD->isThisDeclarationADefinition()); + + // We expect no (ODR) warning during the import. + auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings()); + } +} + +TEST_P(ASTImporterTestBase, ImportingTypedefShouldImportTheCompleteType) { + // We already have an incomplete underlying type in the "To" context. + auto Code = + R"( + template <typename T> + struct S { + void foo(); + }; + using U = S<int>; + )"; + Decl *ToTU = getToTuDecl(Code, Lang_CXX11); + auto *ToD = FirstDeclMatcher<TypedefNameDecl>().match(ToTU, + typedefNameDecl(hasName("U"))); + ASSERT_TRUE(ToD->getUnderlyingType()->isIncompleteType()); + + // The "From" context has the same typedef, but the underlying type is + // complete this time. + Decl *FromTU = getTuDecl(std::string(Code) + + R"( + void foo(U* u) { + u->foo(); + } + )", Lang_CXX11); + auto *FromD = FirstDeclMatcher<TypedefNameDecl>().match(FromTU, + typedefNameDecl(hasName("U"))); + ASSERT_FALSE(FromD->getUnderlyingType()->isIncompleteType()); + + // The imported type should be complete. + auto *ImportedD = cast<TypedefNameDecl>(Import(FromD, Lang_CXX11)); + EXPECT_FALSE(ImportedD->getUnderlyingType()->isIncompleteType()); +} + INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest, ::testing::Values(ArgVector()), ); diff --git a/unittests/AST/CMakeLists.txt b/unittests/AST/CMakeLists.txt index 776e3dcf6d..6621ce681b 100644 --- a/unittests/AST/CMakeLists.txt +++ b/unittests/AST/CMakeLists.txt @@ -28,5 +28,6 @@ target_link_libraries(ASTTests clangASTMatchers clangBasic clangFrontend + clangSerialization clangTooling ) diff --git a/unittests/AST/CommentTextTest.cpp b/unittests/AST/CommentTextTest.cpp index 04475f1c10..5fb779535f 100644 --- a/unittests/AST/CommentTextTest.cpp +++ b/unittests/AST/CommentTextTest.cpp @@ -20,8 +20,8 @@ #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" -#include "clang/Basic/VirtualFileSystem.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" #include <gtest/gtest.h> namespace clang { diff --git a/unittests/AST/StmtPrinterTest.cpp b/unittests/AST/StmtPrinterTest.cpp index a0644401a7..40da6ca6bb 100644 --- a/unittests/AST/StmtPrinterTest.cpp +++ b/unittests/AST/StmtPrinterTest.cpp @@ -106,64 +106,59 @@ PrintedStmtMatches(StringRef Code, const std::vector<std::string> &Args, return ::testing::AssertionSuccess(); } -::testing::AssertionResult -PrintedStmtCXX98Matches(StringRef Code, const StatementMatcher &NodeMatch, - StringRef ExpectedPrinted) { - std::vector<std::string> Args; - Args.push_back("-std=c++98"); - Args.push_back("-Wno-unused-value"); - return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted); -} +enum class StdVer { CXX98, CXX11, CXX14, CXX17, CXX2a }; -::testing::AssertionResult PrintedStmtCXX98Matches( - StringRef Code, - StringRef ContainingFunction, - StringRef ExpectedPrinted) { - std::vector<std::string> Args; - Args.push_back("-std=c++98"); - Args.push_back("-Wno-unused-value"); - return PrintedStmtMatches(Code, - Args, - functionDecl(hasName(ContainingFunction), - has(compoundStmt(has(stmt().bind("id"))))), - ExpectedPrinted); +DeclarationMatcher FunctionBodyMatcher(StringRef ContainingFunction) { + return functionDecl(hasName(ContainingFunction), + has(compoundStmt(has(stmt().bind("id"))))); } +template <typename T> ::testing::AssertionResult -PrintedStmtCXX11Matches(StringRef Code, const StatementMatcher &NodeMatch, - StringRef ExpectedPrinted, - PolicyAdjusterType PolicyAdjuster = None) { - std::vector<std::string> Args; - Args.push_back("-std=c++11"); - Args.push_back("-Wno-unused-value"); +PrintedStmtCXXMatches(StdVer Standard, StringRef Code, const T &NodeMatch, + StringRef ExpectedPrinted, + PolicyAdjusterType PolicyAdjuster = None) { + const char *StdOpt; + switch (Standard) { + case StdVer::CXX98: StdOpt = "-std=c++98"; break; + case StdVer::CXX11: StdOpt = "-std=c++11"; break; + case StdVer::CXX14: StdOpt = "-std=c++14"; break; + case StdVer::CXX17: StdOpt = "-std=c++17"; break; + case StdVer::CXX2a: StdOpt = "-std=c++2a"; break; + } + + std::vector<std::string> Args = { + StdOpt, + "-Wno-unused-value", + }; return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted, PolicyAdjuster); } -::testing::AssertionResult PrintedStmtMSMatches( - StringRef Code, - StringRef ContainingFunction, - StringRef ExpectedPrinted) { - std::vector<std::string> Args; - Args.push_back("-target"); - Args.push_back("i686-pc-win32"); - Args.push_back("-std=c++98"); - Args.push_back("-fms-extensions"); - Args.push_back("-Wno-unused-value"); - return PrintedStmtMatches(Code, - Args, - functionDecl(hasName(ContainingFunction), - has(compoundStmt(has(stmt().bind("id"))))), - ExpectedPrinted); +template <typename T> +::testing::AssertionResult +PrintedStmtMSMatches(StringRef Code, const T &NodeMatch, + StringRef ExpectedPrinted, + PolicyAdjusterType PolicyAdjuster = None) { + std::vector<std::string> Args = { + "-std=c++98", + "-target", "i686-pc-win32", + "-fms-extensions", + "-Wno-unused-value", + }; + return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted, + PolicyAdjuster); } +template <typename T> ::testing::AssertionResult -PrintedStmtObjCMatches(StringRef Code, const StatementMatcher &NodeMatch, +PrintedStmtObjCMatches(StringRef Code, const T &NodeMatch, StringRef ExpectedPrinted, PolicyAdjusterType PolicyAdjuster = None) { - std::vector<std::string> Args; - Args.push_back("-ObjC"); - Args.push_back("-fobjc-runtime=macosx-10.12.0"); + std::vector<std::string> Args = { + "-ObjC", + "-fobjc-runtime=macosx-10.12.0", + }; return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted, PolicyAdjuster); } @@ -171,13 +166,13 @@ PrintedStmtObjCMatches(StringRef Code, const StatementMatcher &NodeMatch, } // unnamed namespace TEST(StmtPrinter, TestIntegerLiteral) { - ASSERT_TRUE(PrintedStmtCXX98Matches( + ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX98, "void A() {" " 1, -1, 1U, 1u," " 1L, 1l, -1L, 1UL, 1ul," " 1LL, -1LL, 1ULL;" "}", - "A", + FunctionBodyMatcher("A"), "1 , -1 , 1U , 1U , " "1L , 1L , -1L , 1UL , 1UL , " "1LL , -1LL , 1ULL")); @@ -192,7 +187,7 @@ TEST(StmtPrinter, TestMSIntegerLiteral) { " 1i32, -1i32, 1ui32, " " 1i64, -1i64, 1ui64;" "}", - "A", + FunctionBodyMatcher("A"), "1i8 , -1i8 , 1Ui8 , " "1i16 , -1i16 , 1Ui16 , " "1 , -1 , 1U , " @@ -201,15 +196,15 @@ TEST(StmtPrinter, TestMSIntegerLiteral) { } TEST(StmtPrinter, TestFloatingPointLiteral) { - ASSERT_TRUE(PrintedStmtCXX98Matches( + ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX98, "void A() { 1.0f, -1.0f, 1.0, -1.0, 1.0l, -1.0l; }", - "A", + FunctionBodyMatcher("A"), "1.F , -1.F , 1. , -1. , 1.L , -1.L")); // Should be: with semicolon } TEST(StmtPrinter, TestCXXConversionDeclImplicit) { - ASSERT_TRUE(PrintedStmtCXX98Matches( + ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX98, "struct A {" "operator void *();" "A operator&(A);" @@ -223,7 +218,7 @@ TEST(StmtPrinter, TestCXXConversionDeclImplicit) { } TEST(StmtPrinter, TestCXXConversionDeclExplicit) { - ASSERT_TRUE(PrintedStmtCXX11Matches( + ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11, "struct A {" "operator void *();" "A operator&(A);" @@ -245,12 +240,12 @@ class A { }; )"; // No implicit 'this'. - ASSERT_TRUE(PrintedStmtCXX11Matches( + ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11, CPPSource, memberExpr(anything()).bind("id"), "field", PolicyAdjusterType( [](PrintingPolicy &PP) { PP.SuppressImplicitBase = true; }))); // Print implicit 'this'. - ASSERT_TRUE(PrintedStmtCXX11Matches( + ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11, CPPSource, memberExpr(anything()).bind("id"), "this->field")); const char *ObjCSource = R"( diff --git a/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index 39a5d57729..d1f9495432 100644 --- a/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -667,6 +667,14 @@ TEST(Matcher, VarDecl_Storage) { EXPECT_TRUE(matches("void f() { static int X; }", M)); } +TEST(Matcher, VarDecl_IsStaticLocal) { + auto M = varDecl(isStaticLocal()); + EXPECT_TRUE(matches("void f() { static int X; }", M)); + EXPECT_TRUE(notMatches("static int X;", M)); + EXPECT_TRUE(notMatches("void f() { int X; }", M)); + EXPECT_TRUE(notMatches("int X;", M)); +} + TEST(Matcher, VarDecl_StorageDuration) { std::string T = "void f() { int x; static int y; } int a;static int b;extern int c;"; @@ -1378,6 +1386,10 @@ TEST(ObjCIvarRefExprMatcher, IvarExpr) { hasDeclaration(namedDecl(hasName("y")))))); } +TEST(BlockExprMatcher, BlockExpr) { + EXPECT_TRUE(matchesObjC("void f() { ^{}(); }", blockExpr())); +} + TEST(StatementCountIs, FindsNoStatementsInAnEmptyCompoundStatement) { EXPECT_TRUE(matches("void f() { }", compoundStmt(statementCountIs(0)))); diff --git a/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/unittests/ASTMatchers/ASTMatchersNodeTest.cpp index b40289fcd5..1bd4e09e77 100644 --- a/unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -199,6 +199,40 @@ TEST(Matcher, UnresolvedLookupExpr) { "-fno-delayed-template-parsing")); } +TEST(Matcher, ADLCall) { + StatementMatcher ADLMatch = callExpr(usesADL()); + StatementMatcher ADLMatchOper = cxxOperatorCallExpr(usesADL()); + auto NS_Str = R"cpp( + namespace NS { + struct X {}; + void f(X); + void operator+(X, X); + } + struct MyX {}; + void f(...); + void operator+(MyX, MyX); +)cpp"; + + auto MkStr = [&](std::string Body) -> std::string { + std::string S = NS_Str; + S += "void test_fn() { " + Body + " }"; + return S; + }; + + EXPECT_TRUE(matches(MkStr("NS::X x; f(x);"), ADLMatch)); + EXPECT_TRUE(notMatches(MkStr("NS::X x; NS::f(x);"), ADLMatch)); + EXPECT_TRUE(notMatches(MkStr("MyX x; f(x);"), ADLMatch)); + EXPECT_TRUE(notMatches(MkStr("NS::X x; using NS::f; f(x);"), ADLMatch)); + + // Operator call expressions + EXPECT_TRUE(matches(MkStr("NS::X x; x + x;"), ADLMatch)); + EXPECT_TRUE(matches(MkStr("NS::X x; x + x;"), ADLMatchOper)); + EXPECT_TRUE(notMatches(MkStr("MyX x; x + x;"), ADLMatch)); + EXPECT_TRUE(notMatches(MkStr("MyX x; x + x;"), ADLMatchOper)); + EXPECT_TRUE(matches(MkStr("NS::X x; operator+(x, x);"), ADLMatch)); + EXPECT_TRUE(notMatches(MkStr("NS::X x; NS::operator+(x, x);"), ADLMatch)); +} + TEST(Matcher, Call) { // FIXME: Do we want to overload Call() to directly take // Matcher<Decl>, too? @@ -760,23 +794,23 @@ TEST(Matcher, Initializers) { has( designatedInitExpr( designatorCountIs(2), - has(floatLiteral( + hasDescendant(floatLiteral( equals(1.0))), - has(integerLiteral( + hasDescendant(integerLiteral( equals(2))))), has( designatedInitExpr( designatorCountIs(2), - has(floatLiteral( + hasDescendant(floatLiteral( equals(2.0))), - has(integerLiteral( + hasDescendant(integerLiteral( equals(2))))), has( designatedInitExpr( designatorCountIs(2), - has(floatLiteral( + hasDescendant(floatLiteral( equals(1.0))), - has(integerLiteral( + hasDescendant(integerLiteral( equals(0))))) ))))); } @@ -1147,6 +1181,14 @@ TEST(ParenExpression, SimpleCases) { parenExpr())); } +TEST(ParenExpression, IgnoringParens) { + EXPECT_FALSE(matches("const char* str = (\"my-string\");", + implicitCastExpr(hasSourceExpression(stringLiteral())))); + EXPECT_TRUE(matches( + "const char* str = (\"my-string\");", + implicitCastExpr(hasSourceExpression(ignoringParens(stringLiteral()))))); +} + TEST(TypeMatching, MatchesTypes) { EXPECT_TRUE(matches("struct S {};", qualType().bind("loc"))); } diff --git a/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp index 713fb5a592..5f6ecc0d0b 100644 --- a/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -1574,13 +1574,16 @@ TEST(SwitchCase, MatchesEachCase) { ifStmt(has(switchStmt(forEachSwitchCase(defaultStmt())))))); EXPECT_TRUE(matches("void x() { switch(42) { case 1+1: case 4:; } }", switchStmt(forEachSwitchCase( - caseStmt(hasCaseConstant(integerLiteral())))))); + caseStmt(hasCaseConstant( + constantExpr(has(integerLiteral())))))))); EXPECT_TRUE(notMatches("void x() { switch(42) { case 1+1: case 2+2:; } }", switchStmt(forEachSwitchCase( - caseStmt(hasCaseConstant(integerLiteral())))))); + caseStmt(hasCaseConstant( + constantExpr(has(integerLiteral())))))))); EXPECT_TRUE(notMatches("void x() { switch(42) { case 1 ... 2:; } }", switchStmt(forEachSwitchCase( - caseStmt(hasCaseConstant(integerLiteral())))))); + caseStmt(hasCaseConstant( + constantExpr(has(integerLiteral())))))))); EXPECT_TRUE(matchAndVerifyResultTrue( "void x() { switch (42) { case 1: case 2: case 3: default:; } }", switchStmt(forEachSwitchCase(caseStmt().bind("x"))), diff --git a/unittests/ASTMatchers/CMakeLists.txt b/unittests/ASTMatchers/CMakeLists.txt index ae1aecf503..4e44c795f8 100644 --- a/unittests/ASTMatchers/CMakeLists.txt +++ b/unittests/ASTMatchers/CMakeLists.txt @@ -24,6 +24,7 @@ target_link_libraries(ASTMatchersTests clangASTMatchers clangBasic clangFrontend + clangSerialization clangTooling ) diff --git a/unittests/ASTMatchers/Dynamic/CMakeLists.txt b/unittests/ASTMatchers/Dynamic/CMakeLists.txt index 0fd96e97cf..07742caef6 100644 --- a/unittests/ASTMatchers/Dynamic/CMakeLists.txt +++ b/unittests/ASTMatchers/Dynamic/CMakeLists.txt @@ -15,5 +15,6 @@ target_link_libraries(DynamicASTMatchersTests clangBasic clangDynamicASTMatchers clangFrontend + clangSerialization clangTooling ) diff --git a/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/unittests/ASTMatchers/Dynamic/ParserTest.cpp index fd7bbdde4a..9e891069c8 100644 --- a/unittests/ASTMatchers/Dynamic/ParserTest.cpp +++ b/unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -148,8 +148,8 @@ TEST(ParserTest, ParseMatcher) { const uint64_t ExpectedBar = Sema.expectMatcher("Bar"); const uint64_t ExpectedBaz = Sema.expectMatcher("Baz"); Sema.parse(" Foo ( Bar ( 17), Baz( \n \"B A,Z\") ) .bind( \"Yo!\") "); - for (size_t i = 0, e = Sema.Errors.size(); i != e; ++i) { - EXPECT_EQ("", Sema.Errors[i]); + for (const auto &E : Sema.Errors) { + EXPECT_EQ("", E); } EXPECT_NE(ExpectedFoo, ExpectedBar); @@ -181,6 +181,21 @@ TEST(ParserTest, ParseMatcher) { EXPECT_EQ("Yo!", Foo.BoundID); } +TEST(ParserTest, ParseComment) { + MockSema Sema; + Sema.expectMatcher("Foo"); + Sema.parse(" Foo() # Bar() "); + for (const auto &E : Sema.Errors) { + EXPECT_EQ("", E); + } + + EXPECT_EQ(1ULL, Sema.Matchers.size()); + + Sema.parse("Foo(#) "); + + EXPECT_EQ("1:4: Error parsing matcher. Found end-of-code while looking for ')'.", Sema.Errors[1]); +} + using ast_matchers::internal::Matcher; Parser::NamedValueMap getTestNamedValues() { diff --git a/unittests/Analysis/CMakeLists.txt b/unittests/Analysis/CMakeLists.txt index 2291e6a8dd..c760ae2d82 100644 --- a/unittests/Analysis/CMakeLists.txt +++ b/unittests/Analysis/CMakeLists.txt @@ -15,5 +15,6 @@ target_link_libraries(ClangAnalysisTests clangASTMatchers clangBasic clangFrontend + clangSerialization clangTooling ) diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt index f5c96bb9b9..537f3ba5fc 100644 --- a/unittests/Basic/CMakeLists.txt +++ b/unittests/Basic/CMakeLists.txt @@ -9,7 +9,6 @@ add_clang_unittest(BasicTests FixedPointTest.cpp MemoryBufferCacheTest.cpp SourceManagerTest.cpp - VirtualFileSystemTest.cpp ) target_link_libraries(BasicTests diff --git a/unittests/Basic/FileManagerTest.cpp b/unittests/Basic/FileManagerTest.cpp index 52cb5b2f0d..c0efaf4fc4 100644 --- a/unittests/Basic/FileManagerTest.cpp +++ b/unittests/Basic/FileManagerTest.cpp @@ -10,9 +10,9 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/FileSystemStatCache.h" -#include "clang/Basic/VirtualFileSystem.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Path.h" +#include "llvm/Support/VirtualFileSystem.h" #include "gtest/gtest.h" using namespace llvm; @@ -60,8 +60,8 @@ public: // Implement FileSystemStatCache::getStat(). LookupResult getStat(StringRef Path, FileData &Data, bool isFile, - std::unique_ptr<vfs::File> *F, - vfs::FileSystem &FS) override { + std::unique_ptr<llvm::vfs::File> *F, + llvm::vfs::FileSystem &FS) override { #ifndef _WIN32 SmallString<128> NormalizedPath(Path); llvm::sys::path::native(NormalizedPath); @@ -222,6 +222,33 @@ TEST_F(FileManagerTest, getFileReturnsNULLForNonexistentFile) { EXPECT_EQ(nullptr, file); } +// When calling getFile(OpenFile=false); getFile(OpenFile=true) the file is +// opened for the second call. +TEST_F(FileManagerTest, getFileDefersOpen) { + llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS( + new llvm::vfs::InMemoryFileSystem()); + FS->addFile("/tmp/test", 0, llvm::MemoryBuffer::getMemBufferCopy("test")); + FS->addFile("/tmp/testv", 0, llvm::MemoryBuffer::getMemBufferCopy("testv")); + FileManager manager(options, FS); + + const FileEntry *file = manager.getFile("/tmp/test", /*OpenFile=*/false); + ASSERT_TRUE(file != nullptr); + ASSERT_TRUE(file->isValid()); + // "real path name" reveals whether the file was actually opened. + EXPECT_FALSE(file->isOpenForTests()); + + file = manager.getFile("/tmp/test", /*OpenFile=*/true); + ASSERT_TRUE(file != nullptr); + ASSERT_TRUE(file->isValid()); + EXPECT_TRUE(file->isOpenForTests()); + + // However we should never try to open a file previously opened as virtual. + ASSERT_TRUE(manager.getVirtualFile("/tmp/testv", 5, 0)); + ASSERT_TRUE(manager.getFile("/tmp/testv", /*OpenFile=*/false)); + file = manager.getFile("/tmp/testv", /*OpenFile=*/true); + EXPECT_FALSE(file->isOpenForTests()); +} + // The following tests apply to Unix-like system only. #ifndef _WIN32 @@ -305,8 +332,8 @@ TEST_F(FileManagerTest, makeAbsoluteUsesVFS) { #endif llvm::sys::path::append(CustomWorkingDir, "some", "weird", "path"); - auto FS = - IntrusiveRefCntPtr<vfs::InMemoryFileSystem>(new vfs::InMemoryFileSystem); + auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>( + new llvm::vfs::InMemoryFileSystem); // setCurrentworkingdirectory must finish without error. ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir)); @@ -322,4 +349,38 @@ TEST_F(FileManagerTest, makeAbsoluteUsesVFS) { EXPECT_EQ(Path, ExpectedResult); } +// getVirtualFile should always fill the real path. +TEST_F(FileManagerTest, getVirtualFileFillsRealPathName) { + SmallString<64> CustomWorkingDir; +#ifdef _WIN32 + CustomWorkingDir = "C:/"; +#else + CustomWorkingDir = "/"; +#endif + + auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>( + new llvm::vfs::InMemoryFileSystem); + // setCurrentworkingdirectory must finish without error. + ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir)); + + FileSystemOptions Opts; + FileManager Manager(Opts, FS); + + // Inject fake files into the file system. + auto statCache = llvm::make_unique<FakeStatCache>(); + statCache->InjectDirectory("/tmp", 42); + statCache->InjectFile("/tmp/test", 43); + + Manager.addStatCache(std::move(statCache)); + + // Check for real path. + const FileEntry *file = Manager.getVirtualFile("/tmp/test", 123, 1); + ASSERT_TRUE(file != nullptr); + ASSERT_TRUE(file->isValid()); + SmallString<64> ExpectedResult = CustomWorkingDir; + + llvm::sys::path::append(ExpectedResult, "tmp", "test"); + EXPECT_EQ(file->tryGetRealPathName(), ExpectedResult); +} + } // anonymous namespace diff --git a/unittests/Basic/VirtualFileSystemTest.cpp b/unittests/Basic/VirtualFileSystemTest.cpp deleted file mode 100644 index 83317e58f7..0000000000 --- a/unittests/Basic/VirtualFileSystemTest.cpp +++ /dev/null @@ -1,1618 +0,0 @@ -//===- unittests/Basic/VirtualFileSystem.cpp ---------------- VFS tests ---===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/VirtualFileSystem.h" -#include "llvm/ADT/Triple.h" -#include "llvm/Config/llvm-config.h" -#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 "gmock/gmock.h" -#include "gtest/gtest.h" -#include <map> -#include <string> - -using namespace clang; -using namespace llvm; -using llvm::sys::fs::UniqueID; - -namespace { -struct DummyFile : public vfs::File { - vfs::Status S; - explicit DummyFile(vfs::Status S) : S(S) {} - llvm::ErrorOr<vfs::Status> status() override { return S; } - llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> - getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, - bool IsVolatile) override { - llvm_unreachable("unimplemented"); - } - std::error_code close() override { return std::error_code(); } -}; - -class DummyFileSystem : public vfs::FileSystem { - int FSID; // used to produce UniqueIDs - int FileID; // used to produce UniqueIDs - std::map<std::string, vfs::Status> FilesAndDirs; - - static int getNextFSID() { - static int Count = 0; - return Count++; - } - -public: - DummyFileSystem() : FSID(getNextFSID()), FileID(0) {} - - ErrorOr<vfs::Status> status(const Twine &Path) override { - std::map<std::string, vfs::Status>::iterator I = - FilesAndDirs.find(Path.str()); - if (I == FilesAndDirs.end()) - return make_error_code(llvm::errc::no_such_file_or_directory); - return I->second; - } - ErrorOr<std::unique_ptr<vfs::File>> - openFileForRead(const Twine &Path) override { - auto S = status(Path); - if (S) - return std::unique_ptr<vfs::File>(new DummyFile{*S}); - return S.getError(); - } - llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { - return std::string(); - } - std::error_code setCurrentWorkingDirectory(const Twine &Path) override { - return std::error_code(); - } - // Map any symlink to "/symlink". - std::error_code getRealPath(const Twine &Path, - SmallVectorImpl<char> &Output) const override { - auto I = FilesAndDirs.find(Path.str()); - if (I == FilesAndDirs.end()) - return make_error_code(llvm::errc::no_such_file_or_directory); - if (I->second.isSymlink()) { - Output.clear(); - Twine("/symlink").toVector(Output); - return std::error_code(); - } - Output.clear(); - Path.toVector(Output); - return std::error_code(); - } - - struct DirIterImpl : public clang::vfs::detail::DirIterImpl { - std::map<std::string, vfs::Status> &FilesAndDirs; - std::map<std::string, vfs::Status>::iterator I; - std::string Path; - bool isInPath(StringRef S) { - if (Path.size() < S.size() && S.find(Path) == 0) { - auto LastSep = S.find_last_of('/'); - if (LastSep == Path.size() || LastSep == Path.size()-1) - return true; - } - return false; - } - DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs, - const Twine &_Path) - : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()), - Path(_Path.str()) { - for ( ; I != FilesAndDirs.end(); ++I) { - if (isInPath(I->first)) { - CurrentEntry = - vfs::directory_entry(I->second.getName(), I->second.getType()); - break; - } - } - } - std::error_code increment() override { - ++I; - for ( ; I != FilesAndDirs.end(); ++I) { - if (isInPath(I->first)) { - CurrentEntry = - vfs::directory_entry(I->second.getName(), I->second.getType()); - break; - } - } - if (I == FilesAndDirs.end()) - CurrentEntry = vfs::directory_entry(); - return std::error_code(); - } - }; - - vfs::directory_iterator dir_begin(const Twine &Dir, - std::error_code &EC) override { - return vfs::directory_iterator( - std::make_shared<DirIterImpl>(FilesAndDirs, Dir)); - } - - void addEntry(StringRef Path, const vfs::Status &Status) { - FilesAndDirs[Path] = Status; - } - - void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { - 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++), - 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++), - std::chrono::system_clock::now(), 0, 0, 0, - sys::fs::file_type::symlink_file, sys::fs::all_all); - addEntry(Path, S); - } -}; - -/// Replace back-slashes by front-slashes. -std::string getPosixPath(std::string S) { - SmallString<128> Result; - llvm::sys::path::native(S, Result, llvm::sys::path::Style::posix); - return Result.str(); -} -} // end anonymous namespace - -TEST(VirtualFileSystemTest, StatusQueries) { - IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem()); - ErrorOr<vfs::Status> Status((std::error_code())); - - D->addRegularFile("/foo"); - Status = D->status("/foo"); - ASSERT_FALSE(Status.getError()); - EXPECT_TRUE(Status->isStatusKnown()); - EXPECT_FALSE(Status->isDirectory()); - EXPECT_TRUE(Status->isRegularFile()); - EXPECT_FALSE(Status->isSymlink()); - EXPECT_FALSE(Status->isOther()); - EXPECT_TRUE(Status->exists()); - - D->addDirectory("/bar"); - Status = D->status("/bar"); - ASSERT_FALSE(Status.getError()); - EXPECT_TRUE(Status->isStatusKnown()); - EXPECT_TRUE(Status->isDirectory()); - EXPECT_FALSE(Status->isRegularFile()); - EXPECT_FALSE(Status->isSymlink()); - EXPECT_FALSE(Status->isOther()); - EXPECT_TRUE(Status->exists()); - - D->addSymlink("/baz"); - Status = D->status("/baz"); - ASSERT_FALSE(Status.getError()); - EXPECT_TRUE(Status->isStatusKnown()); - EXPECT_FALSE(Status->isDirectory()); - EXPECT_FALSE(Status->isRegularFile()); - EXPECT_TRUE(Status->isSymlink()); - EXPECT_FALSE(Status->isOther()); - EXPECT_TRUE(Status->exists()); - - EXPECT_TRUE(Status->equivalent(*Status)); - ErrorOr<vfs::Status> Status2 = D->status("/foo"); - ASSERT_FALSE(Status2.getError()); - EXPECT_FALSE(Status->equivalent(*Status2)); -} - -TEST(VirtualFileSystemTest, BaseOnlyOverlay) { - IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem()); - ErrorOr<vfs::Status> Status((std::error_code())); - EXPECT_FALSE(Status = D->status("/foo")); - - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D)); - EXPECT_FALSE(Status = O->status("/foo")); - - D->addRegularFile("/foo"); - Status = D->status("/foo"); - EXPECT_FALSE(Status.getError()); - - ErrorOr<vfs::Status> Status2((std::error_code())); - Status2 = O->status("/foo"); - EXPECT_FALSE(Status2.getError()); - EXPECT_TRUE(Status->equivalent(*Status2)); -} - -TEST(VirtualFileSystemTest, GetRealPathInOverlay) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - Lower->addRegularFile("/foo"); - Lower->addSymlink("/lower_link"); - IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); - - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(Upper); - - // Regular file. - SmallString<16> RealPath; - EXPECT_FALSE(O->getRealPath("/foo", RealPath)); - EXPECT_EQ(RealPath.str(), "/foo"); - - // Expect no error getting real path for symlink in lower overlay. - EXPECT_FALSE(O->getRealPath("/lower_link", RealPath)); - EXPECT_EQ(RealPath.str(), "/symlink"); - - // Try a non-existing link. - EXPECT_EQ(O->getRealPath("/upper_link", RealPath), - errc::no_such_file_or_directory); - - // Add a new symlink in upper. - Upper->addSymlink("/upper_link"); - EXPECT_FALSE(O->getRealPath("/upper_link", RealPath)); - EXPECT_EQ(RealPath.str(), "/symlink"); -} - -TEST(VirtualFileSystemTest, OverlayFiles) { - IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem()); - IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); - IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem()); - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Base)); - O->pushOverlay(Middle); - O->pushOverlay(Top); - - ErrorOr<vfs::Status> Status1((std::error_code())), - Status2((std::error_code())), Status3((std::error_code())), - StatusB((std::error_code())), StatusM((std::error_code())), - StatusT((std::error_code())); - - Base->addRegularFile("/foo"); - StatusB = Base->status("/foo"); - ASSERT_FALSE(StatusB.getError()); - Status1 = O->status("/foo"); - ASSERT_FALSE(Status1.getError()); - Middle->addRegularFile("/foo"); - StatusM = Middle->status("/foo"); - ASSERT_FALSE(StatusM.getError()); - Status2 = O->status("/foo"); - ASSERT_FALSE(Status2.getError()); - Top->addRegularFile("/foo"); - StatusT = Top->status("/foo"); - ASSERT_FALSE(StatusT.getError()); - Status3 = O->status("/foo"); - ASSERT_FALSE(Status3.getError()); - - EXPECT_TRUE(Status1->equivalent(*StatusB)); - EXPECT_TRUE(Status2->equivalent(*StatusM)); - EXPECT_TRUE(Status3->equivalent(*StatusT)); - - EXPECT_FALSE(Status1->equivalent(*Status2)); - EXPECT_FALSE(Status2->equivalent(*Status3)); - EXPECT_FALSE(Status1->equivalent(*Status3)); -} - -TEST(VirtualFileSystemTest, OverlayDirsNonMerged) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(Upper); - - Lower->addDirectory("/lower-only"); - Upper->addDirectory("/upper-only"); - - // non-merged paths should be the same - ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only"); - ASSERT_FALSE(Status1.getError()); - ErrorOr<vfs::Status> Status2 = O->status("/lower-only"); - ASSERT_FALSE(Status2.getError()); - EXPECT_TRUE(Status1->equivalent(*Status2)); - - Status1 = Upper->status("/upper-only"); - ASSERT_FALSE(Status1.getError()); - Status2 = O->status("/upper-only"); - ASSERT_FALSE(Status2.getError()); - EXPECT_TRUE(Status1->equivalent(*Status2)); -} - -TEST(VirtualFileSystemTest, MergedDirPermissions) { - // merged directories get the permissions of the upper dir - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(Upper); - - ErrorOr<vfs::Status> Status((std::error_code())); - Lower->addDirectory("/both", sys::fs::owner_read); - Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read); - Status = O->status("/both"); - ASSERT_FALSE(Status.getError()); - EXPECT_EQ(0740, Status->getPermissions()); - - // permissions (as usual) are not recursively applied - Lower->addRegularFile("/both/foo", sys::fs::owner_read); - Upper->addRegularFile("/both/bar", sys::fs::owner_write); - Status = O->status("/both/foo"); - ASSERT_FALSE( Status.getError()); - EXPECT_EQ(0400, Status->getPermissions()); - Status = O->status("/both/bar"); - ASSERT_FALSE(Status.getError()); - EXPECT_EQ(0200, Status->getPermissions()); -} - -namespace { -struct ScopedDir { - SmallString<128> Path; - ScopedDir(const Twine &Name, bool Unique=false) { - std::error_code EC; - if (Unique) { - EC = llvm::sys::fs::createUniqueDirectory(Name, Path); - } else { - Path = Name.str(); - EC = llvm::sys::fs::create_directory(Twine(Path)); - } - if (EC) - Path = ""; - EXPECT_FALSE(EC); - } - ~ScopedDir() { - if (Path != "") { - EXPECT_FALSE(llvm::sys::fs::remove(Path.str())); - } - } - operator StringRef() { return Path.str(); } -}; - -struct ScopedLink { - SmallString<128> Path; - ScopedLink(const Twine &To, const Twine &From) { - Path = From.str(); - std::error_code EC = sys::fs::create_link(To, From); - if (EC) - Path = ""; - EXPECT_FALSE(EC); - } - ~ScopedLink() { - if (Path != "") { - EXPECT_FALSE(llvm::sys::fs::remove(Path.str())); - } - } - operator StringRef() { return Path.str(); } -}; -} // end anonymous namespace - -TEST(VirtualFileSystemTest, BasicRealFSIteration) { - ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true); - IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); - - std::error_code EC; - vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC); - ASSERT_FALSE(EC); - EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty - - ScopedDir _a(TestDirectory+"/a"); - ScopedDir _ab(TestDirectory+"/a/b"); - ScopedDir _c(TestDirectory+"/c"); - ScopedDir _cd(TestDirectory+"/c/d"); - - I = FS->dir_begin(Twine(TestDirectory), EC); - ASSERT_FALSE(EC); - ASSERT_NE(vfs::directory_iterator(), I); - // Check either a or c, since we can't rely on the iteration order. - EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c")); - I.increment(EC); - ASSERT_FALSE(EC); - ASSERT_NE(vfs::directory_iterator(), I); - EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c")); - I.increment(EC); - EXPECT_EQ(vfs::directory_iterator(), I); -} - -#ifdef LLVM_ON_UNIX -TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) { - ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); - IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); - - ScopedLink _a("no_such_file", TestDirectory + "/a"); - ScopedDir _b(TestDirectory + "/b"); - ScopedLink _c("no_such_file", TestDirectory + "/c"); - - std::error_code EC; - for (vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC), E; - I != E; I.increment(EC)) { - // Skip broken symlinks. - auto EC2 = std::make_error_code(std::errc::no_such_file_or_directory); - if (EC == EC2) { - EC.clear(); - continue; - } - // For bot debugging. - if (EC) { - outs() << "Error code found:\n" - << "EC value: " << EC.value() << "\n" - << "EC category: " << EC.category().name() - << "EC message: " << EC.message() << "\n"; - - outs() << "Error code tested for:\n" - << "EC value: " << EC2.value() << "\n" - << "EC category: " << EC2.category().name() - << "EC message: " << EC2.message() << "\n"; - } - ASSERT_FALSE(EC); - EXPECT_TRUE(I->path() == _b); - } -} -#endif - -TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) { - ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true); - IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); - - std::error_code EC; - auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); - ASSERT_FALSE(EC); - EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty - - ScopedDir _a(TestDirectory+"/a"); - ScopedDir _ab(TestDirectory+"/a/b"); - ScopedDir _c(TestDirectory+"/c"); - ScopedDir _cd(TestDirectory+"/c/d"); - - I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); - ASSERT_FALSE(EC); - ASSERT_NE(vfs::recursive_directory_iterator(), I); - - std::vector<std::string> Contents; - for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; - I.increment(EC)) { - Contents.push_back(I->path()); - } - - // Check contents, which may be in any order - EXPECT_EQ(4U, Contents.size()); - int Counts[4] = { 0, 0, 0, 0 }; - for (const std::string &Name : Contents) { - ASSERT_FALSE(Name.empty()); - int Index = Name[Name.size()-1] - 'a'; - ASSERT_TRUE(Index >= 0 && Index < 4); - Counts[Index]++; - } - EXPECT_EQ(1, Counts[0]); // a - EXPECT_EQ(1, Counts[1]); // b - EXPECT_EQ(1, Counts[2]); // c - EXPECT_EQ(1, Counts[3]); // d -} - -#ifdef LLVM_ON_UNIX -TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) { - ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true); - IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); - - ScopedLink _a("no_such_file", TestDirectory + "/a"); - ScopedDir _b(TestDirectory + "/b"); - ScopedLink _ba("no_such_file", TestDirectory + "/b/a"); - ScopedDir _bb(TestDirectory + "/b/b"); - ScopedLink _bc("no_such_file", TestDirectory + "/b/c"); - ScopedLink _c("no_such_file", TestDirectory + "/c"); - ScopedDir _d(TestDirectory + "/d"); - ScopedDir _dd(TestDirectory + "/d/d"); - ScopedDir _ddd(TestDirectory + "/d/d/d"); - ScopedLink _e("no_such_file", TestDirectory + "/e"); - - std::vector<StringRef> ExpectedBrokenSymlinks = {_a, _ba, _bc, _c, _e}; - std::vector<StringRef> ExpectedNonBrokenSymlinks = {_b, _bb, _d, _dd, _ddd}; - std::vector<std::string> VisitedBrokenSymlinks; - std::vector<std::string> VisitedNonBrokenSymlinks; - std::error_code EC; - for (vfs::recursive_directory_iterator I(*FS, Twine(TestDirectory), EC), E; - I != E; I.increment(EC)) { - auto EC2 = std::make_error_code(std::errc::no_such_file_or_directory); - if (EC == EC2) { - VisitedBrokenSymlinks.push_back(I->path()); - continue; - } - // For bot debugging. - if (EC) { - outs() << "Error code found:\n" - << "EC value: " << EC.value() << "\n" - << "EC category: " << EC.category().name() - << "EC message: " << EC.message() << "\n"; - - outs() << "Error code tested for:\n" - << "EC value: " << EC2.value() << "\n" - << "EC category: " << EC2.category().name() - << "EC message: " << EC2.message() << "\n"; - } - ASSERT_FALSE(EC); - VisitedNonBrokenSymlinks.push_back(I->path()); - } - - // Check visited file names. - std::sort(VisitedBrokenSymlinks.begin(), VisitedBrokenSymlinks.end()); - std::sort(VisitedNonBrokenSymlinks.begin(), VisitedNonBrokenSymlinks.end()); - EXPECT_EQ(ExpectedBrokenSymlinks.size(), VisitedBrokenSymlinks.size()); - EXPECT_TRUE(std::equal(VisitedBrokenSymlinks.begin(), - VisitedBrokenSymlinks.end(), - ExpectedBrokenSymlinks.begin())); - EXPECT_EQ(ExpectedNonBrokenSymlinks.size(), VisitedNonBrokenSymlinks.size()); - EXPECT_TRUE(std::equal(VisitedNonBrokenSymlinks.begin(), - VisitedNonBrokenSymlinks.end(), - ExpectedNonBrokenSymlinks.begin())); -} -#endif - -template <typename DirIter> -static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) { - std::error_code EC; - SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end()); - SmallVector<std::string, 4> InputToCheck; - - // Do not rely on iteration order to check for contents, sort both - // content vectors before comparison. - for (DirIter E; !EC && I != E; I.increment(EC)) - InputToCheck.push_back(I->path()); - - llvm::sort(InputToCheck); - llvm::sort(Expected); - EXPECT_EQ(InputToCheck.size(), Expected.size()); - - unsigned LastElt = std::min(InputToCheck.size(), Expected.size()); - for (unsigned Idx = 0; Idx != LastElt; ++Idx) - EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]); -} - -TEST(VirtualFileSystemTest, OverlayIteration) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(Upper); - - std::error_code EC; - checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); - - Lower->addRegularFile("/file1"); - checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1")); - - Upper->addRegularFile("/file2"); - checkContents(O->dir_begin("/", EC), {"/file2", "/file1"}); - - Lower->addDirectory("/dir1"); - Lower->addRegularFile("/dir1/foo"); - Upper->addDirectory("/dir2"); - Upper->addRegularFile("/dir2/foo"); - checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo")); - checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"}); -} - -TEST(VirtualFileSystemTest, OverlayRecursiveIteration) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); - IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(Middle); - O->pushOverlay(Upper); - - std::error_code EC; - checkContents(vfs::recursive_directory_iterator(*O, "/", EC), - ArrayRef<StringRef>()); - - Lower->addRegularFile("/file1"); - checkContents(vfs::recursive_directory_iterator(*O, "/", EC), - ArrayRef<StringRef>("/file1")); - - Upper->addDirectory("/dir"); - Upper->addRegularFile("/dir/file2"); - checkContents(vfs::recursive_directory_iterator(*O, "/", EC), - {"/dir", "/dir/file2", "/file1"}); - - Lower->addDirectory("/dir1"); - Lower->addRegularFile("/dir1/foo"); - Lower->addDirectory("/dir1/a"); - Lower->addRegularFile("/dir1/a/b"); - Middle->addDirectory("/a"); - Middle->addDirectory("/a/b"); - Middle->addDirectory("/a/b/c"); - Middle->addRegularFile("/a/b/c/d"); - Middle->addRegularFile("/hiddenByUp"); - Upper->addDirectory("/dir2"); - Upper->addRegularFile("/dir2/foo"); - Upper->addRegularFile("/hiddenByUp"); - checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC), - ArrayRef<StringRef>("/dir2/foo")); - checkContents(vfs::recursive_directory_iterator(*O, "/", EC), - {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp", - "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a", - "/dir1/a/b", "/dir1/foo", "/file1"}); -} - -TEST(VirtualFileSystemTest, ThreeLevelIteration) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); - IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(Middle); - O->pushOverlay(Upper); - - std::error_code EC; - checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); - - Middle->addRegularFile("/file2"); - checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2")); - - Lower->addRegularFile("/file1"); - Upper->addRegularFile("/file3"); - checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"}); -} - -TEST(VirtualFileSystemTest, HiddenInIteration) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); - IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(Middle); - O->pushOverlay(Upper); - - std::error_code EC; - Lower->addRegularFile("/onlyInLow"); - Lower->addDirectory("/hiddenByMid"); - Lower->addDirectory("/hiddenByUp"); - Middle->addRegularFile("/onlyInMid"); - Middle->addRegularFile("/hiddenByMid"); - Middle->addDirectory("/hiddenByUp"); - Upper->addRegularFile("/onlyInUp"); - Upper->addRegularFile("/hiddenByUp"); - checkContents( - O->dir_begin("/", EC), - {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"}); - - // Make sure we get the top-most entry - { - std::error_code EC; - vfs::directory_iterator I = O->dir_begin("/", EC), E; - for ( ; !EC && I != E; I.increment(EC)) - if (I->path() == "/hiddenByUp") - break; - ASSERT_NE(E, I); - EXPECT_EQ(sys::fs::file_type::regular_file, I->type()); - } - { - std::error_code EC; - vfs::directory_iterator I = O->dir_begin("/", EC), E; - for ( ; !EC && I != E; I.increment(EC)) - if (I->path() == "/hiddenByMid") - break; - ASSERT_NE(E, I); - EXPECT_EQ(sys::fs::file_type::regular_file, I->type()); - } -} - -class InMemoryFileSystemTest : public ::testing::Test { -protected: - clang::vfs::InMemoryFileSystem FS; - clang::vfs::InMemoryFileSystem NormalizedFS; - - InMemoryFileSystemTest() - : FS(/*UseNormalizedPaths=*/false), - NormalizedFS(/*UseNormalizedPaths=*/true) {} -}; - -MATCHER_P2(IsHardLinkTo, FS, Target, "") { - StringRef From = arg; - StringRef To = Target; - auto OpenedFrom = FS->openFileForRead(From); - auto OpenedTo = FS->openFileForRead(To); - return !OpenedFrom.getError() && !OpenedTo.getError() && - (*OpenedFrom)->status()->getUniqueID() == - (*OpenedTo)->status()->getUniqueID(); -} - -TEST_F(InMemoryFileSystemTest, IsEmpty) { - auto Stat = FS.status("/a"); - ASSERT_EQ(Stat.getError(),errc::no_such_file_or_directory) << FS.toString(); - Stat = FS.status("/"); - ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString(); -} - -TEST_F(InMemoryFileSystemTest, WindowsPath) { - FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); - auto Stat = FS.status("c:"); -#if !defined(_WIN32) - ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); -#endif - Stat = FS.status("c:/windows/system128/foo.cpp"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); - FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer("")); - Stat = FS.status("d:/windows/foo.cpp"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); -} - -TEST_F(InMemoryFileSystemTest, OverlayFile) { - FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); - NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); - auto Stat = FS.status("/"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); - Stat = FS.status("/."); - ASSERT_FALSE(Stat); - Stat = NormalizedFS.status("/."); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString(); - Stat = FS.status("/a"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_EQ("/a", Stat->getName()); -} - -TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) { - auto Buf = MemoryBuffer::getMemBuffer("a"); - FS.addFileNoOwn("/a", 0, Buf.get()); - auto Stat = FS.status("/a"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_EQ("/a", Stat->getName()); -} - -TEST_F(InMemoryFileSystemTest, OpenFileForRead) { - FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); - FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c")); - FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d")); - NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")); - NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c")); - NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d")); - auto File = FS.openFileForRead("/a"); - ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); - File = FS.openFileForRead("/a"); // Open again. - ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); - File = NormalizedFS.openFileForRead("/././a"); // Open again. - ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer()); - File = FS.openFileForRead("/"); - ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString(); - File = FS.openFileForRead("/b"); - ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString(); - File = FS.openFileForRead("./c"); - ASSERT_FALSE(File); - File = FS.openFileForRead("e/../d"); - ASSERT_FALSE(File); - File = NormalizedFS.openFileForRead("./c"); - ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer()); - File = NormalizedFS.openFileForRead("e/../d"); - ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer()); -} - -TEST_F(InMemoryFileSystemTest, DuplicatedFile) { - ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"))); - ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a"))); - ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"))); - ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b"))); -} - -TEST_F(InMemoryFileSystemTest, DirectoryIteration) { - FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("")); - FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer("")); - - std::error_code EC; - vfs::directory_iterator I = FS.dir_begin("/", EC); - ASSERT_FALSE(EC); - ASSERT_EQ("/a", I->path()); - I.increment(EC); - ASSERT_FALSE(EC); - ASSERT_EQ("/b", I->path()); - I.increment(EC); - ASSERT_FALSE(EC); - ASSERT_EQ(vfs::directory_iterator(), I); - - I = FS.dir_begin("/b", EC); - ASSERT_FALSE(EC); - // When on Windows, we end up with "/b\\c" as the name. Convert to Posix - // path for the sake of the comparison. - ASSERT_EQ("/b/c", getPosixPath(I->path())); - I.increment(EC); - ASSERT_FALSE(EC); - ASSERT_EQ(vfs::directory_iterator(), I); -} - -TEST_F(InMemoryFileSystemTest, WorkingDirectory) { - FS.setCurrentWorkingDirectory("/b"); - FS.addFile("c", 0, MemoryBuffer::getMemBuffer("")); - - auto Stat = FS.status("/b/c"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_EQ("/b/c", Stat->getName()); - ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory()); - - Stat = FS.status("c"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - - NormalizedFS.setCurrentWorkingDirectory("/b/c"); - NormalizedFS.setCurrentWorkingDirectory("."); - ASSERT_EQ("/b/c", - getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get())); - NormalizedFS.setCurrentWorkingDirectory(".."); - ASSERT_EQ("/b", - getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get())); -} - -#if !defined(_WIN32) -TEST_F(InMemoryFileSystemTest, GetRealPath) { - SmallString<16> Path; - EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted); - - auto GetRealPath = [this](StringRef P) { - SmallString<16> Output; - auto EC = FS.getRealPath(P, Output); - EXPECT_FALSE(EC); - return Output.str().str(); - }; - - FS.setCurrentWorkingDirectory("a"); - EXPECT_EQ(GetRealPath("b"), "a/b"); - EXPECT_EQ(GetRealPath("../b"), "b"); - EXPECT_EQ(GetRealPath("b/./c"), "a/b/c"); - - FS.setCurrentWorkingDirectory("/a"); - EXPECT_EQ(GetRealPath("b"), "/a/b"); - EXPECT_EQ(GetRealPath("../b"), "/b"); - EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c"); -} -#endif // _WIN32 - -TEST_F(InMemoryFileSystemTest, AddFileWithUser) { - FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE); - auto Stat = FS.status("/a"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_TRUE(Stat->isDirectory()); - ASSERT_EQ(0xFEEDFACE, Stat->getUser()); - Stat = FS.status("/a/b"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_TRUE(Stat->isDirectory()); - ASSERT_EQ(0xFEEDFACE, Stat->getUser()); - Stat = FS.status("/a/b/c"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_TRUE(Stat->isRegularFile()); - ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); - ASSERT_EQ(0xFEEDFACE, Stat->getUser()); -} - -TEST_F(InMemoryFileSystemTest, AddFileWithGroup) { - FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00); - auto Stat = FS.status("/a"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_TRUE(Stat->isDirectory()); - ASSERT_EQ(0xDABBAD00, Stat->getGroup()); - Stat = FS.status("/a/b"); - ASSERT_TRUE(Stat->isDirectory()); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_EQ(0xDABBAD00, Stat->getGroup()); - Stat = FS.status("/a/b/c"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_TRUE(Stat->isRegularFile()); - ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); - ASSERT_EQ(0xDABBAD00, Stat->getGroup()); -} - -TEST_F(InMemoryFileSystemTest, AddFileWithFileType) { - FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, - sys::fs::file_type::socket_file); - auto Stat = FS.status("/a"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_TRUE(Stat->isDirectory()); - Stat = FS.status("/a/b"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_TRUE(Stat->isDirectory()); - Stat = FS.status("/a/b/c"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType()); - ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions()); -} - -TEST_F(InMemoryFileSystemTest, AddFileWithPerms) { - FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, - None, sys::fs::perms::owner_read | sys::fs::perms::owner_write); - auto Stat = FS.status("/a"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_TRUE(Stat->isDirectory()); - ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write | - sys::fs::perms::owner_exe, Stat->getPermissions()); - Stat = FS.status("/a/b"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_TRUE(Stat->isDirectory()); - ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write | - sys::fs::perms::owner_exe, Stat->getPermissions()); - Stat = FS.status("/a/b/c"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_TRUE(Stat->isRegularFile()); - ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write, - Stat->getPermissions()); -} - -TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) { - FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None, - /*Group=*/None, sys::fs::file_type::directory_file); - FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None, - /*Group=*/None, sys::fs::file_type::regular_file); - auto Stat = FS.status("/a"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_TRUE(Stat->isDirectory()); - Stat = FS.status("/a/b"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); - ASSERT_TRUE(Stat->isRegularFile()); -} - -// Test that the name returned by status() is in the same form as the path that -// was requested (to match the behavior of RealFileSystem). -TEST_F(InMemoryFileSystemTest, StatusName) { - NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), - /*User=*/None, - /*Group=*/None, sys::fs::file_type::regular_file); - NormalizedFS.setCurrentWorkingDirectory("/a/b"); - - // Access using InMemoryFileSystem::status. - auto Stat = NormalizedFS.status("../b/c"); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" - << NormalizedFS.toString(); - ASSERT_TRUE(Stat->isRegularFile()); - ASSERT_EQ("../b/c", Stat->getName()); - - // Access using InMemoryFileAdaptor::status. - auto File = NormalizedFS.openFileForRead("../b/c"); - ASSERT_FALSE(File.getError()) << File.getError() << "\n" - << NormalizedFS.toString(); - Stat = (*File)->status(); - ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" - << NormalizedFS.toString(); - ASSERT_TRUE(Stat->isRegularFile()); - ASSERT_EQ("../b/c", Stat->getName()); - - // Access using a directory iterator. - std::error_code EC; - clang::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC); - // When on Windows, we end up with "../b\\c" as the name. Convert to Posix - // path for the sake of the comparison. - ASSERT_EQ("../b/c", getPosixPath(It->path())); -} - -TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) { - StringRef FromLink = "/path/to/FROM/link"; - StringRef Target = "/path/to/TO/file"; - FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); - EXPECT_TRUE(FS.addHardLink(FromLink, Target)); - EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target)); - EXPECT_TRUE(FS.status(FromLink)->getSize() == FS.status(Target)->getSize()); - EXPECT_TRUE(FS.getBufferForFile(FromLink)->get()->getBuffer() == - FS.getBufferForFile(Target)->get()->getBuffer()); -} - -TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) { - StringRef Link0 = "/path/to/0/link"; - StringRef Link1 = "/path/to/1/link"; - StringRef Link2 = "/path/to/2/link"; - StringRef Target = "/path/to/target"; - FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file")); - EXPECT_TRUE(FS.addHardLink(Link2, Target)); - EXPECT_TRUE(FS.addHardLink(Link1, Link2)); - EXPECT_TRUE(FS.addHardLink(Link0, Link1)); - EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target)); - EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target)); - EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target)); -} - -TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) { - EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target")); -} - -TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) { - StringRef Link = "/path/to/link"; - StringRef Target = "/path/to/target"; - FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); - FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link")); - EXPECT_FALSE(FS.addHardLink(Link, Target)); -} - -TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) { - StringRef Link = "/path/to/link"; - StringRef Target = "/path/to/target"; - FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target")); - EXPECT_TRUE(FS.addHardLink(Link, Target)); - EXPECT_FALSE(FS.addHardLink(Link, Target)); -} - -TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) { - StringRef Link = "/path/to/link"; - StringRef Target = "/path/to/target"; - StringRef Content = "content of target"; - EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); - EXPECT_TRUE(FS.addHardLink(Link, Target)); - EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content))); -} - -TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) { - StringRef Link = "/path/to/link"; - StringRef Target = "/path/to/target"; - StringRef Content = "content of target"; - StringRef LinkContent = "different content of link"; - EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); - EXPECT_TRUE(FS.addHardLink(Link, Target)); - EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent))); -} - -TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) { - StringRef Dir = "path/to/dummy/dir"; - StringRef Link = "/path/to/link"; - StringRef File = "path/to/dummy/dir/target"; - StringRef Content = "content of target"; - EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content))); - EXPECT_FALSE(FS.addHardLink(Link, Dir)); -} - -TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) { - StringRef Dir = "path/to/dummy/dir"; - StringRef Target = "path/to/dummy/dir/target"; - StringRef Content = "content of target"; - EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content))); - EXPECT_FALSE(FS.addHardLink(Dir, Target)); -} - -TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) { - StringRef CommonContent = "content string"; - FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent)); - FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent)); - EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b")); -} - -TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) { - std::error_code EC; - FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string")); - EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b")); - auto I = vfs::recursive_directory_iterator(FS, "/", EC); - ASSERT_FALSE(EC); - std::vector<std::string> Nodes; - for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; - I.increment(EC)) { - Nodes.push_back(getPosixPath(I->path())); - } - EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d")); -} - -// NOTE: in the tests below, we use '//root/' as our root directory, since it is -// a legal *absolute* path on Windows as well as *nix. -class VFSFromYAMLTest : public ::testing::Test { -public: - int NumDiagnostics; - - void SetUp() override { NumDiagnostics = 0; } - - static void CountingDiagHandler(const SMDiagnostic &, void *Context) { - VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context); - ++Test->NumDiagnostics; - } - - IntrusiveRefCntPtr<vfs::FileSystem> - getFromYAMLRawString(StringRef Content, - IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) { - std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content); - return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this, - ExternalFS); - } - - IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString( - StringRef Content, - IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) { - std::string VersionPlusContent("{\n 'version':0,\n"); - VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos); - return getFromYAMLRawString(VersionPlusContent, ExternalFS); - } - - // This is intended as a "XFAIL" for windows hosts. - bool supportsSameDirMultipleYAMLEntries() { - Triple Host(Triple::normalize(sys::getProcessTriple())); - return !Host.isOSWindows(); - } -}; - -TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) { - IntrusiveRefCntPtr<vfs::FileSystem> FS; - FS = getFromYAMLString(""); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString("[]"); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString("'string'"); - EXPECT_EQ(nullptr, FS.get()); - EXPECT_EQ(3, NumDiagnostics); -} - -TEST_F(VFSFromYAMLTest, MappedFiles) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - Lower->addRegularFile("//root/foo/bar/a"); - IntrusiveRefCntPtr<vfs::FileSystem> FS = - getFromYAMLString("{ 'roots': [\n" - "{\n" - " 'type': 'directory',\n" - " 'name': '//root/',\n" - " 'contents': [ {\n" - " 'type': 'file',\n" - " 'name': 'file1',\n" - " 'external-contents': '//root/foo/bar/a'\n" - " },\n" - " {\n" - " 'type': 'file',\n" - " 'name': 'file2',\n" - " 'external-contents': '//root/foo/b'\n" - " }\n" - " ]\n" - "}\n" - "]\n" - "}", - Lower); - ASSERT_TRUE(FS.get() != nullptr); - - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(FS); - - // file - ErrorOr<vfs::Status> S = O->status("//root/file1"); - ASSERT_FALSE(S.getError()); - EXPECT_EQ("//root/foo/bar/a", S->getName()); - EXPECT_TRUE(S->IsVFSMapped); - - ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a"); - EXPECT_EQ("//root/foo/bar/a", SLower->getName()); - EXPECT_TRUE(S->equivalent(*SLower)); - EXPECT_FALSE(SLower->IsVFSMapped); - - // file after opening - auto OpenedF = O->openFileForRead("//root/file1"); - ASSERT_FALSE(OpenedF.getError()); - auto OpenedS = (*OpenedF)->status(); - ASSERT_FALSE(OpenedS.getError()); - EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); - EXPECT_TRUE(OpenedS->IsVFSMapped); - - // directory - S = O->status("//root/"); - ASSERT_FALSE(S.getError()); - EXPECT_TRUE(S->isDirectory()); - EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID - - // broken mapping - EXPECT_EQ(O->status("//root/file2").getError(), - llvm::errc::no_such_file_or_directory); - EXPECT_EQ(0, NumDiagnostics); -} - -TEST_F(VFSFromYAMLTest, CaseInsensitive) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - Lower->addRegularFile("//root/foo/bar/a"); - IntrusiveRefCntPtr<vfs::FileSystem> FS = - getFromYAMLString("{ 'case-sensitive': 'false',\n" - " 'roots': [\n" - "{\n" - " 'type': 'directory',\n" - " 'name': '//root/',\n" - " 'contents': [ {\n" - " 'type': 'file',\n" - " 'name': 'XX',\n" - " 'external-contents': '//root/foo/bar/a'\n" - " }\n" - " ]\n" - "}]}", - Lower); - ASSERT_TRUE(FS.get() != nullptr); - - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(FS); - - ErrorOr<vfs::Status> S = O->status("//root/XX"); - ASSERT_FALSE(S.getError()); - - ErrorOr<vfs::Status> SS = O->status("//root/xx"); - ASSERT_FALSE(SS.getError()); - EXPECT_TRUE(S->equivalent(*SS)); - SS = O->status("//root/xX"); - EXPECT_TRUE(S->equivalent(*SS)); - SS = O->status("//root/Xx"); - EXPECT_TRUE(S->equivalent(*SS)); - EXPECT_EQ(0, NumDiagnostics); -} - -TEST_F(VFSFromYAMLTest, CaseSensitive) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - Lower->addRegularFile("//root/foo/bar/a"); - IntrusiveRefCntPtr<vfs::FileSystem> FS = - getFromYAMLString("{ 'case-sensitive': 'true',\n" - " 'roots': [\n" - "{\n" - " 'type': 'directory',\n" - " 'name': '//root/',\n" - " 'contents': [ {\n" - " 'type': 'file',\n" - " 'name': 'XX',\n" - " 'external-contents': '//root/foo/bar/a'\n" - " }\n" - " ]\n" - "}]}", - Lower); - ASSERT_TRUE(FS.get() != nullptr); - - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(FS); - - ErrorOr<vfs::Status> SS = O->status("//root/xx"); - EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); - SS = O->status("//root/xX"); - EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); - SS = O->status("//root/Xx"); - EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); - EXPECT_EQ(0, NumDiagnostics); -} - -TEST_F(VFSFromYAMLTest, IllegalVFSFile) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - - // invalid YAML at top-level - IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower); - EXPECT_EQ(nullptr, FS.get()); - // invalid YAML in roots - FS = getFromYAMLString("{ 'roots':[}", Lower); - // invalid YAML in directory - FS = getFromYAMLString( - "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}", - Lower); - EXPECT_EQ(nullptr, FS.get()); - - // invalid configuration - FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower); - EXPECT_EQ(nullptr, FS.get()); - - // invalid roots - FS = getFromYAMLString("{ 'roots':'' }", Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString("{ 'roots':{} }", Lower); - EXPECT_EQ(nullptr, FS.get()); - - // invalid entries - FS = getFromYAMLString( - "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], " - "'external-contents': 'other' }", - Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString( - "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }", - Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString( - "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }", - Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString( - "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }", - Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString( - "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }", - Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString( - "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }", - Lower); - EXPECT_EQ(nullptr, FS.get()); - - // missing mandatory fields - FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString( - "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower); - EXPECT_EQ(nullptr, FS.get()); - - // duplicate keys - FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLString( - "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }", - Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = - getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', " - "'external-contents':'blah' } ] }", - Lower); - EXPECT_EQ(nullptr, FS.get()); - - // missing version - FS = getFromYAMLRawString("{ 'roots':[] }", Lower); - EXPECT_EQ(nullptr, FS.get()); - - // bad version number - FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower); - EXPECT_EQ(nullptr, FS.get()); - FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower); - EXPECT_EQ(nullptr, FS.get()); - EXPECT_EQ(24, NumDiagnostics); -} - -TEST_F(VFSFromYAMLTest, UseExternalName) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - Lower->addRegularFile("//root/external/file"); - - IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( - "{ 'roots': [\n" - " { 'type': 'file', 'name': '//root/A',\n" - " 'external-contents': '//root/external/file'\n" - " },\n" - " { 'type': 'file', 'name': '//root/B',\n" - " 'use-external-name': true,\n" - " 'external-contents': '//root/external/file'\n" - " },\n" - " { 'type': 'file', 'name': '//root/C',\n" - " 'use-external-name': false,\n" - " 'external-contents': '//root/external/file'\n" - " }\n" - "] }", Lower); - ASSERT_TRUE(nullptr != FS.get()); - - // default true - EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName()); - // explicit - EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); - EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); - - // global configuration - FS = getFromYAMLString( - "{ 'use-external-names': false,\n" - " 'roots': [\n" - " { 'type': 'file', 'name': '//root/A',\n" - " 'external-contents': '//root/external/file'\n" - " },\n" - " { 'type': 'file', 'name': '//root/B',\n" - " 'use-external-name': true,\n" - " 'external-contents': '//root/external/file'\n" - " },\n" - " { 'type': 'file', 'name': '//root/C',\n" - " 'use-external-name': false,\n" - " 'external-contents': '//root/external/file'\n" - " }\n" - "] }", Lower); - ASSERT_TRUE(nullptr != FS.get()); - - // default - EXPECT_EQ("//root/A", FS->status("//root/A")->getName()); - // explicit - EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); - EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); -} - -TEST_F(VFSFromYAMLTest, MultiComponentPath) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - Lower->addRegularFile("//root/other"); - - // file in roots - IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( - "{ 'roots': [\n" - " { 'type': 'file', 'name': '//root/path/to/file',\n" - " 'external-contents': '//root/other' }]\n" - "}", Lower); - ASSERT_TRUE(nullptr != FS.get()); - EXPECT_FALSE(FS->status("//root/path/to/file").getError()); - EXPECT_FALSE(FS->status("//root/path/to").getError()); - EXPECT_FALSE(FS->status("//root/path").getError()); - EXPECT_FALSE(FS->status("//root/").getError()); - - // at the start - FS = getFromYAMLString( - "{ 'roots': [\n" - " { 'type': 'directory', 'name': '//root/path/to',\n" - " 'contents': [ { 'type': 'file', 'name': 'file',\n" - " 'external-contents': '//root/other' }]}]\n" - "}", Lower); - ASSERT_TRUE(nullptr != FS.get()); - EXPECT_FALSE(FS->status("//root/path/to/file").getError()); - EXPECT_FALSE(FS->status("//root/path/to").getError()); - EXPECT_FALSE(FS->status("//root/path").getError()); - EXPECT_FALSE(FS->status("//root/").getError()); - - // at the end - FS = getFromYAMLString( - "{ 'roots': [\n" - " { 'type': 'directory', 'name': '//root/',\n" - " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n" - " 'external-contents': '//root/other' }]}]\n" - "}", Lower); - ASSERT_TRUE(nullptr != FS.get()); - EXPECT_FALSE(FS->status("//root/path/to/file").getError()); - EXPECT_FALSE(FS->status("//root/path/to").getError()); - EXPECT_FALSE(FS->status("//root/path").getError()); - EXPECT_FALSE(FS->status("//root/").getError()); -} - -TEST_F(VFSFromYAMLTest, TrailingSlashes) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - Lower->addRegularFile("//root/other"); - - // file in roots - IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( - "{ 'roots': [\n" - " { 'type': 'directory', 'name': '//root/path/to////',\n" - " 'contents': [ { 'type': 'file', 'name': 'file',\n" - " 'external-contents': '//root/other' }]}]\n" - "}", Lower); - ASSERT_TRUE(nullptr != FS.get()); - EXPECT_FALSE(FS->status("//root/path/to/file").getError()); - EXPECT_FALSE(FS->status("//root/path/to").getError()); - EXPECT_FALSE(FS->status("//root/path").getError()); - EXPECT_FALSE(FS->status("//root/").getError()); -} - -TEST_F(VFSFromYAMLTest, DirectoryIteration) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - Lower->addDirectory("//root/"); - Lower->addDirectory("//root/foo"); - Lower->addDirectory("//root/foo/bar"); - Lower->addRegularFile("//root/foo/bar/a"); - Lower->addRegularFile("//root/foo/bar/b"); - Lower->addRegularFile("//root/file3"); - IntrusiveRefCntPtr<vfs::FileSystem> FS = - getFromYAMLString("{ 'use-external-names': false,\n" - " 'roots': [\n" - "{\n" - " 'type': 'directory',\n" - " 'name': '//root/',\n" - " 'contents': [ {\n" - " 'type': 'file',\n" - " 'name': 'file1',\n" - " 'external-contents': '//root/foo/bar/a'\n" - " },\n" - " {\n" - " 'type': 'file',\n" - " 'name': 'file2',\n" - " 'external-contents': '//root/foo/bar/b'\n" - " }\n" - " ]\n" - "}\n" - "]\n" - "}", - Lower); - ASSERT_TRUE(FS.get() != nullptr); - - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(FS); - - std::error_code EC; - checkContents(O->dir_begin("//root/", EC), - {"//root/file1", "//root/file2", "//root/file3", "//root/foo"}); - - checkContents(O->dir_begin("//root/foo/bar", EC), - {"//root/foo/bar/a", "//root/foo/bar/b"}); -} - -TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) { - // https://llvm.org/bugs/show_bug.cgi?id=27725 - if (!supportsSameDirMultipleYAMLEntries()) - return; - - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - Lower->addDirectory("//root/zab"); - Lower->addDirectory("//root/baz"); - Lower->addRegularFile("//root/zab/a"); - Lower->addRegularFile("//root/zab/b"); - IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( - "{ 'use-external-names': false,\n" - " 'roots': [\n" - "{\n" - " 'type': 'directory',\n" - " 'name': '//root/baz/',\n" - " 'contents': [ {\n" - " 'type': 'file',\n" - " 'name': 'x',\n" - " 'external-contents': '//root/zab/a'\n" - " }\n" - " ]\n" - "},\n" - "{\n" - " 'type': 'directory',\n" - " 'name': '//root/baz/',\n" - " 'contents': [ {\n" - " 'type': 'file',\n" - " 'name': 'y',\n" - " 'external-contents': '//root/zab/b'\n" - " }\n" - " ]\n" - "}\n" - "]\n" - "}", - Lower); - ASSERT_TRUE(FS.get() != nullptr); - - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(FS); - - std::error_code EC; - - checkContents(O->dir_begin("//root/baz/", EC), - {"//root/baz/x", "//root/baz/y"}); -} - -TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) { - - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - Lower->addDirectory("//root/a"); - Lower->addDirectory("//root/a/b"); - Lower->addDirectory("//root/a/b/c"); - Lower->addRegularFile("//root/a/b/c/file"); - IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( - "{ 'use-external-names': false,\n" - " 'roots': [\n" - "{\n" - " 'type': 'directory',\n" - " 'name': '//root/a/b/c/',\n" - " 'contents': [ {\n" - " 'type': 'file',\n" - " 'name': 'file',\n" - " 'external-contents': '//root/a/b/c/file'\n" - " }\n" - " ]\n" - "},\n" - "]\n" - "}", - Lower); - ASSERT_TRUE(FS.get() != nullptr); - - IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( - new vfs::OverlayFileSystem(Lower)); - O->pushOverlay(FS); - - std::error_code EC; - - // Test recursive_directory_iterator level() - vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator( - *O, "//root", EC), E; - ASSERT_FALSE(EC); - for (int l = 0; I != E; I.increment(EC), ++l) { - ASSERT_FALSE(EC); - EXPECT_EQ(I.level(), l); - } - EXPECT_EQ(I, E); -} - -TEST_F(VFSFromYAMLTest, RelativePaths) { - IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); - // Filename at root level without a parent directory. - IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( - "{ 'roots': [\n" - " { 'type': 'file', 'name': 'file-not-in-directory.h',\n" - " 'external-contents': '//root/external/file'\n" - " }\n" - "] }", Lower); - EXPECT_EQ(nullptr, FS.get()); - - // Relative file path. - FS = getFromYAMLString( - "{ 'roots': [\n" - " { 'type': 'file', 'name': 'relative/file/path.h',\n" - " 'external-contents': '//root/external/file'\n" - " }\n" - "] }", Lower); - EXPECT_EQ(nullptr, FS.get()); - - // Relative directory path. - FS = getFromYAMLString( - "{ 'roots': [\n" - " { 'type': 'directory', 'name': 'relative/directory/path.h',\n" - " 'contents': []\n" - " }\n" - "] }", Lower); - EXPECT_EQ(nullptr, FS.get()); - - EXPECT_EQ(3, NumDiagnostics); -} diff --git a/unittests/CodeGen/CMakeLists.txt b/unittests/CodeGen/CMakeLists.txt index 856dbce08e..e4e7588a93 100644 --- a/unittests/CodeGen/CMakeLists.txt +++ b/unittests/CodeGen/CMakeLists.txt @@ -18,4 +18,5 @@ target_link_libraries(ClangCodeGenTests clangFrontend clangLex clangParse + clangSerialization ) diff --git a/unittests/CrossTU/CMakeLists.txt b/unittests/CrossTU/CMakeLists.txt index 652d91612f..73047b739a 100644 --- a/unittests/CrossTU/CMakeLists.txt +++ b/unittests/CrossTU/CMakeLists.txt @@ -13,5 +13,6 @@ target_link_libraries(CrossTUTests clangBasic clangCrossTU clangFrontend + clangSerialization clangTooling ) diff --git a/unittests/Driver/DistroTest.cpp b/unittests/Driver/DistroTest.cpp index e3686e4980..5a23392e8c 100644 --- a/unittests/Driver/DistroTest.cpp +++ b/unittests/Driver/DistroTest.cpp @@ -12,7 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Driver/Distro.h" -#include "clang/Basic/VirtualFileSystem.h" +#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" using namespace clang; @@ -25,7 +25,7 @@ namespace { // accidentally result in incorrect distribution guess. TEST(DistroTest, DetectUbuntu) { - vfs::InMemoryFileSystem UbuntuTrustyFileSystem; + llvm::vfs::InMemoryFileSystem UbuntuTrustyFileSystem; // Ubuntu uses Debian Sid version. UbuntuTrustyFileSystem.addFile("/etc/debian_version", 0, llvm::MemoryBuffer::getMemBuffer("jessie/sid\n")); @@ -52,7 +52,7 @@ TEST(DistroTest, DetectUbuntu) { ASSERT_FALSE(UbuntuTrusty.IsOpenSUSE()); ASSERT_FALSE(UbuntuTrusty.IsDebian()); - vfs::InMemoryFileSystem UbuntuYakketyFileSystem; + llvm::vfs::InMemoryFileSystem UbuntuYakketyFileSystem; UbuntuYakketyFileSystem.addFile("/etc/debian_version", 0, llvm::MemoryBuffer::getMemBuffer("stretch/sid\n")); UbuntuYakketyFileSystem.addFile("/etc/lsb-release", 0, @@ -83,7 +83,7 @@ TEST(DistroTest, DetectUbuntu) { } TEST(DistroTest, DetectRedhat) { - vfs::InMemoryFileSystem Fedora25FileSystem; + llvm::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. @@ -115,7 +115,7 @@ TEST(DistroTest, DetectRedhat) { ASSERT_FALSE(Fedora25.IsOpenSUSE()); ASSERT_FALSE(Fedora25.IsDebian()); - vfs::InMemoryFileSystem CentOS7FileSystem; + llvm::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. @@ -153,7 +153,7 @@ TEST(DistroTest, DetectRedhat) { } TEST(DistroTest, DetectOpenSUSE) { - vfs::InMemoryFileSystem OpenSUSELeap421FileSystem; + llvm::vfs::InMemoryFileSystem OpenSUSELeap421FileSystem; OpenSUSELeap421FileSystem.addFile("/etc/SuSE-release", 0, llvm::MemoryBuffer::getMemBuffer("openSUSE 42.1 (x86_64)\n" "VERSION = 42.1\n" @@ -178,7 +178,7 @@ TEST(DistroTest, DetectOpenSUSE) { ASSERT_TRUE(OpenSUSELeap421.IsOpenSUSE()); ASSERT_FALSE(OpenSUSELeap421.IsDebian()); - vfs::InMemoryFileSystem OpenSUSE132FileSystem; + llvm::vfs::InMemoryFileSystem OpenSUSE132FileSystem; OpenSUSE132FileSystem.addFile("/etc/SuSE-release", 0, llvm::MemoryBuffer::getMemBuffer("openSUSE 13.2 (x86_64)\n" "VERSION = 13.2\n" @@ -203,7 +203,7 @@ TEST(DistroTest, DetectOpenSUSE) { ASSERT_TRUE(OpenSUSE132.IsOpenSUSE()); ASSERT_FALSE(OpenSUSE132.IsDebian()); - vfs::InMemoryFileSystem SLES10FileSystem; + llvm::vfs::InMemoryFileSystem SLES10FileSystem; SLES10FileSystem.addFile("/etc/SuSE-release", 0, llvm::MemoryBuffer::getMemBuffer("SUSE Linux Enterprise Server 10 (x86_64)\n" "VERSION = 10\n" @@ -221,7 +221,7 @@ TEST(DistroTest, DetectOpenSUSE) { } TEST(DistroTest, DetectDebian) { - vfs::InMemoryFileSystem DebianJessieFileSystem; + llvm::vfs::InMemoryFileSystem DebianJessieFileSystem; DebianJessieFileSystem.addFile("/etc/debian_version", 0, llvm::MemoryBuffer::getMemBuffer("8.6\n")); DebianJessieFileSystem.addFile("/etc/os-release", 0, @@ -241,7 +241,7 @@ TEST(DistroTest, DetectDebian) { ASSERT_FALSE(DebianJessie.IsOpenSUSE()); ASSERT_TRUE(DebianJessie.IsDebian()); - vfs::InMemoryFileSystem DebianStretchSidFileSystem; + llvm::vfs::InMemoryFileSystem DebianStretchSidFileSystem; DebianStretchSidFileSystem.addFile("/etc/debian_version", 0, llvm::MemoryBuffer::getMemBuffer("stretch/sid\n")); DebianStretchSidFileSystem.addFile("/etc/os-release", 0, @@ -261,7 +261,7 @@ TEST(DistroTest, DetectDebian) { } TEST(DistroTest, DetectExherbo) { - vfs::InMemoryFileSystem ExherboFileSystem; + llvm::vfs::InMemoryFileSystem ExherboFileSystem; ExherboFileSystem.addFile("/etc/exherbo-release", 0, // (ASCII art) llvm::MemoryBuffer::getMemBuffer("")); ExherboFileSystem.addFile("/etc/os-release", 0, @@ -282,7 +282,7 @@ TEST(DistroTest, DetectExherbo) { } TEST(DistroTest, DetectArchLinux) { - vfs::InMemoryFileSystem ArchLinuxFileSystem; + llvm::vfs::InMemoryFileSystem ArchLinuxFileSystem; ArchLinuxFileSystem.addFile("/etc/arch-release", 0, // (empty) llvm::MemoryBuffer::getMemBuffer("")); ArchLinuxFileSystem.addFile("/etc/os-release", 0, diff --git a/unittests/Driver/ToolChainTest.cpp b/unittests/Driver/ToolChainTest.cpp index 0d4c545bd7..f1181072a7 100644 --- a/unittests/Driver/ToolChainTest.cpp +++ b/unittests/Driver/ToolChainTest.cpp @@ -15,11 +15,11 @@ #include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/LLVM.h" -#include "clang/Basic/VirtualFileSystem.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" using namespace clang; @@ -33,8 +33,8 @@ TEST(ToolChainTest, VFSGCCInstallation) { IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); struct TestDiagnosticConsumer : public DiagnosticConsumer {}; DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); - IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( - new vfs::InMemoryFileSystem); + IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); Driver TheDriver("/bin/clang", "arm-linux-gnueabihf", Diags, InMemoryFileSystem); @@ -87,8 +87,8 @@ TEST(ToolChainTest, VFSGCCInstallationRelativeDir) { IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); struct TestDiagnosticConsumer : public DiagnosticConsumer {}; DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); - IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( - new vfs::InMemoryFileSystem); + IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); Driver TheDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, InMemoryFileSystem); @@ -127,8 +127,8 @@ TEST(ToolChainTest, DefaultDriverMode) { IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); struct TestDiagnosticConsumer : public DiagnosticConsumer {}; DiagnosticsEngine Diags(DiagID, &*DiagOpts, new TestDiagnosticConsumer); - IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( - new vfs::InMemoryFileSystem); + IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); Driver CCDriver("/home/test/bin/clang", "arm-linux-gnueabi", Diags, InMemoryFileSystem); diff --git a/unittests/Format/CMakeLists.txt b/unittests/Format/CMakeLists.txt index 18e4432308..015c25ee6b 100644 --- a/unittests/Format/CMakeLists.txt +++ b/unittests/Format/CMakeLists.txt @@ -12,9 +12,11 @@ add_clang_unittest(FormatTests FormatTestProto.cpp FormatTestRawStrings.cpp FormatTestSelective.cpp + FormatTestTableGen.cpp FormatTestTextProto.cpp NamespaceEndCommentsFixerTest.cpp SortImportsTestJS.cpp + SortImportsTestJava.cpp SortIncludesTest.cpp UsingDeclarationsSorterTest.cpp ) diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 3e23c5e11b..c05fceb476 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -2660,6 +2660,45 @@ TEST_F(FormatTest, MacroCallsWithoutTrailingSemicolon) { getLLVMStyleWithColumns(40))); verifyFormat("MACRO(>)"); + + // Some macros contain an implicit semicolon. + Style = getLLVMStyle(); + Style.StatementMacros.push_back("FOO"); + verifyFormat("FOO(a) int b = 0;"); + verifyFormat("FOO(a)\n" + "int b = 0;", + Style); + verifyFormat("FOO(a);\n" + "int b = 0;", + Style); + verifyFormat("FOO(argc, argv, \"4.0.2\")\n" + "int b = 0;", + Style); + verifyFormat("FOO()\n" + "int b = 0;", + Style); + verifyFormat("FOO\n" + "int b = 0;", + Style); + verifyFormat("void f() {\n" + " FOO(a)\n" + " return a;\n" + "}", + Style); + verifyFormat("FOO(a)\n" + "FOO(b)", + Style); + verifyFormat("int a = 0;\n" + "FOO(b)\n" + "int c = 0;", + Style); + verifyFormat("int a = 0;\n" + "int x = FOO(a)\n" + "int b = 0;", + Style); + verifyFormat("void foo(int a) { FOO(a) }\n" + "uint32_t bar() {}", + Style); } TEST_F(FormatTest, LayoutMacroDefinitionsStatementsSpanningBlocks) { @@ -3238,11 +3277,12 @@ TEST_F(FormatTest, LayoutNestedBlocks) { "});"); FormatStyle Style = getGoogleStyle(); Style.ColumnLimit = 45; - verifyFormat("Debug(aaaaa,\n" - " {\n" - " if (aaaaaaaaaaaaaaaaaaaaaaaa) return;\n" - " },\n" - " a);", + verifyFormat("Debug(\n" + " aaaaa,\n" + " {\n" + " if (aaaaaaaaaaaaaaaaaaaaaaaa) return;\n" + " },\n" + " a);", Style); verifyFormat("SomeFunction({MACRO({ return output; }), b});"); @@ -6432,6 +6472,8 @@ TEST_F(FormatTest, UnderstandsSquareAttributes) { // Make sure we do not mistake attributes for array subscripts. verifyFormat("int a() {}\n" "[[unused]] int b() {}\n"); + verifyFormat("NSArray *arr;\n" + "arr[[Foo() bar]];"); // On the other hand, we still need to correctly find array subscripts. verifyFormat("int a = std::vector<int>{1, 2, 3}[0];"); @@ -11095,6 +11137,12 @@ TEST_F(FormatTest, ParsesConfiguration) { CHECK_PARSE("ForEachMacros: [BOOST_FOREACH, Q_FOREACH]", ForEachMacros, BoostAndQForeach); + Style.StatementMacros.clear(); + CHECK_PARSE("StatementMacros: [QUNUSED]", StatementMacros, + std::vector<std::string>{"QUNUSED"}); + CHECK_PARSE("StatementMacros: [QUNUSED, QT_REQUIRE_VERSION]", StatementMacros, + std::vector<std::string>({"QUNUSED", "QT_REQUIRE_VERSION"})); + Style.IncludeStyle.IncludeCategories.clear(); std::vector<tooling::IncludeStyle::IncludeCategory> ExpectedCategories = { {"abc/.*", 2}, {".*", 1}}; @@ -11694,6 +11742,18 @@ TEST_F(FormatTest, FormatsLambdas) { " x.end(), //\n" " [&](int, int) { return 1; });\n" "}\n"); + verifyFormat("void f() {\n" + " other.other.other.other.other(\n" + " x.begin(), x.end(),\n" + " [something, rather](int, int, int, int, int, int, int) { return 1; });\n" + "}\n"); + verifyFormat("void f() {\n" + " other.other.other.other.other(\n" + " x.begin(), x.end(),\n" + " [something, rather](int, int, int, int, int, int, int) {\n" + " //\n" + " });\n" + "}\n"); verifyFormat("SomeFunction([]() { // A cool function...\n" " return 43;\n" "});"); @@ -11745,9 +11805,9 @@ TEST_F(FormatTest, FormatsLambdas) { verifyFormat("SomeFunction({[&] {\n" " // comment\n" "}});"); - verifyFormat("virtual aaaaaaaaaaaaaaaa(std::function<bool()> bbbbbbbbbbbb =\n" - " [&]() { return true; },\n" - " aaaaa aaaaaaaaa);"); + verifyFormat("virtual aaaaaaaaaaaaaaaa(\n" + " std::function<bool()> bbbbbbbbbbbb = [&]() { return true; },\n" + " aaaaa aaaaaaaaa);"); // Lambdas with return types. verifyFormat("int c = []() -> int { return 2; }();\n"); @@ -11774,17 +11834,76 @@ TEST_F(FormatTest, FormatsLambdas) { " return 1; //\n" "};"); - // Multiple lambdas in the same parentheses change indentation rules. + // Multiple lambdas in the same parentheses change indentation rules. These + // lambdas are forced to start on new lines. verifyFormat("SomeFunction(\n" " []() {\n" - " int i = 42;\n" - " return i;\n" + " //\n" " },\n" " []() {\n" - " int j = 43;\n" - " return j;\n" + " //\n" " });"); + // A lambda passed as arg0 is always pushed to the next line. + verifyFormat("SomeFunction(\n" + " [this] {\n" + " //\n" + " },\n" + " 1);\n"); + + // A multi-line lambda passed as arg1 forces arg0 to be pushed out, just like the arg0 + // case above. + auto Style = getGoogleStyle(); + Style.BinPackArguments = false; + verifyFormat("SomeFunction(\n" + " a,\n" + " [this] {\n" + " //\n" + " },\n" + " b);\n", + Style); + verifyFormat("SomeFunction(\n" + " a,\n" + " [this] {\n" + " //\n" + " },\n" + " b);\n"); + + // A lambda with a very long line forces arg0 to be pushed out irrespective of + // the BinPackArguments value (as long as the code is wide enough). + verifyFormat("something->SomeFunction(\n" + " a,\n" + " [this] {\n" + " D0000000000000000000000000000000000000000000000000000000000001();\n" + " },\n" + " b);\n"); + + // A multi-line lambda is pulled up as long as the introducer fits on the previous + // line and there are no further args. + verifyFormat("function(1, [this, that] {\n" + " //\n" + "});\n"); + verifyFormat("function([this, that] {\n" + " //\n" + "});\n"); + // FIXME: this format is not ideal and we should consider forcing the first arg + // onto its own line. + verifyFormat("function(a, b, c, //\n" + " d, [this, that] {\n" + " //\n" + " });\n"); + + // Multiple lambdas are treated correctly even when there is a short arg0. + verifyFormat("SomeFunction(\n" + " 1,\n" + " [this] {\n" + " //\n" + " },\n" + " [this] {\n" + " //\n" + " },\n" + " 1);\n"); + // More complex introducers. verifyFormat("return [i, args...] {};"); @@ -12330,14 +12449,14 @@ TEST_F(FormatTest, NoSpaceAfterSuper) { } TEST(FormatStyle, GetStyleWithEmptyFileName) { - vfs::InMemoryFileSystem FS; + llvm::vfs::InMemoryFileSystem FS; auto Style1 = getStyle("file", "", "Google", "", &FS); ASSERT_TRUE((bool)Style1); ASSERT_EQ(*Style1, getGoogleStyle()); } TEST(FormatStyle, GetStyleOfFile) { - vfs::InMemoryFileSystem FS; + llvm::vfs::InMemoryFileSystem FS; // Test 1: format file in the same directory. ASSERT_TRUE( FS.addFile("/a/.clang-format", 0, @@ -12636,6 +12755,45 @@ TEST_F(FormatTest, GuessLanguageWithCaret) { guessLanguage("foo.h", "int(^foo[(kNumEntries + 10)])(char, float);")); } +TEST_F(FormatTest, GuessedLanguageWithInlineAsmClobbers) { + EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", + "void f() {\n" + " asm (\"mov %[e], %[d]\"\n" + " : [d] \"=rm\" (d)\n" + " [e] \"rm\" (*e));\n" + "}")); + EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", + "void f() {\n" + " _asm (\"mov %[e], %[d]\"\n" + " : [d] \"=rm\" (d)\n" + " [e] \"rm\" (*e));\n" + "}")); + EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", + "void f() {\n" + " __asm (\"mov %[e], %[d]\"\n" + " : [d] \"=rm\" (d)\n" + " [e] \"rm\" (*e));\n" + "}")); + EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", + "void f() {\n" + " __asm__ (\"mov %[e], %[d]\"\n" + " : [d] \"=rm\" (d)\n" + " [e] \"rm\" (*e));\n" + "}")); + EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", + "void f() {\n" + " asm (\"mov %[e], %[d]\"\n" + " : [d] \"=rm\" (d),\n" + " [e] \"rm\" (*e));\n" + "}")); + EXPECT_EQ(FormatStyle::LK_Cpp, + guessLanguage("foo.h", "void f() {\n" + " asm volatile (\"mov %[e], %[d]\"\n" + " : [d] \"=rm\" (d)\n" + " [e] \"rm\" (*e));\n" + "}")); +} + TEST_F(FormatTest, GuessLanguageWithChildLines) { EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "#define FOO ({ std::string s; })")); diff --git a/unittests/Format/FormatTestJS.cpp b/unittests/Format/FormatTestJS.cpp index 5a71f00337..a14724f85e 100644 --- a/unittests/Format/FormatTestJS.cpp +++ b/unittests/Format/FormatTestJS.cpp @@ -1186,6 +1186,16 @@ TEST_F(FormatTestJS, WrapRespectsAutomaticSemicolonInsertion) { getGoogleJSStyleWithColumns(25)); } +TEST_F(FormatTestJS, AddsIsTheDictKeyOnNewline) { + // Do not confuse is, the dict key with is, the type matcher. Put is, the dict + // key, on a newline. + verifyFormat("Polymer({\n" + " is: '', //\n" + " rest: 1\n" + "});", + getGoogleJSStyleWithColumns(20)); +} + TEST_F(FormatTestJS, AutomaticSemicolonInsertionHeuristic) { verifyFormat("a\n" "b;", diff --git a/unittests/Format/FormatTestJava.cpp b/unittests/Format/FormatTestJava.cpp index 76e82612e0..f12d7fba50 100644 --- a/unittests/Format/FormatTestJava.cpp +++ b/unittests/Format/FormatTestJava.cpp @@ -155,6 +155,15 @@ TEST_F(FormatTestJava, ClassDeclarations) { " void doStuff(int theStuff);\n" " void doMoreStuff(int moreStuff);\n" "}"); + verifyFormat("class A {\n" + " public @interface SomeInterface {\n" + " int stuff;\n" + " void doMoreStuff(int moreStuff);\n" + " }\n" + "}"); + verifyFormat("class A {\n" + " public @interface SomeInterface {}\n" + "}"); } TEST_F(FormatTestJava, AnonymousClasses) { @@ -443,6 +452,22 @@ TEST_F(FormatTestJava, MethodDeclarations) { getStyleWithColumns(40)); } +TEST_F(FormatTestJava, MethodReference) { + EXPECT_EQ( + "private void foo() {\n" + " f(this::methodReference);\n" + " f(C.super::methodReference);\n" + " Consumer<String> c = System.out::println;\n" + " Iface<Integer> mRef = Ty::<Integer>meth;\n" + "}", + format("private void foo() {\n" + " f(this ::methodReference);\n" + " f(C.super ::methodReference);\n" + " Consumer<String> c = System.out ::println;\n" + " Iface<Integer> mRef = Ty :: <Integer> meth;\n" + "}")); +} + TEST_F(FormatTestJava, CppKeywords) { verifyFormat("public void union(Type a, Type b);"); verifyFormat("public void struct(Object o);"); diff --git a/unittests/Format/FormatTestObjC.cpp b/unittests/Format/FormatTestObjC.cpp index e56df6d130..a417b6710d 100644 --- a/unittests/Format/FormatTestObjC.cpp +++ b/unittests/Format/FormatTestObjC.cpp @@ -584,6 +584,16 @@ TEST_F(FormatTestObjC, FormatObjCMethodDeclarations) { " aaaaa:(a)yyy\n" " bbb:(d)cccc;"); verifyFormat("- (void)drawRectOn:(id)surface ofSize:(aaa)height:(bbb)width;"); + + // BraceWrapping AfterFunction is respected for ObjC methods + Style = getGoogleStyle(FormatStyle::LK_ObjC); + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterFunction = true; + verifyFormat("@implementation Foo\n" + "- (void)foo:(id)bar\n" + "{\n" + "}\n" + "@end\n"); } TEST_F(FormatTestObjC, FormatObjCMethodExpr) { diff --git a/unittests/Format/FormatTestRawStrings.cpp b/unittests/Format/FormatTestRawStrings.cpp index 307394341d..2a8a43dc95 100644 --- a/unittests/Format/FormatTestRawStrings.cpp +++ b/unittests/Format/FormatTestRawStrings.cpp @@ -576,10 +576,13 @@ auto S = auto S = R"pb(item_1:1 item_2:2)pb"+rest;)test", getRawStringPbStyleWithColumns(40))); + // `rest` fits on the line after )pb", but forced on newline since the raw + // string literal is multiline. expect_eq(R"test( auto S = R"pb(item_1: 1, item_2: 2, - item_3: 3)pb" + rest;)test", + item_3: 3)pb" + + rest;)test", format(R"test( auto S = R"pb(item_1:1,item_2:2,item_3:3)pb"+rest;)test", getRawStringPbStyleWithColumns(40))); @@ -615,7 +618,8 @@ auto S = first+R"pb(item_1:1 item_2:2)pb";)test", expect_eq(R"test( auto S = R"pb(item_1: 1, item_2: 2, - item_3: 3)pb" + rest;)test", + item_3: 3)pb" + + rest;)test", format(R"test( auto S = R"pb(item_1:1,item_2:2,item_3:3)pb"+rest;)test", getRawStringPbStyleWithColumns(40))); @@ -889,6 +893,95 @@ item { Style)); } +TEST_F(FormatTestRawStrings, + BreaksBeforeNextParamAfterMultilineRawStringParam) { + FormatStyle Style = getRawStringPbStyleWithColumns(60); + expect_eq(R"test( +int f() { + int a = g(x, R"pb( + key: 1 # + key: 2 + )pb", + 3, 4); +} +)test", + format(R"test( +int f() { + int a = g(x, R"pb( + key: 1 # + key: 2 + )pb", 3, 4); +} +)test", + Style)); + + // Breaks after a parent of a multiline param. + expect_eq(R"test( +int f() { + int a = g(x, h(R"pb( + key: 1 # + key: 2 + )pb"), + 3, 4); +} +)test", + format(R"test( +int f() { + int a = g(x, h(R"pb( + key: 1 # + key: 2 + )pb"), 3, 4); +} +)test", + Style)); + + expect_eq(R"test( +int f() { + int a = g(x, + h(R"pb( + key: 1 # + key: 2 + )pb", + 2), + 3, 4); +} +)test", + format(R"test( +int f() { + int a = g(x, h(R"pb( + key: 1 # + key: 2 + )pb", 2), 3, 4); +} +)test", + Style)); + // Breaks if formatting introduces a multiline raw string. + expect_eq(R"test( +int f() { + int a = g(x, R"pb(key1: value111111111 + key2: value2222222222)pb", + 3, 4); +} +)test", + format(R"test( +int f() { + int a = g(x, R"pb(key1: value111111111 key2: value2222222222)pb", 3, 4); +} +)test", + Style)); + // Does not force a break after an original multiline param that is + // reformatterd as on single line. + expect_eq(R"test( +int f() { + int a = g(R"pb(key: 1)pb", 2); +})test", + format(R"test( +int f() { + int a = g(R"pb(key: + 1)pb", 2); +})test", Style)); +} + } // end namespace } // end namespace format } // end namespace clang diff --git a/unittests/Format/FormatTestTableGen.cpp b/unittests/Format/FormatTestTableGen.cpp new file mode 100644 index 0000000000..820ea783cc --- /dev/null +++ b/unittests/Format/FormatTestTableGen.cpp @@ -0,0 +1,56 @@ +//===- unittest/Format/FormatTestTableGen.cpp -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FormatTestUtils.h" +#include "clang/Format/Format.h" +#include "llvm/Support/Debug.h" +#include "gtest/gtest.h" + +#define DEBUG_TYPE "format-test" + +namespace clang { +namespace format { + +class FormatTestTableGen : public ::testing::Test { +protected: + static std::string format(llvm::StringRef Code, unsigned Offset, + unsigned Length, const FormatStyle &Style) { + LLVM_DEBUG(llvm::errs() << "---\n"); + LLVM_DEBUG(llvm::errs() << Code << "\n\n"); + std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length)); + tooling::Replacements Replaces = reformat(Style, Code, Ranges); + auto Result = applyAllReplacements(Code, Replaces); + EXPECT_TRUE(static_cast<bool>(Result)); + LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); + return *Result; + } + + static std::string format(llvm::StringRef Code) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_TableGen); + Style.ColumnLimit = 60; // To make writing tests easier. + return format(Code, 0, Code.size(), Style); + } + + static void verifyFormat(llvm::StringRef Code) { + EXPECT_EQ(Code.str(), format(Code)) << "Expected code is not stable"; + EXPECT_EQ(Code.str(), format(test::messUp(Code))); + } +}; + +TEST_F(FormatTestTableGen, FormatStringBreak) { + verifyFormat("include \"OptParser.td\"\n" + "def flag : Flag<\"--foo\">,\n" + " HelpText<\n" + " \"This is a very, very, very, very, \"\n" + " \"very, very, very, very, very, very, \"\n" + " \"very long help string\">;\n"); +} + +} // namespace format +} // end namespace clang diff --git a/unittests/Format/SortImportsTestJava.cpp b/unittests/Format/SortImportsTestJava.cpp new file mode 100644 index 0000000000..3bcf809d96 --- /dev/null +++ b/unittests/Format/SortImportsTestJava.cpp @@ -0,0 +1,267 @@ +#include "clang/Format/Format.h" +#include "gtest/gtest.h" + +#define DEBUG_TYPE "format-test" + +namespace clang { +namespace format { +namespace { + +class SortImportsTestJava : public ::testing::Test { +protected: + std::vector<tooling::Range> GetCodeRange(StringRef Code) { + return std::vector<tooling::Range>(1, tooling::Range(0, Code.size())); + } + + std::string sort(StringRef Code, std::vector<tooling::Range> Ranges) { + auto Replaces = sortIncludes(FmtStyle, Code, Ranges, "input.java"); + Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); + auto Sorted = applyAllReplacements(Code, Replaces); + EXPECT_TRUE(static_cast<bool>(Sorted)); + auto Result = applyAllReplacements( + *Sorted, reformat(FmtStyle, *Sorted, Ranges, "input.java")); + EXPECT_TRUE(static_cast<bool>(Result)); + return *Result; + } + + std::string sort(StringRef Code) { return sort(Code, GetCodeRange(Code)); } + + FormatStyle FmtStyle; + +public: + SortImportsTestJava() { + FmtStyle = getGoogleStyle(FormatStyle::LK_Java); + FmtStyle.JavaImportGroups = {"com.test", "org", "com"}; + FmtStyle.SortIncludes = true; + } +}; + +TEST_F(SortImportsTestJava, StaticSplitFromNormal) { + EXPECT_EQ("import static org.b;\n" + "\n" + "import org.a;\n", + sort("import org.a;\n" + "import static org.b;\n")); +} + +TEST_F(SortImportsTestJava, CapitalBeforeLowercase) { + EXPECT_EQ("import org.Test;\n" + "import org.a.Test;\n" + "import org.b;\n", + sort("import org.a.Test;\n" + "import org.Test;\n" + "import org.b;\n")); +} + +TEST_F(SortImportsTestJava, KeepSplitGroupsWithOneNewImport) { + EXPECT_EQ("import static com.test.a;\n" + "\n" + "import static org.a;\n" + "\n" + "import static com.a;\n" + "\n" + "import com.test.b;\n" + "import com.test.c;\n" + "\n" + "import org.b;\n" + "\n" + "import com.b;\n", + sort("import static com.test.a;\n" + "\n" + "import static org.a;\n" + "\n" + "import static com.a;\n" + "\n" + "import com.test.b;\n" + "\n" + "import org.b;\n" + "\n" + "import com.b;\n" + "import com.test.c;\n")); +} + +TEST_F(SortImportsTestJava, SplitGroupsWithNewline) { + EXPECT_EQ("import static com.test.a;\n" + "\n" + "import static org.a;\n" + "\n" + "import static com.a;\n" + "\n" + "import com.test.b;\n" + "\n" + "import org.b;\n" + "\n" + "import com.b;\n", + sort("import static com.test.a;\n" + "import static org.a;\n" + "import static com.a;\n" + "import com.test.b;\n" + "import org.b;\n" + "import com.b;\n")); +} + +TEST_F(SortImportsTestJava, UnspecifiedGroupAfterAllGroups) { + EXPECT_EQ("import com.test.a;\n" + "\n" + "import org.a;\n" + "\n" + "import com.a;\n" + "\n" + "import abc.a;\n" + "import xyz.b;\n", + sort("import com.test.a;\n" + "import com.a;\n" + "import xyz.b;\n" + "import abc.a;\n" + "import org.a;\n")); +} + +TEST_F(SortImportsTestJava, NoSortOutsideRange) { + std::vector<tooling::Range> Ranges = {tooling::Range(27, 15)}; + EXPECT_EQ("import org.b;\n" + "import org.a;\n" + "// comments\n" + "// that do\n" + "// nothing\n", + sort("import org.b;\n" + "import org.a;\n" + "// comments\n" + "// that do\n" + "// nothing\n", + Ranges)); +} + +TEST_F(SortImportsTestJava, SortWhenRangeContainsOneLine) { + std::vector<tooling::Range> Ranges = {tooling::Range(27, 20)}; + EXPECT_EQ("import org.a;\n" + "import org.b;\n" + "\n" + "import com.a;\n" + "// comments\n" + "// that do\n" + "// nothing\n", + sort("import org.b;\n" + "import org.a;\n" + "import com.a;\n" + "// comments\n" + "// that do\n" + "// nothing\n", + Ranges)); +} + +TEST_F(SortImportsTestJava, SortLexicographically) { + EXPECT_EQ("import org.a.*;\n" + "import org.a.a;\n" + "import org.aA;\n" + "import org.aa;\n", + sort("import org.aa;\n" + "import org.a.a;\n" + "import org.a.*;\n" + "import org.aA;\n")); +} + +TEST_F(SortImportsTestJava, StaticInCommentHasNoEffect) { + EXPECT_EQ("import org.a; // static\n" + "import org.b;\n" + "import org.c; // static\n", + sort("import org.a; // static\n" + "import org.c; // static\n" + "import org.b;\n")); +} + +TEST_F(SortImportsTestJava, CommentsWithAffectedImports) { + EXPECT_EQ("import org.a;\n" + "// commentB\n" + "/* commentB\n" + " commentB*/\n" + "import org.b;\n" + "// commentC\n" + "import org.c;\n", + sort("import org.a;\n" + "// commentC\n" + "import org.c;\n" + "// commentB\n" + "/* commentB\n" + " commentB*/\n" + "import org.b;\n")); +} + +TEST_F(SortImportsTestJava, CommentWithUnaffectedImports) { + EXPECT_EQ("import org.a;\n" + "// comment\n" + "import org.b;\n", + sort("import org.a;\n" + "// comment\n" + "import org.b;\n")); +} + +TEST_F(SortImportsTestJava, CommentAfterAffectedImports) { + EXPECT_EQ("import org.a;\n" + "import org.b;\n" + "// comment\n", + sort("import org.b;\n" + "import org.a;\n" + "// comment\n")); +} + +TEST_F(SortImportsTestJava, CommentBeforeAffectedImports) { + EXPECT_EQ("// comment\n" + "import org.a;\n" + "import org.b;\n", + sort("// comment\n" + "import org.b;\n" + "import org.a;\n")); +} + +TEST_F(SortImportsTestJava, FormatTotallyOff) { + EXPECT_EQ("// clang-format off\n" + "import org.b;\n" + "import org.a;\n" + "// clang-format on\n", + sort("// clang-format off\n" + "import org.b;\n" + "import org.a;\n" + "// clang-format on\n")); +} + +TEST_F(SortImportsTestJava, FormatTotallyOn) { + EXPECT_EQ("// clang-format off\n" + "// clang-format on\n" + "import org.a;\n" + "import org.b;\n", + sort("// clang-format off\n" + "// clang-format on\n" + "import org.b;\n" + "import org.a;\n")); +} + +TEST_F(SortImportsTestJava, FormatPariallyOnShouldNotReorder) { + EXPECT_EQ("// clang-format off\n" + "import org.b;\n" + "import org.a;\n" + "// clang-format on\n" + "import org.d;\n" + "import org.c;\n", + sort("// clang-format off\n" + "import org.b;\n" + "import org.a;\n" + "// clang-format on\n" + "import org.d;\n" + "import org.c;\n")); +} + +TEST_F(SortImportsTestJava, DeduplicateImports) { + EXPECT_EQ("import org.a;\n", sort("import org.a;\n" + "import org.a;\n")); +} + +TEST_F(SortImportsTestJava, NoNewlineAtEnd) { + EXPECT_EQ("import org.a;\n" + "import org.b;", + sort("import org.b;\n" + "import org.a;")); +} + +} // end namespace +} // end namespace format +} // end namespace clang diff --git a/unittests/Frontend/CMakeLists.txt b/unittests/Frontend/CMakeLists.txt index e36570a88b..c7851bb394 100644 --- a/unittests/Frontend/CMakeLists.txt +++ b/unittests/Frontend/CMakeLists.txt @@ -21,4 +21,5 @@ target_link_libraries(FrontendTests clangSema clangCodeGen clangFrontendTool + clangSerialization ) diff --git a/unittests/Index/CMakeLists.txt b/unittests/Index/CMakeLists.txt index 32c36504c5..2756fad906 100644 --- a/unittests/Index/CMakeLists.txt +++ b/unittests/Index/CMakeLists.txt @@ -14,5 +14,6 @@ target_link_libraries(IndexTests clangFrontend clangIndex clangLex + clangSerialization clangTooling ) diff --git a/unittests/Index/IndexTests.cpp b/unittests/Index/IndexTests.cpp index e438537b43..2d4463d833 100644 --- a/unittests/Index/IndexTests.cpp +++ b/unittests/Index/IndexTests.cpp @@ -9,7 +9,6 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/Decl.h" -#include "clang/Basic/VirtualFileSystem.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Index/IndexDataConsumer.h" @@ -18,6 +17,7 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/VirtualFileSystem.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include <memory> diff --git a/unittests/Lex/HeaderSearchTest.cpp b/unittests/Lex/HeaderSearchTest.cpp index c2794f5e95..060135bc73 100644 --- a/unittests/Lex/HeaderSearchTest.cpp +++ b/unittests/Lex/HeaderSearchTest.cpp @@ -27,7 +27,7 @@ namespace { class HeaderSearchTest : public ::testing::Test { protected: HeaderSearchTest() - : VFS(new vfs::InMemoryFileSystem), FileMgr(FileMgrOpts, VFS), + : VFS(new llvm::vfs::InMemoryFileSystem), FileMgr(FileMgrOpts, VFS), DiagID(new DiagnosticIDs()), Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()), SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions), @@ -46,7 +46,7 @@ protected: Search.AddSearchPath(DL, /*isAngled=*/false); } - IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS; + IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS; FileSystemOptions FileMgrOpts; FileManager FileMgr; IntrusiveRefCntPtr<DiagnosticIDs> DiagID; diff --git a/unittests/Lex/PPCallbacksTest.cpp b/unittests/Lex/PPCallbacksTest.cpp index 0fa6286cc0..d0ff45ec7c 100644 --- a/unittests/Lex/PPCallbacksTest.cpp +++ b/unittests/Lex/PPCallbacksTest.cpp @@ -95,7 +95,7 @@ public: class PPCallbacksTest : public ::testing::Test { protected: PPCallbacksTest() - : InMemoryFileSystem(new vfs::InMemoryFileSystem), + : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem), FileMgr(FileSystemOptions(), InMemoryFileSystem), DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()), Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()), @@ -104,7 +104,7 @@ protected: Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); } - IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem; + IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem; FileManager FileMgr; IntrusiveRefCntPtr<DiagnosticIDs> DiagID; IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; @@ -116,17 +116,17 @@ protected: // Register a header path as a known file and add its location // to search path. - void AddFakeHeader(HeaderSearch& HeaderInfo, const char* HeaderPath, - bool IsSystemHeader) { - // Tell FileMgr about header. - InMemoryFileSystem->addFile(HeaderPath, 0, - llvm::MemoryBuffer::getMemBuffer("\n")); - - // Add header's parent path to search path. - StringRef SearchPath = llvm::sys::path::parent_path(HeaderPath); - const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath); - DirectoryLookup DL(DE, SrcMgr::C_User, false); - HeaderInfo.AddSearchPath(DL, IsSystemHeader); + void AddFakeHeader(HeaderSearch &HeaderInfo, const char *HeaderPath, + bool IsSystemHeader) { + // Tell FileMgr about header. + InMemoryFileSystem->addFile(HeaderPath, 0, + llvm::MemoryBuffer::getMemBuffer("\n")); + + // Add header's parent path to search path. + StringRef SearchPath = llvm::sys::path::parent_path(HeaderPath); + const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath); + DirectoryLookup DL(DE, SrcMgr::C_User, false); + HeaderInfo.AddSearchPath(DL, IsSystemHeader); } // Get the raw source string of the range. @@ -139,8 +139,9 @@ protected: // Run lexer over SourceText and collect FilenameRange from // the InclusionDirective callback. - CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText, - const char* HeaderPath, bool SystemHeader) { + CharSourceRange InclusionDirectiveFilenameRange(const char *SourceText, + const char *HeaderPath, + bool SystemHeader) { std::unique_ptr<llvm::MemoryBuffer> Buf = llvm::MemoryBuffer::getMemBuffer(SourceText); SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); @@ -198,8 +199,8 @@ protected: return Callbacks; } - PragmaOpenCLExtensionCallbacks::CallbackParameters - PragmaOpenCLExtensionCall(const char* SourceText) { + PragmaOpenCLExtensionCallbacks::CallbackParameters + PragmaOpenCLExtensionCall(const char *SourceText) { LangOptions OpenCLLangOpts; OpenCLLangOpts.OpenCL = 1; @@ -221,9 +222,8 @@ protected: // parser actually sets correct pragma handlers for preprocessor // according to LangOptions, so we init Parser to register opencl // pragma handlers - ASTContext Context(OpenCLLangOpts, SourceMgr, - PP.getIdentifierTable(), PP.getSelectorTable(), - PP.getBuiltinInfo()); + ASTContext Context(OpenCLLangOpts, SourceMgr, PP.getIdentifierTable(), + PP.getSelectorTable(), PP.getBuiltinInfo()); Context.InitBuiltinTypes(*Target); ASTConsumer Consumer; @@ -245,7 +245,7 @@ protected: Callbacks->Name, Callbacks->State }; - return RetVal; + return RetVal; } }; diff --git a/unittests/Rename/CMakeLists.txt b/unittests/Rename/CMakeLists.txt index b625a7a691..f91021dd15 100644 --- a/unittests/Rename/CMakeLists.txt +++ b/unittests/Rename/CMakeLists.txt @@ -21,6 +21,7 @@ target_link_libraries(ClangRenameTests clangFormat clangFrontend clangRewrite + clangSerialization clangTooling clangToolingCore clangToolingRefactor diff --git a/unittests/Rename/ClangRenameTest.h b/unittests/Rename/ClangRenameTest.h index e0f33674cf..13906d15bc 100644 --- a/unittests/Rename/ClangRenameTest.h +++ b/unittests/Rename/ClangRenameTest.h @@ -14,7 +14,6 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" -#include "clang/Basic/VirtualFileSystem.h" #include "clang/Format/Format.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/PCHContainerOperations.h" @@ -26,6 +25,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Format.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" #include "gtest/gtest.h" #include <memory> #include <string> diff --git a/unittests/Sema/CMakeLists.txt b/unittests/Sema/CMakeLists.txt index 45460f1e0f..78601046dc 100644 --- a/unittests/Sema/CMakeLists.txt +++ b/unittests/Sema/CMakeLists.txt @@ -14,5 +14,6 @@ target_link_libraries(SemaTests clangFrontend clangParse clangSema + clangSerialization clangTooling ) diff --git a/unittests/Sema/CodeCompleteTest.cpp b/unittests/Sema/CodeCompleteTest.cpp index 04cb14b971..294807c56c 100644 --- a/unittests/Sema/CodeCompleteTest.cpp +++ b/unittests/Sema/CodeCompleteTest.cpp @@ -14,31 +14,39 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Tooling/Tooling.h" -#include "gtest/gtest.h" #include "gmock/gmock.h" +#include "gtest/gtest.h" +#include <cstddef> +#include <string> namespace { using namespace clang; using namespace clang::tooling; +using ::testing::Each; using ::testing::UnorderedElementsAre; const char TestCCName[] = "test.cc"; -using VisitedContextResults = std::vector<std::string>; -class VisitedContextFinder: public CodeCompleteConsumer { +struct CompletionContext { + std::vector<std::string> VisitedNamespaces; + std::string PreferredType; +}; + +class VisitedContextFinder : public CodeCompleteConsumer { public: - VisitedContextFinder(VisitedContextResults &Results) + VisitedContextFinder(CompletionContext &ResultCtx) : CodeCompleteConsumer(/*CodeCompleteOpts=*/{}, /*CodeCompleteConsumer*/ false), - VCResults(Results), + ResultCtx(ResultCtx), CCTUInfo(std::make_shared<GlobalCodeCompletionAllocator>()) {} void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, CodeCompletionResult *Results, unsigned NumResults) override { - VisitedContexts = Context.getVisitedContexts(); - VCResults = getVisitedNamespace(); + ResultCtx.VisitedNamespaces = + getVisitedNamespace(Context.getVisitedContexts()); + ResultCtx.PreferredType = Context.getPreferredType().getAsString(); } CodeCompletionAllocator &getAllocator() override { @@ -47,7 +55,9 @@ public: CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } - std::vector<std::string> getVisitedNamespace() const { +private: + std::vector<std::string> getVisitedNamespace( + CodeCompletionContext::VisitedContextSet VisitedContexts) const { std::vector<std::string> NSNames; for (const auto *Context : VisitedContexts) if (const auto *NS = llvm::dyn_cast<NamespaceDecl>(Context)) @@ -55,27 +65,25 @@ public: return NSNames; } -private: - VisitedContextResults& VCResults; + CompletionContext &ResultCtx; CodeCompletionTUInfo CCTUInfo; - CodeCompletionContext::VisitedContextSet VisitedContexts; }; class CodeCompleteAction : public SyntaxOnlyAction { public: - CodeCompleteAction(ParsedSourceLocation P, VisitedContextResults &Results) - : CompletePosition(std::move(P)), VCResults(Results) {} + CodeCompleteAction(ParsedSourceLocation P, CompletionContext &ResultCtx) + : CompletePosition(std::move(P)), ResultCtx(ResultCtx) {} bool BeginInvocation(CompilerInstance &CI) override { CI.getFrontendOpts().CodeCompletionAt = CompletePosition; - CI.setCodeCompletionConsumer(new VisitedContextFinder(VCResults)); + CI.setCodeCompletionConsumer(new VisitedContextFinder(ResultCtx)); return true; } private: // 1-based code complete position <Line, Col>; ParsedSourceLocation CompletePosition; - VisitedContextResults& VCResults; + CompletionContext &ResultCtx; }; ParsedSourceLocation offsetToPosition(llvm::StringRef Code, size_t Offset) { @@ -88,21 +96,49 @@ ParsedSourceLocation offsetToPosition(llvm::StringRef Code, size_t Offset) { static_cast<unsigned>(Offset - StartOfLine + 1)}; } -VisitedContextResults runCodeCompleteOnCode(StringRef Code) { - VisitedContextResults Results; - auto TokenOffset = Code.find('^'); - assert(TokenOffset != StringRef::npos && - "Completion token ^ wasn't found in Code."); - std::string WithoutToken = Code.take_front(TokenOffset); - WithoutToken += Code.drop_front(WithoutToken.size() + 1); - assert(StringRef(WithoutToken).find('^') == StringRef::npos && - "expected exactly one completion token ^ inside the code"); - +CompletionContext runCompletion(StringRef Code, size_t Offset) { + CompletionContext ResultCtx; auto Action = llvm::make_unique<CodeCompleteAction>( - offsetToPosition(WithoutToken, TokenOffset), Results); + offsetToPosition(Code, Offset), ResultCtx); clang::tooling::runToolOnCodeWithArgs(Action.release(), Code, {"-std=c++11"}, TestCCName); - return Results; + return ResultCtx; +} + +struct ParsedAnnotations { + std::vector<size_t> Points; + std::string Code; +}; + +ParsedAnnotations parseAnnotations(StringRef AnnotatedCode) { + ParsedAnnotations R; + while (!AnnotatedCode.empty()) { + size_t NextPoint = AnnotatedCode.find('^'); + if (NextPoint == StringRef::npos) { + R.Code += AnnotatedCode; + AnnotatedCode = ""; + break; + } + R.Code += AnnotatedCode.substr(0, NextPoint); + R.Points.push_back(R.Code.size()); + + AnnotatedCode = AnnotatedCode.substr(NextPoint + 1); + } + return R; +} + +CompletionContext runCodeCompleteOnCode(StringRef AnnotatedCode) { + ParsedAnnotations P = parseAnnotations(AnnotatedCode); + assert(P.Points.size() == 1 && "expected exactly one annotation point"); + return runCompletion(P.Code, P.Points.front()); +} + +std::vector<std::string> collectPreferredTypes(StringRef AnnotatedCode) { + ParsedAnnotations P = parseAnnotations(AnnotatedCode); + std::vector<std::string> Types; + for (size_t Point : P.Points) + Types.push_back(runCompletion(P.Code, Point).PreferredType); + return Types; } TEST(SemaCodeCompleteTest, VisitedNSForValidQualifiedId) { @@ -119,7 +155,8 @@ TEST(SemaCodeCompleteTest, VisitedNSForValidQualifiedId) { inline namespace bar { using namespace ns3::nns3; } } // foo namespace ns { foo::^ } - )cpp"); + )cpp") + .VisitedNamespaces; EXPECT_THAT(VisitedNS, UnorderedElementsAre("foo", "ns1", "ns2", "ns3::nns3", "foo::(anonymous)")); } @@ -127,7 +164,8 @@ TEST(SemaCodeCompleteTest, VisitedNSForValidQualifiedId) { TEST(SemaCodeCompleteTest, VisitedNSForInvalideQualifiedId) { auto VisitedNS = runCodeCompleteOnCode(R"cpp( namespace ns { foo::^ } - )cpp"); + )cpp") + .VisitedNamespaces; EXPECT_TRUE(VisitedNS.empty()); } @@ -138,8 +176,153 @@ TEST(SemaCodeCompleteTest, VisitedNSWithoutQualifier) { void f(^) {} } } - )cpp"); + )cpp") + .VisitedNamespaces; EXPECT_THAT(VisitedNS, UnorderedElementsAre("n1", "n1::n2")); } +TEST(PreferredTypeTest, BinaryExpr) { + // Check various operations for arithmetic types. + StringRef Code = R"cpp( + void test(int x) { + x = ^10; + x += ^10; x -= ^10; x *= ^10; x /= ^10; x %= ^10; + x + ^10; x - ^10; x * ^10; x / ^10; x % ^10; + })cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("int")); + + Code = R"cpp( + void test(float x) { + x = ^10; + x += ^10; x -= ^10; x *= ^10; x /= ^10; x %= ^10; + x + ^10; x - ^10; x * ^10; x / ^10; x % ^10; + })cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("float")); + + // Pointer types. + Code = R"cpp( + void test(int *ptr) { + ptr - ^ptr; + ptr = ^ptr; + })cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("int *")); + + Code = R"cpp( + void test(int *ptr) { + ptr + ^10; + ptr += ^10; + ptr -= ^10; + })cpp"; + // Expect the normalized ptrdiff_t type, which is typically long or long long. + const char *PtrDiff = sizeof(void *) == sizeof(long) ? "long" : "long long"; + EXPECT_THAT(collectPreferredTypes(Code), Each(PtrDiff)); + + // Comparison operators. + Code = R"cpp( + void test(int i) { + i <= ^1; i < ^1; i >= ^1; i > ^1; i == ^1; i != ^1; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("int")); + + Code = R"cpp( + void test(int *ptr) { + ptr <= ^ptr; ptr < ^ptr; ptr >= ^ptr; ptr > ^ptr; + ptr == ^ptr; ptr != ^ptr; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("int *")); + + // Relational operations. + Code = R"cpp( + void test(int i, int *ptr) { + i && ^1; i || ^1; + ptr && ^1; ptr || ^1; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool")); + + // Bitwise operations. + Code = R"cpp( + void test(long long ll) { + ll | ^1; ll & ^1; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("long long")); + + Code = R"cpp( + enum A {}; + void test(A a) { + a | ^1; a & ^1; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("enum A")); + + Code = R"cpp( + enum class A {}; + void test(A a) { + // This is technically illegal with the 'enum class' without overloaded + // operators, but we pretend it's fine. + a | ^a; a & ^a; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("enum A")); + + // Binary shifts. + Code = R"cpp( + void test(int i, long long ll) { + i << ^1; ll << ^1; + i <<= ^1; i <<= ^1; + i >> ^1; ll >> ^1; + i >>= ^1; i >>= ^1; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("int")); + + // Comma does not provide any useful information. + Code = R"cpp( + class Cls {}; + void test(int i, int* ptr, Cls x) { + (i, ^i); + (ptr, ^ptr); + (x, ^x); + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE")); + + // User-defined types do not take operator overloading into account. + // However, they provide heuristics for some common cases. + Code = R"cpp( + class Cls {}; + void test(Cls c) { + // we assume arithmetic and comparions ops take the same type. + c + ^c; c - ^c; c * ^c; c / ^c; c % ^c; + c == ^c; c != ^c; c < ^c; c <= ^c; c > ^c; c >= ^c; + // same for the assignments. + c = ^c; c += ^c; c -= ^c; c *= ^c; c /= ^c; c %= ^c; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("class Cls")); + + Code = R"cpp( + class Cls {}; + void test(Cls c) { + // we assume relational ops operate on bools. + c && ^c; c || ^c; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool")); + + Code = R"cpp( + class Cls {}; + void test(Cls c) { + // we make no assumptions about the following operators, since they are + // often overloaded with a non-standard meaning. + c << ^c; c >> ^c; c | ^c; c & ^c; + c <<= ^c; c >>= ^c; c |= ^c; c &= ^c; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE")); +} + } // namespace diff --git a/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp b/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp index 3d8332554f..de41874bde 100644 --- a/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp +++ b/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp @@ -52,23 +52,25 @@ TEST(StaticAnalyzerOptions, SearchInParentPackageTests) { // Checker one has Option specified as true. It should read true regardless of // search mode. CheckerOneMock CheckerOne; - EXPECT_TRUE(Opts.getBooleanOption("Option", false, &CheckerOne)); + EXPECT_TRUE(Opts.getCheckerBooleanOption("Option", false, &CheckerOne)); // The package option is overridden with a checker option. - EXPECT_TRUE(Opts.getBooleanOption("Option", false, &CheckerOne, true)); + EXPECT_TRUE(Opts.getCheckerBooleanOption("Option", false, &CheckerOne, + true)); // The Outer package option is overridden by the Inner package option. No // package option is specified. - EXPECT_TRUE(Opts.getBooleanOption("Option2", false, &CheckerOne, true)); + EXPECT_TRUE(Opts.getCheckerBooleanOption("Option2", false, &CheckerOne, + true)); // No package option is specified and search in packages is turned off. The // default value should be returned. - EXPECT_FALSE(Opts.getBooleanOption("Option2", false, &CheckerOne)); - EXPECT_TRUE(Opts.getBooleanOption("Option2", true, &CheckerOne)); + EXPECT_FALSE(Opts.getCheckerBooleanOption("Option2", false, &CheckerOne)); + EXPECT_TRUE(Opts.getCheckerBooleanOption("Option2", true, &CheckerOne)); // Checker true has no option specified. It should get the default value when // search in parents turned off and false when search in parents turned on. CheckerTwoMock CheckerTwo; - EXPECT_FALSE(Opts.getBooleanOption("Option", false, &CheckerTwo)); - EXPECT_TRUE(Opts.getBooleanOption("Option", true, &CheckerTwo)); - EXPECT_FALSE(Opts.getBooleanOption("Option", true, &CheckerTwo, true)); + EXPECT_FALSE(Opts.getCheckerBooleanOption("Option", false, &CheckerTwo)); + EXPECT_TRUE(Opts.getCheckerBooleanOption("Option", true, &CheckerTwo)); + EXPECT_FALSE(Opts.getCheckerBooleanOption("Option", true, &CheckerTwo, true)); } TEST(StaticAnalyzerOptions, StringOptions) { @@ -83,9 +85,9 @@ TEST(StaticAnalyzerOptions, StringOptions) { CheckerOneMock CheckerOne; EXPECT_TRUE("StringValue" == - Opts.getOptionAsString("Option", "DefaultValue", &CheckerOne)); + Opts.getCheckerStringOption("Option", "DefaultValue", &CheckerOne)); EXPECT_TRUE("DefaultValue" == - Opts.getOptionAsString("Option2", "DefaultValue", &CheckerOne)); + Opts.getCheckerStringOption("Option2", "DefaultValue", &CheckerOne)); } } // end namespace ento } // end namespace clang diff --git a/unittests/StaticAnalyzer/CMakeLists.txt b/unittests/StaticAnalyzer/CMakeLists.txt index ff041452df..3036dec167 100644 --- a/unittests/StaticAnalyzer/CMakeLists.txt +++ b/unittests/StaticAnalyzer/CMakeLists.txt @@ -12,6 +12,7 @@ target_link_libraries(StaticAnalysisTests clangBasic clangAnalysis clangFrontend + clangSerialization clangStaticAnalyzerCore clangStaticAnalyzerFrontend clangTooling diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt index e9910fb87a..7619c7fb23 100644 --- a/unittests/Tooling/CMakeLists.txt +++ b/unittests/Tooling/CMakeLists.txt @@ -40,6 +40,7 @@ add_clang_unittest(ToolingTests RecursiveASTVisitorTests/NestedNameSpecifiers.cpp RecursiveASTVisitorTests/ParenExpr.cpp RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp + RecursiveASTVisitorTests/TraversalScope.cpp RecursiveASTVisitorTestDeclVisitor.cpp RecursiveASTVisitorTestPostOrderVisitor.cpp RecursiveASTVisitorTestTypeLocVisitor.cpp @@ -60,6 +61,7 @@ target_link_libraries(ToolingTests clangFrontend clangLex clangRewrite + clangSerialization clangTooling clangToolingCore clangToolingInclusions diff --git a/unittests/Tooling/DiagnosticsYamlTest.cpp b/unittests/Tooling/DiagnosticsYamlTest.cpp index 83e09eaeef..f4de53fad2 100644 --- a/unittests/Tooling/DiagnosticsYamlTest.cpp +++ b/unittests/Tooling/DiagnosticsYamlTest.cpp @@ -20,16 +20,21 @@ using namespace llvm; using namespace clang::tooling; using clang::tooling::Diagnostic; -static Diagnostic makeDiagnostic(StringRef DiagnosticName, - const std::string &Message, int FileOffset, - const std::string &FilePath, - const StringMap<Replacements> &Fix) { +static DiagnosticMessage makeMessage(const std::string &Message, int FileOffset, + const std::string &FilePath) { DiagnosticMessage DiagMessage; DiagMessage.Message = Message; DiagMessage.FileOffset = FileOffset; DiagMessage.FilePath = FilePath; - return Diagnostic(DiagnosticName, DiagMessage, Fix, {}, Diagnostic::Warning, - "path/to/build/directory"); + return DiagMessage; +} + +static Diagnostic makeDiagnostic(StringRef DiagnosticName, + const std::string &Message, int FileOffset, + const std::string &FilePath, + const StringMap<Replacements> &Fix) { + return Diagnostic(DiagnosticName, makeMessage(Message, FileOffset, FilePath), + Fix, {}, Diagnostic::Warning, "path/to/build/directory"); } TEST(DiagnosticsYamlTest, serializesDiagnostics) { @@ -50,6 +55,10 @@ TEST(DiagnosticsYamlTest, serializesDiagnostics) { TUD.Diagnostics.push_back(makeDiagnostic("diagnostic#3", "message #3", 72, "path/to/source2.cpp", {})); + TUD.Diagnostics.back().Notes.push_back( + makeMessage("Note1", 88, "path/to/note1.cpp")); + TUD.Diagnostics.back().Notes.push_back( + makeMessage("Note2", 99, "path/to/note2.cpp")); std::string YamlContent; raw_string_ostream YamlContentStream(YamlContent); @@ -58,31 +67,38 @@ TEST(DiagnosticsYamlTest, serializesDiagnostics) { YAML << TUD; EXPECT_EQ("---\n" - "MainSourceFile: path/to/source.cpp\n" + "MainSourceFile: 'path/to/source.cpp'\n" "Diagnostics: \n" " - DiagnosticName: 'diagnostic#1\'\n" " Message: 'message #1'\n" " FileOffset: 55\n" - " FilePath: path/to/source.cpp\n" + " FilePath: 'path/to/source.cpp'\n" " Replacements: \n" - " - FilePath: path/to/source.cpp\n" + " - FilePath: 'path/to/source.cpp'\n" " Offset: 100\n" " Length: 12\n" " ReplacementText: 'replacement #1'\n" " - DiagnosticName: 'diagnostic#2'\n" " Message: 'message #2'\n" " FileOffset: 60\n" - " FilePath: path/to/header.h\n" + " FilePath: 'path/to/header.h'\n" " Replacements: \n" - " - FilePath: path/to/header.h\n" + " - FilePath: 'path/to/header.h'\n" " Offset: 62\n" " Length: 2\n" " ReplacementText: 'replacement #2'\n" " - DiagnosticName: 'diagnostic#3'\n" " Message: 'message #3'\n" " FileOffset: 72\n" - " FilePath: path/to/source2.cpp\n" - " Replacements: \n" + " FilePath: 'path/to/source2.cpp'\n" + " Notes: \n" + " - Message: Note1\n" + " FilePath: 'path/to/note1.cpp'\n" + " FileOffset: 88\n" + " - Message: Note2\n" + " FilePath: 'path/to/note2.cpp'\n" + " FileOffset: 99\n" + " Replacements: []\n" "...\n", YamlContentStream.str()); } @@ -113,7 +129,14 @@ TEST(DiagnosticsYamlTest, deserializesDiagnostics) { " Message: 'message #3'\n" " FileOffset: 98\n" " FilePath: path/to/source.cpp\n" - " Replacements: \n" + " Notes:\n" + " - Message: Note1\n" + " FilePath: 'path/to/note1.cpp'\n" + " FileOffset: 66\n" + " - Message: Note2\n" + " FilePath: 'path/to/note2.cpp'\n" + " FileOffset: 77\n" + " Replacements: []\n" "...\n"; TranslationUnitDiagnostics TUDActual; yaml::Input YAML(YamlContent); @@ -162,6 +185,13 @@ TEST(DiagnosticsYamlTest, deserializesDiagnostics) { EXPECT_EQ("message #3", D3.Message.Message); EXPECT_EQ(98u, D3.Message.FileOffset); EXPECT_EQ("path/to/source.cpp", D3.Message.FilePath); + EXPECT_EQ(2u, D3.Notes.size()); + EXPECT_EQ("Note1", D3.Notes[0].Message); + EXPECT_EQ(66u, D3.Notes[0].FileOffset); + EXPECT_EQ("path/to/note1.cpp", D3.Notes[0].FilePath); + EXPECT_EQ("Note2", D3.Notes[1].Message); + EXPECT_EQ(77u, D3.Notes[1].FileOffset); + EXPECT_EQ("path/to/note2.cpp", D3.Notes[1].FilePath); std::vector<Replacement> Fixes3 = getFixes(D3.Fix); EXPECT_TRUE(Fixes3.empty()); } diff --git a/unittests/Tooling/ExecutionTest.cpp b/unittests/Tooling/ExecutionTest.cpp index e5dc98d228..785ec7c2bc 100644 --- a/unittests/Tooling/ExecutionTest.cpp +++ b/unittests/Tooling/ExecutionTest.cpp @@ -248,12 +248,14 @@ private: MATCHER_P(Named, Name, "") { return arg.first == Name; } TEST(AllTUsToolTest, AFewFiles) { - FixedCompilationDatabaseWithFiles Compilations(".", {"a.cc", "b.cc", "c.cc"}, - std::vector<std::string>()); + FixedCompilationDatabaseWithFiles Compilations( + ".", {"a.cc", "b.cc", "c.cc", "ignore.cc"}, std::vector<std::string>()); AllTUsToolExecutor Executor(Compilations, /*ThreadCount=*/0); + Filter.setValue("[a-c].cc"); Executor.mapVirtualFile("a.cc", "void x() {}"); Executor.mapVirtualFile("b.cc", "void y() {}"); Executor.mapVirtualFile("c.cc", "void z() {}"); + Executor.mapVirtualFile("ignore.cc", "void d() {}"); auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>( new ReportResultActionFactory(Executor.getExecutionContext()))); @@ -261,6 +263,7 @@ TEST(AllTUsToolTest, AFewFiles) { EXPECT_THAT( Executor.getToolResults()->AllKVResults(), ::testing::UnorderedElementsAre(Named("x"), Named("y"), Named("z"))); + Filter.setValue(".*"); // reset to default value. } TEST(AllTUsToolTest, ManyFiles) { diff --git a/unittests/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp b/unittests/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp new file mode 100644 index 0000000000..72f6c644b3 --- /dev/null +++ b/unittests/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp @@ -0,0 +1,51 @@ +//===- unittest/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestVisitor.h" + +using namespace clang; + +namespace { + +class Visitor : public ExpectedLocationVisitor<Visitor, clang::TestVisitor> { +public: + Visitor(ASTContext *Context) { this->Context = Context; } + + bool VisitNamedDecl(NamedDecl *D) { + if (!D->isImplicit()) + Match(D->getName(), D->getLocation()); + return true; + } +}; + +TEST(RecursiveASTVisitor, RespectsTraversalScope) { + auto AST = tooling::buildASTFromCode( + R"cpp( +struct foo { + struct bar { + struct baz {}; + }; +}; + )cpp", + "foo.cpp", std::make_shared<PCHContainerOperations>()); + auto &Ctx = AST->getASTContext(); + auto &TU = *Ctx.getTranslationUnitDecl(); + auto &Foo = *TU.lookup(&Ctx.Idents.get("foo")).front(); + auto &Bar = *cast<DeclContext>(Foo).lookup(&Ctx.Idents.get("bar")).front(); + + Ctx.setTraversalScope({&Bar}); + + Visitor V(&Ctx); + V.DisallowMatch("foo", 2, 8); + V.ExpectMatch("bar", 3, 10); + V.ExpectMatch("baz", 4, 12); + V.TraverseAST(Ctx); +} + +} // end anonymous namespace diff --git a/unittests/Tooling/RefactoringActionRulesTest.cpp b/unittests/Tooling/RefactoringActionRulesTest.cpp index e9a12deb3f..acacfa05b4 100644 --- a/unittests/Tooling/RefactoringActionRulesTest.cpp +++ b/unittests/Tooling/RefactoringActionRulesTest.cpp @@ -117,8 +117,8 @@ TEST_F(RefactoringActionRulesTest, MyFirstRefactoringRule) { "Key: 'input.cpp:30'\n" "FilePath: input.cpp\n" "Error: ''\n" - "InsertedHeaders: \n" - "RemovedHeaders: \n" + "InsertedHeaders: []\n" + "RemovedHeaders: []\n" "Replacements: \n" // Extra whitespace here! " - FilePath: input.cpp\n" " Offset: 30\n" diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp index fcd2aa5937..d618c0fb07 100644 --- a/unittests/Tooling/RefactoringTest.cpp +++ b/unittests/Tooling/RefactoringTest.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Tooling/Refactoring.h" #include "ReplacementTest.h" #include "RewriterTestContext.h" #include "clang/AST/ASTConsumer.h" @@ -19,16 +20,15 @@ #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" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Rewrite/Core/Rewriter.h" -#include "clang/Tooling/Refactoring.h" #include "clang/Tooling/Refactoring/AtomicChange.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/VirtualFileSystem.h" #include "gtest/gtest.h" namespace clang { @@ -1032,8 +1032,8 @@ TEST_F(MergeReplacementsTest, OverlappingRanges) { TEST(DeduplicateByFileTest, PathsWithDots) { std::map<std::string, Replacements> FileToReplaces; - llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS( - new vfs::InMemoryFileSystem()); + llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS( + new llvm::vfs::InMemoryFileSystem()); FileManager FileMgr(FileSystemOptions(), VFS); #if !defined(_WIN32) StringRef Path1 = "a/b/.././c.h"; @@ -1053,8 +1053,8 @@ TEST(DeduplicateByFileTest, PathsWithDots) { TEST(DeduplicateByFileTest, PathWithDotSlash) { std::map<std::string, Replacements> FileToReplaces; - llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS( - new vfs::InMemoryFileSystem()); + llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS( + new llvm::vfs::InMemoryFileSystem()); FileManager FileMgr(FileSystemOptions(), VFS); #if !defined(_WIN32) StringRef Path1 = "./a/b/c.h"; @@ -1074,8 +1074,8 @@ TEST(DeduplicateByFileTest, PathWithDotSlash) { TEST(DeduplicateByFileTest, NonExistingFilePath) { std::map<std::string, Replacements> FileToReplaces; - llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS( - new vfs::InMemoryFileSystem()); + llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS( + new llvm::vfs::InMemoryFileSystem()); FileManager FileMgr(FileSystemOptions(), VFS); #if !defined(_WIN32) StringRef Path1 = "./a/b/c.h"; diff --git a/unittests/Tooling/ReplacementsYamlTest.cpp b/unittests/Tooling/ReplacementsYamlTest.cpp index 3e4193d2ad..2e5a87a931 100644 --- a/unittests/Tooling/ReplacementsYamlTest.cpp +++ b/unittests/Tooling/ReplacementsYamlTest.cpp @@ -33,13 +33,13 @@ TEST(ReplacementsYamlTest, serializesReplacements) { // NOTE: If this test starts to fail for no obvious reason, check whitespace. ASSERT_STREQ("---\n" - "MainSourceFile: /path/to/source.cpp\n" + "MainSourceFile: '/path/to/source.cpp'\n" "Replacements: \n" // Extra whitespace here! - " - FilePath: /path/to/file1.h\n" + " - FilePath: '/path/to/file1.h'\n" " Offset: 232\n" " Length: 56\n" " ReplacementText: 'replacement #1'\n" - " - FilePath: /path/to/file2.h\n" + " - FilePath: '/path/to/file2.h'\n" " Offset: 301\n" " Length: 2\n" " ReplacementText: 'replacement #2'\n" diff --git a/unittests/Tooling/RewriterTestContext.h b/unittests/Tooling/RewriterTestContext.h index eee7ea1873..9e66484158 100644 --- a/unittests/Tooling/RewriterTestContext.h +++ b/unittests/Tooling/RewriterTestContext.h @@ -39,15 +39,15 @@ class RewriterTestContext { Diagnostics(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts), DiagnosticPrinter(llvm::outs(), &*DiagOpts), - InMemoryFileSystem(new vfs::InMemoryFileSystem), + InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem), OverlayFileSystem( - new vfs::OverlayFileSystem(vfs::getRealFileSystem())), + new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())), Files(FileSystemOptions(), OverlayFileSystem), Sources(Diagnostics, Files), Rewrite(Sources, Options) { - Diagnostics.setClient(&DiagnosticPrinter, false); - // FIXME: To make these tests truly in-memory, we need to overlay the - // builtin headers. - OverlayFileSystem->pushOverlay(InMemoryFileSystem); + Diagnostics.setClient(&DiagnosticPrinter, false); + // FIXME: To make these tests truly in-memory, we need to overlay the + // builtin headers. + OverlayFileSystem->pushOverlay(InMemoryFileSystem); } ~RewriterTestContext() {} @@ -114,8 +114,8 @@ class RewriterTestContext { IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; DiagnosticsEngine Diagnostics; TextDiagnosticPrinter DiagnosticPrinter; - IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem; - IntrusiveRefCntPtr<vfs::OverlayFileSystem> OverlayFileSystem; + IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem; + IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem; FileManager Files; SourceManager Sources; LangOptions Options; diff --git a/unittests/Tooling/TestVisitor.h b/unittests/Tooling/TestVisitor.h index fb6a76ccad..1a22ae737b 100644 --- a/unittests/Tooling/TestVisitor.h +++ b/unittests/Tooling/TestVisitor.h @@ -44,6 +44,8 @@ public: Lang_CXX98, Lang_CXX11, Lang_CXX14, + Lang_CXX17, + Lang_CXX2a, Lang_OBJC, Lang_OBJCXX11, Lang_CXX = Lang_CXX98 @@ -60,6 +62,8 @@ public: 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_CXX17: Args.push_back("-std=c++17"); break; + case Lang_CXX2a: Args.push_back("-std=c++2a"); break; case Lang_OBJC: Args.push_back("-ObjC"); Args.push_back("-fobjc-runtime=macosx-10.12.0"); diff --git a/unittests/Tooling/ToolingTest.cpp b/unittests/Tooling/ToolingTest.cpp index 0a5150a31b..186463f80a 100644 --- a/unittests/Tooling/ToolingTest.cpp +++ b/unittests/Tooling/ToolingTest.cpp @@ -149,10 +149,10 @@ TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) { } TEST(ToolInvocation, TestMapVirtualFile) { - llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> OverlayFileSystem( - new vfs::OverlayFileSystem(vfs::getRealFileSystem())); - llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( - new vfs::InMemoryFileSystem); + llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( + new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); + llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); OverlayFileSystem->pushOverlay(InMemoryFileSystem); llvm::IntrusiveRefCntPtr<FileManager> Files( new FileManager(FileSystemOptions(), OverlayFileSystem)); @@ -175,10 +175,10 @@ TEST(ToolInvocation, TestVirtualModulesCompilation) { // mapped module.map is found on the include path. In the future, expand this // test to run a full modules enabled compilation, so we make sure we can // rerun modules compilations with a virtual file system. - llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> OverlayFileSystem( - new vfs::OverlayFileSystem(vfs::getRealFileSystem())); - llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( - new vfs::InMemoryFileSystem); + llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( + new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); + llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); OverlayFileSystem->pushOverlay(InMemoryFileSystem); llvm::IntrusiveRefCntPtr<FileManager> Files( new FileManager(FileSystemOptions(), OverlayFileSystem)); @@ -403,10 +403,10 @@ TEST(ClangToolTest, ArgumentAdjusters) { TEST(ClangToolTest, BaseVirtualFileSystemUsage) { FixedCompilationDatabase Compilations("/", std::vector<std::string>()); - llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> OverlayFileSystem( - new vfs::OverlayFileSystem(vfs::getRealFileSystem())); - llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( - new vfs::InMemoryFileSystem); + llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem( + new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())); + llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); OverlayFileSystem->pushOverlay(InMemoryFileSystem); InMemoryFileSystem->addFile( |