mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[IDE][SourceKit/DocSupport] Add members of underscored protocol extensions in extensions of conforming types.
We would previously hide the protocol, its extensions and members, but the '_' prefix really just means the protocol itself isn't intended for clients, rather than its members. This also adds support for 'fully_annotated_decl' entries in doc-info for extensions to be consistent with every other decl, and removes the 'fully_annotated_generic_signature' entry we supplied as a fallback. Also fixes several bugs with the synthesized extensions mechanism: - The type sustitutions applied to the extension's requirements were computed using the extension itself as the decl context rather than the extension's nominal. The meant the extension's requirements themselves were assumed to hold when determining the substitutions, so equality constraints were always met. Because of this extension members were incorrectly merged with the base nominal or its extensions despite having additional constraints. - Types within the requirements weren't being transformed when printed (e.g. 'Self.Element' was printed rather than 'T') both in the interface output and in the requirements list. We were also incorrectly printing requirements that were already satisfied once the base type was subsituted in. - If both the protocol extension and 'enabling' extension of the base nominal that added the protocol conformance had conditional requirements, we were only printing the protocol extension's requirements in the synthesized extension. - The USR and annotated decl output embedded in the 'key.doc.full_as_xml' string for synthesized members were printed to match their original context, rather than the synthesized one. Resolves rdar://problem/57121937
This commit is contained in:
@@ -260,7 +260,9 @@ struct SourceTextInfo {
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
static void initDocGenericParams(const Decl *D, DocEntityInfo &Info) {
|
||||
static void initDocGenericParams(const Decl *D, DocEntityInfo &Info,
|
||||
TypeOrExtensionDecl SynthesizedTarget,
|
||||
bool IsSynthesizedExt) {
|
||||
auto *GC = D->getAsGenericContext();
|
||||
if (!GC)
|
||||
return;
|
||||
@@ -276,36 +278,107 @@ static void initDocGenericParams(const Decl *D, DocEntityInfo &Info) {
|
||||
if (ParentSig && ParentSig->isEqual(GenericSig))
|
||||
return;
|
||||
|
||||
|
||||
// If we have a synthesized target, map from its base type into the this
|
||||
// declaration's innermost type context, or if we're dealing with the
|
||||
// synthesized extention itself rather than a member, into its extended
|
||||
// nominal (the extension's own requirements shouldn't be considered in the
|
||||
// substitution).
|
||||
SubstitutionMap SubMap;
|
||||
Type BaseType;
|
||||
if (SynthesizedTarget) {
|
||||
BaseType = SynthesizedTarget.getBaseNominal()->getDeclaredInterfaceType();
|
||||
if (!BaseType->isExistentialType()) {
|
||||
DeclContext *DC;
|
||||
if (IsSynthesizedExt)
|
||||
DC = cast<ExtensionDecl>(D)->getExtendedNominal();
|
||||
else
|
||||
DC = D->getInnermostDeclContext()->getInnermostTypeContext();
|
||||
auto *M = DC->getParentModule();
|
||||
SubMap = BaseType->getContextSubstitutionMap(M, DC);
|
||||
}
|
||||
}
|
||||
|
||||
auto SubstTypes = [&](Type Ty) {
|
||||
return Ty.subst(SubMap, SubstFlags::DesugarMemberTypes);
|
||||
};
|
||||
|
||||
// FIXME: Not right for extensions of nested generic types
|
||||
if (GC->isGeneric()) {
|
||||
for (auto *GP : GenericSig->getInnermostGenericParams()) {
|
||||
if (GP->getDecl()->isImplicit())
|
||||
continue;
|
||||
Type TypeToPrint = GP;
|
||||
if (!SubMap.empty()) {
|
||||
if (auto ArgTy = SubstTypes(GP)) {
|
||||
if (!ArgTy->hasError()) {
|
||||
// Ignore parameter that aren't generic after substitution
|
||||
if (!ArgTy->is<ArchetypeType>() && !ArgTy->isTypeParameter())
|
||||
continue;
|
||||
TypeToPrint = ArgTy;
|
||||
}
|
||||
}
|
||||
}
|
||||
DocGenericParam Param;
|
||||
Param.Name = std::string(GP->getName());
|
||||
Param.Name = TypeToPrint->getString();
|
||||
Info.GenericParams.push_back(Param);
|
||||
}
|
||||
}
|
||||
|
||||
ProtocolDecl *proto = nullptr;
|
||||
ProtocolDecl *Proto = nullptr;
|
||||
if (auto *typeDC = GC->getInnermostTypeContext())
|
||||
proto = typeDC->getSelfProtocolDecl();
|
||||
Proto = typeDC->getSelfProtocolDecl();
|
||||
|
||||
for (auto &Req : GenericSig->getRequirements()) {
|
||||
// Skip protocol Self requirement.
|
||||
if (proto &&
|
||||
for (auto Req: GenericSig->getRequirements()) {
|
||||
if (Proto &&
|
||||
Req.getKind() == RequirementKind::Conformance &&
|
||||
Req.getFirstType()->isEqual(proto->getSelfInterfaceType()) &&
|
||||
Req.getSecondType()->getAnyNominal() == proto)
|
||||
Req.getFirstType()->isEqual(Proto->getSelfInterfaceType()) &&
|
||||
Req.getSecondType()->getAnyNominal() == Proto)
|
||||
continue;
|
||||
|
||||
auto First = Req.getFirstType();
|
||||
Type Second;
|
||||
if (Req.getKind() != RequirementKind::Layout)
|
||||
Second = Req.getSecondType();
|
||||
|
||||
if (!SubMap.empty()) {
|
||||
Type SubFirst = SubstTypes(First);
|
||||
if (!SubFirst->hasError())
|
||||
First = SubFirst;
|
||||
if (Second) {
|
||||
Type SubSecond = SubstTypes(Second);
|
||||
if (!SubSecond->hasError())
|
||||
Second = SubSecond;
|
||||
// Ignore requirements that don't involve a generic after substitution.
|
||||
if (!(First->is<ArchetypeType>() || First->isTypeParameter()) &&
|
||||
!(Second->is<ArchetypeType>() || Second->isTypeParameter()))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
std::string ReqStr;
|
||||
PrintOptions Opts;
|
||||
llvm::raw_string_ostream OS(ReqStr);
|
||||
Req.print(OS, Opts);
|
||||
PrintOptions Opts;
|
||||
if (Req.getKind() != RequirementKind::Layout) {
|
||||
Requirement(Req.getKind(), First, Second).print(OS, Opts);
|
||||
} else {
|
||||
Requirement(Req.getKind(), First, Req.getLayoutConstraint()).print(OS, Opts);
|
||||
}
|
||||
OS.flush();
|
||||
Info.GenericRequirements.push_back(std::move(ReqStr));
|
||||
}
|
||||
|
||||
if (IsSynthesizedExt) {
|
||||
// If there's a conditional conformance on the base type that 'enabled' this
|
||||
// extension, we need to print its requirements too.
|
||||
if (auto *EnablingExt = dyn_cast<ExtensionDecl>(SynthesizedTarget.getAsDecl())) {
|
||||
if (EnablingExt->isConstrainedExtension()) {
|
||||
initDocGenericParams(EnablingExt, Info,
|
||||
/*Target=*/EnablingExt->getExtendedNominal(),
|
||||
/*IsSynthesizedExtension*/false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool initDocEntityInfo(const Decl *D,
|
||||
@@ -376,29 +449,12 @@ static bool initDocEntityInfo(const Decl *D,
|
||||
llvm::SmallString<128> DocBuffer;
|
||||
{
|
||||
llvm::raw_svector_ostream OSS(DocBuffer);
|
||||
ide::getDocumentationCommentAsXML(D, OSS);
|
||||
ide::getDocumentationCommentAsXML(D, OSS, SynthesizedTarget);
|
||||
}
|
||||
StringRef DocRef = (StringRef)DocBuffer;
|
||||
if (IsSynthesizedExtension &&
|
||||
DocRef.find("<Declaration>") != StringRef::npos) {
|
||||
StringRef Open = "extension ";
|
||||
assert(DocRef.find(Open) != StringRef::npos);
|
||||
auto FirstPart = DocRef.substr(0, DocRef.find(Open) + (Open).size());
|
||||
auto SecondPart = DocRef.substr(FirstPart.size());
|
||||
auto ExtendedName = ((const ExtensionDecl*)D)->getExtendedNominal()
|
||||
->getName().str();
|
||||
assert(SecondPart.startswith(ExtendedName));
|
||||
SecondPart = SecondPart.substr(ExtendedName.size());
|
||||
llvm::SmallString<128> UpdatedDocBuffer;
|
||||
UpdatedDocBuffer.append(FirstPart);
|
||||
UpdatedDocBuffer.append(SynthesizedTargetNTD->getName().str());
|
||||
UpdatedDocBuffer.append(SecondPart);
|
||||
OS << UpdatedDocBuffer;
|
||||
} else
|
||||
OS << DocBuffer;
|
||||
OS << DocBuffer;
|
||||
}
|
||||
|
||||
initDocGenericParams(D, Info);
|
||||
initDocGenericParams(D, Info, SynthesizedTarget, IsSynthesizedExtension);
|
||||
|
||||
llvm::raw_svector_ostream LocalizationKeyOS(Info.LocalizationKey);
|
||||
ide::getLocalizationKey(D, LocalizationKeyOS);
|
||||
@@ -410,14 +466,13 @@ static bool initDocEntityInfo(const Decl *D,
|
||||
VD, SynthesizedTarget, OS);
|
||||
else
|
||||
SwiftLangSupport::printFullyAnnotatedDeclaration(VD, Type(), OS);
|
||||
} else if (auto *E = dyn_cast<ExtensionDecl>(D)) {
|
||||
if (auto Sig = E->getGenericSignature()) {
|
||||
// The extension under printing is potentially part of a synthesized
|
||||
// extension. Thus it's hard to print the fully annotated decl. We
|
||||
// need to at least print the generic signature here.
|
||||
llvm::raw_svector_ostream OS(Info.FullyAnnotatedGenericSig);
|
||||
SwiftLangSupport::printFullyAnnotatedGenericReq(Sig, OS);
|
||||
}
|
||||
} else if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
|
||||
llvm::raw_svector_ostream OS(Info.FullyAnnotatedDecl);
|
||||
if (SynthesizedTarget)
|
||||
SwiftLangSupport::printFullyAnnotatedSynthesizedDeclaration(
|
||||
ED, SynthesizedTarget, OS);
|
||||
else
|
||||
SwiftLangSupport::printFullyAnnotatedDeclaration(ED, OS);
|
||||
}
|
||||
|
||||
if (DeclaringModForCrossImport) {
|
||||
|
||||
Reference in New Issue
Block a user