@@ -518,78 +518,91 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
518518 let results = Synchronized < [ LinkResolveResult ] > ( [ ] )
519519 results. sync ( { $0. reserveCapacity ( references. count) } )
520520
521+ func inheritsDocumentationFromOtherModule( _ documentationNode: DocumentationNode , symbolOriginReference: ResolvedTopicReference ) -> Bool {
522+ // Check that this symbol only has documentation from an in-source documentation comment
523+ guard documentationNode. docChunks. count == 1 ,
524+ case . sourceCode = documentationNode. docChunks. first? . source
525+ else {
526+ return false
527+ }
528+
529+ // Check that that documentation comment is inherited from a symbol belonging to another module
530+ guard let symbolSemantic = documentationNode. semantic as? Symbol ,
531+ let originSymbolSemantic = documentationCache [ symbolOriginReference] ? . semantic as? Symbol
532+ else {
533+ return false
534+ }
535+ return symbolSemantic. moduleReference != originSymbolSemantic. moduleReference
536+ }
537+
521538 let resolveNodeWithReference : ( ResolvedTopicReference ) -> Void = { [ unowned self] reference in
522- if var documentationNode = try ? entity ( with: reference) , documentationNode. semantic is Article || documentationNode. semantic is Symbol {
523- for doc in documentationNode. docChunks {
524- let source : URL ?
525- switch doc. source {
526- case _ where documentationNode. semantic is Article ,
527- . documentationExtension:
528- source = documentLocationMap [ reference]
529- case . sourceCode( let location, _) :
530- // For symbols, first check if we should reference resolve
531- // inherited docs or not. If we don't inherit the docs
532- // we should also skip reference resolving the chunk.
533- if let semantic = documentationNode. semantic as? Symbol ,
534- let origin = semantic. origin, !externalMetadata. inheritDocs
535- {
536- // If the two symbols are coming from different modules,
537- // regardless if they are in the same bundle
538- // (for example Foundation and SwiftUI), skip link resolving.
539- if let originSymbol = documentationCache [ origin. identifier] ? . semantic as? Symbol ,
540- originSymbol. moduleReference != semantic. moduleReference {
541- continue
542- }
543- }
544-
545- source = location? . url ( )
546- }
547-
548- // Find the inheritance parent, if the docs are inherited.
549- let inheritanceParentReference : ResolvedTopicReference ?
550- if let origin = ( documentationNode. semantic as? Symbol ) ? . origin,
551- let originReference = documentationCache. reference ( symbolID: origin. identifier)
552- {
553- inheritanceParentReference = originReference
554- } else {
555- inheritanceParentReference = nil
556- }
557-
558- var resolver = ReferenceResolver ( context: self , bundle: bundle, source: source, rootReference: reference, inheritanceParentReference: inheritanceParentReference)
539+ guard var documentationNode = try ? entity ( with: reference) ,
540+ documentationNode. semantic is Article || documentationNode. semantic is Symbol
541+ else {
542+ return
543+ }
559544
560- // Update the cache with the resolved node.
561- // We aggressively release used memory, since we're copying all semantic objects
562- // on the line below while rewriting nodes with the resolved content.
563- documentationNode. semantic = autoreleasepool { resolver. visit ( documentationNode. semantic) }
545+ let symbolOriginReference = ( documentationNode. semantic as? Symbol ) ? . origin. flatMap { origin in
546+ documentationCache. reference ( symbolID: origin. identifier)
547+ }
548+ // Check if we should skip resolving links for inherited documentation from other modules.
549+ if !externalMetadata. inheritDocs,
550+ let symbolOriginReference,
551+ inheritsDocumentationFromOtherModule ( documentationNode, symbolOriginReference: symbolOriginReference)
552+ {
553+ // Don't resolve any links for this symbol.
554+ return
555+ }
556+
557+ var resolver = ReferenceResolver ( context: self , bundle: bundle, rootReference: reference, inheritanceParentReference: symbolOriginReference)
558+
559+ // Update the node with the markup that contains resolved references instead of authored links.
560+ documentationNode. semantic = autoreleasepool {
561+ // We use an autorelease pool to release used memory as soon as possible, since the resolver will copy each semantic value
562+ // to rewrite it and replace the authored links with resolved reference strings instead.
563+ resolver. visit ( documentationNode. semantic)
564+ }
565+
566+ let problems : [ Problem ]
567+ if documentationNode. semantic is Article {
568+ // Diagnostics for articles have correct source ranges and don't need to be modified.
569+ problems = resolver. problems
570+ } else {
571+ // Diagnostics for in-source documentation comments need to be offset based on the start location of the comment in the source file.
572+
573+ // Get the source location
574+ let inSourceDocumentationCommentInfo = documentationNode. inSourceDocumentationChunk
575+
576+ // Post-process and filter out unwanted diagnostics (for example from inherited documentation comments)
577+ problems = resolver. problems. compactMap { problem in
578+ guard let source = problem. diagnostic. source else {
579+ // Ignore any diagnostic without a source location. These can't be meaningfully presented to the user.
580+ return nil
581+ }
564582
565- let pageImageProblems = documentationNode. metadata? . pageImages. compactMap { pageImage in
566- return resolver. resolve (
567- resource: pageImage. source,
568- range: pageImage. originalMarkup. range,
569- severity: . warning
570- )
571- } ?? [ ]
583+ if source == inSourceDocumentationCommentInfo? . url, let offset = inSourceDocumentationCommentInfo? . offset {
584+ // Diagnostics from an in-source documentation comment need to be offset based on the location of that documentation comment.
585+ var modifiedProblem = problem
586+ modifiedProblem. offsetWithRange ( offset)
587+ return modifiedProblem
588+ }
572589
573- resolver. problems. append ( contentsOf: pageImageProblems)
574-
575- var problems = resolver. problems
576-
577- if case . sourceCode( _, let offset) = doc. source, documentationNode. kind. isSymbol {
578- // Offset all problem ranges by the start location of the
579- // source comment in the context of the complete file.
580- if let docRange = offset {
581- for i in problems. indices {
582- problems [ i] . offsetWithRange ( docRange)
583- }
584- } else {
585- problems. removeAll ( )
586- }
587- }
588-
589- let result : LinkResolveResult = ( reference: reference, node: documentationNode, problems: problems)
590- results. sync ( { $0. append ( result) } )
590+ // Diagnostics from documentation extension files have correct source ranges and don't need to be modified.
591+ return problem
591592 }
592593 }
594+
595+ // Also resolve the node's page images. This isn't part of the node's 'semantic' value (resolved above).
596+ let pageImageProblems = documentationNode. metadata? . pageImages. compactMap { pageImage in
597+ return resolver. resolve (
598+ resource: pageImage. source,
599+ range: pageImage. originalMarkup. range,
600+ severity: . warning
601+ )
602+ } ?? [ ]
603+
604+ let result : LinkResolveResult = ( reference: reference, node: documentationNode, problems: problems + pageImageProblems)
605+ results. sync ( { $0. append ( result) } )
593606 }
594607
595608 // Resolve links concurrently if there are no external resolvers.
@@ -604,7 +617,8 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
604617 // Record symbol links as symbol "mentions" for automatic cross references
605618 // on rendered symbol documentation.
606619 if let article = result. node. semantic as? Article ,
607- case . article = DocumentationContentRenderer . roleForArticle ( article, nodeKind: result. node. kind) {
620+ case . article = DocumentationContentRenderer . roleForArticle ( article, nodeKind: result. node. kind)
621+ {
608622 for markup in article. abstractSection? . content ?? [ ] {
609623 var mentions = SymbolLinkCollector ( context: self , article: result. node. reference, baseWeight: 2 )
610624 mentions. visit ( markup)
@@ -652,7 +666,7 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
652666 autoreleasepool {
653667 let url = technologyResult. source
654668 let unresolvedTechnology = technologyResult. value
655- var resolver = ReferenceResolver ( context: self , bundle: bundle, source : url )
669+ var resolver = ReferenceResolver ( context: self , bundle: bundle)
656670 let technology = resolver. visit ( unresolvedTechnology) as! Technology
657671 diagnosticEngine. emit ( resolver. problems)
658672
@@ -724,7 +738,7 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
724738 autoreleasepool {
725739 let url = tutorialResult. source
726740 let unresolvedTutorial = tutorialResult. value
727- var resolver = ReferenceResolver ( context: self , bundle: bundle, source : url )
741+ var resolver = ReferenceResolver ( context: self , bundle: bundle)
728742 let tutorial = resolver. visit ( unresolvedTutorial) as! Tutorial
729743 diagnosticEngine. emit ( resolver. problems)
730744
@@ -758,7 +772,7 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
758772 autoreleasepool {
759773 let url = articleResult. source
760774 let unresolvedTutorialArticle = articleResult. value
761- var resolver = ReferenceResolver ( context: self , bundle: bundle, source : url )
775+ var resolver = ReferenceResolver ( context: self , bundle: bundle)
762776 let article = resolver. visit ( unresolvedTutorialArticle) as! TutorialArticle
763777 diagnosticEngine. emit ( resolver. problems)
764778
0 commit comments