Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -2999,6 +2999,16 @@ FunctionDecl *FoundByLookup = nullptr; FunctionTemplateDecl *FromFT = D->getDescribedFunctionTemplate(); + // Check if we have found an existing definition. Returns with that + // definition if yes, otherwise returns null. + auto FindAndMapDefinition = [this]( + FunctionDecl *D, FunctionDecl *FoundFunction) -> Decl * { + const FunctionDecl *Definition = nullptr; + if (D->doesThisDeclarationHaveABody() && FoundFunction->hasBody(Definition)) + return Importer.MapImported(D, const_cast(Definition)); + return nullptr; + }; + // If this is a function template specialization, then try to find the same // existing specialization in the "to" context. The lookup below will not // find any specialization, but would find the primary template; thus, we @@ -3010,8 +3020,8 @@ if (!FoundFunctionOrErr) return FoundFunctionOrErr.takeError(); if (FunctionDecl *FoundFunction = *FoundFunctionOrErr) { - if (D->doesThisDeclarationHaveABody() && FoundFunction->hasBody()) - return Importer.MapImported(D, FoundFunction); + if (Decl *Def = FindAndMapDefinition(D, FoundFunction)) + return Def; FoundByLookup = FoundFunction; } } @@ -3030,11 +3040,8 @@ continue; if (IsStructuralMatch(D, FoundFunction)) { - const FunctionDecl *Definition = nullptr; - if (D->doesThisDeclarationHaveABody() && - FoundFunction->hasBody(Definition)) - return Importer.MapImported(D, - const_cast(Definition)); + if (Decl *Def = FindAndMapDefinition(D, FoundFunction)) + return Def; FoundByLookup = FoundFunction; break; } Index: unittests/AST/ASTImporterTest.cpp =================================================================== --- unittests/AST/ASTImporterTest.cpp +++ unittests/AST/ASTImporterTest.cpp @@ -3859,6 +3859,47 @@ std::string getDefinition() { return TypeParam::Definition; } BindableMatcher getPattern() const { return TypeParam().getPattern(); } + void CheckPreviousDecl(Decl *To0, Decl *To1) { + ASSERT_NE(To0, To1); + ASSERT_EQ(&To0->getASTContext(), &To1->getASTContext()); + + auto *ToTU = To0->getTranslationUnitDecl(); + + // Templates. + if (auto *ToT0 = dyn_cast(To0)) { + EXPECT_EQ(To1->getPreviousDecl(), To0); + auto *ToT1 = cast(To1); + ASSERT_TRUE(ToT0->getTemplatedDecl()); + ASSERT_TRUE(ToT1->getTemplatedDecl()); + EXPECT_EQ(ToT1->getTemplatedDecl()->getPreviousDecl(), + ToT0->getTemplatedDecl()); + return; + } + + // Specializations. + if (auto *From0F = dyn_cast(To0)) { + auto *To0F = cast(To0); + if (From0F->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) { + EXPECT_EQ(To0->getCanonicalDecl(), To1->getCanonicalDecl()); + // There may be a hidden fwd spec decl before a spec decl. + // In that case the previous visible decl can be reached through that + // invisible one. + EXPECT_THAT(To0, + testing::AnyOf(To1->getPreviousDecl(), + To1->getPreviousDecl()->getPreviousDecl())); + auto *TemplateD = FirstDeclMatcher().match( + ToTU, functionTemplateDecl()); + auto *FirstSpecD = *(TemplateD->spec_begin()); + EXPECT_EQ(FirstSpecD->getCanonicalDecl(), To0F->getCanonicalDecl()); + return; + } + } + + // The rest: Classes, Functions, etc. + EXPECT_EQ(To1->getPreviousDecl(), To0); + } + void TypedTest_PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition() { Decl *FromTU = getTuDecl(getPrototype(), Lang_CXX); @@ -3911,14 +3952,8 @@ EXPECT_TRUE(Imported1 == To1); EXPECT_FALSE(To0->isThisDeclarationADefinition()); EXPECT_FALSE(To1->isThisDeclarationADefinition()); - EXPECT_EQ(To1->getPreviousDecl(), To0); - if (auto *ToT0 = dyn_cast(To0)) { - auto *ToT1 = cast(To1); - ASSERT_TRUE(ToT0->getTemplatedDecl()); - ASSERT_TRUE(ToT1->getTemplatedDecl()); - EXPECT_EQ(ToT1->getTemplatedDecl()->getPreviousDecl(), - ToT0->getTemplatedDecl()); - } + + CheckPreviousDecl(To0, To1); } void TypedTest_ImportDefinitionAfterImportedPrototype() { @@ -3940,14 +3975,8 @@ EXPECT_TRUE(ImportedDef == ToDef); EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); - EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); - if (auto *ToProtoT = dyn_cast(ToProto)) { - auto *ToDefT = cast(ToDef); - ASSERT_TRUE(ToProtoT->getTemplatedDecl()); - ASSERT_TRUE(ToDefT->getTemplatedDecl()); - EXPECT_EQ(ToDefT->getTemplatedDecl()->getPreviousDecl(), - ToProtoT->getTemplatedDecl()); - } + + CheckPreviousDecl(ToProto, ToDef); } void TypedTest_ImportPrototypeAfterImportedDefinition() { @@ -3969,14 +3998,8 @@ EXPECT_TRUE(ImportedProto == ToProto); EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); - EXPECT_EQ(ToProto->getPreviousDecl(), ToDef); - if (auto *ToDefT = dyn_cast(ToDef)) { - auto *ToProtoT = cast(ToProto); - ASSERT_TRUE(ToDefT->getTemplatedDecl()); - ASSERT_TRUE(ToProtoT->getTemplatedDecl()); - EXPECT_EQ(ToProtoT->getTemplatedDecl()->getPreviousDecl(), - ToDefT->getTemplatedDecl()); - } + + CheckPreviousDecl(ToDef, ToProto); } void TypedTest_ImportPrototypes() { @@ -3998,27 +4021,8 @@ EXPECT_TRUE(Imported1 == To1); EXPECT_FALSE(To0->isThisDeclarationADefinition()); EXPECT_FALSE(To1->isThisDeclarationADefinition()); - EXPECT_EQ(To1->getPreviousDecl(), To0); - if (auto *ToT0 = dyn_cast(To0)) { - auto *ToT1 = cast(To1); - ASSERT_TRUE(ToT0->getTemplatedDecl()); - ASSERT_TRUE(ToT1->getTemplatedDecl()); - EXPECT_EQ(ToT1->getTemplatedDecl()->getPreviousDecl(), - ToT0->getTemplatedDecl()); - } - // Extra check for specializations. - // FIXME Add this check to other tests too (possibly factor out into a - // function), when they start to pass. - if (auto *From0F = dyn_cast(From0)) { - auto *To0F = cast(To0); - if (From0F->getTemplatedKind() == - FunctionDecl::TK_FunctionTemplateSpecialization) { - auto *TemplateD = FirstDeclMatcher().match( - ToTU, functionTemplateDecl()); - auto *FirstSpecD = *(TemplateD->spec_begin()); - EXPECT_EQ(FirstSpecD->getCanonicalDecl(), To0F->getCanonicalDecl()); - } - } + + CheckPreviousDecl(To0, To1); } void TypedTest_ImportDefinitions() { @@ -4063,14 +4067,8 @@ EXPECT_TRUE(ImportedProto == ToProto); EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); - EXPECT_EQ(ToProto->getPreviousDecl(), ToDef); - if (auto *ToDefT = dyn_cast(ToDef)) { - auto *ToProtoT = cast(ToProto); - ASSERT_TRUE(ToDefT->getTemplatedDecl()); - ASSERT_TRUE(ToProtoT->getTemplatedDecl()); - EXPECT_EQ(ToProtoT->getTemplatedDecl()->getPreviousDecl(), - ToDefT->getTemplatedDecl()); - } + + CheckPreviousDecl(ToDef, ToProto); } void TypedTest_ImportPrototypeThenDefinition() { @@ -4094,14 +4092,8 @@ EXPECT_TRUE(ImportedProto == ToProto); EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); - EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); - if (auto *ToDefT = dyn_cast(ToDef)) { - auto *ToProtoT = cast(ToProto); - ASSERT_TRUE(ToDefT->getTemplatedDecl()); - ASSERT_TRUE(ToProtoT->getTemplatedDecl()); - EXPECT_EQ(ToDefT->getTemplatedDecl()->getPreviousDecl(), - ToProtoT->getTemplatedDecl()); - } + + CheckPreviousDecl(ToProto, ToDef); } void TypedTest_WholeRedeclChainIsImportedAtOnce() { @@ -4143,7 +4135,8 @@ EXPECT_TRUE(DefinitionD->getPreviousDecl()); EXPECT_FALSE( DefinitionD->getPreviousDecl()->isThisDeclarationADefinition()); - EXPECT_EQ(DefinitionD->getPreviousDecl()->getPreviousDecl(), ProtoD); + + CheckPreviousDecl(ProtoD, DefinitionD->getPreviousDecl()); } }; @@ -4249,9 +4242,8 @@ DISABLED_, ImportPrototypes) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , ImportPrototypes) -// FIXME This does not pass, possible error with Spec import. -ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, - DISABLED_, ImportPrototypes) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + ImportPrototypes) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , ImportDefinitions) @@ -4264,9 +4256,8 @@ DISABLED_, ImportDefinitions) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , ImportDefinitions) -// FIXME This does not pass, possible error with Spec import. -ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, - DISABLED_, ImportDefinitions) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + ImportDefinitions) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , ImportDefinitionThenPrototype) @@ -4280,9 +4271,7 @@ ImportDefinitionThenPrototype) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , ImportDefinitionThenPrototype) -// FIXME This does not pass, possible error with Spec import. -ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, - DISABLED_, +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , ImportDefinitionThenPrototype) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , @@ -4297,9 +4286,7 @@ ImportPrototypeThenDefinition) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , ImportPrototypeThenDefinition) -// FIXME This does not pass, possible error with Spec import. -ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, - DISABLED_, +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , ImportPrototypeThenDefinition) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , @@ -4321,9 +4308,7 @@ ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, DISABLED_, ImportPrototypeThenProtoAndDefinition) -// FIXME This does not pass, possible error with Spec import. -ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, - DISABLED_, +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , ImportPrototypeThenProtoAndDefinition) INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainFunction,