diff options
Diffstat (limited to 'clang/unittests/AST/ASTImporterTest.cpp')
-rw-r--r-- | clang/unittests/AST/ASTImporterTest.cpp | 211 |
1 files changed, 197 insertions, 14 deletions
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 215dc10a398..d517d633c43 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -358,6 +358,106 @@ TEST_P(RedirectingImporterTest, InterceptIndirectImport) { EXPECT_EQ(0U, count); } +struct ImportPath : ASTImporterOptionSpecificTestBase { + Decl *FromTU; + FunctionDecl *D0, *D1, *D2; + ImportPath() { + FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX); + auto Pattern = functionDecl(hasName("f")); + D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); + D2 = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern); + D1 = D2->getPreviousDecl(); + } +}; + +TEST_P(ImportPath, Push) { + ASTImporter::ImportPathTy path; + path.push(D0); + EXPECT_FALSE(path.hasCycleAtBack()); +} + +TEST_P(ImportPath, SmallCycle) { + ASTImporter::ImportPathTy path; + path.push(D0); + path.push(D0); + EXPECT_TRUE(path.hasCycleAtBack()); + path.pop(); + EXPECT_FALSE(path.hasCycleAtBack()); + path.push(D0); + EXPECT_TRUE(path.hasCycleAtBack()); +} + +TEST_P(ImportPath, GetSmallCycle) { + ASTImporter::ImportPathTy path; + path.push(D0); + path.push(D0); + EXPECT_TRUE(path.hasCycleAtBack()); + std::array<Decl* ,2> Res; + int i = 0; + for (Decl *Di : path.getCycleAtBack()) { + Res[i++] = Di; + } + ASSERT_EQ(i, 2); + EXPECT_EQ(Res[0], D0); + EXPECT_EQ(Res[1], D0); +} + +TEST_P(ImportPath, GetCycle) { + ASTImporter::ImportPathTy path; + path.push(D0); + path.push(D1); + path.push(D2); + path.push(D0); + EXPECT_TRUE(path.hasCycleAtBack()); + std::array<Decl* ,4> Res; + int i = 0; + for (Decl *Di : path.getCycleAtBack()) { + Res[i++] = Di; + } + ASSERT_EQ(i, 4); + EXPECT_EQ(Res[0], D0); + EXPECT_EQ(Res[1], D2); + EXPECT_EQ(Res[2], D1); + EXPECT_EQ(Res[3], D0); +} + +TEST_P(ImportPath, CycleAfterCycle) { + ASTImporter::ImportPathTy path; + path.push(D0); + path.push(D1); + path.push(D0); + path.push(D1); + path.push(D2); + path.push(D0); + EXPECT_TRUE(path.hasCycleAtBack()); + std::array<Decl* ,4> Res; + int i = 0; + for (Decl *Di : path.getCycleAtBack()) { + Res[i++] = Di; + } + ASSERT_EQ(i, 4); + EXPECT_EQ(Res[0], D0); + EXPECT_EQ(Res[1], D2); + EXPECT_EQ(Res[2], D1); + EXPECT_EQ(Res[3], D0); + + path.pop(); + path.pop(); + path.pop(); + EXPECT_TRUE(path.hasCycleAtBack()); + i = 0; + for (Decl *Di : path.getCycleAtBack()) { + Res[i++] = Di; + } + ASSERT_EQ(i, 3); + EXPECT_EQ(Res[0], D0); + EXPECT_EQ(Res[1], D1); + EXPECT_EQ(Res[2], D0); + + path.pop(); + EXPECT_FALSE(path.hasCycleAtBack()); +} + TEST_P(ImportExpr, ImportStringLiteral) { MatchVerifier<Decl> Verifier; testImport( @@ -4547,12 +4647,6 @@ TEST_P(ASTImporterLookupTableTest, LookupSearchesInTheWholeRedeclChain) { EXPECT_EQ(*Res.begin(), A); } -INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest, - ::testing::Values(ArgVector()), ); - -INSTANTIATE_TEST_CASE_P( - ParameterizedTests, CanonicalRedeclChain, - ::testing::Values(ArgVector()),); // FIXME This test is disabled currently, upcoming patches will make it // possible to enable. @@ -4770,19 +4864,99 @@ TEST_P(ErrorHandlingTest, ErrorIsPropagatedFromMemberToClass) { CXXMethodDecl *ImportedF = Import(FromF, Lang_CXX); EXPECT_FALSE(ImportedF); - // There is no error set for ok(). + // There is an error set for the other member too. auto *FromOK = FirstDeclMatcher<CXXMethodDecl>().match( FromTU, cxxMethodDecl(hasName("ok"))); OptErr = Importer->getImportDeclErrorIfAny(FromOK); - EXPECT_FALSE(OptErr); - // And we should be able to import. + EXPECT_TRUE(OptErr); + // Cannot import the other member. CXXMethodDecl *ImportedOK = Import(FromOK, Lang_CXX); - EXPECT_TRUE(ImportedOK); + EXPECT_FALSE(ImportedOK); +} + +// Check that an error propagates to the dependent AST nodes. +// In the below code it means that an error in X should propagate to A. +// And even to F since the containing A is erroneous. +// And to all AST nodes which we visit during the import process which finally +// ends up in a failure (in the error() function). +TEST_P(ErrorHandlingTest, ErrorPropagatesThroughImportCycles) { + Decl *FromTU = getTuDecl( + std::string(R"( + namespace NS { + class A { + template <int I> class F {}; + class X { + template <int I> friend class F; + void error() { )") + ErroneousStmt + R"( } + }; + }; + + class B {}; + } // NS + )", + Lang_CXX, "input0.cc"); + + auto *FromFRD = FirstDeclMatcher<CXXRecordDecl>().match( + FromTU, cxxRecordDecl(hasName("F"), isDefinition())); + auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match( + FromTU, cxxRecordDecl(hasName("A"), isDefinition())); + auto *FromB = FirstDeclMatcher<CXXRecordDecl>().match( + FromTU, cxxRecordDecl(hasName("B"), isDefinition())); + auto *FromNS = FirstDeclMatcher<NamespaceDecl>().match( + FromTU, namespaceDecl(hasName("NS"))); + + // Start by importing the templated CXXRecordDecl of F. + // Import fails for that. + EXPECT_FALSE(Import(FromFRD, Lang_CXX)); + // Import fails for A. + EXPECT_FALSE(Import(FromA, Lang_CXX)); + // But we should be able to import the independent B. + EXPECT_TRUE(Import(FromB, Lang_CXX)); + // And the namespace. + EXPECT_TRUE(Import(FromNS, Lang_CXX)); + + // An error is set to the templated CXXRecordDecl of F. + ASTImporter *Importer = findFromTU(FromFRD)->Importer.get(); + Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromFRD); + EXPECT_TRUE(OptErr); + + // An error is set to A. + OptErr = Importer->getImportDeclErrorIfAny(FromA); + EXPECT_TRUE(OptErr); + + // There is no error set to B. + OptErr = Importer->getImportDeclErrorIfAny(FromB); + EXPECT_FALSE(OptErr); - // Unwary clients may access X even if the error is set, so, at least make - // sure the class is set to be complete. - CXXRecordDecl *ToX = cast<CXXRecordDecl>(ImportedOK->getDeclContext()); - EXPECT_TRUE(ToX->isCompleteDefinition()); + // There is no error set to NS. + OptErr = Importer->getImportDeclErrorIfAny(FromNS); + EXPECT_FALSE(OptErr); + + // Check some of those decls whose ancestor is X, they all should have an + // error set if we visited them during an import process which finally failed. + // These decls are part of a cycle in an ImportPath. + // There would not be any error set for these decls if we hadn't follow the + // ImportPaths and the cycles. + OptErr = Importer->getImportDeclErrorIfAny( + FirstDeclMatcher<ClassTemplateDecl>().match( + FromTU, classTemplateDecl(hasName("F")))); + // An error is set to the 'F' ClassTemplateDecl. + EXPECT_TRUE(OptErr); + // An error is set to the FriendDecl. + OptErr = Importer->getImportDeclErrorIfAny( + FirstDeclMatcher<FriendDecl>().match( + FromTU, friendDecl())); + EXPECT_TRUE(OptErr); + // An error is set to the implicit class of A. + OptErr = + Importer->getImportDeclErrorIfAny(FirstDeclMatcher<CXXRecordDecl>().match( + FromTU, cxxRecordDecl(hasName("A"), isImplicit()))); + EXPECT_TRUE(OptErr); + // An error is set to the implicit class of X. + OptErr = + Importer->getImportDeclErrorIfAny(FirstDeclMatcher<CXXRecordDecl>().match( + FromTU, cxxRecordDecl(hasName("X"), isImplicit()))); + EXPECT_TRUE(OptErr); } TEST_P(ErrorHandlingTest, ErrorIsNotPropagatedFromMemberToNamespace) { @@ -4828,9 +5002,18 @@ TEST_P(ErrorHandlingTest, ErrorIsNotPropagatedFromMemberToNamespace) { INSTANTIATE_TEST_CASE_P(ParameterizedTests, ErrorHandlingTest, DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest, + ::testing::Values(ArgVector()), ); + +INSTANTIATE_TEST_CASE_P(ParameterizedTests, CanonicalRedeclChain, + ::testing::Values(ArgVector()), ); + INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest, DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportPath, + ::testing::Values(ArgVector()), ); + INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportExpr, DefaultTestValuesForRunOptions, ); |