llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Egor Zhdan (egorzhdan) <details> <summary>Changes</summary> This allows annotating C/C++ structs declared within other structs using API Notes. rdar://132083354 --- Full diff: https://github.com/llvm/llvm-project/pull/99655.diff 9 Files Affected: - (modified) clang/lib/APINotes/APINotesFormat.h (+1-1) - (modified) clang/lib/APINotes/APINotesYAMLCompiler.cpp (+11-2) - (modified) clang/lib/Sema/SemaAPINotes.cpp (+75-45) - (modified) clang/test/APINotes/Inputs/Headers/Methods.apinotes (+24) - (modified) clang/test/APINotes/Inputs/Headers/Methods.h (-1) - (modified) clang/test/APINotes/Inputs/Headers/Namespaces.apinotes (+3) - (modified) clang/test/APINotes/Inputs/Headers/Namespaces.h (+3) - (modified) clang/test/APINotes/methods.cpp (+8) - (modified) clang/test/APINotes/namespaces.cpp (+5) ``````````diff diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index cd6456dbe37b2..9d254dcc1c9ef 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 27; // SingleDeclTableKey +const uint16_t VERSION_MINOR = 28; // nested tags const uint8_t kSwiftCopyable = 1; const uint8_t kSwiftNonCopyable = 2; diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 060e1fdaf2fd9..a87e49be83cef 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -406,6 +406,9 @@ template <> struct ScalarEnumerationTraits<EnumConvenienceAliasKind> { } // namespace llvm namespace { +struct Tag; +typedef std::vector<Tag> TagsSeq; + struct Tag { StringRef Name; AvailabilityItem Availability; @@ -421,9 +424,8 @@ struct Tag { std::optional<EnumConvenienceAliasKind> EnumConvenienceKind; std::optional<bool> SwiftCopyable; FunctionsSeq Methods; + TagsSeq Tags; }; - -typedef std::vector<Tag> TagsSeq; } // namespace LLVM_YAML_IS_SEQUENCE_VECTOR(Tag) @@ -456,6 +458,7 @@ template <> struct MappingTraits<Tag> { IO.mapOptional("EnumKind", T.EnumConvenienceKind); IO.mapOptional("SwiftCopyable", T.SwiftCopyable); IO.mapOptional("Methods", T.Methods); + IO.mapOptional("Tags", T.Tags); } }; } // namespace yaml @@ -958,12 +961,18 @@ class YAMLConverter { ContextInfo CI; auto TagCtxID = Writer.addContext(ParentContextID, T.Name, ContextKind::Tag, CI, SwiftVersion); + Context TagCtx(TagCtxID, ContextKind::Tag); for (const auto &CXXMethod : T.Methods) { CXXMethodInfo MI; convertFunction(CXXMethod, MI); Writer.addCXXMethod(TagCtxID, CXXMethod.Name, MI, SwiftVersion); } + + // Convert nested tags. + for (const auto &Tag : T.Tags) { + convertTagContext(TagCtx, Tag, SwiftVersion); + } } void convertTopLevelItems(std::optional<Context> Ctx, diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 055e66a0c3486..92ccbde9b4d35 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -783,51 +783,76 @@ static void ProcessVersionedAPINotes( } } +static std::optional<api_notes::Context> +UnwindNamespaceContext(DeclContext *DC, api_notes::APINotesManager &APINotes) { + if (auto NamespaceContext = dyn_cast<NamespaceDecl>(DC)) { + for (auto Reader : APINotes.findAPINotes(NamespaceContext->getLocation())) { + // Retrieve the context ID for the parent namespace of the decl. + std::stack<NamespaceDecl *> NamespaceStack; + { + for (auto CurrentNamespace = NamespaceContext; CurrentNamespace; + CurrentNamespace = + dyn_cast<NamespaceDecl>(CurrentNamespace->getParent())) { + if (!CurrentNamespace->isInlineNamespace()) + NamespaceStack.push(CurrentNamespace); + } + } + std::optional<api_notes::ContextID> NamespaceID; + while (!NamespaceStack.empty()) { + auto CurrentNamespace = NamespaceStack.top(); + NamespaceStack.pop(); + NamespaceID = + Reader->lookupNamespaceID(CurrentNamespace->getName(), NamespaceID); + if (!NamespaceID) + return std::nullopt; + } + if (NamespaceID) + return api_notes::Context(*NamespaceID, + api_notes::ContextKind::Namespace); + } + } + return std::nullopt; +} + +static std::optional<api_notes::Context> +UnwindTagContext(TagDecl *DC, api_notes::APINotesManager &APINotes) { + for (auto Reader : APINotes.findAPINotes(DC->getLocation())) { + // Retrieve the context ID for the parent tag of the decl. + std::stack<TagDecl *> TagStack; + { + for (auto CurrentTag = DC; CurrentTag; + CurrentTag = dyn_cast<TagDecl>(CurrentTag->getParent())) + TagStack.push(CurrentTag); + } + assert(!TagStack.empty()); + std::optional<api_notes::Context> Ctx = + UnwindNamespaceContext(TagStack.top()->getDeclContext(), APINotes); + while (!TagStack.empty()) { + auto CurrentTag = TagStack.top(); + TagStack.pop(); + auto CtxID = Reader->lookupTagID(CurrentTag->getName(), Ctx); + if (!CtxID) + return std::nullopt; + Ctx = api_notes::Context(*CtxID, api_notes::ContextKind::Tag); + } + return Ctx; + } + return std::nullopt; +} + /// Process API notes that are associated with this declaration, mapping them /// to attributes as appropriate. void Sema::ProcessAPINotes(Decl *D) { if (!D) return; - auto GetNamespaceContext = - [&](DeclContext *DC) -> std::optional<api_notes::Context> { - if (auto NamespaceContext = dyn_cast<NamespaceDecl>(DC)) { - for (auto Reader : - APINotes.findAPINotes(NamespaceContext->getLocation())) { - // Retrieve the context ID for the parent namespace of the decl. - std::stack<NamespaceDecl *> NamespaceStack; - { - for (auto CurrentNamespace = NamespaceContext; CurrentNamespace; - CurrentNamespace = - dyn_cast<NamespaceDecl>(CurrentNamespace->getParent())) { - if (!CurrentNamespace->isInlineNamespace()) - NamespaceStack.push(CurrentNamespace); - } - } - std::optional<api_notes::ContextID> NamespaceID; - while (!NamespaceStack.empty()) { - auto CurrentNamespace = NamespaceStack.top(); - NamespaceStack.pop(); - NamespaceID = Reader->lookupNamespaceID(CurrentNamespace->getName(), - NamespaceID); - if (!NamespaceID) - break; - } - if (NamespaceID) - return api_notes::Context(*NamespaceID, - api_notes::ContextKind::Namespace); - } - } - return std::nullopt; - }; - // Globals. if (D->getDeclContext()->isFileContext() || D->getDeclContext()->isNamespace() || D->getDeclContext()->isExternCContext() || D->getDeclContext()->isExternCXXContext()) { std::optional<api_notes::Context> APINotesContext = - GetNamespaceContext(D->getDeclContext()); + UnwindNamespaceContext(D->getDeclContext(), APINotes); // Global variables. if (auto VD = dyn_cast<VarDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { @@ -899,6 +924,10 @@ void Sema::ProcessAPINotes(Decl *D) { } for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + if (Tag->getName() == "inner_char_box") + llvm::errs(); + if (auto ParentTag = dyn_cast<TagDecl>(Tag->getDeclContext())) + APINotesContext = UnwindTagContext(ParentTag, APINotes); auto Info = Reader->lookupTag(LookupName, APINotesContext); ProcessVersionedAPINotes(*this, Tag, Info); } @@ -1014,23 +1043,24 @@ void Sema::ProcessAPINotes(Decl *D) { } } - if (auto CXXRecord = dyn_cast<CXXRecordDecl>(D->getDeclContext())) { - auto GetRecordContext = [&](api_notes::APINotesReader *Reader) - -> std::optional<api_notes::ContextID> { - auto ParentContext = GetNamespaceContext(CXXRecord->getDeclContext()); - if (auto Found = Reader->lookupTagID(CXXRecord->getName(), ParentContext)) - return *Found; - - return std::nullopt; - }; - + if (auto TagContext = dyn_cast<TagDecl>(D->getDeclContext())) { if (auto CXXMethod = dyn_cast<CXXMethodDecl>(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Context = GetRecordContext(Reader)) { - auto Info = Reader->lookupCXXMethod(*Context, CXXMethod->getName()); + if (auto Context = UnwindTagContext(TagContext, APINotes)) { + auto Info = + Reader->lookupCXXMethod(Context->id, CXXMethod->getName()); ProcessVersionedAPINotes(*this, CXXMethod, Info); } } } + + if (auto Tag = dyn_cast<TagDecl>(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + if (auto Context = UnwindTagContext(TagContext, APINotes)) { + auto Info = Reader->lookupTag(Tag->getName(), Context); + ProcessVersionedAPINotes(*this, Tag, Info); + } + } + } } } diff --git a/clang/test/APINotes/Inputs/Headers/Methods.apinotes b/clang/test/APINotes/Inputs/Headers/Methods.apinotes index 0fa6991a51ff4..618c2cb14b47f 100644 --- a/clang/test/APINotes/Inputs/Headers/Methods.apinotes +++ b/clang/test/APINotes/Inputs/Headers/Methods.apinotes @@ -6,3 +6,27 @@ Tags: - Name: getIncremented Availability: none AvailabilityMsg: "oh no" + - Name: getDecremented + Availability: none + AvailabilityMsg: "this should have no effect" +- Name: Outer + Tags: + - Name: Inner + Methods: + - Name: getDecremented + Availability: none + AvailabilityMsg: "nope" + - Name: getIncremented + Availability: none + AvailabilityMsg: "this should have no effect" + Methods: + - Name: getDecremented + Availability: none + AvailabilityMsg: "this should have no effect" +Functions: +- Name: getIncremented + Availability: none + AvailabilityMsg: "this should have no effect" +- Name: getDecremented + Availability: none + AvailabilityMsg: "this should have no effect" diff --git a/clang/test/APINotes/Inputs/Headers/Methods.h b/clang/test/APINotes/Inputs/Headers/Methods.h index f46fe31533e5d..6a96b12762871 100644 --- a/clang/test/APINotes/Inputs/Headers/Methods.h +++ b/clang/test/APINotes/Inputs/Headers/Methods.h @@ -4,7 +4,6 @@ struct IntWrapper { IntWrapper getIncremented() const { return {value + 1}; } }; -// TODO: support nested tags struct Outer { struct Inner { int value; diff --git a/clang/test/APINotes/Inputs/Headers/Namespaces.apinotes b/clang/test/APINotes/Inputs/Headers/Namespaces.apinotes index 68073932d600e..b7d962e0efda6 100644 --- a/clang/test/APINotes/Inputs/Headers/Namespaces.apinotes +++ b/clang/test/APINotes/Inputs/Headers/Namespaces.apinotes @@ -41,6 +41,9 @@ Namespaces: Methods: - Name: methodInNestedNamespace SwiftName: swiftMethodInNestedNamespace() + Tags: + - Name: inner_char_box + SwiftName: InnerCharBox Namespaces: - Name: Namespace1 Tags: diff --git a/clang/test/APINotes/Inputs/Headers/Namespaces.h b/clang/test/APINotes/Inputs/Headers/Namespaces.h index e996b8ffa6b6e..c7a891f205c06 100644 --- a/clang/test/APINotes/Inputs/Headers/Namespaces.h +++ b/clang/test/APINotes/Inputs/Headers/Namespaces.h @@ -10,6 +10,9 @@ void funcInNestedNamespace(int i); struct char_box { char c; void methodInNestedNamespace(); + struct inner_char_box { + char c; + }; }; } diff --git a/clang/test/APINotes/methods.cpp b/clang/test/APINotes/methods.cpp index 692f750ed66c7..910565745bea2 100644 --- a/clang/test/APINotes/methods.cpp +++ b/clang/test/APINotes/methods.cpp @@ -1,9 +1,17 @@ // RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Methods -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -x c++ // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Methods -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter IntWrapper::getIncremented -x c++ | FileCheck --check-prefix=CHECK-METHOD %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Methods -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Outer::Inner::getDecremented -x c++ | FileCheck --check-prefix=CHECK-DEEP-METHOD %s #include "Methods.h" // CHECK-METHOD: Dumping IntWrapper::getIncremented: // CHECK-METHOD-NEXT: CXXMethodDecl {{.+}} getIncremented // CHECK-METHOD: UnavailableAttr {{.+}} <<invalid sloc>> "oh no" + +// CHECK-DEEP-METHOD: Dumping Outer::Inner::getDecremented: +// CHECK-DEEP-METHOD-NEXT: CXXMethodDecl {{.+}} getDecremented +// CHECK-DEEP-METHOD: UnavailableAttr {{.+}} <<invalid sloc>> "nope" + +// CHECK-METHOD-NOT: this should have no effect +// CHECK-DEEP-METHOD-NOT: this should have no effect diff --git a/clang/test/APINotes/namespaces.cpp b/clang/test/APINotes/namespaces.cpp index a6517a324b9c5..df108b02e6301 100644 --- a/clang/test/APINotes/namespaces.cpp +++ b/clang/test/APINotes/namespaces.cpp @@ -8,6 +8,7 @@ // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::varInNestedNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-GLOBAL-IN-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested2::varInNestedNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-ANOTHER-GLOBAL-IN-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::char_box -x objective-c++ | FileCheck -check-prefix=CHECK-STRUCT-IN-NESTED-NAMESPACE %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::char_box::inner_char_box -x objective-c++ | FileCheck -check-prefix=CHECK-STRUCT-IN-STRUCT-IN-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::funcInNestedNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-FUNC-IN-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::char_box::methodInNestedNamespace -x objective-c++ | FileCheck -check-prefix=CHECK-METHOD-IN-NESTED-NAMESPACE %s // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter Namespace1::Nested1::Namespace1::char_box -x objective-c++ | FileCheck -check-prefix=CHECK-STRUCT-IN-DEEP-NESTED-NAMESPACE %s @@ -56,6 +57,10 @@ // CHECK-STRUCT-IN-NESTED-NAMESPACE-NEXT: CXXRecordDecl {{.+}} imported in Namespaces <undeserialized declarations> struct char_box // CHECK-STRUCT-IN-NESTED-NAMESPACE: SwiftNameAttr {{.+}} <<invalid sloc>> "NestedCharBox" +// CHECK-STRUCT-IN-STRUCT-IN-NESTED-NAMESPACE: Dumping Namespace1::Nested1::char_box::inner_char_box: +// CHECK-STRUCT-IN-STRUCT-IN-NESTED-NAMESPACE-NEXT: CXXRecordDecl {{.+}} imported in Namespaces <undeserialized declarations> struct inner_char_box +// CHECK-STRUCT-IN-STRUCT-IN-NESTED-NAMESPACE: SwiftNameAttr {{.+}} <<invalid sloc>> "InnerCharBox" + // CHECK-METHOD-IN-NESTED-NAMESPACE: Dumping Namespace1::Nested1::char_box::methodInNestedNamespace: // CHECK-METHOD-IN-NESTED-NAMESPACE-NEXT: CXXMethodDecl {{.+}} imported in Namespaces methodInNestedNamespace // CHECK-METHOD-IN-NESTED-NAMESPACE: SwiftNameAttr {{.+}} <<invalid sloc>> "swiftMethodInNestedNamespace()" `````````` </details> https://github.com/llvm/llvm-project/pull/99655 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits