diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3646,19 +3646,23 @@ // initializer of a FieldDecl might not had been instantiated in the // "To" context. However, the "From" context might instantiated that, // thus we have to merge that. + // Note: `hasInClassInitializer()` is not the same as non-null + // `getInClassInitializer()` value. if (Expr *FromInitializer = D->getInClassInitializer()) { - // We don't have yet the initializer set. - if (FoundField->hasInClassInitializer() && - !FoundField->getInClassInitializer()) { - if (ExpectedExpr ToInitializerOrErr = import(FromInitializer)) + if (ExpectedExpr ToInitializerOrErr = import(FromInitializer)) { + // Import of the FromInitializer may result in the setting of + // InClassInitializer. If not, set it here. + assert(FoundField->hasInClassInitializer() && + "Field should have an in-class initializer if it has an " + "expression for it."); + if (!FoundField->getInClassInitializer()) FoundField->setInClassInitializer(*ToInitializerOrErr); - else { - // We can't return error here, - // since we already mapped D as imported. - // FIXME: warning message? - consumeError(ToInitializerOrErr.takeError()); - return FoundField; - } + } else { + // We can't return error here, + // since we already mapped D as imported. + // FIXME: warning message? + consumeError(ToInitializerOrErr.takeError()); + return FoundField; } } return FoundField; @@ -8127,8 +8131,23 @@ if (!UsedContextOrErr) return UsedContextOrErr.takeError(); - return CXXDefaultInitExpr::Create( - Importer.getToContext(), *ToBeginLocOrErr, *ToFieldOrErr, *UsedContextOrErr); + FieldDecl *ToField = *ToFieldOrErr; + assert(ToField->hasInClassInitializer() && + "Field should have in-class initializer if there is a default init " + "expression that uses it."); + if (!ToField->getInClassInitializer()) { + // The in-class initializer may be not yet set in "To" AST even if the + // field is already there. This must be set here to make construction of + // CXXDefaultInitExpr work. + auto ToInClassInitializerOrErr = + import(E->getField()->getInClassInitializer()); + if (!ToInClassInitializerOrErr) + return ToInClassInitializerOrErr.takeError(); + ToField->setInClassInitializer(*ToInClassInitializerOrErr); + } + + return CXXDefaultInitExpr::Create(Importer.getToContext(), *ToBeginLocOrErr, + ToField, *UsedContextOrErr); } ExpectedStmt ASTNodeImporter::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) { diff --git a/clang/test/Analysis/Inputs/ctu-cxxdefaultinitexpr-import.cpp b/clang/test/Analysis/Inputs/ctu-cxxdefaultinitexpr-import.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/Inputs/ctu-cxxdefaultinitexpr-import.cpp @@ -0,0 +1,26 @@ +namespace QHashPrivate { +template int b; +struct Data; +} // namespace QHashPrivate + +struct QDomNodePrivate {}; +template struct QMultiHash { + QHashPrivate::Data *d = nullptr; +}; + +struct QDomNamedNodeMapPrivate { + QMultiHash<> map; +}; +struct QDomElementPrivate : QDomNodePrivate { + QDomElementPrivate(); + void importee(); + QMultiHash<> *m_attr = nullptr; +}; +// --------- common part end --------- + +QDomElementPrivate::QDomElementPrivate() : m_attr{new QMultiHash<>} {} +void QDomElementPrivate::importee() { (void)QMultiHash<>{}; } +struct foo { + QDomElementPrivate m = {}; + static const int value = (QHashPrivate::b, 22); +}; diff --git a/clang/test/Analysis/Inputs/ctu-cxxdefaultinitexpr-import.cpp.externalDefMap.ast-dump.txt b/clang/test/Analysis/Inputs/ctu-cxxdefaultinitexpr-import.cpp.externalDefMap.ast-dump.txt new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/Inputs/ctu-cxxdefaultinitexpr-import.cpp.externalDefMap.ast-dump.txt @@ -0,0 +1,4 @@ +c:@S@foo@value ctu-cxxdefaultinitexpr-import.cpp.ast +c:@S@QDomElementPrivate@F@importee# ctu-cxxdefaultinitexpr-import.cpp.ast +c:@S@QDomElementPrivate@F@QDomElementPrivate# ctu-cxxdefaultinitexpr-import.cpp.ast +c:@S@QDomNodePrivate@F@QDomNodePrivate# ctu-cxxdefaultinitexpr-import.cpp.ast diff --git a/clang/test/Analysis/ctu-cxxdefaultinitexpr.cpp b/clang/test/Analysis/ctu-cxxdefaultinitexpr.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/ctu-cxxdefaultinitexpr.cpp @@ -0,0 +1,35 @@ +// RUN: rm -rf %t && mkdir %t +// RUN: mkdir -p %t/ctudir +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++17 \ +// RUN: -emit-pch -o %t/ctudir/ctu-cxxdefaultinitexpr-import.cpp.ast %S/Inputs/ctu-cxxdefaultinitexpr-import.cpp +// RUN: cp %S/Inputs/ctu-cxxdefaultinitexpr-import.cpp.externalDefMap.ast-dump.txt %t/ctudir/externalDefMap.txt +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -std=c++17 -analyze \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -verify %s + +// Check that importing this code does not cause crash. +// expected-no-diagnostics + +namespace QHashPrivate { +template int b; +struct Data; +} // namespace QHashPrivate + +struct QDomNodePrivate {}; +template struct QMultiHash { + QHashPrivate::Data *d = nullptr; +}; + +struct QDomNamedNodeMapPrivate { + QMultiHash<> map; +}; +struct QDomElementPrivate : QDomNodePrivate { + QDomElementPrivate(); + void importee(); + QMultiHash<> *m_attr = nullptr; +}; +// --------- common part end --------- + +void importer(QDomElementPrivate x) { x.importee(); } diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -530,6 +530,21 @@ has(floatLiteral(equals(1.0))))))))); } +const internal::VariadicDynCastAllOfMatcher + cxxDefaultInitExpr; + +TEST_P(ImportExpr, ImportCXXDefaultInitExpr) { + MatchVerifier Verifier; + testImport("class declToImport { int DefInit = 5; }; declToImport X;", + Lang_CXX11, "", Lang_CXX11, Verifier, + cxxRecordDecl(hasDescendant(cxxConstructorDecl( + hasAnyConstructorInitializer(cxxCtorInitializer( + withInitializer(cxxDefaultInitExpr()))))))); + testImport( + "struct X { int A = 5; }; X declToImport{};", Lang_CXX17, "", Lang_CXX17, + Verifier, + varDecl(hasInitializer(initListExpr(hasInit(0, cxxDefaultInitExpr()))))); +} const internal::VariadicDynCastAllOfMatcher vaArgExpr;