Skip to content

Commit d3cc09f

Browse files
committed
Cascade doc comments from protocol requirements to extensions
This allows authors to not have to copy and paste doc comments from requirements to default implementations to get documentation. For now, this will only be active if a protocol doesn't inherit two equivalent requirements from other protocols with doc comments. Those will need special treatment and it would be better not to just copy and paste them into the same doc comment. More output structure is needed to differentiate documentation for different protocols' requirements. rdar://problem/16512247
1 parent 79bc7f9 commit d3cc09f

File tree

2 files changed

+116
-2
lines changed

2 files changed

+116
-2
lines changed

lib/AST/DocComment.cpp

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,47 @@ getAnyBaseClassDocComment(swift::markup::MarkupContext &MC,
392392
return None;
393393
}
394394

395+
static Optional<DocComment *>
396+
getProtocolRequirementDocComment(swift::markup::MarkupContext &MC,
397+
const ProtocolDecl *ProtoExt,
398+
const Decl *D) {
399+
400+
auto getSingleRequirementWithNonemptyDoc = [](const ProtocolDecl *P,
401+
const ValueDecl *VD)
402+
-> const ValueDecl * {
403+
SmallVector<ValueDecl *, 2> Members;
404+
P->lookupQualified(P->getType(), VD->getFullName(),
405+
NLOptions::NL_ProtocolMembers,
406+
/*resolver=*/nullptr, Members);
407+
SmallVector<const ValueDecl *, 1> ProtocolRequirements;
408+
for (auto Member : Members)
409+
if (!Member->isDefinition())
410+
ProtocolRequirements.push_back(Member);
411+
412+
if (ProtocolRequirements.size() == 1) {
413+
auto Requirement = ProtocolRequirements.front();
414+
if (!Requirement->getRawComment().isEmpty())
415+
return Requirement;
416+
}
417+
418+
return nullptr;
419+
};
420+
421+
if (const auto *VD = dyn_cast<ValueDecl>(D)) {
422+
SmallVector<const ValueDecl *, 4> RequirementsWithDocs;
423+
if (auto Requirement = getSingleRequirementWithNonemptyDoc(ProtoExt, VD))
424+
RequirementsWithDocs.push_back(Requirement);
425+
426+
for (auto Proto : ProtoExt->getInheritedProtocols(/*resolver=*/nullptr))
427+
if (auto Requirement = getSingleRequirementWithNonemptyDoc(Proto, VD))
428+
RequirementsWithDocs.push_back(Requirement);
429+
430+
if (RequirementsWithDocs.size() == 1)
431+
return getSingleDocComment(MC, RequirementsWithDocs.front());
432+
}
433+
return None;
434+
}
435+
395436
Optional<DocComment *>
396437
swift::getCascadingDocComment(swift::markup::MarkupContext &MC, const Decl *D) {
397438
auto Doc = getSingleDocComment(MC, D);
@@ -404,8 +445,9 @@ swift::getCascadingDocComment(swift::markup::MarkupContext &MC, const Decl *D) {
404445
if (auto BaseClassDoc = getAnyBaseClassDocComment(MC, CD, D))
405446
return BaseClassDoc;
406447

407-
// FIXME: Look at protocol requirement declarations if a protocol
408-
// extension implementation doesn't have a doc comment.
448+
if (const auto *PE = D->getDeclContext()->getAsProtocolExtensionContext())
449+
if (auto ReqDoc = getProtocolRequirementDocComment(MC, PE, D))
450+
return ReqDoc;
409451

410452
return None;
411453
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// RUN: %target-swift-ide-test -print-comments -source-filename %s | FileCheck %s
2+
3+
protocol ParentProtocol1 {
4+
/// ParentProtocol1.onlyParent1()
5+
func onlyParent1()
6+
// CHECK: Func/ParentProtocol1.onlyParent1 {{.*}} DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>onlyParent1()</Name><USR>s:FP14swift_ide_test15ParentProtocol111onlyParent1FT_T_</USR><Declaration>func onlyParent1()</Declaration><Abstract><Para>ParentProtocol1.onlyParent1()</Para></Abstract></Function>]
7+
8+
/// ParentProtocol1.commonParentRequirement()
9+
func commonParentRequirement()
10+
// CHECKL: Func/ParentProtocol1.commonParentRequirement {{.*}} DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>commonParentRequirement()</Name><USR>s:FP14swift_ide_test15ParentProtocol123commonParentRequirementFT_T_</USR><Declaration>func commonParentRequirement()</Declaration><Abstract><Para>ParentProtocol1.commonParentRequirement()</Para></Abstract></Function>]
11+
12+
/// ParentProtocol1.commonRequirementWithDocComment()
13+
func commonRequirementWithDocComment()
14+
// CHECK: Func/ParentProtocol1.commonRequirementWithDocComment {{.*}} DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>commonRequirementWithDocComment()</Name><USR>s:FP14swift_ide_test15ParentProtocol131commonRequirementWithDocCommentFT_T_</USR><Declaration>func commonRequirementWithDocComment()</Declaration><Abstract><Para>ParentProtocol1.commonRequirementWithDocComment()</Para></Abstract></Function>]
15+
16+
/// ParentProtocol1.commonRequirementWithoutDocComment()
17+
func commonRequirementWithoutDocComment()
18+
// CHECK: Func/ParentProtocol1.commonRequirementWithoutDocComment {{.*}} DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>commonRequirementWithoutDocComment()</Name><USR>s:FP14swift_ide_test15ParentProtocol134commonRequirementWithoutDocCommentFT_T_</USR><Declaration>func commonRequirementWithoutDocComment()</Declaration><Abstract><Para>ParentProtocol1.commonRequirementWithoutDocComment()</Para></Abstract></Function>]
19+
}
20+
21+
protocol ParentProtocol2 {
22+
/// ParentProtocol2.onlyParent2()
23+
func onlyParent2()
24+
// CHECK: Func/ParentProtocol2.onlyParent2 {{.*}} DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>onlyParent2()</Name><USR>s:FP14swift_ide_test15ParentProtocol211onlyParent2FT_T_</USR><Declaration>func onlyParent2()</Declaration><Abstract><Para>ParentProtocol2.onlyParent2()</Para></Abstract></Function>]
25+
26+
/// ParentProtocol2.commonParentRequirement()
27+
func commonParentRequirement()
28+
// CHECK: Func/ParentProtocol2.commonParentRequirement {{.*}} DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>commonParentRequirement()</Name><USR>s:FP14swift_ide_test15ParentProtocol223commonParentRequirementFT_T_</USR><Declaration>func commonParentRequirement()</Declaration><Abstract><Para>ParentProtocol2.commonParentRequirement()</Para></Abstract></Function>]
29+
30+
/// ParentProtocol2.commonRequirementWithDocComment()
31+
func commonRequirementWithDocComment()
32+
// CHECK: Func/ParentProtocol2.commonRequirementWithDocComment {{.*}} DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>commonRequirementWithDocComment()</Name><USR>s:FP14swift_ide_test15ParentProtocol231commonRequirementWithDocCommentFT_T_</USR><Declaration>func commonRequirementWithDocComment()</Declaration><Abstract><Para>ParentProtocol2.commonRequirementWithDocComment()</Para></Abstract></Function>]
33+
34+
/// ParentProtocol2.commonRequirementWithoutDocComment()
35+
func commonRequirementWithoutDocComment()
36+
// CHECK: Func/ParentProtocol2.commonRequirementWithoutDocComment {{.*}} DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>commonRequirementWithoutDocComment()</Name><USR>s:FP14swift_ide_test15ParentProtocol234commonRequirementWithoutDocCommentFT_T_</USR><Declaration>func commonRequirementWithoutDocComment()</Declaration><Abstract><Para>ParentProtocol2.commonRequirementWithoutDocComment()</Para></Abstract></Function>]
37+
}
38+
39+
protocol ChildProtocol : ParentProtocol1, ParentProtocol2 {
40+
/// ChildProtocol.commonRequirementWithDocComment()
41+
func commonRequirementWithDocComment()
42+
// CHECK: Func/ChildProtocol.commonRequirementWithDocComment {{.*}} DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>commonRequirementWithDocComment()</Name><USR>s:FP14swift_ide_test13ChildProtocol31commonRequirementWithDocCommentFT_T_</USR><Declaration>func commonRequirementWithDocComment()</Declaration><Abstract><Para>ChildProtocol.commonRequirementWithDocComment()</Para></Abstract></Function>]
43+
44+
// This should show nothing because there are two inherited requirements.
45+
func commonRequirementWithoutDocComment()
46+
// CHECK: Func/ChildProtocol.commonRequirementWithoutDocComment {{.*}} DocCommentAsXML=none
47+
}
48+
49+
// Test that ChildProtocol's default implementation for requirements
50+
// come from the right place.
51+
extension ChildProtocol {
52+
// Should come from ParentProtocol1.
53+
func onlyParent1() {}
54+
// CHECK: Func/onlyParent1 {{.*}} DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>onlyParent1()</Name><USR>s:FP14swift_ide_test15ParentProtocol111onlyParent1FT_T_</USR><Declaration>func onlyParent1()</Declaration><Abstract><Para>ParentProtocol1.onlyParent1()</Para></Abstract></Function>]
55+
56+
// Should come from ParentProtocol2.
57+
func onlyParent2() {}
58+
// CHECK: Func/onlyParent2 {{.*}} DocCommentAsXML=[<Function file="{{.*}}" line="{{.*}}" column="{{.*}}"><Name>onlyParent2()</Name><USR>s:FP14swift_ide_test15ParentProtocol211onlyParent2FT_T_</USR><Declaration>func onlyParent2()</Declaration><Abstract><Para>ParentProtocol2.onlyParent2()</Para></Abstract></Function>]
59+
60+
// Should show nothing because the requirement is in both parents.
61+
func commonParentRequirement() {}
62+
// CHECK: Func/commonParentRequirement {{.*}} DocCommentAsXML=none
63+
64+
// Should show nothing because the requirement is in both parents.
65+
func commonRequirementWithDocComment() {}
66+
// CHECK: Func/commonRequirementWithDocComment {{.*}} DocCommentAsXML=none
67+
68+
// Should show nothing because there are multiple requirements.
69+
func commonRequirementWithoutDocComment() {}
70+
// CHECK: Func/commonRequirementWithoutDocComment {{.*}} DocCommentAsXML=none
71+
}
72+

0 commit comments

Comments
 (0)