Skip to content

[IDE][SourceKit/DocSupport] Add members of underscored protocol extensions in extensions of conforming types. #32148

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
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
5 changes: 4 additions & 1 deletion include/swift/IDE/CommentConversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#define SWIFT_IDE_COMMENT_CONVERSION_H

#include "swift/Basic/LLVM.h"
#include "swift/AST/TypeOrExtensionDecl.h"
#include <memory>
#include <string>

Expand All @@ -27,7 +28,9 @@ namespace ide {
/// in Clang-like XML format.
///
/// \returns true if the declaration has a documentation comment.
bool getDocumentationCommentAsXML(const Decl *D, raw_ostream &OS);
bool getDocumentationCommentAsXML(
const Decl *D, raw_ostream &OS,
TypeOrExtensionDecl SynthesizedTarget = TypeOrExtensionDecl());

/// If the declaration has a documentation comment and a localization key,
/// print it into the given output stream and return true. Else, return false.
Expand Down
96 changes: 84 additions & 12 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,10 @@ class PrintAST : public ASTVisitor<PrintAST> {
TypeArrayView<GenericTypeParamType> genericParams,
ArrayRef<Requirement> requirements, unsigned flags,
llvm::function_ref<bool(const Requirement &)> filter);
void printSingleDepthOfGenericSignature(
TypeArrayView<GenericTypeParamType> genericParams,
ArrayRef<Requirement> requirements, bool &isFirstReq, unsigned flags,
llvm::function_ref<bool(const Requirement &)> filter);
void printRequirement(const Requirement &req);

private:
Expand Down Expand Up @@ -872,7 +876,12 @@ class PrintAST : public ASTVisitor<PrintAST> {
return false; // not needed for the parser library.
#endif

if (!shouldPrint(D, true))
bool Synthesize =
Options.TransformContext &&
Options.TransformContext->isPrintingSynthesizedExtension() &&
isa<ExtensionDecl>(D);

if (!shouldPrint(D, true) && !Synthesize)
return false;

Decl *Old = Current;
Expand All @@ -890,10 +899,6 @@ class PrintAST : public ASTVisitor<PrintAST> {

SWIFT_DEFER { CurrentType = OldType; };

bool Synthesize =
Options.TransformContext &&
Options.TransformContext->isPrintingSynthesizedExtension() &&
isa<ExtensionDecl>(D);
if (Synthesize) {
Printer.setSynthesizedTarget(Options.TransformContext->getDecl());
}
Expand Down Expand Up @@ -1456,6 +1461,15 @@ void PrintAST::printSingleDepthOfGenericSignature(
TypeArrayView<GenericTypeParamType> genericParams,
ArrayRef<Requirement> requirements, unsigned flags,
llvm::function_ref<bool(const Requirement &)> filter) {
bool isFirstReq = true;
printSingleDepthOfGenericSignature(genericParams, requirements, isFirstReq,
flags, filter);
}

void PrintAST::printSingleDepthOfGenericSignature(
TypeArrayView<GenericTypeParamType> genericParams,
ArrayRef<Requirement> requirements, bool &isFirstReq, unsigned flags,
llvm::function_ref<bool(const Requirement &)> filter) {
bool printParams = (flags & PrintParams);
bool printRequirements = (flags & PrintRequirements);
printRequirements &= Options.PrintGenericRequirements;
Expand Down Expand Up @@ -1502,7 +1516,6 @@ void PrintAST::printSingleDepthOfGenericSignature(
}

if (printRequirements || printInherited) {
bool isFirstReq = true;
for (const auto &req : requirements) {
if (!filter(req))
continue;
Expand Down Expand Up @@ -1564,9 +1577,6 @@ void PrintAST::printSingleDepthOfGenericSignature(
}
} else {
Printer.callPrintStructurePre(PrintStructureKind::GenericRequirement);

// We don't substitute type for the printed requirement so that the
// printed requirement agrees with separately reported generic parameters.
printRequirement(req);
Printer.printStructurePost(PrintStructureKind::GenericRequirement);
}
Expand All @@ -1578,7 +1588,7 @@ void PrintAST::printSingleDepthOfGenericSignature(
}

void PrintAST::printRequirement(const Requirement &req) {
printType(req.getFirstType());
printTransformedType(req.getFirstType());
switch (req.getKind()) {
case RequirementKind::Layout:
Printer << " : ";
Expand All @@ -1592,7 +1602,7 @@ void PrintAST::printRequirement(const Requirement &req) {
Printer << " == ";
break;
}
printType(req.getSecondType());
printTransformedType(req.getSecondType());
}

bool PrintAST::shouldPrintPattern(const Pattern *P) {
Expand Down Expand Up @@ -2180,16 +2190,78 @@ static void printExtendedTypeName(Type ExtendedType, ASTPrinter &Printer,
Ty->print(Printer, Options);
}


void PrintAST::printSynthesizedExtension(Type ExtendedType,
ExtensionDecl *ExtDecl) {

auto printRequirementsFrom = [&](ExtensionDecl *ED, bool &IsFirst) {
auto Sig = ED->getGenericSignature();
printSingleDepthOfGenericSignature(Sig->getGenericParams(),
Sig->getRequirements(),
IsFirst, PrintRequirements,
[](const Requirement &Req){
return true;
});
};

auto printCombinedRequirementsIfNeeded = [&]() -> bool {
if (!Options.TransformContext ||
!Options.TransformContext->isPrintingSynthesizedExtension())
return false;

// Combined requirements only needed if the transform context is an enabling
// extension of the protocol rather than a nominal (which can't have
// constraints of its own).
ExtensionDecl *Target = dyn_cast<ExtensionDecl>(
Options.TransformContext->getDecl().getAsDecl());
if (!Target || Target == ExtDecl)
return false;

bool IsFirst = true;
if (ExtDecl->isConstrainedExtension()) {
printRequirementsFrom(ExtDecl, IsFirst);
}
if (Target->isConstrainedExtension()) {
if (auto *NTD = Target->getExtendedNominal()) {
// Update the current decl and type transform for Target rather than
// ExtDecl.
PrintOptions Adjusted = Options;
Adjusted.initForSynthesizedExtension(NTD);
llvm::SaveAndRestore<Decl*> TempCurrent(Current, NTD);
llvm::SaveAndRestore<PrintOptions> TempOptions(Options, Adjusted);
printRequirementsFrom(Target, IsFirst);
}
}
return true;
};


if (Options.BracketOptions.shouldOpenExtension(ExtDecl)) {
printDocumentationComment(ExtDecl);
printAttributes(ExtDecl);
Printer << tok::kw_extension << " ";

printExtendedTypeName(ExtendedType, Printer, Options);
printInherited(ExtDecl);
printDeclGenericRequirements(ExtDecl);

// We may need to combine requirements from ExtDecl (which has the members
// to print) and the TransformContexts' decl if it is an enabling extension
// of the base NominalDecl (which can have its own requirements) rather than
// base NominalDecl itself (which can't). E.g:
//
// protocol Foo {}
// extension Foo where <requirments from ExtDecl> { ... }
// struct Bar {}
// extension Bar: Foo where <requirments from TransformContext> { ... }
//
// should produce a synthesized extension of Bar with both sets of
// requirments:
//
// extension Bar where <requirments from ExtDecl+TransformContext { ... }
//
if (!printCombinedRequirementsIfNeeded())
printDeclGenericRequirements(ExtDecl);

}
if (Options.TypeDefinitions) {
printMembersOfDecl(ExtDecl, false,
Expand Down
29 changes: 21 additions & 8 deletions lib/IDE/CommentConversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ struct CommentToXMLConverter {
OS << "</Tags>";
}

void visitDocComment(const DocComment *DC);
void visitDocComment(const DocComment *DC, TypeOrExtensionDecl SynthesizedTarget);
void visitCommentParts(const swift::markup::CommentParts &Parts);
};
} // unnamed namespace
Expand Down Expand Up @@ -297,7 +297,8 @@ void CommentToXMLConverter::visitCommentParts(const swift::markup::CommentParts
}
}

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

StringRef RootEndTag;
Expand Down Expand Up @@ -347,6 +348,10 @@ void CommentToXMLConverter::visitDocComment(const DocComment *DC) {
{
llvm::raw_svector_ostream OS(SS);
Failed = ide::printValueDeclUSR(VD, OS);
if (!Failed && SynthesizedTarget) {
OS << "::SYNTHESIZED::";
Failed = ide::printValueDeclUSR(SynthesizedTarget.getBaseNominal(), OS);
}
}
if (!Failed && !SS.empty()) {
OS << "<USR>" << SS << "</USR>";
Expand All @@ -362,6 +367,9 @@ void CommentToXMLConverter::visitDocComment(const DocComment *DC) {
PO.VarInitializers = false;
PO.ShouldQualifyNestedDeclarations =
PrintOptions::QualifyNestedDeclarations::TypesOnly;
PO.SkipUnderscoredStdlibProtocols = false;
if (SynthesizedTarget)
PO.initForSynthesizedExtension(SynthesizedTarget);

OS << "<Declaration>";
llvm::SmallString<32> DeclSS;
Expand Down Expand Up @@ -398,12 +406,15 @@ static bool getClangDocumentationCommentAsXML(const clang::Decl *D,
return true;
}

static void replaceObjcDeclarationsWithSwiftOnes(const Decl *D,
StringRef Doc,
raw_ostream &OS) {
static void
replaceObjcDeclarationsWithSwiftOnes(const Decl *D, StringRef Doc,
raw_ostream &OS,
TypeOrExtensionDecl SynthesizedTarget) {
StringRef Open = "<Declaration>";
StringRef Close = "</Declaration>";
PrintOptions Options = PrintOptions::printQuickHelpDeclaration();
if (SynthesizedTarget)
Options.initForSynthesizedExtension(SynthesizedTarget);
std::string S;
llvm::raw_string_ostream SS(S);
D->print(SS, Options);
Expand Down Expand Up @@ -444,14 +455,16 @@ std::string ide::extractPlainTextFromComment(const StringRef Text) {
return getLineListFromComment(SourceMgr, MC, Text).str();
}

bool ide::getDocumentationCommentAsXML(const Decl *D, raw_ostream &OS) {
bool ide::getDocumentationCommentAsXML(const Decl *D, raw_ostream &OS,
TypeOrExtensionDecl SynthesizedTarget) {
auto MaybeClangNode = D->getClangNode();
if (MaybeClangNode) {
if (auto *CD = MaybeClangNode.getAsDecl()) {
std::string S;
llvm::raw_string_ostream SS(S);
if (getClangDocumentationCommentAsXML(CD, SS)) {
replaceObjcDeclarationsWithSwiftOnes(D, SS.str(), OS);
replaceObjcDeclarationsWithSwiftOnes(D, SS.str(), OS,
SynthesizedTarget);
return true;
}
}
Expand All @@ -464,7 +477,7 @@ bool ide::getDocumentationCommentAsXML(const Decl *D, raw_ostream &OS) {
return false;

CommentToXMLConverter Converter(OS);
Converter.visitDocComment(DC);
Converter.visitDocComment(DC, SynthesizedTarget);

OS.flush();
return true;
Expand Down
48 changes: 38 additions & 10 deletions lib/IDE/IDETypeChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,16 +282,27 @@ struct SynthesizedExtensionAnalyzer::Implementation {

auto handleRequirements = [&](SubstitutionMap subMap,
GenericSignature GenericSig,
ExtensionDecl *OwningExt,
ArrayRef<Requirement> Reqs) {
ProtocolDecl *BaseProto = OwningExt->getInnermostDeclContext()
->getSelfProtocolDecl();
for (auto Req : Reqs) {
auto Kind = Req.getKind();

// FIXME: Could do something here
if (Kind == RequirementKind::Layout)
continue;

auto First = Req.getFirstType();
auto Second = Req.getSecondType();
Type First = Req.getFirstType();
Type Second = Req.getSecondType();

// Skip protocol's Self : <Protocol> requirement.
if (BaseProto &&
Req.getKind() == RequirementKind::Conformance &&
First->isEqual(BaseProto->getSelfInterfaceType()) &&
Second->getAnyNominal() == BaseProto)
continue;

if (!BaseType->isExistentialType()) {
First = First.subst(subMap);
Second = Second.subst(subMap);
Expand Down Expand Up @@ -346,19 +357,29 @@ struct SynthesizedExtensionAnalyzer::Implementation {
// the extension to the interface types of the base type's
// declaration.
SubstitutionMap subMap;
if (!BaseType->isExistentialType())
subMap = BaseType->getContextSubstitutionMap(M, Ext);
if (!BaseType->isExistentialType()) {
if (auto *NTD = Ext->getExtendedNominal())
subMap = BaseType->getContextSubstitutionMap(M, NTD);
}

assert(Ext->getGenericSignature() && "No generic signature.");
auto GenericSig = Ext->getGenericSignature();
if (handleRequirements(subMap, GenericSig, GenericSig->getRequirements()))
if (handleRequirements(subMap, GenericSig, Ext, GenericSig->getRequirements()))
return {Result, MergeInfo};
}

if (Conf && handleRequirements(Conf->getSubstitutions(M),
Conf->getGenericSignature(),
Conf->getConditionalRequirements()))
return {Result, MergeInfo};
if (Conf) {
SubstitutionMap subMap;
if (!BaseType->isExistentialType()) {
if (auto *NTD = EnablingExt->getExtendedNominal())
subMap = BaseType->getContextSubstitutionMap(M, NTD);
}
if (handleRequirements(subMap,
Conf->getGenericSignature(),
EnablingExt,
Conf->getConditionalRequirements()))
return {Result, MergeInfo};
}

Result.Ext = Ext;
return {Result, MergeInfo};
Expand Down Expand Up @@ -431,7 +452,14 @@ struct SynthesizedExtensionAnalyzer::Implementation {
auto handleExtension = [&](ExtensionDecl *E, bool Synthesized,
ExtensionDecl *EnablingE,
NormalProtocolConformance *Conf) {
if (Options.shouldPrint(E)) {
PrintOptions AdjustedOpts = Options;
if (Synthesized) {
// Members from underscored system protocols should still appear as
// members of the target type, even if the protocols themselves are not
// printed.
AdjustedOpts.SkipUnderscoredStdlibProtocols = false;
}
if (AdjustedOpts.shouldPrint(E)) {
auto Pair = isApplicable(E, Synthesized, EnablingE, Conf);
if (Pair.first) {
InfoMap->insert({E, Pair.first});
Expand Down
8 changes: 4 additions & 4 deletions test/IDE/print_synthesized_extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -237,21 +237,21 @@ extension S13 : P5 {
public func foo1() {}
}

// CHECK1: <synthesized>extension <ref:Struct>S1</ref> where <ref:GenericTypeParam>Self</ref>.P2T1 : <ref:Protocol>P2</ref> {
// CHECK1: <synthesized>extension <ref:Struct>S1</ref> where T : <ref:Protocol>P2</ref> {
// CHECK1-NEXT: <decl:Func>public func <loc>p2member()</loc></decl>
// CHECK1-NEXT: <decl:Func>public func <loc>ef1(<decl:Param>t: T</decl>)</loc></decl>
// CHECK1-NEXT: <decl:Func>public func <loc>ef2(<decl:Param>t: <ref:Struct>S2</ref></decl>)</loc></decl>
// CHECK1-NEXT: }</synthesized>

// CHECK2: <synthesized>extension <ref:Struct>S1</ref> where <ref:GenericTypeParam>Self</ref>.T1 : <ref:Protocol>P3</ref> {
// CHECK2: <synthesized>extension <ref:Struct>S1</ref> where T : <ref:Protocol>P3</ref> {
// CHECK2-NEXT: <decl:Func>public func <loc>p3Func(<decl:Param>i: <ref:Struct>Int</ref></decl>)</loc> -> <ref:Struct>Int</ref></decl>
// CHECK2-NEXT: }</synthesized>

// CHECK3: <synthesized>extension <ref:Struct>S1</ref> where <ref:GenericTypeParam>Self</ref>.T1 == <ref:Struct>Int</ref> {
// CHECK3: <synthesized>extension <ref:Struct>S1</ref> where T == <ref:Struct>Int</ref> {
// CHECK3-NEXT: <decl:Func>public func <loc>p1IntFunc(<decl:Param>i: <ref:Struct>Int</ref></decl>)</loc> -> <ref:Struct>Int</ref></decl>
// CHECK3-NEXT: }</synthesized>

// CHECK4: <synthesized>extension <ref:Struct>S1</ref> where <ref:GenericTypeParam>Self</ref>.T1 == <ref:Struct>S9</ref><<ref:Struct>Int</ref>> {
// CHECK4: <synthesized>extension <ref:Struct>S1</ref> where T == <ref:Struct>S9</ref><<ref:Struct>Int</ref>> {
// CHECK4-NEXT: <decl:Func>public func <loc>S9IntFunc()</loc></decl>
// CHECK4-NEXT: }</synthesized>

Expand Down
Loading