Skip to content

Commit

Permalink
Nesting parameter/returns/throws doc comments for closure parameters
Browse files Browse the repository at this point in the history
Under parameter doc comment list items, allow function doc comment
syntax to nest so you can document the meaning of closure parameters'
signatures.

rdar://problem/24794725
  • Loading branch information
bitjammer committed Apr 10, 2016
1 parent a9297ee commit c20f3db
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 63 deletions.
25 changes: 22 additions & 3 deletions bindings/xml/comment-xml-schema.rng
Original file line number Diff line number Diff line change
Expand Up @@ -650,9 +650,28 @@
<!-- In general, template parameters with whitespace discussion
should not be emitted, unless direction is explicitly specified.
Schema might be more strict here. -->
<element name="Discussion">
<ref name="BlockContent" />
</element>
<choice>
<element name="ClosureParameter">
<optional>
<ref name="Abstract" />
</optional>
<optional>
<ref name="Parameters" />
</optional>
<optional>
<ref name="ResultDiscussion" />
</optional>
<optional>
<ref name="ThrowsDiscussion" />
</optional>
<optional>
<ref name="Discussion" />
</optional>
</element>
<element name="Discussion">
<ref name="BlockContent" />
</element>
</choice>
</element>
</oneOrMore>
</element>
Expand Down
20 changes: 3 additions & 17 deletions include/swift/AST/Comment.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,20 @@ class DocComment;
struct RawComment;

class DocComment {
public:
struct CommentParts {
Optional<const swift::markup::Paragraph *>Brief;
ArrayRef<const swift::markup::MarkupASTNode *> BodyNodes;
ArrayRef<const swift::markup::ParamField *> ParamFields;
Optional<const swift::markup::ReturnsField *> ReturnsField;
Optional<const swift::markup::ThrowsField *> ThrowsField;

bool isEmpty() const {
return !Brief.hasValue() && !ReturnsField.hasValue() && !ThrowsField.hasValue() && BodyNodes.empty() && ParamFields.empty();
}
};

private:
const Decl *D;
const swift::markup::Document *Doc = nullptr;
const CommentParts Parts;
const swift::markup::CommentParts Parts;

public:
DocComment(const Decl *D, swift::markup::Document *Doc,
CommentParts Parts)
swift::markup::CommentParts Parts)
: D(D), Doc(Doc), Parts(Parts) {}

const Decl *getDecl() const { return D; }

const swift::markup::Document *getDocument() const { return Doc; }

CommentParts getParts() const {
swift::markup::CommentParts getParts() const {
return Parts;
}

Expand Down
48 changes: 48 additions & 0 deletions include/swift/Markup/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,35 @@ namespace swift {
namespace markup {

class MarkupContext;
class MarkupASTNode;
class Paragraph;
class ParamField;
class ReturnsField;
class ThrowsField;

/// The basic structure of a doc comment attached to a Swift
/// declaration.
struct CommentParts {
Optional<const Paragraph *> Brief;
ArrayRef<const MarkupASTNode *> BodyNodes;
ArrayRef<ParamField *> ParamFields;
Optional<const ReturnsField *> ReturnsField;
Optional<const ThrowsField *> ThrowsField;

bool isEmpty() const {
return !Brief.hasValue() &&
!ReturnsField.hasValue() &&
!ThrowsField.hasValue() &&
BodyNodes.empty() &&
ParamFields.empty();
}

bool hasFunctionDocumentation() const {
return !ParamFields.empty() ||
ReturnsField.hasValue() ||
ThrowsField.hasValue();
}
};

#define MARKUP_AST_NODE(Id, Parent) class Id;
#define ABSTRACT_MARKUP_AST_NODE(Id, Parent) class Id;
Expand Down Expand Up @@ -585,6 +614,10 @@ class ParamField final : public PrivateExtension,

StringRef Name;

// Parameter fields can contain a substructure describing a
// function or closure parameter.
llvm::Optional<CommentParts> Parts;

ParamField(StringRef Name, ArrayRef<MarkupASTNode *> Children);

public:
Expand All @@ -596,6 +629,21 @@ class ParamField final : public PrivateExtension,
return Name;
}

llvm::Optional<CommentParts> getParts() const {
return Parts;
}

void setParts(CommentParts P) {
Parts = P;
}

bool isClosureParameter() const {
if (!Parts.hasValue())
return false;

return Parts.getValue().hasFunctionDocumentation();
}

ArrayRef<MarkupASTNode *> getChildren() {
return {getTrailingObjects<MarkupASTNode *>(), NumChildren};
}
Expand Down
17 changes: 11 additions & 6 deletions lib/AST/DocComment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Optional<swift::markup::ParamField *> extractParamOutlineItem(

bool extractParameterOutline(
swift::markup::MarkupContext &MC, swift::markup::List *L,
SmallVectorImpl<const swift::markup::ParamField *> &ParamFields) {
SmallVectorImpl<swift::markup::ParamField *> &ParamFields) {
SmallVector<swift::markup::MarkupASTNode *, 8> NormalItems;
auto Children = L->getChildren();
if (Children.empty())
Expand Down Expand Up @@ -145,7 +145,7 @@ bool extractParameterOutline(

bool extractSeparatedParams(
swift::markup::MarkupContext &MC, swift::markup::List *L,
SmallVectorImpl<const swift::markup::ParamField *> &ParamFields) {
SmallVectorImpl<swift::markup::ParamField *> &ParamFields) {
SmallVector<swift::markup::MarkupASTNode *, 8> NormalItems;
auto Children = L->getChildren();

Expand Down Expand Up @@ -209,7 +209,7 @@ bool extractSeparatedParams(

bool extractSimpleField(
swift::markup::MarkupContext &MC, swift::markup::List *L,
DocComment::CommentParts &Parts,
swift::markup::CommentParts &Parts,
SmallVectorImpl<const swift::markup::MarkupASTNode *> &BodyNodes) {
auto Children = L->getChildren();
SmallVector<swift::markup::MarkupASTNode *, 8> NormalItems;
Expand Down Expand Up @@ -274,11 +274,11 @@ bool extractSimpleField(
return NormalItems.size() == 0;
}

static DocComment::CommentParts
static swift::markup::CommentParts
extractCommentParts(swift::markup::MarkupContext &MC,
swift::markup::MarkupASTNode *Node) {

DocComment::CommentParts Parts;
swift::markup::CommentParts Parts;
auto Children = Node->getChildren();
if (Children.empty())
return Parts;
Expand All @@ -289,7 +289,7 @@ extractCommentParts(swift::markup::MarkupContext &MC,
Parts.Brief = FirstParagraph;

SmallVector<const swift::markup::MarkupASTNode *, 4> BodyNodes;
SmallVector<const swift::markup::ParamField *, 8> ParamFields;
SmallVector<swift::markup::ParamField *, 8> ParamFields;

// Look for special top-level lists
size_t StartOffset = FirstParagraph == nullptr ? 0 : 1;
Expand Down Expand Up @@ -317,6 +317,11 @@ extractCommentParts(swift::markup::MarkupContext &MC,
Parts.BodyNodes = MC.allocateCopy(llvm::makeArrayRef(BodyNodes));
Parts.ParamFields = MC.allocateCopy(llvm::makeArrayRef(ParamFields));

for (auto Param : Parts.ParamFields) {
auto ParamParts = extractCommentParts(MC, Param);
Param->setParts(ParamParts);
}

return Parts;
}

Expand Down
85 changes: 49 additions & 36 deletions lib/IDE/CommentConversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,24 @@ struct CommentToXMLConverter {
}

void printParamField(const ParamField *PF) {
OS << "<Parameter><Name>";
OS << "<Parameter>";
OS << "<Name>";
OS << PF->getName();
OS << "</Name><Direction isExplicit=\"0\">in</Direction><Discussion>";
for (auto Child : PF->getChildren())
printASTNode(Child);

OS << "</Discussion></Parameter>";
OS << "</Name>";
OS << "<Direction isExplicit=\"0\">in</Direction>";

if (PF->isClosureParameter()) {
OS << "<ClosureParameter>";
visitCommentParts(PF->getParts().getValue());
OS << "</ClosureParameter>";
} else {
OS << "<Discussion>";
for (auto Child : PF->getChildren()) {
printASTNode(Child);
}
OS << "</Discussion>";
}
OS << "</Parameter>";
}

void printResultDiscussion(const ReturnsField *RF) {
Expand All @@ -235,9 +246,40 @@ struct CommentToXMLConverter {
}

void visitDocComment(const DocComment *DC);
void visitCommentParts(const swift::markup::CommentParts &Parts);
};
} // unnamed namespace

void CommentToXMLConverter::visitCommentParts(const swift::markup::CommentParts &Parts) {
if (Parts.Brief.hasValue()) {
OS << "<Abstract>";
printASTNode(Parts.Brief.getValue());
OS << "</Abstract>";
}

if (!Parts.ParamFields.empty()) {
OS << "<Parameters>";
for (const auto *PF : Parts.ParamFields)
printParamField(PF);

OS << "</Parameters>";
}

if (Parts.ReturnsField.hasValue())
printResultDiscussion(Parts.ReturnsField.getValue());

if (Parts.ThrowsField.hasValue())
printThrowsDiscussion(Parts.ThrowsField.getValue());

if (!Parts.BodyNodes.empty()) {
OS << "<Discussion>";
for (const auto *N : Parts.BodyNodes)
printASTNode(N);

OS << "</Discussion>";
}
}

void CommentToXMLConverter::visitDocComment(const DocComment *DC) {
const Decl *D = DC->getDecl();

Expand Down Expand Up @@ -313,36 +355,7 @@ void CommentToXMLConverter::visitDocComment(const DocComment *DC) {
OS << "</Declaration>";
}

auto Brief = DC->getBrief();
if (Brief.hasValue()) {
OS << "<Abstract>";
printASTNode(Brief.getValue());
OS << "</Abstract>";
}

if (!DC->getParamFields().empty()) {
OS << "<Parameters>";
for (const auto *PF : DC->getParamFields())
printParamField(PF);

OS << "</Parameters>";
}

auto RF = DC->getReturnsField();
if (RF.hasValue())
printResultDiscussion(RF.getValue());

auto TF = DC->getThrowsField();
if (TF.hasValue())
printThrowsDiscussion(TF.getValue());

if (!DC->getBodyNodes().empty()) {
OS << "<Discussion>";
for (const auto *N : DC->getBodyNodes())
printASTNode(N);

OS << "</Discussion>";
}
visitCommentParts(DC->getParts());

OS << RootEndTag;
}
Expand Down
3 changes: 2 additions & 1 deletion lib/Markup/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ Strong *Strong::create(MarkupContext &MC,

ParamField::ParamField(StringRef Name, ArrayRef<MarkupASTNode *> Children)
: PrivateExtension(ASTNodeKind::ParamField), NumChildren(Children.size()),
Name(Name) {
Name(Name),
Parts(None) {
std::uninitialized_copy(Children.begin(), Children.end(),
getTrailingObjects<MarkupASTNode *>());
}
Expand Down
36 changes: 36 additions & 0 deletions test/Inputs/comment_to_something_conversion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,39 @@ public func codeListingWithDefaultLanguage() {}
/// ```
public func codeListingWithOtherLanguage() {}
// CHECK: DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>codeListingWithOtherLanguage()</Name><USR>s:F14swift_ide_test28codeListingWithOtherLanguageFT_T_</USR><Declaration>public func codeListingWithOtherLanguage()</Declaration><Abstract><Para>Brief.</Para></Abstract><Discussion><CodeListing language="c++"><zCodeLineNumbered><![CDATA[Something::Something::create();]]></zCodeLineNumbered><zCodeLineNumbered></zCodeLineNumbered></CodeListing></Discussion></Function>]

/// Partially applies a binary operator.
///
/// - Parameter a: The left-hand side to partially apply.
/// - Parameter combine: A binary operator.
/// - Parameter lhs: The left-hand side of the operator
/// - Parameter rhs: The right-hand side of the operator
/// - Returns: A result.
/// - Throws: Nothing.
public func closureParameterExplodedExploded<T>(a: T, combine: (lhs: T, rhs: T) -> T) {}
// CHECK: DocCommentAsXML=[<Function file="{{.*}} line="{{.*}}" column="{{.*}}"><Name>closureParameterExplodedExploded(a:combine:)</Name><USR>s:F14swift_ide_test32closureParameterExplodedExplodedurFT1ax7combineFT3lhsx3rhsx_x_T_</USR><Declaration>public func closureParameterExplodedExploded&lt;T&gt;(a: T, combine: (lhs: T, rhs: T) -&gt; T)</Declaration><Abstract><Para>Partially applies a binary operator.</Para></Abstract><Parameters><Parameter><Name>a</Name><Direction isExplicit="0">in</Direction><Discussion><Para>The left-hand side to partially apply.</Para></Discussion></Parameter><Parameter><Name>combine</Name><Direction isExplicit="0">in</Direction><ClosureParameter><Abstract><Para>A binary operator.</Para></Abstract><Parameters><Parameter><Name>lhs</Name><Direction isExplicit="0">in</Direction><Discussion><Para>The left-hand side of the operator</Para></Discussion></Parameter><Parameter><Name>rhs</Name><Direction isExplicit="0">in</Direction><Discussion><Para>The right-hand side of the operator</Para></Discussion></Parameter></Parameters><ResultDiscussion><Para>A result.</Para></ResultDiscussion><ThrowsDiscussion><Para>Nothing.</Para></ThrowsDiscussion></ClosureParameter></Parameter></Parameters></Function>] CommentXMLValid

/// Partially applies a binary operator.
///
/// - Parameters:
/// - a: The left-hand side to partially apply.
/// - combine: A binary operator.
/// - Parameter lhs: The left-hand side of the operator
/// - Parameter rhs: The right-hand side of the operator
/// - Returns: A result.
/// - Throws: Nothing.
public func closureParameterOutlineExploded<T>(a: T, combine: (lhs: T, rhs: T) -> T) {}
// CHECK: DocCommentAsXML=[<Function file="{{.*}} line="{{.*}}" column="{{.*}}"><Name>closureParameterOutlineExploded(a:combine:)</Name><USR>s:F14swift_ide_test31closureParameterOutlineExplodedurFT1ax7combineFT3lhsx3rhsx_x_T_</USR><Declaration>public func closureParameterOutlineExploded&lt;T&gt;(a: T, combine: (lhs: T, rhs: T) -&gt; T)</Declaration><Abstract><Para>Partially applies a binary operator.</Para></Abstract><Parameters><Parameter><Name>a</Name><Direction isExplicit="0">in</Direction><Discussion><Para>The left-hand side to partially apply.</Para></Discussion></Parameter><Parameter><Name>combine</Name><Direction isExplicit="0">in</Direction><ClosureParameter><Abstract><Para>A binary operator.</Para></Abstract><Parameters><Parameter><Name>lhs</Name><Direction isExplicit="0">in</Direction><Discussion><Para>The left-hand side of the operator</Para></Discussion></Parameter><Parameter><Name>rhs</Name><Direction isExplicit="0">in</Direction><Discussion><Para>The right-hand side of the operator</Para></Discussion></Parameter></Parameters><ResultDiscussion><Para>A result.</Para></ResultDiscussion><ThrowsDiscussion><Para>Nothing.</Para></ThrowsDiscussion></ClosureParameter></Parameter></Parameters></Function>] CommentXMLValid

/// Partially applies a binary operator.
///
/// - Parameters:
/// - a: The left-hand side to partially apply.
/// - combine: A binary operator.
/// - Parameters:
/// - lhs: The left-hand side of the operator
/// - rhs: The right-hand side of the operator
/// - Returns: A result.
/// - Throws: Nothing.
public func closureParameterOutlineOutline<T>(a: T, combine: (lhs: T, rhs: T) -> T) {}
// CHECK: {{.*}}DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>closureParameterOutlineOutline(a:combine:)</Name><USR>s:F14swift_ide_test30closureParameterOutlineOutlineurFT1ax7combineFT3lhsx3rhsx_x_T_</USR><Declaration>public func closureParameterOutlineOutline&lt;T&gt;(a: T, combine: (lhs: T, rhs: T) -&gt; T)</Declaration><Abstract><Para>Partially applies a binary operator.</Para></Abstract><Parameters><Parameter><Name>a</Name><Direction isExplicit="0">in</Direction><Discussion><Para>The left-hand side to partially apply.</Para></Discussion></Parameter><Parameter><Name>combine</Name><Direction isExplicit="0">in</Direction><ClosureParameter><Abstract><Para>A binary operator.</Para></Abstract><Parameters><Parameter><Name>lhs</Name><Direction isExplicit="0">in</Direction><Discussion><Para>The left-hand side of the operator</Para></Discussion></Parameter><Parameter><Name>rhs</Name><Direction isExplicit="0">in</Direction><Discussion><Para>The right-hand side of the operator</Para></Discussion></Parameter></Parameters><ResultDiscussion><Para>A result.</Para></ResultDiscussion><ThrowsDiscussion><Para>Nothing.</Para></ThrowsDiscussion></ClosureParameter></Parameter></Parameters></Function>]

3 comments on commit c20f3db

@erica
Copy link
Contributor

@erica erica commented on c20f3db Jul 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still isn't working for me. Any idea when this is going to be in a snapshot?

@bitjammer
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is in the development snapshot in terms of Swift/SourceKit but it's possible that Xcode isn't consuming it yet for QuickHelp and the like.

@erica
Copy link
Contributor

@erica erica commented on c20f3db Sep 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So....is it there now?

Please sign in to comment.