martong created this revision. martong added a reviewer: a_sidorin. Herald added subscribers: cfe-commits, gamesh411, Szelethus, dkrupp, rnkovacs. Herald added a reviewer: a.sidorin. Herald added a reviewer: shafik. Herald added a project: clang. martong added a parent revision: D62373: [ASTImporter] Store import errors for Decls. martong added a child revision: D62375: [ASTImporter] Mark erroneous nodes in from ctx.
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. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D63603 Files: clang/lib/AST/ASTImporter.cpp clang/unittests/AST/ASTImporterTest.cpp
Index: clang/unittests/AST/ASTImporterTest.cpp =================================================================== --- clang/unittests/AST/ASTImporterTest.cpp +++ clang/unittests/AST/ASTImporterTest.cpp @@ -4695,6 +4695,54 @@ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); } +// An error should be set for a class if we cannot import one member. +TEST_P(ErrorHandlingTest, ErrorIsPropagatedFromMemberToClass) { + std::string Code = formatv( + R"( + class X { + void f() { {0}; } // This member has the error during import. + void ok(); // The error should not prevent importing this. + }; // An error will be set for X too. + )", + ErroneousStmt); + TranslationUnitDecl *FromTU = getTuDecl(Code, 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()); +} + INSTANTIATE_TEST_CASE_P(ParameterizedTests, ErrorHandlingTest, DefaultTestValuesForRunOptions, ); Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -1631,16 +1631,32 @@ 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( @@ -1697,7 +1713,15 @@ return Error::success(); } - 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. + struct DefinitionCompleter { + RecordDecl *To; + DefinitionCompleter(RecordDecl *To) : To(To) { To->startDefinition(); } + ~DefinitionCompleter() { To->completeDefinition(); } + }; + DefinitionCompleter CompleterRAII(To); if (Error Err = setTypedefNameForAnonDecl(From, To, Importer)) return Err; @@ -1822,7 +1846,6 @@ if (Error Err = ImportDeclContext(From, /*ForceImport=*/true)) return Err; - To->completeDefinition(); return Error::success(); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits