@@ -315,9 +315,13 @@ bool SymbolGraph::synthesizedMemberIsBestCandidate(const ValueDecl *VD,
315315}
316316
317317void SymbolGraph::recordConformanceSynthesizedMemberRelationships (Symbol S) {
318- if (!Walker.Options .EmitSynthesizedMembers || Walker.Options .SkipProtocolImplementations ) {
319- return ;
320- }
318+ // Even if we don't want to emit synthesized members or protocol
319+ // implementations, we still want to emit synthesized members from hidden
320+ // underscored protocols. Save this check so we can skip emitting members
321+ // later if needed.
322+ bool dropSynthesizedMembers = !Walker.Options .EmitSynthesizedMembers ||
323+ Walker.Options .SkipProtocolImplementations ;
324+
321325 const auto D = S.getLocalSymbolDecl ();
322326 const NominalTypeDecl *OwningNominal = nullptr ;
323327 if (const auto *ThisNominal = dyn_cast<NominalTypeDecl>(D)) {
@@ -341,59 +345,89 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
341345 PrintOptions::printModuleInterface (
342346 OwningNominal->getASTContext ().TypeCheckerOpts .PrintFullConvention ));
343347 auto MergeGroupKind = SynthesizedExtensionAnalyzer::MergeGroupKind::All;
344- ExtensionAnalyzer.forEachExtensionMergeGroup (MergeGroupKind,
345- [&](ArrayRef<ExtensionInfo> ExtensionInfos){
346- for (const auto &Info : ExtensionInfos) {
347- if (!Info.IsSynthesized ) {
348- continue ;
349- }
350-
351- // We are only interested in synthesized members that come from an
352- // extension that we defined in our module.
353- if (Info.EnablingExt ) {
354- const auto *ExtM = Info.EnablingExt ->getModuleContext ();
355- if (!Walker.isOurModule (ExtM))
356- continue ;
357- }
358-
359- // If D is not the OwningNominal, it is an ExtensionDecl. In that case
360- // we only want to get members that were enabled by this exact extension.
361- if (D != OwningNominal && Info.EnablingExt != D) {
362- continue ;
363- }
364-
365- for (const auto ExtensionMember : Info.Ext ->getMembers ()) {
366- if (const auto SynthMember = dyn_cast<ValueDecl>(ExtensionMember)) {
367- if (SynthMember->isObjC ()) {
348+ ExtensionAnalyzer.forEachExtensionMergeGroup (
349+ MergeGroupKind, [&](ArrayRef<ExtensionInfo> ExtensionInfos) {
350+ const auto StdlibModule =
351+ OwningNominal->getASTContext ().getStdlibModule (
352+ /* loadIfAbsent=*/ true );
353+
354+ for (const auto &Info : ExtensionInfos) {
355+ if (!Info.IsSynthesized ) {
368356 continue ;
369357 }
370358
371- const auto StdlibModule = OwningNominal->getASTContext ()
372- .getStdlibModule (/* loadIfAbsent=*/ true );
359+ // We are only interested in synthesized members that come from an
360+ // extension that we defined in our module.
361+ if (Info.EnablingExt ) {
362+ const auto *ExtM = Info.EnablingExt ->getModuleContext ();
363+ if (!Walker.isOurModule (ExtM))
364+ continue ;
365+ }
373366
374- // There can be synthesized members on effectively private protocols
375- // or things that conform to them. We don't want to include those.
376- if (isImplicitlyPrivate (SynthMember,
377- /* IgnoreContext =*/
378- SynthMember->getModuleContext () == StdlibModule)) {
367+ // If D is not the OwningNominal, it is an ExtensionDecl. In that case
368+ // we only want to get members that were enabled by this exact
369+ // extension.
370+ if (D != OwningNominal && Info.EnablingExt != D) {
379371 continue ;
380372 }
381373
382- if (!synthesizedMemberIsBestCandidate (SynthMember, OwningNominal)) {
374+ // Extensions to protocols should generate synthesized members only if
375+ // that protocol would otherwise be hidden.
376+ if (auto *Nominal = Info.Ext ->getExtendedNominal ()) {
377+ if (dropSynthesizedMembers &&
378+ !isImplicitlyPrivate (
379+ Nominal, /* IgnoreContext =*/ Nominal->getModuleContext () ==
380+ StdlibModule))
381+ continue ;
382+ } else if (dropSynthesizedMembers) {
383383 continue ;
384384 }
385385
386- auto ExtendedSG = Walker.getModuleSymbolGraph (OwningNominal);
386+ for (const auto ExtensionMember : Info.Ext ->getMembers ()) {
387+ if (const auto SynthMember = dyn_cast<ValueDecl>(ExtensionMember)) {
388+ if (SynthMember->isObjC ()) {
389+ continue ;
390+ }
391+
392+ // There can be synthesized members on effectively private
393+ // protocols or things that conform to them. We don't want to
394+ // include those.
395+ if (isImplicitlyPrivate (SynthMember,
396+ /* IgnoreContext =*/
397+ SynthMember->getModuleContext () ==
398+ StdlibModule)) {
399+ continue ;
400+ }
401+
402+ if (!synthesizedMemberIsBestCandidate (SynthMember,
403+ OwningNominal)) {
404+ continue ;
405+ }
406+
407+ Symbol Source (this , SynthMember, OwningNominal);
408+
409+ if (auto *InheritedDecl = Source.getInheritedDecl ()) {
410+ if (auto *ParentDecl =
411+ InheritedDecl->getDeclContext ()->getAsDecl ()) {
412+ if (dropSynthesizedMembers &&
413+ !isImplicitlyPrivate (
414+ ParentDecl,
415+ /* IgnoreContext =*/ ParentDecl->getModuleContext () ==
416+ StdlibModule)) {
417+ continue ;
418+ }
419+ }
420+ }
387421
388- Symbol Source ( this , SynthMember, OwningNominal);
422+ auto ExtendedSG = Walker. getModuleSymbolGraph ( OwningNominal);
389423
390- ExtendedSG->Nodes .insert (Source);
424+ ExtendedSG->Nodes .insert (Source);
391425
392- ExtendedSG->recordEdge (Source, S, RelationshipKind::MemberOf ());
393- }
394- }
395- }
396- });
426+ ExtendedSG->recordEdge (Source, S, RelationshipKind::MemberOf ());
427+ }
428+ }
429+ }
430+ });
397431}
398432
399433void
@@ -784,6 +818,21 @@ bool SymbolGraph::isImplicitlyPrivate(const Decl *D,
784818 // thing is also private. We could be looking at the `B` of `_A.B`.
785819 if (const auto *DC = D->getDeclContext ()) {
786820 if (const auto *Parent = DC->getAsDecl ()) {
821+ // Exception: Children of underscored protocols should be considered
822+ // public, even though the protocols themselves aren't. This way,
823+ // synthesized copies of those symbols are correctly added to the public
824+ // API of public types that conform to underscored protocols.
825+ if (isa<ProtocolDecl>(Parent) && Parent->hasUnderscoredNaming ()) {
826+ return false ;
827+ }
828+ if (const auto *ParentExtension = dyn_cast<ExtensionDecl>(Parent)) {
829+ if (const auto *ParentNominal = ParentExtension->getExtendedNominal ()) {
830+ if (isa<ProtocolDecl>(ParentNominal) &&
831+ ParentNominal->hasUnderscoredNaming ()) {
832+ return false ;
833+ }
834+ }
835+ }
787836 return isImplicitlyPrivate (Parent, IgnoreContext);
788837 }
789838 }
0 commit comments