Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -438,6 +438,8 @@ 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); @@ -2945,6 +2947,17 @@ 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 Redecls = getCanonicalForwardRedeclChain(D); @@ -2968,7 +2981,7 @@ 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 @@ -3039,6 +3052,19 @@ } } + if (FoundByLookup) { + if (auto *MD = dyn_cast(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)) @@ -3184,12 +3210,10 @@ } 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? Index: test/Analysis/Inputs/ctu-other.cpp =================================================================== --- test/Analysis/Inputs/ctu-other.cpp +++ test/Analysis/Inputs/ctu-other.cpp @@ -24,33 +24,38 @@ int fens(int x) { return x - 3; } -} +} // namespace embed_ns class embed_cls { public: - int fecl(int x) { - return x - 7; - } + int fecl(int x); }; +int embed_cls::fecl(int x) { + return x - 7; } +} // namespace myns class mycls { public: - int fcl(int x) { - return x + 5; - } - static int fscl(int x) { - return x + 6; - } + int fcl(int x); + static int fscl(int x); class embed_cls2 { public: - int fecl2(int x) { - return x - 11; - } + int fecl2(int x); }; }; +int mycls::fcl(int x) { + return x + 5; +} +int mycls::fscl(int x) { + return x + 6; +} +int mycls::embed_cls2::fecl2(int x) { + return x - 11; +} + namespace chns { int chf2(int x); Index: test/Analysis/ctu-main.cpp =================================================================== --- test/Analysis/ctu-main.cpp +++ test/Analysis/ctu-main.cpp @@ -78,6 +78,6 @@ clang_analyzer_eval(fun_using_anon_struct(8) == 8); // expected-warning{{TRUE}} clang_analyzer_eval(other_macro_diag(1) == 1); // expected-warning{{TRUE}} - // expected-warning@Inputs/ctu-other.cpp:75{{REACHABLE}} + // expected-warning@Inputs/ctu-other.cpp:80{{REACHABLE}} MACRODIAG(); // expected-warning{{REACHABLE}} } Index: unittests/AST/ASTImporterTest.cpp =================================================================== --- unittests/AST/ASTImporterTest.cpp +++ unittests/AST/ASTImporterTest.cpp @@ -2233,6 +2233,191 @@ }).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().match(FromTU0, DFP); + Import(DF, Lang_CXX); + + Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); + auto *BF = FirstDeclMatcher().match(FromTU1, BFP); + Import(BF, Lang_CXX); + + auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter().match(ToTU, BFP), 1u); + EXPECT_EQ(DeclCounter().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().match(FromTU, DFP); + Import(FromD, Lang_CXX); + } + { + Decl *FromTU = getTuDecl(CodeWithoutDef, Lang_CXX, "input1.cc"); + auto *FromB = FirstDeclMatcher().match(FromTU, BFP); + Import(FromB, Lang_CXX); + } + + auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter().match(ToTU, BFP), 1u); + EXPECT_EQ(DeclCounter().match(ToTU, DFP), 1u); + EXPECT_EQ(DeclCounter().match(ToTU, BFDefP), 1u); + EXPECT_EQ(DeclCounter().match(ToTU, DFDefP), 1u); + EXPECT_EQ(DeclCounter().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 BFPIsDefP = 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().match(FromTU0, DFP); + Import(D, Lang_CXX); + + Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); + auto *B = FirstDeclMatcher().match(FromTU1, BFP); + Import(B, Lang_CXX); + + auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter().match(ToTU, BFP), 1u); + EXPECT_EQ(DeclCounter().match(ToTU, BFPIsDefP), 0u); + + auto *ToB = FirstDeclMatcher().match( + ToTU, cxxRecordDecl(hasName("B"))); + auto *ToBFInClass = FirstDeclMatcher().match(ToTU, BFP); + auto *ToBFOutOfClass = FirstDeclMatcher().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 BFPIsDefP = cxxMethodDecl( + hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition()); + auto DFP = + cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D")))); + auto DFPIsDefP = cxxMethodDecl( + hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition()); + auto FooDef = functionDecl(hasName("foo")); + + { + Decl *FromTU0 = getTuDecl(CodeTU0, Lang_CXX, "input0.cc"); + auto *D = FirstDeclMatcher().match(FromTU0, DFP); + Import(D, Lang_CXX); + } + + { + Decl *FromTU1 = getTuDecl(CodeTU1, Lang_CXX, "input1.cc"); + auto *Foo = FirstDeclMatcher().match(FromTU1, FooDef); + Import(Foo, Lang_CXX); + } + + auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter().match(ToTU, BFP), 1u); + EXPECT_EQ(DeclCounter().match(ToTU, DFP), 1u); + EXPECT_EQ(DeclCounter().match(ToTU, BFPIsDefP), 0u); + EXPECT_EQ(DeclCounter().match(ToTU, DFPIsDefP), 0u); + + auto *ToB = FirstDeclMatcher().match( + ToTU, cxxRecordDecl(hasName("B"))); + auto *ToD = FirstDeclMatcher().match( + ToTU, cxxRecordDecl(hasName("D"))); + auto *ToBFInClass = FirstDeclMatcher().match(ToTU, BFP); + auto *ToBFOutOfClass = FirstDeclMatcher().match( + ToTU, cxxMethodDecl(hasName("f"), isDefinition())); + auto *ToDFInClass = FirstDeclMatcher().match(ToTU, DFP); + auto *ToDFOutOfClass = LastDeclMatcher().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) {