ASTImporterLookupTable(TranslationUnitDecl &TU);
void add(NamedDecl *ND);
void remove(NamedDecl *ND);
+ // Sometimes a declaration is created first with a temporarily value of decl
+ // context (often the translation unit) and later moved to the final context.
+ // This happens for declarations that are created before the final declaration
+ // context. In such cases the lookup table needs to be updated.
+ // (The declaration is in these cases not added to the temporary decl context,
+ // only its parent is set.)
+ // FIXME: It would be better to not add the declaration to the temporary
+ // context at all in the lookup table, but this requires big change in
+ // ASTImporter.
+ // The function should be called when the old context is definitely different
+ // from the new.
+ void update(NamedDecl *ND, DeclContext *OldDC);
using LookupResult = DeclList;
LookupResult lookup(DeclContext *DC, DeclarationName Name) const;
+ // Check if the `ND` is within the lookup table (with its current name) in
+ // context `DC`. This is intended for debug purposes when the DeclContext of a
+ // NamedDecl is changed.
+ bool contains(DeclContext *DC, NamedDecl *ND) const;
void dump(DeclContext *DC) const;
void dump() const;
};
for (auto *Param : Parameters) {
Param->setOwningFunction(ToFunction);
ToFunction->addDeclInternal(Param);
+ if (ASTImporterLookupTable *LT = Importer.SharedState->getLookupTable())
+ LT->update(Param, Importer.getToContext().getTranslationUnitDecl());
}
ToFunction->setParams(Parameters);
remove(ReDC, ND);
}
+void ASTImporterLookupTable::update(NamedDecl *ND, DeclContext *OldDC) {
+ assert(OldDC != ND->getDeclContext() &&
+ "DeclContext should be changed before update");
+ if (contains(ND->getDeclContext(), ND)) {
+ assert(!contains(OldDC, ND) &&
+ "Decl should not be found in the old context if already in the new");
+ return;
+ }
+
+ remove(OldDC, ND);
+ add(ND);
+}
+
ASTImporterLookupTable::LookupResult
ASTImporterLookupTable::lookup(DeclContext *DC, DeclarationName Name) const {
auto DCI = LookupTable.find(DC->getPrimaryContext());
return NamesI->second;
}
+bool ASTImporterLookupTable::contains(DeclContext *DC, NamedDecl *ND) const {
+ return 0 < lookup(DC, ND->getDeclName()).count(ND);
+}
+
void ASTImporterLookupTable::dump(DeclContext *DC) const {
auto DCI = LookupTable.find(DC->getPrimaryContext());
if (DCI == LookupTable.end())
CheckError(FromFooC);
}
+TEST_P(ErrorHandlingTest, ODRViolationWithinParmVarDecls) {
+ // Importing of 'f' and parameter 'P' should cause an ODR error.
+ // The error happens after the ParmVarDecl for 'P' was already created.
+ // This is a special case because the ParmVarDecl has a temporary DeclContext.
+ // Expected is no crash at error handling of ASTImporter.
+ constexpr auto ToTUCode = R"(
+ struct X {
+ char A;
+ };
+ )";
+ constexpr auto FromTUCode = R"(
+ struct X {
+ enum Y { Z };
+ };
+ void f(int P = X::Z);
+ )";
+ Decl *ToTU = getToTuDecl(ToTUCode, Lang_CXX11);
+ static_cast<void>(ToTU);
+ Decl *FromTU = getTuDecl(FromTUCode, Lang_CXX11);
+ auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ ASSERT_TRUE(FromF);
+
+ auto *ImportedF = Import(FromF, Lang_CXX11);
+ EXPECT_FALSE(ImportedF);
+}
+
TEST_P(ASTImporterOptionSpecificTestBase, LambdaInFunctionBody) {
Decl *FromTU = getTuDecl(
R"(
ASSERT_TRUE(ToD);
}
+TEST_P(ImportFunctions, ParmVarDeclDeclContext) {
+ constexpr auto FromTUCode = R"(
+ void f(int P);
+ )";
+ Decl *FromTU = getTuDecl(FromTUCode, Lang_CXX11);
+ auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ ASSERT_TRUE(FromF);
+
+ auto *ImportedF = Import(FromF, Lang_CXX11);
+ EXPECT_TRUE(ImportedF);
+ EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains(
+ ImportedF, ImportedF->getParamDecl(0)));
+}
+
// FIXME Move these tests out of ASTImporterTest. For that we need to factor
// out the ASTImporter specific pars from ASTImporterOptionSpecificTestBase
// into a new test Fixture. Then we should lift up this Fixture to its own