Error ImportTemplateInformation(FunctionDecl *FromFD, FunctionDecl *ToFD);
+ Error ImportFunctionDeclBody(FunctionDecl *FromFD, FunctionDecl *ToFD);
+
bool IsStructuralMatch(Decl *From, Decl *To, bool Complain);
bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord,
bool Complain = true);
return FoundSpec;
}
+Error ASTNodeImporter::ImportFunctionDeclBody(FunctionDecl *FromFD,
+ FunctionDecl *ToFD) {
+ if (Stmt *FromBody = FromFD->getBody()) {
+ if (ExpectedStmt ToBodyOrErr = import(FromBody))
+ ToFD->setBody(*ToBodyOrErr);
+ else
+ return ToBodyOrErr.takeError();
+ }
+ return Error::success();
+}
+
ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
SmallVector<Decl *, 2> Redecls = getCanonicalForwardRedeclChain(D);
if (ToD)
return ToD;
- const FunctionDecl *FoundByLookup = nullptr;
+ FunctionDecl *FoundByLookup = nullptr;
FunctionTemplateDecl *FromFT = D->getDescribedFunctionTemplate();
// If this is a function template specialization, then try to find the same
}
}
+ // We do not allow more than one in-class declaration of a function. This is
+ // because AST clients like VTableBuilder asserts on this. VTableBuilder
+ // assumes there is only one in-class declaration. Building a redecl
+ // chain would result in more than one in-class declaration for
+ // overrides (even if they are part of the same redecl chain inside the
+ // derived class.)
+ if (FoundByLookup) {
+ if (auto *MD = dyn_cast<CXXMethodDecl>(FoundByLookup)) {
+ if (D->getLexicalDeclContext() == D->getDeclContext()) {
+ if (!D->doesThisDeclarationHaveABody())
+ return Importer.MapImported(D, FoundByLookup);
+ else {
+ // Let's continue and build up the redecl chain in this case.
+ // FIXME Merge the functions into one decl.
+ }
+ }
+ }
+ }
+
DeclarationNameInfo NameInfo(Name, Loc);
// Import additional name location/type info.
if (Error Err = ImportDeclarationNameLoc(D->getNameInfo(), NameInfo))
}
if (D->doesThisDeclarationHaveABody()) {
- if (Stmt *FromBody = D->getBody()) {
- if (ExpectedStmt ToBodyOrErr = import(FromBody))
- ToFunction->setBody(*ToBodyOrErr);
- else
- return ToBodyOrErr.takeError();
- }
+ Error Err = ImportFunctionDeclBody(D, ToFunction);
+
+ if (Err)
+ return std::move(Err);
}
// FIXME: Other bits to merge?
}).match(ToTU, functionDecl()));
}
+TEST_P(ImportFunctions, ImportOverriddenMethodTwice) {
+ auto Code =
+ R"(
+ struct B { virtual void f(); };
+ struct D:B { void f(); };
+ )";
+ auto BFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
+ auto DFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
+
+ Decl *FromTU0 = getTuDecl(Code, Lang_CXX);
+ auto *DF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP);
+ Import(DF, Lang_CXX);
+
+ Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc");
+ auto *BF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP);
+ Import(BF, Lang_CXX);
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u);
+}
+
+TEST_P(ImportFunctions, ImportOverriddenMethodTwiceDefinitionFirst) {
+ auto CodeWithoutDef =
+ R"(
+ struct B { virtual void f(); };
+ struct D:B { void f(); };
+ )";
+ auto CodeWithDef =
+ R"(
+ struct B { virtual void f(){}; };
+ struct D:B { void f(){}; };
+ )";
+ auto BFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
+ auto DFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
+ auto BFDefP = cxxMethodDecl(
+ hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition());
+ auto DFDefP = cxxMethodDecl(
+ hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition());
+ auto FDefAllP = cxxMethodDecl(hasName("f"), isDefinition());
+
+ {
+ Decl *FromTU = getTuDecl(CodeWithDef, Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, DFP);
+ Import(FromD, Lang_CXX);
+ }
+ {
+ Decl *FromTU = getTuDecl(CodeWithoutDef, Lang_CXX, "input1.cc");
+ auto *FromB = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, BFP);
+ Import(FromB, Lang_CXX);
+ }
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFDefP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FDefAllP), 2u);
+}
+
+TEST_P(ImportFunctions, ImportOverriddenMethodTwiceOutOfClassDef) {
+ auto Code =
+ R"(
+ struct B { virtual void f(); };
+ struct D:B { void f(); };
+ void B::f(){};
+ )";
+
+ auto BFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
+ auto BFDefP = cxxMethodDecl(
+ hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition());
+ auto DFP = cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))),
+ unless(isDefinition()));
+
+ Decl *FromTU0 = getTuDecl(Code, Lang_CXX);
+ auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP);
+ Import(D, Lang_CXX);
+
+ Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc");
+ auto *B = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP);
+ Import(B, Lang_CXX);
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 0u);
+
+ auto *ToB = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("B")));
+ auto *ToBFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, BFP);
+ auto *ToBFOutOfClass = FirstDeclMatcher<CXXMethodDecl>().match(
+ ToTU, cxxMethodDecl(hasName("f"), isDefinition()));
+
+ // The definition should be out-of-class.
+ EXPECT_NE(ToBFInClass, ToBFOutOfClass);
+ EXPECT_NE(ToBFInClass->getLexicalDeclContext(),
+ ToBFOutOfClass->getLexicalDeclContext());
+ EXPECT_EQ(ToBFOutOfClass->getDeclContext(), ToB);
+ EXPECT_EQ(ToBFOutOfClass->getLexicalDeclContext(), ToTU);
+
+ // Check that the redecl chain is intact.
+ EXPECT_EQ(ToBFOutOfClass->getPreviousDecl(), ToBFInClass);
+}
+
+TEST_P(ImportFunctions,
+ ImportOverriddenMethodTwiceOutOfClassDefInSeparateCode) {
+ auto CodeTU0 =
+ R"(
+ struct B { virtual void f(); };
+ struct D:B { void f(); };
+ )";
+ auto CodeTU1 =
+ R"(
+ struct B { virtual void f(); };
+ struct D:B { void f(); };
+ void B::f(){}
+ void D::f(){}
+ void foo(B &b, D &d) { b.f(); d.f(); }
+ )";
+
+ auto BFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
+ auto BFDefP = cxxMethodDecl(
+ hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition());
+ auto DFP =
+ cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
+ auto DFDefP = cxxMethodDecl(
+ hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition());
+ auto FooDef = functionDecl(hasName("foo"));
+
+ {
+ Decl *FromTU0 = getTuDecl(CodeTU0, Lang_CXX, "input0.cc");
+ auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP);
+ Import(D, Lang_CXX);
+ }
+
+ {
+ Decl *FromTU1 = getTuDecl(CodeTU1, Lang_CXX, "input1.cc");
+ auto *Foo = FirstDeclMatcher<FunctionDecl>().match(FromTU1, FooDef);
+ Import(Foo, Lang_CXX);
+ }
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 0u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFDefP), 0u);
+
+ auto *ToB = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("B")));
+ auto *ToD = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("D")));
+ auto *ToBFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, BFP);
+ auto *ToBFOutOfClass = FirstDeclMatcher<CXXMethodDecl>().match(
+ ToTU, cxxMethodDecl(hasName("f"), isDefinition()));
+ auto *ToDFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, DFP);
+ auto *ToDFOutOfClass = LastDeclMatcher<CXXMethodDecl>().match(
+ ToTU, cxxMethodDecl(hasName("f"), isDefinition()));
+
+ // The definition should be out-of-class.
+ EXPECT_NE(ToBFInClass, ToBFOutOfClass);
+ EXPECT_NE(ToBFInClass->getLexicalDeclContext(),
+ ToBFOutOfClass->getLexicalDeclContext());
+ EXPECT_EQ(ToBFOutOfClass->getDeclContext(), ToB);
+ EXPECT_EQ(ToBFOutOfClass->getLexicalDeclContext(), ToTU);
+
+ EXPECT_NE(ToDFInClass, ToDFOutOfClass);
+ EXPECT_NE(ToDFInClass->getLexicalDeclContext(),
+ ToDFOutOfClass->getLexicalDeclContext());
+ EXPECT_EQ(ToDFOutOfClass->getDeclContext(), ToD);
+ EXPECT_EQ(ToDFOutOfClass->getLexicalDeclContext(), ToTU);
+
+ // Check that the redecl chain is intact.
+ EXPECT_EQ(ToBFOutOfClass->getPreviousDecl(), ToBFInClass);
+ EXPECT_EQ(ToDFOutOfClass->getPreviousDecl(), ToDFInClass);
+}
+
struct ImportFriendFunctions : ImportFunctions {};
TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainProto) {