Skip to content

Commit 17c3eaf

Browse files
author
Gabor Marton
committed
[ASTImporter] Propagate error from ImportDeclContext
Summary: During analysis of one project we failed to import one CXXDestructorDecl. But since we did not propagate the error in importDeclContext we had a CXXRecordDecl without a destructor. Then the analyzer engine had a CallEvent where the nonexistent dtor was requested (crash). Solution is to propagate the errors we have during importing a DeclContext. Reviewers: a_sidorin, a.sidorin, shafik Subscribers: rnkovacs, dkrupp, Szelethus, gamesh411, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D63603 llvm-svn: 364752
1 parent 08c38f7 commit 17c3eaf

File tree

2 files changed

+114
-6
lines changed

2 files changed

+114
-6
lines changed

clang/lib/AST/ASTImporter.cpp

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include "llvm/ADT/DenseMap.h"
5858
#include "llvm/ADT/None.h"
5959
#include "llvm/ADT/Optional.h"
60+
#include "llvm/ADT/ScopeExit.h"
6061
#include "llvm/ADT/STLExtras.h"
6162
#include "llvm/ADT/SmallVector.h"
6263
#include "llvm/Support/Casting.h"
@@ -1631,16 +1632,32 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
16311632
auto ToDCOrErr = Importer.ImportContext(FromDC);
16321633
return ToDCOrErr.takeError();
16331634
}
1635+
1636+
// We use strict error handling in case of records and enums, but not
1637+
// with e.g. namespaces.
1638+
//
1639+
// FIXME Clients of the ASTImporter should be able to choose an
1640+
// appropriate error handling strategy for their needs. For instance,
1641+
// they may not want to mark an entire namespace as erroneous merely
1642+
// because there is an ODR error with two typedefs. As another example,
1643+
// the client may allow EnumConstantDecls with same names but with
1644+
// different values in two distinct translation units.
1645+
bool AccumulateChildErrors = isa<TagDecl>(FromDC);
1646+
1647+
Error ChildErrors = Error::success();
16341648
llvm::SmallVector<Decl *, 8> ImportedDecls;
16351649
for (auto *From : FromDC->decls()) {
16361650
ExpectedDecl ImportedOrErr = import(From);
1637-
if (!ImportedOrErr)
1638-
// Ignore the error, continue with next Decl.
1639-
// FIXME: Handle this case somehow better.
1640-
consumeError(ImportedOrErr.takeError());
1651+
if (!ImportedOrErr) {
1652+
if (AccumulateChildErrors)
1653+
ChildErrors =
1654+
joinErrors(std::move(ChildErrors), ImportedOrErr.takeError());
1655+
else
1656+
consumeError(ImportedOrErr.takeError());
1657+
}
16411658
}
16421659

1643-
return Error::success();
1660+
return ChildErrors;
16441661
}
16451662

16461663
Error ASTNodeImporter::ImportDeclContext(
@@ -1698,6 +1715,11 @@ Error ASTNodeImporter::ImportDefinition(
16981715
}
16991716

17001717
To->startDefinition();
1718+
// Complete the definition even if error is returned.
1719+
// The RecordDecl may be already part of the AST so it is better to
1720+
// have it in complete state even if something is wrong with it.
1721+
auto DefinitionCompleter =
1722+
llvm::make_scope_exit([To]() { To->completeDefinition(); });
17011723

17021724
if (Error Err = setTypedefNameForAnonDecl(From, To, Importer))
17031725
return Err;
@@ -1822,7 +1844,6 @@ Error ASTNodeImporter::ImportDefinition(
18221844
if (Error Err = ImportDeclContext(From, /*ForceImport=*/true))
18231845
return Err;
18241846

1825-
To->completeDefinition();
18261847
return Error::success();
18271848
}
18281849

clang/unittests/AST/ASTImporterTest.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4738,6 +4738,93 @@ TEST_P(ErrorHandlingTest, ErrorHappensAfterNodeIsCreatedAndLinked) {
47384738
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
47394739
}
47404740

4741+
// An error should be set for a class if we cannot import one member.
4742+
TEST_P(ErrorHandlingTest, ErrorIsPropagatedFromMemberToClass) {
4743+
TranslationUnitDecl *FromTU = getTuDecl(
4744+
std::string(R"(
4745+
class X {
4746+
void f() { )") + ErroneousStmt + R"( } // This member has the error
4747+
// during import.
4748+
void ok(); // The error should not prevent importing this.
4749+
}; // An error will be set for X too.
4750+
)",
4751+
Lang_CXX);
4752+
auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
4753+
FromTU, cxxRecordDecl(hasName("X")));
4754+
CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX);
4755+
4756+
// An error is set for X.
4757+
EXPECT_FALSE(ImportedX);
4758+
ASTImporter *Importer = findFromTU(FromX)->Importer.get();
4759+
Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
4760+
ASSERT_TRUE(OptErr);
4761+
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
4762+
4763+
// An error is set for f().
4764+
auto *FromF = FirstDeclMatcher<CXXMethodDecl>().match(
4765+
FromTU, cxxMethodDecl(hasName("f")));
4766+
OptErr = Importer->getImportDeclErrorIfAny(FromF);
4767+
ASSERT_TRUE(OptErr);
4768+
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
4769+
// And any subsequent import should fail.
4770+
CXXMethodDecl *ImportedF = Import(FromF, Lang_CXX);
4771+
EXPECT_FALSE(ImportedF);
4772+
4773+
// There is no error set for ok().
4774+
auto *FromOK = FirstDeclMatcher<CXXMethodDecl>().match(
4775+
FromTU, cxxMethodDecl(hasName("ok")));
4776+
OptErr = Importer->getImportDeclErrorIfAny(FromOK);
4777+
EXPECT_FALSE(OptErr);
4778+
// And we should be able to import.
4779+
CXXMethodDecl *ImportedOK = Import(FromOK, Lang_CXX);
4780+
EXPECT_TRUE(ImportedOK);
4781+
4782+
// Unwary clients may access X even if the error is set, so, at least make
4783+
// sure the class is set to be complete.
4784+
CXXRecordDecl *ToX = cast<CXXRecordDecl>(ImportedOK->getDeclContext());
4785+
EXPECT_TRUE(ToX->isCompleteDefinition());
4786+
}
4787+
4788+
TEST_P(ErrorHandlingTest, ErrorIsNotPropagatedFromMemberToNamespace) {
4789+
TranslationUnitDecl *FromTU = getTuDecl(
4790+
std::string(R"(
4791+
namespace X {
4792+
void f() { )") + ErroneousStmt + R"( } // This member has the error
4793+
// during import.
4794+
void ok(); // The error should not prevent importing this.
4795+
}; // An error will be set for X too.
4796+
)",
4797+
Lang_CXX);
4798+
auto *FromX = FirstDeclMatcher<NamespaceDecl>().match(
4799+
FromTU, namespaceDecl(hasName("X")));
4800+
NamespaceDecl *ImportedX = Import(FromX, Lang_CXX);
4801+
4802+
// There is no error set for X.
4803+
EXPECT_TRUE(ImportedX);
4804+
ASTImporter *Importer = findFromTU(FromX)->Importer.get();
4805+
Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
4806+
ASSERT_FALSE(OptErr);
4807+
4808+
// An error is set for f().
4809+
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
4810+
FromTU, functionDecl(hasName("f")));
4811+
OptErr = Importer->getImportDeclErrorIfAny(FromF);
4812+
ASSERT_TRUE(OptErr);
4813+
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
4814+
// And any subsequent import should fail.
4815+
FunctionDecl *ImportedF = Import(FromF, Lang_CXX);
4816+
EXPECT_FALSE(ImportedF);
4817+
4818+
// There is no error set for ok().
4819+
auto *FromOK = FirstDeclMatcher<FunctionDecl>().match(
4820+
FromTU, functionDecl(hasName("ok")));
4821+
OptErr = Importer->getImportDeclErrorIfAny(FromOK);
4822+
EXPECT_FALSE(OptErr);
4823+
// And we should be able to import.
4824+
FunctionDecl *ImportedOK = Import(FromOK, Lang_CXX);
4825+
EXPECT_TRUE(ImportedOK);
4826+
}
4827+
47414828
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ErrorHandlingTest,
47424829
DefaultTestValuesForRunOptions, );
47434830

0 commit comments

Comments
 (0)