diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -186,22 +186,6 @@ return import(*From); } - // Helper for chaining together multiple imports. If an error is detected, - // subsequent imports will return default constructed nodes, so that failure - // can be detected with a single conditional branch after a sequence of - // imports. - template T importChecked(Error &Err, const T &From) { - // Don't attempt to import nodes if we hit an error earlier. - if (Err) - return T{}; - Expected MaybeVal = import(From); - if (!MaybeVal) { - Err = MaybeVal.takeError(); - return T{}; - } - return *MaybeVal; - } - ExplicitSpecifier importExplicitSpecifier(Error &Err, ExplicitSpecifier ESpec); @@ -669,6 +653,22 @@ ExpectedStmt VisitCXXTypeidExpr(CXXTypeidExpr *E); ExpectedStmt VisitCXXFoldExpr(CXXFoldExpr *E); + // Helper for chaining together multiple imports. If an error is detected, + // subsequent imports will return default constructed nodes, so that failure + // can be detected with a single conditional branch after a sequence of + // imports. + template T importChecked(Error &Err, const T &From) { + // Don't attempt to import nodes if we hit an error earlier. + if (Err) + return T{}; + Expected MaybeVal = import(From); + if (!MaybeVal) { + Err = MaybeVal.takeError(); + return T{}; + } + return *MaybeVal; + } + template Error ImportArrayChecked(IIter Ibegin, IIter Iend, OIter Obegin) { using ItemT = std::remove_reference_t; @@ -8408,8 +8408,118 @@ return ToContext.getTrivialTypeSourceInfo(*TOrErr, *BeginLocOrErr); } +// To use this object, it should be created before the new attribute is created, +// and destructed after it is created. The construction already performs the +// import of the data. +template struct AttrArgImporter { + AttrArgImporter(const AttrArgImporter &) = delete; + AttrArgImporter(AttrArgImporter &&) = default; + AttrArgImporter &operator=(const AttrArgImporter &) = delete; + AttrArgImporter &operator=(AttrArgImporter &&) = default; + + AttrArgImporter(ASTNodeImporter &I, Error &Err, const T &From) + : To(I.importChecked(Err, From)) {} + + const T &value() { return To; } + +private: + T To; +}; + +// To use this object, it should be created before the new attribute is created, +// and destructed after it is created. The construction already performs the +// import of the data. The array data is accessible in a pointer form, this form +// is used by the attribute classes. This object should be created once for the +// array data to be imported (the array size is not imported, just copied). +template struct AttrArgArrayImporter { + AttrArgArrayImporter(const AttrArgArrayImporter &) = delete; + AttrArgArrayImporter(AttrArgArrayImporter &&) = default; + AttrArgArrayImporter &operator=(const AttrArgArrayImporter &) = delete; + AttrArgArrayImporter &operator=(AttrArgArrayImporter &&) = default; + + AttrArgArrayImporter(ASTNodeImporter &I, Error &Err, + const llvm::iterator_range &From, + unsigned ArraySize) { + if (Err) + return; + To.reserve(ArraySize); + Err = I.ImportContainerChecked(From, To); + } + + T *value() { return To.data(); } + +private: + llvm::SmallVector To; +}; + +class AttrImporter { + Error Err = Error::success(); + ASTImporter &Importer; + ASTNodeImporter NImporter; + +public: + AttrImporter(ASTImporter &I) : Importer(I), NImporter(I) {} + + // Create an "importer" for an attribute parameter. + // Result of the 'value()' of that object is to be passed to the function + // 'createImpoprtedAttr', in the order that is expected by the attribute + // class. + template AttrArgImporter importArg(const T &From) { + return AttrArgImporter(NImporter, Err, From); + } + + // Create an "importer" for an attribute parameter that has array type. + // Result of the 'value()' of that object is to be passed to the function + // 'createImpoprtedAttr', then the size of the array as next argument. + template + AttrArgArrayImporter importArrayArg(const llvm::iterator_range &From, + unsigned ArraySize) { + return AttrArgArrayImporter(NImporter, Err, From, ArraySize); + } + + // Create an attribute object with the specified arguments. + // The 'FromAttr' is the original (not imported) attribute, the 'ImportedArg' + // should be values that are passed to the 'Create' function of the attribute. + // (The 'Create' with 'ASTContext' first and 'AttributeCommonInfo' last is + // used here.) As much data is copied or imported from the old attribute + // as possible. The passed arguments should be already imported. + template + Expected createImportedAttr(const T *FromAttr, Arg &&...ImportedArg) { + static_assert(std::is_base_of::value, + "T should be subclass of Attr."); + + const IdentifierInfo *ToAttrName = Importer.Import(FromAttr->getAttrName()); + const IdentifierInfo *ToScopeName = + Importer.Import(FromAttr->getScopeName()); + SourceRange ToAttrRange = + NImporter.importChecked(Err, FromAttr->getRange()); + SourceLocation ToScopeLoc = + NImporter.importChecked(Err, FromAttr->getScopeLoc()); + + if (Err) + return std::move(Err); + + AttributeCommonInfo ToI(ToAttrName, ToScopeName, ToAttrRange, ToScopeLoc, + FromAttr->getParsedKind(), FromAttr->getSyntax(), + FromAttr->getAttributeSpellingListIndex()); + // The "SemanticSpelling" is not needed to be passed to the constructor. + // That value is recalculated from the SpellingListIndex if needed. + T *ToAttr = T::Create(Importer.getToContext(), + std::forward(ImportedArg)..., ToI); + + ToAttr->setImplicit(FromAttr->isImplicit()); + ToAttr->setPackExpansion(FromAttr->isPackExpansion()); + if (auto *ToInheritableAttr = dyn_cast(ToAttr)) + ToInheritableAttr->setInherited(FromAttr->isInherited()); + + return ToAttr; + } +}; + Expected ASTImporter::Import(const Attr *FromAttr) { Attr *ToAttr = nullptr; + // FIXME: Use AttrImporter as much as possible, try to remove the import + // of range from here. SourceRange ToRange; if (Error Err = importInto(ToRange, FromAttr->getRange())) return std::move(Err); @@ -8451,6 +8561,20 @@ ToAttr = To; break; } + + case attr::AssertCapability: { + const auto *From = cast(FromAttr); + AttrImporter AI(*this); + Expected ToAttrOrErr = AI.createImportedAttr( + From, AI.importArrayArg(From->args(), From->args_size()).value(), + From->args_size()); + if (ToAttrOrErr) + ToAttr = *ToAttrOrErr; + else + return ToAttrOrErr.takeError(); + break; + } + default: // FIXME: 'clone' copies every member but some of them should be imported. // Handle other Attrs that have parameters that should be imported. diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -6406,6 +6406,83 @@ ToSM.getBufferOrFake(ImportedID, SourceLocation()).getBuffer()); } +struct ImportAttributes : public ASTImporterOptionSpecificTestBase { + void checkAttrImportCommon(const Attr *From, const Attr *To, + const Decl *ToD) { + + // Verify that dump does not crash because invalid data. + ToD->dump(llvm::nulls()); + + EXPECT_EQ(From->getParsedKind(), To->getParsedKind()); + EXPECT_EQ(From->getSyntax(), To->getSyntax()); + if (From->getAttrName()) { + EXPECT_TRUE(To->getAttrName()); + EXPECT_STREQ(From->getAttrName()->getNameStart(), + To->getAttrName()->getNameStart()); + } else { + EXPECT_FALSE(To->getAttrName()); + } + if (From->getScopeName()) { + EXPECT_TRUE(To->getScopeName()); + EXPECT_STREQ(From->getScopeName()->getNameStart(), + To->getScopeName()->getNameStart()); + } else { + EXPECT_FALSE(To->getScopeName()); + } + EXPECT_EQ(From->getSpellingListIndex(), To->getSpellingListIndex()); + EXPECT_STREQ(From->getSpelling(), To->getSpelling()); + EXPECT_EQ(From->isInherited(), To->isInherited()); + EXPECT_EQ(From->isImplicit(), To->isImplicit()); + EXPECT_EQ(From->isPackExpansion(), To->isPackExpansion()); + EXPECT_EQ(From->isLateParsed(), To->isLateParsed()); + } + + template + void importAttr(const char *Code, AT *&FromAttr, AT *&ToAttr) { + static_assert(std::is_base_of::value, "AT should be an Attr"); + static_assert(std::is_base_of::value, "DT should be a Decl"); + + Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input.cc"); + DT *FromD = + FirstDeclMatcher
().match(FromTU, namedDecl(hasName("test"))); + ASSERT_TRUE(FromD); + + DT *ToD = Import(FromD, Lang_CXX11); + ASSERT_TRUE(ToD); + + FromAttr = FromD->template getAttr(); + ToAttr = ToD->template getAttr(); + ASSERT_TRUE(FromAttr); + EXPECT_TRUE(ToAttr); + + checkAttrImportCommon(FromAttr, ToAttr, ToD); + } + + template void checkImported(const T *From, const T *To) { + EXPECT_TRUE(To); + EXPECT_NE(From, To); + } + + template + void checkImportVariadicArg(const llvm::iterator_range &From, + const llvm::iterator_range &To) { + for (auto FromI = From.begin(), ToI = To.begin(); FromI != From.end(); + ++FromI, ++ToI) { + ASSERT_NE(ToI, To.end()); + checkImported(*FromI, *ToI); + } + } +}; + +template <> +void ImportAttributes::checkImported(const Decl *From, const Decl *To) { + EXPECT_TRUE(To); + EXPECT_NE(From, To); + EXPECT_EQ(To->getTranslationUnitDecl(), + ToAST->getASTContext().getTranslationUnitDecl()); +} + +// FIXME: Use ImportAttributes for this test. TEST_P(ASTImporterOptionSpecificTestBase, ImportExprOfAlignmentAttr) { // Test if import of these packed and aligned attributes does not trigger an // error situation where source location from 'From' context is referenced in @@ -6441,6 +6518,7 @@ EXPECT_TRUE(ToA); } +// FIXME: Use ImportAttributes for this test. TEST_P(ASTImporterOptionSpecificTestBase, ImportFormatAttr) { Decl *FromTU = getTuDecl( R"( @@ -6466,6 +6544,15 @@ ToAttr->getAttributeSpellingListIndex()); EXPECT_EQ(FromAttr->getType()->getName(), ToAttr->getType()->getName()); } + +TEST_P(ImportAttributes, ImportAssertCapability) { + AssertCapabilityAttr *FromAttr, *ToAttr; + importAttr( + "void test(int A1, int A2) __attribute__((assert_capability(A1, A2)));", + FromAttr, ToAttr); + checkImportVariadicArg(FromAttr->args(), ToAttr->args()); +} + template auto ExtendWithOptions(const T &Values, const std::vector &Args) { auto Copy = Values; @@ -6849,5 +6936,8 @@ INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportWithExternalSource, DefaultTestValuesForRunOptions); +INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportAttributes, + DefaultTestValuesForRunOptions); + } // end namespace ast_matchers } // end namespace clang