r364752 - [ASTImporter] Propagate error from ImportDeclContext
Gabor Marton via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 1 05:44:39 PDT 2019
Author: martong
Date: Mon Jul 1 05:44:39 2019
New Revision: 364752
URL: http://llvm.org/viewvc/llvm-project?rev=364752&view=rev
Log:
[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
Modified:
cfe/trunk/lib/AST/ASTImporter.cpp
cfe/trunk/unittests/AST/ASTImporterTest.cpp
Modified: cfe/trunk/lib/AST/ASTImporter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTImporter.cpp?rev=364752&r1=364751&r2=364752&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTImporter.cpp (original)
+++ cfe/trunk/lib/AST/ASTImporter.cpp Mon Jul 1 05:44:39 2019
@@ -57,6 +57,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
@@ -1631,16 +1632,32 @@ ASTNodeImporter::ImportDeclContext(DeclC
auto ToDCOrErr = Importer.ImportContext(FromDC);
return ToDCOrErr.takeError();
}
+
+ // We use strict error handling in case of records and enums, but not
+ // with e.g. namespaces.
+ //
+ // FIXME Clients of the ASTImporter should be able to choose an
+ // appropriate error handling strategy for their needs. For instance,
+ // they may not want to mark an entire namespace as erroneous merely
+ // because there is an ODR error with two typedefs. As another example,
+ // the client may allow EnumConstantDecls with same names but with
+ // different values in two distinct translation units.
+ bool AccumulateChildErrors = isa<TagDecl>(FromDC);
+
+ Error ChildErrors = Error::success();
llvm::SmallVector<Decl *, 8> ImportedDecls;
for (auto *From : FromDC->decls()) {
ExpectedDecl ImportedOrErr = import(From);
- if (!ImportedOrErr)
- // Ignore the error, continue with next Decl.
- // FIXME: Handle this case somehow better.
- consumeError(ImportedOrErr.takeError());
+ if (!ImportedOrErr) {
+ if (AccumulateChildErrors)
+ ChildErrors =
+ joinErrors(std::move(ChildErrors), ImportedOrErr.takeError());
+ else
+ consumeError(ImportedOrErr.takeError());
+ }
}
- return Error::success();
+ return ChildErrors;
}
Error ASTNodeImporter::ImportDeclContext(
@@ -1698,6 +1715,11 @@ Error ASTNodeImporter::ImportDefinition(
}
To->startDefinition();
+ // Complete the definition even if error is returned.
+ // The RecordDecl may be already part of the AST so it is better to
+ // have it in complete state even if something is wrong with it.
+ auto DefinitionCompleter =
+ llvm::make_scope_exit([To]() { To->completeDefinition(); });
if (Error Err = setTypedefNameForAnonDecl(From, To, Importer))
return Err;
@@ -1822,7 +1844,6 @@ Error ASTNodeImporter::ImportDefinition(
if (Error Err = ImportDeclContext(From, /*ForceImport=*/true))
return Err;
- To->completeDefinition();
return Error::success();
}
Modified: cfe/trunk/unittests/AST/ASTImporterTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/ASTImporterTest.cpp?rev=364752&r1=364751&r2=364752&view=diff
==============================================================================
--- cfe/trunk/unittests/AST/ASTImporterTest.cpp (original)
+++ cfe/trunk/unittests/AST/ASTImporterTest.cpp Mon Jul 1 05:44:39 2019
@@ -4738,6 +4738,93 @@ TEST_P(ErrorHandlingTest, ErrorHappensAf
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
}
+// An error should be set for a class if we cannot import one member.
+TEST_P(ErrorHandlingTest, ErrorIsPropagatedFromMemberToClass) {
+ TranslationUnitDecl *FromTU = getTuDecl(
+ std::string(R"(
+ class X {
+ void f() { )") + ErroneousStmt + R"( } // This member has the error
+ // during import.
+ void ok(); // The error should not prevent importing this.
+ }; // An error will be set for X too.
+ )",
+ Lang_CXX);
+ auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X")));
+ CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX);
+
+ // An error is set for X.
+ EXPECT_FALSE(ImportedX);
+ ASTImporter *Importer = findFromTU(FromX)->Importer.get();
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+
+ // An error is set for f().
+ auto *FromF = FirstDeclMatcher<CXXMethodDecl>().match(
+ FromTU, cxxMethodDecl(hasName("f")));
+ OptErr = Importer->getImportDeclErrorIfAny(FromF);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+ // And any subsequent import should fail.
+ CXXMethodDecl *ImportedF = Import(FromF, Lang_CXX);
+ EXPECT_FALSE(ImportedF);
+
+ // There is no error set for ok().
+ auto *FromOK = FirstDeclMatcher<CXXMethodDecl>().match(
+ FromTU, cxxMethodDecl(hasName("ok")));
+ OptErr = Importer->getImportDeclErrorIfAny(FromOK);
+ EXPECT_FALSE(OptErr);
+ // And we should be able to import.
+ CXXMethodDecl *ImportedOK = Import(FromOK, Lang_CXX);
+ EXPECT_TRUE(ImportedOK);
+
+ // Unwary clients may access X even if the error is set, so, at least make
+ // sure the class is set to be complete.
+ CXXRecordDecl *ToX = cast<CXXRecordDecl>(ImportedOK->getDeclContext());
+ EXPECT_TRUE(ToX->isCompleteDefinition());
+}
+
+TEST_P(ErrorHandlingTest, ErrorIsNotPropagatedFromMemberToNamespace) {
+ TranslationUnitDecl *FromTU = getTuDecl(
+ std::string(R"(
+ namespace X {
+ void f() { )") + ErroneousStmt + R"( } // This member has the error
+ // during import.
+ void ok(); // The error should not prevent importing this.
+ }; // An error will be set for X too.
+ )",
+ Lang_CXX);
+ auto *FromX = FirstDeclMatcher<NamespaceDecl>().match(
+ FromTU, namespaceDecl(hasName("X")));
+ NamespaceDecl *ImportedX = Import(FromX, Lang_CXX);
+
+ // There is no error set for X.
+ EXPECT_TRUE(ImportedX);
+ ASTImporter *Importer = findFromTU(FromX)->Importer.get();
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
+ ASSERT_FALSE(OptErr);
+
+ // An error is set for f().
+ auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ OptErr = Importer->getImportDeclErrorIfAny(FromF);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+ // And any subsequent import should fail.
+ FunctionDecl *ImportedF = Import(FromF, Lang_CXX);
+ EXPECT_FALSE(ImportedF);
+
+ // There is no error set for ok().
+ auto *FromOK = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("ok")));
+ OptErr = Importer->getImportDeclErrorIfAny(FromOK);
+ EXPECT_FALSE(OptErr);
+ // And we should be able to import.
+ FunctionDecl *ImportedOK = Import(FromOK, Lang_CXX);
+ EXPECT_TRUE(ImportedOK);
+}
+
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ErrorHandlingTest,
DefaultTestValuesForRunOptions, );
More information about the cfe-commits
mailing list