@@ -315,9 +315,13 @@ bool SymbolGraph::synthesizedMemberIsBestCandidate(const ValueDecl *VD,
315
315
}
316
316
317
317
void 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
+
321
325
const auto D = S.getLocalSymbolDecl ();
322
326
const NominalTypeDecl *OwningNominal = nullptr ;
323
327
if (const auto *ThisNominal = dyn_cast<NominalTypeDecl>(D)) {
@@ -341,59 +345,89 @@ void SymbolGraph::recordConformanceSynthesizedMemberRelationships(Symbol S) {
341
345
PrintOptions::printModuleInterface (
342
346
OwningNominal->getASTContext ().TypeCheckerOpts .PrintFullConvention ));
343
347
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 ) {
368
356
continue ;
369
357
}
370
358
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
+ }
373
366
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) {
379
371
continue ;
380
372
}
381
373
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) {
383
383
continue ;
384
384
}
385
385
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
+ }
387
421
388
- Symbol Source ( this , SynthMember, OwningNominal);
422
+ auto ExtendedSG = Walker. getModuleSymbolGraph ( OwningNominal);
389
423
390
- ExtendedSG->Nodes .insert (Source);
424
+ ExtendedSG->Nodes .insert (Source);
391
425
392
- ExtendedSG->recordEdge (Source, S, RelationshipKind::MemberOf ());
393
- }
394
- }
395
- }
396
- });
426
+ ExtendedSG->recordEdge (Source, S, RelationshipKind::MemberOf ());
427
+ }
428
+ }
429
+ }
430
+ });
397
431
}
398
432
399
433
void
@@ -784,6 +818,21 @@ bool SymbolGraph::isImplicitlyPrivate(const Decl *D,
784
818
// thing is also private. We could be looking at the `B` of `_A.B`.
785
819
if (const auto *DC = D->getDeclContext ()) {
786
820
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
+ }
787
836
return isImplicitlyPrivate (Parent, IgnoreContext);
788
837
}
789
838
}
0 commit comments