From 57475013c9a456b7a88381b0514514f7933944d9 Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Mon, 18 Jul 2022 16:51:42 -0600 Subject: [PATCH 1/4] use newer cmark node kind names the older names were kept in the cmark header for compatibility, but these are the ones that are actually defined in the main enum --- lib/Markup/Markup.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/Markup/Markup.cpp b/lib/Markup/Markup.cpp index 61275f19c0dc7..6d248985ed4b0 100644 --- a/lib/Markup/Markup.cpp +++ b/lib/Markup/Markup.cpp @@ -137,7 +137,7 @@ ParseResult parseStrong(MarkupContext &MC, ParseState State) { } ParseResult
parseHeader(MarkupContext &MC, ParseState State) { - assert(cmark_node_get_type(State.Node) == CMARK_NODE_HEADER + assert(cmark_node_get_type(State.Node) == CMARK_NODE_HEADING && State.Event == CMARK_EVENT_ENTER); auto Level = cmark_node_get_header_level(State.Node); SmallVector Children; @@ -149,19 +149,19 @@ ParseResult
parseHeader(MarkupContext &MC, ParseState State) { } ParseResult parseHRule(MarkupContext &MC, ParseState State) { - assert(cmark_node_get_type(State.Node) == CMARK_NODE_HRULE + assert(cmark_node_get_type(State.Node) == CMARK_NODE_THEMATIC_BREAK && State.Event == CMARK_EVENT_ENTER); return { HRule::create(MC), State.next() }; } ParseResult parseHTML(MarkupContext &MC, ParseState State) { - assert(cmark_node_get_type(State.Node) == CMARK_NODE_HTML + assert(cmark_node_get_type(State.Node) == CMARK_NODE_HTML_BLOCK && State.Event == CMARK_EVENT_ENTER); return {HTML::create(MC, getLiteralContent(MC, State.Node)), State.next()}; } ParseResult parseInlineHTML(MarkupContext &MC, ParseState State) { - assert(cmark_node_get_type(State.Node) == CMARK_NODE_INLINE_HTML + assert(cmark_node_get_type(State.Node) == CMARK_NODE_HTML_INLINE && State.Event == CMARK_EVENT_ENTER); return {InlineHTML::create(MC, getLiteralContent(MC, State.Node)), State.next()}; @@ -257,21 +257,18 @@ ParseResult parseElement(MarkupContext &MC, ParseState State) { case CMARK_NODE_EMPH: { return parseEmphasis(MC, State); } - case CMARK_NODE_HEADER: { + case CMARK_NODE_HEADING: { return parseHeader(MC, State); } - case CMARK_NODE_HRULE: { - return parseHRule(MC, State); - } - case CMARK_NODE_HTML: { + case CMARK_NODE_HTML_BLOCK: { return parseHTML(MC, State); } + case CMARK_NODE_HTML_INLINE: { + return parseInlineHTML(MC, State); + } case CMARK_NODE_IMAGE: { return parseImage(MC, State); } - case CMARK_NODE_INLINE_HTML: { - return parseInlineHTML(MC, State); - } case CMARK_NODE_ITEM: { return parseItem(MC, State); } @@ -296,6 +293,9 @@ ParseResult parseElement(MarkupContext &MC, ParseState State) { case CMARK_NODE_TEXT: { return parseText(MC, State); } + case CMARK_NODE_THEMATIC_BREAK: { + return parseHRule(MC, State); + } default: { llvm_unreachable("Can't parse a Markup node of type 'None'"); } From 76fe621c1f225551c35590274bad7e86d5626084 Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Thu, 21 Jul 2022 08:12:07 -0600 Subject: [PATCH 2/4] markup: add support for swift-cmark inline attributes rdar://96830173 --- bindings/xml/comment-xml-schema.rng | 7 +++++ include/swift/Markup/AST.h | 26 +++++++++++++++++++ include/swift/Markup/ASTNodes.def | 3 ++- lib/Frontend/PrintingDiagnosticConsumer.cpp | 9 +++++++ lib/IDE/CommentConversion.cpp | 18 +++++++++++++ lib/Markup/AST.cpp | 18 +++++++++++++ lib/Markup/Markup.cpp | 16 ++++++++++++ .../comment_to_something_conversion.swift | 21 +++++++++++++++ .../Inputs/comments-expected-output.h | 18 +++++++++++++ 9 files changed, 135 insertions(+), 1 deletion(-) diff --git a/bindings/xml/comment-xml-schema.rng b/bindings/xml/comment-xml-schema.rng index e077c7ccbb9c8..22af2efe614e6 100644 --- a/bindings/xml/comment-xml-schema.rng +++ b/bindings/xml/comment-xml-schema.rng @@ -959,6 +959,13 @@ .*\S.* + + + + + .*\S.* + + diff --git a/include/swift/Markup/AST.h b/include/swift/Markup/AST.h index fa5efdedc6be4..7c375c8d55fbb 100644 --- a/include/swift/Markup/AST.h +++ b/include/swift/Markup/AST.h @@ -589,6 +589,32 @@ class Image final : public InlineContent, } }; +class Attribute final : public InlineContent, private llvm::TrailingObjects { + friend TrailingObjects; + + size_t NumChildren; + StringRef Attributes; + + Attribute(StringRef Attributes, ArrayRef Children); + +public: + static Attribute *create(MarkupContext &MC, StringRef Attributes, ArrayRef Children); + + StringRef getAttributes() const { return Attributes; } + + ArrayRef getChildren() { + return {getTrailingObjects(), NumChildren}; + } + + ArrayRef getChildren() const { + return {getTrailingObjects(), NumChildren}; + } + + static bool classof(const MarkupASTNode *N) { + return N->getKind() == ASTNodeKind::Attribute; + } +}; + #pragma mark Private Extensions class PrivateExtension : public MarkupASTNode { diff --git a/include/swift/Markup/ASTNodes.def b/include/swift/Markup/ASTNodes.def index c999fc924e5c3..27a5a31bbf93c 100644 --- a/include/swift/Markup/ASTNodes.def +++ b/include/swift/Markup/ASTNodes.def @@ -40,7 +40,8 @@ ABSTRACT_MARKUP_AST_NODE(InlineContent, MarkupASTNode) MARKUP_AST_NODE(Strong, InlineContent) MARKUP_AST_NODE(Link, InlineContent) MARKUP_AST_NODE(Image, InlineContent) -MARKUP_AST_NODE_RANGE(Inline, Text, Image) + MARKUP_AST_NODE(Attribute, InlineContent) +MARKUP_AST_NODE_RANGE(Inline, Text, Attribute) /// Private Markdown Extensions - these should not be implemented in the /// underlying cmark parser. diff --git a/lib/Frontend/PrintingDiagnosticConsumer.cpp b/lib/Frontend/PrintingDiagnosticConsumer.cpp index 412419f9fb4f5..981365a1a4ea2 100644 --- a/lib/Frontend/PrintingDiagnosticConsumer.cpp +++ b/lib/Frontend/PrintingDiagnosticConsumer.cpp @@ -142,6 +142,15 @@ namespace { } } + void visitAttribute(const Attribute *A) { + print("^["); + for (const auto *Child : A->getChildren()) + visit(Child); + print("]("); + print(A->getAttributes()); + print(")"); + } + void visitBlockQuote(const BlockQuote *BQ) { indent(); printNewline(); diff --git a/lib/IDE/CommentConversion.cpp b/lib/IDE/CommentConversion.cpp index b9cd2554399e9..c78d376857a1b 100644 --- a/lib/IDE/CommentConversion.cpp +++ b/lib/IDE/CommentConversion.cpp @@ -72,6 +72,18 @@ struct CommentToXMLConverter { llvm_unreachable("Can't print a swift::markup::Document as XML directly"); } + void printAttribute(const Attribute *A) { + OS << "getAttributes()); + OS << "\">"; + + for (const auto *N : A->getChildren()) { + printASTNode(N); + } + + OS << ""; + } + void printBlockQuote(const BlockQuote *BQ) { for (const auto *N : BQ->getChildren()) printASTNode(N); @@ -638,6 +650,12 @@ class DoxygenConverter : public MarkupASTVisitor { visit(Child); } + void visitAttribute(const Attribute *A) { + // attributed strings don't have an analogue in Doxygen, so just print out the text + for (const auto *Child : A->getChildren()) + visit(Child); + } + void visitBlockQuote(const BlockQuote *BQ) { print("
"); printNewline(); diff --git a/lib/Markup/AST.cpp b/lib/Markup/AST.cpp index 9d55716a20382..7afe628ef1a47 100644 --- a/lib/Markup/AST.cpp +++ b/lib/Markup/AST.cpp @@ -157,6 +157,16 @@ Paragraph *Paragraph::create(MarkupContext &MC, return new (Mem) Paragraph(Children); } +Attribute::Attribute(StringRef Attributes, ArrayRef Children) : InlineContent(ASTNodeKind::Attribute), NumChildren(Children.size()), Attributes(Attributes) { + std::uninitialized_copy(Children.begin(), Children.end(), getTrailingObjects()); +} + +Attribute *Attribute::create(MarkupContext &MC, StringRef Attributes, ArrayRef Children) { + void *Mem = MC.allocate(totalSizeToAlloc(Children.size()), alignof(Attribute)); + StringRef AttrsCopy = MC.allocateCopy(Attributes); + return new (Mem) Attribute(AttrsCopy, Children); +} + HRule *HRule::create(MarkupContext &MC) { void *Mem = MC.allocate(sizeof(HRule), alignof(HRule)); return new (Mem) HRule(); @@ -408,6 +418,14 @@ void swift::markup::dump(const MarkupASTNode *Node, llvm::raw_ostream &OS, dumpChildren(Node->getChildren(), OS, indent + 1); break; } + case swift::markup::ASTNodeKind::Attribute: { + auto A = cast(Node); + OS << "Attribute:"; + OS << " Attributes=" << A->getAttributes(); + OS << " Children=" << Node->getChildren().size(); + dumpChildren(Node->getChildren(), OS, indent + 1); + break; + } case swift::markup::ASTNodeKind::BlockQuote: { OS << "BlockQuote: Children=" << Node->getChildren().size(); dumpChildren(Node->getChildren(), OS, indent + 1); diff --git a/lib/Markup/Markup.cpp b/lib/Markup/Markup.cpp index 6d248985ed4b0..b9494dfd22570 100644 --- a/lib/Markup/Markup.cpp +++ b/lib/Markup/Markup.cpp @@ -216,6 +216,15 @@ ParseResult parseLink(MarkupContext &MC, ParseState State) { return { Link::create(MC, Destination, Children), ResultState.next() }; } +ParseResult parseAttribute(MarkupContext &MC, ParseState State) { + assert(cmark_node_get_type(State.Node) == CMARK_NODE_ATTRIBUTE && State.Event == CMARK_EVENT_ENTER); + std::string Attributes(cmark_node_get_attributes(State.Node)); + SmallVector Children; + auto ResultState = parseChildren(MC, State, Children); + assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); + return { Attribute::create(MC, Attributes, Children), ResultState.next() }; +} + ParseResult parseList(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_LIST && State.Event == CMARK_EVENT_ENTER); @@ -245,6 +254,13 @@ ParseResult parseElement(MarkupContext &MC, ParseState State) { case CMARK_NODE_DOCUMENT: { llvm_unreachable("Markup documents cannot be nested"); } + case CMARK_NODE_FOOTNOTE_REFERENCE: + case CMARK_NODE_FOOTNOTE_DEFINITION: { + llvm_unreachable("Footnotes are not currently parsed by swiftMarkup"); + } + case CMARK_NODE_ATTRIBUTE: { + return parseAttribute(MC, State); + } case CMARK_NODE_BLOCK_QUOTE: { return parseBlockQuote(MC, State); } diff --git a/test/Inputs/comment_to_something_conversion.swift b/test/Inputs/comment_to_something_conversion.swift index f88565bd005f4..52b6396686ec1 100644 --- a/test/Inputs/comment_to_something_conversion.swift +++ b/test/Inputs/comment_to_something_conversion.swift @@ -62,6 +62,15 @@ public enum A012_AttachToEntities { // CHECK: {{.*}}DocCommentAsXML=[f0()c:@M@comment_to_xml@objc(cs)ATXHeaders(im)f0@objc public func f0()]]>LEVEL ONE]]>]]>LEVEL TWO]]>] } +@objc public class Attributes { +// CHECK: {{.*}}DocCommentAsXML=none + /// Here is an attribute: + /// + /// ^[Attribute text](string: "attributed") + @objc public func f0() {} + // CHECK: {{.*}}DocCommentAsXML=[f0()c:@M@comment_to_xml@objc(cs)Attributes(im)f0@objc public func f0()Here is an attribute:Attribute text] +} + @objc public class AutomaticLink { // CHECK: {{.*}}DocCommentAsXML=none /// And now for a URL. @@ -198,6 +207,18 @@ public enum A012_AttachToEntities { // CHECK: {{.*}}DocCommentAsXML=[f4()c:@M@comment_to_xml@objc(cs)EmptyComments(im)f4@objc public func f4()Aaa.] } +@objc public class Footnotes { +// CHECK: {{.*}}DocCommentAsXML=none + /// Has some footnotes. + /// + /// Footnotes aren't handled by swiftMarkup yet[^footnote], but they may in the future. + /// + /// [^footnote]: Footnotes aren't parsed by default in swift-cmark, and swiftMarkup doesn't + /// enable the feature. + @objc public func f0() {} + // CHECK: {{.*}}DocCommentAsXML=[f0()c:@M@comment_to_xml@objc(cs)Footnotes(im)f0@objc public func f0()Has some footnotes.Footnotes aren’t handled by swiftMarkup yet[^footnote], but they may in the future.[^footnote]: Footnotes aren’t parsed by default in swift-cmark, and swiftMarkup doesn’t enable the feature.] +} + @objc public class HasThrowingFunction { // CHECK: {{.*}}DocCommentAsXML=none diff --git a/test/PrintAsObjC/Inputs/comments-expected-output.h b/test/PrintAsObjC/Inputs/comments-expected-output.h index 2a23d5ed3d579..f25746a6164b7 100644 --- a/test/PrintAsObjC/Inputs/comments-expected-output.h +++ b/test/PrintAsObjC/Inputs/comments-expected-output.h @@ -32,6 +32,14 @@ SWIFT_CLASS("_TtC8comments10ATXHeaders") @end +SWIFT_CLASS("_TtC8comments10Attributes") +@interface Attributes +/// Here is an attribute: +/// Attribute text +- (void)f0; +@end + + SWIFT_CLASS("_TtC8comments13AutomaticLink") @interface AutomaticLink /// And now for a URL. @@ -185,6 +193,16 @@ SWIFT_CLASS("_TtC8comments13EmptyComments") @end +SWIFT_CLASS("_TtC8comments9Footnotes") +@interface Footnotes +/// Has some footnotes. +/// Footnotes aren’t handled by swiftMarkup yet[^footnote], but they may in the future. +/// [^footnote]: Footnotes aren’t parsed by default in swift-cmark, and swiftMarkup doesn’t +/// enable the feature. +- (void)f0; +@end + + SWIFT_CLASS("_TtC8comments19HasThrowingFunction") @interface HasThrowingFunction /// Might throw something. From 0639a9e2fbc991e5ec030a2dd18c1807d4c68e2e Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Thu, 21 Jul 2022 17:22:41 -0600 Subject: [PATCH 3/4] review: rename Attribute to InlineAttributes --- include/swift/Markup/AST.h | 12 ++++++++---- include/swift/Markup/ASTNodes.def | 4 ++-- lib/Frontend/PrintingDiagnosticConsumer.cpp | 2 +- lib/IDE/CommentConversion.cpp | 4 ++-- lib/Markup/AST.cpp | 12 ++++++------ lib/Markup/Markup.cpp | 4 ++-- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/include/swift/Markup/AST.h b/include/swift/Markup/AST.h index 7c375c8d55fbb..98fd98cf5b8dd 100644 --- a/include/swift/Markup/AST.h +++ b/include/swift/Markup/AST.h @@ -589,16 +589,20 @@ class Image final : public InlineContent, } }; -class Attribute final : public InlineContent, private llvm::TrailingObjects { +class InlineAttributes final : public InlineContent, private llvm::TrailingObjects { friend TrailingObjects; + // Note that inline attributes are like links, in that there are child inline nodes that are + // collectively styled by the attribute text. The child nodes are the text that should be + // displayed. + size_t NumChildren; StringRef Attributes; - Attribute(StringRef Attributes, ArrayRef Children); + InlineAttributes(StringRef Attributes, ArrayRef Children); public: - static Attribute *create(MarkupContext &MC, StringRef Attributes, ArrayRef Children); + static InlineAttributes *create(MarkupContext &MC, StringRef Attributes, ArrayRef Children); StringRef getAttributes() const { return Attributes; } @@ -611,7 +615,7 @@ class Attribute final : public InlineContent, private llvm::TrailingObjectsgetKind() == ASTNodeKind::Attribute; + return N->getKind() == ASTNodeKind::InlineAttributes; } }; diff --git a/include/swift/Markup/ASTNodes.def b/include/swift/Markup/ASTNodes.def index 27a5a31bbf93c..0704e71116b14 100644 --- a/include/swift/Markup/ASTNodes.def +++ b/include/swift/Markup/ASTNodes.def @@ -40,8 +40,8 @@ ABSTRACT_MARKUP_AST_NODE(InlineContent, MarkupASTNode) MARKUP_AST_NODE(Strong, InlineContent) MARKUP_AST_NODE(Link, InlineContent) MARKUP_AST_NODE(Image, InlineContent) - MARKUP_AST_NODE(Attribute, InlineContent) -MARKUP_AST_NODE_RANGE(Inline, Text, Attribute) + MARKUP_AST_NODE(InlineAttributes, InlineContent) +MARKUP_AST_NODE_RANGE(Inline, Text, InlineAttributes) /// Private Markdown Extensions - these should not be implemented in the /// underlying cmark parser. diff --git a/lib/Frontend/PrintingDiagnosticConsumer.cpp b/lib/Frontend/PrintingDiagnosticConsumer.cpp index 981365a1a4ea2..7e3673415b01a 100644 --- a/lib/Frontend/PrintingDiagnosticConsumer.cpp +++ b/lib/Frontend/PrintingDiagnosticConsumer.cpp @@ -142,7 +142,7 @@ namespace { } } - void visitAttribute(const Attribute *A) { + void visitInlineAttributes(const InlineAttributes *A) { print("^["); for (const auto *Child : A->getChildren()) visit(Child); diff --git a/lib/IDE/CommentConversion.cpp b/lib/IDE/CommentConversion.cpp index c78d376857a1b..2d89a27815aa1 100644 --- a/lib/IDE/CommentConversion.cpp +++ b/lib/IDE/CommentConversion.cpp @@ -72,7 +72,7 @@ struct CommentToXMLConverter { llvm_unreachable("Can't print a swift::markup::Document as XML directly"); } - void printAttribute(const Attribute *A) { + void printInlineAttributes(const InlineAttributes *A) { OS << "getAttributes()); OS << "\">"; @@ -650,7 +650,7 @@ class DoxygenConverter : public MarkupASTVisitor { visit(Child); } - void visitAttribute(const Attribute *A) { + void visitInlineAttributes(const InlineAttributes *A) { // attributed strings don't have an analogue in Doxygen, so just print out the text for (const auto *Child : A->getChildren()) visit(Child); diff --git a/lib/Markup/AST.cpp b/lib/Markup/AST.cpp index 7afe628ef1a47..68dc44cdd6703 100644 --- a/lib/Markup/AST.cpp +++ b/lib/Markup/AST.cpp @@ -157,14 +157,14 @@ Paragraph *Paragraph::create(MarkupContext &MC, return new (Mem) Paragraph(Children); } -Attribute::Attribute(StringRef Attributes, ArrayRef Children) : InlineContent(ASTNodeKind::Attribute), NumChildren(Children.size()), Attributes(Attributes) { +InlineAttributes::InlineAttributes(StringRef Attributes, ArrayRef Children) : InlineContent(ASTNodeKind::InlineAttributes), NumChildren(Children.size()), Attributes(Attributes) { std::uninitialized_copy(Children.begin(), Children.end(), getTrailingObjects()); } -Attribute *Attribute::create(MarkupContext &MC, StringRef Attributes, ArrayRef Children) { - void *Mem = MC.allocate(totalSizeToAlloc(Children.size()), alignof(Attribute)); +InlineAttributes *InlineAttributes::create(MarkupContext &MC, StringRef Attributes, ArrayRef Children) { + void *Mem = MC.allocate(totalSizeToAlloc(Children.size()), alignof(InlineAttributes)); StringRef AttrsCopy = MC.allocateCopy(Attributes); - return new (Mem) Attribute(AttrsCopy, Children); + return new (Mem) InlineAttributes(AttrsCopy, Children); } HRule *HRule::create(MarkupContext &MC) { @@ -418,8 +418,8 @@ void swift::markup::dump(const MarkupASTNode *Node, llvm::raw_ostream &OS, dumpChildren(Node->getChildren(), OS, indent + 1); break; } - case swift::markup::ASTNodeKind::Attribute: { - auto A = cast(Node); + case swift::markup::ASTNodeKind::InlineAttributes: { + auto A = cast(Node); OS << "Attribute:"; OS << " Attributes=" << A->getAttributes(); OS << " Children=" << Node->getChildren().size(); diff --git a/lib/Markup/Markup.cpp b/lib/Markup/Markup.cpp index b9494dfd22570..0f007c15000c6 100644 --- a/lib/Markup/Markup.cpp +++ b/lib/Markup/Markup.cpp @@ -216,13 +216,13 @@ ParseResult parseLink(MarkupContext &MC, ParseState State) { return { Link::create(MC, Destination, Children), ResultState.next() }; } -ParseResult parseAttribute(MarkupContext &MC, ParseState State) { +ParseResult parseAttribute(MarkupContext &MC, ParseState State) { assert(cmark_node_get_type(State.Node) == CMARK_NODE_ATTRIBUTE && State.Event == CMARK_EVENT_ENTER); std::string Attributes(cmark_node_get_attributes(State.Node)); SmallVector Children; auto ResultState = parseChildren(MC, State, Children); assert(State.Node == ResultState.Node && ResultState.Event == CMARK_EVENT_EXIT); - return { Attribute::create(MC, Attributes, Children), ResultState.next() }; + return { InlineAttributes::create(MC, Attributes, Children), ResultState.next() }; } ParseResult parseList(MarkupContext &MC, ParseState State) { From fe14235116d667b0b6bae13981ec9c43b3dcf190 Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Fri, 22 Jul 2022 10:50:17 -0600 Subject: [PATCH 4/4] review: rename Attribute to InlineAttributes in MarkupXML --- bindings/xml/comment-xml-schema.rng | 2 +- lib/IDE/CommentConversion.cpp | 4 ++-- test/Inputs/comment_to_something_conversion.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/xml/comment-xml-schema.rng b/bindings/xml/comment-xml-schema.rng index 22af2efe614e6..df05cfc92d25d 100644 --- a/bindings/xml/comment-xml-schema.rng +++ b/bindings/xml/comment-xml-schema.rng @@ -959,7 +959,7 @@ .*\S.* - + diff --git a/lib/IDE/CommentConversion.cpp b/lib/IDE/CommentConversion.cpp index 2d89a27815aa1..db7a501bef18d 100644 --- a/lib/IDE/CommentConversion.cpp +++ b/lib/IDE/CommentConversion.cpp @@ -73,7 +73,7 @@ struct CommentToXMLConverter { } void printInlineAttributes(const InlineAttributes *A) { - OS << "getAttributes()); OS << "\">"; @@ -81,7 +81,7 @@ struct CommentToXMLConverter { printASTNode(N); } - OS << ""; + OS << ""; } void printBlockQuote(const BlockQuote *BQ) { diff --git a/test/Inputs/comment_to_something_conversion.swift b/test/Inputs/comment_to_something_conversion.swift index 52b6396686ec1..71bd6859238a1 100644 --- a/test/Inputs/comment_to_something_conversion.swift +++ b/test/Inputs/comment_to_something_conversion.swift @@ -68,7 +68,7 @@ public enum A012_AttachToEntities { /// /// ^[Attribute text](string: "attributed") @objc public func f0() {} - // CHECK: {{.*}}DocCommentAsXML=[f0()c:@M@comment_to_xml@objc(cs)Attributes(im)f0@objc public func f0()Here is an attribute:Attribute text] + // CHECK: {{.*}}DocCommentAsXML=[f0()c:@M@comment_to_xml@objc(cs)Attributes(im)f0@objc public func f0()Here is an attribute:Attribute text] } @objc public class AutomaticLink {