Skip to content

Cascade doc comments from protocol requirements to extensions #3301

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions lib/AST/DocComment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,47 @@ getAnyBaseClassDocComment(swift::markup::MarkupContext &MC,
return None;
}

static Optional<DocComment *>
getProtocolRequirementDocComment(swift::markup::MarkupContext &MC,
const ProtocolDecl *ProtoExt,
const Decl *D) {

auto getSingleRequirementWithNonemptyDoc = [](const ProtocolDecl *P,
const ValueDecl *VD)
-> const ValueDecl * {
SmallVector<ValueDecl *, 2> Members;
P->lookupQualified(P->getType(), VD->getFullName(),
NLOptions::NL_ProtocolMembers,
/*resolver=*/nullptr, Members);
SmallVector<const ValueDecl *, 1> ProtocolRequirements;
for (auto Member : Members)
if (!Member->isDefinition())
ProtocolRequirements.push_back(Member);

if (ProtocolRequirements.size() == 1) {
auto Requirement = ProtocolRequirements.front();
if (!Requirement->getRawComment().isEmpty())
return Requirement;
}

return nullptr;
};

if (const auto *VD = dyn_cast<ValueDecl>(D)) {
SmallVector<const ValueDecl *, 4> RequirementsWithDocs;
if (auto Requirement = getSingleRequirementWithNonemptyDoc(ProtoExt, VD))
RequirementsWithDocs.push_back(Requirement);

for (auto Proto : ProtoExt->getInheritedProtocols(/*resolver=*/nullptr))
if (auto Requirement = getSingleRequirementWithNonemptyDoc(Proto, VD))
RequirementsWithDocs.push_back(Requirement);

if (RequirementsWithDocs.size() == 1)
return getSingleDocComment(MC, RequirementsWithDocs.front());
}
return None;
}

Optional<DocComment *>
swift::getCascadingDocComment(swift::markup::MarkupContext &MC, const Decl *D) {
auto Doc = getSingleDocComment(MC, D);
Expand All @@ -404,8 +445,9 @@ swift::getCascadingDocComment(swift::markup::MarkupContext &MC, const Decl *D) {
if (auto BaseClassDoc = getAnyBaseClassDocComment(MC, CD, D))
return BaseClassDoc;

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

return None;
}
72 changes: 72 additions & 0 deletions test/IDE/comment_inherited_protocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// RUN: %target-swift-ide-test -print-comments -source-filename %s | FileCheck %s

protocol ParentProtocol1 {
/// ParentProtocol1.onlyParent1()
func onlyParent1()
// 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>]

/// ParentProtocol1.commonParentRequirement()
func commonParentRequirement()
// 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>]

/// ParentProtocol1.commonRequirementWithDocComment()
func commonRequirementWithDocComment()
// 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>]

/// ParentProtocol1.commonRequirementWithoutDocComment()
func commonRequirementWithoutDocComment()
// 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>]
}

protocol ParentProtocol2 {
/// ParentProtocol2.onlyParent2()
func onlyParent2()
// 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>]

/// ParentProtocol2.commonParentRequirement()
func commonParentRequirement()
// 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>]

/// ParentProtocol2.commonRequirementWithDocComment()
func commonRequirementWithDocComment()
// 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>]

/// ParentProtocol2.commonRequirementWithoutDocComment()
func commonRequirementWithoutDocComment()
// 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>]
}

protocol ChildProtocol : ParentProtocol1, ParentProtocol2 {
/// ChildProtocol.commonRequirementWithDocComment()
func commonRequirementWithDocComment()
// 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>]

// This should show nothing because there are two inherited requirements.
func commonRequirementWithoutDocComment()
// CHECK: Func/ChildProtocol.commonRequirementWithoutDocComment {{.*}} DocCommentAsXML=none
}

// Test that ChildProtocol's default implementation for requirements
// come from the right place.
extension ChildProtocol {
// Should come from ParentProtocol1.
func onlyParent1() {}
// 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>]

// Should come from ParentProtocol2.
func onlyParent2() {}
// 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>]

// Should show nothing because the requirement is in both parents.
func commonParentRequirement() {}
// CHECK: Func/commonParentRequirement {{.*}} DocCommentAsXML=none

// Should show nothing because the requirement is in both parents.
func commonRequirementWithDocComment() {}
// CHECK: Func/commonRequirementWithDocComment {{.*}} DocCommentAsXML=none

// Should show nothing because there are multiple requirements.
func commonRequirementWithoutDocComment() {}
// CHECK: Func/commonRequirementWithoutDocComment {{.*}} DocCommentAsXML=none
}