forked from OSchip/llvm-project
[clang][ASTImporter] Fix a bug when importing CXXDefaultInitExpr.
The "in-class initializer" expression should be set in the field of a default initialization expression before this expression node is created. The `CXXDefaultInitExpr` objects are created after the AST is loaded and at import not present in the "To" AST. And the in-class initializers of the used fields can be missing too, these must be set at import. This fixes a github issue #54061. Reviewed By: martong Differential Revision: https://reviews.llvm.org/D120824
This commit is contained in:
parent
fcbf00f098
commit
c5d83cdca4
|
@ -3646,19 +3646,23 @@ ExpectedDecl ASTNodeImporter::VisitFieldDecl(FieldDecl *D) {
|
|||
// 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 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
|
|||
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) {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
namespace QHashPrivate {
|
||||
template <typename> int b;
|
||||
struct Data;
|
||||
} // namespace QHashPrivate
|
||||
|
||||
struct QDomNodePrivate {};
|
||||
template <typename = struct QString> 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<foo>, 22);
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
14:c:@S@foo@value ctu-cxxdefaultinitexpr-import.cpp.ast
|
||||
35:c:@S@QDomElementPrivate@F@importee# ctu-cxxdefaultinitexpr-import.cpp.ast
|
||||
45:c:@S@QDomElementPrivate@F@QDomElementPrivate# ctu-cxxdefaultinitexpr-import.cpp.ast
|
||||
39:c:@S@QDomNodePrivate@F@QDomNodePrivate# ctu-cxxdefaultinitexpr-import.cpp.ast
|
|
@ -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 <typename> int b;
|
||||
struct Data;
|
||||
} // namespace QHashPrivate
|
||||
|
||||
struct QDomNodePrivate {};
|
||||
template <typename = struct QString> 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(); }
|
|
@ -530,6 +530,21 @@ TEST_P(ImportExpr, ImportInitListExpr) {
|
|||
has(floatLiteral(equals(1.0)))))))));
|
||||
}
|
||||
|
||||
const internal::VariadicDynCastAllOfMatcher<Expr, CXXDefaultInitExpr>
|
||||
cxxDefaultInitExpr;
|
||||
|
||||
TEST_P(ImportExpr, ImportCXXDefaultInitExpr) {
|
||||
MatchVerifier<Decl> 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<Expr, VAArgExpr> vaArgExpr;
|
||||
|
||||
|
@ -7529,6 +7544,92 @@ TEST_P(ASTImporterOptionSpecificTestBase,
|
|||
EXPECT_TRUE(ToA->isCompleteDefinition());
|
||||
}
|
||||
|
||||
TEST_P(ASTImporterOptionSpecificTestBase, ImportInClassInitializerFromField) {
|
||||
// Encounter import of a field when the field already exists but has the
|
||||
// in-class initializer expression not yet set. Such case can occur in the AST
|
||||
// of generated template specializations.
|
||||
// The first code forces to create a template specialization of
|
||||
// `A<int>` but without implicit constructors.
|
||||
// The second ("From") code contains a variable of type `A<int>`, this
|
||||
// results in a template specialization that has constructors and
|
||||
// CXXDefaultInitExpr nodes.
|
||||
Decl *ToTU = getToTuDecl(
|
||||
R"(
|
||||
void f();
|
||||
template<typename> struct A { int X = 1; };
|
||||
struct B { A<int> Y; };
|
||||
)",
|
||||
Lang_CXX11);
|
||||
auto *ToX = FirstDeclMatcher<FieldDecl>().match(
|
||||
ToTU,
|
||||
fieldDecl(hasName("X"), hasParent(classTemplateSpecializationDecl())));
|
||||
ASSERT_TRUE(ToX->hasInClassInitializer());
|
||||
ASSERT_FALSE(ToX->getInClassInitializer());
|
||||
|
||||
Decl *FromTU = getTuDecl(
|
||||
R"(
|
||||
void f();
|
||||
template<typename> struct A { int X = 1; };
|
||||
struct B { A<int> Y; };
|
||||
//
|
||||
A<int> Z;
|
||||
)",
|
||||
Lang_CXX11, "input1.cc");
|
||||
auto *FromX = FirstDeclMatcher<FieldDecl>().match(
|
||||
FromTU,
|
||||
fieldDecl(hasName("X"), hasParent(classTemplateSpecializationDecl())));
|
||||
|
||||
auto *ToXImported = Import(FromX, Lang_CXX11);
|
||||
EXPECT_EQ(ToXImported, ToX);
|
||||
EXPECT_TRUE(ToX->getInClassInitializer());
|
||||
}
|
||||
|
||||
TEST_P(ASTImporterOptionSpecificTestBase,
|
||||
ImportInClassInitializerFromCXXDefaultInitExpr) {
|
||||
// Encounter AST import of a CXXDefaultInitExpr where the "to-field"
|
||||
// of it exists but has the in-class initializer not set yet.
|
||||
Decl *ToTU = getToTuDecl(
|
||||
R"(
|
||||
namespace N {
|
||||
template<typename> int b;
|
||||
struct X;
|
||||
}
|
||||
template<typename> struct A { N::X *X = nullptr; };
|
||||
struct B { A<int> Y; };
|
||||
)",
|
||||
Lang_CXX14);
|
||||
auto *ToX = FirstDeclMatcher<FieldDecl>().match(
|
||||
ToTU,
|
||||
fieldDecl(hasName("X"), hasParent(classTemplateSpecializationDecl())));
|
||||
ASSERT_TRUE(ToX->hasInClassInitializer());
|
||||
ASSERT_FALSE(ToX->getInClassInitializer());
|
||||
|
||||
Decl *FromTU = getTuDecl(
|
||||
R"(
|
||||
namespace N {
|
||||
template<typename> int b;
|
||||
struct X;
|
||||
}
|
||||
template<typename> struct A { N::X *X = nullptr; };
|
||||
struct B { A<int> Y; };
|
||||
//
|
||||
void f() {
|
||||
(void)A<int>{};
|
||||
}
|
||||
struct C {
|
||||
C(): attr(new A<int>{}){}
|
||||
A<int> *attr;
|
||||
const int value = N::b<C>;
|
||||
};
|
||||
)",
|
||||
Lang_CXX14, "input1.cc");
|
||||
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
|
||||
FromTU, functionDecl(hasName("f"), isDefinition()));
|
||||
auto *ToF = Import(FromF, Lang_CXX11);
|
||||
EXPECT_TRUE(ToF);
|
||||
EXPECT_TRUE(ToX->getInClassInitializer());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ASTImporterLookupTableTest,
|
||||
DefaultTestValuesForRunOptions);
|
||||
|
||||
|
|
Loading…
Reference in New Issue